Note: This blog is an updated version of a piece originally published in the March 2017 edition of eForensics Magazine
As a security researcher and part-time Incident Response (IR) analyst, I know that fine details are of paramount importance. The role requires ongoing research to understand an attacker’s actions on compromised machines. A typical research process requires examining hundreds, or even thousands, of artifacts to find the needle in the haystack.
But what if not all artifacts are created equal? What if specific artifacts enable the identification of attackers’ activities quicker and more accurately? This article discusses two artifacts we’ve identified as being significantly helpful when solving incidents: command prompt history and console output.
To contextualize the scenario, we developed an automatic system that collects real-time forensic information from compromised machines. The collection and analysis of such information is called "Live Response." The goal of this process is to identify and analyze the incident's trigger as quickly as possible. In Live Response, investigators capture volatile information that would not normally be present in a postmortem investigation.
Live Response holds many challenges for investigators. The fact that artifacts are being discovered from a live running system with an active adversary presents the following issues:
- It can be difficult to remotely access and run code on the compromised machine.
- It can be difficult to remain unnoticed by the attacker; the information-gathering process must be quick and stealthy.
- Operations must not "jeopardize the crime scene." (This is known as Locard's Exchange Principle.)
A SANS Institute whitepaper lists examples of commonly collected artifacts in Live Response. These include running processes, running services, current login information, open files, open shares, open handles, and so on. While building the forensic collection tool, we identified several artifacts beyond those in the whitepaper’s list.
As we’ve learned through investigating a wide range of attacks, it is common for attackers to use consoles (for example, Windows Command Prompt) to execute native commands as well as malicious code written in PowerShell, Python, and other scripting languages. This makes command-line history and console output extremely valuable when examining compromised machines.
Let’s examine how these artifacts can be retrieved without dumping memory or performing code injections.
Retrieving Windows Command Prompt History
The command history of an attacker’s console is extremely valuable in Live Response. Unlike the Linux shell (which stores command history in a file on disk, making the task of fetching command history trivial), Windows Command Prompt history resides only in memory.
The only native Windows option to retrieve command history is Doskey.exe. From host memory, Doskey retrieves all commands executed on a cmd.exe console (see example below). Doskey’s major limitation is that it is called from a Command Prompt session and can only retrieve commands executed during that session. For example, calling Doskey from cmd.exe (PID 1337) can retrieve only the history of cmd.exe (PID 1337).
|Figure 1: Doskey example|
As Doskey cannot retrieve the Command Prompt history of an external console, it can’t be used as a Live Response tool.
It is worth mentioning that the Volatility Framework offers a useful plugin called cmdscan. The cmdscan plugin searches process memory (csrss.exe or conhost.exe, depending on OS version) for commands executed via a console. Unfortunately, as with all volatility plugins, it requires a memory dump as input. As memory-dump creation requires a unique tool, is labor-intensive, and takes time, cmdscan cannot be used as part of Live Response. Our goal is to retrieve the commands an attacker has executed while the attack is underway.
Having no luck finding a native solution, we decided to dive into the task ourselves.
Doskey was revisited and reverse-engineered, finding two valuable kernel32 functions that are undocumented on MSDN:
- GetConsoleCommandHistoryLengthW - retrieves the buffer size of the Command Prompt history, and expects a console process’s name as input.
- GetConsoleCommandHistoryW - retrieves Command Prompt history as a null-delimited buffer of strings, and expects the buffer size and process name as input.
Figure 2: IDA view of Doskey.exe
The code below extracts the command history, similar to the operation of doskey.exe:
After execution of GetConsoleCommandHistoryW, output_history_buff holds a null-delimited buffer with the Command Prompt’s entire command history.
Attaching to the attacker’s console
For the command history from the attacker’s console to be obtained, our application must be ‘attached’ to it. When calling a process from an ‘unattached’ console, the two undocumented Doskey functions operate similarly to the following Windows API:
- GetConsoleHistoryInfo - retrieves the history settings of the calling process's console.
The function’s MSDN page states that if the calling process is not a console process, the function fails and sets the last error to ERROR_ACCESS_DENIED.
The following two documented Kernel32 APIs enable this obstacle to be overcome:
- FreeConsole - detaches the calling process from its console
- AttachConsole - attaches the calling process to the console of a specified process
These APIs should be executed prior to GetConsoleCommandHistoryLengthW and GetConsoleCommandHistoryW.
Lastly, the following code can be used to print the commands from output_history_buff:
Running the code above on the console in Figure 1 results in the following output:
Retrieving the console output buffer
The ability to retrieve the Command Prompt history of cmd.exe using AttachConsole fueled our greed; we also wanted to see the entire console output buffer, which includes not only the attacker’s executed commands but the commands’ output as well.
Surprisingly, this was simpler than retrieving command history. Once the application is ‘attached’ to the attacker’s console, the Windows GetStdHandle API obtains a handle to the specified standard input, output, or error. In this case, the STD_OUTPUT_HANDLE was used to obtain a handle to the standard output.
The next phase is to retrieve information about the process's console screen buffer using the Windows GetConsoleScreenBufferInfo API. The API returns the screen buffer's attributes in the CONSOLE_SCREEN_BUFFER_INFO structure. This information is used in order to read data of the right size from the console buffer.
The following code was used to print the output and skip empty lines:
Running the above code on the following console’s PID generated the output below.
|Figure 3: Command Prompt Example|
The same code can be used to retrieve the output on any kind of console, including PowerShell and Python.
Live Response can be used to resolve incidents quickly and efficiently. Of the artifacts collected, some can provide more value to IR analysts than others. We concluded that the Command Prompt’s history and console output are highly valuable. We therefore recommend that more analysts start to collect them as part of forensic investigations.
There may be additional evidence that help in Live Response investigations, and the community should be encouraged to share any other relevant information.
Source code is available at the IllusiveNetworks-Labs GitHub.
*Update – On September 1, 2017, FireEye released a great two-part blog called “Monitoring Windows Console Activity”. In the blog, they review the concept of using a filter driver to capture console input and output on a Windows 8 and later systems.