Hello. In this video, we will talk about how REVEN can help with analyzing a
malware. More specifically, we'll analyze a light recent version of the Uroboros
malware. We recorded a trace that's about 50 seconds long where we started the
malware dropper and from that trace we'll see : First, that by connecting
third-party tools we can detect compromission of the system and use the
hooks in place as entry points for our analysis. Second, by using the taint
engine, we can get a good idea of the dropping mechanisms the malware used.
Finally, by using the Python API in conjunction with manual analysis, we can
dump all binaries involved including those that don't appear on disk or
somehow try to hide.
This is what the trace looks like. It's pretty simple. We start the dropper
binary and then it looks like nothing happens. About 50 seconds later, we
stop the recording. So the first step here is to check for compromission.
Thankfully, REVEN integrates well with the tool volatility and although usually
using it will require having a core dump of the virtual machine, here, it can
simply be connected to any moment of the recorded trace. It's about 3.5 billion
instructions long, so if we assume that the malware is fully loaded near the end
of the record, we can use the command callbacks at about 3.5 billion
instructions and see what happens. Pretty quickly we get the first results.
This one is particularly interesting. There's a generic callback set by an
unknown module. Let's stop the command here. Let's look at that address to
see if it's executed somewhere in the trace. Yes, it is multiple times.
It belongs to an unknown binary. This might happen when we cannot find a
binary on the disk. It's called on process termination which means it's
probably been set up through a call to CreateProcessNotifyRoutine. Let's
look for that.
There's one match. Let's look at the first argument to that call. This is
the address of our unknown call back. If we get further inside CreateProcessNotifyRoutine
by skipping a few things.
We see the table where the callback structure is stored. The algorithm here
is going to look for the first empty entry in this table to store our callback.
Right there. That means we could use the memory history on that table to
detect row callbacks that are not set up using the CreateProcessNotifyRoutine.
We could also monitor other types of callbacks like CreateThreadNotifyRoutine.
In this case, there aren't any. Or we could look for Network callbacks.
There are some. And again, they are called by our unknown binary. So we know the
malware is also setting of hooks on network activity. Note here, we could have
also analyzed the array of callbacks just like we did earlier. About this unknown binary.
Perhaps it's time we try to see where it's coming from. So let's have
a look at the dropping mechanism. I take any executed byte of the unknown
binary and taint it backwards. From my current moment in time up to the start
of the trace taint one byte.
In a couple of seconds, what I get here is a list of copies. Note that the
results go back more than a billion instructions before the taint starting point.
Actually, what we see here is the entire dropping process including what's
happening inside the kernel. Now, all I have to do is look at each successive
copy to understand what happens. The first one takes me to a memcpy call.
Looking at the destination buffer, we can verify that we are indeed writing a PE
image. So let's place a bookmark here.
The second item in the list doesn't tell me much. But the third one shows we're
using a driver called usbehub. Now that's interesting because there exists a legit
Windows driver called usbhub.sys and this name usbehub is obviously an
attempt at hiding in plain sight. Again let's place a bookmark. The next
operation in the list is a memcpy instructed by an anti-device IO control
file. And if we look at who called it. We find a binary called pxinsi64. This
binary seems to be the junction between user mode and kernel mode for this
malware. So again, let's place a bookmark.
Continuing the analysis we find our binary, until we finally reach the
initial dropper binary. Now, that we've seen all stages of the dropper.
Let's see how we can actually, extract the binaries involved
so we can load them in IDA analyze them etc. Going back to the first memcpy
we can see what is going to be the based address of our binary. But know that
this binary does not appear on disk so there's no file we can dump. And dumping
the loaded image right here is not going to result in a clean binary. So instead
we'll use volatilities moddump module to reconstruct the PE file from the loaded
image. Let's assume the binary is still loaded toward the end of the trace and
launch the appropriate command to the base address.
Well, it didn't work! What's going on?
If we look at the base address toward the end of the trace indeed the
magic has disappeared. Using the Memory History on that and looking at the
content before the last write it's easy to see that this magic has been overwritten.
So let's try to dump it again but this time before the magic has
been overwritten.
This time it works. I can now load that PE file into my favourite disassembler.
The next stage in the whole chain was the usbehub driver so let's see what's
coming from and how to dump it. To do so, I'll use the same methodology
we used on the unknown driver and taint any executed bytes.
From my current transition again backwards to the start of the trace.
There are only a few entries
so I can take them in chronological order this time.
The first one is just the disk loading the file. Then the initial dropper copy.
Followed by an NTWriteFile. Next, the driver is loaded and we see this
involves a cryptographic validation with CiValidateImageHeader here.
Finally, the binary is copied so we can be loaded.
In this chain, the best moment to dump the file is during the NtWrite call since this will get us a valid PE file
directly including any signature the driver may have. So let's dump the memory buffer that got written.
Going back to the NtWriteFile call. Right here.
And if I skip the syscall and the windows on windows 64, back to the userland WriteFile call here.
The buffer is mentioned there. And this is its size. Using this
information I can run a script to dump the memory.
This is the location and this is the size.
Now, let's have a look at this file. First, it's interesting to note that dumping
the file as is preserved the driver's signature. Then I can load this file in IDA.
Which tells me it's the infamous Vbox driver. Finally, using the open source
plug-in ret-sync. I can sync IDA views on REVEN.
Loading our connector plug-in.
The two views are now in sync and I can get more context, explore an executed path, etc.
Now, we'd like to dump the last binary called pxinsi64.
Again, I will taint a byte and try to look for a call to NtWriteFile.
There's one here. Going back to the userland call again skipping the windows on
Windows 64. This is the address and this is the size. Let's dump it.
What happens if I now load this file in IDA.
Well, it appears the binaries don't match.
What's happening then?
Well, the taint suggests there's a lot of activity that we skipped here. So maybe,
we should start from the end instead of starting from the beginning.
What is copied here?
Going back to the origin of that copy in userland.
I see that the code is actually copying over itself.
If I search for matches to that call.
There are some! By iterating over these. I can see that the code seems to be
overwriting multiple sections of itself. And although, it writes over the loaded image,
if we look at the source back when we wrote the first section.
It's in that it's stored as a regular PE file. Perhaps in the binaries resources.
So, let's try to dump this source instead.
Here is the origin and here is the last written section which I can use to
compute the size of the dump. Now, let's load that in IDA and see if the
synchronization works.
And indeed, the binaries now seem to be similar.
To sum up, we've been able to find a code responsible for the system's compromission
even though this code was running in kernel mode.
We've also analyzed the process dropping a malicious driver and dumped the
various stages as very PE files even though they did not touch the disk or
were altered in the process to allow analyzing with regular static tools.
If you found this content as interesting as we do and I want to know more about it
or REVEN in general do not hesitate to follow our blog for more content or
contact us via the website or contact at Tetrane.com.