Dissect vs SysInternals Case, Part 2

If you’re curious about how dissect works, or why I took this approach to using it, have a look at my previous post.

Recap: Mini-Timelines

In Part 1, we hammered out an approach to pulling out a mini-timeline using dissect to get a bird’s-eye view for the 3 minutes surrounding the download event. The first approach, which saves all plugin output to the disk and reduces memory footprint, requires two commands to be run:

target-dump -o . --restart -f mft,evtx,usnjrnl,amcache.applaunches,amcache.application_files,amcache.applications,amcache.device_containers,amcache.drivers,amcache.files,amcache.programs,amcache.shortcuts,defender.evtx,defender.exclusions,defender.quarantine,shimcache,lnk,services,runkeys,shellbags,browser.history,browser.downloads,tasks SysInternalsCase.E01 

rdump --multi-timestamp -J -w -MSEDGEWIN10/shimcache/windows_shimcache.jsonl MSEDGEWIN10/runkeys/windows_registry_run.jsonl MSEDGEWIN10/services/windows_service.jsonl MSEDGEWIN10/shellbags/windows_shellbag.jsonl MSEDGEWIN10/usnjrnl/filesystem_ntfs_usnjrnl.jsonl MSEDGEWIN10/mft/filesystem_ntfs_mft_std.jsonl MSEDGEWIN10/mft/filesystem_ntfs_mft_filename.jsonl MSEDGEWIN10/lnk/windows_filesystem_lnk.jsonl MSEDGEWIN10/browser/browser_ie_history.jsonl MSEDGEWIN10/browser/browser_ie_download.jsonl MSEDGEWIN10/evtx/filesystem_windows_evtx.jsonl MSEDGEWIN10/amcache/windows_appcompat_InventoryApplicationFile.jsonl MSEDGEWIN10/defender/filesystem_windows_defender_evtx.jsonl MSEDGEWIN10/defender/filesystem_windows_defender_exclusion.jsonl MSEDGEWIN10/tasks/filesystem_windows_task_grouped.jsonl | rdump --csv -w combined.csv -s "r.ts and r.ts.year == 2022 and r.ts.month == 11 and r.ts.day == 15 and r.ts.hour == 21 and r.ts.minute in range(18,21)"

It’s not necessarily true that each plugin will succeed, so it would be best to enumerate .jsonl files rather than specify them.

We can do the same thing with a one-liner for the output of target-query, but it will all happen in memory. The following is the one-liner equivalent without dumping all plugin output to disk (Note: I had to omit the tasks plugin from this list, as a bug fix for one of its fields is still making its way downstream. You can still dump raw tasks using target-dump):

target-query -f mft,evtx,usnjrnl,defender,amcache,shimcache,lnk,services,runkeys,shellbags,browser.history,browser.downloads SysInternalsCase.E01 | rdump -w - --multi-timestamp | rdump --csv -w combined2.csv -s "r.ts and r.ts.year == 2022 and r.ts.month == 11 and r.ts.day == 15 and r.ts.hour == 21 and r.ts.minute in range(18,21)"

If you’re running target-query and want to see which plugin fails, if one does, use the --report-dir option. With the resources I allocated to my WSL VM, this command took about 25 minutes and we end up with over 17,200 records. I did a good amount of scrolling through this and filtering output from columns to get a bird’s eye view, but in order to show interesting artifacts, I’ll just show the output of a couple of commands during that time period to get the same effect.

Root Cause

The first references to SysInternals in our output come from the browser.downloads and browser.history plugins:

target-query -f browser.history,browser.downloads SysInternalsCase.E01 | rdump --multi-timestamp -w - | rdump -f "{ts} {url} {path}" -s "r.ts and r.ts.year == 2022 and r.ts.month == 11 and r.ts.day == 15 and r.ts.hour == 21 and r.ts.minute in range(18,21)" | sort -n
...
2022-11-15 21:18:33.308250+00:00 https://go.microsoft.com/ {path}
2022-11-15 21:18:33.308250+00:00 https://go.microsoft.com/fwlink/?LinkId=525773 {path}
2022-11-15 21:18:33.355026+00:00 ms-appx-web://microsoft.microsoftedge/ {path}
2022-11-15 21:18:33.355026+00:00 ms-appx-web://microsoft.microsoftedge/assets/errorpages/dnserror.html?DNSError=11001&ErrorStatus=0x800C0005&NetworkStatusSupported=1 {path}
2022-11-15 21:18:33.386221+00:00 ms-appx-web://microsoft.microsoftedge/assets/errorpages/dnserror.html?DNSError=11001&ErrorStatus=0x800C0005&NetworkStatusSupported=1 {path}
2022-11-15 21:18:33.761377+00:00 https://www.msn.com/ {path}
2022-11-15 21:18:33.808432+00:00 ms-appx-web://microsoft.microsoftedge/assets/errorpages/dnserror.html?DNSError=11001&ErrorStatus=0x800C0005&NetworkStatusSupported=1 {path}
2022-11-15 21:18:40.824183+00:00 http://www.sysinternals.com/SysInternals.exe {path}
2022-11-15 21:18:40.824183+00:00 http://www.sysinternals.com/SysInternals.exe {path}
2022-11-15 21:18:52.073889+00:00 http://www.sysinternals.com/SysInternals.exe C:\Users\Public\Downloads\SysInternals.exe

I’ve removed the record headers and most of the fields to save space, but the last record, from browser.downloads, shows that the file at the path C:\Users\Public\Downloads\SysInternals.exe was downloaded from the url www.sysinternals.com/sysinternals.exe and finished downloading at 2022-11-15 21:18:52. Unfortunately the start timestamp for this download is None, but since we have the URL being visited at 21:18:40, that seems a fair approximation of when the download began.

Seeing that there were some DNS errors nearby in the browser history, I felt it would be good to check the local hosts file for any oddities, if it is still present. This is no problem using dissect with target-fs.

target-fs SysInternalsCase.E01 cat "C:\Windows\System32\Drivers\etc\hosts"

# Copyright (c) 1993-2009 Microsoft Corp.
#
# This is a sample HOSTS file used by Microsoft TCP/IP for Windows.
#
# This file contains the mappings of IP addresses to host names. Each
...

192.168.15.10   www.malware430.com

192.168.15.10   www.sysinternals.com

It appears that the local hosts file has been modified to redirect sysinternals[.]com traffic to a local IP address listed here, which would explain the malicious file being downloaded from a fairly well-known domain (but sysinternals.com could have been compromised all the same). It could be worth it to add this IP and the domain malware430[.]com to our searches as well, at some point.

In any case, the search backwards for the root cause of this incident is probably coming to a close. We can determine the modification date of the hosts file according to the MFT for good measure.

As far as I’m aware, there are 2 main ways of getting the last modification date of a particular file using dissect. We can either use target-shell and once inside the “shell” we can use the stat command on the hosts file, like so:

target-shell SysInternalsCase.E01
MSEDGEWIN10 /> stat 'C:\Windows\System32\Drivers\etc\hosts'
  File: /C:/Windows/System32/Drivers/etc/hosts
  Size: 896
 Inode: 42564   Links: 1
Access: (0o777/-rwxrwxrwx)  Uid: ( 0 )   Gid: ( 0 )
Access: 2022-11-15T21:18:11.308264
Modify: 2022-11-15T21:17:03.567879
Change: 2018-09-15T07:31:36.585163

Or, we could instead use the mft plugin to retrieve timestamps, using rdump to select the particular file. This would take forever using target-query if we had not already dumped the parsed MFT to disk using target-dump earlier. I’m curious if these timestamps all agree, so I parsed the 2 jsonl output files on disk with the following command:

rdump --count 8 -F ts_type,ts -s "r.path.match('c:\\Windows\\System32\\drivers\\etc\\hosts')" MSEDGEWIN10/mft/filesystem_ntfs_mft_std.jsonl MSEDGEWIN10/mft/filesystem_ntfs_mft_filename.jsonl

The downside to this approach is that dissect uses the pathlib Python library, which has some quirks in regards to path naming. The drive path c: happens to be lowercase, and if you take this approach searching for a path and use an uppercase drive path, dissect will loop through both MFTs and return with no results.

Another thing to note is, I initially ran this command with --count 2 to have dissect stop after finding the record corresponding to this file in both the Standard_Info and Filename MFTs, but it turns out target-dump split the record into multiple records for each timestamp (or maybe I made some mistake in the original dump).

In any case, since we expect at most the 4 MACB timestamps for each of the two MFT entries, I upped the limit to 8. Here was the result:

<filesystem/ntfs/mft/std ts_type='B' ts=2018-09-15 07:31:36.585163>
<filesystem/ntfs/mft/std ts_type='C' ts=2022-11-15 21:17:03.567879>
<filesystem/ntfs/mft/std ts_type='M' ts=2022-11-15 21:17:03.567879>
<filesystem/ntfs/mft/std ts_type='A' ts=2022-11-15 21:18:11.308264>
<filesystem/ntfs/mft/fn ts_type='B' ts=2019-03-19 21:53:31.200783>
<filesystem/ntfs/mft/fn ts_type='C' ts=2019-03-19 21:53:31.200783>
<filesystem/ntfs/mft/fn ts_type='M' ts=2019-03-19 21:53:31.200783>
<filesystem/ntfs/mft/fn ts_type='A' ts=2019-03-19 21:53:31.200783>

What’s interesting is that all of the Filename timestamps for this file are aligned at a date that is in between the Birth date and the last Modification date. This could be a sign of timestomping, but could just as likely indicate that the file was copied or moved from another volume at that time.

What’s more, we can see that the Standard_Information timestamps basically align with the output we get from the stat command, the only differences being that the stat command only outputs the timestamps for file Access, Modification, and Change (of file inode). In this case the Change timestamp from Unix aligns with the Birth timestamp from Windows. Although the stat command gives us the reliable and informative Standard_Information timestamps, it behooves us to stick with the parsed MFT for more granular information.

For example, using the timestamps parsed by the MFT we can see that the Modification and Change timestamps align at 2022-11-15 21:17:03.567879, which gives us confidence that a file modification of the hosts file happened at that time. This folds into our narrative that the file was modified right before the download of the malicious executable.

We can already see via our mini-timeline that the birth timestamp for Sysinternals.exe in the user’s Downloads folder is approximately 2022-11-15 21:18:51 UTC. With this in mind, we can start working forward in time looking for signs of malicious activity and persistence on MSEDGEWIN10.

What About Defender?

Before going onto malicious action, I’m curious what Windows Defender thought of this file, if it was active. Let’s query using the defender plugin for the time in question:

target-query -f defender SysInternalsCase.E01 | rdump --multi-timestamp -w - | rdump -s "r.ts and r.ts.year == 2022 and r.ts.month == 11 and r.ts.day == 15 and r.ts.hour == 21 and r.ts.minute in range(18,21)"

Hmm, I got no output from this, as in no event log or timestamped records associated with quarantining or exclusion during our 3 minutes. Maybe there are few enough logs that we can look at the entire day instead. Let’s modify the query a bit:

target-query -f defender SysInternalsCase.E01 | rdump --multi-timestamp -w - | rdump -s "r.ts and r.ts.year == 2022 and r.ts.month == 11 and r.ts.day == 15"

<filesystem/windows/defender/exclusion ts=2022-11-15 21:17:00.942986+00:00 ts_description='regf_mtime' hostname=None domain=None regf_mtime=2022-11-15 21:17:00.942986+00:00 type='Paths' value='C:'>

So the only record we get back from 11-15 explains why we have no Defender-related activity in general. It looks like someone excluded the entire C: drive from Defender scanning at 21:17, minutes before the time of the download. It would be great to get the username that did the modification in the record, but since there’s only one real user on the system (IEUser), I think we’re safe to move on.

Execution Time

Now I think it’s time to figure out if there is any consensus around when SysInternals.exe was launched. I did a bit of scouring of the available artifacts to see if I could find anything new in the dissect plugins to throw together. I came up with the following:

target-query -f userassist,shimcache,amcache,bam,prefetch,sru.application_timeline,sru.application,activitiescache SysInternalsCase.E01 | rdump --multi-timestamp -w - | rdump -s "r.ts and r.ts.year == 2022 and r.ts.month == 11 and r.ts.day == 15 and r.ts.hour == 21 and r.ts.minute in range(18,21) and 'sysinternals' in str(r).lower()"

I would typically add a couple of things to this, including event logs (EID 4688 for one), but although some process auditing seemed to be enabled and 4688s were recorded, nothing was relevant here. Also, it was notable that the activitiescache plugin didn’t typically include the path to the executable referred to in the activity, but just gave it a name. I manually checked out those records as well, and didn’t find anything relevant to the execution of SysInternals.exe. Also, there were no Prefetch records or files on disk, as noted in my earlier blog, even though in the MFT and USNJrnl we see Prefetch files being created.

While those three sources weren’t very fruitful, we still get enough information from the remaining artifacts to get a picture of the execution. After putting the records in order (I used rdump -J and piped to sort to sort them by timestamp) we get the following:

  • 21:18:51 – Shimcache last modified time
  • 21:19:00 – UserAssist execution timestamp (GUI user interaction)
  • 21:19:01 – Amcache registry modification time
  • 21:19:36 – Background Activity Monitor (BAM) timestamp
  • 21:19:55 – System Resource Usage (SRU) Application Timeline End Time

This last one was pretty interesting to me, as it also comes with an included duration of 59994 milliseconds, almost exactly a minute. This would put its start time for SysInternals.exe at 21:18:55, 5 seconds before UserAssist. I felt that this was a bit of an odd gap so I had a look at the whole application_timeline record in dissect:

target-query -f sru.application_timeline,sru.application SysInternalsCase.E01 | rdump -s "'sysinternals' in str(r.app).lower()"

<filesystem/windows/sru/application_timeline hostname='MSEDGEWIN10' domain=None ts=2022-11-15 21:21:00+00:00 app='!!SysInternals.exe!2020\11\18:19:09:04!0!' user='S-1-5-21-321011808-3761883066-353627080-1000' flags=17563650 end_time=2022-11-15 21:19:55.186077+00:00 duration_ms=59994 span_ms=60000 timeline_end=407360150 in_focus_timeline=None user_input_timeline=None comp_rendered_timeline=None comp_dirtied_timeline=None comp_propagated_timeline=None audio_in_timeline=None audio_out_timeline=None cpu_timeline=639 disk_timeline=None network_timeline=49 mbb_timeline=None in_focus_s=None psm_foreground_s=None user_input_s=None comp_rendered_s=None comp_dirtied_s=None comp_propagated_s=None audio_in_s=None audio_out_s=None cycles=492526653 cycles_breakdown=71776119061217280 cycles_attr=10465119 cycles_attr_breakdown=71776119061217280 cycles_wob=None cycles_wob_breakdown=None disk_raw=None network_tail_raw=75675 network_bytes_raw=609227 mbb_tail_raw=None mbb_bytes_raw=None display_required_s=None display_required_timeline=None keyboard_input_timeline=None keyboard_input_s=None mouse_input_s=None>

Since it seemed to have another timestamp, this gave me some pause about the artifact and I wanted a second opinion about what they each mean. First, I saved the SRUDB.dat file from the challenge locally using target-fs, then I opened it using the NirSoft tool Application Resources Usage Viewer.

target-fs SysInternalsCase.E01 cp "C:\Windows\System32\sru\SRUDB.dat"

Unfortunately, the artifact I was looking for was actually missing from this tool so I ended up using ESEDatabaseView and finding the event there. I won’t go into it here but I didn’t get any additional information from those tools. It looks like for SRUDB the closest we get to a start time for the application is based on the duration and end time.

Overall, I still like the UserAssist timestamp the best here, since it’s tied to user interaction via the GUI and aligns closely with the amcache timestamp. I think it’s safe to say that the user executed SysInternals.exe at approximately 2022-11-15 21:19:00.

Dropped/Downloaded Files

Identifying files dropped or downloaded by malware using forensic data can be a pain, especially because malware often delays its own execution purposefully. Narrowing the window to “new” files in between 21:19 and 21:20 (timeframe taken from the application end time in the SRU artifact), we can make a fairly short list. We could just grep through the jsonl to save time, but if we utilize rdump we can easily select fields to display before filering:

rdump -f "{ts} {path} {filesize} {ts_type}" MSEDGEWIN10/mft/filesystem_ntfs_mft_std.jsonl | grep -E "B$" | grep -E "2022-11-15 21:(19|20)" | sort

Leaving out files that just look like index or cache files, there are only a couple interesting ones left:

2022-11-15 21:19:17.287640 c:\Users\IEUser\AppData\Local\Microsoft\Windows\INetCache\IE\WNC4UP6F\VMwareUpdate[1].exe 0.28 MB B
2022-11-15 21:19:17.287640 c:\Windows\VMTOOL~1.EXE 0.28 MB B
2022-11-15 21:19:17.287640 c:\Windows\vmtoolsIO.exe 0.28 MB B
...
2022-11-15 21:19:22.040771 c:\Windows\Prefetch\VMTOOLSIO.EXE-B05FE979.pf 2.43 KB B
2022-11-15 21:19:22.040771 c:\Windows\Prefetch\VMTOOL~2.PF 2.43 KB B

From these created files, it appears that vmtoolsIO.exe was both downloaded and potentially executed in a matter of 5 seconds. I repeated our command to find execution artifacts for this file to verify (slightly different time frame):

target-query -f userassist,shimcache,amcache,bam,prefetch,sru.application_timeline,sru.application,activitiescache SysInternalsCase.E01 | rdump --multi-timestamp -w - | rdump -L -F ts,ts_description,app,path,user,duration_ms -s "r.ts and r.ts.year == 2022 and r.ts.month == 11 and r.ts.day == 15 and r.ts.hour == 21 and r.ts.minute in range(19,22) and 'vmtoolsio' in str(r).lower()"

--[ RECORD 1 ]--
            ts = 2022-11-15 21:19:17.301279+00:00
ts_description = last_modified
          path = C:\Windows\vmtoolsIO.exe
--[ RECORD 2 ]--
            ts = 2022-11-15 21:21:00+00:00
ts_description = ts
           app = !!vmtoolsIO.exe!2020\11\18:19:10:20!0!
          user = S-1-5-21-321011808-3761883066-353627080-1000
   duration_ms = 59994
--[ RECORD 3 ]--
            ts = 2022-11-15 21:19:55.186077+00:00
ts_description = end_time
           app = !!vmtoolsIO.exe!2020\11\18:19:10:20!0!
          user = S-1-5-21-321011808-3761883066-353627080-1000
   duration_ms = 59994
--[ RECORD 4 ]--
            ts = 2022-11-15 21:21:00+00:00
ts_description = ts
           app = !!vmtoolsIO.exe!2020\11\18:19:10:20!0!
          user = S-1-5-18
   duration_ms = 131338
--[ RECORD 5 ]--
            ts = 2022-11-15 21:20:59.395466+00:00
ts_description = end_time
           app = !!vmtoolsIO.exe!2020\11\18:19:10:20!0!
          user = S-1-5-18
   duration_ms = 131338
--[ RECORD 6 ]--
            ts = 2022-11-15 21:21:00+00:00
ts_description = ts
           app = \Device\HarddiskVolume1\Windows\vmtoolsIO.exe
          user = S-1-5-18

I decided to use the -L option this time, since as expected it greatly enhances readability. I’m a fan. While some things are a little confusing here since there are multiple timestamps for each record, it was somewhat necessary to properly time filter. Record 1 is actually a shimcache record, with a timestamp about .02 seconds after the birth record for vmtoolsIO.exe. That’s fast! The next 4 timestamped records are 2 Application Timeline records from the SRU database (which have 2 timestamps each), and the last record is an Application record from the same database, which has only one timestamp.

Since the 2 Application Timeline records seem to show different durations, it’s unclear if this might represent 2 seperate executions of the same file or not. What seems consistent is that the timestamp end_time always seems to be before the other unlabeled timestamp ts, which is pretty confusing. But that’s probably a rabbit hole deserving of its own article. Onto the next part:

Persistence

Now that we have two files related to the malware, we can do a look through various persistence mechanisms to determine whether and where the malware installed itself. As far as registry persistence, dissect mainly has the runkeys plugin. It would normally behoove us to extract all registry hives and run RegRipper to get a comprehensive look at possible registry persistence, but I’ll stick with dissect for now and see what all we recover. These are the persistence techniques I think we can cover with dissect:

  • Runkeys AKA Auto-Start Extensibility Points
  • Scheduled Tasks
  • Services
  • Shortcuts in Startup folder
  • Installed browser extensions

These and a scan of registry persistence using another tool would cover persistence for many malware families, but dissect lacks the ability to identify more surreptitious persistence modifications, like Image Hijacks, modifications to the KnownDLLs list, print providers and WMI Event subscriptions (and of course, bootkits and firmware implants).

Several of these methods are registry-based and would be possible to add to the dissect framework. There is also the startupinfo plugin, which parses the StartupInfo.xml files listing programs that run in the first 90 seconds after the user logs in; this could help identify any lingering active persistence. But for now we’ll work with what we have. What I want to do is include anything matching on the keywords of our known files (SysInternals.exe and vmtoolsIO.exe) as well as anything registered in the near timeframe:

target-query -f browser.extensions,runkeys,services SysInternalsCase.E01 | rdump --multi-timestamp -w - | rdump -L -s "(r.ts and r.ts.year == 2022 and r.ts.month == 11 and r.ts.day == 15 and r.ts.hour == 21 and r.ts.minute in range(18,23)) or ('sysinternals' in str(r).lower() or 'vmtoolsio' in str(r).lower())"

This was my general strategy for persistence, but I needed to run the lnk and tasks plugins separately. The tasks plugin needs to be run without --multi-timestamp because a bug fix is still making its way to release, and the lnk plugin is fairly noisy and we are only concerned with one persistence location:

target-query -f lnk,tasks SysInternalsCase.E01 | rdump -L -s "(r.lnk_path and r.lnk_path.match('*\\Start Menu\\Programs\\Startup\\*')) or 'sysinternals' in str(r).lower() or 'vmtoolsio' in str(r).lower()"

The second command returns no records, but there is one of interest from the first command:

--[ RECORD 7 ]--
             ts = 2022-11-15 21:19:25.359259+00:00
 ts_description = ts
       hostname = MSEDGEWIN10
         domain = None
           name = VMwareIOHelperService
    displayname = VMWare IO Helper Service
     servicedll = None
      imagepath = c:\Windows\vmtoolsIO.exe
 imagepath_args =
     objectname = NT AUTHORITY\SYSTEM
          start = Auto Start (2)
           type = Service - Own Process (0x10)
   errorcontrol = Normal (1)
        _source = None
_classification = None
     _generated = 2024-01-12 23:42:07.634423+00:00
       _version = 1

With a timestamp of the service being in our execution window, it looks like this VMware IO Helper Service is a good candidate for the malware’s persistence. Now that we have that, we can take a “quick” look at the parsed event logs to see whether the service triggered and when. We can either grep for the service Display Name in the jsonl output from target-dump or be we can more thorough with rdump like so (this is fairly fast):

rdump -L -s "r.ts and r.ts.year==2022 and r.ts.month==11 and r.ts.day==15 and r.ts.hour==21 and r.ts.minute in range(19,23) and r.Provider_Name=='Service Control Manager'" MSEDGEWIN10/evtx/filesystem_windows_evtx.jsonl

...
--[ RECORD 1 ]--
                     hostname = MSEDGEWIN10
                       domain = None
                           ts = 2022-11-15 21:19:22.026651+00:00
                Provider_Name = Service Control Manager
                      EventID = 7045
                  AccountName = NT AUTHORITY\SYSTEM
                      Channel = System
                     Computer = MSEDGEWIN10
       Correlation_ActivityID = None
Correlation_RelatedActivityID = None
           EventID_Qualifiers = 16384
                EventRecordID = 975
          Execution_ProcessID = 692
           Execution_ThreadID = 6572
                    ImagePath = c:\Windows\vmtoolsIO.exe
                     Keywords = 0x8080000000000000
                        Level = 4
                       Opcode = 0
     Provider_EventSourceName = Service Control Manager
                Provider_Guid = {555908d1-a6d7-4695-8e1e-26931d2012f4}
              Security_UserID = S-1-5-21-321011808-3761883066-353627080-1000
                  ServiceName = VMWare IO Helper Service
                  ServiceType = user mode service
                    StartType = demand start
                         Task = 0
                      Version = 0
                      _source = SysInternalsCase.E01
              _classification = None
                   _generated = 2024-01-09 00:13:09.306075+00:00
                     _version = 1
--[ RECORD 2 ]--
                     hostname = MSEDGEWIN10
                       domain = None
                           ts = 2022-11-15 21:19:25.359259+00:00
                Provider_Name = Service Control Manager
                      EventID = 7040
                      Channel = System
                     Computer = MSEDGEWIN10
       Correlation_ActivityID = None
Correlation_RelatedActivityID = None
           EventID_Qualifiers = 16384
                EventRecordID = 976
          Execution_ProcessID = 692
           Execution_ThreadID = 8108
                     Keywords = 0x8080000000000000
                        Level = 4
                       Opcode = 0
     Provider_EventSourceName = Service Control Manager
                Provider_Guid = {555908d1-a6d7-4695-8e1e-26931d2012f4}
              Security_UserID = S-1-5-21-321011808-3761883066-353627080-1000
                         Task = 0
                      Version = 0
                       param1 = VMWare IO Helper Service
                       param2 = demand start
                       param3 = auto start
                       param4 = VMwareIOHelperService
                      _source = SysInternalsCase.E01
              _classification = None
                   _generated = 2024-01-09 00:13:09.307208+00:00
                     _version = 1

Here we can see records for Security Event IDs 7045 and 7040, respectively for a new service being registered, and the start type being changed (from demand start to auto-start). We don’t see any indication that the service was started manually before being set to auto-start at the next boot. Just out of curiosity, I also ran the below keyword search across parsed event logs, removing time and event provider restrictions, and got the same two events:

rdump -L -s "'vmware io helper service' in str(r).lower() or 'vmtoolsio.exe' in str(r).lower() or 'sysinternals.exe' in str(r).lower()" MSEDGEWIN10/evtx/filesystem_windows_evtx.jsonl

But even with all of this done, we don’t really know what the malware does…

Malicious Activity

It’s difficult to track particular changes to the registry as forensic data or assign changes in files to a particular process when so much is going on in the operating system. This is why malware forensics is a difficult subject often left to the malware analyst. In this case, we’re left with the time-honored tradition of scrolling through our mini super-timeline to look for anything suspicious.

I started with refining the CSV a bit. Filtering out Access records from the MFT cuts our total in half. Then scrolling down past our SysInternals.exe execution approximation of 21:19:00, there wasn’t much of note that I could see, besides the download and execution of vmtoolsIO.exe. At about 21:19:22 we see the creation of Prefetch records for vmtoolsIO.exe and its service being installed in the system.

Directly after that there is a curious Security Event of ID 4672 “Special Privileges Assigned To New Logon”. The following privileges were assigned:

  • SeAssignPrimaryTokenPrivilege
  • SeTcbPrivilege
  • SeSecurityPrivilege
  • SeTakeOwnershipPrivilege
  • SeLoadDriverPrivilege
  • SeBackupPrivilege
  • SeRestorePrivilege
  • SeDebugPrivilege
  • SeAuditPrivilege
  • SeSystemEnvironmentPrivilege
  • SeImpersonatePrivilege
  • SeDelegateSessionUserImpersonatePrivilege

Of these, SeDebugPrivilege and SeLoadDriverPrivilege are quite powerful. After this at 21:19:23, we see via USNJrnl records a fairly suspicious pattern of Prefetch files being deleted from C:\Windows\Prefetch in alphabetical order:

Further along, there are some creations of Prefetch files for cmd.exe, sc.exe and net.exe. We can only guess at what commands may have been run here, but the timestamps for the Prefetch creations are sandwiched between the modification of the VMware IO Helper Service from on-demand to auto-start. So this could have been a modification of the service using the sc command:

Aside from this, we see that the original file at C:\Users\Public\Downloads\SysInternals.exe is deleted at 21:20:58. If there is any activity after that, which is certainly possible, it falls outside of our mini-timeline. This all suggests that whatever the full functionality of SysInternals.exe may be, it seems to download an anti-forensic tool as part of its operations (vmtoolsIO.exe).

Conclusion

Let’s sum up what we found in this challenge as a timeline:

  1. On 2022-11-15 at 21:17:00 UTC, someone excluded the C:\ drive from Windows Defender scanning.
  2. At 21:17:03 the hosts file was modified to redirect traffic from the sysinternals.com domain to the local IP 192.168.15.10.
  3. At 21:18:40 the user IEUser browsed to https://sysinternals.com/SysInternals.exe and downloaded a file from 192.168.15.10, which finished downloading to C:\Users\Public\Downloads\ at about 21:18:52.
  4. The user executed SysInternals.exe at approximately 21:19:00 UTC.
  5. SysInternals.exe downloaded and executed another file named vmtoolsIO.exe to C:\Windows\vmtoolsIO.exe at about 21:19:17.
  6. vmtoolsIO.exe registered a Windows service with display name “VMware IO Helper Service” at 21:19:22 for its own persistence, and at 21:19:23 began deleting files ending in the extension .pf from C:\Windows\Prefetch.
  7. vmtoolsIO.exe modified its service to auto-start at boot at 21:19:25.
  8. SysInternals.exe was deleted from C:\Users\Public\Downloads at 21:20:58.

So malware forensics is pretty hard! It’s only fair that we check our work in a later blog with some malware analysis. Thank you for reading.

Dissect vs SysInternals Case Part 1: Planning and Testing

I’ve been wanting to try out Dissect more often so that I can understand its strengths and limitations. Last time I mainly used it interactively, which is very useful for triage. While I’ll do that again to start in this case, my goal this time is to get closer to bulk ingesting and super-timelining, or maybe even working with their Python API.

Challenge Approach

My goal here is to identify all artifact types that may be involved and to make a super-timeline that I can work with, outputting it as a spreadsheet. This output can be refined until it includes mostly relevant artifacts. First, I’ll have to triage what’s happening since all we know is that some malicious program was probably run. Here’s the scenario:

The user downloaded what they thought was the SysInternals tool suite, double-clicked it, but the tools did not open and were not accessible. Since that time, the user has noticed that the system has “slowed down” and become less and less responsive.

It’s interesting that this challenge sounds approachable from the malware reversing angle. Of course, this may not be the case if the malware was a downloader and didn’t have the payload embedded in it. But we’re focused on forensics this time. What happened and when? My first thought is to look for any artifacts related to download and execution, find the time period of interest, then do a super-timeline of the surrounding 5 minutes or so.

First things first, we’ll get some general information about the host using the following command:

target-query SysInternalsCase.E01 -f osinfo
OS version, ArchitectureWindows 10 Enterprise (amd64) build 17763.279
HostnameMSEDGEWIN10
IPs192.168.15.130
Primary UserIEUser
Straightforward enough.

Artifact Fields and Basic Searching

Next, a shot in the dark: let’s just look for something in the Amcache or Prefetch with the name SysInternals. It turns out there was no output of the Prefetch plugin, so I went with Amcache first. Determining which field to search requires some testing to determine which fields are available in the artifact. If you want to list fields and their types, pipe your plugin to rdump -l like this:

target-query SysInternalsCase.E01 -f amcache | rdump -l

In this case, there are 3 types of flow records present: DeviceContainer, InventoryApplication, and InventoryApplicationFile. In our case I’m thinking we’re interested in InventoryApplicationFile records. So, for our first shot at our query for records related to SysInternals, we’re going to use the field “path,” which in this case has the type of a typical Python Path (this was just recently changed from the type URI as I wrote the blog, so you may have to change some scripts if you treated this field as a string in the past). I think this is great, as there are all kinds of manipulations you can do with Paths. The good news is that this is a Windows Path and is case-insensitive (we can test this).

In addition to that path field matching SysInternals, I’d like any timestamps related to this record to be separated out so we can see distinct events separately. So I’ll also use the –multi-timestamp argument and we end up with this command:

target-query SysInternalsCase.E01 -f amcache | rdump --multi-timestamp -s "r.path.match('*sysinternals*')"

If you like, you can also run the following command to test whether the case matters when we use the matches method of a Path object:

target-query SysInternalsCase.E01 -f amcache | rdump --multi-timestamp -s "r.path.match('*SysInteRnals*')"


Good, in this case we got two events related to the same file. In addition to the path and sha1 hash being the same, we can see that the program ID is the same as well (0006d7bfadc0b7889d7c68a8542f389becce00000904). We can see the timestamp for the modification time in the Registry is 2022-11-15 21:19:01 while the timestamp for when the executable was linked is 2020-11-18 19:09:04 (dissect outputs timestamps in UTC).

Adding Context with the MFT Plugin

Now that we have an indication of an amcache entry being created, I want to add MFT records to this to provide some context. Which field should I use for records from that plugin?

target-query SysInternalsCaseE01 -f mft | rdump -l

According to the field list, both the FILE_NAME and STANDARD_INFORMATION records have the field “path” as well. So ideally we could just run:

target-query SysInternalsCase.E01 -f amcache,mft | rdump --multi-timestamp -s "r.path.match('*sysinternals*')"

For some reason this didn’t work though. I think it’s due to some incompatibility between the plugins? I was getting some errors related to a broken pipe, but they weren’t too descriptive. Since this selection statement works fine with the amcache records, I went ahead and ran the same query with just the mft plugin alone. (Note: this command will take on the order of 15-20 minutes). I decided to write the filtered output of the MFT command to a .rec file so we can work with the output again quickly (I also recommend this when using the evtx plugin):

target-query SysInternalsCase.E01 -f mft | rdump --multi-timestamp -s "r.path.match('*sysinternals*')" -w mft_filtered.rec

With that being said, I get the feeling I’ll be working mostly with its JSON output for the sake of scripting in Python when things get up to scale.

Combining Multiple Artifacts

Anyways now that we have the records file mft_filtered.rec, we can make it into a .csv and/or select columns of interest using rdump again. For the purposes of this exercise, all we care about is timestamp, path, artifact name and timestamp type. So I dump to a csv using this command:

rdump mft_filtered.rec -F ts,path,ts_type --csv -w sysinternals.csv

This gives good output, even though it seems a bunch of header rows are interspersed. It’s easy to remove these once you sort by the first column.


However, if you’d prefer to work with text and are only interested in a couple of fields, you can format this as text and won’t end up with the extraneous headers:

rdump mft_filtered.rec -f {ts},{path},{ts_type}

In any case, once things are sorted we can see that the MFT has file entries for this SysInternals.exe file in the Microsoft Edge Cache, as a .partial file in the TempState\Downloads folder, and finally at the path C:\Users\Public\Downloads\SysInternals.exe.

So now, how can we combine the timestamps from these two plugins (amcache and mft) into one CSV? To do this, I first dumped amcache records matching “sysinternals” to a file named amcache.rec:

target-query SysInternalsCase.E01 -f amcache | rdump --multi-timestamp -s "r.path.match('*sysinternals*')" -w amcache.rec

We can check that there’s content in the .rec file by using the command rdump amcache.rec:

And next, to combine these two record types into one CSV, I used the following command:

rdump --csv -w combined.csv amcache.rec mft_filtered.rec

In this output combined.csv, we can see the last modification time for this amcache entry alongside the MFT events for the download:

Building a Mini Super-Timeline (Time Filtering)

Now that we have what seems like a strong indication of an execution event. I can add in lnk files and some user interaction artifacts in the same way, but now that we have a time I want to see if we can get a mini-timeline going, using the timestamp in the selector statement instead of our keyword “*sysinternals*”.

For this mini-timeline, I’m going to pick the artifacts mft, shimcache, amcache, shellbags, lnk, services, and runkeys. I would like to add scheduled tasks in, but for some reason the –multi-timestamp argument breaks this plugin (bummer). I’m selecting the minute before and after the amcache record to see if there’s anything additional and to pick up persistence mechanisms. Here is my draft command:

target-query -f shimcache,amcache,shellbags,lnk,services,runkeys SysInternalsCase.E01 | rdump --multi-timestamp -s "r.ts and r.ts >= datetime.datetime(2022,11,15,21,18) and r.ts <= datetime.datetime(2022,11,15,21,20)" --csv -w mini_timeline.rec

Unfortunately, the datetime and timedelta modules aren’t accessible from the string selector. We could try converting the timestamp to a string, but it is clunky and perhaps it is about time to switch to Python scripting here. Still, I feel that this should be an important use of the selector in rdump that should be supported. Last try at getting something working:

target-query -f shimcache,amcache,shellbags,lnk,services,runkeys SysInternalsCase.E01 | rdump --multi-timestamp -s "r.ts and r.ts.year == 2022 and r.ts.month == 11 and r.ts.day == 15 and r.ts.hour == 21 and r.ts.minute in range(18,20)" --csv -w mini_timeline.csv

This resulted in only 7 records in the outputted CSV. I opened up an issue with the flow.record module and one of the maintainers helped me realize my mistake here. rdump –multi-timestamp is the operation that breaks copies each timestamp in the record into its own event with an r.ts field, which means that the field is not yet present in the record in the first operation. I needed to pipe the output of –multi-timestamp to another rdump execution to select that field. Like so:

target-query -f shimcache,amcache,shellbags,lnk,services,runkeys SysInternalsCase.E01 | rdump --multi-timestamp -w - | rdump -s "r.ts and r.ts.year == 2022 and r.ts.month == 11 and r.ts.day == 15 and r.ts.hour == 21 and r.ts.minute in range(18,20)" --csv -w mini_timeline.csv

After sorting by timestamp, we get 29 rows of timestamped events, plus header rows. Between amcache, shimcache, shellbags and lnk files, we can see that the user navigated to the Public downloads folder and executed SysInternals.exe. Since there seems to have been a download, I want to add in the dissect function browsers to enumerate all browsers and extract relevant events. Running the following command took approximately 4 minutes and 15 seconds:

target-query -f shimcache,amcache,shellbags,lnk,services,runkeys,browsers SysInternalsCase.E01 | rdump --multi-timestamp -w - | rdump -s "r.ts and r.ts.year == 2022 and r.ts.month == 11 and r.ts.day == 15 and r.ts.hour == 21 and r.ts.minute in range(18,20)" --csv -w mini_timeline.csv

In addition to the information in the previous mini-timeline, the following URLs were extracted from the browser plugin, occurring in order:

https://go.microsoft.com/fwlink/?LinkId=525773
https://go.microsoft.com/
ms-appx-web://microsoft.microsoftedge/
ms-appx-web://microsoft.microsoftedge/assets/errorpages/dnserror.html?DNSError=11001&ErrorStatus=0x800C0005&NetworkStatusSupported=1
ms-appx-web://microsoft.microsoftedge/assets/errorpages/dnserror.html?DNSError=11001&ErrorStatus=0x800C0005&NetworkStatusSupported=1
https://www.msn.com/
ms-appx-web://microsoft.microsoftedge/assets/errorpages/dnserror.html?DNSError=11001&ErrorStatus=0x800C0005&NetworkStatusSupported=1
http://www.sysinternals.com/SysInternals.exe
http://www.sysinternals.com/SysInternals.exe

It’s fascinating the malware seems to come from a well-known site. However, it’s not clear at this point that the user connected to the proper IP to download this file. To determine that, it would be nice to get some confirmation from event logs, especially the DNS client.

Trying out target-dump

I was thinking that there might be some benefit to dumping the content of all these plugins to jsonlines format instead, in case I want to do an operation on a particular artifact a bit more quickly or debug that artifact. I’m also curious if there are speed or memory benefits of this.

Note: It turns out there is a bug in target-dump and it doesn’t support plugin namespaces (yet). So for the amcache function specifically, I had to qualify the plugin names:

target-dump --restart -o . -f shimcache,amcache.files,amcache.application_files,amcache.programs,shellbags,lnk,services,runkeys,browsers SysInternalsCase.E01

The output of this command creates a folder with the name of the endpoint, in this case MSEDGEWIN10. Then you can use this command to combine them all into one .jsonlines file:

rdump --multi-timestamp -J -w combined.jsonl MSEDGEWIN10/shimcache/windows_shimcache.jsonl MSEDGEWIN10/runkeys/windows_registry_run.jsonl MSEDGEWIN10/services/windows_service.jsonl MSEDGEWIN10/shellbags/windows_shellbag.jsonl MSEDGEWIN10/tasks/filesystem_windows_task_grouped.jsonl

I also noticed that the –multi-timestamp argument doesn’t work with the task output filesystem_windows_task.json, which is why I left that file out in the above command. With that caveat in mind, we can use the same selector as before, but operating on combined.jsonl:

rdump --csv -w combined.csv -s "r.ts and r.ts.year == 2022 and r.ts.month == 11 and r.ts.day == 15 and r.ts.hour == 21 and r.ts.minute in range(18,21)" combined.jsonl

Here is the result (combined.csv). We end up with 20 rows of timestamped events and 4 header rows:

To flesh things out more like a proper timeline, I also did a target-dump of the mft, usnjrnl, browser.downloads, browser.history and evtx plugins, which took about 30 minutes, and followed the above steps to narrow down to the 3 minutes of interest. This resulted in about 18,000 rows in the CSV, good enough to start straining the eyes. Parsing the MFT or USN journal always takes forever, but you get so much more data than you might expect. For example, if I run the Prefetch plugin on this E01, or do a target-shell and go to the Prefetch folder, there’s no output (or .pf files found). Yet, the parsed MFT was able to show that they were created at the time (not for SysInternals.exe itself, though):

Although the column says “index,” in the MFT events these are full paths.

Conclusions and Recommendations

Next time, we can turn towards actually tackling the challenge with dissect. My recommendation for the time being is to set up dissect in an Ubuntu environment, separating it from other tools with a python virtual environment under pipx. I also recommend dumping plugin output to files at the beginning to save time when debugging or playing around.

For this challenge, I’m dumping the output of relevant plugins using target-dump and making a CSV mini-super-timeline of the couple of minutes surrounding the incident, just to get a general idea, then working the timeline forward as needed. I want to stick with text instead of jumping into the Python API just in case data isn’t in its proper field. In Part 2, we’ll attempt to reconstruct the story and focus on solving the challenge.

Also, apologies for all of the screenshots of shell output and CSVs! I’ll attempt to use the formatting tools in both dissect and WordPress in Part 2 to select particular columns and make it more readable. Thanks for reading!

Investigating Windows Systems with Dissect – IWS Chapter 2

I recently found out about the Dissect toolset by Fox-IT/NCC Group, which abstracts out a lot of the target format and filesystem to streamline accessing particular artifacts. I’m curious how easy it is to use and its limitations, since it seems very portable and easy to install. To practice, I’m using several different images from the book Investigating Windows Systems by Harlan Carvey. In a later post, I’ll use DFIR Challenge 7 from Ali Hadi.

Trying Out the Demo

First, I decided to try using the demo instance of Dissect to play around with some features before installing. My first test is one of backwards compatibility using a Windows XP image. In this case, I used WinXP2.E01:

The Dissect demo GUI.

No automatic recognition of OS and other host information yet, but I haven’t interacted with it via the shell so far. My first step was to use a couple commands in the shell to check these details:

The results of my first commands came through quickly.

This is a good sign! So continuing with the scenario, we’re interested in malware. I’ve read Harlan’s approach to investigating this image, and I’m interested in rapid-triage type approaches. In this case I’ll want to look at persistence mechanisms, including Run keys, Services, Scheduled Tasks, Startup items, KnownDLLs and anything else I have access to. Granted, I’m not expecting the kind of coverage I’d get with RegRipper on unconventional persistence techniques.

Unfortunately, here seems to be where the demo, at least in terms of rendering things in the top pane, fell flat (at least for this image). When choosing several functions from the drop-down menu nothing happened. So back to the shell I went:

Run Keys

Using the runkeys command quickly outputs a list of autostart extensibility points, a couple of which look suspicious. But the number of Run keys recovered is rather small. I noticed no RunOnce keys were present, so I took a look at the Dissect source code to see what keys were supported. I’m pretty okay with the list they have. In this case I find it suspicious that a Run key is named RPC Drivers, since generally drivers are loaded into the kernel as part of a service and you generally don’t need programs to run at login in order to do anything with them. These keys stick out especially:

<windows/registry/run hostname='REG-OIPK81M2WC8' domain=None ts=2004-06-18 23:49:49.937500+00:00 name='RPC Drivers' path='C:/WINDOWS/System32/inetsrv/rpcall.exe' key='HKEY_LOCAL_MACHINE\\Software\\Microsoft\\Windows\\CurrentVersion\\Run' regf_hive_path='sysvol/windows/system32/config/SOFTWARE' regf_key_path='$$$PROTO.HIV\\Microsoft\\Windows\\CurrentVersion\\Run' username=None user_id=None user_group=None user_home=None>
<windows/registry/run hostname='REG-OIPK81M2WC8' domain=None ts=2004-06-18 23:49:49.937500+00:00 name='RPC Drivers' path='C:/WINDOWS/System32/inetsrv/rpcall.exe' key='HKEY_CURRENT_USER\\Software\\Microsoft\\Windows\\CurrentVersion\\Run' regf_hive_path='sysvol/Documents and Settings/vmware/ntuser.dat' regf_key_path='$$$PROTO.HIV\\Software\\Microsoft\\Windows\\CurrentVersion\\Run' username='vmware' user_id='S-1-5-21-1123561945-606747145-682003330-1004' user_group=None user_home='%SystemDrive%\\Documents and Settings\\vmware'

Another interesting piece of information we get is the user associated with this the username associated with the last key, vmware. This gives us an indication that this particular user might have been infected. You might also note that the timestamps for both entries are the same: 2004-06-18 23:49:49. The path to the executable rpcall.exe is also interesting, since it seems like inetsrv could possibly be an IIS server directory.

Checking the Hash

The next thing I wanted to do for triage purposes was checking the hash of this executable. I poked around for a bit by running “help” in the shell:

To calculate the hash of a particular file, we can just run hash <filepath>:

hash "C:/WINDOWS/System32/inetsrv/rpcall.exe"
MD5:	a183965f42bda106370d9bbcc0fc56b3
SHA1:	5d5a53182e73742acb027bb3a3abc1472d02dde9
SHA256:	776b26c9c516e1cd60871097e586026f73bc0f0c210582d1b2ea1ae7c954b2be

Pivoting on this, we can see that someone has uploaded it to VirusTotal for analysis, and it’s being widely detected as malicious:

While the detection names are rather generic and may be low confidence, by clicking on the Behavior tab we can see a sandbox run. In addition to the Run keys we expected, I saw that many keys under HKCU\Software\Microsoft\Windows\CurrentVersion\Policies\Explorer\DisallowRun were written to:

A variety of Registry keys were written under Policies\Explorer\DisallowRun.

Googling this registry key tree, I found that it’s a technique for disallowing the execution of certain programs (mostly Antiviruses in this case). In addition, the first key in the screenshot is written to create a firewall exception for the worm. These actions match activity described in reports on several worm variants.

Information Filtering

Now that we have some situational awareness with targeted artifacts, what I want to do is test Dissect’s ability to filter larger amounts of data. Event logs, MFT and Prefetch are what I’m hoping for here. So how do we filter?

The answer, after some digging, is the command rdump. We can pipe the result of a command to rdump and do all sorts of filtering. For example, with prefetch! Unfortunately, at this point I needed to officially install Dissect locally, since the demo doesn’t seem to support piping to sort or rdump.

The prefetch information came quickly and had a surprising amount of detail. In addition to the name of the executable that may have run, the prefetch records also included a list of loaded libraries, which is great for investigating DLL hijacking incidents:

Snippet of all Prefetch records in the image.

But there were more than just DLLs: the list includes .nls files, .log files, ocx libraries and others.

Issues with PowerShell/Windows

Now, to try filtering by the filename field I followed the docs and tried this command:

PS> target-query.exe -f prefetch .\WinXP2.E01 | rdump.exe -s '"rpcall" in r.filename.lower()'

Here I’m searching for prefetch records that contain the keyword “rpcall.”

However, I got the error ERROR RecordReader('-'): Unknown file format, not a RecordStream. After this point I did some troubleshooting and ran into a number of issues in both PowerShell and the Command Prompt. The authors behind Dissect were very helpful in explaining the following:

  1. PowerShell does not support putting binary data (like our record streams) in a pipe. It will try to interpret it as text. Thus, it is easier to use the normal command prompt.
  2. rdump.exe -s ‘”rpcall” in r.filename.lower()’ (as it says in the docs) will not work with the Command Prompt (cmd.exe), you’ll need to use rdump.exe -s “‘rpcall’ in r.filename.lower()”. This is due to how rdump.exe was compiled here (apparently an artifact of compilation for windows). So in this case, you need double quotes on the outside, single quotes on the inside (for strings within the statement).

If this was a bit confusing, I apologize, but in summary: I recommend installing Dissect on Linux in a Python virtual environment, whether that’s in a separate Ubuntu virtual machine (maybe the SIFT VM) or on Windows Subsystem for Linux in your Windows VM. For the latter I recommend WSL 1, as the nested virtualization required for WSL 2 broke countless times on VirtualBox. Install on Linux to be able to follow the Dissect documentation without these issues, and use a Python virtual environment to avoid dependency issues. But since I figured out how to get piping and commands working on Windows, I continue the walkthrough there. Back to the challenge!

Again, But in the Command Prompt

After trying the following, I got the output I expected:

target-query.exe -f prefetch .\WinXP2.E01 | rdump.exe -s "'rpcall' in r.filename.lower()"
Prefetch records where the executing file contains ‘rpcall.’

The cool thing about this the Prefetch output from Dissect and the linked files is that we can look not only at the DLLs loaded (which can indicate things about functionality of the malware, but also we can see accessed files that are not DLLs, by simply adding to our Python condition for the filter:

target-query.exe -f prefetch .\WinXP2.E01 | rdump.exe -s "'rpcall' in r.filename.lower() and not r.linkedfile.lower().endswith('.dll')"
Filtering the previous Prefetch records for non-DLL linked files.

How interesting! We can see that the last 3 files linked to this prefetch that are not DLLs are related to Internet Explorer:

/DEVICE/HARDDISKVOLUME1/DOCUMENTS AND SETTINGS/VMWARE/LOCAL SETTINGS/TEMPORARY INTERNET FILES/CONTENT.IE5/INDEX.DAT
/DEVICE/HARDDISKVOLUME1/DOCUMENTS AND SETTINGS/VMWARE/COOKIES/INDEX.DAT
/DEVICE/HARDDISKVOLUME1/DOCUMENTS AND SETTINGS/VMWARE/LOCAL SETTINGS/HISTORY/HISTORY.IE5/INDEX.DAT

While accessing these files is not conclusive evidence of stealing cache, history or cookies, it gives a potential thread to pull in the malware analysis and may be a part of networking functionality.

Other File Artifacts

Now that we know how to filter using rdump, we should check out noisy evidence sources like the MFT. The following query took a bit longer, probably on the order of a minute and a half. For comparisons, queries before this took about 5 seconds:

target-query.exe -f mft .\WinXP2.E01 | rdump.exe -s "'rpcall' in r.path.lower()"
MFT entries with “rpcall” in the path.

We can see the four timestamps for Birth, MFT Change, Modification, and Access are each different for the malicious file, whereas for the Prefetch records all four are the same. That lines up with intuition. I wonder what else is in that same directory?

I didn’t find anything else searching the directory, but I did notice something cool that I hadn’t spotted in the previous query:

There are 2 different types of timestamps output by the plugin.

Upon closer inspection we have both records named filesystem/ntfs/mft/std and filesystem/ntfs/mft/filename, referring to $STANDARD_INFO and $FILENAME timestamps respectively. As we might expect, the $STANDARD_INFO timestamps (especially the C timestamp) reflect metadata changes, whereas the $FILENAME timestamps are all aligned at the last move or copy action. This definitely aligns with my intuition.

Conclusion

I checked for other forms of persistence and didn’t find much else going on in this image. Chapter 2 in the book (I encourage reading it, it’s short) goes into some time anomalies in this image, but I was mostly focused on targeted artifact searching capabilities.

I’ve been impressed! I started this with an XP image expecting more hiccups in artifact extraction, but I successfully used the plugins info, evt, prefetch, userassist, mft, and runkeys with no issues. Unfortunately, the following were not supported on this XP image: shimcache (not implemented ShimCache version) and tasks (I saw that C:\Windows\Tasks, the directory for tasks in the legacy Task Scheduler, isn’t in the list of paths in the plugin source).

But the project is open source and I’m excited to see it develop! This could make for a very fast and flexible triage tool for answering specific questions and whipping up particular artifact timelines. Thanks to the Fox-IT squad for making such a cool tool open-source.

The Honeynet Project Forensic Challenge 2010: Challenge 1

I found this forensics challenge while doing research for work and thought it should be fun (you can find it here). It’s a 3-part challenge so it should keep me busy for a couple of posts.

The first evidence file is a network packet capture of the attack. By default, I’ll be using Wireshark, and maybe NetworkMiner if needed. And now, for the questions:

Which systems (i.e. IP addresses) are involved? (2pts)

You can find the answer to this question through a variety of windows in Wireshark under the Statistics menu, including Conversations, Endpoints, IPv4/IPv6 Statistics and more. To get an overview, I like the TCP tab in the Endpoints Window:

There are several ports in use, but only on 2 IP addresses.

So the answer to this question is the two IPs 98.114.205.102 and 192.150.11.111.

What can you find out about the attacking host (e.g., where is it located)? (2pts)

In order to understand which system is attacking, we have to look at what protocols are at play on the different ports and what the conversations look like. After a cursory look through the protocols, we see use of the SMB (Server Message Block) protocol, which could indicate that an SMB exploit was used. There are other protocols at play here, but SMB happens to be critical to how this exploit happens.

We can view only SMB packets using a filter in Wireshark (we could just follow the stream of any of the SMB packets, but there could be multiple streams):

The Wireshark filtered to “smb.”

Since 98.114.205.102 initiates the Negotiate Protocol Request, I’m inclined to think it’s the attacker. We can reinforce this prediction later. As for where the attacker is located, we can use WHOIS to get some information, but unless a function called GeoIP works in Wireshark, we can only get the Domain Registrar, which could be bogus or hidden:

The Whois information; not the most helpful if it just identifies the headquarters of Verizon.

So from Whois we get the registered organization, Verizon, but have no clue where the attacker may have been (especially if they’re just using a Verizon access point). Let’s give GeoIP a try:

Well isn’t that cool. I have no idea whether these GeoIP databases were up-to-date with the actual devices. I also don’t know how GeoIP works, but this is super nice. Let’s say the attacker was in Philly then.

How many TCP sessions are contained in the dump file? (2pts)

Another easy one; just use Statistics > Conversations and click on the TCP tab to see that there are 5 sessions.

The Conversations view.

How long did it take to perform the attack? (2pts)

Well, this challenge is a great way to learn Wireshark. The challenge writers made it easy for us by only including the attack. In this case, we can use Statistics > Captured File Properties to look at Elapsed Time, or just look at the time of the last packet in the capture: the attack took around 16.2 seconds.

The packet capture window beside the Captured File Properties Window.

Which operating system was targeted by the attack? And which service? Which vulnerability? (6pts)

To answer this question, let’s head back to the SMB packets in Wireshark. A couple of packets in the SMB filter stood out:

The presumed attacker connects to the IPC share on 192.150.11.111, which allows anonymous null sessions. This is a feature of Windows which facilitates writing to named pipes. Named pipes are data pathways to and from running processes. In this case, writing to the named pipe lsarpc allowed the attacker to leverage DSSETUP, an Active Directory Tool.

The attacker used DSSETUP to call the function “DsRoleUpgradeDownlevelServer,” which looks suspicious in itself. If we take a close look at that packet, especially at the 3 Reassembled TCP Segments, we see a bunch of 0x90 bytes followed by some bytes that look a bit like code. At the end of the TCP segments there are a bunch of ASCII “1” bytes.

From experience, this looks like a buffer overflow. Meaning that the DsRoleUpgradeDownlevelServer call can be exploited by sending a large buffer of “1” characters, then inserting an address or assembly command to jump to the code. The 0x90 bytes are NOP instructions in Assembly, which do nothing but advance the instruction pointer. Having a “NOP Sled” is common in stack-based exploits when it’s unclear where the attacker’s jump will land. Regardless of where the jump lands in the NOPs, they lead to the exploit code.

Google searching something like “DsRoleUpgradeDownlevelServer buffer overflow” is a good way to confirm our suspicions. But instead of running down this lead all the way and potentially spoiling the whole surprise, let’s try to answer the question:

The buffer overflow vulnerability exposes Windows systems through XP (got that from the NVD), and it appears that a person can gain remote access to Active Directory by writing to the lsarpc named pipe. This pipe handles Remote Procedural Calls for the Local Security Authority Subsystem Service (hence the LSA and RPC in lsarpc). Therefore, we can conclude that the vulnerability is in LSASS.

Can you sketch an overview of the general actions performed by the attacker? (6pts)

Of course, there should be more to this case than the exploit. The attacker had to use the exploit for some further goal. Let’s follow the next stream of TCP activity, which has some interesting activity:

In summary, once the attacker has remote code execution, they write some commands to a file called ‘o’, execute the commands in ‘o’ with ftp, delete it and execute a file. Basically, the attacker is making a downloader so that the victim machine retrieves and executes their second stage, ssms.exe. We can confirm that this actually happens by looking at the next stream, which the victim initiates:

“happy r00ting”

And that’s it! it downloads the second stage and that’s the last packet. There’s our outline; next question:

What specific vulnerability was attacked? (2pts)

Hmm, this seems like a repeat question; I’m unsure if this is asking about the lsass exploit or something else. For now, I’ll just drop the CVE we found as the answer to this question (CVE-2003-0533) and we’ll move on to the next.

What actions does the shellcode perform? Pls list the shellcode. (8pts)

Okay, in order to figure out how the LSA service was exploited, we need to look at that buffer overflow again. The packet in which the attacker calls DsRoleUpgradeDownLevelServer contains bytes that look like this:

the shellcode, probably.

This is a little introduction to memory exploitation. Again, the 0x90 bytes around the shellcode are NOP sleds. The shellcode writer uses them to lead to the shellcode and to fill stack space. Further down in the packet, you have the multitude of 0x31s that make up the buffer overflow. The order of the exploit is something like: Buffer Overflow > NOP sled > shellcode > NOP sled. Now that we’ve identified the shellcode, I’ll take it to one of my favorite tools for these situations: scdbg.exe, the shellcode debugger.

scdbg.exe output

I didn’t know much about this tool until I started using it for triage in my new job. It’s incredibly helpful when you have access to shellcode, especially when it’s auto-generated or obfuscated by Metasploit or similar frameworks. Be sure to export the specific bytes of the shellcode for it to work; the NOPs and buffer will probably cause scdbg to fail.

Anyways, scdbg gives us some function calls that tell us everything we need to know (minus some understanding of the Windows API, if you’re unfamiliar). The shellcode creates a bind shell using socket functions from Ws2_32.dll. The bind shell listens on port 1957 and spawns a command shell when it receives a connection.

Do you think a Honeypot was used to pose as a vulnerable victim? Why? (6pts)

This is a pretty difficult question, even in 2009, honeypots were able to emulate SMB functionality quite well. I would say that the honeypot would have to be able to execute shell commands and download files, which is a bit more full-featured than low-interaction honeypots. It’s certainly possible today. The only reason I’d lean toward this possibly being a honeypot is that the attacker’s initial logon gave a null user but the victim still allowed an SMB session:

It’s a toss-up for me, so I’ll say it is a honeypot.

Was there malware involved? Whats the name of the malware? (We are not looking for a detailed malware analysis for this challenge) (2pts)

Well, it certainly looks like malware was involved, but we should grab the executable to be sure. I’ll probably reverse it, but the hash and the name of the malware family shouldn’t spoil anything if I don’t read the writeups :).

I usually prefer using NetworkMiner to pull executables because copying/saving the bytes from Wireshark can be buggy, but in this case Wireshark worked out great! We follow the stream as soon as the malicious server starts transmitting smss.exe, which looks like this (Raw on the left, ASCII on the right).

We can save the executable by using “Save as…” while viewing the data in Raw mode. You’ll want to make sure you don’t execute it outside of a VM, though.

I used Reverse.it to search for the hash in question this time (sometimes I just like it better than VT)

After searching the hash (14A09A48AD23FE0EA5A180BEE8CB750A) and doing a bit of research, we find that the smss.exe is actually an SDBot sample, part of a family of backdoors operated by IRC (Internet Relay Chat). Which brings us to the next question:

Do you think this is a manual or an automated attack? Why? (2pts)

IRC is great for managing a number of hosts, and is often automated, especially in IRC-controlled botnets. We combine this information with the fact that none of the attack seemed user or domain-specific. The login was anonymous and the commands written to ‘o’ for FTP were also scripted. Finally, the fact that the entire compromise happened in 16 seconds makes it clear that this is probably a worm that automatically propagates using vulnerable shares.

Well, that’s all the questions! This took me forever to write up, but I definitely came away knowing a bit more; even though this is an old example, it’s one I haven’t seen. I’ll look ahead at the next challenge and decide whether I want to reverse this executable or keep going. Thanks for reading.