You can buy appliances to put in your network in an effort to find evil on systems in your enterpise. I know a wicked smart individual who develops one such system and I strongly recommend you check them out, especially if you can afford them. The one I'm thinking of rhymes with "beer."
But let's say you didn't budget for one of these systems this year, there's still something you can cobble together using Autoruns, Psexec, Cygwin and VirusTotal. It may not be as effective or capable as the system that rhymes with "beer," but it's going to be useful. Let's get to it.
I've written about Autoruns before so if you're not familiar with it, check out the link above and this post about how attackers maintain persistence. Psexec is another Microsoft Sysinternals tool that you can use to execute commands on remote hosts. If you're an incident responder or system administrator, having the ability to "psexec" into remote systems is a must.
Cygwin is "a collection of tools which provide a Linux look and feel environment for Windows." If you follow the outstanding, Command Line Kung Fu Blog, you know well that what's relatively easy at the command line in Linux can be far more difficult to achieve using built in tools in Windows. Installing Cygwin will facilitate our little project here. Alternatively, if you have a Linux box, you can use it instead.
VirusTotal is a great service where you can upload binaries and have them scanned by 40+ antivirus tools to see if any of them recognize the binary as something malicious. Too many people don't know that in lieu of uploading a binary to VirusTotal, you can take an MD5, SHA1 or SHA256 hash of a binary and search for that value on the site. VirusTotal will return a report showing how many antivirus scanners recognize a file with that same hash as a malicious file. See the footnote at the end of this article for a reason why you may not want to immediately upload suspicious binaries to VirusTotal for analysis.
Conveniently, Autoruns can be configured to generate MD5, SHA1 and SHA256 hashes. Combine that chocolate, with the flavor that is VirusTotal and you've got yourself a nice bit of kit for finding evil. Where do Psexec and Cygwin fit into this? With Psexec and a for loop, we can collect Autoruns data from many hosts in a few minutes. Mind the wraps.
for /L %i in (1, 1, 254) do @psexec -s -n 4 -d \\n.n.n.%i cmd /c "net use o: \\server\share PASSWORD /user:doman\username && \\live.sysinternals.com\tools\autorunsc -a -v -f -c '*' > o:n.n.n.%i.csv && net use o: /delete"
Let's break this down. First the for loop is going to count from 1 to 254 and assign that value to the variable %i. Within the loop we run psexec with -s -n 4 and -d options, these will run commands on the remote system as SYSTEM, timeout after 4 seconds if it can't connect and lastly, -d runs the commands non-interactively, think of it as -d for "dropping" the command on the system and moving on.
Next is the IP address of the remote host -- \\n.n.n.%i. You can run this loop inside another loop to cover more than one octet at a time (i.e. \\n.n.%j.%i and so on). Next comes the command we want to run on the remote host, in this case it is a compound command (i.e. a command shell followed by another command (i.e. cmd /c...)). In this case, the compound command that follows first maps a drive to some share somewhere in your environment, this may require that you supply credentials, depending on how your environment is configured.
Having mapped the drive, we call Autorunsc (note the trailing c there indicates the command line version). The flags and arguments provided here, -a -v -f -c '*', have the following effects respectively: collect all Autoruns, verify certificates for signed code, create file hashes, write the output as comma separated values and lastly gather Autoruns data for all profiles on the system. We redirect the output to the drive that we mapped, naming the file for the IP address of the system that it came from and lastly, we delete the drive mapping.
Depending on how you do this, you'll have a single system's Autoruns data or the data from many systems. Now we want to analyze all of this data to see if we can find any malicious binaries in the mix. Since we told Autorunsc to verify signed code, we can make a possibly horrible decision and direct our attention to only the unsigned code. The assumption here is that only legit code will be signed and that malicious code will be unsigned. There have been examples of malicious code that was signed and I suspect the future will bring more and more of the same. But for demonstration purposes, I'm only going to analyze unsigned code.
If you have a single Autoruns output file, rename it to aruns.csv and drop it into the same directory as the following script, which you can download from my git repo [RECOMMENDED]. You'll need Cygwin or a system with bash, grep, awk and wget for this:
If you have a bunch of Autoruns output from multiple hosts, you can combine them with a little command line foo as follows:#!/bin/bash # A working proof of concept, lacking many features # Remove old VirusTotal results files=$(ls *.html 2>/dev/null | wc -l) if [ "$files" != "0" ]; then ls *.html | xargs rm -rf fi # Gather all hashes for unsigned code from autoruns csv output file named aruns.csv grep -i "(Not Verified)" aruns.csv | awk -F, '{print $(NF-2)}' | sort | uniq > aruns_hashes # Reduce the data set to hashes that aren't in our good list if [ -e hashes_cleared ]; then grep -vif hashes_cleared aruns_hashes > hashes2check else mv aruns_hashes hashes2check fi # Should create a list of bad hashes and check against it too if [ -e hashes_evil ]; then grep -if hashes_evil hashes2check > aruns_malware_hashes fi # Remove malware hashes from hashes2check if [ -e aruns_malware_hashes ]; then grep -vif aruns_malware_hashes hashes2check > vtsubmissions else mv hashes2check vtsubmissions fi # Search VirusTotal for reports on remaining hashes echo "[+] $(wc -l vtsubmissions) hashes to check with Virus Total" sleep 2 for i in $(cat vtsubmissions); do wget --header= -O $i.html --no-check-certificate \ https://www.virustotal.com/latest-scan/$i; sleep 15; done # Check results for malware grep -l "[1-9][0-9]* / " *.html | awk -F. '{print $1}' | tee -a aruns_malware_hashes \ >> hashes_evil # Pull out malware entries from aruns.csv grep -if aruns_malware_hashes aruns.csv > aruns_malware # Check results for non-malicious files grep -l "0 / " *.html | awk -F. '{print $1}' >> hashes_cleared # Check for results tnat are unknown to VT grep -li "not found" *.html | awk -F. '{print $1}' > unknowns # Pull unkown entries from aruns.csv grep -if unknowns aruns.csv > aruns_unknown # Report results let j=$(wc -l aruns_malware) echo "[+] VirusTotal shows $j Autoruns entries may be malicious." echo "[+] Check the aruns_malware file for details." let j=$(wc -l aruns_unknown) echo "[+] VirusTotal has never seen $j Autoruns entries." echo "[+] Check the aruns_unknown file for details." echo
cat n.n.n.* | sort | uniq > aruns.csvYou'll need to edit this aruns.csv file and remove the header line created by Autorunsc, search for MD5 to find the header line. Now place that file in the same directory as the script above and you'll be all set.
What does the script above do? It pulls out all of the MD5 hashes for unsigned Autoruns, compares them against a list of known good hashes from previous runs, if this is your first run through with the script, the file won't exist and this will be skipped. Next it compares those hashes against hashes of known malicious files, again, if this is your first run, there will be nothing to compare against and this step will be skipped. Known malicious hashes will removed from the list and saved for later notification. Whatever hashes are left will be submitted to VirusTotal as search strings at the public API rate of four hashes per minute, the results from VirusTotal will be written to files named for the hashes with .html extensions added.
Once all the hashes have been submitted to VirusTotal, the script will search through all the results looking for any that were reported as malicious by the antivirus products. Those hashes will be written to the same file as any that had previously been marked as malicious.
Then the script looks through the html files for results where none of the antivirus products found the hash to match a malicious file, these hashes are saved into the hashes_cleared file and they will not be submitted to VirusTotal on future runs.
The script then searches through the results from VirusTotal for any reports that indicate no file with the provided hash has been submitted for analysis. These hashes are marked as unknowns and may warrant further analysis, possibly even submitting the files to VirusTotal (see the footnote below).
Finally, the script reports to the user how many of the hashes were reported to match malicious files and how many were unknown. It pulls these Autoruns entries from the aruns.csv file so you can have the reduced data set for analysis.
Below are some screen shots of the script, which I'm calling "lamelyzer.sh," pronounced lame-ah-lyzer:
Figure 1: lamelyzer's first run as evidenced by the lack of data files. |
Figure 2: lamelyzer reports there are 114 hashes to submit to VirusTotal and begins making requests |
Figure 3: Directory listing while lamelyzer is in progress. Each html file is a VirusTotal report. |
Figure 4: lamelyzer has finished and is showing results. |
Figure 5: Post execution directory listing of non-html files. |
Figure 5 shows a directory listing after the lamelyzer script has finished. When we started there were two files, the script itself and the aruns.csv file. Now we have several new files, aruns_malware will contain the Autoruns entries that some antivirus product recognized as malicious; aruns_malware_hashes contains the hashes for those files; aruns_unknown contains those Autoruns entries that had MD5 hashes that didn't match any files that VirusTotal had seen before, these may warrant further investigation; hashes_cleared contains a list of hashes that have been scanned by antivirus products at VirusTotal and the results came back clean, in future runs, hashes matching entries in this file will not be submitted to VirusTotal; hashes_evil contains the hashes for files that VirusTotal said were malicious, in future runs hashes matching entries in this file will not be submitted to VirusTotal, they will however be reported to the user; unknowns contains the hashes for files VirusTotal hasn't seen before; and vtsubmissions contains the list of hashes that were submitted to VirusTotal.
On subsequent runs hashes will be appended to hashes_cleared and hashes_evil as appropriate. All the other data files will be overwritten. If you want to see what VirusTotal says about a particular file, open the corresponding html file in a web browser. When you're finished reviewing the results, delete the html files. The next time you need to analyze Autoruns output, copy it into the directory as aruns.csv and run lamelyzer again. Known good and bad files will be filtered out and reported accordingly, all others will be submitted to VirusTotal with results reported accordingly.
Figure 6: A subsequent run of lamelyzer with an aruns.csv with 838 entries, only 77 will be submitted to VirusTotal. |
In Figure 6, I've collected another set of Autoruns data from multiple systems, 838 entries in total, but due to the existence of the hashes_evil and hashes_cleared files, only 77 of the 838 entries will have their hashes submitted to VirusTotal.
If you compile many sets of Autoruns data into one aruns.csv file, as I have, you can map a particular entry back to the host(s) that it came from by grepping through the original csv files for the hashes in question. Recall near the beginning of this post, the for loop that wrote Autoruns data to files named for the IP addresses of the hosts they came from, simply grep through those files for the hash in question.
I have to admit that lamelyzer was given its name because it was a hastily assembled proof of concept for a more robust tool I've been working on, but some folks that I'd talked to about it wanted more information on what I was planning to do. Rather than put together slides or whiteboard it, I spent a few minutes putting this script together. It works well enough, that I think many could put it to good use. I will still work on a more robust tool with more options, but wanted to get this out.
If you have any questions or comments, please don't hesitate to let me know.
* There are reasons why you should not immediately upload a potentially malicious file to VirusTotal. If I'm an attacker and I'm targeting your organization, I may create custom malware or repackage some existing malware in such a way that it has a unique set of MD5, SHA1 and SHA256 hashes. Once I've dropped my kit in your network, I can monitor VirusTotal by searching for my hashes. If VirusTotal comes back with a report for any one of those hashes, then I know someone has submitted the binary to VirusTotal (or there's a collision with another file) and therefore, I know that your organization has found my kit and that it's time for me to switch things up.
MIR
ReplyDeleteThe big problem with this technique is that MS Sysinternals Autoruns relies on Windows API calls, which are easily intercepted by root kits. TDSS Rootkit is one common malware beast that is known to intercept Windows API calls and the file hashes that are reported by Autoruns are bogus. The file will even show that it has a valid code signing certificate. It would be nice if Autoruns bypassed the Windows API in favor of raw device access for its hash calculations.
ReplyDeleteGreat post and scripting, interesting approach. I didn't know about the hashes feature of autoruns, thanks!
ReplyDeleteOne comment:
# Check results for non-malicious files
grep -l "0 / " *.html | awk -F. '{print $1}' >> hashes_cleared
Wouldn't that match also for 10, 20, 30, 40 detections and not just for 0 ??
You wouldn't want to treat 40 detections the same as 0, right? ;-)
I had a similar idea using the persistence audit of Mandiant's Redline Collector (previously IOC-Finder), getting unique MD5 of all restisted files per host, remove known good hashes from the list (using a trusted whitelist), looking for the rarest MD5's on all hosts from a network. I can share more details if interested...
Cheers,
@c_APT_ure