CSAW CTF Finals were really dope, not just because we had Pwnadventure Z, but also because of all the really cool people that were there.
Hipster (or Hipster Hitler) was a basic exploitation challenge that was fun to exploit. It is more or less similar to Contacts which appeared in CSAW CTF Prelims.
Let’s check it out:
NX is disabled which suggests that we are more than likely gonna wanna be using shellcode (unless they are tryna trick us :3).
Now let’s checkout what this thing actually looks like:
So from inital inspection, this appears to be a stack based calculator in which you can create different different calculation stacks. And apparently “Reverse Polish Notation” is just postfix notation which just makes it easier to program a stack based calculator.
Given that this is a calculator themed for Hitler, I figured that the rest of strings in this binary were also pretty funny:
…See something interesting in there?
Well for starters at 0804A617 we have a mention of /dev/urandom which suggests some sort of random values being read in. But even more interesting is that there are Unable to bind socket and Unable to listen on socket which only appear in a server application, but this isn’t a server… I guess we will keep our eyes open for any other mention of this socket code.
So the first thing we should really just do is play around with the program and see if we can get an easy crash (always a good thing to try first, you can almost always get a crash doing this in exploitation problems).
Going on a hunch about how this program stored its stacks, I got a crash pretty quickly.
[vagrant@kamino csaw2015finals]$ ./hipster
Sieg Heil!
Welcome to Hipster Hitler's Pocket Calculator!
My computing resources await you, mein Führer.
We now use Reverse Polish Notation as a result of our recent conquest of Poland.
All commands are prefixed with ':'.
Type :help for a list of commands.
==> :new
Switching to new stack, mein Führer.
==> :new
Switching to new stack, mein Führer.
==> :next
Switched to stack #0.
==> 111111111111111111111111111111111111111111111111111111111111111111111111111111111111
==> ==> ==> ==> ==> ==> :next
Switched to stack #1.
==> :top
zsh: segmentation fault ./hipster
Program received signal SIGSEGV, Segmentation fault.
[----------------------------------registers-----------------------------------]
EAX: 0x804b8b0 ("Switched to stack #1.\n")
ESI: 0x1
[-------------------------------------code-------------------------------------]
=> 0x8048b21: mov esi,DWORD PTR [esi-0x4]
So we can overflow from one stack into another as it looks like the program is attempting to dereference a 1 which is what we input into the program.
Now we should checkout how these stacks are actually stored in memory.
Poking around create_stack (remember, there are no symbols in this program so I’m logically naming all these functions) we can see how big each stack is:
We also see dword_804B8F0 being stored in the location pointed to by esi (a pointer to the current stack)
If we check the xrefs of this value we see it is first assigned here:
And checked in do_calcuation:
Just a guess right now, but it looks like this value is some sort of stack canary that checks if we have overflowed from one stack into another. So we are going to have to leak this value out somehow before we do our overflow.
Something else that is interesting that is happening in create_stack is:
Here we see the current stack storing a pointer to 8 bytes into itself in itself. Looking at get_stack_top we see that this pointer is a pointer to the top of the stack.
But what is interesting here, is that in create_stack it stored a pointer 8 bytes into the stack, but when we go to print out the top value, mov esi, [esi-4] we subtract 4 from this pointer. Let’s draw out what we conceptualize a stack to look like at this point:
[-------- top --------]
stack+0 random value
stack+4 pointer to top of stack
stack+8 numbers and stuff
...
stack+164 numbers and stuff
[-------- end --------]
So 8 bytes into our stack - 4 is just 4 bytes in, which is a pointer to the top of the stack. This is important as we will use this to read out the random value from a stack we create.
[vagrant@kamino csaw2015finals]$ ./hipster
Sieg Heil!
Welcome to Hipster Hitler's Pocket Calculator!
My computing resources await you, mein Führer.
We now use Reverse Polish Notation as a result of our recent conquest of Poland.
All commands are prefixed with ':'.
Type :help for a list of commands.
==> :new
Switching to new stack, mein Führer.
==> :top
154038288
==> ^C
[vagrant@kamino csaw2015finals]$ python -c 'print hex(154038288)'
0x92e7010
Kewl, so since the allocation of each stack is a multiple of 4, we can pretty much be sure that each stack will be allocated right next to each other. Therefore, with the leak that we have of the top of the stack for the first allocation, we will have to throw in 0xac bytes into stack #1 to get to the point where we are about to overflow into stack #2s top of stack pointer. We will then put a pointer to point to stack #1s random value.
[-------- top --------]
stack1+0 random value
stack1+4 pointer to top of stack
stack1+8 shellcode and nops
...
stack1+164 shellcode and nops
[-------- end --------]
[-------- top --------]
stack2+0 shellcode and nops
stack2+4 overflowed with pointer to stack1+0
stack2+8 numbers and stuff
...
stack2+164 numbers and stuff
[-------- end --------]
I also decided to just use stack #1 as the place to store the shellcode and just pad it with nops. Since we are just using a stack based calculator, you have to come up with some way to write arbiturary values by using math. write_num in the exploit script is a demonstration on how to do this.
Now that we have the random value, we can start writing to an arbituary location by setting the top of stack to be our location of choice. Since we already have our shellcode in the program at this point, we just need a function pointer to point to our first stack. The GOT is a great place to overwrite for this :3 The rest of the exploit is pretty self explanatory.
Turns out the socket code was just left over from another version of the challenge, so nothing super secret about it :3