CVE-2023-38408
Room Link: https://tryhackme.com/r/room/cve202338408
Background Info
Initially, ssh-agent allowed loading any shared library without filtering, leading to security concerns. In response to CVE-2016-10009, an allow-list (/usr/lib*/,/usr/local/lib/
by default) was added to limit library loading. However, it is possible to abuse the side effects of the library's constructors (dlopen
) and destructors (dlclose
) to manipulate memory and control the program flow.
Code execution can be achieved by making the stack executable, registering a signal handler for SIGSEGV
and manipulating its code, replacing the signal handler's code with code from another library, triggering a SIGSEGV
, and replacing his handler's code to finally jump into the stack where the shellcode is stored.
The following paragraphs will present the detailed steps:
1. Making the Stack Executable:
Leveraging dlopen()
to load one of the libraries grants the ability to make the stack memory region (specifically the target process ssh-pkcs11-helper
's stack) executable. This allows bypassing the usual protection against executing code on the stack.
2. Copy the shellcode to the stack:
Once the shellcode is generated, typically using tools like Metasploit, it can be copied to the stack memory using the socket generated from the SSH connection. The shellcode is also combined with a NOP sled
which is a sequence of No-Operation assembly instructions. The reason why is to provide a larger target for the program execution flow to land on during the gadget execution.
To verify whether the process has the desired executable flag, you can use the following commands in dbg, a Linux debugger. You can repeat the next few gdb commands once you have gained access as alice:
First, obtain the PID of the process ssh-pkcs11-helper.
Attach dbg to the target process using its PID.
Use the command info proc mapping to examine the memory mappings of the process.
Look for the memory region corresponding to the stack, and check its flags, marked as rwx.
Terminal
Using gdb, you can verify whether the stack has the shellcode loaded by inspecting its content.
Regarding the command $rsp+10100
, it is an expression that references a specific memory location relative to the stack pointer ($rsp
). In this case, $rsp+10100
points to the memory location located 10100 bytes above the stack pointer.
Observe that it contains a series of NOP (No Operation) instructions followed by the start of the shellcode: 0x31 0xc0 0x48 0x31 0xc0 0xff 0x48
Terminal
3. Registering a Signal Handler:
To successfully execute arbitrary code within the exploit, a custom signal handler must be registered for the SIGSEGV
signal. This signal, known as a Segmentation Fault, is triggered when a program attempts to access an invalid memory address. By registering the signal handler, the exploit gains the ability to define a user-defined function that will be executed whenever the SIGSEGV
signal occurs.
4. Replacing the Signal Handler's Code:
At this stage, the exploit leverages the technique of side-loading another library to replace the current signal handler's code with an alternative code segment that will jump to the stack where the shellcode is stored.
To ensure that the code segments of this new library remain mapped in memory even after dlclose()
is invoked, the library must be marked with the "Nodelete
" attribute. Ordinarily, when dlclose()
is called to close a shared library, the library's code segments are unloaded from memory, and any associated resources are released. However, by utilizing the "Nodelete
" attribute, the attacker prevents the code segments from being unloaded, effectively preserving their existence in memory beyond the dlclose()
operation. This ensures that the replacement code, responsible for jumping to the shellcode in the stack, remains accessible and functional throughout the exploit's execution.
5. Triggering SIGSEGV:
By employing yet another library, the attacker intentionally triggers a SIGSEGV
signal, which prompts the kernel to execute the custom signal handler previously registered. This strategic step is a critical part of the exploit's progression.
Upon receiving the SIGSEGV
signal, the kernel recognizes that an invalid memory access has occurred and proceeds to invoke the custom signal handler rather than terminate the program abruptly. By doing so, the attacker seizes the opportunity to manipulate the program's execution and steer it toward the injected malicious code located within the NOP sled.
6. Executing the Replacement Code:
By achieving this precise jump into the executable stack, the exploit ensures that the program's execution is directed toward the specific memory location where the shellcode resides.
Exploitation
In this task, we will delve into running the exploit and gaining a more practical understanding of its functionality.
For this particular scenario, we will be working with two machines: the workstation (a vulnerable instance of Ubuntu 21.04) and another server which is under the attacker's control (Attackbox). To simulate the vulnerable instance, a connection between the workstation and the server has been employed in the previous task. There is a user alice who is connected from the workstation to the attacker using SSH agent forwarding. Alice does this by executing the following commands (so you do not have to):
Kali
To copy the shellcode into the process using the SSH socket, you need to follow these steps:
Obtain the
PID
of the SSH agent running on the remote attacker machine.Once you have the socket, use netcat (
nc
) to transfer the shellcode to the agent's memory (workstation).After starting the transfer, wait for a few seconds to ensure the shellcode is fully copied into the target memory.
Finally, press Ctrl-C to stop the netcat transfer once the shellcode is successfully placed in the agent's memory.
Note that the next command will not use ssh-add
. Because the malicious payload is around 10KB passphrase and ssh-add has a limit of 1KB.
Kali
The next step to the exploitation process is register the signal handler for the Segmentation Fault (SIGSEGV
) signal.
Kali
After successfully registering the custom signal handler for SIGSEGV
, the next crucial step in the exploitation process is to replace the original signal handler routine with a carefully chosen gadget. This gadget will serve as a means to redirect the program's execution flow and jump into the stack when the SIGSEGV signal is triggered.
Kali
Finally, by intentionally causing a segmentation fault, the SIGSEGV
event can be triggered, executing the shellcode:
Kali
Victim
Last updated