Writing Yara Rules for Fun and Profit: Notes from the FireEye Breach Countermeasures
By now, everyone should have seen that FireEye got breached and their red team tools got stolen. What is truly unique about their response is publishing detection rules to detect the use of those tools in the wild. However, the nature of some of those rules is that the detection will be short-lived. This isn't necessarily a fault of FireEye (as I will explain below) but it is useful as an exercise in writing Yara rules (or Snort, HXIOC, STIX, et al).
For programs, particularly compiled ones, you can think of it as seperated into three parts: the stuff that makes the program do what it does, free-form text for output (error messages, requests for user input, filenames, etc), and metadata (i.e. compiler artifacts). All of these can be modified, of course, but the most difficult to modify is the "stuff that makes the program go". To do that, you need a strong understanding of the code, what the functions do, what returns are expected, and what the expected inputs are. Changing metadata or free-form text, on the other hand, doesn't affect the running of the program and detection that relies on that is transient. An attacker can simply go in, search for the strings or metadata and change them with the tooling of their choice all with no real understanding of the underlying code.
Many of FireEye's countermeasures look for free-form text or metadata and not the functioning of the tool which is why those countermeasures will likely be short-lived. That doesn't mean, however, FireEye's approach is wrong.
For instance, the below Yara rule is detecting a specific GUID for a specific project in a .csproj file. This GUID likely has nothing to do with how Rubeus actually works so it can be arbitrarily changed.
rule HackTool_MSIL_Rubeus_1 { meta: description = "The TypeLibGUID present in a .NET binary maps directly to the ProjectGuid found in the '.csproj' file of a .NET project. This rule looks for .NET PE files that contain the ProjectGuid found in the public Rubeus project." md5 = "66e0681a500c726ed52e5ea9423d2654" rev = 4 author = "FireEye" strings: $typelibguid = "658C8B7F-3664-4A95-9572-A3E5871DFC06" ascii nocase wide condition: uint16(0) == 0x5A4D and $typelibguid }
The approach to writing rules based on metadata or free-form text is not wrong, depending on the purpose you write the rules for. You can either write rules to detect tools or you can write tools to detect actors. Both are useful, but which you use depends on your specific objective.
Among other things, FireEye does good work in attribution of nation-state attacks. When doing the work of intelligence analysis, I love free-form text. It is extremely hard for an attacker to put in free-form text that doesn't give me something to work with, especially when there is a reasonably sized body of text to work with. For instance, an attacker using the word "colour" vs "color" tells me (potentially) something, namely whether they speak American or speak proper English. I have no inside knowledge as to how FireEye writes their rules, but it comes as little surprise to me that many of the rules are written in a way that seems more calibrated to detect actors than tools. For instance, lots of actors use Cobalt Strike, but if I want to know who is behind a specific campaign, I need to look for other things than just the use of Cobalt Strike.
Both approaches are valid, but likely most enterprises are more interested in detecting the use of FireEye's specific tools rather than the attributive information associated with this tools, so other techniques will be needed. That said, both approaches are still valid for their defined use-case, as long as you are intentional about which road you want to travel (or that you're traveling both roads simultaenously by having rules that have both approaches).
However, writing detection logic and thinking through things can sometimes be lacking which leads to completely ineffective rule writing. One of my favorite Snort rules for this example is SID 1239.
# alert tcp $EXTERNAL_NET any -> $HOME_NET 139 (msg:"OS-WINDOWS RFParalyze Attempt"; flow:to_server,established; content:"BEAVIS"; content:"yep yep"; metadata:ruleset community; reference:bugtraq,1163; reference:cve,2000-0347; reference:nessus,10392; classtype:attempted-recon; sid:1239; rev:14;)
This is obviously an old rule on CVE-2000-0347. It was discussed on Bugtraq back in the day and you can read the disclosure with a PoC here. What this PoC does is popup a window on a remote vulnerable Windows machine with the title "BEAVIS" and the content "yep yep" (the comment in the PoC is wrong, it is "yep" not "yeh". This Snort rule doesn't detect the exploit, it only detects the PoC payload which means any weaponized use of that exploit would be undetected. All that it would detect is the script kiddies lurking on Bugtraq who downloaded the PoC and launched it against the Internet (in fairness, this was a very common thing to happen back in the bugtaq full disclosure days). However, if I am an enterprise looking to protect myself, this rule gives me nothing. No serious attacker is going to launch popups on remote workstations with Beavis and Butthead references.
So for those of you writing Yara, Snort, or other detection rules, be mindful of what objective you are trying to accomplish with the detection and pick your logic based on what is hopefully enduring and not transitory features of the attacker.
--
John Bambenek
bambenek \at\ gmail /dot/ com
Bambenek Labs - Well Fed Intelligence
Comments