From 5fdc1297d7d29d709b552a29c35bad2d06a68d26 Mon Sep 17 00:00:00 2001 From: itm4n Date: Fri, 10 Apr 2026 13:30:54 +0200 Subject: [PATCH] Add AES encryption as a code obfuscation layer --- .vscode/settings.json | 1 + build/Build.ps1 | 127 +++++++++++++++++++++++++++++++-------- build/Seed.txt | 2 +- info/CHANGELOG.md | 6 ++ src/check/Main.ps1 | 2 +- src/core/Compression.ps1 | 4 +- 6 files changed, 112 insertions(+), 30 deletions(-) diff --git a/.vscode/settings.json b/.vscode/settings.json index f218702..01bdcf6 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -78,6 +78,7 @@ "DUID", "DVWS", "DWORD", + "Encryptor", "exploitability", "FFFD", "Filetime", diff --git a/build/Build.ps1 b/build/Build.ps1 index 6e8a543..9b0dc6e 100644 --- a/build/Build.ps1 +++ b/build/Build.ps1 @@ -55,6 +55,8 @@ function Invoke-Build { if ($TestModuleImport -eq $false) { Write-Message -Type Warning -Message "Unsupported platform for module import testing: $($CurrentPlatform)" } + + $EncryptionKey = New-RandomByteArray -Size 32 } process { @@ -157,6 +159,7 @@ function Invoke-Build { $ScriptEncoded = [Text.Encoding]::UTF8.GetBytes($ScriptBlock) } + $ScriptEncoded = ConvertTo-AesEncrypted -InputBuffer $ScriptEncoded -Key $EncryptionKey $ScriptEncoded = [System.Convert]::ToBase64String($ScriptEncoded) $ScriptContent += "`$$($ModuleName) = `"$($ScriptEncoded)`"`r`n" } @@ -165,7 +168,7 @@ function Invoke-Build { if ($ErrorCount -eq 0) { Write-Message -Type Success "Build successful, writing result to file '$($ScriptPath)'..." - $ScriptContent += "`r`n$(Get-ScriptLoader -Modules $Modules)" + $ScriptContent += "`r`n$(Get-ScriptLoader -Modules $Modules -EncodedKey ([System.Convert]::ToBase64String($EncryptionKey)))" $ScriptContent | Out-File -FilePath $ScriptPath -Encoding ascii if ($BuildProfileName -eq "PrivescCheck") { @@ -414,37 +417,35 @@ function Update-WordList { function Get-ScriptLoader { - [CmdletBinding()] - param( - [string[]] $Modules + [OutputType([String])] + param ( + [String[]] $Modules, + [String] $EncodedKey ) - begin { - $LoaderBlock = @" -`$Modules = @({{MODULE_LIST}}) -`$Modules | ForEach-Object { - `$Decoded = [System.Convert]::FromBase64String(`$_) - if (`$_ -like "H4s*") { - `$Decoded = ConvertFrom-Gzip -Bytes `$Decoded + $LoaderBlock = @" +@({{MODULE_LIST}}) | ForEach-Object { + `$dec = [Convert]::FromBase64String(`$_) + `$aes = [Security.Cryptography.Aes]::Create() + `$outbuf = New-Object Byte[] (`$dec.Length - `$aes.IV.Length) + `$aes.Key = [Convert]::FromBase64String("{{ENCODED_KEY}}") + `$aes.IV = `$dec[0..(`$aes.IV.Length - 1)] + `$rc = (New-Object Security.Cryptography.CryptoStream (New-Object IO.MemoryStream (, `$dec[`$aes.IV.Length..(`$dec.Length - 1)])), (`$aes.CreateDecryptor()), ([Security.Cryptography.CryptoStreamMode]::Read)).Read(`$outbuf, 0, `$outbuf.Length) + try { + `$sb = `$ExecutionContext.InvokeCommand.NewScriptBlock((ConvertFrom-Gzip -InputBuffer `$outbuf[0..(`$rc - 1)])) + } catch { + `$sb = `$ExecutionContext.InvokeCommand.NewScriptBlock(([Text.Encoding]::UTF8.GetString(`$outbuf[0..(`$rc - 1)]))) } - else { - `$Decoded = [Text.Encoding]::UTF8.GetString(`$Decoded) - } - `$ScriptBlock = `$ExecutionContext.InvokeCommand.NewScriptBlock(`$Decoded) - . `$ScriptBlock - Remove-Variable -Name "Decoded" - Remove-Variable -Name "ScriptBlock" + . `$sb } -Remove-Variable -Name "Modules" -@({{MODULE_STR_LIST}}) | ForEach-Object { Remove-Variable -Name `$_ } "@ - } - process { - $ModuleList = ($Modules | ForEach-Object { "`$$($_)" }) -join ',' - $ModuleStrList = ($Modules | ForEach-Object { "`"$($_)`"" }) -join ',' - ($LoaderBlock -replace "{{MODULE_LIST}}", $ModuleList) -replace "{{MODULE_STR_LIST}}", $ModuleStrList - } + $ModuleList = ($Modules | ForEach-Object { "`$$($_)" }) -join ',' + $LoaderBlockResult = $LoaderBlock + $LoaderBlockResult = $LoaderBlockResult -replace "{{MODULE_LIST}}", $ModuleList + $LoaderBlockResult = $LoaderBlockResult -replace "{{ENCODED_KEY}}", $EncodedKey + + return $LoaderBlockResult } function Remove-CommentFromScriptBlock { @@ -502,4 +503,78 @@ function ConvertTo-Gzip { $MemoryStream.Close() $MemoryStream.ToArray() } +} + +function New-RandomByteArray { + + [OutputType([Byte[]])] + param ( + [Parameter(Mandatory=$true)] + [ValidateSet(16, 32)] + [UInt32] $Size + ) + + # https://gist.github.com/lennybacon/9fcce97f84d1b760e8da0e9d0738536a + $Random = [System.Security.Cryptography.RandomNumberGenerator]::Create() + $Buffer = New-Object Byte[] $Size + $Random.GetBytes($Buffer) + return $Buffer +} + +function ConvertTo-AesEncrypted { + + [OutputType([Byte[]])] + param ( + [Parameter(Mandatory=$true)] + [Byte[]] $InputBuffer, + [Parameter(Mandatory=$true)] + [Byte[]] $Key + ) + + # https://learn.microsoft.com/en-us/dotnet/api/system.security.cryptography.aes + $AesAlg = [System.Security.Cryptography.Aes]::Create() + + $AesAlg.Key = $Key + $AesAlg.IV = New-RandomByteArray -Size 16 + + $Encryptor = $AesAlg.CreateEncryptor() + $MemoryStream = New-Object IO.MemoryStream + $CryptoStream = New-Object System.Security.Cryptography.CryptoStream $MemoryStream, $Encryptor, ([System.Security.Cryptography.CryptoStreamMode]::Write) + $CryptoStream.Write($InputBuffer, 0, $InputBuffer.Length) + $CryptoStream.FlushFinalBlock() + $Encrypted = $MemoryStream.ToArray() + $MemoryStream.Close() + $CryptoStream.Close() + + return $AesAlg.IV + $Encrypted +} + +function ConvertFrom-AesEncrypted { + + [OutputType([Byte[]])] + param ( + [Parameter(Mandatory=$true)] + [Byte[]] $InputBuffer, + [Parameter(Mandatory=$true)] + [Byte[]] $Key + ) + + # https://learn.microsoft.com/en-us/dotnet/api/system.security.cryptography.aes + $AesAlg = [System.Security.Cryptography.Aes]::Create() + + $IV = $InputBuffer[0..($AesAlg.IV.Length - 1)] + $Encrypted = $InputBuffer[$AesAlg.IV.Length..($InputBuffer.Length - 1)] + $Buffer = New-Object Byte[] ($InputBuffer.Length - $AesAlg.IV.Length) + + $AesAlg.Key = $Key + $AesAlg.IV = $IV + + $Decryptor = $AesAlg.CreateDecryptor() + $MemoryStream = New-Object IO.MemoryStream (, $Encrypted) + $CryptoStream = New-Object System.Security.Cryptography.CryptoStream $MemoryStream, $Decryptor, ([System.Security.Cryptography.CryptoStreamMode]::Read) + $ReadCount = $CryptoStream.Read($Buffer, 0, $Buffer.Length) + $CryptoStream.Close() + $MemoryStream.Close() + + return $Buffer[0..($ReadCount - 1)] } \ No newline at end of file diff --git a/build/Seed.txt b/build/Seed.txt index 5db87c8..f0144b2 100644 --- a/build/Seed.txt +++ b/build/Seed.txt @@ -1 +1 @@ -1208804952 +328070458 diff --git a/info/CHANGELOG.md b/info/CHANGELOG.md index ab6dc7c..6221eaf 100644 --- a/info/CHANGELOG.md +++ b/info/CHANGELOG.md @@ -1,5 +1,11 @@ # Changelog +## 2026-04-05 + +### Added + +- Add AES encryption on top of GZIP compression to scramble the output script. + ## 2026-03-28 ### Added diff --git a/src/check/Main.ps1 b/src/check/Main.ps1 index 71373fa..3216a28 100644 --- a/src/check/Main.ps1 +++ b/src/check/Main.ps1 @@ -202,7 +202,7 @@ function Invoke-PrivescCheck { function ConvertFrom-EmbeddedTextBlob { param([String] $TextBlob) $Decoded = [System.Convert]::FromBase64String($TextBlob) - ConvertFrom-Gzip -Bytes $Decoded + ConvertFrom-Gzip -InputBuffer $Decoded } function Invoke-DynamicCommand { diff --git a/src/core/Compression.ps1 b/src/core/Compression.ps1 index d270a73..a386f8b 100644 --- a/src/core/Compression.ps1 +++ b/src/core/Compression.ps1 @@ -1,7 +1,7 @@ function ConvertFrom-Gzip { [CmdletBinding()] - param([byte[]] $Bytes) - $is = New-Object IO.MemoryStream(, $Bytes) + param ([Byte[]] $InputBuffer) + $is = New-Object IO.MemoryStream(, $InputBuffer) $gs = New-Object IO.Compression.GzipStream $is, ([IO.Compression.CompressionMode]::Decompress) $sr = New-Object IO.StreamReader($gs) $sbd = $sr.ReadToEnd()