Sunday, April 22, 2012

64-bit Process Replacement in Powershell

Download here: Replace-x64-Process.ps1

For those of you who follow me on Twitter, you may have noticed that I posted a few teasers related to replacing processes in Powershell. Without further ado, I am releasing Replace-x64-Process. This tool is intended solely as a proof-of-concept tool. I say that because I make absolutely no guarantees that it will work in all cases. In fact, it won't work in many cases.

The intent of this tool is to circumvent application whitelisting products. Assuming Powershell is whitelisted, you can use this tool to load a blacklisted executable within a whitelisted process. I have not tested this tool on any whitelisting products, however so I cannot attest to its level of effectiveness.

Replace-x64-Process makes use of some common techniques seen in malware to replace a suspended process' executable image. Fortunately, Powershell gives us access to many of the mechanisms required to achieve this goal.

Basic steps required to replace a process in memory:

1) Load the host process in a suspended state.
2) Open a handle to the main thread of the executable
3) Get the context of the suspended thread. In 64-bit the entry point to the process is located in RCX. A pointer to the PEB (Process Environment Block) is located in RDX.
4) Call ZwUnmapViewOfSection to unmap the image of the host process
5) Parse the section headers of the target process
6) Find the image base of the target process
7) Allocate space to contain the image of the target process
8) Map the PE header and each section of the executable to the newly allocated memory.
9) Update the PEB to reflect the new image base address
10) Call SetThreadContext to redirect execution to the target process
11) Resume the process

Here is an example of the script in action:

Replacing Internet Explorer with cmd.exe
Replacing notepad.exe with a 64-bit bind shell
In the last example, it is worth noting that the Microsoft code signature is not invalidated after loading the malicious process. This is due to the fact that code signatures are validated  prior to the main thread loading. This is an unintended but awesome side effect of this process.

Lastly, I have a 32-bit version of this script. It is however even more flaky than the 64-bit version. I suspect it's due to the fact that I don't account for relocations. Because instructions in 64-bit are RIP-relative, relocations are no longer necessary. I may release a more polished script for both 32 and 64-bit in the future once I become more familiar with the loading process.

Replace-x64-Process Help File


Enjoy and try not to get too frustrated when the tool doesn't work for you. Also, if you use the -verbose option, you'll find that much of the code in this script demonstrates the beginnings of a full-fledged PE parser. Perhaps more on that in a future posting... If you have any suggestions on how to increase the reliability of this tool, please leave a comment.

4 comments:

  1. The replaced process is of course restricted to the permissions (integrity level, SID, DACLs, etc.) that the host process provides. This limitation isn't easily overcome by the injector but rather the tool wielder must take this into consideration when choosing a host process. One of the few things the tool could possibly do is parse any manifest that is included with the replacement process to see if it's going to require elevation.

    One thing that this tool could do however, is check to see if ASLR and DEP settings of the replacement process are compatible with those of the host process.

    Finally, although you copy over the entire PE header, you don't re-patch the IAT. Since the host process has already been processed by the loader, you'll have to process the IAT of the replacement process including loading any libraries required by the replacement process's IAT that weren't already loaded by the host process.

    ReplyDelete
  2. Awesome comments. Thanks! I certainly overlooked the code to fixup the IAT. Fortunately, I have a good amount of code that will accomplish that. I'll begin incorporating IAT patching into the next version. Thanks again.

    ReplyDelete
  3. Valuable information and excellent design you got here! I would like to thank you for sharing your thoughts and time into the stuff you post!! Thumbs up

    ReplyDelete
  4. What might be useful would be an option to give the executable to replace with as a byte array instead of a file name, or to give a file name and a key and use AES or similar to decrypt the executable. Some AV have quite strict rules about "potentially unwanted programs" or to disable programs that might be used to kill time (like games). Would be a nice way to circumvent those :-)

    I can patch the line that calls ReadAllBytes, but having an official option would be nicer :-)

    ReplyDelete