Level 10 - Diffuse
Information Gathering and Privilege Escalation
Based on the challenge description, I can interact with my own Level 10 instance via the Telegram bot with the handle @DiffuseInstanceBot
. By sending my platform username followed by the level password, the bot provides the SSH credentials to connect to the challenge instance:
After connecting, I gained access to a Windows environment:
It appears that the RDP port is open:
To make the information gathering process more manageable, I can attempt to forward the RDP port through SSH and then use RDP to access the instance for further analysis:
Text Only | |
---|---|
|
Once the port forwarding is set up, I can connect to the Windows environment using Remmina through 127.0.0.1:3389
:
After performing basic enumeration, I discovered the diffuse
home directory, but the current user does not have sufficient privileges to list its contents.
Additionally, a xampp
directory was also found, located in C:\
. However, the current user also lacks the necessary permissions to list its contents, which likely includes web source code.
We can perform a simple curl request to http://127.0.0.1
to download the index page. From the downloaded page, we can see that PHP is being used.
HTML | |
---|---|
Since PHP is being used, I assumed the index page likely has a .php
extension, meaning an index.php
file probably exists.
Despite the restrictions on accessing the xampp
directory, files within C:\xampp\htdocs\
, which includes as index.php
, can be overwritten. I created an index.php
file to execute the whoami
command and overwrite the existing index.php
file.
Here is the code:
Making a HTTP request to the modified index.php
returns a response showing NT AUTHORITY/SYSTEM
as the executing user:
At this point, we can escalate privileges by updating the index.php
to add diffuse
and diffuser
to the Administrators
group and assign them a simple password.
Here is the code:
PHP | |
---|---|
Information Gathering via diffuse
User
During my time exploring as diffuse
, I went down several rabbit holes trying to figure out where to find the contents of a few powershell scripts (setup.ps1
, and setupv2.ps1
) that were referenced in Setup.ps1
which can be found within one of his directories. I also found references to arduino_bomb_for_participants.zip
in the Recycle Bin
... However, the files in the Recycle Bin seems corrupted...
While exploring as diffuse
, I went down several rabbit holes trying to locate the contents of a few PowerShell scripts (setup.ps1
and setupv2.ps1
) that were referenced in Setup.ps1
, which I found within one of his directories. Additionally, I found references to arduino_bomb_for_participants.zip
in the Recycle Bin. However, the files in the Recycle Bin
appear to be corrupted:
Thinking level 10 was a forensics challenge, I went to install numerous forensic analysis tools in an attempt to carve out the deleted files.
However, there were several interesting files in the AppData
and Desktop
directories of the diffuse
user. After much exploration, the most crucial file for solving this challenge had been in clear sight all along: firmware.hex
, located in C:\Users\diffuse\Desktop\project_incendiary
. The content of this file is in Intel HEX format.
Additionally, I discovered a hidden directory at C:\Users\diffuse\AppData\Roaming\Icendiary\Schematics\
containing a file named schematic.pdf
. Upon opening the file, it revealed the schematic of an Arduino-based bomb:
Interestingly, at the bottom of the schematic, it shows "page 1 / 2", but only one page was visible in the PDF. This suggests the presence of a hidden layer. Using an online PDF viewer like DocHub, I was able to uncover the hidden layer, which revealed a key: m59F$6/lHI^wR~C6
.
Not knowing what the key was for, I moved on. While searching for the various components of the bomb on Google, I came across a link to a Wokwi project. In this project, the components were linked up exactly like the schematics. The key from the hidden PDF layer also matched the key in this project. It appears the key resides in the uart-key-chip
custom component, and will be transmitted to the Arduino firmware.
Here is the custom chip source, which appears to leverage Wokwi's UART API:
By uploading the firmware (using the shortcut key F1 -> Upload Firmware and Start Simulation...
) to the Wokwi project, the bomb starts. From the Wokwi logs, we see that the chip gets initialized and data is transmitted in (F8g3a_9V7G2$d#0h
) and out (m59F$6/lHI^wR~C6
) of the chip.
From the schematics, we can infer that the Intel HEX file found earlier likely contains the firmware for the Arduino used in this project. A quick search also revealed that the Arduino UNO Rev 2
uses the ATmega4809
microcontroller, which is part of the AVR8
family. Unfortunately, both Ghidra and IDA do not have configurations to handle this specific processor variant, which presents a significant challenge in analyzing the firmware directly using these tools.
Firmware Analysis
To ensure the file can be properly parsed by disassemblers, the Intel HEX file was converted into a binary format:
Bash | |
---|---|
Ghidra doesn't seem to have the processor specification configuration to properly parse the firmware. I tried reading the processor manual and writing my own specs for the ATmega4809
(used by Arduino UNO Rev 2), but it did not work out. Instead, I can only make do with the existing configurations and interpret the results cautiously.
The article https://www.jonaslieb.de/blog/arduino-ghidra-intro/ provides useful guidance on analyzing the entry point of the firmware (Reset
) and more. From this article, it helps with identifying where the main()
function is located.
Here is the Reset
logic:
FUN_code_0c45()
likely corresponds to main()
, and FUN_code_1852()
corresponds to GCC's exit()
. Therefore, we can start renaming these functions accordingly.
The following code block should maps to __do_copy_data()
which is the function responsible for loading a .data
section from ROM:
C | |
---|---|
When analyzing main()
, it felt very overwhelming and chaotic. So, I shifted my focus to looking for defined strings to create some structure for my analysis. I started by searching for defined strings in the firmware:
However, none of the strings seemed to be referenced elsewhere... or are they?
The writeup by larsh
helps with explaining how data is loaded and how we can find string references: https://ctf.harrisongreen.me/2021/midnightsun/twi-light/. The writeup also references another writeup which included instructions on how to set up simavr for dynamic analysis: https://ctf.harrisongreen.me/2020/midnightsunctf/avr/. They very nicely included the Dockerfile
to setup the debugger:
After reading the mentioned writeups, I learned how to calculate the RAM address of a defined string. We need to use the formula <address in ROM> + 0x100 - 0x30BE
. It is also important to note that all the addresses in Ghidra seem to be halved, so remember to multiply them by 2 to get the actual ROM address.
Also some strings were not shown in Ghidra's Defined Strings
window. They are F8g3a_9V7G2$d#0h
and 39AB41D072C
.
We can note down some details of each defined string as shown below:
From here, we can annotate where the strings are being used. After annotating, you will notice that the function FUN_code_07e9
is always called immediately afterward. This is likely the function responsible for displaying the string. Therefore, we can rename FUN_code_07e9
to display_string
.
Between displaying Read key chip:
and GoodLuckDefusing
, it displays a string from the RAM address 0x3dd
:
Cross-referencing with the Wokwi project, this appears to be displaying the secret key:
When performing dynamic analysis later on, we can manually break at the call to the display_string
instruction and manually write the secret key to 0x33d
. This allows the firmware to perform subsequent logic that may require the key.
This is where we want our execution to eventually flow to:
Keep in mind that we want to avoid branches that lead to the call of FUN_code_07f8
, which will display Wrong decryption
.
With all the information at hand, we can now proceed with dynamic analysis. We first need to set up simavr
. This can be done with the following commands:
One thing to note when using avr-gdb
is that when setting a breakpoint at a ROM address, it can lead to unexpected behavior due to the way AVR's modified Harvard architecture handles memory. Specifically, the same address can refer to both ROM and RAM, leading to potential confusion. As explained in this StackOverflow answer, avr-gdb
handles this architecture. The issue arises because ROM and RAM share address spaces, and avr-gdb
distinguishes between them by utilizing different address spaces. Understanding how to reference ROM and RAM addresses correctly in avr-gdb
is crucial to avoid issues when setting breakpoints.
With all the information in mind, we can proceed with debugging the firmware. The key steps involve adding the key chip in RAM at 0x33d
by setting a breakpoint at the ROM address 0x1BDE
. We should also bypass specific checks that would lead to the FUN_code_07f8
function, which displays the message Wrong decryption
. By constantly bypassing those checks, we will eventually reach ROM address 0x26DA
, where the message Bomb defused!
is displayed. At this point, we can inspect the RAM for any presence of a flag.
Solution
Here are the final avr-gdb
commands to execute, summarizing what was mentioned in the previous paragraph:
The flag is TISC{h3y_Lo0k_1_m4d3_My_0wn_h4rdw4r3_t0k3n!}
.