The fresh released version of haka (v0.3.0) features a new module allowing to disassemble network data into instructions. This is useful to detect obfuscated shellcodes at network level as suggested in [raid05] for instance. The disassembler leverages on Capstone engine which supports several architecture (x86, arm, mips, etc.).

Here we will try to answer to question 8 of a well-known network forensic challenge. The challenge provides a pcap and asks to dump the shellcode used to exploit a vulnerability and then to provide the list of actions done by this shellcode.

Shellcodes are usually prepended with a nop sled as shown by the following figure. We assume in our first attempt that the nop sled is made of nop instructions (opcode 0x90).

For those who had already done this challenge, we know, from question 5, that the attack exploits a vulnerability present in smb service (port 445). So we write a security rule that inspects only that flow. If a shellcode is detected, then we start the disassembly in order do dump its content:

local tcp_connection = require ( ' protocol/tcp_connection' ) local rem = require ( ' regexp/pcre' ) local re = rem . re : compile ( ' %x90{100,}' ) local asm = require ( ' misc/asm' ) local dasm = asm . new_disassembler ( ' x86' , ' 32' ) dasm : setsyntax ( ' att' ) haka . rule { hook = tcp_connection . events . receive_data , options = { streamed = true , }, eval = function ( flow , iter , direction ) if flow . dstport ~= 445 then return end if re : match ( iter , false ) then -- raise an alert haka . alert { description = " nop sled detected" , targets = { haka . alert . service ( flow . dstport , " smb" ) } } -- dump instructions following nop sled dasm : dump_instructions ( iter ) end end }

We use a regular expression to detect a nop sled made of more than 100 nops and raise an alert in case of match. The pattern matching function updates the iterator which points at the beginning of the shellcode as shown by the previous figure. Then, we use our disassembler module in order to dump all instructions

haka@haka:~/workspace/haka/$ hakapcap dump-shellcode.lua attack-trace.pcap ... alert: id = 1 time = Tue Nov 25 10:09:01 2014 description = nop sled detected targets = { service: 445, smb } 0x00000000 jmp 0x12 eb 10 0x00000002 popl %edx 5a 0x00000003 decl %edx 4a 0x00000004 xorl %ecx, %ecx 33 c9 0x00000006 movw $0x17d, %cx 66 b9 7d 01 0x0000000a xorb $-0x67, (%edx, %ecx) 80 34 0a 99 0x0000000e loop 0xa e2 fa 0x00000010 jmp 0x17 eb 05 0x00000012 calll 2 e8 eb ff ff ff 0x00000017 jo 0xffffffffffffffae 70 95 0x00000019 cwtl 98 ...

For those who are familiar with shellcode obfuscation techniques, this is a polymorphic shellcode that starts with a decipher routine (see figure below).

The first instructions retrieve the address of the ciphered shellcode (starting at offset 0x17) in edx register. Then, we have a description loop that xor each byte of ciphered shellcode (of size 0x17d) with key 0x99.

In the following, we write a second security rule that will collect the shellcode in a buffer, decipher its content and then outputs the shellcode instructions:

local tcp_connection = require ( ' protocol/tcp_connection' ) local rem = require ( ' regexp/pcre' ) local re = rem . re : compile ( ' %x90{100,}' ) local asm = require ( ' misc/asm' ) local dasm = asm . new_disassembler ( ' x86' , ' 32' ) dasm : setsyntax ( ' att' ) haka . rule { hook = tcp_connection . events . receive_data , options = { streamed = true , }, eval = function ( flow , iter , direction ) if flow . dstport ~= 445 then return end if re : match ( iter , false ) then -- shellcode info extracted from previous dump local key = 0x99 local decipher_routine_size = 0x17 local shellcode_size = 0x17d -- fill shellcode buffer from stream local code = haka . vbuffer_allocate ( 0 ) local size = 0 local sub for sub in iter : foreach_available () do code : append ( haka . vbuffer_from ( sub : asstring ())) size = size + # sub if size >= shellcode_size then break end end -- remove superfluous data code : sub ( decipher_routine_size + shellcode_size ): erase () -- decipher shellcode local byte for i = decipher_routine_size , # code - 1 do byte = bit32 . bxor ( code : sub ( i ): asbits ( 0 , 8 ), key ) code : sub ( i ): setbits ( 0 , 8 , byte ) end -- dump shellcode local start = code : pos ( decipher_routine_size ) dasm : dump_instructions ( start ) end end }

The dump reveals the real instructions of the shellcode.

haka@haka:~/workspace/haka/$ hakapcap dump-shellcode.lua attack-trace.pcap ... 0x00000000 jmp 0x111 e9 0c 01 00 00 0x00000005 popl %edx 5a ... ... 0x00000111 calll 5 e8 ef fe ff ff 0x00000116 incl %edi 47 0x00000117 je 0x16a 65 74 50 0x0000011a jb 0x18b 72 6f 0x0000011c arpl %ax, 0x64(%ecx) 63 41 64 ... ... 0x00000175 jae 0x1e7 65 73 6f 0x00000178 arpl %bp, 0x65(%ebx) 63 6b 65 0x0000017b je 0x17d 74 00

Note that the above code leverages on bit32 lib which is available only with Lua 5.2. As Haka interprets Lua code using LuaJit by default, you should select Lua option ( -DBUILD=lua ) while building Haka from source or find another way to xor bytes.

The shellcode starts by storing address 0x00000116 on edx register. This address seems to hold data. A slight modification to the end of the above security rule confirms our assumption:

--local start = code:pos(decipher_routine_size) --dasm:dump_instructions(start) local data = code : sub ( decipher_routine_size + 0x116 ): asstring () print ( safe_string ( data ))

Haka outputs the list of primitives and libraries used by the shellcode. More precisely, this is a classical bindshell code: