Extracting Digital Signatures from Signed Malware with pf

September 3, 2015

Introduction

Lot of malware/PUP (Potential Unwanted Programs)/Adwares are now digitally signed. Those signatures can contain interesting properties that can be used as Indicators Of Compromise (IOC) by analysts or used to perform some large-scale analysis on a lot of samples. As an example, let’s use the recent signed dridex sample sample (5df62149bb91084eb677aecff7a8ca5fffeaaa23).

On Windows the Portable Executable file format uses IMAGE_DIRECTORY_ENTRY_SECURITY to store the information which corresponds to the 5th IMAGE_DATA_DIRECTORY. Let’s use radare2 to dump the certificate using pf command.

Command construction

First we need to open our binary in a special mode using the -nn flag (Only load the rbin structures) : r2 -nn binary.exe.

By default you can notice that some structure are already parsed for certain filetype (PE, ELF, MachO, …), you can display those structures using pf. (see also the article: parsing a fileformat with radare2.

  1. pf.pe_nt_image_headers32 will parse a structure of the type pe_nt_image_headers32 at the current offset

  2. pf.pe_nt_image_headers32 @ pe_nt_image_headers32 will parse a structure of the type pe_nt_image_headers32 at the offset which should contain pe_nt_image_headers32, if we type this command we should get our array of struct<pe_image_data_directory> displaying virtualAddress and size.

  3. Using pfv we are able to retrieve the actual value parsed: pfv.pe_nt_image_headers32.optionalHeader.dataDirectory[4].virtualAddress @ pe_nt_image_headers32 should return the virtualAdress and pfv.pe_nt_image_headers32.optionalHeader.dataDirectory[4].size @ pe_nt_image_headers32 the size of the IMAGE_DIRECTORY_ENTRY_SECURITY.

  4. Now we need to dump IMAGE_DIRECTORY_ENTRY_SECURITY, using pr (print N raw bytes), we can pass the two command as argument of pr. Be careful to use the backticks `` that indicates we want to execute the sub-commands before, and also add +8 to the virtualAddress value.

pr `pfv.pe_nt_image_headers32.optionalHeader.dataDirectory[4].size @ pe_nt_image_headers32` @ `pfv.pe_nt_image_headers32.optionalHeader.dataDirectory[4].virtualAddress @ pe_nt_image_headers32`+8 > pe_certificate
  1. Ok now that the parsing has taken place, let’s try to decode the certificate using openssl: openssl pkcs7 -inform DER -print_certs -text -in pe_certificate
[0x00000000]> pr ...

Certificate:
    Data:
        Version: 3 (0x2)
        Serial Number:
            9f:ea:c8:11:b0:f1:62:47:a5:fc:20:d8:05:23:ac:e6
    Signature Algorithm: sha1WithRSAEncryption
        Issuer: C=US, ST=UT, L=Salt Lake City, O=The USERTRUST Network, OU=http://www.usertrust.com, CN=UTN-USERFirst-Object
        Validity
            Not Before: May  5 00:00:00 2015 GMT
            Not After : Dec 31 23:59:59 2015 GMT
        Subject: C=GB, ST=Greater Manchester, L=Salford, O=COMODO CA Limited, CN=COMODO Time Stamping Signer
        Subject Public Key Info:
            Public Key Algorithm: rsaEncryption
                Public-Key: (2048 bit)
                Modulus:
                    00:bc:35:a0:36:70:22:81:11:c3:b2:83:b9:d3:28:
                    c6:36:cd:25:6b:a9:7b:b2:1c:f6:9b:51:9c:ef:35:
                    f4:ed:08:8e:5e:38:08:f8:77:3c:0a:42:e0:f3:70:
                    ....
...
  1. TADA! We can also use this one liner and add such command to a dedicated HUD for future use:
pr `pfv.pe_nt_image_headers32.optionalHeader.dataDirectory[4].size @ pe_nt_image_headers32` @ `pfv.pe_nt_image_headers32.optionalHeader.dataDirectory[4].virtualAddress @ pe_nt_image_headers32`+8 > pe_certificate ; !openssl pkcs7 -inform DER -print_certs -text -in pe_certificate