.NET Heap Objects
This post is meant to be read in conjunction with my whitepaper, Acquiring .NET Objects from the Managed Heap located here, and I will be discussing how to find any object on the CLR heap in .NET both both x86 and x64 and CLR runtime 2.0 and 4.0. Objects contain a plethora of information useful in attacking / reversing such as what fields (instance and static) and the instance methods it contains. I will show how to construct an arbitrary object and how that object can be used to locate any other object of that type on the heap, much like you would with SOS Debugging Extension (SOS.dll) in Windbg.
It is worth noting what versions of .NET exist and what CLR they operate in…
We will only care about the major version of the runtime for the remainder of this post (each .NET version has several releases).
Also, I will only be discussing the 2.0 and 4.0 CLR version as I do not feel 1.0 is relevant for attacking modern applications. s
In order to start finding objects, it must be known how to find them and what they look like in raw memory. For this we will:
.NET CLR 2.0 use mscorwks.dll
.NET versions 4.0+ use clr.dll
Naveen Srinivasan has a script to automatically detect the version of the CLR and which sos.dll to use…
Also, having windbg symbols setup is never a bad idea…
CLR 2.0 and Object Primer
Now that we have Windbg running how we need it to figure out where objects live and look like, we can begin to examine them.
Start with !Dumpheap to find a method table of interest.
Using a MethodTable (MT) address from above, run !dumpheap -mt <address>
Use !dumpobj / !do on an objects address to show information about it
That’s cool and all.. but how about in a running application?
First, an object of a given type will need to be construed so the Method Table address can be located. This is easy with .NET Reflection and can be done like so for any object (Ones requiring parameters is a little trickier).
To get the address of an object we have access to, we can use a local methods parameter addresses to trick an object into an Intptr. Note the use of unsafe code… that doesn’t matter when injected into an application as it is just a compile option!
Using windbg, the offset of &objectPointer was somewhere in memory and I used !dumpobject on each location until I discovered for both the 2.0 and 4.0 CLR on x86 the wantedObject was at a negative offset of 3.
This gives the address of a raw object! Turns out in the .NET CLR Objects are actually just pointers back to their object table!
So we clearly now have an object pointer to the PURE object for whatever we constructed… however, how do we find all objects of this type? We can use the method table!
The method table is the first 4 bytes of the address of the object
20 48 68 00 changed for endianess is 00684820 which matches the method table from above.
Dumping the method table some more information about objects, as shown above.
This just tells us the address of the pure object, 0296a0cc, which we had from above. Thus, it is shown that each object of a given type will have the same Method Table pointer in the first four bytes of the object table.
What if we had more than one of these objects?
I made three more..
And we see the original one, at 0296a0cc, and then three more..
Well, we now can constructed an object, grab the raw pointer to it (which is somewhere within what I call the ‘object heap’) and look at the address of its Method Table.
How can we grab objects that are already instantiated?
I use a brute force approach to locate other objects of a type. Because I now know where the object heap lives, I can do a brute force scan.
By taking the address of the object I knew about, I can search down the heap by jumping over the size of each object and travse up the heap in 4 byte increments. Each object location will be compared with the 4 bytes address to the Method Table the original object had.
The most questionable part of the above pseudo code is getting an IntPtr back to an actual object in .NET. We already looked at manipulating pointers to take an objects IntPtr and we can use similar logic to put an IntPtr back into an object.
!dumpheap still shows objects around the same addr range as CLR 2.0… hover around the ~02700000 range and grows in both directions
Does the 4.0 CLR MethodTable information change that is used for a signature of the object?
First four bytes are still the Method Table!
Rinse and repeat steps from the 2.0 CLR. Note that the pointer offsets will change, though.
Following the same steps as x86, let’s figure out where objects are and what their structure looks like.
1st 8 bytes = Method Table!
000007ff002051f0 = Method Table
002051f0 000007ff changed for endianess is 000007ff002051f0 which matches the method table from above.
So where is this stuff on the stack?
After much tweaking with my local variable range, I discovered that if there are two local variables between the object then it will present itself!
Address of objectPointer: 0059d9a0 address of objectPointer2: 0059d9b0
and we see our object at 0000000002f2e610
So now we can say from objectPointer, we need to go one in to get the object pointer
Success! We now have the pointer to our 64bit object of memoryHijacker.abc
We can use the same method as our 32bit signature and just compare bytes of the Method Table. Note everything is now 8 bytes instead of 4 as we are in x64 land.
And to restore the object pointer when we have a match just reverse what we did to find the raw object IntPtr.
The 4.0 CLR does not requiring changing the x64 signature!
AutoThink has the source code required to launch the automated attack against ThinkVantage to recover a windows password. The key points are
1.) Utilizing reflection to construct the object of interest
2.) Obtaining all objects on the managed heap of the matching type
3.) Finding the property of interest… in this case, the WindowsPassword (the 15th location of the object’s properties).
It is important to note that I wrapped the calls for getting properties and fields in threads. This is because occasionally the “.toString()” method causes complications and results in an application hang or crash. This practice makes sure that our target won’t do either.
Feel free to read the code in the git repo and ask me if you have questions about it!
Objects on the .NET Managed Heap can be acquired by an attacker once an application capable of performing the above techniques is introduced into an applications memory space. If an object is instantiated locally in order to find its Method Table, the heap can be brute forced to locate any object declared in the applications runtime!
Utilizing more reflection, instance methods can be invoked and variables can be changed. The limits of locating runtime objects is up to the user!