Linux x86 Socket Reuse Shellcode
Last updated
Was this helpful?
Last updated
Was this helpful?
The target executable is a custom binary written for the purpose of teaching the development of socket reuse shellcode.
While using reverse TCP shellcode may be sufficient in many cases, many servers will have strict outbound firewall rules in place preventing the outbound connection required by reverse TCP shellcode. However these firewall rules can be bypassed by reusing the existing connection to the target server. This is where socket reuse shellcode comes into play.
Running the "file" command on the binary shows this is a 32-bit ELF executable.
Checking the memory protections in place shows this binary has no protections enabled.
Running the binary as intended and providing a port number as an argument spawns a simple server.
Browsing to the vuln() function within a disassembler, spotting the vulnerability is clear.
The value of "0x0" is passed as the flag argument for recv.
"0x400" bytes are being read in from the endpoint user.
The destination buffer is located "0x218" bytes relative from the base pointer.
The address of the destination buffer is now pushed onto the stack.
The Socket FD argument for recv is now pushed onto the stack.
recv() function call is made
Due to the fact that the distance between the base of the destination buffer is smaller than the amount of bytes read by the recv() function, this binary suffers from a buffer overflow vulnerability.
In order to manipulate the stored return address with the intent of redirecting code execution, the distance between the user's input and the stored return address must first be calculated.
For this calculation, consider that the destination buffer is located "0x218" bytes relative to the base pointer. By providing 0x218 bytes of data with an additional 4 bytes which overwrites the stack frame's old base pointer. In conclusion reaching the stored return address requires 540 total bytes of padding to be given.
Due to Data Execution Prevention being disabled on the binary, returning to shellcode becomes an option. However because of ASLR, the location of the user input is unknown.
However, if the binary contains any "jmp" or "call" instructions to a register which points at the user input this can be utilized to redirect the execution flow into the user data.
Using ropper, a "jmp esp" instruction can be found within the binary.
In order to reuse the existing connection, the relative location of the socket file descriptor for the current connection has to be known. This can be found by analyzing the section of the binary where the accept() function call is made.
Within the "run_server" function, the accept() function call is made.
Following this function call, the return value of accept is stored at "ebp-0x10".
Due to the buffer overflow overwriting the stored base pointer and the function prologue storing this old base pointer value back into the EBP register an offset from the base pointer cannot be used. However, by debugging the binary and comparing the address at which the socket file descriptor resides to the stack pointer value at the time of the crash the offset can be determined.
The importance of obtaining the socket file descriptor is due to it being the descriptor of the current connection.
By setting a break point on the "mov" instruction within the "run_server" function then running the binary, the address at which the socket FD resides can be seen.
Placing the break point.
Executing the binary within GDB.
Hitting the break point.
Inspecting the address.
Register state at crash.
Now that the address of the socket file descriptor is known, the offset can be determined by subtracting the value of ESP from the address of the socket file descriptor.
Confirming the offset.
With the information from analyzing the binary, an address relative to the ESP register can be dereferenced to retrieve the socket file descriptor from memory.
Using the DUP2() system call the STDIN, STDOUT, and STDERR can all be set to the socket file descriptor.
The shellcode can be finalized by performing an execve() system call with the "/bin//sh" string.
Below is a snippet of the completed shellcode
Using all of the information above, a simple exploit script can be crafted. An example of this is below: