Flare-On 7 – Task 9

This year’s FlareOn was very interesting. I managed to finish it with 87th place. In this small series I will describe my favorite tasks, and how I solved them. I hope to provide some educational value for others, so this post is intended to be beginner-friendly.

Overview

In this task we are provided with the following package (password: flare). It contains a 64 bit PE (crackinstaller.exe), and a description that says:

What kind of crackme doesn't even ask for the password? We need to work on our COMmunication skills.

By the name and the description we can guess that it is going to be an installer for some other components, and also that some knowledge about COM (Component Object Model) is going to be required.

Roadmap

Before we go into details of the solution, lets see the roadmap of the elements that we are going to discover.

The following diagram presents the loading order of particular components involved in this task:

The elements with solid borders are loaded from files. The elements with dash line borders are loaded in-memory only. Yellow – executes only in a usermode, blue – only in a kernelmode, gray – part in usermode and part in kernel mode.

Tracing

The crackme runs silently, without displaying any UI. In order to see what is happening during execution, we can use some methods of tracing the activities (i.e. ProcMon). I wanted to see what exactly are the APIs called from the main application, so started by running it via Tiny Tracer. In order to get the complete trace, it must be run as an Administrator.

This is the trace log that I obtained:

https://gist.github.com/hasherezade/668c9f1d42112fe7c0eaca781a6e28e5#file-crackinstaller-exe-tag

It gives a pretty good overview what is going on at what points of the code. Let’s go through the log first, and see how much can we discover by reading the order of APIs called.

The first fragment that triggered my interest is the following:

2f13;kernel32.CreateFileW
2f3a;kernel32.CreateFileMappingW
2f59;kernel32.MapViewOfFile
2f80;kernel32.UnmapViewOfFile
2f89;kernel32.CloseHandle
2f92;kernel32.CloseHandle
1ff5;advapi32.OpenSCManagerW
2013;advapi32.OpenServiceW
2074;advapi32.CreateServiceW
2082;advapi32.CloseServiceHandle
2094;advapi32.OpenServiceW
20aa;advapi32.StartServiceW
20bd;advapi32.CloseServiceHandle
20e7;kernel32.CreateFileW
20fa;advapi32.CloseServiceHandle
2d98;kernel32.VirtualAlloc
2e1a;kernel32.DeviceIoControl
2e33;kernel32.CloseHandle
1ee8;advapi32.OpenSCManagerW
1f06;advapi32.OpenServiceW
1f1c;kernel32.SetLastError
1f2f;advapi32.ControlService
1f58;advapi32.CloseServiceHandle
1f6a;advapi32.OpenServiceW
1f7b;advapi32.DeleteService

By reading it we can find that the crackinstaller:

  1. drops some file (CreateFileW, CreateFileMappingW, MapViewOfFile, CloseHandle)
  2. installs it as a service (OpenSCManager, OpenServiceW, StartService)
  3. sends an IOCTL (DeviceIoControl) – most likely the receiver is this newly installed service, that is a driver
  4. uninstalls the created service (OpenServiceW, DeleteService)

Another interesting fragment of the log follows the previous one:

658a;ntdll.RtlAllocateHeap
2199;shell32.SHGetKnownFolderPath
2203;combase.CoTaskMemFree
2230;kernel32.CreateFileW
2257;kernel32.WriteFile
6522;kernel32.HeapFree
2284;kernel32.CloseHandle
22af;shell32.SHGetKnownFolderPath
2319;combase.CoTaskMemFree
232a;kernel32.LoadLibraryW
234f;kernel32.GetProcAddress
2355;credhelper.DllRegisterServer

In this fragment we can see that some file is being dropped (CreateFileW, WriteFile). Then it is registered as a COM server.

So, at this point we can expect two elements are going to be installed: a driver (which is uninstalled right after use) and the COM component. In order to find them we must see what are the files that are being dropped. We can load the generated .tag into x64dbg, and set breakpoints on the interesting functions.

The dropped components

First I set breakpoints at CreateFileW to see what are the paths to the dropped components. We can collect them from those paths once they are saved.

As we observed before, there are two elements dropped:

  1. The driver: da6ca1fb539f825ca0f012ed6976baf57ef9c70143b7a1e88b4650bf7a925e24
    • dropped in: C:\Windows\System32\cfs.dll
  2. The COM server: 4d5bf57a7874dcd97b19570b8bad0fa748698671d67593744df08d104e6bd763
    • dropped in: C:\Users\[username]\AppData\Local\Microsoft\Credentials\credHelper.dll

The first element executed is the driver, so this is where I started the analysis.

The dropped driver (cfs.dll)

As we could find out by reading the comments on Virus Total, this is a legitimate, but vulnerable Capcom driver, that was a part of the Street Fighter V game (more about it you can read here and here). Due to the vulnerable design, this signed driver allows for execution of an arbitrary code in kernel mode. By sending a particular IOCTL we can pass it a buffer that will be executed (it is possible since the driver disabled SMEP as well). This vulnerability makes it a perfect vector to install untrusted kernelmode code on the machine – that feature is used by the current crackme.

First, the driver is dropped from the crackinstaller into:

C:\Windows\System32\cfs.dll

And installed as a service. Its path is:

\\.\Htsysm72FB”\

Then, the aforementioned IOCTL is being called. Below you can see an example of the parameters that were passed to the IOCTL (DeviceIoControl function), along with their explanation:

1: rcx 00000000000001E4 ; driver
2: rdx 00000000AA013044 ; IOCTL
3: r8 0000007B3EAFF6C8 ; input buffer
4: r9 0000000000000008 ; input buffer size
5: [rsp+28] 0000007B3EAFF6C0 ; output buffer

The input buffer turns out to be the following small stub, written in additionally allocated executable memory page:

025E86BD0008 | sti
025E86BD0009 | mov rdx,25E86AF2080 ; address of: driver.sys
025E86BD0013 | mov r8d,5800 ; size of the driver
025E86BD0019 | mov r9d,3170 ; address of DriverBootstrap function
025E86BD001F | jmp qword ptr ds:[25E86BD0025] ; function inside crackinstaller.exe

The stub sets parameters, that are going to be used by the next function. Then it leads the execution back to the crackinstaller.exe – to another function (at RVA 0x2A10). Although the dropper is a userland application, this part of the code will be called in a kernel mode – because the execution to this function is redirected via the kernelmode component.

This function is responsible for loading yet another driver (driver.sys) that is also passed as one of the parameters.

By looking at the loading function, we can see that this driver is going to be mapped manually into the kernel-mode memory. The “DriverBootstrap” function exported by driver.sys is a kernel-mode Reflective Loader variant (similar to this one).

After this installation, the first driver (cfs.dll) gets unloaded and uninstalled – however, the second one: driver.sys – persists in the memory (in contrast to usermode applications, the memory allocated by a driver is not freed automatically when the driver is unloaded).

What I initially did, was dumping this driver.sys in a user mode (before the IOCTL was executed), and analyzed it statically. Then, I tried to load it as a standalone driver. However, it was a mistake. This driver has a buffer that is supposed to be overwritten on load, in kernel mode. At this stage, it is not filled with the proper content yet. This buffer is crucial for decoding a password. Since I overlooked the part that was overwriting it, although I understood the full logic of the driver, the output that I was getting was a garbage. After consulting it with other researchers, confirmed that the output was supposed to be a valid ASCII – so I realized that I missed something on the way, and I shouldn’t have been making shortcuts and dumping the driver in the userland. I then decided to walk through the full way of loading the driver in the kernel mode, and dumped it again in kernel mode, just before its execution.

The driver.sys

Before we move further to the dynamic analysis, let’s have a look at the driver.sys in IDA. As I mentioned earlier, dumping this driver in userland is not a perfect option (some important buffer is filled on load in kernel mode). However, for now, this version is good enough for the static analysis of the driver’s logic.

As always the execution starts in DriverEntry.

In our case, this function redirects execution to another one, which I labeled as “driver_main”.

Click to enlarge

Some interesting strings inside the driver are obfuscated – they are dynamically decoded just before use. There are various ways to retrieve them – I have chosen to write a simple wrapper in libPeConv that allowed me to call the decoding function without analyzing it, and apply it on the chosen buffers.

This module (driver.sys) is a filter driver with an altitude of 360000, which means “FSFilter Activity Monitor”.

The main function is pretty simple: its role is to initialize the device, and to set the callback that will be used for event filtering. The function CmRegisterCallback sets the callback that will be triggered each time an operation on Windows Registry is executed.

The routine that is registered to handle the callback (DispatchCallback) must follow the prototype of EX_CALLBACK_FUNCTION.

The second argument (denoted as Arg1) is of type REG_NOTIFY_CLASS – it informs about what type of the operation triggered the callback. In our case the event is processed further only in the case if the value of the REG_NOTIFY_CLASS is 26 (RegNtKeyHandleClose ?). The next argument (Arg2) holds a pointer to the structure of different types, depending on the value of the previous one (Arg1). In our case, Arg2 holds the pointer to the UNICODE_STRING with the name of the operated Registry Key.

The name of the key is copied into additionally allocated memory with a tag “FLAR”. It is compared further with a dynamically decoded string:

Only if the name of the key matches the hardcoded one, the next, more interesting part of the code is executed. If we checked the changes in the registry made during the execution of crackinstaller, we will notice, that this registry key is created on the installation on the COM server. So, this is how those components are tangled together.

The next part of the driver’s code decrypts some mysterious buffer. We can recognize the involved algorithms by their typical constants. First, SHA256 hash is calculated from a buffer hardcoded in the driver (denoted as “start_val”). Then, the hash is used as a key for the next algorithm, that is probably Salsa20 (eventually it may be a similar cipher, ChaCha).

Click to enlarge

At this point we can guess that our next goal is to get this decoded buffer.

In order to get the valid solution, we need to first get the overwritten version of the above driver, so, the one that is loaded in the kernel mode.

Notes on kernel mode debugging

Before we can start kernel mode debugging, we need to have an environment set up. The setup that I used is almost identical to this one. Yet, there are few differences that I am going to mention in this part.

First of all, we need a 64 bit version of Windows – I used Windows 10 64 bit VM on VirtualBox (linked clones for Debugee and Debugger).

As always, the usermode analysis tools (i.e. x64 dbg) as well as the crackme itself, are going to be run on the Debugee VM. The kernel mode debugger (WinDbg) will be run on the Debugger VM, connected to the Debugee.

Configuring the Debugee VM

There are few more steps (in addition to the ones described here) that we have to take in order to configure the Debugee VM. In case of Windows 10, explicitly setting the debug interface is necessary (by default, even if we enable debugging on the machine, it is going to be set in a local mode, and we will not be able to connect the Debugger VM). Since we are going to establish a debug session over a serial port, the following settings apply:

bcdedit /dbgsettings serial debugport:1 baudrate:115200

We can test if the proper options are applied by deploying the command dbgsettings without parameters:

bcdedit /dbgsettings

Expected result:

DbgSettings after

We need to remember that on 64 bit Windows a driver must be signed in order to be loaded. This is not gonna be an issue if we want to load the first driver: cfs.dll – because this is a legitimate, signed driver. However the second one: driver.sys – which is more important to the task – is not signed. It loads just fine as long as the first, signed driver is used as a loader. But for the sake of the convenience, at some point we are going to load the driver.sys as a standalone module. To be able to do so, we must change an option in bcdedit, in order to allow unsigned drivers to be loaded. It can be done running this command on the Debugee machine:

bcdedit /set TESTSIGNING ON

After changing the settings, the system must be rebooted.

We also have to disable Windows Defender, otherwise the crackme will be mistaken as a malware and removed.

Dumping driver.sys in kernel mode

In order to understand what exactly is going on, and not to miss anything, I decided to walk through the full flow since the IOCTL is executed inside cfs.sys, till the driver.sys is loaded in memory.

To start following it in kernel mode, we need to locate the address of the function inside cfs.dll that is going to be triggered when the IOCTL is sent. Let’s open cfs.dll in IDA, and see the function registered to handle IOCTLs:

Inside we can see the IOCTLs numbers being checked, and then the function to execute the passed buffer is being called:

In the next function (that I labeled “to_call_shellcode”) we can see the operations of disabling SMEP, calling the passed buffer, and then enabling the SMEP again:


The function disabling SMEP :

So, we need to set the breakpoint at the address just after the function disabling SMEP returns, because in this line there is a call passing execution to the shellcode. This happens at VA = 0x10573 (RVA = 0x573):

If we step into that call in WinDbg, we will be able to follow the passed shellcode executed in kernel mode.

Before we will go to set the breakpoint in kernel mode, we need to load the crackinstaller into a userland debugger (such as x64dbg) and set the breakpoint before the DeviceIoControl function is called.

Then, on the Debugger machine (connected to the Debugee where the crackme runs) we deploy WinDbg and connect to the Debugee.

We can set a breakpoint on load of the cfs.dll in WinDbg by:

sxe ld cfs

After that, we run the crackme. The breakpoint should hit and the Debugee freezes. With the help of the following command:

lm

We can see the list of all the loaded modules, and find the module of our interest on the list:

If we want to view this list from the Debugee perspective, we can also use Driver List by Daniel Pistelli.

Now, let’s set a breakpoint on the offset inside the driver, that executes the shellcode:

bp cfs + 0x573

And we resume the Debugee. Lets step over the breakpoint at DeviceIoControl in x64dbg. Now, in the Debugger VM, we can see again that the breakpoint has been hit.

Opening the Disassembly window allows us to see this line in the original context:

Click to enlarge

As we can see, it is the same code fragment that we observed in IDA before, analyzing the relevant fragment of cfs.dll.

Using the command:

t

We can step into the call. And what do we see? The very same shellcode that we observed being passed to the DeviceIoControl!

The address moved to RDX is the address of the buffer holding driver.sys.

Now as we know from the previous analysis, the execution should be redirected back to crackme.exe, but the execution will take place in a kernel mode. We can set the breakpoint at the first jump which will do the redirection

bp [address]

After setting the breakpoint, we can resume the execution (“g”) and once the breakpoint is hit, step in again (“t”):

This is where we end up:

…and it is exactly the function at 0x2A10 in crackinstaller.exe, that we found before. As we know, this function will do the modifications in the driver, and then redirect execution to there, inside the DriverBootstrap function (RVA = 0x3D70 , raw = 0x3170).

By analyzing the flow of the corresponding function in crackinstaller, we can guess that the redirection happens at RVA = 0x2c26

inside crackistaller.exe

Let’s set a breakpoint there, and resume the execution.

At this point we can see the function PSCreateSystemThread is being called. The start routine is going to be the DriverBootstrap function.

The address of the bootstrap function is stored in RAX register:

At this point the driver is in the raw format, so we know that the raw address of the bootstrap function was used: 0x3170. By subtracting it from the whole address, we can get the driver’s base. By looking up this address in the Memory window we can see that indeed this is where the driver has been loaded:

Now it’s time to dump the driver. We can do it with the help of command .writemem. We need to supply it the path where we want to save the dump, and the range to be dumped. The size of the driver was supplied to the shellcode, and it is 0x5800. So, we can dump the range in the following way:

The new version dumped as “mydriver.sys”

After having the driver dumped, we can see what was patched. The comparison done via PE-bear:

Comparison – the original vs the modified

The patched content is the buffer that was used to derive the Salsa20 key (the “start_val” is filled with a string “BBACABA”).

Extracting the password in kernel mode

After the driver.sys is loaded in the memory, the crackinstaller.exe installs the COM server. On installation, the COM server creates the Registry key with the server GUID: “{CEEACC6E-CCB2-4C4F-BCF6-D2176037A9A7}\Config”. Creation of this key triggers the filter function inside the driver.sys to decrypt the hardcoded password. Our next goal is to fetch this password from the memory while it is being decoded.

Finding of this password can be achieved easily – all we need to do is to set a breakpoint in WinDbg, that will be triggered after the password is decoded, and then dump the output from the memory.

Yet, setting the breakpoint on the function of the reflectively loaded driver would be very inconvenient. Reflectively loaded driver will not be listed among the loaded modules, so we cannot reference it by its name. We also don’t know the base at which it was loaded. So, this is the point where it comes very handy to load the driver.sys independently.

For this part, we are going to use the patched version of the driver.sys – the one that was dumped as mydriver.sys in the previous part.

Loading the driver.sys as a standalone driver

Once we dumped the modified version of the driver, we can load it as an independent module. However, now the loader is not signed, so it won’t load in Windows unless we disable signature checking in the bcdedit (as mentioned before, reboot is required each time we change the settings):

bcdedit /set TESTSIGNING ON

We install it on the Debugee VM:

sc create [service name] type=kernel binpath=[driver path] 
sc start [service name] 

Let’s break the execution via Debugger VM (WinDbg : Debug -> Break) and see if the driver.sys is present on the list of the modules, using the command:

lm

We should see it on the list, just like on the example above.

Dumping the password from the memory

Now we can set the breakpoint inside the filter function. As mentioned before, it is gonna be called each time when some registry key is read/written. Then the name of the key is going to be compared with the hard-coded one (which is dynamically decrypted). If the name matches, another buffer is decrypted with the help of Salsa20. So, the password decryption is executed immediately when the COM server creates this key.

We can set the breakpoint after the key name verification is passed (RVA = 0x48C9):

bp driver + 0x48C9

In order to trigger the event, we need to use the the credhelper.dll now, and run the DllRegisterServer function. It can be done just by running (on Debugee):

rundll32.exe credhelper.dll,DllRegisterServer

This will trigger the breakpoint that we can follow in WinDbg…

Let’s set a breakpoint at the address where the Salsa20 algorithm was executed (it happens at RVA = 0x49AC):

driver.sys – IDA view
bp driver + 0x49AC

After that we can resume the execution

g

…and the breakpoint will be hit:

At this point, the address of the output buffer is in the R8 register. So we need copy this address to the memory view. Now we can step over the function.

And the decryptet content got filled in the buffer that we previewed:

So this is the password: “H@n $h0t FiRst!”.

Now we need to learn how to use this password to decode the flag…

The COM component

The driver.sys is quite small, and there is nothing more in it to decode, so I guessed the next pieces of this puzzle are hidden somewhere in the COM component. Let’s take a look…

We aleady saw in the Pin tracer log. that one function from this DLL is being called:

2355;credhelper.DllRegisterServer

If we open the credhelper.dll in IDA, we can see that this function is probably the one responsible for decoding the flag:

We can see the registry keys “Password” and “Flag” being referenced.

However, if we take a closer look, we will see that the function responsible for setting the Flag is not inside the DllRegisterServer.

There are two unreferenced functions that manipulate the same registry keys:

The first one, reads the value of the Password from the registry, and initializes some structure with its help (snippet here).

The other is responsible for decoding the Flag (snippet here).

I guessed that the “Password” must be the string decoded from the driver.sys. So, we need to fill it in the registry, and then call those functions in proper order – probably using the COM interface.

This should probably be the “right” way to solve this task. However, when I was taking a closer look at those functions, they started to remind me something familiar: the functions used by RC4 encryption algorithm, which is commonly used in malware.

So, my guess was:

  1. The function that I denoted as “get_password_value” was an RC4 password expansion function – it was initializing the context with the password (“H@n $h0t FiRst!”).
  2. The function that I denoted as “set_flag_value” was using this context, and decoding a hardcoded buffer by the RC4 decryption algorithm

I dumped the hardcoded buffer, and decided to check those assumptions using CyberChef. It turned out correct: S0_m@ny_cl@sse$_in_th3_Reg1stry@flare-on.com

So, the final flag was RC4 encrypted, with the password extracted from the driver.

About hasherezade

Programmer and researcher, interested in InfoSec.
This entry was posted in CrackMe, KernelMode, Tutorial and tagged , , . Bookmark the permalink.

6 Responses to Flare-On 7 – Task 9

  1. Pingback: Protected: FlareOn 7 – Task 9

  2. JavadYasari says:

    Nice
    will be good if FireEye guys publish ctf sources too

  3. MaxPayne says:

    When you load the second driver manually (as a standalone driver) don’t you have to change the decryption key manually (in memory) to the correct key (which is “BBACABA”) to get the correct password? because when i was solving it i kept loading the driver manually and i always got a wrong password.

    • hasherezade says:

      read carefully, and you will see that I already wrote about this problem. I don’t have to replace any key in the memory, because I use the version of the driver.sys that I dumped in kernel mode, which is already patched with the correct “BBACABA” key.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s