MITRE ATT&CK turned purple – Part 1: Hijack execution flow

The MITRE ATT&CK framework is probably the most well-known framework in terms of adversary emulation and by extent, red teaming.
It features numerous TTPs (Tactics, Techniques, and Procedures) and maps them to threat actors. Being familiar with this framework is not only benefiting the red team operations but the blue team operations as well! To create the most secure environment for your enterprise, it is imperative that you know what threat actors are using and how to defend against it.

Having a 100% coverage of MITRE ATT&CK is probably not feasible, by choosing which TTPs are most relevant for your environment however, you can start setting up baseline defenses and expand from there. This will help you mature your enterprise’s security posture significantly. We at NVISO are using the framework in our daily operations and have therefore decided it was time to combine the knowledge we have in-house from both our blue and red teams to provide insight into how these techniques can be leveraged from an offensive point of view AND how to prevent (or at least detect) the technique from a defensive point of view. In our first blogpost of the series, we decided to cover T1574 – Hijack execution flow.

Offensive point of view: Leveraging execution flow hijacking in red team engagements and threat emulations

Execution flow hijacking usually boils down to the following: identifying a binary present on the system that is missing dependencies (typically a DLL) and providing said missing dependency. Luckily for us, the good people at Microsoft have gifted us with a tool suite called sysinternals, which we will happily leverage to identify missing dependencies.

It should be noted that casually dropping sysinternals tools on a target environment is very poor operational security and probably won’t do you much good anyway. For most of the tooling (if not all), you will need to have administrative privilege on the machine you are running it from. Therefore it is much more interesting to either have some “educated” knowledge beforehand on what tools are living on your targeted environment. Alternatively (and simpler), you can hijack a program you know will most likely be installed. Some fine examples of this would be Teams, Chrome, Firefox, …

We can identify missing dependencies using a tool that was created by our friends over at SpectreOps called “DLLHijackTest”. This tool needs an extract from sysinternals’ Process Monitor and will attempt to verify if the processes identified are indeed hijackable, as not all missing DLLs are getting loaded in the same way (calling DLLMain) at execution time.

Let’s identify some nice missing dependencies on our trusted Internet Explorer using the following Process Monitor filter:

After this filter is applied, let’s open Internet Explorer and check our process monitor light up like a Christmas tree:

Now we can export this as a CSV file by going to file -> save and choosing CSV as an output format.

Everything we need now is a valid hijack, which we can test using the aforementioned PowerShell script from SpectreOps:

Get-PotentialDLLHijack -CSVPath "G:\testzone\DLLHijackTest-master\InternetExplorer\IE.CSV" -MaliciousDLLPath "G:\testzone\DLLHijackTest-master\x64\Release\DLLHijackTest.dll" -ProcessPath "C:\Program Files\Internet Explorer\iexplore.exe"

What happens now is the following chain of steps:

  • A DLL gets dropped in the location of the application and is named after a missing dependency
  • The process gets launched
  • If the DLLMain method is called, the DLL will write its own path into an arbitrary location that you need to replace in the source code of the SpectreOps project.
  • The process terminates

This repeats until the entire CSV is parsed. If the application has a vulnerable Hijack, an output file will be created at the location you hardcoded.

In the case of Internet Explorer this is indeed the case:

We have successfully fuzzed Internet Explorer and identified four missing DLLs that are in fact loaded and their DLLMain is executed.

Note: for this blogpost, IE was chosen as a PoC. You will need admin rights to write in C:\Program Files\ so for red team ops, this is a pretty weak candidate, unless you will abuse this for persistence reasons.

Now all that is left to do is create a DLL that executes your payload, name it one of the missing dependencies identified in the results file and drop it on disk.
Every time Internet Explorer will be opened, your DLL payload will fire.

Defensive point of view: Preventing and detecting execution flow Hijacks

When looking through the public Sigma repository, it is noticeable how only a few rules to detect this technique exist:
Some 20 exist, of which two are authored by NVISO: Maxime Thiebaut’s “Windows Registry Persistence COM Search Order Hijacking”, and Bart Parys and yours truly’s “Fax Service DLL Search Order Hijack”. All of these only cover specific instances of this technique. The reason for this is very simply that it is next to impossible to write a rule that covers the many options the red team/adversary has to exploit this technique. Proper detection is achievable however, by getting a baseline of your environment and alerting on any DLLs/EXEs loaded from unexpected locations.

While Sysmon can be configured to log ImageLoaded events as event ID 7, this is disabled by default because of the massive amount of logs it would generate.
To help with triaging you can use a PowerShell script to semi-automatically generate a Sysmon config that excludes all known-good DLLs that are loaded.
See the example below for one such (basic) PowerShell script:

# Run this script repeatedly to automatically add the newly used DLLs to the exclusions.
# Do a reboot after installing the "base" Sysmon config to log all the DLLs loaded in the Windows boot process.

# Modify to point to the Sysmon executable.
$SYSMON_EXECUTABLE = "C:\Sysmon\Sysmon64.exe"
# Modify to point to the new config. (Will be overwritten by a run of the script!)
$CONFIG_FILE = "C:\Sysmon\config.xml"

Function Get-DLLs {
    # Using a HashSet to avoid having to filter for duplicates
	$dlls = New-Object System.Collections.Generic.HashSet[String]
	
    try {
        # Retrieve all Sysmon ImageLoaded events
	    $events = Get-WinEvent -LogName "Microsoft-Windows-Sysmon/Operational" -FilterXPath "Event[System[(EventID=7)]]"
        # Extract the ImageLoaded from the events' Message fields
		$events.Message | ForEach-Object -Process {
			$loaded = (Select-String -InputObject $_ -Pattern "ImageLoaded: (.*)").Matches.Groups[1]
			$dlls.add($loaded) | Out-Null
		}
	} catch {}
	
    # Sort before returning for consistent & managable output
	$dlls | Sort-Object
}

Function Export-SysmonConfig {
	Param($dlls)
	
	$XMLHeader = @"
<Sysmon schemaversion=`"4.22`">
    <EventFiltering>
        <RuleGroup name="" groupRelation=`"or`">
            <ImageLoad onmatch=`"exclude`">

"@
	$XMLTrailer = @"
            </ImageLoad>
        </RuleGroup>
    </EventFiltering>
</Sysmon>
"@
    # To indent <ImageLoaded> for readability.
    $Offset = "                "
	
	Function Format-Exclusion {
		Param($dll)
		$dll = $dll.trim()
		$Offset + "<ImageLoaded condition=`"is`">$dll</ImageLoaded>`n"
	}
		
	$XMLConfig = $XMLHeader
	$XMLConfig += $Offset + "<ImageLoaded condition=`"is`">$SYSMON_EXECUTABLE</ImageLoaded>`n"
	$XMLConfig += $Offset + "<ImageLoaded condition=`"begin with`">C:\Windows\System32\</ImageLoaded>`n"
	$XMLConfig += $Offset + "<ImageLoaded condition=`"begin with`">C:\Windows\SysWOW64\</ImageLoaded>`n"
	foreach ($dll in $dlls) {
		$XMLConfig += Format-Exclusion $dll
	}
	$XMLConfig += $XMLTrailer
	
	$XMLConfig
}

$dlls = Get-DLLs
Export-SysmonConfig $dlls | Tee-Object -FilePath $CONFIG_FILE
# Install the new config to lower the amount of logs generated.
Start-Process -FilePath $SYSMON_EXECUTABLE -ArgumentList @('-c', $CONFIG_FILE)

Be sure to only execute this on a known-good device, such as a freshly imaged laptop or a new VM:
If you use a potentially compromised device to generate this, there is a chance of excluding a malicious DLL that can then remain completely undetected in your environment.
You will need to run this every time a piece of software gets updated, as the loaded DLLs may change (new DLLs added, older DLLs no longer relevant) depending on the version of the software.

Note that to limit the amount of exclusions the config needs, the C:\Windows\System32\ and C:\Windows\SysWOW64\ directories are excluded in their entirety by the script.
You should set up a SIEM alert for Sysmon event ID 11 (FileCreate) if the TargetFileName starts with either of these directories.
A Sigma rule to detect this looks as follows:

title: DLL Created In Windows System Folder
id: ddc5624d-4127-4787-8cd9-e0943ebb10e8
status: experimental
description: |
  Detects new DLLs written to the Windows system folders.
  Can be used to gain persistence on a system by exploiting DLL hijacking vulnerabilities.
references: 
  - https://blog.nviso.eu/2020/10/06/mitre-attack-turned-purple-part-1-hijack-execution-flow
tags:
  - attack.t1574.001
  - attack.t1574.002
author: NVISO
date: 2020/10/05
logsource:
  product: windows
  service: sysmon
detection:
  selection:
    EventID: 11
    TargetFilename|startswith:
      - 'C:\Windows\System32\'
      - 'C:\Windows\SysWOW64\'
    TargetFilename|endswith: '.dll'
  condition: selection
falsepositives:
  - Driver installations
  - Some other software installations 
level: high

If your configuration is correct, you should not generate any Sysmon event ID 7 for legitimate DLLs and you can simply alert on any occurrence of the event as potentially malicious.
Any DLLs dropped in the excluded directories get flagged by the Sigma rule for proper coverage.
Even if your Sysmon config is not covering 100% of the legitimately loaded DLLs, the volume of generated events should be low enough to be able to be workable, and additional filtering can also be done using a SIEM or automated in a SOAR solution, for example.
With sufficient time, your detection capabilities for this technique should be tuned finely enough as to not generate many false positives.

Detection for this technique is obviously not cut-and-dry but it is possible to have very good coverage, provided your blue team gets a proper testing environment to improve their detection capabilities.

Prevention of this technique works very similar to detection:
One can write AppLocker policies to only allow known DLLs to load.
You can create a list of loaded DLLs by setting the policy to audit for several weeks and appending DLLs that were missed by the initial testing to the list of known-good ones before enforcing your policies.

The script and rule in this blogpost are available on our GitHub.

Conclusion

We hope that this blogpost has provided you with actionable information and has given you more insight into leveraging this technique and defending against it.
This was the first blogpost of a recurring series, we hope to see you again when we cover another ATT&CK technique in the near future!
From all of us at NVISO, stay safe!

About the author(s)

  • Jean-François Maes is a red teaming and social engineering expert working in the NVISO Cyber Resilience team. When he is not working, you can probably find Jean-François in the Gym or conducting research. Apart from his work with NVISO, he is also the creator of redteamer.tips, a website dedicated to help red teamers.
    He was also ranked #1 on the Belgian leaderboard of Hack The Box (a popular penetration testing platform).
    You can find Jean-François on LinkedIn and on Hack The Box.
  • Remco Hofman is an intrusion analyst in NVISO’s MDR team, always looking at improving the detection capabilities of the service. A few cups of properly brewed tea help him unwind after a long day’s work.
    You can find him on Twitter or LinkedIn.

Join the Conversation

18 Comments

Leave a comment

Leave a Reply

%d bloggers like this: