Training
Get a free hour of SANS training

Experience SANS training through course previews.

Learn More
Learning Paths
Can't find what you are looking for?

Let us help.

Contact us
Resources
Join the SANS Community

Become a member for instant access to our free resources.

Sign Up
For Organizations
Interested in developing a training plan to fit your organization’s needs?

We're here to help.

Contact Us
Talk with an expert

Month of PowerShell: Process Threat Hunting, Part 1

PowerShell is a powerful tool for threat hunting. Let's look at PowerShell threat hunting steps by assessing processes on Windows.

Authored byJoshua Wright
Joshua Wright

#monthofpowershell

When performing threat hunting and live system analysis, I will often look at the processes running on the system. Often, a compromised system will run one or more processes that look suspicious, which gives us an opportunity to identify the threat.

Let's look at how we can use PowerShell to evaluate a running system. We'll focus on two primary PowerShell commands: [code]Get-Process[/code] and [code]Get-CimInstance[/code] using the [code]Win32_Process[/code] class. This first article will focus on using the PowerShell commands and collecting the data. In the second article, we'll look at applying these commands to investigate malicious code running on a Windows host.

Listing Processes

PowerShell makes it easy to list processes using [code]Get-Process[/code]:

PS C:\Users\Sec504> Get-Process

Handles  NPM(K)    PM(K)      WS(K)     CPU(s)     Id  SI ProcessName
-------  ------    -----      -----     ------     --  -- -----------
    317      32    42576     107816       2.77   1520   1 chrome
    310      33   103376      81224       2.98   1708   1 chrome
    151       9     2008       6980       0.02   2660   1 chrome
    209      13     6788      16640       0.05   4844   1 chrome
    988      42    51576     114540       3.28   6368   1 chrome
...

We can investigate a specific process by supplying the process name as an argument:

PS C:\Users\Sec504> Get-Process explorer

Handles  NPM(K)    PM(K)      WS(K)     CPU(s)     Id  SI ProcessName
-------  ------    -----      -----     ------     --  -- -----------
   2332      93    82448     134420      14.31   4808   1 explorer

Wildcards work too:

PS C:\Users\Sec504> Get-Process vm*

Handles  NPM(K)    PM(K)      WS(K)     CPU(s)     Id  SI ProcessName
-------  ------    -----      -----     ------     --  -- -----------
     92       7     1328        920       0.03   6520   1 vm3dservice
    445      23     8672      10872              3200   0 vmtoolsd
    541      36    23796      28980      13.64   6356   1 vmtoolsd

In the [code]Get-Process[/code] output, we get several columns of detail for each process:

  • Handles: The number of handles (threads, open files, registry objects, etc.) accessible
  • NPM(K): Non-paged memory in use
  • PM(K): Pageable memory in use
  • WS(K): Memory working set (memory that is actively in use)
  • VM(M): Virtual memory in use (real and paged memory on disk)
  • CPU(s): Cumulative processor time used, in seconds.
  • ID: Process ID (PID)
  • ProcessName: Process name

The defaults are useful, but perhaps more to system administrators than security analysts. Fortunately, [code]Get-Process[/code] offers more process details as well.

Process Details

We can get a list of the process properties details available with [code]Get-Process[/code] using [code]Get-Member[/code]:

PS C:\Users\Sec504>  Get-Process | Get-Member -MemberType Properties


   TypeName: System.Diagnostics.Process

Name                       MemberType     Definition
----                       ----------     ----------
Handles                    AliasProperty  Handles = Handlecount
Name                       AliasProperty  Name = ProcessName
NPM                        AliasProperty  NPM = NonpagedSystemMemorySize64
PM                         AliasProperty  PM = PagedMemorySize64
SI                         AliasProperty  SI = SessionId
VM                         AliasProperty  VM = VirtualMemorySize64
WS                         AliasProperty  WS = WorkingSet64
__NounName                 NoteProperty   string __NounName=Process
BasePriority               Property       int BasePriority {get;}
Container                  Property       System.ComponentModel.IContainer Container {get;}
EnableRaisingEvents        Property       bool EnableRaisingEvents {get;set;}
...

For incident response investigations, I like to examine the following parameters for processes:

  • Name
  • Handle count
  • Id
  • Path
  • Command line
  • Bytes read and written to the disk
  • Memory used (real and virtual)
  • Parent Id
  • Process creation date and time

Here's where I get disappointed with [code]Get-Process[/code]: it offers a lot of useful information, but it also omits lots of useful details about processes. We can modify the columns retrieved to get specific properties using [code]Select-Object[/code]:

PS C:\Users\Sec504> Get-Process chrome | Select-Object -Property Name, Id, Path, WorkingSet64

Name     Id Path                                                        WorkingSet64
----     -- ----                                                        ------------
chrome 1520 C:\Program Files (x86)\Google\Chrome\Application\chrome.exe    110432256
chrome 1708 C:\Program Files (x86)\Google\Chrome\Application\chrome.exe     82894848
chrome 2660 C:\Program Files (x86)\Google\Chrome\Application\chrome.exe      7159808
chrome 4844 C:\Program Files (x86)\Google\Chrome\Application\chrome.exe     16973824
chrome 6368 C:\Program Files (x86)\Google\Chrome\Application\chrome.exe    117047296
chrome 7496 C:\Program Files (x86)\Google\Chrome\Application\chrome.exe     26345472
chrome 7528 C:\Program Files (x86)\Google\Chrome\Application\chrome.exe     26374144

Unfortunately, [code]Get-Process[/code] doesn't offer all of the detail we want. Fortunately, Microsoft also makes [code]Get-CimInstance[/code] available.

Detailed Process ... Details

[code]Get-Process[/code] is good for simple interrogation of processes, but if you want detailed information, you'll want to use [code]Get-CimInstance[/code] instead with the [code]Win32_Process[/code] class.

PS C:\Users\Sec504> Get-CimInstance -Class Win32_Process

ProcessId Name                        HandleCount WorkingSetSize VirtualSize
--------- ----                        ----------- -------------- -----------
0         System Idle Process         0           8192           8192
4         System                      2471        36864          3985408
92        Registry                    0           27586560       94515200
316       smss.exe                    53          274432         2203359694848
424       csrss.exe                   578         2191360        2203413659648
...

At first glance, [code]Get-CimInstance -Class Win32_Process[/code] returns information that is similar to that of [code]Get-Process[/code], but we can get a lot more process detail information from [code]Get-CimInstance[/code]:

PS C:\Users\Sec504> Get-CimInstance -Class Win32_Process | Get-Member -MemberType Properties


   TypeName: Microsoft.Management.Infrastructure.CimInstance#root/cimv2/Win32_Process

Name                       MemberType     Definition
----                       ----------     ----------
Handles                    AliasProperty  Handles = Handlecount
ProcessName                AliasProperty  ProcessName = Name
VM                         AliasProperty  VM = VirtualSize
WS                         AliasProperty  WS = WorkingSetSize
Caption                    Property       string Caption {get;}
CommandLine                Property       string CommandLine {get;}
CreationClassName          Property       string CreationClassName {get;}
...

Often I'll use the following set of parameters to collect information about running processes when I'm looking for threats on a Windows system:

PS C:\Users\Sec504> Get-CimInstance -Class Win32_Process | Select-Object -Property Name, HandleCount, ProcessId, ParentProcessId, Path, CommandLine, WriteTransferCount, ReadTransferCount, WorkingSetSize
...
Name               : chrome.exe
HandleCount        : 317
ProcessId          : 1520
ParentProcessId    : 6368
Path               : C:\Program Files (x86)\Google\Chrome\Application\chrome.exe
CommandLine        : "C:\Program Files (x86)\Google\Chrome\Application\chrome.exe" --type=renderer
                     --display-capture-permissions-policy-allowed --lang=en-US --device-scale-factor=2
                     --num-raster-threads=1 --renderer-client-id=5 --launch-time-ticks=3385309652
                     --mojo-platform-channel-handle=2832
                     --field-trial-handle=1804,i,7538668890417119548,1058345838094988730,131072 /prefetch:1
WriteTransferCount : 22843548
ReadTransferCount  : 2816643
WorkingSetSize     : 110432256
...

[code]Get-CimInstance[/code] does not allow us to specify a process name as an argument like [code]Get-Process[/code] does. If you want to use [code]Get-CimInstance[/code] to return information about a specific process by name, you can add a [code]Where-Object[/code] command to the pipeline:

PS C:\Users\Sec504> Get-CimInstance -Class Win32_Process | Select-Object -Property Name, HandleCount, ProcessId, ParentProcessId, Path, CommandLine, WriteTransferCount, ReadTransferCount, WorkingSetSize | Where-Object -Property Name -E "explorer.exe"


Name               : explorer.exe
HandleCount        : 2244
ProcessId          : 4808
ParentProcessId    : 4760
Path               : C:\WINDOWS\Explorer.EXE
CommandLine        : C:\WINDOWS\Explorer.EXE
WriteTransferCount : 89089
ReadTransferCount  : 5787331
WorkingSetSize     : 131350528

PID Relationships

Let's look at an example of applying these PowerShell process-interrogation techniques: parent and child relationships. Using [code]Get-CimInstance[/code], we can identify the parent process ID ([code]ParentProcessId[/code]) for a given process. The parent process ID tells us the process that launched the process. Let's get the PID of the Chrome process first:

PS C:\Users\Sec504> Get-Process chrome |Select-Object -Property Name, Id

Name     Id
----     --
chrome 1520
chrome 1708
chrome 2660
chrome 4844
chrome 6368
chrome 7496
chrome 7528

Here we see [code]Get-Process[/code] has identified 7 Chrome processes with different process ID values. Unfortunately, [code]Get-Process[/code] can't identify the parent process ID, so we turn to [code]Get-CimInstance[/code] for that:

PS C:\Users\Sec504> Get-CimInstance -Class Win32_Process | Where-Object -Property Name -EQ chrome.exe | Select-Object Name, ProcessId, ParentProcessId

Name       ProcessId ParentProcessId
----       --------- ---------------
chrome.exe      6368            4808
chrome.exe      2660            6368
chrome.exe      1708            6368
chrome.exe      7496            6368
chrome.exe      4844            6368
chrome.exe      7528            6368
chrome.exe      1520            6368

In this output we see that the first Chrome process has a process ID of 6368; all other Chrome processes are children of this first Chrome process since they all have process ID 6368 as their parent process ID.

That's great, but how do we identify the parent of process ID 6368? Just a slight modification to the [code]Get-CimInstance[/code] command gives us the answer:

PS C:\Users\Sec504> Get-CimInstance -Class Win32_Process | Where-Object -Property ProcessId -EQ 4808 | Select-Object Name, ProcessId, ParentProcessId

Name         ProcessId ParentProcessId
----         --------- ---------------
explorer.exe      4808            4760

After identifying process ID 4808 as the parent for the first Chrome process, we can identify the process name by changing the [code]Where-Object[/code] clause to filter on the [code]ProcessId[/code] field, revealing the parent process as [code]explorer.exe[/code]. We can repeat this process for Explorer as well:

PS C:\Users\Sec504> Get-CimInstance -Class Win32_Process | Where-Object -Property ProcessId -EQ 4760 | Select-Object Name, ProcessId, ParentProcessId
PS C:\Users\Sec504>

Notably here, Explorer has no parent. It's a special process launched by Userinit when a user logs in, and then Userinit exits. This makes Explorer an orphan process, revealing no parent information when we investigate the parent process ID.

Summary

We looked at the [code]Get-Process[/code] command as a tool to retrieve information about running processes. [code]Get-Process[/code] accepts a process name or wildcard, allowing us to quickly filter the results. This is convenient, but [code]Get-Process[/code] is also limited: it doesn't allow us to inspect several properties that are useful for incident response analysis.

We also looked at [code]Get-CimInstance[/code] using the [code]Win32_Process[/code] class. [code]Get-CimInstance[/code] can reveal valuable process properties for incident response analysts: path, command line, parent process ID, and more. Filtering is a little more complicated with [code]Get-CimInstance[/code], but the [code]Where-Object[/code] command helps out here.

Finally we looked at a practical investigation opportunity using [code]Get-CimInstance[/code], investigating the relationship between process ID and parent process ID. We'll continue to leverage this in the next article, where we use these process threat hunting skills on a compromised system to hunt our malware.

-Joshua Wright

Return to Getting Started With PowerShell


Joshua Wright is the author of SANS SEC504: Hacker Tools, Techniques, and Incident Handling, a faculty fellow for the SANS Institute, and a senior technical director at Counter Hack.

Month of PowerShell: Process Threat Hunting, Part 1 | SANS Institute