Extracting Digital Signatures from Signed Malware with pf

September 3, 2015


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

5) 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 ...

        Version: 3 (0x2)
        Serial Number:
    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
            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)

6) 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