-
Notifications
You must be signed in to change notification settings - Fork 70
Conducting AMSI Scans
PSAmsi is flexible and can be used in a variety of ways.
PSAmsi exposes the [PSAmsiScanner]
class for conducting simple AMSI scans.
Here's a couple of examples of conducting AMSI scans with [PSAmsiScanner]
:
PS > $Scanner = [PSAmsiScanner]::new()
PS > $Scanner.GetPSAmsiScanResult('test')
False
PS > $Scanner.GetPSAmsiScanResult([Uri]::new('https://github.com/PowerShellMafia/PowerSploit/raw/master/Exfiltration/Invoke-Mimikatz.ps1'))
True
Everything that can be accomplished with the [PSAmsiScanner]
class can also be done using PowerShell cmdlets exposed by PSAmsi that wrap [PSAmsiScanner]
's functionality.
For example:
PS > $Scanner = New-PSAmsiScanner
PS > Get-PSAmsiScanResult -ScriptString 'test' -PSAmsiScanner $Scanner
False
PS > Get-PSAmsiScanResult -ScriptUri [Uri]::new('https://github.com/PowerShellMafia/PowerSploit/raw/master/Exfiltration/Invoke-Mimikatz.ps1') -PSAmsiScanner $Scanner
True
Like all functions that accept a Script as input in PSAmsi, the Get-PSAmsiScanResult
function accepts either a String, Path, Uri, or ScriptBlock using the -ScriptString
, -ScriptPath
, -ScriptUri
, or -ScriptBlock
parameters.
You also do not need to create the [PSAmsiScanner]
before calling Get-PSAmsiScanResult
, it will create one for you if it is not provided:
PS > Get-PSAmsiScanResult -ScriptString 'test'
False
PS > Get-PSAmsiScanResult -ScriptPath '.\EvilScript.ps1'
True
PS > Get-PSAmsiScanResult -ScriptBlock { 'test' }
False
PS > Get-PSAmsiScanResult -ScriptUri 'http://example.com/EvilScript.ps1'
True
Get-PSAmsiScanResult
also accepts pipeline input for bulk scanning!
PS > @({ 'test1' }, { 'test2'}, {'test3'}) | Get-PSAmsiScanResult
False
False
False
The [PSAmsiScanner]
class maintains a cache of scan results, and it will return the cached result if it is asked to scan a repeated request. This is mainly done to avoid generating unnecessary AMSI alerts.
For example, the following will only generate one AMSI alert:
PS > $PSAmsiScanner = New-PSAmsiScanner
PS > Get-PSAmsiScanResult -ScriptUri 'http://example.com/EvilScript.ps1' # This generates an AMSI alert
True
PS > Get-PSAmsiScanResult -ScriptUri 'http://example.com/EvilScript.ps1' # This does NOT generate an AMSI alert, the cached result is returned
True
Keep in mind that this only works if you are using the same PSAmsiScanner, the following generates two AMSI alerts:
PS > Get-PSAmsiScanResult -ScriptUri 'http://example.com/EvilScript.ps1' # This generates an AMSI alert
True
PS > Get-PSAmsiScanResult -ScriptUri 'http://example.com/EvilScript.ps1' # This also generates an AMSI alert, because a new PSAmsiScanner is created and used
True
The pipeline is safe, and utilizes caching
# This only generates a single AMSI alert, as the PSAmsiScanner is re-used.
PS > @([uri]::new('http://example.com/EvilScript.ps1'), [uri]::new('http://example.com/EvilScript.ps1')) | Get-PSAmsiScanResult
True
True
This caching is really handy for offensive operations. But if you are trying to maintain an up-to-date list of strings flagged by AMSI over a long period of time, signatures could change and caching could cause problems.
PSAmsi allows for disabling the ScanCache in a couple of ways. You can create a new PSAmsiScanner that has caching disabled:
PS > $Scanner = New-PSAmsiScanner -DisableCache
You can also turn it off on an existing PSAmsiScanner:
PS > $Scanner.CacheEnabled = $False
An interesting alernative for the use case of long-term analysis of constanly updating AMSI singatures is to reset the ScanCache periodically, instead of disabling it. This allows us to maintain the efficiency of using a cache, but can periodically reset it to learn of new signatures.
This can be accomplished with the PowerShell cmdlet:
PS > Reset-PSAmsiScanCache -PSAmsiScanner $Scanner
Or by simply setting the ScanCache to an empty Hashtable:
PS > $Scanner.ScanCache = @{}
Credit - PSAmsi uses PSReflect written by Matt Graeber to call the export AMSI.dll functions in memory.