Malicious.exe running, but does not show up in Procexp.exe
I was researching ways to not just do anti-analysis, but to rather run executables in spite of analysis, while evading analysis. I found ways to go invisible from a few common analysis tools, but the final boss was Sysinternals Suite, no Administrative nor SEDebugPriveledge allowed for this challenge. I figure I’d share what I found here, which also ended up being a privilege escalation.
ProcExp’s “HiddenProc” Easter Egg
I first started with Procexp. Sifting through Procexp in IDA I thought I instantly found a promising section of code and was going to call it a day.
This routine searches for a MULTI_SZ registry value named “HiddenProcs”. If it exists, it will parse a list of new-line delimited process names and what I assume, later filter them out of the viewing. Unfortunately, the actual routine in charge of hiding these process names doesn’t exist (probably compiled out for release mode?). This is a dead registry key. Moving on.
ProcExp Image Hijack 1 (failed)
If we can hijack procexp, then we can control it to display whatever we want to the user. When you run Procexp32.exe (or other 32-bit Sysinternal tools) on a 64-bit OS, it will often dump a 64-bit version of itself to disk, and then run the 64 version instead. Is there a way we can image hijack this, post drop?
What we are looking at is the drop and execute code from Procmon32. This routine is responsible for writing a 64-bit version of itself and executing if on 64-bit OS. From the top green node (Drop64bitProcExp function) to the bottom green node (CreateProcessW). There is a gap. If we can ensure ProcExp32.exe spends as much time in the red while we repetitively try to write to the dropped exe, can we intercept the image before CreateProcess is called?
Below I try this, with a simple POC to set my thread to TIME_PRIORITY_TIME_CRITICAL, while attempting to write my own code to the dropped exe. The goal is to get my quantum to get executed in between the 2 green nodes.
When running this program, and a user attempts to open ProcExp, we get the error below. Looks like it may have only wrote partial way for the Image Hijack. This is not deterministic enough, and too SCIFI.
ProcExp Image Hijack 2 (success)
Investigating the file drop code, we see ProcExp doesn’t instantly exit if wfopen_s(“ProcExp64.exe”, “wb”) fails.
This will be its flaw. It ignores the fopen error as long as “GetFileAttributes” succeeds.
..so the image hijack is very simple. We just write out own “ProcExp64.exe” to the temp directory, and make it “READ ONLY”. That way, fopen(“ProcExp64.exe” ,”wb”) fails, but when trying GetFileAttributes it will succeed, and execution flow will lead us right to CreateProcess.
Here my process drops a fake version of ProcessExplorer under the %temp% dir as “PROCEXP64.exe”. Its marked read-only (you can try this at home actually). This is a simple program that just says “Hijacked” to the console.
Now when we try to run Procexp.exe, it hits this flaw and instead runs our fake “PROCEXP64.exe”.
This shows a hijack is possible, but I think we can do better, since this is limited to 64-bit OS running 32-bit Sysinternals.
The DLL Hijack (final solution)
This is final method I choose for the POC. Looking at Sysinternal RegKeys, we see one called “DbghelpPath”. This Registry key is writable for most applications since its the USER registry hive.
DbghelpPath key just points it to a path that it trusts to hold the “dbghelp.dll”, thats loaded at runtime. I am able to modify this, so dll hijack is just a step away . My binary sets this key to point to the %TEMP% dir while dropping its own dbghelp.dll in temp (%TEMP%/DbgHelp.dll). When Procexp is runs, it will load the dll in this path. Once i get it to load my dll we hook ProcExp routines to hide my process. You can see the main code for this in
This involved reversing ProcExp logic. After doing so, I found the best point to target the hook – OpenProcess API.
We patch OpenProcess Api, since, it is called for each process it shows to the output. Its better if I target a hook here, rather than hardcoded offset. If I do the later, it will be more volatile to different versions of ProcExp (since offsets can change, code added/removed). So instead I patch OpenProcess API, because when ProcExp calls it, theres a dangling r14 register that happens to point to the ProcExp PROCESS linked list. This PROCESS linked list structure is less likely to change between versions.
Here we patch:
Here we hook, to hide our process from ProcExp linked list. This involved reversing the PROCEXP process linked list structure a bit, but once done, we have a filter for filtering processes. Below this POC filters processes named “Malicious.exe”.
One cool thing about this, is that nearly all SysInternal tools have this DbgHelp path writable key, so you can theoretically do this with all Sysinternal Suite tools.