Ed Prepares to Open Up a Can of Process Whoop-Ass:I've never considered myself a particularly violent man. But, I have to admit it: Sometimes it just feels good to kill processes. I've even been heard to mutter a deadpan "Dodge This" in my lab late at night as I obliterate errant or evil processes just begging to meet their maker. Then, to make sure such a process doesn't pop back up to start bothering me again, I sow the ground with command-line kung fu salt to strangle any other similar process that might pop up in its place.
This technique, which we've taken to calling "Process Whack-a-Mole", can be helpful to people in all walks of life. I'm sure it's happened to pretty much everyone at some point. You find yourself playing defense in a Capture the Flag tournament against an elite team of ninjas from a three-letter government agency who want to completely control your boxen. They repeatedly gain access, and you have to shew them out before they score and you lose points. To deal with such situations, we can run a command to continuously look for processes with certain characteristics of our adversaries, and then kill them when they appear. We touched upon the idea of killing a process that starts to listen on a given TCP port in
Episode #76. But, let's go further this time, discussing how you can make much more flexible whack-a-mole commands to deal with various process characteristics.
These techniques are useful even outside of Capture the Flag games. I often use them in malware analysis and even system administration when I want to suppress some activity temporarily while I'm analyzing or configuring something else.
The basic structure I use for process whack-a-mole consists of the following three parts:
<Continuous Loopinator> <Process Selector> <Process Terminator>
Quite often the Process Selector and Process Terminator are combined together in a single command, because we can select or filter for the process we want in the same command we use to whack it. However, to filter for certain specific process characteristics, we'll have to split out these two entities. I'll show you what I mean in a bit.
We start out with our Continuous Loopinator:
C:\> for /L %i in (1,0,2) do @ping -n 2 127.0.0.1 >nul
This is a simple FOR /L loop that starts counting at 1, goes up to 2, in steps of 0. In other words, it's the cmd.exe equivalent of while (true), used to keep something running continuously. At the start of the loop, we introduce a 1-second delay by pinging ourselves twice (-n 2) and throwing the standard output away so as not to clutter our output (>nul). That way, we'll run our Process Selector and Process Terminator approximately every 1 second, helping to minimize our impact on performance. If you want a faster Process Selector, simply omit that ping, and your system will run our whack-a-mole command as fast as it can, but performance may drag.
We then follow with our Process Selector. If you keep in the ping delay, put in an & followed by the Process Selector, which lets us make one command run after another. Otherwise, just put the Process Selector after the @ (which turns off command echo, by the way... no sense having our output clogged up with commands).
The two most common Process Selectors I use are wmic and taskkill, which have the nice property of also including the ability to act as Process Terminators in the same command. Let's look at wmic first.
The wmic command can be used to select given processes based on our constructing a where clause, using the following syntax:
C:\> wmic process <where clause> <verb clause>
In the where clause, we can specify any attribute or group of attributes of processes that can be listed via wmic process. To get a list of these attributes, you could run:
C:\> wmic process get /?
So, for example, if you want to select a processID of 4242, you could write your wmic command as:
C:\> wmic process where processid=4242
Or, we could look for processes that have a given Parent Process ID:
C:\> wmic process where parentprocessid=3788
Or, we could look for processes with a given name:
C:\> wmic process where name="cmd.exe"
These where clauses also support AND and OR, but you've got to make sure you put the guts of your where clause inside of parentheses. The where clauses also support not equals (!=). Check this out:
C:\> wmic process where (name="cmd.exe" and processid != 676)
Now, we haven't supplied a verb clause here, so all our wmic commands are simply displaying raw, unformatted process information on Standard Output.
Let's start doing our whack-a-mole by specifying a Process Terminator by using the verb clause of wmic with the simple verb "delete". That'll kill a process.
Putting these pieces together, suppose you want to kill all cmd.exe processes other than the current cmd.exe you, the administrator, are running. Let's assume that your own cmd.exe has a process ID of 676. You could run:
C:\> for /L %i in (1,0,2) do @ping -n 2 127.0.0.1 >nul & wmic process where (name=
"cmd.exe" and processid!=676) delete
Now, let's see 'em try to run a cmd.exe. Every time someone tries to launch one, your loop will kill it.
Next, suppose you want to prevent a given process with processid 4008 from spawning child processes. Maybe process ID 4008 is a cmd.exe shell, and you want to prevent the person who is using it from being able to run any commands that aren't built-into the shell itself. Or, better yet, maybe process ID 4008 is Tim Medin's PowerShell process, and you wanted to pee in his Corn Flakes, depriving him of the ability to run any separate EXE commands, forcing him to rely solely on built-in capabilities of Powershell itself. We can do this one without our ping-induced delay to really confound him:
C:\> for /L %i in (1,0,2) do @wmic process where parentprocessid=4008 delete
These wmic where clauses also support substring matches, with the use of "like" and %. For example, suppose you want to continuously kill every process that is running from an EXE with a path in a given user's directory. You could run:
c:\> for /L %i in (1,0,2) do @ping -n 2 127.0.0.1 >nul & wmic process where
(executablepath like "c:\\users\\tim\\%") delete
Note that in a "where" clause with the "like" syntax, you have to surround the elements with parens. Also, note that if you have a \ in your where clause, you have to specify it as \\, the first \ indicating an escape, and the second indicating your backslash.
You can combine these where clause elements (=, !=, AND, OR, LIKE, and %) in all kinds of ways to mix and match against various process attributes for whack-a-mole. I'm sure our readers can dream up all kinds of interesting and useful combinations.
But, there is a missing attribute from "wmic process get /?" -- it's the user name that process is running under. To play whack-a-mole based on user names, we can turn to another Process Selector and Terminator: taskkill. I wrote about taskkill filters back in
Episode 22, showing how we can use it to kill a process based on its owner username. Here, we'll wrap that in our whack-a-mole construct:
C:\> for /L %i in (1,0,2) do @ping -n 2 127.0.0.1 >nul & taskkill /F /FI
"username eq tim"
Sorry, Tim. That's what you get for using a shell on the same system I'm on. Hal's over on some lonely Linux that no one ever uses, so I leave him alone. :)
Anyway, where was I? Ah, yes, we were discussing attributes of processes that wmic doesn't include, but which may be handy in whack-a-mole games. How about DLLs? Suppose a bad guy is hacking your system and keeps trying to inject a DLL into some process, and you want to kill that process. Maybe Evil Badguy (yes, that's his full name) has injected metsrv.dll, the Metasploit Meterpreter, into a running process, and uses process migration to jump from process to process. Sorry, but getting the system to unload that DLL using only built-in tools at the command line is very difficult, but killing that process is totally doable:
C:\> for /L %i in (1,0,2) do @ping -n 2 127.0.0.1 >nul & taskkill /F /FI
"modules eq metsrv.dll"
Now, I mentioned above that the Process Selector and Process Terminator components are typically combined in a single command, as we've seen so far with wmic and tasklist. When would you have two different commands for these pieces? Well, one example is with netstat, which can show TCP and UDP port usage and the processes associated with each port. That's exactly what we used in Episode #76, where our Process Selector was netstat (whose output I parsed with a FOR /F loop to pull out the ProcessID number), and the Process Terminator was taskkill:
C:\> for /L %i in (1,0,2) do @(ping -n 2 127.0.0.1>nul & for /f "tokens=5"
%j in ('netstat -nao ^| find ^":47145^"') do @taskkill /f /PID %j)
So, keeping in mind those three components of process whack-a-mole, you can use almost any command that lists processes in pretty much any arbitrary way to build a whack-a-mole command for fun and profit.
And now, for a routine disclaimer:
Be careful with any of these commands. If you kill a vital system process, such as lsass.exe, you could bring your whole box down. You have been warned. So, now that you are armed and dangerous, go have fun!
Tim prepares for war:It seems that Ed has a bit of shell envy. So let's kick that inferior shell off "our" machine and keep it (and him) off our machine.
As Ed described, the structure for
whack-an-Ed whack-a-mole has three parts, and that basic structure will be very similar in PowerShell.
<Continuous Loopinator> { <Process Selector> | <Process Terminator> }
The Continuous Loopinator repeatedly calls the Process Selector whose results are piped into the Process Terminator. Let's see how each piece works.
Continuous Loopinator:There are many ways to do a continuous loop, but the easiest and quickest method is to use the While loop.
PS C:\> while (1) { <code to run ad infinitum>; Start-Sleep 1 }
This loop is pretty self explanatory. It is a simple While loop that runs while 1 is true, which it always will be. The Start-Sleep cmdlet (alias sleep) will suspend activity for the specified amount of time. If we wanted a shorter nap we could use the -milliseconds parameter. Since Ed's command runs every second, we should run ours a bit faster just because we can. How about 5 times a second?
PS C:\> while (1) { <code to run ad infinitum>; Start-Sleep -milliseconds 200 }
Process Terminator:I'm covering this a bit out of order because the Terminator is so simple, so indulge me for a bit. The cmdlet used for killing is Stop-Process (alias spps or kill). It can even be used for some rudimentary process selection before the assassination. We can kill based on the Process Id:
PS C:\> Stop-Process 1337
...or the process name.
PS C:\> Stop-Process -Name cmd
In the second example every process with the name "cmd" would be stopped, but what if we wanted to be a little more high tech in making Ed's processes "sleep with the fishes?"
As described earlier, the results of the Process Selector can be piped into our Process Terminator. We can pick any method to retrieve the process(es) to be killed, but more on that later. Here is what it would look like:
<Get Processes> | Stop-Process
By default, Stop-Process will ask for confirmation prior to terminating any process not owned by the current user. To get around that safety mechanism we can use the Force parameter.
<Get Processes> | Stop-Process -Force
Short version:
<Get Processes> | kill -f
We could just kill the processes with Stop-Process by giving it a Process Id or process name, but we want more options. Now let's see how we can find more processes to kill.
Process Selector:To get a process or a number of processes we use Get-Process (aliases ps and gps). This is our Process Selector. We have covered this before, but we have a number of ways to get a process or a list of processes. To get help on the command you can run:
PS C:\> Get-Help Get-Process
...or for those who have seen the light and come from the linux side but have a bad memory, this works too:
PS C:\> man ps
To see the examples use the Examples parameter, or use the Full parameter to see everything. From the help we can see how to get a process with a given Process ID. We will be looking for PID 4242:
PS C:\> Get-Process -Id 4242
PS C:\> ps -Id 4242
To get all the cmd.exe processes:
PS C:\> Get-Process cmd
PS C:\> ps cmd
Note that the process name does NOT include .exe.We can also use filters in order to get more granular. We already had our loop to kill all cmd processes, but what if Ed wants to use PowerShell? We need to make sure that we are King of the PowerShell Hill, and any other PowerShell usurper is destroyed. This will find any PowerShell processes that aren't ours.
PS C:\> Get-Process powershell | ? { $_.ID -ne 21876 }
The weaponized version of the command could look like this:
PS C:\> While (1) { ps powershell | ? { $_.ID -ne 21876 } | kill -f; sleep 1 }
The next thing Ed did, after tee-tee'ing in my Kelloggs, was to prevent me from kicking off any processes from a cmd or PowerShell process. So let's do the same to him. Unfortunately, the objects returned by Get-Process do not have a Parent Process Id property, so we will have to use WMI to find processes with a given parent.
PS C:\> Get-WmiObject win32_process -Filter "ParentProcessId=5552" |
% { Get-Process -Id $_.ProcessID }
Get-WmiObject (alias gwmi) is used to access WMI in order to get all processes with a Parent Process Id of 5552. The results are piped into a ForEach-Object (alias %) loop. In the loop we use Get-Process and the Process Id retrieved from WMI in order to get the process object. We can then pipe that into our kill(er). Similar to what Ed did, we want to run this continuously so he doesn't have a chance. Here is what our command looks like:
PS C:\> While (1) { gwmi win32_process -Filter "ParentProcessId=5552" |
% { ps -Id $_.ProcessID } | kill -f }
We also want to make sure that Ed isn't able to run anything from his user directory (which includes his desktop).
PS C:\> While (1) { ps | ? { $_.Path -like "c:\users\ed\*" } | kill -f }
We use the Where-Object (alias ?) to filter our list of processes based on the path. The Like operator is used with our wildcard search string in order to find any of Ed's processes. Again, we pipe the results into Stop-Process in order to kill it.
Just to make sure that Ed doesn't run anything, we will kill any process where he is the owner. Again, we will have to use WMI in order to find the owner of a process.
PS C:\> While (1) { Get-WmiObject Win32_Process |
? { $_.GetOwner().User -eq "ed" } | % { Get-Process -Id $_.ProcessId } |
Stop-Process -Force }
This command is a little complicated, so let's break it down piece by piece. The While loop portion should be obvious so we'll skip that bit of explanation. The first chunk...
Get-WmiObject Win32_Process | ? { $_.GetOwner().User -eq "ed" }
We start off by querying WMI and retrieving WMI objects representing each process running on the current machine. The results are then piped into our filter, Where-Object (alias ?). The "current pipeline object", represented by the variable $_, allows us to access the properties of each object passed down the pipeline. For all intents and purposes, the $_ variable is used to iterate through each object passed down the pipeline. It takes the first object, in our case the first process, and accesses the GetOwner method's User property. We then check to see if the value is equal (-eq) to "ed". If it is equal, then our WMI object passes our filter and is sent further down the pipeline. Remember, the objects are WMI Process Objects, not PowerShell Process Objects, and they will need to be converted to the PowerShell version so we can natively deal with them in PowerShell. On to the conversion.
... | % { Get-Process -Id $_.ProcessId } ...
The objects that passed through the filter are now sent into our ForEach-Object (alias %) loop. This loop is used to iterate through each object and execute some fu on each of the WMI objects. Again, $_ represents the current object. To retrieve the PowerShell version of the process object we use Get-Process. We need to use Id parameter with the Process Id property of the current object ($_.ProcessId). Now we have PowerShell Process Objects. YAY!
... | Stop-Process -Force
Finally, the processes are piped into Stop-Process to be destroyed. The Force option is used since we don't want a confirmation to kill each process.
Next, let's look for the processes with the injected Meterpreter dll. How do we find this dll? We need to look at the modules a process has loaded. Here is what the Modules property looks like for the PowerShell process.
PS C:\> Get-Process powershell | select modules
Modules : {System.Diagnostics.ProcessModule (powershell.exe), System.Diagnostic
s.ProcessModule (ntdll.dll), System.Diagnostics.ProcessModule (kernel
32.dll), System.Diagnostics.ProcessModule (KERNELBASE.dll)...}
As you can see the dll name is wrapped in parenthesis. So here is how we find it and kill it.
PS C:\> Get-Process | ? { $_.Modules -like "*(metsrv.dll)*" } | Stop-Process
EDIT: In MetaSploit v2 and v3.0-3.2 this technique worked to find meterpreter. In v3.3 (and presumably future versions) this does not work since MetaSploit uses Reflective DLL injection to load the dll. I wrote a separate blog post on my personal blog on how to find the footprints of meterpreter:
Finding Meterpreter.
Actually, the modules property is a collection of module objects. So we can use a nested Where-Object to filter.
PS C:\> ps | ? { $_.Modules | ? {$_.ModuleName -eq "metsrv.dll" }} | kill
In this command we retrieve all the processes. We then filter the Modules, where the ModuleName is metsrv.dll. The results are piped into Stop-Process.
We can also parse netstat in order to kill a process similar to Episode #76. Let's take that command and wrap it in our infinite loop.
PS C:\> While (1) { netstat -ano | ? {$_ -like "*:47145 *"} |
% { $_ -match "\d+$"; stop-process $matches[0] } }
And as Ed said, be careful not to kill the wrong process or the whole box could go down. Of course, when it is down it is pretty dang hard to attack. Of course, it is also pretty dang hard to use too.
Now that Ed and I have spent all of our energy going after each other, Hal is going to show up and mop the floor with our tired carcases.
Disclaimer: No Eds where harmed in the making of this episode.
Hal's Analysis:Why are Ed and Tim so angry all the time? It couldn't have anything to do with the platform they've chosen to work on, could it? Hey guys, don't worry, be happy! You can always install Linux for free, or even just use Cygwin.
When Ed first proposed this topic, I was pretty stoked because I thought it was going to be a cake-walk for me with my little friend lsof. But not all of Ed's challenges that could be answered purely with lsof. Some required a bit more shell fu.
Let's start with the simple stuff first. The "infinite loop with 1 second delay" idiom for bash is something we've seen before in previous Episodes:
# while :; do [...your commands here...]; sleep 1; done
In this case, the commands we put into the while loop are going to be a kill command and usually some variant of "`lsof -t ...`" we'll be using to select the PIDs we want to kill. Remember from previous Episodes that "lsof -t" causes lsof to print out just the PIDs of the matching processes, specifically so we can use the output as arguments to the kill command.
For example, let's suppose we want to kill all of Ed's processes. We can use lsof's "-u" option to select processes for a particular user:
# while :; do kill -9 `lsof -t -u skodo`; sleep 1; done
Or we could nuke all the bash shells on the machine, using "-c" to select commands by name:
# while :; do kill -9 `lsof -t -c bash`; sleep 1; done
Of course, this would hammer our own shell, so it pays to be more selective:
# while :; do kill -9 `lsof -t -a -c bash -u^root -u^hal`; sleep 1; done
Here I've added the "-a" flag which means do a logical "and" on my selection criteria. Those criteria are "all commands named bash" ("-c bash") and "not user root" ("-u^root") and "not user hal" ("-u^hal"). Note that lsof's negation operator ("^") only works when selecting user names, PIDs (with "-p"), process group IDs (with "-g"), command names (with "-c"), and protocol state info ("-s", as in "-s^TCP:LISTEN").
Another one of Ed's challenges was killing processes where the binary is in a particular directory. Again we can do this with lsof:
# while :; do kill -9 `lsof -t -a -d txt +d /home/skodo`; sleep 1; done
Here we're looking for process binaries using "-d txt". In the lingo, the binary is what's used to create the "text segment" of a process (where the executable code lives), hence "-d txt" for lsof. The "+d" tells lsof to look for open files under a particular directory. Yes, lsof has so many command line options that the author had to start doubling up on letters using "+" instead of "-" (there's a reason the lsof manual page is nearly 50 pages long when printed out).
Note that "+d" only searches "one level deep". So if Ed were running "/home/skodo/evil", then our loop above would whack that process. But if Ed were running "/home/skodo/bin/evil", then we wouldn't catch it. If you want to do full directory recursion, use "+D" instead of "+d". lsof distinguishes these with separate options because full directory searches are so time-consuming.
However, as I mentioned earlier, Ed had challenges that I wasn't able to come up with a "pure lsof" solution for. For example, while lsof has the "-R" option for displaying parent process ID (PPID) values, there aren't any switches in lsof to select particular processes by PPID. So we'll need to resort to some awk:
# while :; do kill -9 `lsof -R -d cwd | awk '($3 == 8552) { print $2 }'`; sleep 1; done
Here the lsof command is outputting PPID values ("-R") in addition to the normal lsof output, and we're only outputting the lines showing the current working directory of each process ("-d cwd"). The "-d cwd" hack is a good way of ensuring that you only get one line of lsof output per process-- so we don't end up outputting the same PID multiple times and generating spurious error messages from kill. The awk code simply matches against a particular PPID value in column #3 and outputs the PID value in column #2.
Even though I had to resort to a bit of awk in the last example, you have to admit that having lsof makes this challenge unfairly easy for us Unix/Linux folks. Ahhh, lsof! How I love thee! Let me count the ways...