Here’s a quick and simple anti-virus avoidance trick for malicious payloads.
I came across the need today to avoid static/string based detection in a suite of AV products for a few powershell scripts I was staging to a box. Typically, I’d use some combination of encryption or custom, semi-complex obfuscation to get past proxy and endpoint AV. But, I needed something much simpler to keep my loader lightweight.
For demonstration purposes and for this article, I’ll be using a mostly-used-for-evil powershell function, Invoke-ReflectivePEInjection.ps1. Source https://github.com/PowerShellMafia/PowerSploit/blob/master/CodeExecution/Invoke-ReflectivePEInjection.ps1.
I’ll be using Virus Total to show the AV avoidance success rate. Note: I understand that VT is not as accurate as having the actual targeted product(s) installed with the vendor’s recommended settings. But it will work there also…at least it did for me.
Results of the VT scan against Invoke-ReflectivePEInjection.ps1, which I named someEvil.ps1.
21 AV engines flagged it as evil. My only question is, what the heck is wrong with the other 39? Note: VT shows the original script name as it flagged it, apparently, based on the well known file hash alone.
As stated above, I was looking for simple, lightweight, encoding methods that were prevalent throughout the majority of programming languages. Results after b64 encoding the file…
Darn, b64 alone was not enough. But, we are down to 13 of 59.
Based on my testing from attempt #0, I wanted to see what would happen if I simply padded the script. I added the comment “padding” at the bottom.
Wow, the same file from attempt #0 dropped from 21 hits down to just 7. It would appear that the majority of the AV engines may have been relying on the checksum alone. Dumb. If that assumption is correct, attempt #1 may have been b64 decoded by the AV engine and flagged on the checksum alone for many of the AV engines.
Combing methods from attempt #1 and #2, I padded the powershell script, then b64 encoded it.
It seems that all but 3 of the AV engines failed to decode the b64 and/or are not performing basic string analysis. Note, I did get a locally installed AV product, represented in VT, to flag this as evil when VT did not. That is consistent with VT’s statement, “VirusTotal will not behave exactly the same as the equivalent public commercial version of the given product”.
At this point, I pulled my hands off the keyboard to ponder on this for a few seconds. Then it hit me. What if the b64 decoder in the AV engine was unable to decode it, but it’s a simple enough tweak that I can decode it with a few lines of code in my loader?
I came up with two separate, potential solutions:
Both of those did it! Both were able to avoid detection in my local install as well. To a b64 decoder, the result of both of these is just gibberish.
With either solution, regardless of the languages I was using at the time (vba, powershell, go), it only took a few lines of code to deobfuscate back to the original script.
Example one-line powershell decode command for solution #1:
$a = (Get-Content [FILE PATH HERE]\test.b64.rev | Out-String).ToCharArray(); [array]::Reverse($a); [System.Text.Encoding]::ASCII.GetString([System.Convert]::FromBase64String(-join($a)))
And an example one-line powershell decode command for solution #2:
[System.Text.Encoding]::ASCII.GetString([System.Convert]::FromBase64String((Get-Content [FILE PATH HERE]\someEvil.ps1.b64ish.txt | Out-String).Substring(1)))