Posts Tagged ‘memory forensics’

Lately I’ve had to deal with setups which had transparent full disk encryption and were pretty hardened. If you are wondering what ‘transparent full disk encryption’  means, that’s how I call solutions that encrypt your hard disk, but don’t require any interaction from the user to boot into the operating system. They usually accomplish this because they:

  • use secure boot and a TPM with key sealing (good)
  • they use proprietary software-only obfuscation to hide the key (bad)
  • use an external hardware device to store the keys without secure boot or key sealing (bad)

Most of the time the goal is to break out of a preconfigured application and the usual tricks like these ones, don’t really work:

However getting access to safe mode / start up repair does partially work for some of these setups:

Partially, because most of the options were not present and those that were present only gave me a cmd.exe which was disabled with a local group policy. An interesting approach the defence side took was replacing explorer.exe with an executable which did nothing. Even if you managed to break out of their application you still had nothing, no desktop, no menu, no buttons etc. For a few setups where the ‘startup-repair’ options seemed to work the encryption drivers did not load, resulting in an environment with no access to the target disk. In case you were wondering about network attacks, those were a no go as well, since the firewalls were strictly configured for ingress and egress traffic, based on ip/port/application and yes the connection themselves used TLS with client certificates and not vulnerable to man in the middle attacks.

Usually when I encounter these environment it still is possible to perform a variety of Direct Memory Access (DMA) attacks using tools like inception or pcileech. In these cases however this was physically not possible, either because there were no DMA ports available or just because I didn’t have the correct hardware with me to perform the attacks.

A common issues with all those setups however was the fact that the disk encryption software did not seal the encryption keys to a hardware security device like a TPM. This enables an attacker to create an image from the hard disk and boot this image on another computer. If the attacker also got a hold of the enclosure (USB key, smart card, obfuscated algorithm, unencrypted partition) holding the encryption keys it becomes possible to boot the disk image and fully control the victim disk in an untrusted environment.

In this blog article we are going to have a look at some of the things that you can do when you can boot a disk image of an otherwise unpenetrable environment. Please keep in mind that in part we are reinventing the wheel for two reasons:

  • Learning the nitty gritty details
  • Having a portable and understandable solution

There are solutions available that probably would enable you to achieve the same result, but for my personal taste I prefer to have something much more lightweight that can be easily ported between QEMU versions. Additionally you could also achieve the same result with the quick & dirty approach of booting the image in VMWare, pausing the machine, editing the memory file, resuming the machine. However I prefer QEMU since it allows full control over the entire process, due to the build in GDB server as well as customising the inner workings by editing/adding code and recompiling it. The following existing projects already wrap QEMU with cool and handy features if you want to use these type of setups to analyse malware or other applications:

Enough introduction of what we are going to do, let’s dive in and start elevating our shells to SYSTEM ;)
(more…)

Zeroization of information is a long standing practices on systems that handle highly sensitive data (usually cryptography systems), yet it’s something that isn’t done very often by most applications on your regular desktop environment. I’ve got no clue why, although I guess that it is because most people assume that if memory is not available anymore to their application it won’t be available to others. This however is a flawed assumption which is eagerly used by investigations that use memory forensics techniques. If you want to see the power of memory forensics take a look at volatility which is an excellent memory forensics framework. Just to give you an idea of how powerful memory forensics can be here is an example taken from the volatility blog:

Taking screenshots from memory files

Like you can see an investigator (or an attacker) is able to pull the complete layout of your windows from a memory dump, isn’t that impressive? So like you are probably guessing by now it should actually be best practice to zero memory before it’s released. In an ideal situation your operating system would take of this for you, but unfortunately it doesn’t. With ideal I mean zero it immediately when the programmer calls the *free*()  functions.

Dear OS makers could you implement it by default?

So to try and promote the use of zeroization in regular software I’ve decided to create a few simple wrappers that zero the memory before it’s released. The message to take away here is

*always zero your memory before releasing it*

These wrappers are nothing fancy and as usual can be found on my github.

As said before the wrappers itself are not that interesting, strictly speaking they are not even needed since you can also zero memory yourself before calling a function to free the allocated memory. The important thing to take into account when doing any zeroization of memory is to make sure that the function or technique you use is not optimized away by the compiler. Compilers have a nasty habit of optimizing stuff that you don’t want optimized. It’s however pretty convenient to just call a free function and not having to worry about zeroing the memory yourself. So the first thing I did was having a look at the general concept of memory allocation which is more or less something along the following lines:

  • Caller requests 100 bytes
  • Operating system allocates a bit more, say four bytes, so it becomes 104
  • Operating system stores the size of the requested memory in the extra allocated bytes
  • Caller gets a memory block of 100 bytes without knowing that it’s actually 104

That sounds simple enough so I decided to start with writing a wrapper for the free() functions to retrieve the size of the block to be free just like the operating system would do it. To my surprise however it wasn’t as easy as it conceptually sounded at first. I started to digging into the malloc() and free() and in my tests it seemed that they are just wrappers for HeapAllocate() and HeapFree(). That didn’t sound to bad at first until I landed in the wonderful world of heap management. If you want a good read on that check out this paper which does a very nice job of explaining it.

This is when I decided to discard the method of trying to retrieve the size of the memory block based on the received pointer and decided to just add a thin layer of “memory size management”. Stupid me though because as you will read later it’s actually dead easy to retrieve the size of a memory block. So I started to write some code that just implemented the conceptual method that I first encountered. This however means I would not only need to wrap the *free*() functions but also the *alloc*() functions. This resulted in the following code for the malloc() and free() functions:

/*
	Thanks TheColonial for reminding me of pointer arithmethic and re-educating me on it.
	Old unused code, left here in case someone prefers to do it this way.
	This code also wrapped the allocater to prepend the size of the allocated memory block.
*/
void *zmalloc( size_t size ){
	size_t newsize = (sizeof(size_t) + size);
	size_t *newmalloc = 0;
	void *originalmalloc = NULL;

	originalmalloc = malloc(newsize);
	printf("size %i\n",_msize(originalmalloc));
	if(originalmalloc == NULL){
		return NULL;
	}

	(*(size_t *)originalmalloc) = size;
	newmalloc = (size_t *)originalmalloc;

	return (void *)(newmalloc + 1);
}

void zfree(void *memblock){
	size_t *newmemblock = NULL;
	size_t size = 0;

	newmemblock = ((size_t *)memblock)-1;
	memcpy_s(&size,sizeof(size_t),newmemblock,sizeof(size_t));
	size += sizeof(size_t);
	SecureZeroMemory((void *)newmemblock,size);
	free((void *)newmemblock);
}

Now as the comment suggested I first failed at properly implementing this since I forgot that when you do pointer arithmetic the size of the destination pointer is used for the operations. So in my first code I ended up with 4*size_t being allocated which is kind of a waste of space. This was all great and so but when I saw the function definition of other memory allocation functions it didn’t really inspire me to continue. So I decided to have one more look at the whole “extract size from memory block pointer” issue.

I often learn a lot of new stuff when working out ideas or playing around, but now I just felt plain stupid see for yourself:

http://msdn.microsoft.com/en-us/library/windows/desktop/aa366781(v=vs.85).aspx

Do you see anything that could be remotely useful? YES most of the memory allocation functions have a size companion that will retrieve the size of the memory block. Which means I go back to only wrapping the free function and reducing the code to:

void zfree(void *memblock){
	size_t blocksize = _msize(memblock);

	if(blocksize != -1){
		SecureZeroMemory(memblock,blocksize);
	}
	free(memblock);
}

So this was a fun and interesting path to finally end up with the original idea of just wrapping the free functions. Hope you enjoyed this entry and that after reading this you’ll all be doing zeroing of memory before freeing it :)