- Authors Info
- Description
- Problems Encountered
- Problems with our approach
- Alternative approaches
- References
Shay Roe John Ryan Karl O Hinneirghee
Find the vulnerabilities in the server code Generate suitable Shellcode Write an exploit program to construct our attack string and launch an attack on the server
The server contains (at least) two vulnerabilities
-
A format string vulnerability in "read_name" function snprintf(greeting, BLENGTH, buffer);
-
A Buffer overflow vulnerability in "read_message" function strcpy(message, buffer);
Format String Vulnerability / Attack
The format string attack can be found in the "read_name" function, in the form of a call to snprintf without the inclusion of a format string containing format specifiers.
snprintf(greeting, BLENGTH, buffer);
We take advantage of this format string vulnerability by providing our own format string in "buffer", with the knowledge that based on the format specifiers we supply, snprintf will make the assumption that the corresponding values for each of those format specifiers can be found on the stack.
So using %p format specifiers within our string in "buffer" we force the call to snprintf to read from the stack, providing us with a good indication of a "stack start address" that we can then use in calculating our new return address.
char response_buffer[] = "%p %p Heres the value %p "; (from exploit.c)
The buffer overflow vulnerability can be found in the "read_message" function in the form of an unbounded call to "strcpy".
The function read_mesage receives a message from the connected client(exploit) and stores it in "buffer" before making the strcpy call:
Receive a response from the client recv(s, (void *)buffer, BLENGTH, 0);
The unbounded strcpy call is then made:
strcpy(message, buffer);
The contents of "buffer", and subsequently, the contents of "message" comes directly from the client(exploit) and therefore we can take advantage of this vulnerability in order to overflow the buffer and inject our attack string into Server's process address space.
Our exploit program takes advantage of both these vulnerabilities within the server code in order to construct our attack string and launch our attack.
We make use of the format string vulnerability in order to return a "stack start address", providing us with a good starting point for estimating/calculating our new return address. A return address that points into our overflowed buffer on the stack.
The buffer overflow vulnerability is then used in order to inject our constructed "attack string" into the server's process address space, overflowing the "message" buffer and overwriting the original return address with our newly constructed return address.
As this new retrun address points back to the overflowed buffer, when it's popped off the stack into the instruction pointer, we return to some point within our NOP Sled. Execution then proceeds upwards, we reach our injected code and exploit the vulnerable server. As a result of our exploit the server launches a shell and begins listening.
PAYLOAD
-
Constructed Return Address = Stack Start Address - Offset
-
Payload: This is the executable part of our attack string. Details below
-
NOP Slide: Provides us with a wider landing area and increases our chances of success. We don't have to be 100% accurate with our return address, returning to the exact address corresponding to the very beginning of our injected code. We just have to "land" somewhere inside our NOP Slide. Execution will continue upwards then and we will execute our injected code.
Our shellcode was obtained from an online source Here: http://www.shell-storm.org/shellcode/files/shellcode-672.php [1]
This shellcode launches a shell binds and listens on a specified port Port: 64533
The commented assembly of the payload can be found in payload.s
One of the most interesting and troublesome issues we hit along the way was when our executing shell code started to overwrite itself on the stack.
This was as a result of the location of the stack pointer(esp) after we injected our attack string into the sever address space.
As the shellcode executed, in order to perform some operations it pushed values onto the stack. Eventually the instruction pointer would end up pointing to that part of the stack, however the code that we expected to be there had been overwritten by some data and the exploit failed.
We resolved this issue by moving the stack pointer out of our way before executing our shellcode. This was done by simply adding a sufficient amount to esp.
"\x83\xc4\x80" // add esp, 0x40
This worked and we stopped overwriting our shellcode and the exploit succeeded.
We would like to include the connection to the exploited server within our source code (exploit.c source code), rather than using netcat. This would not improve the quality of the exploit but would just mean we'd have all steps contained within our exploit.c program.
Yes, in it's current state our attack could be easily detect in a number of ways.
-
Server dies after we exit After we exploit the server and exit from the client/exploit connection, the server will also exit. This of course would raise suspicion and draw attention to our attack
-
Shellcode is not polymorphic As our shellcode is not polymorphic it would likely be easily detected by intrusion detection systems. Intrusion detection Systems (IDS) will usually trigger if they encounter a number of consecutive NOP instructions (0x90). They would also trigger on the presence of the "/bin/sh" string. This is a dead giveaway of an exploit attempt.
Therefore in order to improve our exploit code we would look at options for making our shellcode polymorphic. For example, using something like ADMmutate or Metasploit.
"In early 2001, a new tool was released that allows an attacker to obfuscate any buffer overflow attack against any service. This tool is called ADMmutate and was written by K2. The primary purpose of this tool is to change an exploit signature every time that it is executed, resulting in “polymorphic shellcode”. " [2]
It's also possible to produce polymorphic shellcode using metasploit [3]
One alternative approach we tried, was to attempt to reuse the socket that the exploit had opened so that commands could be issued to the server without the need for netcat. Below is a snippet of the code required. Some guess work would have been required to determine the file descriptor assigned to the exploits socket (4 in the example code).
We didn't use this approach even though we got the shell code to run on the server, we could not get the socket to bind to our exploit shell.
dup2(4,0); dup2(4,1); dup2(4,2); execve("/bin/sh",0,0);
[1] http://www.shell-storm.org/shellcode/files/shellcode-672.php [2] http://www.sans.org/security-resources/idfaq/polymorphic_shell.php [3] http://www.metasploit.com/ [4] Buffer Overflow Attacks: Ddetect, Exploit , Prevent. Foster
Other sites referenced: http://dev.fyicenter.com/Interview-Questions/Socket-4/Explain_the_TIME_WAIT_state_.html http://wiki.apache.org/HttpComponents/FrequentlyAskedConnectionManagementQuestions http://www.softlab.ntua.gr/facilities/documentation/unix/unix-socket-faq/unix-socket-faq-2.html#ss2.7 http://www.shell-storm.org http://stormsecurity.wordpress.com http://www.strchr.com/machine_code_redundancy http://tom.bespin.org/src/low-level/opx86.html