Monday, 28 September 2015

Carving Scheduled Tasks (*.JOB)

This is continuation to my previous post about carving AT jobs. The end result is a Python script for carving any .JOB files:

As a reminder, the methodology for carving AT jobs relied on a specific string that remained the same across all AT jobs, and therefore was fairly efficient. Once we had a hit on the string, all we needed to do was to identify the beginning of the job file and carve an arbitrary amount of data that was large enough not to trim the data structure.

This time I decided to look for JOB files by searching for the Fixed Length data section using a regular expression. Once that’s identified, the code performs sanity checks on the section, determines where the Variable Length section finishes and runs extra sanity checks on it. Then it’s just a matter of writing both sections into a file.

The Regex
After studying each field in the Fixed Length data section and testing my theories against various memory images, I determined which fields should remain constant or have predictable values. Constructing a regular expression that would look for data that matches these requirements was relatively easy, e.g. two bytes that represent month can only have values between 1 and 12 and therefore the regex would be “[\x01-\x0c]\x00]” (little-endian format). In the back of my head I kept the idea to make it efficient while keeping the number of false positives low. It’s therefore I went with an approach to simplify the matching regex and be more relax on some fields, and once it yields a hit, to perform extra verification. The end result are 2 extra regular expressions that verify the values in RunDate and Priority fields.

Variable Length Data Section
Once the Fixed Length data section has been found, I could take a generic approach and carve an arbitrary amount of data that follows it, because tools like Gleeda’s Job Parser would ignore the excessive bytes. That’s also the approach I took when developing the AT Jobs carver. This time however, I decided to parse the fields to determine where the section ends and only carve out the bytes that belong to the JOB file. This approach allowed to perform further sanity checks allowing to reduce the number of false positives even further.

D:\Tools\job carver> "memory.img" carved_files
[*] Searching...
[+] Found hit: 0x20d47310
 Written: test\carved_1.job
[+] Found hit: 0x24e1f8a0
 Written: test\carved_2.job
[+] Found hit: 0x5ab997a8
 Written: test\carved_3.job
[+] Found hit: 0x5e55d000
 Written: test\carved_4.job
[+] Found hit: 0x649a9d07
[-] Failed verification
[+] Found hit: 0x86aff1d8
 Written: test\carved_5.job
[+] Found hit: 0xde52a0f8
 Written: test\carved_21.job
[*] Done

D:\Tools\job carver> –d carved_files > job_files_analysis.txt

D:\Tools\job carver>

Have fun fighting evil and let me know if you encounter any problems!

PS. It would be great I found time to incorporate the two parsers into a Volatility plug-in…

Tuesday, 8 September 2015

Carving AT Job Files

The post explains why it’s important an incident responder checks scheduled tasks for signs of lateral movement and most importantly, how to carve unnamed scheduled tasks (aka “At jobs”) from a blob of data, such as a physical memory image or page file. The Python script for carving those JOB files is available here:

When the attackers move laterally across the network, it’s a common practice to use the built-in utilities to stay under the radar, e.g. “makecab” and “expand” instead of WinRAR (for more examples, see Patrick Olsen’s post). It’s therefore frequent to see the attackers drop their tools on a remote system via a network share and then use the command line utility “at” to schedule tasks that will execute them. Some attackers clean up their tools and others take it even a step further and try to remove the artefacts left in the system – event logs, entries in “schedlgu.txt”, and At#.job files, to name a few. What follows is a short story that illustrates when carving job files from a memory image

When investigating a Windows system recently I discovered some JOB files that confirmed the attackers’ activity but I suspected some of the older JOB files might be missing. Since the system wasn’t shut down for over 100 days I decided to take an image of the physical memory and see if I can dig them up.

I remembered that At1.job file that already existed on the box had a Unicode text (“Created by NetScheduleJobAdd”) that appeared very unique. Luckily for me, searching it across the memory image yielded hits and soon I discovered new commands that were executed on the system. I didn’t even have to carve the job files because the commands were also in Unicode and closely preceded the comment I used for searching.

The only piece of a puzzle that was missing were the timestamps the scheduled jobs were created and executed on. For this, and other metadata, I relied on Gleeda’s JOB file parser. She also very kindly wrote a blog post describing the file’s data structure and referencing the MSDN’s “JOB File Format”. Once I tinkered around, I figured that I needed to grab 0x48 bytes before the command (most of the time it was “cmd.exe”) and an arbitrary amount following it (but enough so the structure doesn’t get corrupted) as the parser will ignore the excess bytes.

After having recovered 5 out of 10 JOB files I found, I started wondering how feasible would it be to automate the carving process, making sure that it works even if the command wasn’t “cmd.exe”. I quickly Googled for carving JOB files but a mention of the manual carving process on the SANS blog was all I could find, and so I decided to give it ago myself. The main obstacle was identifying where does the data structure start and this was overcame by the observation that two sequential fields that are always at the same offset – Error Code and Status, have fixed values. This led me to the idea that we can identify the starting position by finding bytes that match the values of these two fields, precede the comment and are in close proximity.

This resulted in a carver that is available on GitHub and worked well when tested on raw memory images of Windows 2003 SP2 x86 and Windows Server 2008 SP2 x64. I don’t suspect the comment has changed between Windows versions but please do let me know if that’s not the case. Below is an example of running the script:

** Memory image of a clean VM **
bart:~ bart.inglot$ python before.raw ~/jobs
[*] Created output folder: /Users/bart.inglot/carved_jobs
[*] Searching...
[*] Done

** Memory image of a VM after creating an AT job **
bart:~ bart.inglot$ python after.raw ~/jobs
[*] Searching...
[+] Found hit: 0x3a95d000
[-] Failed verification
[+] Found hit: 0x7a5ad518
[*] Done
bart:~ bart.inglot$ cat ~/jobs/carved_1.job
I�GW� M����
       !cmd.exeZ/c "\\WIN-TESTBOX\c$\Windows\System32\netstat.exe > \\WIN-TESTBOX\c$\netstat.txt"SYSTEMCreated by NetScheduleJobAdd0�

As mentioned earlier, the carving process relies on finding the comment that is unique to JOB files created by the “at” utility, and therefore it won’t find JOB files created by other means (e.g. GUI or “schtasks.exe”).

Have fun and find moar evil!