Thursday, November 14, 2013

Volatility 2.3 and FireEye's diskless, memory-only Trojan.APT.9002

If you needed more any more evidence as to why your DFIR practice should evolve to a heavy focus on memory analysis, let me offer you some real impetus.
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
 I was interested in putting timeliner through its paces as it is new to Volatility in 2.3, and was not disappointed. Issued vol.py --profile=Win7SP1x86 -f WIN-L905IILDALU-20131111-234404.raw timeliner --output=body --output-file=output.body and spotted 111.68.9.93 in network connections tied closely to a timestamp of 1384212827? Er? That's Unix timestamp. Translated to human readable: Mon, 11 Nov 2013 23:33:47 GMT. Check! See Figure 2.

Figure 2



Clearly PID 3176 is interesting, keep it in mind as we proceed.
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
What, what?! It's rundll32.exe. Mmm-hmm.
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
When I checked the file system for C:\users\malman\SoundMax.dll, it was indeed present.
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!

2 comments:

Anonymous said...

do you know if fireye has released the hash for the diskless version.

Russ McRee said...

Not that I'm aware of, any the issue is compounded by the fact that a BUNCH of samples were classified as Trojan.APT.9002 on the heels of some of this initial report. If you're interested because you'd like to analyze the sample, let me know, I can ask a few folks.

Moving blog to HolisticInfoSec.io

toolsmith and HolisticInfoSec have moved. I've decided to consolidate all content on one platform, namely an R markdown blogdown sit...