.NET Machine Code Manipulation
This is the first entry in a series of blog entries describing GrayStorm, a memory reconnaissance platform released at DEF CON 23. In this entry, I will describe how to overwrite a Method’s Machine Code after Just-In-Time Compilation (JIT) with techniques implemented in GrayStorm.
Introduction to .NET Machine Code
When an application is compiled, IL code is generated through implicit compilation. The .NET Framework will then generate machine code at runtime. The common language runtime (CLR) is used by the framework to generate assembly code from IL code. IL code is an independent set of instructions that are converted to native machine code at JIT. When a method is about to be executed, the framework uses JIT to generate assembly code that the CPU can execute by accessing a JIT stub.
For example, the snippets below show C# -> IL -> Machine Code
Utilizing Machine Code
The JIT process leaves the executable memory with rwx permissions. Because of this, an attacker can overwrite a method’s memory at the machine code level. Note that even if the JIT didn’t leave rwx, once an application is injected into another memory permissions could be changed with VirtualProtect.
In order to locate the address of machine code, Reflection can be used on a method. In order to perform this task, a MethodInfo object will need to be constructed to obtain the attributes of a method.
In GrayStorm, a TreeNode viewer located in hierarchyViewer.cs is used to traverse through an AppDomain. When a method is selected from the tree, a MethodInfo object is created and stored. When the targeted Method has been just-in-time compiled, the raw function pointer to the machine code can be discovered. If the method has not yet been jitted, it will be forced with PrepareMethod.
The address IntPtr will now contain the function pointer to the executable machine code for a specified method.
Using this address, machine code can be read and written over.
Writing Machine Code
To write machine code over a method, a byte array must be construed that would be suitable for the targeted method. The possibilities are endless here and the imagination of the attacker can come into play! For demonstration purposes, a simple return true payload will be used.
The method I will overwrite is returnStatement.
The return true payload is as follows as will just return “1”.
In order to overwrite a method with new machine code, the address of the targeted method will be referenced and overwritten with the returnTrue byte array.
To see this in action, a video demo is prepared below:
Using This Chain
This chain opens up quite a few possibilities for attacking .NET applications post-exploitation. Machine code payloads can be constructed to steal parameters to methods and pass them along to logs/exfil (such as passwords, keys, e-mails, etc), change events (button presses, timers), overwrite core logic (password validation, licensing) and more. The bounds of this technique are up to the developer/attacker. Metasploit payloads can even be brought into play, given they handle parsing the PEB correctly.
Outside of Gray Storm
If you want to perform similar tasks without the use of GrayStorm a payload specific to your purposes can be created and injected into a target with GrayFrost.
For demonstration purposes, the application we will attack is below:
To construct an attack against the returnStatement method, the follow code can be used in an external program. Because we are constructing a payload before we have injected into the demoAttack program, a reference can be added to the executable from VisualStudio and Reflection can be done easier. To do this, simply right click on the project’s References -> Add new Reference -> select target executable
Reflection will now be a breeze as we can reference demoAttack directly as we have a reference to the namespace.
Once compiled, we now have an attack payload for our demoAttack. Bundling this executable into GrayFrost an injectable DLL will be created. Now while demoAttack is running and stuck in the “return FALSE” output, inject attackingDemo and it will change to “return TRUE”. For demonstration, I have prepared a video using GrayDragon to inject GrayFrost into my target.
Note: both applications were compiled in x86, but the same techniques work in x64 (with a x64 payload).
All resources used (demoAttack, attackingDemo & GrayFrost32.dll) are included here. Inject GrayFrost32.dll into demoAttack with any DLL injector you wish.
If you have any questions, do not hesitate to reach out to me!