SEPTEMBER 18, 2024

SHIM Me What You Got: Manipulating Shim and Office for Code Injection

Deep Instinct security researchers Ron Ben-Yizhak and David Shandalov share the research they presented at DEF CON 32: an in-depth exploration of two different attack methods. Explore the methods they presented and follow along as they explain how they work.

Curiosity is a necessary personality trait for cybersecurity professionals. For many of us, that means taking the defensive knowledge we’ve built and using it as a weapon. Understanding attack methodologies and how attackers think is key to successfully defending against them.  

This blog is a supplement to our talk at DEF CON 32. In that talk we discussed the resurrection of an attack surface that cybersecurity vendors believed had been addressed a long time ago. We focused on two sets of unique attack surface research.  

The first study targeted RPC servers related to Office. Analyzing a single RPC method led to a noteworthy attack that combined several manipulations to achieve both code injection and privilege escalation.  

The second study focused on reverse-engineering the App Compatibility framework and its undocumented structures. We uncovered a novel and stealthy technique to apply malicious shims in a process that does not require registry modification or SDB files and leaves no traces on the disk. 

RPC Interface in Office 

Microsoft Office is one of the most popular pieces of software in the world.  It’s found on almost every Windows machine, no matter where it’s operating. It is a complex tool composed of many components necessary for its operation. Microsoft Office has com objects, scripting engines, and cloud synchronization, as well as backward compatibility for many features, all of which make it the perfect candidate for attack surface research. 

We began our research by looking for RPC servers running on the machine once Office has been installed. We found the process OfficeClickToRun.exe, which hosts the service ClickToRunSvc. OfficeClickToRun.exe exposes several interfaces, which are implemented in the following RPC files:  

  • OfficeClickToRun.exe 

  • AppVIsvVirtualization.dll 

  • AppVIsvSubsystemController.dll 

Picture1.png-1.png
RPC interfaces of ClickToRunSvc displayed with RPC Investigator

A string in AppVIsvVirtualization.dll caught our attention: "Could not inject subsystem dll to child process, process id %3%, module %2%, error code %1%." This is a debug string printed by one of the RPC methods the DLL exposes. 

Picture1.png-2.png
String in AppVIsvVirtualization.dll indicating on code injection

To verify that this method performs DLL injection, we debugged the service and put breakpoints on WinAPI for code injection. While starting an Office program, we saw this RPC method led to WriteProcessMemory! This looked promising, so we decided to dig deeper into this DLL. 

AppVIsvVirtualization.dll 

The description of AppVIsvVirtualization.dll is “Microsoft Application Virtualization Client Virtualization Manager Component.” In reality, it’s an RPC server that exposes two undocumented methods. The second method performs the DLL Injection. We used PowerShell to look for RPC clients and that led us to the file AppvIsvSubsystems64.dll. The public symbols of the client reveal that the functions that perform the RPC calls are named: 

    a. virtman_notification_server_notify_new_process 

    b. virtman_notification_server_notify_new_child_process 

Picture1.png-3.png
Searching for RPC clients with PowerShell
The Injection Process 

To understand the injection process, we started at the beginning and looked at what the client sends to the server. While debugging the software, Word showed us that a child process is created before the RPC method is invoked. The program ai.exe is launched in a suspended state. Afterward, the request is sent to the server with two parameters: The first is the PID of ai.exe. The second is a structure containing the string AppvIsvSubsystems64.dll and its length (i.e., the number of characters it contains). 

Next, we looked at the server and the files related to the app virtualization platform before we started to reverse-engineer it. We discovered that there were files with names like those found in the system32 directory.  

Picture1.png-4.png
App virtualization files in system32

Those files have symbols indicating that the Detours library is being used. ’Detours’ is an open-source library developed by Microsoft for monitoring and instrumenting API calls on Windows. It also offers the ability to inject code into other processes. AppVIsvSubsystemController.dll uses this ability by calling the functions DetourUpdateProcessWithDll and DetourCopyPayloadToProcess. It can be seen when comparing the Office file to the system32 file with BinDiff. 

Picture1.png-5.png
Comparison of the Office files to the system32 files with BinDiff

The source code of Detours reveals how the DLL injection is executed. The optional header is modified to point to a new copy of the import table that includes an additional import descriptor to the injected DLL. When viewing the headers of the injected process with WinDbg, the full path to the injected DLL is shown. 

Picture1.png-6.png
The headers of the injected process displayed with WinDBG

The suspended process is then resumed. Since the process is not fully initialized yet, the loader of the operating system uses the modified import table, and AppvIsvSubsystems64.dll is injected into the process. When OfficeClickToRun.exe receives a command to inject a DLL into a 32-bit process, it launches mavinject32.exe, which performs the same injection. 

Abusing ClickToRunSvc 

Popular software is often excluded from the behavioral analysis of security products to avoid false positives. Knowing that, forcing OfficeClickToRun.exe to inject a DLL might result in a detection bypass. 

We were able to imitate the call done by virtman_notification_server_notify_new_child_process and inject AppvIsvSubsystems64.dll into a suspended process... but how can we inject another DLL? We know that the function receives a string, so we can change the name of the DLL that will be injected, but writing our DLL file to the Office directory requires administrator privileges and looks very suspicious. 

Changing the string from AppvIsvSubsystems64.dll to C:\Temp\Injected.dll results in an attempt to load C:\Program Files\CommonFiles\microsoftshared\ClickToRun\C:\Temp\Injected.dll.  

It looks like the string is just appended to a directory without any checks, so we can use an old trick – directory traversal! The dot-dot-slash (..\) sequence will cause the file lookup to look at the parent directory. Several consecutive sequences of dot-dot-slash will manipulate the file lookup to look into a completely different directory. The following import descriptor will end up loading the file C:\temp\Injected.dll

Picture1.png-7.png
The headers of the process after manipulating the injection method

At this point, we have already found a new technique to perform DLL injection using a benign application. That alone is a respectable achievement, but we want more. 

The injection is performed by a process that runs as NT AUTHORITY\SYSTEM. The client can run with low privileges... so can we abuse this service for privilege escalation? 

Security Mechanisms in ClickToRunSvc 

ClickToRunSvc impersonates the RPC client before injecting to verify that it is alloed to access the remote process. This means that an RPC client running with a medium integrity level cannot inject a DLL into a high integrity level process. The thread handling the RPC request will use the same token as the client, and when it tries to gain a handle to the target process, the call to OpenProcess will fail with the error code ERROR_ACCESS_DENIED. 

Picture1.png-8.png
Security check ClickToRunSvc performs on the client's permissions

In conclusion, escalation from non-admin to admin privileges is not possible. But it does raise a different question: can we escalate from admin to NT AUTHORITY\SYSTEM? 

Launching Suspended Process as SYSTEM 

We used API monitoring to look for processes running as NT AUTHORITY\SYSTEM that call CreateProcess with the CREATE_SUSPENEDED flag, leading us to the task scheduler service. 

This service is a perfect candidate for abuse. Many scheduled tasks are launched as SYSTEM, including some that belong to Office. Forcing ClickToRunSvc to inject a DLL into another Office process is even less likely to raise alarms since it injects a DLL into ai.exe every time Word is launched. The task we chose is “Office Automatic Updates 2.0.” 

Picture1.png-9.png
Scheduled task created by Office and configuted to execute as "NT AUTHORITY\SYSTEM"

The problem is that the task scheduler service resumes the process shortly after it is launched. We need to find a way to hold the execution of the task scheduler service before it resumes the process so that we can modify the IAT. We can do that using an opportunistic lock. 

Opportunistic Locks 

An Opportunistic Lock (OpLock) is a mechanism used in networked file systems to optimize file access and improve performance. When a file is accessed by a client application, the server grants an opportunistic lock to the client. This lock allows the client to perform operations on the file without interference from other clients. While a client holds an opportunistic lock, it caches data locally. If another client attempts to access the same file, the server can break the opportunistic lock held by the first client. This ensures data consistency and prevents conflicts between clients. When the lock is broken, the server typically notifies the first client to flush any cached data and reestablish its lock, if necessary. 

Applications can also request an oplock when accessing a local file to prevent other applications and processes from corrupting the file. The application can request the oplock from the local filesystem and then cache the file locally. In these cases, the local server uses the opportunistic lock like a semaphore, the main purpose of which is to prevent data incoherency and notify the process about file access. 

If we can find a file that is accessed by the task scheduler service after the process is created but before it is resumed and the loader parses the IAT, we can request an oplock for this file and stop the service from resuming the process until the injection is done.  Monitoring file access during the launch of scheduled tasks showed that the file C:\Windows\apppatch\sysmain.sdb was read. This file is a shim database. It stores app compatibility settings. 

Picture1.png-10.png
Callstack showing the API CreateProcess open the shim database
App Compatibility 

The App Compatibility framework is a set of tools, libraries, and techniques provided by Microsoft to ensure that older Windows applications can still run smoothly on newer versions of the Windows operating system. This framework includes various compatibility modes, shims, and other mechanisms. 

The goal of the Windows App Compatibility framework is to minimize disruptions for users and businesses when upgrading to newer versions of Windows by maintaining compatibility with older software. It helps address issues related to changes in the operating system that might affect the behavior of existing applications, such as changes in APIs, security features, or system configurations. 

The framework offers single fixes, such as resolution change, or file redirection, and modes (also referred to as layers) that act as a pack of fixes applied together. 

Picture1.png-11.png
App compatibility fixes available through the file properties

This framework also uses .sdb files, or "Shim Database" files, to store compatibility fixes. The .sdb files are managed by the Compatibility Administrator tool, which is part of the Windows Assessment and Deployment Kit (ADK). Administrators can use this tool to create, modify, and deploy compatibility fixes stored in .sdb files to address compatibility issues across their organization’s applications. 

Our goal is to find a scheduled task that executes a file with app compatibility settings. To do that, we ran every task and checked which ones caused the service to read the sdb file. The only tasks we found were related to the Microsoft Edge update. They execute C:\Program Files (x86)\Microsoft\EdgeUpdate\MicrosoftEdgeUpdate.exe, which we can see has compatibility settings using Microsoft’s Compatibility Administrator software. Every process containing the string “Update” will receive the “GenericInstaller” fix. 

Picture1.png-12.png
Entry in the shim database about files with the word "update"

This can be a good target for injecting a DLL into a 32-bit process, but we still need to find a 64-bit target. 

While researching this subject, we noticed a strange behavior. The sdb file was read the first time an executable ran since the machine booted, even if it didn’t have matching app compatibility settings. After that, in all subsequent runs, the sdb file wasn’t read. We wanted to find a way to guarantee the sdb file would be read any time an executable runs. We could achieve that legitimately by configuring a compatibility for the file: 

  1. The tool sdbinst, which installs a custom sdb file on the system by configuring the following registry values: 
    HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion\AppCompatFlags\Custom 
    HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion\AppCompatFlags\InstalledSDB 

  2. The compatibility window under the file’s properties 

SDB files are a known attack vector and can be abused for code injection and API hooking, which means Endpoint Detection & Response (EDR) tools might detect new app compatibility configurations, even if they are benign. It’s also why EDR is not enough to protect your organization. We want to avoid registry changes and make this configuration without touching the disk. 

Looking for files related to app compatibility led to apphelp.dll, and the main API it uses is NtApphelpCacheControl

NtApphelpCacheControl 

This is an undocumented API that is exported by ntdll. It receives two parameters: an integer and a pointer to an unknown structure. Looking for usages of this API across GitHub led us to the file ahcache.h, which was included in previous versions of Windows SDK. This header file has detailed information about the function’s parameters. 

In addition, we looked for API usage by other OS components. It is used by several DLL files, such as kernel32.dll, sxssrv.dll, apphelp.dll, and more. 

When we applied the definitions of the WinAPI on calls to it in the DLL files we found inconsistencies. The size of memory allocated for the structure was larger than its definition. In some functions, the value of the first parameter was higher than the possible values of the Enum. This means that those definitions were changed since the SDK with ahcache.h was published. 

Picture1.png-13.png
NtApphelpCacheControl is called with unknown parameters

We continued looking at the usages of NtApphelpCacheControl and discovered that the symbols of combase.dll contained information about the API! Extracting the relevant definitions of the symbols resulted in an updated version of ahcache.h. It shows us the various operations that can be performed with the API.

Picture1.png-14.png
Updated definition of the first parameter for NtApphelpCacheControl

Once we understand these definitions, the calls to the API in other DLL files are much clearer. CreateProcess calls this API behind the scenes with the parameter ApphelpCacheServiceLookupProcess:

Picture1.png-15.png
Callstack showing how the API CreateProcess looks up values in the cache

When the operating system shuts down, the cache is flushed to the registry and, on startup, is read and “loaded” as the current cache database. If the cache is clear when a process is launched for the first time, there is no information about it, so the sdb file is read, and an entry is added to the cache that specifies if the file has app compatibility fixes. That is why the sdb file isn’t read when a process is launched for the second time. 

If we remove this entry from the cache, the sdb file will be read again. 

After looking at cross-references to NtApphelpCacheControl, we saw that both kernel32.dll and apphelp.dll perform the “remove” operation. They also have an exported function that simplifies the creation of the structure by accepting only the file name and the file handle. 

Picture1.png-16.png
Function from kernel32.dll that removes values from the shim cache

After calling NtApphelpCacheControl with the “remove” parameter, the file’s execution will once again trigger reading the sdb file, which we can lock to keep the process suspended. We can then inject our DLL. 

Attack #1 Workflow 
  1. Write to disk the DLL that will be injected 

  2. Find the path of the file that an arbitrary scheduled task will execute ("Office Automatic Updates 2.0" for example) 

  3. Remove the shim cache for this file by calling BaseUpdateAppcompatCacheWorker from kernel32.dll 

  4. Request oplock for C:\windows\apppatch\sysmain.sdb 

  5. Send a message to the scheduled tasks service to execute “Office Automatic Updates 2.0” 

  6. The process will create SYSTEM in a suspended state 

  7. Scheduled tasks service will attempt to read C:\windows\apppatch\sysmain.sdb, then enter a wait state and won't resume the process 

  8. The callback from the oplock will be called  

  9. Send an RPC to the OfficeClickToRun service to inject the DLL into the suspended process 

  10. Release the oplock, and the scheduled tasks service resumes the process 

  11. The patched IAT will be used and the DLL will be loaded into a SYSTEM process

mp4_demo1-ezgif.com-video-to-gif-converter.gif
Showcasing the first attack
Attack #1 Summary 

This attack has the following advantages: 

  1. A benign and known process does the injection for us by executing a technique not monitored by EDR solutions. As such, it is unlikely to raise any alarms. 

  2. It injects the DLL into a benign process that cannot be connected to us. It is spawned as a child process of the task scheduler. 

  3. We achieve code execution as “NT AUTHORITY\SYSTEM.” 

  4. The rest of the actions we perform, such as calling NtApphelpCacheControl and requesting an OpLock, are mostly unmonitored by security products. 

This attack achieves both code injection and privilege escalation. It also demonstrates the process of looking for vulnerabilities. We found an RPC method that can be abused, but calling it wasn’t enough to execute a significant attack. When we hit an obstacle, we looked at another component of the OS to overcome it. Only by chaining all the manipulations can we gain something from the initial RPC call. 

Digging into the Apphelp Cache 

We wanted to gain a deeper understanding of what happens when we call NtApphelpCacheControl and what we can do with it. 

When the call to this API reaches the kernel, ntoskrnl.exe sends a device IO request to \Device\ahcache, which is controlled by ahcache.sys

Picture1.png-17.png
A look into the callstack of NtApphelpCacheControl

 

When ahcache.sys is loaded, it reads the registry values under 
\Registry\MACHINE\System\CurrentControlSet\Control\Session Manager\AppCompatCache
a. AppCompatCache 
b. CacheMainSdb 
c. SdbTime 

Picture1.png-18.png
The registry values that are used for cache operations

These values contain binary data that the driver sorts into an AVL table.

Picture1.png-19.png
From driver entry to the initialization of the AVL table

The various operations performed by NtApphelpCacheControl result in the lookup, insertion, or deletion of entries in the table. 

Each entry in the value’s AVL table that should be shimmed contains an EXE TAG. 
All entries contain the following: size of entry, size of data, data buffer, and NtImagePath. 
Data from the in-memory table is dumped into the registry value on shutdown/restart. 

Screenshot 2024-09-11 at 4.18.43 PM.png
Looking inside the AVL table

 

Let’s take the VRCHAT.exe entry, for example, and grab 9CCC20. If we convert from the little-endian format, we get 0x20cc9c. With sdb-explorer, we can search for the converted value in a readable version of sysmain.sdb. 

Picture1.png-20.png
Readable version of the sysmain.sdb entry by sdb-explorer

If we look inside the actual sysmain.sdb file at the 0x20cc9c address, we can see the entry, including the TAGs presented above. 

Picture1.png-21.png
The raw look of the entry inside of sysmain.sdb

vid1.gif
From AVL table entry to sysmain.sdb

While looking at the dispatch routines for \Device\ahcache, we noticed that the function that handles the request of ApphelpCacheServiceInitProcessData doesn’t access the AVL table. Instead, it writes into the memory of another process! 

Picture1.png-22.png
Writing to the memory of another process
Init Process Data 

When calling NtApphelpCacheControl with the parameter ApphelpCacheServiceInitProcessData, the structure that needs to be filled is: 

Picture1.png-23.png
Undocumented structure for ApphelpCacheServiceInitProcessData

Based on reverse engineering and old definitions from ReactOS, the undocumented Data field should look like this:  

Picture1.png-24.png
Undocumented Shim Data Structure

The function in ahcache.sys that handles this request is AhcApiInitProcessData. Before it writes to the memory of another process, several checks are made: 

a. ProcessHandle is not null 

b. DataSize is 0x11C0 or higher 

c. Data.Magic is 0xAC0DEDAB 

d. Data.Size is 0x11C0 

e. The calling process runs as NT AUTHORITY\SYSTEM and has the privilege SeTcbPrivilege 

f. Calling PsGetProcessProtection on the target process returns 0x81 (expanded upon below) 

g. The PEB of the target process is checked. BitField needs to have the flag IsPackagedProcess (0x10) turned on.  The call fails when PsGetProcessProtection is called. This function returns the field Protection from the EPROCESS structure. This field can be retrieved from user-mode and is documented under ZwQueryInformationProcess. The value 0x81 means that PS_PROTECTION.Type is PsProtectedTypeProtectedLight (0x1) and PS_PROTECTION.Signer is PsProtectedSignerApp (0x8). 

Picture1.png-25.png
PsProtectedTypeProtectedLight from the EPROCESS structure
Picture1.png-26.png
PsProtectedSignerApp from the EPROCESS structure

Looking at every process in Windows 11, we concluded that there are NO Windows Apps with this kind of protection. 
This means that we don’t have a target. 

We concluded that this call would not result in code injection, but we’ll leave it to the community to investigate this type of request and find a way to abuse it.  

Still, this undocumented structure piqued our intrest, and we wanted to understand what it represents. So, we looked for the magic values in all the DLL files on the system and found that several functions in kernel32.dll and apphelp.dll reference it: SdbUnpackAppCompatData, SdbPackAppCompatData, SdbGetAppCompatDataSize, BaseReadAppCompatDataForProcessWorker, BasepGetAppCompatData, and SbpGetShimData

image30.jpg
Magic Values search results
Compatibility Fixes Initialization 

Reverse engineering the functions that reference this magic value revealed that this structure represents the shim fixes that need to be applied to the process. When a new process is created, the following steps occur as part of kernelbase!CreateProcessInternalW

a. The parent queries the apphelp cache to check if the child process requires shim fixes. 

b. The parent process reads sysmain.sdb to retrieve the complete information about the fixes. 

c. The parent builds the structure according to these specific fixes and writes it to the memory of the child process.  

d. The parent modifies the value of PEB->pShimData to point to the structure. 

Picture1.png-27.png
A look at pShimData from the PEB structure

e. ntdll loads apphelp.dll if pShimData is not empty 

f. apphelp.dll applies the shim fixes according to the structure 

vid2.gif
Overview of what happens when a new process is created
Creating SHIM_DATA 

After the shim cache is queried, the parent process builds the SHIM_DATA structure as part of the call to CreateProcess.   

Picture1.png-28.png
Shim Data build flow inside the parent process

 

pShimData Injection 

To achieve our goal, we need to understand the fields of the undocumented structure written to PEB->pShimData.  

The tool “Compatibility Administrator” was used to install custom rules for various shims on the system.  

Picture1.png-29.png
The InjectDll Fix inside of the "Compatibility Administrator" tool

Once a custom rule was applied to a process, the raw data of SHIM_DATA was dumped from the memory of the process. The raw data was fuzzed by comparing it to other instances where a shim was applied to a process. Specific rules were created for different filenames and the structure was analyzed by looking at the difference between the values of the fields.  

In addition to custom rules, existing rules from sysmain.sdb were also tested. Processes were created to match the specifications of a rule, such as filename, version, company name, etc. Comparing the structure describing a custom rule to one describing a default rule sheds some light on additional fields.  

This methodology allows for the identification of patterns within both custom and standard sysmain.sdb entries, highlighting instances of exact matches, similarities, and complete differences in specific addresses in the dumps.

Finally, similar addresses are consolidated, and a process of elimination is initiated to determine which fields are non-essential, streamlining the structure for our uses. 

Picture1.png-30.png
A sysmain.sdb entry

Picture1.png-31.png
A custom sdb entry

This is the definition of the structure after reverse engineering: 

Picture1.png-32.png
The reverse-engineered SHIM_DATA structure we unveiled

We want to avoid registering a custom SDB file since it might be detected by a security product, so we need to find an existing entry in sysmain.sdb that applies the fix “Inject DLL.” The tool sdb-explorer can help us with that. 

Picture1.png-33.png
Our target sysmain.sdb entry for the attack

According to this entry, RTvideo.dll will be injected if the process name starts with GLJ, ends with the temp extension, and has the correct company and product name. Luckily, these checks are done by the parent process. Once SHIM_DATA->ExeTag points to this entry, apphelp.dll will apply the fix without checking the conditions.  

Limitations 

a. The injected process must be launched as suspended. We cannot inject into a process that is already running. The callstack that leads to LoadLibrary goes through apphelp!SE_InstallAfterInit, which is an exported function. We tried executing the attack on an already-running process by writing our custom SHIM_DATA to it and then launching a remote thread on the process that will execute apphelp!SE_InstallAfterInit again. This attempt failed because apphelp!SE_InstallAfterInit checks a global variable, and if the process is already initialized, the check will fail and the shim data won’t be processed. 

Picture1.png-34.png
The InjectDll fix call stack

b. The injected process cannot be a system file. AcGenral!NS_InjectDll::NotifyFn calls AcGenral!ShimLib::IsSystemExeFromSelf before injecting the DLL. The injection is skipped if the file is under C:\Windows\WinSXS or belongs to “Trusted Installer.”  

c. This shim fix is unavailable for 64-bit processes. To verify that Microsoft isn’t hiding this feature – and it truly is impossible – we can look at the code of the 64-bit version of AcGenral.dll. It doesn’t have a class called NS_InjectDll

With these limitations in mind, we started looking for a suitable executable file. 
And our previously mentioned “friend,” C:\Program Files (x86)\Microsoft\EdgeUpdate\MicrosoftEdgeUpdate.exe, is our target as it complies with all three limitations. 

Attack #2 Flow   

At this point, we have enough knowledge to abuse the App Compatibility mechanism. We can inject a DLL by performing the following steps: 

a. Write a DLL to disk named RTvideo.dll

b. Launch a suspended process and set the current directory to be the same as the one containing RTvideo.dll 

c. Build a SHIM_DATA structure and set the ExeTag to be 0x4ed54 

d. Write the structure to the memory of the child process  

e. Point PEB->pShimData to the structure 

f. Resume the child process 

g. apphelp.dll will apply the InjectDll fix, and our DLL will be loaded

mp4_demo2-ezgif.com-video-to-gif-converter.gif
Showcasing the second attack
Attack #2 Summary 

Malicious shims are a known attack vector detected by many security products. By reverse engineering this infrastructure, we were able to renovate it. We found a novel method to make this attack file-less and registry-less. We applied a malicious shim in a process without registering an SDB file on the system. We effectively bypassed EDR detection by writing to a child process and loading the target dll from the suspended child process before any EDR hook can be established. We chose the “InjectDll” shim, but this attack can be adapted to other malicious shim fixes. 

Detection 
  1. First Attack:
    1. RPC to ClickToRunSvc with unusual DLL Paths
    2. Requesting OpLock for sysmain.sdb
    3. ClickToRunSvc writing to the memory of a process running as “NT AUTHORITY\SYSTEM”. 
  2. Second Attack:
    1. Writing DLLs that appear in sysmain.sdb, to an unusual directory
Further Research - App Compatibility 
  1. Find target for AhcApilnitProcessData
  2. Reverse engineer SHIM_DATA to find more ways to manipulate apphelp.dll 
  3. Poison the cache to cause another process to apply a malicious shim 

Conclusion 

We presented methodologies for attack surface research that led to new stealthy injection and privilege escalation techniques, which can be achieved by abusing two different OS components.  

By leveraging and manipulating various system components, including services, oplocks, and compatibility mechanisms, we demonstrated how to consolidate these elements into a singular, cohesive attack strategy.  

The oplock and compatibility mechanisms could be integral parts of other multi-component, sophisticated, and complex attacks. Oplock can be used in many scenarios as a building block, and there are far more shim fixes that can be used with malicious intent, even with 64-bit processes. 

Our extensive research uncovered new techniques for stealthy injection and privilege escalation, enhancing the effectiveness of these attacks. We demonstrated two attacks that won’t be monitored EDRs. The injections occur in a very early stage where EDRs can’t establish a hook yet.  

Through reverse engineering an undocumented API and its associated structures, we gained valuable insights that contributed to the development of these methods.  

Additionally, we modernized a previously known malicious technique, transforming it into a more elusive, fileless, and registry-less attack.  

We encourage the community to continue building on our research, exploring the leads we have provided to further advance the field. 

In the meantime, if you’re solely relying on EDR to detect and respond to breaches, learn more about why EDR is not enough, and then request a demo to find out how Deep Instinct leverages the only deep learning AI platform built for cybersecurity to prevent threats others can’t find. 

Github Repository