Sésame, ouvre-toi (harder) #
-
Starting point #
First thing we see is the bootloader, u-boot in this case, printing informations.
U-Boot 2023.07.02 (Jul 11 2023 - 15:20:44 +0000) DRAM: 24 MiB Core: 41 devices, 10 uclasses, devicetree: board Loading Environment from nowhere... OK In: pl011@9000000 Out: pl011@9000000 Err: pl011@9000000 Autoboot in 2 seconds ## Booting kernel from Legacy Image at 40200000 ... Image Name: EFI Shell Created: 1980-01-01 0:00:00 UTC Image Type: AArch64 EFI Firmware Kernel Image (no loading done) (uncompressed) Data Size: 1028096 Bytes = 1004 KiB Load Address: 00000000 Entry Point: 00000000 Verifying Checksum ... OK XIP Kernel Image (no loading done) No EFI system partition No EFI system partition Failed to persist EFI variables ## Transferring control to EFI (at address 40200040) ... Booting /MemoryMapped(0x0,0x40200040,0xfb000)We can therefore see a lot of interesting stuff, but the main thing is that we boot on an UEFI shell, not a linux system. We can not access the u-boot prompt, so we have to find another way to get the flag.
-
Search for a flaw #
U-Boot need a boot command to automatically boot the system, so the first things to look for is the content of the
bootcmdvariable. Usingstringson thebootloader.binfile, we can find the following line:bootcmd=bootm 0x40200000; poweroff. This means that the bootloader will try to execute the code at address0x40200000and then power off the system.We can then assume that, if the
bootmcommand is returning an error, the command will stop there and give us the prompt back, without powering off the system. However, we can not write to the memory address0x40200000, because it would require to find where the UEFI image is stored in memory, tamper with it and hope that u-boot is not checking the integrity of the image before executing it but thanks to the boot information, we know that the image has a checksum, so it is likely that the integrity of the image is checked before executing it.Although we can not tamper directly with the image on the disk, we can try to find a way to tamper with the memory.
-
Memory tampering #
U-Boot is loaded into memory and, since we have the image of the bootloader, we can reverse it to see how it is executed and if there is a way to tamper with it.
At this point, the plan is clear: Reverse the bootloader, find the function poweroff, replace the first line of the function by “return 0” and then type exit in the shell to end the
bootmcommand, which will then jump on the poweroff function, which will do nothing and return 0, giving us the prompt back without powering off the system.To print memory, we have the function
dmem, which is a simple function that takes an address as argument and prints the content of the memory at that address. To write to memory, we have themmfunction, which takes an address and a value as argument and writes the value to the memory at that address.But now, we need to figure out where the poweroff function is located !
-
Reverse time ! #
I used Ghidra to reverse the bootloader. To start, i searched for the string “poweroff” and found where it is stored in the binary.
We can see that there is two references to this string. If we follow them, we end up on the same function.
We can see that there are two functions called afterwards. Let’s look into the first one:
This is kind of hard to read but this function is too little to be the poweroff function, meaning it is probably a wrapper function or a set up. Let’s look into the second one:
This function is looking way more like a poweroff function, and we can even see the print
"Powering off..."(s_power_off...) ! However, since U-Boot’s command table points to the wrapper function (0x0000d314) as the default entry point for the command, this is the one we need to patch. By replacing the first instruction of this wrapper with a “RET”, it will return immediately and never call the actual poweroff routineNow, we have the address of the first instruction of the poweroff function, which is at the address
0x0000d314. The plan is to replace the first instruction by “RET” which is0xd65f03c0in arm64. However, we need to keep in mind that we are in little endian, so we must write ‘C0 03 5F D6’ to the memory address of the first instruction of the poweroff function. -
Easy as that ?! #
To write the value to memory, we do
mm 0xADDRESS, address being the address we found (0x0000d314), so in theory if we do the command and writeC0 03 5F D6to the memory, we should be able to typeexitin the shell and get the prompt back without powering off the system.let’s try it !
Why does it not work ? We are writing the correct value to the correct address, so it should work, but it does not.
The problem is that the address we found is the address of the instruction in the binary, but when the binary is loaded in memory, it is loaded at a different address. We need to find the base address of the binary in memory and then add the offset of the instruction to get the correct address to write to.
-
Let’s end it #
Lucky for us, we have the
dmemfunction that allows us to print the content of memory at a given address. I searched for the bootcmd variable in memory and found it.Now that I have the address of a variable in memory, i can search for the address of this exact variable in the binary.
We need to calculate the offset of the memory. The address of the variable in the binary is
0x0006c805, and the address of the variable in memory is0x417bd805, so the offset is0x41751000.We now have the offset where U-Boot is loaded in memory. If we add this offset to the address of the first instruction of the poweroff function (
0x0000d314), we get the correct address to write to, which is0x4175e314.Now, we can write the value as we did before but with the correct address :
-
Flag #
After we type
exitin the shell, we get the prompt back without powering off the system ! Now, we need to find the flag. A quickprintenvshows us that there is an environment variable calledprintflag:printflag=hash sha256 40900100 1000 flaghash; echo FCSC{$flaghash};We only need to execute this command to get the flag !