FireEye's Operation Ephemeral Hydra: IE Zero-Day Linked to DeputyDog Uses Diskless Method, posted 10 NOV 2013 is specific to an attack that "loaded the payload directly into memory without first writing to disk." As such, this "will further complicate network defenders’ ability to triage compromised systems, using traditional forensics methods." Again, what is described is a malware sample (payload) that " does not write itself to disk, leaving little to no artifacts that can be used to identify infected endpoints." This FireEye analysis is obviously getting its share of attention, but folks are likely wondering "how the hell are we supposed to detect that on compromised systems?"
Question: Why does Volatility rule?
Answer: Because we don't need no stinking file system artifacts.
In preparation for a Memory Analysis with Volatility presentation I gave at SecureWorld Expo Seattle last evening, I had grabbed the malware sample described in great length by FireEye from VirusShare (MD5 104130d666ab3f640255140007f0b12d), executed it on a Windows 7 32-bit virtual machine, used DumpIt to grab memory, and imported the memory image to my SIFT 2.14 VM running Volatility 2.3 (had to upgrade as 2.2 is native to SIFT 2.14).
I had intended to simply use a very contemporary issue (3 days old) to highlight some of the features new to the just released stable Volatility 2.3, but what resulted was the realization that "hey, this is basically one of the only ways to analyze this sort of malware."
So here's the breakdown.
The FireEye article indicated that "this Trojan.APT.9002 variant connected to a command and control server at 111.68.9.93 over port 443."
Copy that. Ran vol.py --profile=Win7SP1x86 -f WIN-L905IILDALU-20131111-234404.raw netscan and quickly spotted 111.68.9.93 as seen in Figure 1.
Figure 1 |
Figure 2 |
The article states that "after an initial XOR decoding of the payload with the key “0x9F”, an instance of rundll32.exe is launched and injected with the payload using CreateProcessA, OpenProcess, VirtualAlloc, WriteProcessMemory, and CreateRemoteThread."
Ok, so what is PID 3176 associated with? vol.py --profile=Win7SP1x86 -f WIN-L905IILDALU-20131111-234404.raw pslist | grep 3176 will tell us in Figure 3.
Figure 3 |
Strings can help us for the next step to spot CreateProcessA, OpenProcess, VirtualAlloc, WriteProcessMemory, and CreateRemoteThread as associated with PID 3176. The Volatility wiki recommends using Sysinternals strings so we can use –q and –o switches to ensure that the header is not output (-q) and that there is an offset for each line (-o), as in strings -q -o WIN-L905IILDALU-20131111-234404.raw > strings.txt. We then convert strings.txt for Volatility with vol.py --profile=Win7SP1x86 -f WIN-L905IILDALU-20131111-234404.raw strings -s strings.txt --output-file=stringsVol.txt. Now we can search for strings that include 3176 and the likes of CreateProcessA, along with offsets to see if there are associations. A search immediately produced:
04cfce5a [3176:701f8e5a] CreateProcessA
abd60bd8 [3176:00191bd8] OpenProcess
abd60ae4 [3176:00191ae4] VirtualAlloc
bedd8384 [3176:10002384] WriteProcessMemory
bedd835a [3176:1000235a] CreateRemoteThread
What we've just validated is that PID 3176 (rundll32.exe) shows indications of the five functions described by FireEye.
Per the article, "inside the in-memory version of the Trojan.APT.9002 payload used in this strategic Web compromise, we identified the following interesting string: “rat_UnInstall”. Gotcha; a quick string search says: bd75bcc0 [3176:0035fcc0] __rat_UnInstall__3176.
The rat_UnInstall IOC is clearly associated with PID 3176.
Just for giggles, I checked one last point made by FireEye. They stated that "we also found the following strings of interest present in these 9002 RAT samples (excluding the in-memory variant): McpRoXy.exe, SoundMax.dll
I was intrigued by the "excluding the in-memory variant claim", so I did I quick check. I could, as always, be wrong (tell me if I am), buy the dlllist module seems to disagree.
vol.py --profile=Win7SP1x86 -f WIN-L905IILDALU-20131111-234404.raw dlllist -p 3176 | grep SoundMax.dll produced Figure 4.
Figure 4 |
While I am operating on the belief that my analysis of 104130d666ab3f640255140007f0b12d matches the FireEye IOCs via Volatility memory analysis alone, dlllist does indicate that the malware drops SoundMax.dll on the file system. I attribute this to the possibility that my "delivery system" was different than the IE 0-day FireEye describes; I had to download the sample and execute it to replicate behavior.
Correction 15 NOV 2013: Ned Moran from FireEye contacted me to let me know that my assumption based on interpretation of the FireEye blogpost was incorrect. 104130d666ab3f640255140007f0b12d is not the diskless version of 9002; at this time FireEye is not providing hashes or sharing that sample at this time. I clearly misinterpreted their post to indicate that 104130d666ab3f640255140007f0b12d was that sample, I was incorrect and I apologize. That being said Ned assured me that I was not out of my mind and let me know "yes, my reading of your methodology is that it would have produced very similar results, the only difference being that had you would not have found the 'SoundMax.dll' string in the diskless version. So, your approach was sound you were just looking at a different sample."
Regardless, we wouldn't need any file system artifacts to confirm the presence of the diskless, memory-only version of Trojan.APT.9002 on a victim system.
Confirmed connection to 111.68.9.93 with netscan:
vol.py --profile=Win7SP1x86 -f WIN-L905IILDALU-20131111-234404.raw netscan
Confirmed timeline for connection to 111.68.9.93 with timeliner:
vol.py --profile=Win7SP1x86 -f WIN-L905IILDALU-20131111-234404.raw timeliner --output=body --output-file=output.body
Identified rundll32.exe as owner of the suspect PID (3176) with pslist:
vol.py --profile=Win7SP1x86 -f WIN-L905IILDALU-20131111-234404.raw pslist | grep 3176
Used strings as analysis to further confirm:
vol.py --profile=Win7SP1x86 -f WIN-L905IILDALU-20131111-234404.raw strings -s strings.txt --output-file=stringsVol.txt
Used dlllist to call out SoundMax.dll:
vol.py --profile=Win7SP1x86 -f WIN-L905IILDALU-20131111-234404.raw dlllist -p 3176 | grep SoundMax.dll
One more time, with feeling: Why does Volatility rule? Hopefully, I've helped answer that...again.
Cheers!