With the release of YARA 3.2.0 I wanted to show people how to utilize some of the new features. In particular I'll show an example for rich_signatures, import hashing and Authenticode signatures.
You'll need both of these for obvious reasons. :)
import "pe"
import "hash"
Now on to the rules. This is an example of combining the hash module with the parsing from the PE module.
rule rich_signature_hash {
condition:
hash.sha256(pe.rich_signature.clear_data) == "3637ce56e03203a0f0b29b5ebbac570cfeeb32b40700c37d5a620c5f3eea7cad"
}
The import hashing is slightly different as it hashes the parsed import table, as opposed to a straight forward blob like the previous example. This is why it is a function and not a blob of data.
The hashes generated by pefile should be identical to the ones generated by YARA.
rule imphash {
condition:
pe.imphash() == "11dcb25f7dfdb6827f7b885863da82bd"
}
Now on to signed binaries...
First thing to note is that although very rare you can technically have more than one signature in a binary. This is why you have to do the "for any i in..." dance. Under the hood the signatures are stored in a list of structures.
Second thing to notice is that YARA parses the X509 certificates stored in the binaries, and nothing else. Keep this in mind when using other tools which may be parsing other parts of the Authenticode structures.
The third thing to notice is that "issuer" and "subject" are stored in the OpenSSL forward slash format, as opposed to the more commonly displayed comma separated format. Keep this in mind when writing signatures.
This rule does a bunch of things, but there are some details to point out which may illuminate some interesting use cases. When it comes to searching strings stored in modules (as opposed to ones defined in the string section of a rule) you can do either a substring match or an exact match. In this example, "issuer" is an exact match using "==", while "subject" is a substring match using "contains".
rule specific_signature_test {
condition:
for any i in (0..pe.number_of_signatures - 1):
(pe.signatures[i].issuer == "/C=GB/ST=Greater Manchester/L=Salford/O=COMODO CA Limited/CN=COMODO Code Signing CA 2" and
pe.signatures[i].version == 3 and
pe.signatures[i].subject contains "Athinodorou" and
pe.signatures[i].serial == "e2:47:ea:06:60:29:b7:05:33:c1:57:92:b6:0e:d4:d8" and
pe.signatures[i].not_before == 1409184000 and
pe.signatures[i].not_after == 1440806399)
}
Since not_before and not_after are just integers you can do more than equality comparisons. You can use this to find all signed binaries valid for a given range.
rule valid_test {
condition:
for any i in (0..pe.number_of_signatures - 1):
(pe.signatures[i].not_before > 1409184000 and
pe.signatures[i].not_after < 1440241723)
}
If you just have a single date and you want to find all signed binaries for that date use "valid_on()".
// Given a posix timestamp argument, make sure not_before <= arg <= not_after
rule valid_on_test {
condition:
for any i in (0..pe.number_of_signatures - 1):
(pe.signatures[i].valid_on(1440241723))
}
In the future I'd like to eliminate the need to use integers. I'd ideally like to replace them with a "date()" which will do the conversion for you.
If there are any questions on this or things aren't working please let me know via [email protected]. Thanks!
-- WXS