Where’s the Money - Supplemental Findings

While creating the content for my DefCon 32 talk, Where’s the Money: Defeating ATM Disk Encryption, I observed two additional vulnerabilities that had been overlooked in the heat of the research. These issues were reported to Diebold Nixdorf and later identified as low impact issues and, thus, did not receive additional patching. The impact of these issues vary across the different versions of Superschaf, the Linux architecture supporting the FDE module of Diebold Nixdorf’s Vynamic Security Suite (VSS), detailed in my white-paper. As of VSS v3.3.0 SR16, they are little more than information discovery tools but provide some additional insight to the remediation controls implemented by Diebold Nixdorf. In this article I plan to publicly disclose these additional details as supplemental content to my white-paper, linked above.

As a refresher, Diebold Nixdorf’s Vynamic Security Suite (VSS) employs the software solution CryptoPro Security Disk for BitLocker, aka CryptoPro. This solution introduces an abstraction layer to the system through the addition of a small Linux partition aka Superschaf, located at the end of a GUID partition table. The purpose of this environment is to conduct file integrity checks to assert a known system state, performing a number cryptographic routines with a SHA256 index table. This index table is hardcoded into the EFI binary Bootxsa.efi and contains a SHA sum for a number of files, within the Linux partition, that should be validated to assert system integrity. These sum values are represented as a double hash, where the SHA256 sum of the target file is generated and then that value is summed again. Additionally, this software solution is designed to work with or without a TPM and supports TPMs of version 1.1, 1.2, and 2.0.

Once CryptoPro is installed, the system will operate in a dual-boot state, switching between Linux and Windows. Each boot sequence will run through both operating systems before the ATM becomes fully operational. In this architecture, system integrity validation is done between UEFI and Linux - this process is defined as Pre-Boot Authorization (PBA) Phase I and Phase II. Additionally, CryptoPro updates the firmware keychain information and activates SecureBoot, disabling the execution of unsigned or untrusted EFI binaries.

From an information security perspective, exploitation of the secured architecture requires bypassing of PBA Phase I, or the UEFI initialization process, and obtaining code execution prior to the initialization of PBA Phase II. The following graphic depicts this circular boot sequence:

Vynamic Security Suite - PBA Boot Cycle

CVE-2024-46916

In my original content, I described an attack surface where /etc/fstab could be moved and linked from /etc/mtab, labeled as CVE-2023-24063. This slight system change disabled the creation of several temporary file systems and gave persistence to /root. From this vantage point, root’s .profile script could be modified - allowing for code execution during system initialization. This path of compromise was removed from the next patch release of VSS, version 3.3.0 SR10, as the rm /etc/mtab instruction was removed from /etc/rc.d/init.d/mountfs. However, there were two additional delete instructions that perpetuated this attack /fastboot and /forcefsck, also located in /etc/rc.d/init.d/mountfs:

# Remove fsck-related file system watermarks.
rm -f /fastboot /forcefsck

...

boot_mesg "Recording existing mounts in /etc/mtab..."
> /etc/mtab
mount -f / || failed=1
mount -f /proc || failed=1
mount -f /sys || failed=1
mount -f /sys/kernel/security ||  failed=1
(exit ${failed})
evaluate_retval

Before I continue with the details surrounding this attack surface, I would like to highlight that the PBA Phase I EFI index table for v3.3.0 SR9 and SR10 only contains about 58 elements that are validated as part of the Linux file system. Extraction of this index is elementary with the use of strings and a filter:

$ strings /EFI/CPSD/Bootxsa.efi | awk '/^\/[a-z]+\// && !/(main|debug)/ {print $0}'        
/bin/bash
/bin/cat
/bin/chmod
/bin/dmesg
/bin/echo
/bin/grep
/bin/keyctl
/bin/mkdir
/bin/mknod
/bin/mount
/bin/sh
...

Having visibility of this content is critical to properly construct an exploit against the platform. As this list represents the Linux integrity SHA256 index table, contained in Bootxsa.efi. However, the full partition is not integrity validated and only the files that physically exist or selected are checked. Therefore, content that does not already exist or is not indexed, is fair game. As a result, there is a fairly large file based attack surface for the Linux partition. The default rootfs is listed below, notice it only contains a list of directories:

$ ls -l ./
total 84
drwxr-xr-x  2 root root  4096 Sep 14  2022 bin
drwxr-xr-x  3 root root  4096 Nov 10  2022 boot
drwxr-xr-x  2 root root  4096 Apr 18  2008 dev
drwxr-xr-x 22 root root  4096 Sep 14  2022 etc
drwxr-xr-x  2 root root  4096 Apr 21  2008 home
drwxr-xr-x  8 root root  4096 Sep 14  2022 lib
drwxr-xr-x  3 root root  4096 Sep 14  2022 libexec
drwxr-xr-x  2 root root  4096 Nov 17  2022 LOG
drwx------  2 root root 16384 Sep 14  2022 lost+found
drwxr-xr-x  2 root root  4096 Nov 17  2022 mnt
drwxr-xr-x  2 root root  4096 Apr 18  2008 proc
drwxr-xr-x  2 root root  4096 Nov 17  2022 root
drwxr-xr-x  4 root root  4096 Aug 19  2021 run
drwxr-xr-x  2 root root  4096 Sep 14  2022 sbin
drwxr-xr-x  2 root root  4096 Apr 18  2008 sys
drwxr-xr-x  2 root root  4096 Nov 17  2022 tmp
drwxr-xr-x 13 root root  4096 Sep 14  2022 usr
drwxr-xr-x  2 root root  4096 Nov 17  2022 var

Since /fastboot and /forcefsck don’t exist, they are open targets. As you may have guessed, yes, this vulnerability does provide an alternative code execution path in both VSS v3.3.0 SR9 and SR10, overlapping both CVE-2023-24063 and CVE-2023-24062. This vantage point of code execution is ultimately disabled with the introduction of SR12, where an rm and mkdir instruction set is introduced to /etc/rc.d/init.d/mountfs. This, of course, superseeds the rm instruction for /fastboot and /forcefsck.

# remove and re-create /root /var /tmp /mnt
rm -rf /root /var /tmp /mnt
mkdir /root /var /tmp /mnt

# Remove fsck-related file system watermarks.
rm -f /fastboot /forcefsck

This attack path is further shackled by the introduction of the PBA Phase I additions, in SR15 and SR16, which contain null hash sum and symlink validation checks. However, relevance of this attack surface does not end there. In VSS v3.3.0 SR16 the rm and mkdir instructions were commented out of /etc/rc.d/init.d/mountfs - reintroducing some aspects of this vulnerability!

# remove and re-create /root /var /mnt
# rm -rf /root /var /mnt
# mkdir /root /var /mnt

# Remove fsck-related file system watermarks.
rm -f /fastboot /forcefsck

Removal of these instructions now provide a vantage point to again delete /etc/fstab and obtain visibility to the otherwise inaccessible content of the TMPFS protected directories.

$ ls -al etc/fstab           
lrwxrwxrwx 1 root root 9 Mar 31 08:50 etc/fstab -> /fastboot

$ ls -al ./      
total 96
drwxr-xr-x 20 root root  4096 Mar 31 08:50 .
drw-------  6 root root  4096 Mar 31 08:49 ..
drwxr-xr-x  2 root root  4096 Jun  5  2023 bin
drwxr-xr-x  3 root root  4096 Jul 14  2023 boot
drwxr-xr-x  2 root root  4096 Apr 18  2008 dev
drwxr-xr-x 22 root root  4096 Mar 31 08:50 etc
-rw-r--r--  1 root root   675 Nov 22  2019 fastboot
drwxr-xr-x  2 root root  4096 Apr 21  2008 home
drwxr-xr-x  8 root root  4096 Jun  5  2023 lib
drwxr-xr-x  3 root root  4096 Jun  5  2023 libexec
...

As a result, we are able to get some insight into /root and /var. From /var we can pull the bootlog.

# ls -al root
total 96
drwx------ 10 root root 4096 Apr 23 08:28 .
drwxr-xr-x 20 root root 4096 Apr 23 08:28 ..
-rw-r--r--  1 root root  420 Mar 18  2016 .bashrc
drwx------  2 root root 4096 Apr 24  2013 .cache
drwx------  4 root root 4096 Apr 17  2018 .config
-rwxr-xr-x  1 root root   83 Oct 14  2008 console.sh
drwx------  3 root root 4096 Apr 23 08:28 .dbus
drwxr-xr-x  2 root root 4096 Jul 18  2012 .devilspie
drwx------  2 root root 4096 Apr 23 08:28 .fluxbox
-rwxr-xr-x  1 root root 6894 Jan 26  2011 lang
drwxr-xr-x  3 root root 4096 Apr 28  2009 .local
-rwxr-xr-x  1 root root  616 Jan 29  2009 mount_fatstore.sh
-rw-r--r--  1 root root  631 Aug 23  2012 .profile
drwx------  4 root root 4096 Sep 24  2012 .scim
-rwxr-xr-x  1 root root  111 Nov 23  2010 startx_local.sh
-rwxr-xr-x  1 root root  363 Apr 28  2021 startx.sh
-rw-r--r--  1 root root  521 Aug 17  2021 startx.sh.sig
-rwxr-xr-x  1 root root  920 Jan 22  2019 sushe_start.sh
-rw-r--r--  1 root root  521 Aug 17  2021 sushe_start.sh.sig
drwxr-xr-x  3 root root 4096 Sep  2  2009 .themes
-rw-------  1 root root    0 Oct 28  2010 .Xauthority
-rw-r--r--  1 root root    0 Apr 23  2013 .Xdefaults
-rw-r--r--  1 root root   37 Apr 25  2008 .xinitrc
# tree
...
├── log
│   ├── boot.log
│   ├── btmp
│   ├── hotplug
│   ├── lastlog
│   ├── wtmp
│   └── Xorg.0.log
# cat boot.log
Apr 23 12:28:34 +00:00 (none)  Mounting virtual file systems: /run /proc /sys /tmp /dev /sys/kernel/security OK
Apr 23 12:28:34 +00:00 (none)  Creating IMA keyringApr 23 12:28:34 +00:00 (none)  Loading IMA policiesApr 23 12:28:34 +00:00 (none)  Bringing up the loopback interface... OK
Apr 23 12:28:34 +00:00 (none)  Setting hostname to (none)... OK
Apr 23 12:28:34 +00:00 (none)  Populating /dev with device nodes...  OK
Apr 23 12:28:35 +00:00 (none)  Activating all swap files/partitions... OK
Apr 23 12:28:35 +00:00 (none)  /fastboot found, will omit  file system checks as requested.
...

But unfortunately this does not give us any insight to the contents of /tmp 🙁.. This directory is protected in VSS v330 SR16 via /etc/rc.d/init.d/mountvirtfs, where additional mitigation controls have been introduced. Let me briefly take you back to VSS v330 SR9 and mountkrnfs, the predecessor of mountvirtfs:

...
        if ! mountpoint /run > /dev/null; then
            boot_mesg "Mounting /run" ${INFO}
            mount /run || failed=1
        fi

        mkdir -p /run/lock /run/shm
        chmod 1777 /run/shm /run/lock

        boot_mesg -n "Mounting kernel-based file systems:" ${INFO}

        if ! mountpoint /proc >/dev/null; then
            boot_mesg -n " /proc" ${NORMAL}
            mount -n /proc || failed=1
        fi

        if ! mountpoint /sys >/dev/null; then
            boot_mesg -n " /sys" ${NORMAL}
            mount -n /sys || failed=1
        fi

        if ! mountpoint /dev >/dev/null; then
            boot_mesg " ${INFO}/dev"
            mount -o mode=0755,nosuid /dev || failed=1
        fi

        if ! mountpoint /sys/kernel/security >/dev/null; then
            boot_mesg " ${INFO}/sys/kernel/security"
            mount -n /sys/kernel/security || failed=1
        fi
...

his file is responsible for mapping kernel runtime memory to user space during sysinit as the first executed script of SystemV:

# ls -al nix/etc/rc.d/rcsysinit.d 
total 8
drwxr-xr-x  2 root root 4096 Nov 25  2019 .
drwxr-xr-x 11 root root 4096 Oct 12  2010 ..
lrwxrwxrwx  1 root root   21 Oct 12  2010 S00mountkernfs -> ../init.d/mountkernfs
lrwxrwxrwx  1 root root   13 Nov 25  2019 S01ima -> ../init.d/ima
lrwxrwxrwx  1 root root   20 Oct 12  2010 S02consolelog -> ../init.d/consolelog
lrwxrwxrwx  1 root root   17 Oct 12  2010 S05modules -> ../init.d/modules
lrwxrwxrwx  1 root root   14 Oct 12  2010 S10udev -> ../init.d/udev
lrwxrwxrwx  1 root root   14 Oct 12  2010 S20swap -> ../init.d/swap
lrwxrwxrwx  1 root root   17 Oct 12  2010 S30checkfs -> ../init.d/checkfs
lrwxrwxrwx  1 root root   17 Oct 12  2010 S40mountfs -> ../init.d/mountfs
lrwxrwxrwx  1 root root   17 Oct 12  2010 S45cleanfs -> ../init.d/cleanfs
lrwxrwxrwx  1 root root   20 Oct 12  2010 S50udev_retry -> ../init.d/udev_retry
lrwxrwxrwx  1 root root   17 Oct 12  2010 S70console -> ../init.d/console
lrwxrwxrwx  1 root root   18 Oct 12  2010 S80localnet -> ../init.d/localnet
lrwxrwxrwx  1 root root   16 Oct 12  2010 S90sysctl -> ../init.d/sysctl

You will notice that mountfs is also executed shortly after mountkernfs, to establish the rest of the file system. If we examine the contents of mountvirtfs from v330 SR16 there are several additional mitigation controls to prevent persistent access of /tmp:

...
Loop ()
{
        while [ "1" == "1" ]
        do
            let i=5
        done
}

case "${1}" in
   start)

      # make some sanity checks
      if [ ! -x /bin/mountpoint -o ! -x /bin/rm -o ! -x /sbin/shutdown ]
      then
           Loop
      fi

      # Make sure /run is available before logging any messages
      rm -rf /run
      mkdir /run
      if ! mountpoint /run >/dev/null; then
         mount /run || failed=1
      fi

      mkdir -p /run/lock /run/shm
      chmod 1777 /run/shm /run/lock

      log_info_msg "Mounting virtual file systems: ${INFO}/run" 

      if ! mountpoint /proc >/dev/null; then
         log_info_msg2 " ${INFO}/proc"
         mount -o nosuid,noexec,nodev /proc || failed=1
      fi

      if ! mountpoint /sys >/dev/null; then
         log_info_msg2 " ${INFO}/sys" 
         mount -o nosuid,noexec,nodev /sys || failed=1
      fi

      if ! mountpoint /tmp >/dev/null; then
         log_info_msg2 " ${INFO}/tmp" 
         mount /tmp || failed=1
      fi

      if ! mountpoint /dev >/dev/null; then
         log_info_msg2 " ${INFO}/dev"
    
...

Ok, there are a few things going on here. First, mountvirtfs will confirm if execute permissions have been removed from /bin/mountpoint or /sbin/shutdown and then block forever. That’s pretty cool, a mitigation control that introduces a DoS event - useful. This was added to prevent an attacker from disabling mountpoint, which is obviously used by mountvirtfs to validate the file system, and use of shutdown, the exit instruction used during sysinit to kill the bootup sequence.

This now brings us to /tmp; mountvirtfs checks if this mountpoint exists and, if not, mounts it. Since this is carried out as the first script during sysinit there is not much of an attack surface to circumvent this configuration. You may have noticed the rm -rf /run and mkdir /run instructions at the start of the script and thought to yourself, “This would be a clever way of removing mountpoint from this script”. You would not be wrong. However, the default state of the kernel mounts the rootfs as read-only and, thus, those instructions accomplish nothing.

During disclosure of this issue with Diebold Nixdorf, they found no risk to VSS and no patch was released. As a result, the rm instruction for /fastboot and /forcefsck continues to remain. Based on my research and in the context of SR16, this issue is of low severity and mostly useful for information reconnaissance.

My original research concluded with VSS v3.3.0 SR16 and Superschaf Version 7.2-72242. However, this rm instruction has also been observed in newer versions of CryptoPro. Below is an excerpt of mountfs from Superschaf Version 7.6-76204.

log_info_msg "Remounting root file system in read-write mode..."
mount --options remount,rw / >/dev/null
evaluate_retval

# Remove fsck-related file system watermarks.
rm -f /fastboot /forcefsck

# Make sure /dev/pts exists
mkdir -p /dev/pts

# This will mount all filesystems that do not have _netdev in
# their option list.  _netdev denotes a network filesystem.

log_info_msg "Mounting remaining file systems...\n"
mount --all --test-opts no_netdev >/dev/null
evaluate_retval

This vulnerability has been MITRE registered as CVE-2024-46916 with the following timeline:

  • July 31, 2024 - Vulnerability identification
  • August 02, 2024 - Vendor notification
  • August 26, 2024 - Vendor acknowledgement and acceptance
  • September 13, 2024 - MITRE registration

CVE-2024-46917

To provide background context on the second issue I will be discussing, I would like to return to VSS v3.3.0 SR12. In this release, the rm and mkdir instructions were added to remove /root, /var, /tmp, and /mnt from the system disk prior to the execution of the mount instructions:

# remove and re-create /root /var /tmp /mnt
rm -rf /root /var /tmp /mnt
mkdir /root /var /tmp /mnt

# Remove fsck-related file system watermarks.
rm -f /fastboot /forcefsck

The attack surface I publicly released against this configuration was based on logic within /etc/rc.d/rcsysinit.d/S00mountvirtfs, where kernel runtime resources were mapped into user space:

if ! mountpoint /proc >/dev/null; then
    log_info_msg2 " ${INFO}/proc"
    mount -o nosuid,noexec,nodev /proc || failed=1
fi

if ! mountpoint /sys >/dev/null; then
    log_info_msg2 " ${INFO}/sys" 
    mount -o nosuid,noexec,nodev /sys || failed=1
fi

if ! mountpoint /dev >/dev/null; then
    log_info_msg2 " ${INFO}/dev" 
    mount -o mode=0755,nosuid /dev  || failed=1
fi

if ! mountpoint /sys/kernel/security >/dev/null; then
    log_info_msg2 " ${INFO}/sys/kernel/security"
    mount -n /sys/kernel/security || failed=1
fi

In the SystemV initialization process, S00mountvirtfs was executed before S40mountfs:

$ ls -l rcsysinit.d 
total 0
lrwxrwxrwx 1 root root 21 Apr 26  2021 S00mountvirtfs -> ../init.d/mountvirtfs
lrwxrwxrwx 1 root root 13 Aug 13  2021 S01ima -> ../init.d/ima
lrwxrwxrwx 1 root root 17 Apr 26  2021 S05modules -> ../init.d/modules
lrwxrwxrwx 1 root root 18 Apr 26  2021 S08localnet -> ../init.d/localnet
lrwxrwxrwx 1 root root 14 Apr 26  2021 S10udev -> ../init.d/udev
lrwxrwxrwx 1 root root 14 Apr 26  2021 S20swap -> ../init.d/swap
lrwxrwxrwx 1 root root 17 Apr 26  2021 S30checkfs -> ../init.d/checkfs
lrwxrwxrwx 1 root root 17 Apr 26  2021 S40mountfs -> ../init.d/mountfs
lrwxrwxrwx 1 root root 12 Apr 27  2021 S41at -> ../init.d/at
lrwxrwxrwx 1 root root 17 Apr 26  2021 S45cleanfs -> ../init.d/cleanfs
lrwxrwxrwx 1 root root 20 Apr 26  2021 S50udev_retry -> ../init.d/udev_retry
lrwxrwxrwx 1 root root 17 Apr 26  2021 S70console -> ../init.d/console
lrwxrwxrwx 1 root root 16 Apr 26  2021 S90sysctl -> ../init.d/sysctl

This order of operations allowed for ../init.d/mountfs to be moved to a mount path in S00mountvirtfs, disabling the mitigation controls introduced in mountfs - once mountvirtfs was executed. However, this was not the only path of compromise, one could also set the immutability bit for the /root directory. Doing so would disable the ability for the rm -rf /root command to be carried out as the directory would be immutable.

root# ls -al root
drwx------  9 root root  4096 May 19 10:00 root

root# chattr +i root
root# rm -rf root
rm: cannot remove 'root/mount_fatstore.sh': Operation not permitted
rm: cannot remove 'root/.scim': Operation not permitted
rm: cannot remove 'root/startx.sh.sig': Operation not permitted
rm: cannot remove 'root/.xinitrc': Operation not permitted
rm: cannot remove 'root/.themes': Operation not permitted
...

This vulnerability has been MITRE registered as CVE-2024-46917 with the following timeline:

  • July 31, 2024 - Vulnerability identification
  • August 02, 2024 - Vendor notification
  • August 26, 2024 - Vendor acknowledgement and acceptance
  • September 13, 2024 - MITRE registration

Conclusion

To repeat the message I had discussed during my talk, it is a cat & mouse game - attempting to protect an unencrypted Linux partition from offline data manipulation. Even with static integrity validation, there will always be a bypass method if the full content of the disk is not validated and/or encrypted.

For additional background content please see the following references:

Next
Next

Uncovering Privilege Escalation Bugs in Lenovo Vantage