ram-wipe against RAM attacks

Hello there! This post will introduce some of the very popular attacks that target electronic devices - the RAM attacks, but the main topic will be the verification of ram-wipe software solution protection from the attacks.

The Kicksecure project engaged 3mdeb to evaluate the effectiveness of ram-wipe while it was still in testing. This research was supported by Power Up Privacy, a privacy advocacy group dedicated to empowering privacy-focused projects with the resources they need to fulfill their mission of making the world a better place.

Introduction to RAM attacks

Here, the focus is on two types of attacks: cold-boot and warm-boot attacks.

Warm-boot attacks focus on reading data left in RAM by the victim’s operating system or application using the attacker’s operating system or application without disconnecting power from the device. For example, rebooting the victim’s operating system via systemctl reboot (without breaking the victim’s system execution flow) and booting some malware to dump or search RAM. That is, the platform is not being moved into the G3 state according to ACPI specifications and the operating system code execution flow is not being broken.

On the other hand, the cold boot attack requires the platform to be cold booted, which appears when a platform loses power, that is, the G3 state according to ACPI documentation. Then, the device is booted again.

This attack can utilize the ability to break software execution flow by sudden power loss. In such a case, the device execution flow breaks, and the tasks that should be executed during a normal shutdown sequence are not being executed, and everything present in RAM before the shutdown stays there for a specific for the hardware period of time (that could be extended) and could be read.

During such a cold boot, an attacker boots malware or a specific operating system equipped with tools needed to read RAM and read it, gaining access to all data and secrets present in RAM before the shutdown. By doing so, the attacker can get access, among others, to keys that are used to encrypt certain secrets and file systems.

Below is a graphical representation of the attacks:

warm-vs-cold-boot-attcks-img

Legend:

  • Initial state: the platform has no power.
  • Black arrows: standard boot/power off process.
  • Black dashed arrow: getting the platform to power off state with breaking software execution flow.
  • Red dashed arrows: malware boot process.
  • 1: The platform executing firmware or bootloader code.
  • 2: Firmware or bootloader boots operating system or application that manipulates secrets in RAM.
  • 3: Malware in the form of an operating system or application that dumps secrets from RAM.

The only difference between the attacks is how the malware is being booted (check the red dashed arrows): from platform power off state with the previous system not properly shut down or without powering off the platform.

ram-wipe

ram-wipe is a full software solution designed by Kicksecure and published under AGPL-3+ license. The goal is to protect from RAM attacks by utilizing GNU/Linux tools. Currently, the only solution available on the market with the same goal is Tails Memory Erasure. The ram-wipe is available for testing and evaluation, more information can be found in its installation and design documentation.

Using the tree tool inside the ram-wipe repository shows that ram-wipe uses dracut and systemd:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
~/Repos/ram-wipe on tags/3.3-1 ● ● λ tree
(...)
├── README.md
└── usr
    ├── lib
    │   ├── dracut
    │   │   ├── dracut.conf.d
    │   │   │   └── 30-ram-wipe.conf
    │   │   └── modules.d
    │   │       ├── 10ram-wipe-exit
    │   │       │   ├── module-setup.sh
    │   │       │   ├── wipe-ram-exit-needshutdown.sh
    │   │       │   └── wipe-ram-exit.sh
    │   │       └── 40cold-boot-attack-defense
    │   │           ├── module-setup.sh
    │   │           ├── wipe-ram-needshutdown.sh
    │   │           └── wipe-ram.sh
    │   ├── systemd
    │   │   └── system
    │   │       └── ram-wipe-kexec-prepare.service
    │   └── tmpfiles.d
    │       └── ram-wipe.conf
    ├── libexec
    │   └── ram-wipe
    │       ├── cold-boot-attack-defense-kexec-prepare
    │       ├── cold-boot-attack-defense-status
    │       └── ram-wipe-lib.sh
    ├── sbin
    │   └── wipe-ram-shutdown-helper
    └── share
        ├── lintian
        │   └── overrides
        │       └── ram-wipe
        └── ram-wipe
            └── placeholder

And file tool shows that all executables are shell scripts:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
~/Repos/ram-wipe on tags/3.3-1 ● ● λ file $(find ./usr/ -type f)
./usr/lib/dracut/modules.d/10ram-wipe-exit/module-setup.sh:                     Bourne-Again shell script, ASCII text executable
./usr/lib/dracut/modules.d/10ram-wipe-exit/wipe-ram-exit-needshutdown.sh:       POSIX shell script, ASCII text executable
./usr/lib/dracut/modules.d/10ram-wipe-exit/wipe-ram-exit.sh:                    POSIX shell script, ASCII text executable
./usr/lib/dracut/modules.d/40cold-boot-attack-defense/module-setup.sh:          Bourne-Again shell script, ASCII text executable
./usr/lib/dracut/modules.d/40cold-boot-attack-defense/wipe-ram-needshutdown.sh: POSIX shell script, ASCII text executable
./usr/lib/dracut/modules.d/40cold-boot-attack-defense/wipe-ram.sh:              POSIX shell script, ASCII text executable
./usr/lib/dracut/dracut.conf.d/30-ram-wipe.conf:                                ASCII text
./usr/lib/systemd/system/ram-wipe-kexec-prepare.service:                        ASCII text
./usr/lib/tmpfiles.d/ram-wipe.conf:                                             ASCII text
./usr/libexec/ram-wipe/cold-boot-attack-defense-status:                         Bourne-Again shell script, ASCII text executable
./usr/libexec/ram-wipe/ram-wipe-lib.sh:                                         POSIX shell script, ASCII text executable
./usr/libexec/ram-wipe/cold-boot-attack-defense-kexec-prepare:                  Bourne-Again shell script, ASCII text executable
./usr/sbin/wipe-ram-shutdown-helper:                                            Bourne-Again shell script, ASCII text executable
./usr/share/lintian/overrides/ram-wipe:                                         ASCII text
./usr/share/ram-wipe/placeholder:                                               ASCII text

Though shell scripts are easy to analyze, the most complicated thing here is to put all variables dependencies from the scripts and services that are being executed in different environments: in Linux during shutdown (systemd, service), inside RAM disk during shutdown, and inside RAM disk after booting second Linux kernel using kexec. Because of the complexity, it is better to demonstrate its structure via diagrams.

The first diagram demonstrates the system boot process:

ram-wipe-flowchart-1

The only thing to note here is the way ram-wipe decides whether to run or not during reboot/poweroff/halt: it checks Linux kernel command line parameter wiperam and then creates a file that tells dracut services to fall back to RAM disk during reboot/poweroff/halt, where the first ram-wipe stage will run.

Then, the system mounts rootfs and switches to userspace:

ram-wipe-flowchart-2

Here, ram-wipe relies on the ram-wipe-kexec-prepare.service for setting up the second stage and on the dracut-shutdown.service for getting back to the RAM disk and launching the first ram-wipe stage.

After that, the system switches back to the RAM disc:

ram-wipe-flowchart-3

Now the ram-wipe first wipe stage is being launched. The wiperam and wiperamexit are Linux kernel command line boot parameters used to skip this stage (the former one) or prevent the ram-wipe loop (the latter one).

Then, in case the second stage is enabled, kexec is being run during execution of the first stage that results in booting to the second kernel:

Note: The first wipe stage before loading Linux kernel via kexec checks whether secure boot is enabled by mokutil tool, so it is possible to run it on platforms with Secure Boot protection enabled.

ram-wipe-flowchart-4

This phase wipes the RAM again, but this time during booting the second kernel when the RAM disk is being mounted first time. After wiping is done - the system is forced to reboot/powerof/halt (depending on the wiperamaction Linux kernel command line parameter) without complete boot.

Now, when the workflow is clear, it is important to note two things:

  1. Both ram-wipe stages rely on the sdmem tool, which is being called twice with the same arguments -l -l -v, resulting in RAM being overwritten with zeroes (-l -l means only one sdmem walk filling up RAM with zeroes, -v is for verbose). This fact will be used later for testing.
  2. ram-wipe fully relies on Linux tools and systemd services execution workflow. This fact will be very important during testing and writing down conclusions.

Testing methodology and tools

As ram-wipe is based on setting RAM to zeroes during Linux shutdown or reboot workflows, to check whether it succeeded or not, the RAM should be checked after Linux has finished executing. The main problem here is the way the RAM will be checked. There are two ways: checking the RAM externally, that is, by software that will not use the wiped RAM for execution (executing software in another RAM or chip), or by making sure that the software load address and size will be known, so to be able to conclude what has been overwritten and why some addresses are not zeroed.

This post will use the second way by executing an EFI application during the early device boot stage. The problem with some EFI structures (e.g., logs) being randomly loaded into RAM was solved by checking where those structures are being written before every test. During checking, the application records the addresses in memory modified by firmware and excludes it from dumping.

The application can be found here. It is a simple application that runs automatically during boot and dumps specified RAM range values into files. The application was written as minimalistic as possible to prevent it from modifying RAM.

The final testing methodology using the application is as follows:

  1. Boot to the application and choose option 1 to write the pattern.
  2. Reboot to the application and choose option 2 to exclude RAM modified by firmware.
  3. Reboot to OS and run ram-wipe.
  4. Boot to the application and dump memory.

It is important to note that the booting to the OS was set as automatic by changing the Dasharo firmware boot order. But the application was booted by choosing the needed drive in the One Time Boot menu. When booting the application by manually choosing the hard drive, it is important to take exactly the same steps every time. Otherwise, the firmware will generate another set of data in RAM and make the second step from above useless.

After RAM has been dumped, it is being analyzed by a Python script that checks what addresses were overwritten by zeroes and what addresses still contain any values. Though this script shows what addresses contain zeroes and what do not, the dumped RAM will still be analyzed manually in depth to check what addresses were not wiped and why. To analyze results, further the addresses used by kernels and RAM disks during both stages could be checked via dumping /proc/iomem and analyzing dmesg logs. This will give extra information why some addresses were not zeroed because ram-wipe as a software solution cannot overwrite kernel address space as well as some other processes address space.

The tests will be done for one warm boot attack via rebooting Linux and for one cold boot attack utilizing QEMU Monitor system_reset functionality.

Testing environment

The hardware requirements are rather small: the ability to run EFI applications and as little RAM as possible to reduce the time needed to dump RAM. Because the ram-wipe is a software solution and, therefore, does not depend on hardware (except for the fact that more RAM will take more time to wipe) - the QEMU with Dasharo Firmware is being used as a test platform.

While there is no difference between the real platform and QEMU for warm boot attacks, the real cold boot attack cannot be done on QEMU because, QEMU is software, and it cannot not be reset via power loss. Hence, the QEMU RAM cells will not lose their charge as on the real platform. In this case, the cold boot attack could be decomposed into two parts: the software execution flow brake and platform power loss.

The consequence of the software execution flow brake is that everything that was going to be executed will not execute. For example, if somebody is watching a film - the film will immediately stop, if an application wants to wipe its passwords from RAM - the password will not be wiped, etc. This is important because, as was described above, ram-wipe is a full software solution and relies on Linux kernel and systemd services execution during reboot/poweroff/halt. As was described previously it is the key difference between warm boot and cold boot attacks.

The consequence of the power loss is that all data that was stored in volatile memory is going to be lost because of the fact, that volatile memory relies on storing its data in cells built up from transistors and capacitors. The loss means that after platform loses its power and then booted up again, the volatile memory holds random data instead of what was written to it before the power loss. It is important to note that it takes some time for the capacitors to lose their charge, so the data could be acquired from the volatile memory during a short period of time after power loss.

The fact that data loss after platform power loss actually prevents the attacker from getting all the data from RAM. Sometimes, the regions that were used to store secrets will lose their charge faster than other regions, sometimes not. But security should not rely on random events, should it? If you want to get more information on why the memory loses its data after power loss and how long it takes, you should check the series of our blog posts:

Because the data loss after platform power loss makes it harder to get data during a cold boot attack, it will be easier to test protection from cold boot attacks on systems where software execution flow could be broken without losing power, so to eliminate the randomness of data loss and dump all data from RAM, that will prove, whether the RAM was wiped by ram-wipe or not. Hence, QEMU might come in handy here because it provides a system_reset feature that brakes software execution without reallocating memory for QEMU.

As an operating system, the Debian Trixie testing release with encrypted rootfs was used. To install and reproduce the results, follow the following steps:

  1. Install Debian Trixie following instructions from the Debian Testing article with rootfs and swap protected by LUKS.

  2. Migrate to dracut (the dracut version in this set-up is 106-6, the systemd version is 257.5-2):

    1
    2
    
    sudo apt install dracut
    sudo dracut -f
    

    Note: If you want to do tests with LUKS on Debian Trixie, it is important to do the fifth step from Kicksecire’s dracut migration Wiki page and then run sudo dracut -f again. Otherwise, you might end up with LUKS unlock prompt not showing up during boot.

  3. Verify that dracut is being used by checking system logs, e.g.:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    
    $ sudo dmesg | grep dracut
    (...)
    [  OK  ] Finished dracut-initqueue.service - dracut initqueue hook.
    (...)
           Starting dracut-pre-pivot.service - dracut pre-pivot and cleanup hook...
    [  OK  ] Finished dracut-pre-pivot.service - dracut pre-pivot and cleanup hook.
    (...)
    [  OK  ] Stopped dracut-pre-pivot.service - dracut pre-pivot and cleanup hook.
    (...)
    
  4. Compile and install ram-wipe package and its dependency - helper-scripts:

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    
    $ sudo apt install build-essential debhelper debhelper-compat dh-python\
    dh-apparmor config-package-dev
    $ git clone https://github.com/Kicksecure/helper-scripts.git
    $ cd helper-scripts
    $ git checkout 37a9ef2e84e352e37b7e84bd8ed36e5d31218778
    $ dpkg-buildpackage -b
    $ cd -
    $ sudo apt install ./helper-scripts_31.3-1_all.deb
    $ git clone https://github.com/Kicksecure/ram-wipe.git
    $ cd ram-wipe
    $ git checkout ed9c2978b2c69cebd37b3e1412b648d8f00584e4
    $ dpkg-buildpackage -b
    $ cd -
    $ sudo apt install ./ram-wipe_3.3-1_all.deb
    $ sudo dracut -f
    

    Note: Do not mind the failed to sign issues. These will appear if the GPG key is not configured properly and is not important here.

  5. Verify that ram-wipe is being launched during reboot or shutdown by checking system logs. For the first stage, check for the following logs:

    1
    2
    3
    4
    5
    6
    7
    
    (...)
    [   66.353653] wipe-ram-shutdo (4396): drop_caches: 3
    [   66.359901] dracut INFO: wipe-ram.sh: First RAM wipe pass completed, OK. (1/2)
    [   66.368166] dracut INFO: wipe-ram.sh: Checking if there are still mounted encrypted disks...
    [   66.379157] dracut INFO: wipe-ram.sh: Success, there are no more mounted encrypted disks, OK.
    [   66.388631] dracut INFO: wipe-ram.sh: Now running 'kexec --exec'..
    (...)
    

    The second stage has issues regarding printing, so its logs might not be visible on serial or graphical output. But the OOM Killer logs caused by ram-wipe and sdmem should be visible.

The test setup using the following keys for LUKS:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
$ cryptsetup luksDump --dump-master-key /dev/sda3

WARNING!
========
The header dump with volume key is sensitive information
that allows access to encrypted partition without a passphrase.
This dump should be stored encrypted in a safe place.

Are you sure? (Type 'yes' in capital letters): YES
Enter passphrase for /dev/sda3:
LUKS header information for /dev/sda3
Cipher name:    aes
Cipher mode:    xts-plain64
Payload offset: 32768
UUID:           b414ff1d-6f9e-498b-a56f-fc0890738800
MK bits:        512
MK dump: fb d6 ea 69 49 6a 02 f6 af 8b d0 e8 89 37 22 14
  a5 ad 90 5e 62 23 7a 45 ab 97 19 b7 10 8e 8e e9
  15 98 a0 16 9d aa 9a 0b 35 ef c5 77 46 23 d0 7a
  73 62 ef 77 be 66 d5 a4 2b 39 6f 1b c7 7e 01 d2

Important note: this security information leak was done on purpose, do not share your secrets with anyone!

ram-wipe tests

Running first stage only

To make ram-wipe run only the first stage on reboot, the Linux kernel command line parameter wiperamexit was set to no. This resulted in kexec failure because of the ram-wipe-kexec-prepare.service not starting. Hence, the second Linux kernel was not booted, and the second ram-wipe stage was not running:

1
2
3
4
5
6
7
8
[   66.267493] dracut INFO: wipe-ram.sh: Now running 'kexec --exec'...
dracut INFO: wipe-ram.sh: Success, there are no more mounted encrypted disks, OK.
dracut INFO: wipe-ram.sh: Now running 'kexec --exec'...
Nothing has been loaded!
[   66.270216] dracut INFO: wipe-ram.sh: 'kexec --exec' failed!
dracut INFO: wipe-ram.sh: 'kexec --exec' failed!
Rebooting.
[   71.285689] reboot: Restarting system

The bootloader (GRUB in this case) and Linux kernel got the following memory addresses from firmware (inf. from the EFI application):

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
Available RAM [            1000 -            86FFF]
Available RAM [           88000 -            9FFFF]
Available RAM [          100000 -           7FFFFF]
Available RAM [         1600000 -         7BD1FFFF]
Available RAM [        7BD40000 -         7D971FFF]
Available RAM [        7D986000 -         7D995FFF]
Available RAM [        7D9A1000 -         7D9A8FFF]
Available RAM [        7F5EE000 -         7F5F4FFF]
Available RAM [        7FC00000 -         7FC6FFFF]
Available RAM [        7FD20000 -         7FD2CFFF]
Available RAM [       100000000 -        17FFFFFFF]
Found 1035148 pages of available RAM (4043 MB)

With the following pages and addresses excluded as used by firmware (inf. from the EFI application):

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
Exclude modified by firmware was selected

...   0%
Excluding range @ 0x10000, 119 pages

Excluding range @ 0x88000, 24 pages

Excluding range @ 0x7BD40000, 8 pages

...  49%
Excluding range @ 0x7D922000, 80 pages

Excluding range @ 0x7D986000, 16 pages

Excluding range @ 0x7D9A1000, 8 pages

Excluding range @ 0x7F5F4000, 1 pages

Exclude modified by firmware done

The memory provided by firmware to bootloader and Linux was the same for both attacks.

Warm boot attack on first stage

The /proc/iomem showed the Linux kernel loaded in the following addresses:

1
2
3
4
5
6
7
(...)
100000000-17fffffff : System RAM
  169600000-16a5fffff : Kernel code
  16a600000-16b17cfff : Kernel rodata
  16b200000-16b46d47f : Kernel data
  16bd27000-16c1fffff : Kernel bss
180000000-97fffffff : PCI Bus 0000:00

Hence, these ranges should be excluded from the analysis because sdmem, which is used in ram-wipe, is an application and cannot overwrite kernel space addresses. Analysis of other addresses:

  • 0x1000-0x10000: not zeroed, but hexdump showed that the only data in this region is the pattern written by the EFI application. Hence, this region was not used by Linux or bootloader.
  • 0x100000-0x800000: 524288 bytes zeroed and 6815744 non-zeroed. All non-zeroed bytes contain the pattern written by the EFI application. Hence, these bytes were not used by Linux or bootloader.
  • 0x1600000-0x75ff0000: 2026242048 zeroed and 28049408 non-zeroed. Most of the non-zeroed addresses contain some data. Other addresses contain the pattern.
  • 0x7bd48000-0x7d922000: 26517504 bytes zeroed and 2686976 non-zeroed. Non-zeroed bytes contain the GRUB executable and its modules.
  • 0x7f5ee000-0x7f5f4000: zeroed.
  • 0x7fc00000-0x7fc70000: zeroed.
  • 0x7fd20000-0x7fd2d000: zeroed.
  • 0x100000000-0x180000000: this region is very fragmented in terms of zeroed and non-zeroed addresses. 1607467008 bytes was zeroed, and 540016640 was not zeroed. With 36451452 bytes taken by the kernel and 503565188 used by processes.

After raw analysis, it is high time for some real attack. The attack will be done by searching the dumped memory for LUKS keys using aeskeyfind, and the results are:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
$ while read -r file; do
echo -e "Checking file: $file"
aeskeyfind -v $file
done < <(ls ./*.csv)
Checking file: ./2025_05_02_14_49_0x0000000000001000.csv
Keyfind progress: 100%
Checking file: ./2025_05_02_14_49_0x0000000000100000.csv
Keyfind progress: 100%
Checking file: ./2025_05_02_14_49_0x0000000001600000.csv
Keyfind progress: 100%
Checking file: ./2025_05_02_14_49_0x000000007BD48000.csv
Keyfind progress: 100%
Checking file: ./2025_05_02_14_49_0x000000007F5EE000.csv
Keyfind progress: 100%
Checking file: ./2025_05_02_14_49_0x000000007FC00000.csv
Keyfind progress: 100%
Checking file: ./2025_05_02_14_49_0x000000007FD20000.csv
Keyfind progress: 100%
Checking file: ./2025_05_02_14_49_0x0000000100000000.csv
Keyfind progress: 100%

So indeed, the first ram-wipe stage saved the keys.

Cold boot attack on first stage

The /proc/iomem showed the first Linux kernel loaded in the following addresses:

1
2
3
4
5
6
7
(...)
100000000-17fffffff : System RAM
  11d800000-11e7fffff : Kernel code
  11e800000-11f37cfff : Kernel rodata
  11f400000-11f66d47f : Kernel data
  11ff27000-1203fffff : Kernel bss
180000000-97fffffff : PCI Bus 0000:00

Hence, these ranges should be excluded from analysis because sdmem used in ram-wipe is an application and cannot overwrite kernel space addresses. Analysis of other addresses:

  • 0x1000-0x10000: not zeroed, but hexdump showed that the only data in this region is the pattern written by the EFI application. Hence, this region was not used by Linux or bootloader.
  • 0x100000-0x800000: 524288 bytes zeroed and 6815744 not zeroed. All non-zeroed bytes contain the pattern written by the EFI application. Hence, these bytes were not used by Linux or bootloader.
  • 0x1600000-0x75ff0000: 71892992 bytes zeroed and 1982398464 bytes non-zeroed. Most of the non-zeroed addresses contain mostly GRUB data. Other addresses contain the pattern.
  • 0x7bd48000-0x7d922000: not zeroed, contains process data.
  • 0x7f5ee000-0x7f5f4000: not zeroed, contains process data.
  • 0x7fc00000-0x7fc70000: not zeroed, contains process data.
  • 0x7fd20000-0x7fd2d000: not zeroed, contains process data.
  • 0x100000000-0x180000000: this region is very fragmented in terms of zeroed and not zeroed addresses. 328466432 bytes was zeroed and 1819017216 was not zeroed.

Now, let’s do the attack on LUKS keys:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
$ while read -r file; do
echo -e "Checking file: $file"
aeskeyfind -v $file
done < <(ls ./*.csv)
Checking file: ./2025_05_02_14_54_0x0000000000001000.csv
Keyfind progress: 100%
Checking file: ./2025_05_02_14_54_0x0000000000100000.csv
Keyfind progress: 100%
Checking file: ./2025_05_02_14_55_0x0000000001600000.csv
Keyfind progress: 100%
Checking file: ./2025_05_02_14_55_0x000000007BD48000.csv
Keyfind progress: 100%
Checking file: ./2025_05_02_14_55_0x000000007F5EE000.csv
Keyfind progress: 100%
Checking file: ./2025_05_02_14_55_0x000000007FC00000.csv
Keyfind progress: 100%
Checking file: ./2025_05_02_14_55_0x000000007FD20000.csv
Keyfind progress: 100%
Checking file: ./2025_05_02_14_55_0x0000000100000000.csv
FOUND POSSIBLE 256-BIT KEY AT BYTE 2854c20

KEY: 1598a0169daa9a0b35efc5774623d07a7362ef77be66d5a42b396f1bc77e01d2

EXTENDED KEY:
1598a0169daa9a0b35efc5774623d07a
7362ef77be66d5a42b396f1bc77e01d2
e7e415d07a4e8fdb4fa14aac09829ad6
72715781cc178225e72eed3e2050ecec
b62adb67cc6454bc83c51e108a4784c6
0cd10835c0c68a1027e8672e07b88bc2
de17fea21273aa1e91b6b40e1bf130c8
a3700cdd63b686cd445ee1e343e66a21
581503b84a66a9a6dbd01da8c0212d60
198dd40d7a3b52c03e65b3237d83d902
a4207447ee46dde13596c049f5b7ed29
ff2481a8851fd368bb7a604bc6f9b949
1d764ff3f3309212c6a6525b3311bf72
3ca689e8b9b95a8002c33acbc43a8382
dd9a5cef2eaacefde80c9ca6db1d23d4

CONSTRAINTS ON ROWS:
0000000000000000000000000000000000000000000000000000000000000000
0000000000000000000000000000000000000000000000000000000000000000
0000000000000000000000000000000000000000000000000000000000000000
0000000000000000000000000000000000000000000000000000000000000000
0000000000000000000000000000000000000000000000000000000000000000
0000000000000000000000000000000000000000000000000000000000000000
00000000000000000000000000000000
6a05df4cf5c301a1707d1029697f6b83daa7acc71f33fff01806cd27108ae7ca
eac1106501e9d83993b9cb1a48d0044638ce1f1d0f644d46ad134a239c8a7b2e
0c56163cb24a642392501323db69cf5cf5b605e210cb1983a27707653199310d
94288153a6025ab1201a77004939dc7fcdfa9e2434d1890fb2bc1ee693ee3668
e99a93c79a7ceeea86182db16923ab7f7c4f167f87aabb23866d97e92152288e
ff99f82fdb9390ed1c64c35bef3b86ceca92909aa9a6e10801c72ccaa73fbf67
7aac22897d6d7c30c7f753b6f35f4595d97b662d2ecaa9b07c559e71a890c890

FOUND POSSIBLE 256-BIT KEY AT BYTE 2857c20

KEY: fbd6ea69496a02f6af8bd0e889372214a5ad905e62237a45ab9719b7108e8ee9

EXTENDED KEY:
fbd6ea69496a02f6af8bd0e889372214
a5ad905e62237a45ab9719b7108e8ee9
e3cff4a3aaa5f655052e26bd8c1904a9
c179628da35a18c808cd017f18438f96
fbbc640e5119925b5437b4e6d82eb04f
a048850903129dc10bdf9cbe139c1328
21c1507370d8c22824ef76cefcc1c681
103031051322acc418fd307a0b612352
c6e75058b63f927092d0e4be6e11223f
8fb2a2709c900eb4846d3ece8f0c1d9c
28438e2b9e7c1c5b0cacf8e562bddada
25c8f527b958fb933d35c55db239d8c1
1a22f61c845eea4788f212a2ea4fc878
a24c1d9b1b14e608262123559418fb94
f72dd43e73733e79fb812cdb11cee4a3

CONSTRAINTS ON ROWS:
0000000000000000000000000000000000000000000000000000000000000000
0000000000000000000000000000000000000000000000000000000000000000
0000000000000000000000000000000000000000000000000000000000000000
0000000000000000000000000000000000000000000000000000000000000000
0000000000000000000000000000000000000000000000000000000000000000
0000000000000000000000000000000000000000000000000000000000000000
00000000000000000000000000000000
56de1b21addaab1267887381738dd32766ea6ffaf6a968d5739831ea7d4795e8
0621c9eaff653ef3aa9ac59d743350f6f1f6b3dd5bec2982ef71bcf06aabea5c
533d3758c29e840855fffb6edea9956b9f6984a024bfc1bdb49d957285da56ac
fb63cc40f733162997617f668b566e05512226b834a51c67902254cf3147c3de
c3218667f6ed12796052694f1c37116358ac834ec530ecf7a48748a8a1659711
faadf6045fd4bf0796bf7b367c65782ce8ea413d082b2c5961b7a45f05e2dfb9
4c16e99d04f1abcfc96bc431eada031a93fc9107d6e993797ee212f56e8a420a

Keyfind progress: 100%

Both keys were successfully stolen after cold boot! Hence, running only the first ram-wipe stage does not protect from cold boot attack.

Running second stage only

To work around running only the second ram-wipe stage on reboot, the following modification was used:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
~/Repos/ram-wipe on tags/3.3-1 ● λ git diff
diff --git a/usr/lib/dracut/modules.d/40cold-boot-attack-defense/wipe-ram.sh b/usr/lib/dracut/modules.d/40cold-boot-attack-defense/wipe-ram.sh
index e61618184300..95c85c72efe2 100755
--- a/usr/lib/dracut/modules.d/40cold-boot-attack-defense/wipe-ram.sh
+++ b/usr/lib/dracut/modules.d/40cold-boot-attack-defense/wipe-ram.sh
@@ -28,7 +28,7 @@ ram_wipe() {

    force_echo "wipe-ram.sh: Cold boot attack defense... Starting first RAM wipe pass on shutdown... (1/2)"

-   wipe-ram-shutdown-helper
+   #wipe-ram-shutdown-helper

    force_echo "wipe-ram.sh: First RAM wipe pass completed, OK. (1/2)"

Dasharo firmware proved to be pretty stable in its memory allocation during boot by providing the same memory map:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
Available RAM [            1000 -            86FFF]
Available RAM [           88000 -            9FFFF]
Available RAM [          100000 -           7FFFFF]
Available RAM [         1600000 -         7BD1FFFF]
Available RAM [        7BD40000 -         7D971FFF]
Available RAM [        7D986000 -         7D995FFF]
Available RAM [        7D9A1000 -         7D9A8FFF]
Available RAM [        7F5EE000 -         7F5F4FFF]
Available RAM [        7FC00000 -         7FC6FFFF]
Available RAM [        7FD20000 -         7FD2CFFF]
Available RAM [       100000000 -        17FFFFFFF]
Found 1035148 pages of available RAM (4043 MB)


Choose the mode:
1. Pattern write
2. Exclude modified by firmware
3. Dump

Exclude modified by firmware was selected

...   0%
Excluding range @ 0x10000, 119 pages

Excluding range @ 0x88000, 24 pages

...   1%
...
...  48%
Excluding range @ 0x7BD40000, 8 pages

...  49%
Excluding range @ 0x7D922000, 80 pages

Excluding range @ 0x7D986000, 16 pages

Excluding range @ 0x7D9A1000, 8 pages

Excluding range @ 0x7F5F4000, 1 pages

...  50%
...
... 100%
Exclude modified by firmware done

The memory provided by firmware to bootloader and Linux was the same for both attacks.

Warm boot attack on second stage

This time /proc/iomem shows the Linux kernel is hidden under the following addresses:

1
2
3
4
5
6
7
(...)
100000000-17fffffff : System RAM
  15f000000-15fffffff : Kernel code
  160000000-160b7cfff : Kernel rodata
  160c00000-160e6d47f : Kernel data
  161727000-161bfffff : Kernel bss
180000000-97fffffff : PCI Bus 0000:00

And the kernel loaded by kexec before the second ram-wipe stage execution:

1
2
3
4
5
6
7
(...)
100000000-17fffa24f : System RAM
  17c800000-17d7fffff : Kernel code
  17d800000-17e37cfff : Kernel rodata
  17e400000-17e66d47f : Kernel data
  17ef27000-17f3fffff : Kernel bss
17fffa250-17fffa2cf : System RAM

Analysis of other addresses:

  • 0x1000-0x10000: not zeroed, but hexdump showed that the only data in this region is the pattern written by the EFI application. Hence, this region was not used by Linux or bootloader.
  • 0x100000-0x800000: 524288 bytes zeroed and 6815744 not zeroed. All non-zeroed bytes contain the pattern written by the EFI application. Hence, these bytes were not used by Linux or bootloader.
  • 0x1600000-0x7bd20000: 2047803392 zeroed and 6488064 non-zeroed. Some of not zeroed addresses contain some data, others contain the pattern.
  • 0x7bd48000-0x7d922000: zeroed.
  • 0x7f5ee000-0x7f5f4000: zeroed.
  • 0x7fc00000-0x7fc70000: zeroed.
  • 0x7fd20000-0x7fd2d000: zeroed.
  • 0x100000000-0x180000000: this region is very fragmented in terms of zeroed and non-zeroed addresses. 1875509248 bytes was zeroed and 271974400 was not zeroed. The situation is the same ass before: a part of the non-zeroed addresses was used by the kernel and another part by processes.

It is about time for the same attack as before:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
$ while read -r file; do
echo -e "Checking file: $file"
aeskeyfind -v $file
done < <(ls ./*.csv)
Checking file: ./2025_05_02_15_17_0x0000000000001000.csv
Keyfind progress: 100%
Checking file: ./2025_05_02_15_17_0x0000000000100000.csv
Keyfind progress: 100%
Checking file: ./2025_05_02_15_17_0x0000000001600000.csv
Keyfind progress: 100%
Checking file: ./2025_05_02_15_18_0x000000007BD48000.csv
Keyfind progress: 100%
Checking file: ./2025_05_02_15_18_0x000000007F5EE000.csv
Keyfind progress: 100%
Checking file: ./2025_05_02_15_18_0x000000007FC00000.csv
Keyfind progress: 100%
Checking file: ./2025_05_02_15_18_0x000000007FD20000.csv
Keyfind progress: 100%
Checking file: ./2025_05_02_15_18_0x0000000100000000.csv
Keyfind progress: 100%

And again, no keys dumped.

Cold boot attack on second stage

This time /proc/iomem shows the Linux kernel is hidden under the following addresses:

1
2
3
4
5
6
7
(...)
00100000-7d9ddfff : System RAM
  52000000-52ffffff : Kernel code
  53000000-53b7cfff : Kernel rodata
  53c00000-53e6d47f : Kernel data
  54727000-54bfffff : Kernel bss
7d9de000-7da65fff : Reserved

Analysis of other addresses:

  • 0x1000-0x10000: not zeroed, but hexdump showed that the only data in this region is the pattern written by the EFI application. Hence, this region was not used by Linux or bootloader.
  • 0x100000-0x800000: 524288 bytes zeroed and 6815744 not zeroed. All non-zeroed bytes contain the pattern written by the EFI application. Hence, these bytes were not used by Linux or bootloader.
  • 0x1600000-0x7bd20000: 84869120 zeroed and 1969422336 not zeroed. Some of non-zeroed addresses contain some data, others contain the pattern.
  • 0x7bd48000-0x7d922000: not zeroed, contains data.
  • 0x7f5ee000-0x7f5f4000: not zeroed, contains data.
  • 0x7fc00000-0x7fc70000: not zeroed, contains data.
  • 0x7fd20000-0x7fd2d000: not zeroed, contains data.
  • 0x100000000-0x180000000: this region is very fragmented in terms of zeroed and non-zeroed addresses. 350945280 bytes was zeroed and 1796538368 was not zeroed. Still a lot of processes data not zeroed.

The attack on LUKS is still successful:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
$ while read -r file; do
echo -e "Checking file: $file"
aeskeyfind -v $file
done < <(ls ./*.csv)
Checking file: ./2025_05_02_15_26_0x0000000000001000.csv
Keyfind progress: 100%
Checking file: ./2025_05_02_15_26_0x0000000000100000.csv
Keyfind progress: 100%
Checking file: ./2025_05_02_15_26_0x0000000001600000.csv
Keyfind progress: 100%
Checking file: ./2025_05_02_15_27_0x000000007BD48000.csv
Keyfind progress: 100%
Checking file: ./2025_05_02_15_27_0x000000007F5EE000.csv
Keyfind progress: 100%
Checking file: ./2025_05_02_15_27_0x000000007FC00000.csv
Keyfind progress: 100%
Checking file: ./2025_05_02_15_27_0x000000007FD20000.csv
Keyfind progress: 100%
Checking file: ./2025_05_02_15_27_0x0000000100000000.csv
FOUND POSSIBLE 256-BIT KEY AT BYTE 7aae420

KEY: fbd6ea69496a02f6af8bd0e889372214a5ad905e62237a45ab9719b7108e8ee9

EXTENDED KEY:
fbd6ea69496a02f6af8bd0e889372214
a5ad905e62237a45ab9719b7108e8ee9
e3cff4a3aaa5f655052e26bd8c1904a9
c179628da35a18c808cd017f18438f96
fbbc640e5119925b5437b4e6d82eb04f
a048850903129dc10bdf9cbe139c1328
21c1507370d8c22824ef76cefcc1c681
103031051322acc418fd307a0b612352
c6e75058b63f927092d0e4be6e11223f
8fb2a2709c900eb4846d3ece8f0c1d9c
28438e2b9e7c1c5b0cacf8e562bddada
25c8f527b958fb933d35c55db239d8c1
1a22f61c845eea4788f212a2ea4fc878
a24c1d9b1b14e608262123559418fb94
f72dd43e73733e79fb812cdb11cee4a3

CONSTRAINTS ON ROWS:
0000000000000000000000000000000000000000000000000000000000000000
0000000000000000000000000000000000000000000000000000000000000000
0000000000000000000000000000000000000000000000000000000000000000
0000000000000000000000000000000000000000000000000000000000000000
0000000000000000000000000000000000000000000000000000000000000000
0000000000000000000000000000000000000000000000000000000000000000
00000000000000000000000000000000
56de1b21addaab1267887381738dd32766ea6ffaf6a968d5739831ea7d4795e8
0621c9eaff653ef3aa9ac59d743350f6f1f6b3dd5bec2982ef71bcf06aabea5c
533d3758c29e840855fffb6edea9956b9f6984a024bfc1bdb49d957285da56ac
fb63cc40f733162997617f668b566e05512226b834a51c67902254cf3147c3de
c3218667f6ed12796052694f1c37116358ac834ec530ecf7a48748a8a1659711
faadf6045fd4bf0796bf7b367c65782ce8ea413d082b2c5961b7a45f05e2dfb9
4c16e99d04f1abcfc96bc431eada031a93fc9107d6e993797ee212f56e8a420a

FOUND POSSIBLE 256-BIT KEY AT BYTE 7aaf020

KEY: 1598a0169daa9a0b35efc5774623d07a7362ef77be66d5a42b396f1bc77e01d2

EXTENDED KEY:
1598a0169daa9a0b35efc5774623d07a
7362ef77be66d5a42b396f1bc77e01d2
e7e415d07a4e8fdb4fa14aac09829ad6
72715781cc178225e72eed3e2050ecec
b62adb67cc6454bc83c51e108a4784c6
0cd10835c0c68a1027e8672e07b88bc2
de17fea21273aa1e91b6b40e1bf130c8
a3700cdd63b686cd445ee1e343e66a21
581503b84a66a9a6dbd01da8c0212d60
198dd40d7a3b52c03e65b3237d83d902
a4207447ee46dde13596c049f5b7ed29
ff2481a8851fd368bb7a604bc6f9b949
1d764ff3f3309212c6a6525b3311bf72
3ca689e8b9b95a8002c33acbc43a8382
dd9a5cef2eaacefde80c9ca6db1d23d4

CONSTRAINTS ON ROWS:
0000000000000000000000000000000000000000000000000000000000000000
0000000000000000000000000000000000000000000000000000000000000000
0000000000000000000000000000000000000000000000000000000000000000
0000000000000000000000000000000000000000000000000000000000000000
0000000000000000000000000000000000000000000000000000000000000000
0000000000000000000000000000000000000000000000000000000000000000
00000000000000000000000000000000
6a05df4cf5c301a1707d1029697f6b83daa7acc71f33fff01806cd27108ae7ca
eac1106501e9d83993b9cb1a48d0044638ce1f1d0f644d46ad134a239c8a7b2e
0c56163cb24a642392501323db69cf5cf5b605e210cb1983a27707653199310d
94288153a6025ab1201a77004939dc7fcdfa9e2434d1890fb2bc1ee693ee3668
e99a93c79a7ceeea86182db16923ab7f7c4f167f87aabb23866d97e92152288e
ff99f82fdb9390ed1c64c35bef3b86ceca92909aa9a6e10801c72ccaa73fbf67
7aac22897d6d7c30c7f753b6f35f4595d97b662d2ecaa9b07c559e71a890c890

Keyfind progress: 100%

Running both stages

Running both ram-wipe stages on reboot did not require any changes because it is the default behavior.

And again, Dasharo firmware, because of its stability and predictability, saves this blog post from another copy of logs from the EFI application because the memory map and excluded pages used by the firmware are the same as the ones shown in previous sections and are the same for two attacks described in the next two sections.

Warm boot attack on both stages

This time /proc/iomem shows the Linux kernel is loaded under the following addresses:

1
2
3
4
5
6
7
(...)
00100000-7d9ddfff : System RAM
  1f200000-201fffff : Kernel code
  20200000-20d7cfff : Kernel rodata
  20e00000-2106d47f : Kernel data
  21927000-21dfffff : Kernel bss
7d9de000-7da65fff : Reserved

And the kernel loaded by kexec before second ram-wipe stage execution:

1
2
3
4
5
6
7
(...)
100000000-17fffa23f : System RAM
  17c800000-17d7fffff : Kernel code
  17d800000-17e37cfff : Kernel rodata
  17e400000-17e66d47f : Kernel data
  17ef27000-17f3fffff : Kernel bss
17fffa240-17fffa2bf : System RAM

Analysis of other addresses:

  • 0x1000-0x10000: not zeroed, but hexdump showed that the only data in this region is the pattern written by the EFI application. Hence, this region was not used by Linux or bootloader.
  • 0x100000-0x800000: 524288 bytes zeroed and 6815744 not zeroed. All non-zeroed bytes contain the pattern written by the EFI application. Hence, these bytes were not used by Linux or bootloader.
  • 0x1600000-0x7bd20000: 2054160384 zeroed and 131072 not zeroed. Some of not zeroed addresses contain some data. Other addresses contain the pattern.
  • 0x7bd48000-0x7d8d7000: zeroed.
  • 0x7f5ee000-0x7f5f4000: zeroed.
  • 0x7fc00000-0x7fc70000: zeroed.
  • 0x7fd20000-0x7fd2d000: zeroed.
  • 0x100000000-0x180000000: this region is very fragmented in terms of zeroed and not zeroed addresses. 1892876288 bytes was zeroed and 254607360 was not zeroed.

And the attack shows negative results:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
$ while read -r file; do
echo -e "Checking file: $file"
aeskeyfind -v $file
done < <(ls ./*.csv)
Checking file: ./2025_05_03_21_08_0x0000000000001000.csv
Keyfind progress: 100%
Checking file: ./2025_05_03_21_08_0x0000000000100000.csv
Keyfind progress: 100%
Checking file: ./2025_05_03_21_08_0x0000000001600000.csv
Keyfind progress: 100%
Checking file: ./2025_05_03_21_09_0x000000007BD48000.csv
Keyfind progress: 100%
Checking file: ./2025_05_03_21_09_0x000000007F5EE000.csv
Keyfind progress: 100%
Checking file: ./2025_05_03_21_09_0x000000007FC00000.csv
Keyfind progress: 100%
Checking file: ./2025_05_03_21_09_0x000000007FD20000.csv
Keyfind progress: 100%
Checking file: ./2025_05_03_21_09_0x0000000100000000.csv
Keyfind progress: 100%

Cold boot attack both stages

This time /proc/iomem shows the Linux kernel is placed under the following addresses:

1
2
3
4
5
6
7
(...)
00100000-7d9ddfff : System RAM
  13a00000-149fffff : Kernel code
  14a00000-1557cfff : Kernel rodata
  15600000-1586d47f : Kernel data
  16127000-165fffff : Kernel bss
7d9de000-7da65fff : Reserved

Analysis of other addresses:

  • 0x1000-0x10000: not zeroed, but hexdump showed that the only data in this region is the pattern written by the EFI application. Hence, this region was not used by Linux or bootloader.
  • 0x100000-0x800000: 524288 bytes zeroed and 6815744 not zeroed. All non-zeroed bytes contain the pattern written by the EFI application. Hence, these bytes were not used by Linux or bootloader.
  • 0x1600000-0x7bd20000: 84606976 zeroed and 1969684480 not zeroed. Most of non-zeroed addresses contain some data.
  • 0x7bd48000-0x7d922000: not zeroed, contains data.
  • 0x7f5ee000-0x7f5f4000: not zeroed, contains data.
  • 0x7fc00000-0x7fc70000: not zeroed, contains data.
  • 0x7fd20000-0x7fd2d000: not zeroed, contains data.
  • 0x100000000-0x180000000: this region is very fragmented in terms of zeroed and not zeroed addresses. 319815680 bytes was zeroed and 1827667968 was not zeroed. Still a lot of processes data not zeroed.

Yet another pack of stolen LUKS keys:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
$ while read -r file; do
echo -e "Checking file: $file"
aeskeyfind -v $file
done < <(ls ./*.csv)
Checking file: ./2025_05_03_21_13_0x0000000000001000.csv
Keyfind progress: 100%
Checking file: ./2025_05_03_21_13_0x0000000000100000.csv
Keyfind progress: 100%
Checking file: ./2025_05_03_21_13_0x0000000001600000.csv
Keyfind progress: 100%
Checking file: ./2025_05_03_21_14_0x000000007BD48000.csv
Keyfind progress: 100%
Checking file: ./2025_05_03_21_14_0x000000007F5EE000.csv
Keyfind progress: 100%
Checking file: ./2025_05_03_21_14_0x000000007FC00000.csv
Keyfind progress: 100%
Checking file: ./2025_05_03_21_14_0x000000007FD20000.csv
Keyfind progress: 100%
Checking file: ./2025_05_03_21_14_0x0000000100000000.csv
FOUND POSSIBLE 256-BIT KEY AT BYTE 62b6020

KEY: fbd6ea69496a02f6af8bd0e889372214a5ad905e62237a45ab9719b7108e8ee9

EXTENDED KEY:
fbd6ea69496a02f6af8bd0e889372214
a5ad905e62237a45ab9719b7108e8ee9
e3cff4a3aaa5f655052e26bd8c1904a9
c179628da35a18c808cd017f18438f96
fbbc640e5119925b5437b4e6d82eb04f
a048850903129dc10bdf9cbe139c1328
21c1507370d8c22824ef76cefcc1c681
103031051322acc418fd307a0b612352
c6e75058b63f927092d0e4be6e11223f
8fb2a2709c900eb4846d3ece8f0c1d9c
28438e2b9e7c1c5b0cacf8e562bddada
25c8f527b958fb933d35c55db239d8c1
1a22f61c845eea4788f212a2ea4fc878
a24c1d9b1b14e608262123559418fb94
f72dd43e73733e79fb812cdb11cee4a3

CONSTRAINTS ON ROWS:
0000000000000000000000000000000000000000000000000000000000000000
0000000000000000000000000000000000000000000000000000000000000000
0000000000000000000000000000000000000000000000000000000000000000
0000000000000000000000000000000000000000000000000000000000000000
0000000000000000000000000000000000000000000000000000000000000000
0000000000000000000000000000000000000000000000000000000000000000
00000000000000000000000000000000
56de1b21addaab1267887381738dd32766ea6ffaf6a968d5739831ea7d4795e8
0621c9eaff653ef3aa9ac59d743350f6f1f6b3dd5bec2982ef71bcf06aabea5c
533d3758c29e840855fffb6edea9956b9f6984a024bfc1bdb49d957285da56ac
fb63cc40f733162997617f668b566e05512226b834a51c67902254cf3147c3de
c3218667f6ed12796052694f1c37116358ac834ec530ecf7a48748a8a1659711
faadf6045fd4bf0796bf7b367c65782ce8ea413d082b2c5961b7a45f05e2dfb9
4c16e99d04f1abcfc96bc431eada031a93fc9107d6e993797ee212f56e8a420a

FOUND POSSIBLE 256-BIT KEY AT BYTE 62b7820

KEY: 1598a0169daa9a0b35efc5774623d07a7362ef77be66d5a42b396f1bc77e01d2

EXTENDED KEY:
1598a0169daa9a0b35efc5774623d07a
7362ef77be66d5a42b396f1bc77e01d2
e7e415d07a4e8fdb4fa14aac09829ad6
72715781cc178225e72eed3e2050ecec
b62adb67cc6454bc83c51e108a4784c6
0cd10835c0c68a1027e8672e07b88bc2
de17fea21273aa1e91b6b40e1bf130c8
a3700cdd63b686cd445ee1e343e66a21
581503b84a66a9a6dbd01da8c0212d60
198dd40d7a3b52c03e65b3237d83d902
a4207447ee46dde13596c049f5b7ed29
ff2481a8851fd368bb7a604bc6f9b949
1d764ff3f3309212c6a6525b3311bf72
3ca689e8b9b95a8002c33acbc43a8382
dd9a5cef2eaacefde80c9ca6db1d23d4

CONSTRAINTS ON ROWS:
0000000000000000000000000000000000000000000000000000000000000000
0000000000000000000000000000000000000000000000000000000000000000
0000000000000000000000000000000000000000000000000000000000000000
0000000000000000000000000000000000000000000000000000000000000000
0000000000000000000000000000000000000000000000000000000000000000
0000000000000000000000000000000000000000000000000000000000000000
00000000000000000000000000000000
6a05df4cf5c301a1707d1029697f6b83daa7acc71f33fff01806cd27108ae7ca
eac1106501e9d83993b9cb1a48d0044638ce1f1d0f644d46ad134a239c8a7b2e
0c56163cb24a642392501323db69cf5cf5b605e210cb1983a27707653199310d
94288153a6025ab1201a77004939dc7fcdfa9e2434d1890fb2bc1ee693ee3668
e99a93c79a7ceeea86182db16923ab7f7c4f167f87aabb23866d97e92152288e
ff99f82fdb9390ed1c64c35bef3b86ceca92909aa9a6e10801c72ccaa73fbf67
7aac22897d6d7c30c7f753b6f35f4595d97b662d2ecaa9b07c559e71a890c890

Keyfind progress: 100%

Summary

The ram-wipe software solution could protect from warm boot attacks, but:

  • ram-wipe cannot wipe RAM used by kernel, because it is an application that is not allowed to modify Linux kernel address space. Hence, if your secrets are stored inside kernel address space, the ram-wipe will not help protecting them from a warm boot attack.
  • ram-wipe wipes memory previously used by some processes, but it cannot wipe RAM of currently used processes (you can even notice OOM Killer logs when ram-wipe tries to wipe RAM unassigned to it). Hence, you should make sure that ram-wipe can and will wipe RAM, where your process left your secrets. This is why it’s important to show the difference between zeroed and non-zeroed addresses in RAM during tests - ram-wipe does not just fail to wipe the kernel address space, but also leaves parts of other processes' memory unwiped!

What about cold boot attacks? The tests showed that ram-wipe does not wipe anything during cold boot attacks (the zeroes at some addresses in RAM are caused by some processes zeroing out memory). The reason for this is hidden in the design: as discussed above, ram-wipe relies on proper system reboot/shutdown/halt sequences and if these sequences are not executed (what is true for cold boot attack) - ram-wipe wiping stages will not be launched. Hence, ram-wipe will not protect from cold boot attacks!

Thoughts on how to protect your secrets from cold boot attacks:

  • Make ram-wipe wipe RAM on boot too. This will make sure, that Linux userspace processes will not get access to secrets leftovers in RAM. This will need building a chain of trust up to Linux kernel and RAM disk though, because there will be no point in wiping during Linux boot if an attacker will boot malware instead of the needed kernel.
  • Wipe RAM earlier in the boot sequence. For example, make firmware wipe RAM for you. This will make sure RAM is being wiped as soon as possible during boot and that the program that is responsible for this is fully trusted and has a very small TCB.

Other improvements include fixing the printing issues, making sure that secrets are being wiped and rewriting shell scripts, making sure that they are solid and trustworthy (for example, by following already classic lectures on how to write secure and stable shell scripts).

During the review process of this blog post Kicksecure announced improvements based on this blog post, feel free to check it and suggest any further changes that will bring all of us closer to more secure components and solutions.

Thank you for reading this blog post, and see you again soon!

Unlock the full potential of your hardware and secure your firmware with the experts at 3mdeb! If you’re looking to boost your product’s performance and protect it from potential security threats, our team is here to help. Schedule a call with us or drop us an email at contact@3mdeb.com to start unlocking the hidden benefits of your hardware. And if you want to stay up-to-date on all things firmware security and optimization, be sure to sign up for our newsletter. Don’t let your hardware hold you back, work with 3mdeb to achieve more!


Daniil Klimuk
Programming, electronics and cycling nerd, bookworm and very passionate in learning. Left his own country at seventeen years old for studying and working abroad, and still searching for a place to settle. Big critic of fatalism and a fan of what-you-are-is-what-you-do way of thinking. In professional life tries to be as pedantic as possible, believes that nothing is perfect and nobody is perfect and every solution should be questioned and evaluated. Likes to solve complex problems, even if it implies a lot of digging before stating the final solution. States that developing skills in different fields is important as well as perfecting a one field, because everything has its intersections and sometimes it is crucial to investigate something from a different angle. Most of the time a workaholic, but sometimes a bit lazy. Not always punctual and often unconsciously altruistic.