TL;DR; This blog post describes a technique to inject a DLL into a process using only Duplicate Handle process access (caveats apply) which will also bypass Code Integrity Guard.
I’ve been attending Blackhat USA 2019 and watched a presentation by Amit Klein and Itzik Kotler on Windows Process Injection techniques. While I didn’t learn anything new from the presentation that you couldn’t from just reading Hexacorn’s blog it was interesting to see them document what techniques worked against Code Integrity Guard (CIG) and what did not. CIG if you don’t know, is Microsoft’s term for blocking non-MS signed DLLs from being loaded into a process. If CIG is enabled on a process then you can load an arbitrary DLL not signed by Microsoft, instead you’ll have to do some sort of shellcode or ROP.
During the presentation I was waiting for the punchline of a technique which bypasses CIG to load an arbitrary DLL, but it never arrived. I’m guessing the researchers don’t bother to read my blog posts sigh, such as this one on injecting code into a Protected Processes though abusing the KnownDll mechanism. This would also work to bypass CIG if injecting from an external process not under CIG (or Device Guard). All the ways of hijacking the Known DLL loader that I’ve documented rely on knowing the location of Known DLL handle in NTDLL’s data section. That’s useful when you have little control over the target process and only an arbitrary read/write primitive. For user-mode code injection you’re likely to be able to do anything to the process.
Writing a new handle value does have draw backs if you’re thinking about it from a generic code injection perspective. Firstly the location of the handle can (and does) change depending on the version of NTDLL and secondly if you access and write memory of another process you might as well call your binary malware.exe. Of course writing to memory is not the only way to hijack Known DLLs, you can achieve the same thing with only Duplicate Handle access on the process, which is probably slightly less suspicious.
How can we do this without modifying the handle value? There’s 3 key observations we can make that only require Duplicate Handle access:
- We can find the existing handle value of the KnownDlls directory by duplicating handles from a process to another and querying for the name.
- We can close a handle in another process by specifying DUPLICATE_CLOSE_SOURCE to DuplicateHandle.
- The kernel’s handle allocator will reuse the handle values so we can replace the original handle with a different object through brute force.
break
</div><div> -Root $dir -Path "blah.dll" -Protection Execute</div><div> }</div><div>}</div></div><div><br /></div><div>Step 3: Close the original Known DLLs handle. Again this only needs Duplicate Handle access. At this point you probably also want to suspend the process to ensure something doesn't execute and allocate the handle over the top of your now closed handle. Of course if you suspend the process you'll need a bit more access.</div><div><br /></div><div><div>$proc = Get-NtProcess -ProcessId $id -Access DupHandle</div><div>Copy-NtObject -SourceHandle $handle -SourceProcess $proc
The question you might be asking is, “Do any AV/Host Detection tools catch this trick?”. Honestly I don’t know, nor do I care. However it has some things going for it:
- It doesn’t requiring reading or writing memory from the target process.
- Inline hooks on LoadLibrary/LdrLoadDll will just see loading a system32 DLL unless they also then query for the mapped file name after the operation has completed.
- It bypasses CIG, so anyone thinking that’ll prevent injection will be surprised.
Article Link: https://tyranidslair.blogspot.com/2019/08/windows-code-injection-bypassing-cig.html