tag:blogger.com,1999:blog-8970103716443635402024-03-04T23:30:52.004-08:00Threat Hunting with PowerShellPowerShell Hunterhttp://www.blogger.com/profile/15841787123729180224noreply@blogger.comBlogger3125tag:blogger.com,1999:blog-897010371644363540.post-63695192648987797802021-01-28T10:57:00.006-08:002021-02-01T06:18:03.145-08:00Process Memory Utilization / Iron Scripter Challlenge (20 Jul 2021)I've heard of the Iron Scripter Challenge, but never really looked into it. This
week I decided to take the plunge and I'm glad I did. Basically, the Iron
Scripter Challenge is a series (I believe monthly) of scripting challenges in
PowerShell. If your like me and you love challenges, or learn by doing, I highly
recommend it. Here is a link to the challenge I worked on this week
<a href="https://ironscripter.us/a-memory-reporting-challenge/" target="_blank">https://ironscripter.us/a-memory-reporting-challenge/</a> <div><br /><div>The overall goal was to collect memory data on running processes by process
name. For example, if there are 20 instances of Chrome process running,
combine the memory data for all of them. I modified this requirement a little and included
the path with the name because I know malicious process can use legitimate
names running in incorrect locations. For example, what if one of those 20
Chrome processes was executed from C:\Windows\Temp? My Spidey sense would be
tingling. </div><div><br /></div><div>Here's the basic data I needed to collect: </div></div><blockquote style="border: none; margin: 0px 0px 0px 40px; padding: 0px; text-align: left;"><div><ol style="text-align: left;"><li>Process name </li><li>Total number of processes </li><li>Total WorkingSet </li><li>Percentage of in-use memory</li></ol></div></blockquote><div><br /></div><div>While it would be easy to write this in a verbose script with variables and foreach loops to solve this, I thought a good challenge for me was to write this in one command with many pipes. Additionally, I wanted to make this as efficient as possible with that limitation.</div><div><br /></div><div>Process objects returned from the Get-Process, Group-Object, and Measure-Command cmdlets provide all the data we need. I'll break down each here with the corresponding requirement:</div><div><ol style="text-align: left;"><ol><li>Process name (Get-Process ProcessName property)</li><li>Total number of processes (Use Group-Object Count property)</li><li>Total WorkingSet (Use Measure-Object Sum property on the WorkingSet of the group)</li><li>Percentage of in-use memory (Divide the WorkingSet for the group by the WorkingSet sum for all properties)</li></ol></ol></div>
<div style="background-color: #1e1e1e; color: #d4d4d4; font-family: Consolas, "Courier New", monospace; font-size: 14px; font-weight: normal; line-height: 19px;">
<div><span>1 </span><span style="color: #dcdcaa;">Get-Process</span><span style="color: #d4d4d4;"> |</span></div>
<div><span>2 </span><span style="color: #d4d4d4;"> </span><span style="color: #dcdcaa;">Group-Object</span><span style="color: #d4d4d4;"> Name, path |</span></div>
<div><span>3 </span><span style="color: #d4d4d4;"> </span><span style="color: #dcdcaa;">Select-Object</span><span style="color: #d4d4d4;"> </span><span style="color: #569cd6;">@</span><span style="color: #d4d4d4;">{</span></div>
<div><span>4 </span><span style="color: #d4d4d4;"> </span><span style="color: #9cdcfe;">n</span><span style="color: #d4d4d4;">=</span><span style="color: #ce9178;">"ProcName"</span></div>
<div><span>5 </span><span style="color: #d4d4d4;"> </span><span style="color: #9cdcfe;">e</span><span style="color: #d4d4d4;">={(</span><span style="color: #9cdcfe;">$_</span><span style="color: #dcdcaa;">.name</span><span style="color: #d4d4d4;"> -split </span><span style="color: #ce9178;">", "</span><span style="color: #d4d4d4;">)[</span><span style="color: #b5cea8;">0</span><span style="color: #d4d4d4;">]}</span></div>
<div><span>6 </span><span style="color: #d4d4d4;"> },</span></div>
<div><span>7 </span><span style="color: #d4d4d4;"> </span><span style="color: #569cd6;">@</span><span style="color: #d4d4d4;">{</span></div>
<div><span>8 </span><span style="color: #d4d4d4;"> </span><span style="color: #9cdcfe;">n</span><span style="color: #d4d4d4;">=</span><span style="color: #ce9178;">"Path"</span></div>
<div><span>9 </span><span style="color: #d4d4d4;"> </span><span style="color: #9cdcfe;">e</span><span style="color: #d4d4d4;">={(</span><span style="color: #9cdcfe;">$_</span><span style="color: #dcdcaa;">.name</span><span style="color: #d4d4d4;"> -split </span><span style="color: #ce9178;">", "</span><span style="color: #d4d4d4;">)[</span><span style="color: #b5cea8;">-1</span><span style="color: #d4d4d4;">]}</span></div>
<div><span>10 </span><span style="color: #d4d4d4;"> },</span></div>
<div><span>11 </span><span style="color: #d4d4d4;"> </span><span style="color: #569cd6;">@</span><span style="color: #d4d4d4;">{</span></div>
<div><span>12 </span><span style="color: #d4d4d4;"> </span><span style="color: #9cdcfe;">n</span><span style="color: #d4d4d4;">=</span><span style="color: #ce9178;">"TotalProcs"</span></div>
<div><span>13 </span><span style="color: #d4d4d4;"> </span><span style="color: #9cdcfe;">e</span><span style="color: #d4d4d4;">={</span><span style="color: #9cdcfe;">$_</span><span style="color: #dcdcaa;">.Count</span><span style="color: #d4d4d4;">}</span></div>
<div><span>14 </span><span style="color: #d4d4d4;"> }, </span></div>
<div><span>15 </span><span style="color: #d4d4d4;"> </span><span style="color: #569cd6;">@</span><span style="color: #d4d4d4;">{</span></div>
<div><span>16 </span><span style="color: #d4d4d4;"> </span><span style="color: #9cdcfe;">n</span><span style="color: #d4d4d4;">=</span><span style="color: #ce9178;">"WorkingSetTotal"</span></div>
<div><span>17 </span><span style="color: #d4d4d4;"> </span><span style="color: #9cdcfe;">e</span><span style="color: #d4d4d4;">={(</span><span style="color: #9cdcfe;">$_</span><span style="color: #dcdcaa;">.Group.WorkingSet</span><span style="color: #d4d4d4;"> | </span><span style="color: #dcdcaa;">Measure-Object</span><span style="color: #d4d4d4;"> -Sum).sum}</span></div>
<div><span>18 </span><span style="color: #d4d4d4;"> },</span></div>
<div><span>19 </span><span style="color: #d4d4d4;"> </span><span style="color: #569cd6;">@</span><span style="color: #d4d4d4;">{</span></div>
<div><span>20 </span><span style="color: #d4d4d4;"> </span><span style="color: #9cdcfe;">n</span><span style="color: #d4d4d4;">=</span><span style="color: #ce9178;">"Percentage"</span></div>
<div><span>21 </span><span style="color: #d4d4d4;"> </span><span style="color: #9cdcfe;">e</span><span style="color: #d4d4d4;">={(</span><span style="color: #9cdcfe;">$_</span><span style="color: #dcdcaa;">.Group.WorkingSet</span><span style="color: #d4d4d4;"> | </span><span style="color: #dcdcaa;">Measure-Object</span><span style="color: #d4d4d4;"> -Sum).sum / </span></div>
<div><span>22 </span><span style="color: #d4d4d4;"> ((</span><span style="color: #dcdcaa;">Get-Process</span><span style="color: #d4d4d4;">).workingset | </span><span style="color: #dcdcaa;">Measure-Object</span><span style="color: #d4d4d4;"> -Sum).Sum </span></div>
<div><span>23 </span><span style="color: #d4d4d4;"> }</span></div>
<div><span>24 </span><span style="color: #d4d4d4;"> }</span></div></div><!--EndFragment-->
<div><br /></div><div>I used calculated properties for most of the requirements. The Group-Object command will give me a Name, Count, and Group property. Name will be a string in the format of "Name, Path". Depending on the user's permissions when running the command, some process paths may be null. By using the -split operator with the regular expression of ", " I account for these instances. In those cases the Path value will be the same as the name because in an array with 1 element, item [0] and [-1] are the same. The Count property will be the total number of processes for that group. The group property will be all the process objects belonging to that group, so $_.group will be an array of those objects.</div><div><br /></div><div>After this first go around, I wasn't quite satisfied with the Percentage calculation even though it worked just fine. I didn't like the fact I was running Get-Process again for each group when I already ran it. This is where Tee-Object comes. By using Tee-Object, I can assign the original process objects collected in the first command on the pipeline to a variable then call it later down the pipe. Here's the updated version.</div><div><br /></div>
<div style="background-color: #1e1e1e; color: #d4d4d4; font-family: Consolas, "Courier New", monospace; font-size: 14px; font-weight: normal; line-height: 19px;">
<div><span>1 </span><span style="color: #dcdcaa;">Get-Process</span><span style="color: #d4d4d4;"> |</span></div>
<div><span>2 </span><span style="color: #d4d4d4;"> </span><span style="color: #dcdcaa;">Tee-Object</span><span style="color: #d4d4d4;"> -Variable Procs |</span></div>
<div><span>3 </span><span style="color: #d4d4d4;"> </span><span style="color: #dcdcaa;">Group-Object</span><span style="color: #d4d4d4;"> Name, path |</span></div>
<div><span>4 </span><span style="color: #d4d4d4;"> </span><span style="color: #dcdcaa;">Select-Object</span><span style="color: #d4d4d4;"> </span><span style="color: #569cd6;">@</span><span style="color: #d4d4d4;">{</span></div>
<div><span>5 </span><span style="color: #d4d4d4;"> </span><span style="color: #9cdcfe;">n</span><span style="color: #d4d4d4;">=</span><span style="color: #ce9178;">"ProcName"</span></div>
<div><span>6 </span><span style="color: #d4d4d4;"> </span><span style="color: #9cdcfe;">e</span><span style="color: #d4d4d4;">={(</span><span style="color: #9cdcfe;">$_</span><span style="color: #dcdcaa;">.name</span><span style="color: #d4d4d4;"> -split </span><span style="color: #ce9178;">", "</span><span style="color: #d4d4d4;">)[</span><span style="color: #b5cea8;">0</span><span style="color: #d4d4d4;">]}</span></div>
<div><span>7 </span><span style="color: #d4d4d4;"> },</span></div>
<div><span>8 </span><span style="color: #d4d4d4;"> </span><span style="color: #569cd6;">@</span><span style="color: #d4d4d4;">{</span></div>
<div><span>9 </span><span style="color: #d4d4d4;"> </span><span style="color: #9cdcfe;">n</span><span style="color: #d4d4d4;">=</span><span style="color: #ce9178;">"Path"</span></div>
<div><span>10 </span><span style="color: #d4d4d4;"> </span><span style="color: #9cdcfe;">e</span><span style="color: #d4d4d4;">={(</span><span style="color: #9cdcfe;">$_</span><span style="color: #dcdcaa;">.name</span><span style="color: #d4d4d4;"> -split </span><span style="color: #ce9178;">", "</span><span style="color: #d4d4d4;">)[</span><span style="color: #b5cea8;">-1</span><span style="color: #d4d4d4;">]}</span></div>
<div><span>11 </span><span style="color: #d4d4d4;"> },</span></div>
<div><span>12 </span><span style="color: #d4d4d4;"> </span><span style="color: #569cd6;">@</span><span style="color: #d4d4d4;">{</span></div>
<div><span>13 </span><span style="color: #d4d4d4;"> </span><span style="color: #9cdcfe;">n</span><span style="color: #d4d4d4;">=</span><span style="color: #ce9178;">"TotalProcs"</span></div>
<div><span>14 </span><span style="color: #d4d4d4;"> </span><span style="color: #9cdcfe;">e</span><span style="color: #d4d4d4;">={</span><span style="color: #9cdcfe;">$_</span><span style="color: #dcdcaa;">.Count</span><span style="color: #d4d4d4;">}</span></div>
<div><span>15 </span><span style="color: #d4d4d4;"> }, </span></div>
<div><span>16 </span><span style="color: #d4d4d4;"> </span><span style="color: #569cd6;">@</span><span style="color: #d4d4d4;">{</span></div>
<div><span>17 </span><span style="color: #d4d4d4;"> </span><span style="color: #9cdcfe;">n</span><span style="color: #d4d4d4;">=</span><span style="color: #ce9178;">"WorkingSetTotal"</span></div>
<div><span>18 </span><span style="color: #d4d4d4;"> </span><span style="color: #9cdcfe;">e</span><span style="color: #d4d4d4;">={(</span><span style="color: #9cdcfe;">$_</span><span style="color: #dcdcaa;">.Group.WorkingSet</span><span style="color: #d4d4d4;"> | </span><span style="color: #dcdcaa;">Measure-Object</span><span style="color: #d4d4d4;"> -Sum).sum}</span></div>
<div><span>19 </span><span style="color: #d4d4d4;"> },</span></div>
<div><span>20 </span><span style="color: #d4d4d4;"> </span><span style="color: #569cd6;">@</span><span style="color: #d4d4d4;">{</span></div>
<div><span>21 </span><span style="color: #d4d4d4;"> </span><span style="color: #9cdcfe;">n</span><span style="color: #d4d4d4;">=</span><span style="color: #ce9178;">"Percentage"</span></div>
<div><span>22 </span><span style="color: #d4d4d4;"> </span><span style="color: #9cdcfe;">e</span><span style="color: #d4d4d4;">={(</span><span style="color: #9cdcfe;">$_</span><span style="color: #dcdcaa;">.Group.WorkingSet</span><span style="color: #d4d4d4;"> | </span><span style="color: #dcdcaa;">Measure-Object</span><span style="color: #d4d4d4;"> -Sum).sum / </span></div>
<div><span>23 </span><span style="color: #d4d4d4;"> (</span><span style="color: #9cdcfe;">$Procs</span><span style="color: #dcdcaa;">.workingset</span><span style="color: #d4d4d4;"> | </span><span style="color: #dcdcaa;">Measure-Object</span><span style="color: #d4d4d4;"> -Sum).Sum </span></div>
<div><span>24 </span><span style="color: #d4d4d4;"> }</span></div>
<div><span>25 </span><span style="color: #d4d4d4;"> }</span></div><br /></div><!--EndFragment-->
<div><div class="separator" style="clear: both; text-align: center;"><br /></div>Being satisfied with this, I moved on to the "bonus" requirements. Here they are:</div><div style="text-align: left;"><ol style="text-align: left;"><ol><li>Create a PowerShell function around your code</li><li>Support querying a remote computer</li><li>Provide sorted results</li><li>Provide formatted output</li><li>Services run in a process so create a command to report on the same memory usage for a given service. It is OK if the service shares a process. Although you might want to indicate that.</li></ol></ol>I sat out to tackle the first four. For the first requirement, I decided to create 2 functions a "Get" and a "Show". I did this because a couple requirements were formatting and sorting which are not appropriate for a "Get" function, so I split them up into 2. Both functions will work the same way in fact I just call the "Get" function from the "Show" function to get the data before formatting.</div><div style="text-align: left;"><br /></div><div style="text-align: left;">Let's break down the "Get" function first. Here is the top of the function with the param block.</div><div style="text-align: left;"><br /></div>
<div style="background-color: #1e1e1e; color: #d4d4d4; font-family: Consolas, "Courier New", monospace; font-size: 14px; font-weight: normal; line-height: 19px;">
<div><span>1 </span><span style="color: #569cd6;">function</span><span style="color: #d4d4d4;"> </span><span style="color: #dcdcaa;">Get-ProcessMemory</span><span style="color: #d4d4d4;"> {</span></div>
<div><span>2 </span><span style="color: #d4d4d4;"> [</span><span style="color: #dcdcaa;">CmdletBinding</span><span style="color: #d4d4d4;">(</span><span style="color: #9cdcfe;">DefaultParameterSetName</span><span style="color: #d4d4d4;"> = </span><span style="color: #ce9178;">'None'</span><span style="color: #d4d4d4;">)]</span></div>
<div><span>3 </span><span style="color: #d4d4d4;"> [</span><span style="color: #dcdcaa;">OutputType</span><span style="color: #d4d4d4;">([</span><span style="color: #569cd6;">PSCustomObject</span><span style="color: #d4d4d4;">])]</span></div>
<div><span>4 </span></div>
<div><span>5 </span><span style="color: #d4d4d4;"> </span><span style="color: #c586c0;">param</span><span style="color: #d4d4d4;"> (</span></div>
<div><span>6 </span><span style="color: #d4d4d4;"> </span><span style="color: #6a9955;"># Remote computer or collection of remote computers</span></div>
<div><span>7 </span><span style="color: #d4d4d4;"> [</span><span style="color: #dcdcaa;">Parameter</span><span style="color: #d4d4d4;">(</span><span style="color: #9cdcfe;">Mandatory</span><span style="color: #d4d4d4;"> = </span><span style="color: #569cd6;">$true</span><span style="color: #d4d4d4;">, </span><span style="color: #9cdcfe;">ValueFromPipeline</span><span style="color: #d4d4d4;"> = </span><span style="color: #569cd6;">$true</span><span style="color: #d4d4d4;">, </span><span style="color: #9cdcfe;">ParameterSetName</span><span style="color: #d4d4d4;"> = </span><span style="color: #ce9178;">'CN'</span><span style="color: #d4d4d4;">)]</span></div>
<div><span>8 </span><span style="color: #d4d4d4;"> [</span><span style="color: #569cd6;">string</span><span style="color: #d4d4d4;">[]]</span></div>
<div><span>9 </span><span style="color: #d4d4d4;"> </span><span style="color: #9cdcfe;">$ComputerName</span><span style="color: #d4d4d4;">,</span></div>
<div><span>10 </span></div>
<div><span>11 </span><span style="color: #d4d4d4;"> </span><span style="color: #6a9955;"># PSCredential for remote computer(s)</span></div>
<div><span>12 </span><span style="color: #d4d4d4;"> [</span><span style="color: #dcdcaa;">Parameter</span><span style="color: #d4d4d4;">(</span><span style="color: #9cdcfe;">ParameterSetName</span><span style="color: #d4d4d4;"> = </span><span style="color: #ce9178;">'CN'</span><span style="color: #d4d4d4;">)]</span></div>
<div><span>13 </span><span style="color: #d4d4d4;"> [</span><span style="color: #569cd6;">pscredential</span><span style="color: #d4d4d4;">]</span></div>
<div><span>14 </span><span style="color: #d4d4d4;"> </span><span style="color: #9cdcfe;">$Credential</span><span style="color: #d4d4d4;">,</span></div>
<div><span>15 </span></div>
<div><span>16 </span><span style="color: #d4d4d4;"> </span><span style="color: #6a9955;"># PSSession for remote connection</span></div>
<div><span>17 </span><span style="color: #d4d4d4;"> [</span><span style="color: #dcdcaa;">Parameter</span><span style="color: #d4d4d4;">(</span><span style="color: #9cdcfe;">Mandatory</span><span style="color: #d4d4d4;"> = </span><span style="color: #569cd6;">$true</span><span style="color: #d4d4d4;">, </span><span style="color: #9cdcfe;">ParameterSetName</span><span style="color: #d4d4d4;"> = </span><span style="color: #ce9178;">'Session'</span><span style="color: #d4d4d4;">)]</span></div>
<div><span>18 </span><span style="color: #d4d4d4;"> [</span><span style="color: #569cd6;">System.Management.Automation.Runspaces.PSSession</span><span style="color: #d4d4d4;">]</span></div>
<div><span>19 </span><span style="color: #d4d4d4;"> </span><span style="color: #9cdcfe;">$Session</span></div>
<div><span>20 </span><span style="color: #d4d4d4;"> )</span></div>
<div><span>21 </span><span style="color: #d4d4d4;"> </span></div>
<div><span>22 </span><span style="color: #d4d4d4;"></span></div></div><!--EndFragment-->
<div style="text-align: left;"><br /></div><div style="text-align: left;">I intentionally used parameter names that match some of the Invoke-Command parameters. I did this because I intend to use that cmdlet to satisfy the second bonus requirement using the $PSBoundParameters automatic variable. I also have 3 parameter sets, even though you only see 2 explicitly named in the parameters. The third one is default "None" which will happen when no parameters passed (technically it would be the default if the parameter set couldn't be determined). What I'm trying to allow here is the ability to just call the function on the local machine without passing any arguments, but include them for remoting if needed.</div><div style="text-align: left;"><br /></div><div style="text-align: left;">On to the process block:</div><div style="text-align: left;"><br /></div>
<div style="background-color: #1e1e1e; color: #d4d4d4; font-family: Consolas, "Courier New", monospace; font-size: 14px; font-weight: normal; line-height: 19px;">
<div><span>23 </span><span style="color: #d4d4d4;"> </span><span style="color: #c586c0;">process</span><span style="color: #d4d4d4;"> {</span></div>
<div><span>24 </span><span style="color: #d4d4d4;"> </span><span style="color: #9cdcfe;">$Command</span><span style="color: #d4d4d4;"> = </span><span style="color: #ce9178;">@'</span></div>
<div><span>25 </span><span style="color: #ce9178;"> Get-Process |</span></div>
<div><span>26 </span><span style="color: #ce9178;"> Tee-Object -Variable Procs |</span></div>
<div><span>27 </span><span style="color: #ce9178;"> Group-Object Name, path |</span></div>
<div><span>28 </span><span style="color: #ce9178;"> Select-Object @{</span></div>
<div><span>29 </span><span style="color: #ce9178;"> n="ProcName"</span></div>
<div><span>30 </span><span style="color: #ce9178;"> e={($_.name -split ", ")[0]}</span></div>
<div><span>31 </span><span style="color: #ce9178;"> },</span></div>
<div><span>32 </span><span style="color: #ce9178;"> @{</span></div>
<div><span>33 </span><span style="color: #ce9178;"> n="Path"</span></div>
<div><span>34 </span><span style="color: #ce9178;"> e={($_.name -split ", ")[-1]}</span></div>
<div><span>35 </span><span style="color: #ce9178;"> },</span></div>
<div><span>36 </span><span style="color: #ce9178;"> @{</span></div>
<div><span>37 </span><span style="color: #ce9178;"> n="TotalProcs"</span></div>
<div><span>38 </span><span style="color: #ce9178;"> e={$_.Count}</span></div>
<div><span>39 </span><span style="color: #ce9178;"> }, </span></div>
<div><span>40 </span><span style="color: #ce9178;"> @{</span></div>
<div><span>41 </span><span style="color: #ce9178;"> n="WorkingSetTotal"</span></div>
<div><span>42 </span><span style="color: #ce9178;"> e={($_.Group.WorkingSet | Measure-Object -Sum).sum}</span></div>
<div><span>43 </span><span style="color: #ce9178;"> },</span></div>
<div><span>44 </span><span style="color: #ce9178;"> @{</span></div>
<div><span>45 </span><span style="color: #ce9178;"> n="Percentage"</span></div>
<div><span>46 </span><span style="color: #ce9178;"> e={($_.Group.WorkingSet | Measure-Object -Sum).sum / </span></div>
<div><span>47 </span><span style="color: #ce9178;"> ($Procs.workingset | Measure-Object -Sum).Sum </span></div>
<div><span>48 </span><span style="color: #ce9178;"> }</span></div>
<div><span>49 </span><span style="color: #ce9178;"> }</span></div>
<div><span>50 </span><span style="color: #ce9178;">'@</span><span style="color: #d4d4d4;"> </span><span style="color: #6a9955;">#Command here-string to invoke</span></div>
<div><span>51 </span></div>
<div><span>52 </span><span style="color: #d4d4d4;"></span></div></div><!--EndFragment-->
<div style="text-align: left;"><br /></div><div style="text-align: left;">I created a Here-String containing my original command. I did this so I can use it for both Invoke-Expression for the local machine or Invoke-Command for remote machines. On to the next/last portion.</div><div style="text-align: left;"><br /></div>
<div style="background-color: #1e1e1e; color: #d4d4d4; font-family: Consolas, "Courier New", monospace; font-size: 14px; font-weight: normal; line-height: 19px;">
<div><span>53 </span><span style="color: #d4d4d4;"> </span><span style="color: #c586c0;">if</span><span style="color: #d4d4d4;"> (</span><span style="color: #9cdcfe;">$PSCmdlet</span><span style="color: #dcdcaa;">.ParameterSetName</span><span style="color: #d4d4d4;"> -eq </span><span style="color: #ce9178;">"None"</span><span style="color: #d4d4d4;">) {</span></div>
<div><span>54 </span><span style="color: #d4d4d4;"> </span><span style="color: #dcdcaa;">Invoke-Expression</span><span style="color: #d4d4d4;"> -Command </span><span style="color: #9cdcfe;">$Command</span></div>
<div><span>55 </span><span style="color: #d4d4d4;"> } </span><span style="color: #6a9955;">#if ParameterSetName is None (local machine)</span></div>
<div><span>56 </span><span style="color: #d4d4d4;"> </span></div>
<div><span>57 </span><span style="color: #d4d4d4;"> </span><span style="color: #c586c0;">else</span><span style="color: #d4d4d4;"> {</span></div>
<div><span>58 </span><span style="color: #d4d4d4;"> </span><span style="color: #9cdcfe;">$InvokeCommandArgs</span><span style="color: #d4d4d4;"> = </span><span style="color: #9cdcfe;">$PSBoundParameters</span><span style="color: #d4d4d4;"> </span><span style="color: #6a9955;">#works because I use the same parameter names</span></div>
<div><span>59 </span><span style="color: #d4d4d4;"> </span><span style="color: #9cdcfe;">$InvokeCommandArgs</span><span style="color: #dcdcaa;">.ScriptBlock</span><span style="color: #d4d4d4;"> = [</span><span style="color: #569cd6;">scriptblock</span><span style="color: #d4d4d4;">]::Create(</span><span style="color: #9cdcfe;">$Command</span><span style="color: #d4d4d4;">)</span></div>
<div><span>60 </span><span style="color: #d4d4d4;"> </span><span style="color: #dcdcaa;">Invoke-Command</span><span style="color: #d4d4d4;"> </span><span style="color: #9cdcfe;">@InvokeCommandArgs</span></div>
<div><span>61 </span><span style="color: #d4d4d4;"> } </span><span style="color: #6a9955;">#else - ParameterSetName is NOT None (remoting) </span></div>
<div><span>62 </span></div>
<div><span>63 </span><span style="color: #d4d4d4;"> } </span><span style="color: #6a9955;">#Process Script Block for Get-ProcessMemory Function </span></div>
<div><span>64 </span></div>
<div><span>65 </span><span style="color: #d4d4d4;">} </span><span style="color: #6a9955;">#Get-ProcessMemory Function Definition</span></div>
<div><span>66 </span></div></div><!--EndFragment-->
<div style="text-align: left;"><div class="separator" style="clear: both; text-align: center;"><br /></div><br />In this last section I determine if it is going to be ran on the local machine (ParameterSet -eq "None") or remote machine. For local machine I just used the Invoke-Expression cmdlet to run my command. I could have converted it to a script block and called the Invoke method, but I like to use native cmdlets when I can. </div><div style="text-align: left;"><br /></div><div style="text-align: left;">For remote machines, I take advantage of using standard names for my parameters so I can just use the $PSBoundParameters automatic variable to splat Invoke-Command. I did convert $Command to a scriptblock to make this call.</div><div style="text-align: left;"><br /></div><div style="text-align: left;">The "Show" function has the exact same parameters so I'll skip that and go straight to the process block.</div><div style="text-align: left;"><br /></div>
<div style="background-color: #1e1e1e; color: #d4d4d4; font-family: Consolas, "Courier New", monospace; font-size: 14px; font-weight: normal; line-height: 19px;">
<div><span>100 </span><span style="color: #d4d4d4;"> </span><span style="color: #c586c0;">process</span><span style="color: #d4d4d4;"> {</span></div>
<div><span>101 </span><span style="color: #d4d4d4;"> </span></div>
<div><span>102 </span><span style="color: #d4d4d4;"> </span><span style="color: #dcdcaa;">Get-ProcessMemory</span><span style="color: #d4d4d4;"> </span><span style="color: #9cdcfe;">@PSBoundParameters</span><span style="color: #d4d4d4;"> |</span></div>
<div><span>103 </span><span style="color: #d4d4d4;"> </span><span style="color: #dcdcaa;">Sort-Object</span><span style="color: #d4d4d4;"> WorkingSetTotal -Descending |</span></div>
<div><span>104 </span><span style="color: #d4d4d4;"> </span><span style="color: #dcdcaa;">Format-Table</span><span style="color: #d4d4d4;"> -Property </span><span style="color: #569cd6;">@</span><span style="color: #d4d4d4;">{</span></div>
<div><span>105 </span><span style="color: #d4d4d4;"> </span><span style="color: #9cdcfe;">n</span><span style="color: #d4d4d4;">=</span><span style="color: #ce9178;">"Name"</span></div>
<div><span>106 </span><span style="color: #d4d4d4;"> </span><span style="color: #9cdcfe;">e</span><span style="color: #d4d4d4;">={</span><span style="color: #9cdcfe;">$_</span><span style="color: #dcdcaa;">.ProcName</span><span style="color: #d4d4d4;">}</span></div>
<div><span>107 </span><span style="color: #d4d4d4;"> </span><span style="color: #9cdcfe;">w</span><span style="color: #d4d4d4;">=</span><span style="color: #b5cea8;">35</span></div>
<div><span>108 </span><span style="color: #d4d4d4;"> },</span></div>
<div><span>109 </span><span style="color: #d4d4d4;"> </span><span style="color: #569cd6;">@</span><span style="color: #d4d4d4;">{</span></div>
<div><span>110 </span><span style="color: #d4d4d4;"> </span><span style="color: #9cdcfe;">n</span><span style="color: #d4d4d4;">=</span><span style="color: #ce9178;">"Path"</span></div>
<div><span>111 </span><span style="color: #d4d4d4;"> </span><span style="color: #9cdcfe;">e</span><span style="color: #d4d4d4;">={</span><span style="color: #9cdcfe;">$_</span><span style="color: #dcdcaa;">.Path</span><span style="color: #d4d4d4;">}</span></div>
<div><span>112 </span><span style="color: #d4d4d4;"> </span><span style="color: #9cdcfe;">w</span><span style="color: #d4d4d4;">=</span><span style="color: #b5cea8;">30</span></div>
<div><span>113 </span><span style="color: #d4d4d4;"> },</span></div>
<div><span>114 </span><span style="color: #d4d4d4;"> </span><span style="color: #569cd6;">@</span><span style="color: #d4d4d4;">{</span></div>
<div><span>115 </span><span style="color: #d4d4d4;"> </span><span style="color: #9cdcfe;">n</span><span style="color: #d4d4d4;">=</span><span style="color: #ce9178;">"Procesess"</span></div>
<div><span>116 </span><span style="color: #d4d4d4;"> </span><span style="color: #9cdcfe;">e</span><span style="color: #d4d4d4;">={</span><span style="color: #9cdcfe;">$_</span><span style="color: #dcdcaa;">.TotalProcs</span><span style="color: #d4d4d4;">}</span></div>
<div><span>117 </span><span style="color: #d4d4d4;"> </span><span style="color: #9cdcfe;">w</span><span style="color: #d4d4d4;">=</span><span style="color: #b5cea8;">10</span></div>
<div><span>118 </span><span style="color: #d4d4d4;"> },</span></div>
<div><span>119 </span><span style="color: #d4d4d4;"> </span><span style="color: #569cd6;">@</span><span style="color: #d4d4d4;">{</span></div>
<div><span>120 </span><span style="color: #d4d4d4;"> </span><span style="color: #9cdcfe;">n</span><span style="color: #d4d4d4;">=</span><span style="color: #ce9178;">"Usage/MB"</span></div>
<div><span>121 </span><span style="color: #d4d4d4;"> </span><span style="color: #9cdcfe;">e</span><span style="color: #d4d4d4;">={</span><span style="color: #9cdcfe;">$_</span><span style="color: #dcdcaa;">.WorkingSetTotal</span><span style="color: #d4d4d4;"> / </span><span style="color: #b5cea8;">1</span><span style="color: #569cd6;">MB</span><span style="color: #d4d4d4;">}</span></div>
<div><span>122 </span><span style="color: #d4d4d4;"> </span><span style="color: #9cdcfe;">f</span><span style="color: #d4d4d4;">=</span><span style="color: #ce9178;">"N2"</span></div>
<div><span>123 </span><span style="color: #d4d4d4;"> </span><span style="color: #9cdcfe;">w</span><span style="color: #d4d4d4;">=</span><span style="color: #b5cea8;">15</span></div>
<div><span>124 </span><span style="color: #d4d4d4;"> },</span></div>
<div><span>125 </span><span style="color: #d4d4d4;"> </span><span style="color: #569cd6;">@</span><span style="color: #d4d4d4;">{</span></div>
<div><span>126 </span><span style="color: #d4d4d4;"> </span><span style="color: #9cdcfe;">n</span><span style="color: #d4d4d4;">=</span><span style="color: #ce9178;">"Percentage"</span></div>
<div><span>127 </span><span style="color: #d4d4d4;"> </span><span style="color: #9cdcfe;">e</span><span style="color: #d4d4d4;">={</span><span style="color: #9cdcfe;">$_</span><span style="color: #dcdcaa;">.Percentage</span><span style="color: #d4d4d4;">}</span></div>
<div><span>128 </span><span style="color: #d4d4d4;"> </span><span style="color: #9cdcfe;">f</span><span style="color: #d4d4d4;">=</span><span style="color: #ce9178;">"P2"</span></div>
<div><span>129 </span><span style="color: #d4d4d4;"> </span><span style="color: #9cdcfe;">w</span><span style="color: #d4d4d4;">=</span><span style="color: #b5cea8;">15</span></div>
<div><span>130 </span><span style="color: #d4d4d4;"> }</span></div>
<div><span>131 </span><span style="color: #d4d4d4;"> </span></div>
<div><span>132 </span><span style="color: #d4d4d4;"> } </span><span style="color: #6a9955;">#Process Script Block for Show-ProcessMemory Function</span></div>
<div><span>133 </span><span style="color: #d4d4d4;"></span></div></div><!--EndFragment-->
<div style="text-align: left;"><br /></div><div style="text-align: left;">Since the parameters were the same I just called my "Get" function splatting the $PSBoundParameters variable. I decided to sort it by memory usage so the highest usage is at the top. Then, I put all the data neatly in a table using format specifiers for the numbers and specified the width as well. If you are unfamiliar with this take a look at about_calculated_properties. This is about the only place I use shorthand. "n" is short for "name", "e" is short for "expression", "f" is short for "formatstring" and "w" is short for "width".</div><div style="text-align: left;"><br /></div><div style="text-align: left;">Finally I bundled it up in a module, exporting both functions. All the code is available for download in my github repository here <a href="https://github.com/ralphmwr/MemoryUse">https://github.com/ralphmwr/MemoryUse</a></div><div style="text-align: left;"><br /></div><div style="text-align: left;">Here is a screen shot of the Show-ProcessMemory function in action:</div><div style="text-align: left;"><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjilejBBq5KqFWLYcqFzbKjpzRZ77o-HS8eh8sVSXUHLHDImamgFr5zqG-VuXJCF8f_TrxMXFxFpAGQAv_xbjXdJbiPK9c6hDtWXsJWDtfBLunwKLSuQ12VxlQtqgrvCqWizGvNRnltXto/" style="margin-left: 1em; margin-right: 1em;"><img alt="" data-original-height="244" data-original-width="773" height="202" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjilejBBq5KqFWLYcqFzbKjpzRZ77o-HS8eh8sVSXUHLHDImamgFr5zqG-VuXJCF8f_TrxMXFxFpAGQAv_xbjXdJbiPK9c6hDtWXsJWDtfBLunwKLSuQ12VxlQtqgrvCqWizGvNRnltXto/w640-h202/image.png" width="640" /></a></div><br /><br /></div><div style="text-align: left;">Looking forward to the next Iron Scripter Challenge.</div><div style="text-align: left;"><br /></div><div style="text-align: left;">Please comment if you have questions or suggestions.</div>PowerShell Hunterhttp://www.blogger.com/profile/15841787123729180224noreply@blogger.com0tag:blogger.com,1999:blog-897010371644363540.post-11110524651600519042020-04-18T20:17:00.001-07:002021-01-29T13:42:12.215-08:00Running Process Analysis - Part 1 Data CollectionComparing artifacts to a baseline is one of the simplest ways to find indicators of compromise. Basically, you have a known good baseline of data used to compare with host data. If there are differences, then you might have an indicator of compromise to hunt down. This basic analysis method can be used for many different host artifacts. In this article, I will focus on running processes.<br />
<br />
When evaluating processes, first think about what properties you might want to compare. A simple comparison might just be the name of the process, but malware can be much stealthier than just running under its name like "evil_process". One of the ways malware can disguise itself is by using common names but running from incorrect locations. In this case the path of the process executable would be a good property to check. What about an instance where the malware is actually infected a legitimate process. I'm not talking about process hollowing. I'll save that discussion for a later post. In a simpler context, the malware could just overwrite the legitimate executable. In this case we could analyze the hash of the executable.<br />
<br />
So far we have decided to look at the name, path and hash of the running process objects. How could we get this data using a PowerShell script? Fortunately, PowerShell has a built-in cmdlet that will retrieve process objects for each running process on a host machine. You could find this cmdlet using a search. This search takes advantage of PowerShell's consistent naming convention and superior documentation. These attributes combine to make PowerShell a very discoverable language. Here is how we could search for a cmdlet to retrieve running processes.<br />
<br />
<script src="https://gist.github.com/ralphmwr/d24b040648e0ea1fbeb40772d1b16f4c.js"></script>
To make sure the cmdlet does what you expect, you can review the documentation. PowerShell's documentation is top notch and will not only give you a terrific description of the cmdlet, but descriptions of all of its parameters along with several examples.<br />
<br />
<script src="https://gist.github.com/ralphmwr/3995bbc0b9f2c3bedde01eb592f10861.js"></script><br />
<br />
Next, we need to test and see if the cmdlet provides all the properties we want to analyze. From the previous discussion, what we want is name, executable path and a hash of the executable. To discover all the properties of objects returned from our cmdlet, simply pipe the results to Get-Member. This command will perform what's called "introspection" and display the object types along with their associated properties and methods.<br />
<br />
<script src="https://gist.github.com/ralphmwr/721e09f07d906367471739eef43bb73a.js"></script><br />
<br />
Looking at the list of properties returned, we can see 2 of the 3 properties we need are contained in these process objects. The only one not contained is the file hash of the executable. That's ok, we can add that one in using a different cmdlet. How could we find a cmdlet that will return a hash of file? You guessed it with Get-Command.<br />
<br />
<script src="https://gist.github.com/ralphmwr/5c2b1f68be6818d552be1aaf23d702a5.js"></script><br />
<br />
Let's test this cmdlet out and find out what type of object it returns along with its properties. We'll just run it against a file on our computer and pipe the results to Get-Member.<br />
<br />
<script src="https://gist.github.com/ralphmwr/3ea532dc0c926968fb5ab8a8a53988f7.js"></script><br />
<br />
Looking at the properties, it appears the "hash" property contains the data we need for our script.<br />
<br />
Now lets start putting this together. We need to run Get-Process but only need 2 of the automatic object properties. We also need a third custom property that will use Get-FileHash to generate the property value. We can use the Select-Object cmdlet to select object properties along with creating our own custom properties. We can pipe objects to Select-Object to enumerate the properties. We'll start with the 2 automatic properties.<br />
<br />
<script src="https://gist.github.com/ralphmwr/93653cb1aa9802a01848205126ca33b5.js"></script><br />
<br />
Now we need to add the custom property. To add a custom property, simply add a hashtable to the list of properties. Hashtables in PowerShell can be created with a special operator @{}. Inside the curly braces place the key/value pairs. This syntax is <key>=<value>. For custom properties we need two keys: name and expression. These can be abbreviated with n and e. Name will be a string that is the name of the new property and expression will be a script block that returns the value for the new custom property. So it looks like this:<br />
<br />
<script src="https://gist.github.com/ralphmwr/71e85cda4bad385d048fb85d3166cf5a.js"></script><br />
<br />
Note the $_ is used to reference the current object in the pipeline. Now we have the command all built out we can view the data right on the screen, save the results to variable or export the results to a file for analysis later.<br />
<br />
In the next post we will add to the functionality of this command by exporting the data and comparing it to a baseline.<br />
<br />
<br />
<br />
<br />
<script src="https://gist.github.com/ralphmwr/76d0a6edcd4dd13b973eb0acfa83228e.js"></script>
PowerShell Hunterhttp://www.blogger.com/profile/15841787123729180224noreply@blogger.com0tag:blogger.com,1999:blog-897010371644363540.post-19035115501916220012020-03-22T14:07:00.000-07:002020-03-23T08:53:50.477-07:00Why PowerShellWhile there are some great tools out there for cyber threat hunting, most require some type of agent installed on the hosts or a centralized server to function effectively. Unfortunately, host and sever configuration for many networks, especially those on DoD or other government systems is very controlled and the agents required for those tools simply can't be installed. In those instances, cyber threat hunters must "live off the land" when collecting host data across networks. This means we must use tools already present on hosts/servers to hunt for indicators of compromise.<br />
<br />
Fortunately, most of the networks I deal with and many other networks are already administered using PowerShell. This means that not only is PowerShell installed on most of the host machines, but the network is already configured to allow PowerShell commands to be remotely executed. Since PowerShell rides on the .NET framework, there are many providers available to expose data critical to cyber threat hunting. Additionally, PowerShell has the ability to leverage WMI so even if standard .NET classes or PowerShell's thousands of built-in cmdlets/functions don't expose the data you need, WMI probably does.<br />
<br />
PowerShell was built to help administrators get back on the command line and automate tasks for management of network systems. It is very well documented and supported. Unfortunately, much of what I find online in the way of PowerShell support/examples revolve around system administration tasks and not threat hunting. There are some unique challenges a threat hunter might have when leveraging PowerShell that a system administrator may not have. For example, since a threat hunter is likely to be limited to whatever is installed on host systems, he or she may have to use older cmdlets or WMI if an older version of PowerShell is installed. I have seen many cases where someone creates an amazing PowerShell script for threat hunting data collection but it doesn't function on all or some of the hosts in a network because it relies on functionality not available in older versions. Threat hunters have to be flexible and not just rely on scripts that can't be customized for specific situations. Additionally, threat hunters may have to work around network limitations. Recently, I was on a network where the WinRM service was disabled on several machines. While WinRM is necessary for standard PowerShell remoting, there is more than one way to skin a cat. In this case we leveraged RPC using specific PowerShell cmdlets in Windows PowerShell v5.1.<br />
<br />
This post was intended to just give you a taste of why PowerShell is an important tool for cyber threat hunting. In future posts, I will show real-world examples and hypotheticals to illustrate how PowerShell can be leveraged for threat hunting.PowerShell Hunterhttp://www.blogger.com/profile/15841787123729180224noreply@blogger.com0