Sednit, también conocido como APT28, Fancy Bear y Sofacy, es un grupo de atacantes que operan desde al menos 2004 y cuyo objetivo principal es robar información confidencial de objetivos específicos. En octubre de 2016, ESET publicó un white paper titulado "En Route with Sednit", donde analiza detalladamente las tácticas empleadas y su arsenal; además, en este video se describe su modo de operar:

El mes pasado, el grupo volvió a mostrar actividad, aparentemente para interferir en las elecciones francesas y atacar al candidato centrista Emmanuel Macron. En este mismo período, nos llamó la atención un correo electrónico de phishing que contenía un archivo adjunto llamado Trump's_Attack_on_Syria_English.docx (Ataque de Trump a Siria en inglés).

El análisis del documento reveló su objetivo real: descargar el archivo Seduploader, la conocida herramienta de Sednit empleada para detectar sus objetivos de ataque. Para lograr su propósito, utilizaron dos exploits 0-day: uno para aprovechar una vulnerabilidad de ejecución remota de código en Microsoft Word (CVE-2017-0261) y otro para escalar privilegios de usuarios locales en Windows (CVE-2017-0263). ESET informó sobre ambas vulnerabilidades a Microsoft, que ayer lanzó parches como parte de su programa habitual de revisiones de los martes.

En este artículo se describe el ataque en sí y las vulnerabilidades utilizadas para infectar a los objetivos.

Del exploit de Word al dropper Seduploader

El siguiente gráfico muestra que este ataque específico coincide perfectamente con los métodos de ataque habituales de Sednit, ya que usa un correo electrónico de phishing dirigido que contiene un archivo adjunto malicioso para instalar un payload conocido, lo cual constituye la primera etapa del ataque.

En esta ocasión, el correo electrónico de phishing usaba la temática del ataque de Trump a Siria.

El archivo adjunto infectado es un documento señuelo que contiene una copia literal de un artículo titulado "Trump’s Attack on Syria: Wrong for so Many Reasons" (Ataque de Trump a Siria: un error por muchísimas razones), publicado el 12 de abril de 2017 en The California Courier:

Aquí es donde el ataque se pone interesante. El documento señuelo contiene dos exploits que permiten la instalación de Seduploader, como se muestra en el siguiente esquema:

Estos dos exploits se pueden agregar a la lista de vulnerabilidades 0-day utilizadas por Sednit durante los últimos dos años, como se muestra en la línea de tiempo:

Una vez abierto, el documento señuelo primero aprovecha una vulnerabilidad del filtro EPS en Microsoft Office, conocida como CVE-2017-0261. En este caso, el archivo EPS malicioso lleva el nombre image1.eps dentro del archivo .docx:

$ file Trump\'s_Attack_on_Syria_English.docx
Trump's_Attack_on_Syria_English.docx: Zip archive data, at least v2.0 to extrac2
$ unzip Trump\'s_Attack_on_Syria_English.docx
Archive: Trump’s_Attack_on_Syria_English.docx
 inflating: [Content_Types].xml
 inflating: docProps/app.xml
 inflating: docProps/core.xml
 inflating: word/document.xml
 inflating: word/fontTable.xml
 inflating: word/settings.xml
 inflating: word/styles.xml
 inflating: word/webSettings.xml
 inflating: word/media/image1.eps
 inflating: word/theme/theme1.xml
 inflating: word/_rels/document.xml.rels
 inflating: _rels/.rels
$ file word/media/image1.eps
word/media/image1.eps: PostScript document text conforming DSC level 3.0

El archivo del exploit de EPS está ofuscado por un simple XOR. EPS proporciona la funcionalidad a variables XOR y evalúa source (exec). La clave utilizada aquí es 0xc45d6491 en una cadena hexadecimal grande y exec se llama en el búfer descifrado.

$ cat word/media/image1.eps
%!PS-Adobe-3.0
%%BoundingBox:   36   36 576 756
%%Page: 1 1
/A3{ token pop exch pop } def /A2 <c45d6491> def /A4{ /A1 exch def 0 1 A1 length 1 sub { /A5 exch def A1 A5 2 copy get A2 A5 4 mod get xor put } for A1 } def <bf7d4bd9[...]b97d44b1> A4 A3 exec quit

Una vez descifrado, el exploit luce muy sumilar al que fue bien documentado por FireEye en 2015. La vulnerabilidad usada en ese entonces era CVE-2015-2545. La principal diferencia se resalta en el siguiente bloque, y es cómo ejecuta la corrupción de memoria con la instrucción forall.

[...]
500 {
    A31 589567 string copy pop
} repeat
1 array 226545696 forall
/A19 exch def
[...]

Una vez que se obtiene la ejecución del código, carga un shellcode que recupera algunas API de Windows indocumentadas como NtAllocateVirtualMemoryNtFreeVirtualMemory y ZwProtectVirtualMemory.

[...]
 v1 = (*(__readfsdword(0x30u) + 12) + 12);
 v2 = v1->InLoadOrderModuleList.Flink;
 [...]
 for ( addr_user32 = 0; v2 != v1; v135 = v2 )
 {
   v3 = *(v2 + 48);
   v132 = *(v2 + 44);
   if ( v3 )
   {
     v4 = *v3;
     v5 = 0;
     v6 = 0;
     if ( *v3 )
     {
       do
       {
         if ( v132 && v6 >= v132 )
           break;
         if ( (v4 - 0x41) <= 0x19u )
           v4 += 0x20;
         v2 = v135;
         v7 = __ROL4__(v5, 7);
         ++v3;
         v5 = v4 ^ v7;
         v4 = *v3;
         ++v6;
       }
       while ( *v3 );
       v1 = v133;
     }
     switch ( v5 )
     {
       case kernel32:
         addr_kernel32 = *(v2 + 24);
         break;
       case ntdll:
         addr_ntdll = *(v2 + 24);
         break;
       case user32:
         addr_user32 = *(v2 + 24);
         break;
       }
     }
[...]

Luego de más instancias de descifrado, el dropper Seduploader se carga y ejecuta. Ten en cuenta que toda esta ejecución se produce dentro del proceso WINWORD.EXE que se ejecuta con los privilegios del usuario actual.

El dropper Seduploader

Seduploader está conformado por dos componentes distintos: un dropper y un payload persistente (consulta la página 27 de nuestro whitepaper "En Route with Sednit").

Por más que el dropper utilizado en este ataque evolucionó desde la última versión que analizamos, su objetivo final sigue siendo el mismo: entregar el payload Seduploader. Esta nueva versión del dropper ahora contiene código para integrar el exploit LPE y usarlo en la vulnerabilidad CVE-2017-2063. El análisis detallado de esta vulnerabilidad se describe en la próxima sección de este artículo; por ahora nos ocuparemos de Seduploader.

En primer lugar, el nuevo código del dropper comprueba si el proceso se está ejecutando en una versión de Windows de 32 o 64 bits. Dependiendo del resultado, se cargará la versión correcta del exploit en la memoria.

 

[...]
 if ( Is64Process() == 1 )
 {
     addr_exploit = exploit_64b;
     size_exploit = 0x2E00;
 }
 else
 {
     addr_exploit = exploit_32b;
     size_exploit = 0x2400;
 }
[...]

Una vez que el exploit se ejecuta correctamente, el dropper Seduploader se volverá a cargar a sí mismo en el espacio de memoria de WINWORD y llamará a CreateRemoteThread con la dirección del punto de entrada UpLoader, que ejecutará el código encargado de instalar el payload de Seduploader. Este código se ejecutará con privilegios del sistema, gracias al exploit.

El payload Seduploader

El payload Seduploader es un downloader utilizado por los operadores de Sednit como malware de reconocimiento y está compuesto por dos partes. La primera parte es la responsable de inyectar la segunda parte en el proceso correcto, dependiendo de si se carga en el proceso WINWORD.EXE o no. La segunda parte es el downloader en sí.

Si Seduploader se ejecuta en WINWORD.EXE, su primera parte creará un mutex denominado flPGdvyhPykxGvhDOAZnU y abrirá un identificador en el proceso actual. Dicho identificador se utilizará para asignar memoria y escribir en ella el código de la segunda parte del componente payload, que luego será ejecutado mediante una llamada a CreateRemoteThread. De lo contrario, si no se está ejecutando en WINWORD.EXE, Seduploader utilizará CreateThread para iniciar su segunda parte.

El downloader contiene las funciones habituales de Seduploader y el algoritmo de cifrado de strings. Sin embargo, tiene algunos cambios que describimos a continuación.

En primer lugar, el algoritmo de hash utilizado para identificar los nombres de DLL y las funciones de la API que se deben resolver fue reemplazado por uno nuevo. Los lectores de nuestro white paper recordarán que el algoritmo de hash anterior había estado basado en gran parte en el código encontrado en Carberp. Bueno, el nuevo algoritmo tampoco fue creado desde cero: esta vez, Sednit usó código muy similar a PowerSniff.

Por otro lado, se agregó una nueva etiqueta img al mensaje del informe de Seduploader. Esta etiqueta permite extraer capturas de pantalla:

 

[...]
keybd_event(VK_SNAPSHOT, 0x45u, KEYEVENTF_EXTENDEDKEY, 0u);
Sleep(1000u);
keybd_event(VK_SNAPSHOT, 0x45u, KEYEVENTF_EXTENDEDKEY|KEYEVENTF_KEYUP, 0u);
OpenClipboard(0u);
hData = GetClipboardData(CF_BITMAP);
CloseClipboard();
if ( !hData )
  return 0;
GdiplusStartupInput = (const int *)1;
v10 = 0;
v11 = 0;
v12 = 0;
GdiplusStartup(&token, &GdiplusStartupInput, 0);
if ( fGetEncoderClsid((int)L"image/jpeg", &imageCLSID) )
{
  v4 = sub_10003C5F((int)hData, 0);
  ppstm = 0;
  CreateStreamOnHGlobal(0u, 1u, &ppstm);
  v5 = GdipSaveImageToStream(v4[1], ppstm, &imageCLSID, 0);
  if ( v5 )
    v4[2] = v5;
  (*(void (__thiscall **)(_DWORD *, signed int))*v4)(v4, 1);
  IStream_Size(ppstm, &pui);
  cb = pui.s.LowPart;
  v7 = ppstm;
  *a1 = pui.s.LowPart;
  IStream_Reset(v7);
  v1 = j_HeapAlloc(cb);
  IStream_Read(ppstm, v1, cb);
  ppstm->lpVtbl->Release(ppstm);
}
GdiplusShutdown(token);
return v1;
}

Como acostumbran, los operadores de Sednit no reinventaron la rueda: encontramos varias similitudes entre su implementación de la función de captura de pantalla y el código disponible en stackoverflow. En lugar de usar GetForegroundWindow para recuperar un identificador de la ventana de primer plano en la que el usuario está trabajando, Sednit eligió usar keybd_event para enviar una pulsación de tecla "Imprimir pantalla" y recuperar la imagen desde el portapapeles.

A continuación, codifica la imagen con Base64 y la adjunta al informe, cuya estructura ahora se ve así:

Etiqueta Valor
id= Número de serie del disco rígido*
w= Lista de procesos
None Información de la tarjeta de interfaz de red
disk= Llave de registro**
build= 4 bytes
inject Campo opcional***
img= Captura de pantalla codificada en Base64

* resultado de “import win32api;print hex(win32api.GetVolumeInformation("C:\\")[1])
** contenido de HKLM\SYSTEM\CurrentControlSet\Services\Disk\Enum
*** se activa si SEDUPLOADER usa la inyección en un navegador para conectarse a Internet

El grupo Sednit ya había usado antes las capturas de pantalla. En el pasado, la funcionalidad se creó como una herramienta separada y autónoma, a menudo invocada por Xtunnel en una etapa posterior de infección (consulta la página 77 de nuestro whitepaper), pero ahora está incorporada a Seduploader para usarla en la fase de reconocimiento.

Finalmente, se añadieron dos nuevas funciones en el Config: shell LoadLib. La función shell le permite al atacante ejecutar código arbitrario directamente en la memoria. La función LoadLib es un campo de bits que le permite ejecutar una Dll arbitraria llamando a rundll32.exe.

CVE-2017-0263: vulnerabilidad para escalar los privilegios de los usuarios

Cómo opera el exploit

Como se mencionó anteriormente, para desplegar el payload Seduploader, el dropper Seduploader obtiene privilegios de sistema mediante el aprovechamiento de una vulnerabilidad de LPE (Escalada de privilegios para usuarios locales) conocida como CVE-2017-0263. En esta sección, describiremos cómo Sednit aprovecha dicha vulnerabilidad.

En primer lugar, aunque la vulnerabilidad afecta a los sistemas Windows 7 y posteriores (al final de este post figura la lista completa de las plataformas afectadas), el exploit está diseñado para evitar su ejecución en las versiones de Windows 8.1 y posteriores.

Dado que el exploit puede atacar plataformas de 32 y 64 bits, primero determinará si el proceso se está ejecutando en WOW64. El exploit asignará varias páginas hasta llegar a una dirección alta (0x02010000). Luego creará la siguiente estructura:

 

struct Payload
 {
   LONG PTEAddress;               // Points to the PTE entry containing the physical address of the page containing our structure. Only used for windows 8+
   LONG pid;                      // Injected process pid;
   LONG offset_of_lpszMenuName;   // Offset of the lpszMenuName in the win32k!tagCLS structure
   LONG offset_of_tagTHREADINFO;  // Offset of the pti field in the win32k!tagWND structure.
   LONG offset_of_tagPROCESSINFO; // Offset of the ppi field in the win32k!tagTHREADINFO structure.
   LONG offset_of_TOKEN;          // Offset of the Token field in the nt!_EPROCESS structure.
   LONG tagCLS[0x100];            // Array containing the tagCLS of the created windows.
   LONG WndProcCode;              // Code of the WndProc meant to be run in kernel mode.
 };

A continuación, recuperará la dirección de HMValidateHandle. Esta función le permite al atacante obtener la dirección del kernel a partir de un objeto tagWND.

Aquí mostramos en líneas generales cómo funciona el resto del exploit:

El exploit creará 256 clases de ventanas aleatorias y sus ventanas asociadas. Cada ventana tendrá 512 bytes de memoria adicional. Esta memoria adicional es contigua al objeto tagWND en el espacio del kernel. Una vez creada la primera ventana (es decir, en la memoria extra), el exploit crea un objeto falso que contendrá principalmente su propia dirección para uso posterior, como se muestra en la imagen:

Cuando se crean todas las ventanas, el exploit asignará dos ventanas adicionales. El propósito de la primera es ejecutarse en un subproceso del kernel; vamos a llamar a esta ventana KernelWnd. La otra principalmente recibirá todos los mensajes necesarios requeridos por el exploit para completar su función, vamos a llamarla TargetWindow. A continuación, el exploit asocia este procedimiento con el objeto recién asignado, KernelWnd.

// ...
TargetWindow = CreateWindowExW(0x80088u, MainWindowClass, 0, WS_VISIBLE, 0, 0, 1, 1, 0, 0, hModuleSelf, 0);
KernelWnd = CreateWindowExW(0, MainWindowClass, 0, 0, 0, 0, 1, 1, 0, 0, hModuleSelf, 0);
// ...
SetWindowLongW(KernelWnd, GWL_WNDPROC, (LONG)Payload_0->WndProc);

Vamos a dar un poco de contexto para el comportamiento del componente win32k. Cada vez que se crea una nueva ventana mediante CreateWindowExW, el controlador asignará un nuevo objeto tagWND en el kernel. El objeto se puede describir de la siguiente forma (algunos campos fueron eliminados para mejorar la claridad):

kd> dt tagWND
 win32k!tagWND
   +0x000 head             : _THRDESKHEAD
   +0x028 state            : Uint4B
   // ...
   +0x028 bServerSideWindowProc : Pos 18, 1 Bit
   // ...
   +0x042 fnid             : Uint2B
   +0x048 spwndNext        : Ptr64 tagWND
   +0x050 spwndPrev        : Ptr64 tagWND
   +0x058 spwndParent      : Ptr64 tagWND
   +0x060 spwndChild       : Ptr64 tagWND
   +0x068 spwndOwner       : Ptr64 tagWND
   +0x070 rcWindow         : tagRECT
   +0x080 rcClient         : tagRECT
   +0x090 lpfnWndProc      : Ptr64     int64
   +0x098 pcls             : Ptr64 tagCLS
   // ...

Como se puede ver, tagWND→lpfnWindowProc contiene la dirección del procedimiento asociado con esta ventana. Por lo general, el controlador disminuye sus privilegios para ejecutar este procedimiento en el contexto del usuario. Este comportamiento está controlado por el bit tagWND→bServerSideProc. Si se establece este bit, el procedimiento se ejecutará con privilegios elevados, es decir, en el kernel. Para que funcione el exploit, es necesario invertir el bit tagWND→bServerSideProc. Lo único que debe hacer el atacante es encontrar la forma de invertirlo.

Durante la destrucción de los menús, el enlace previamente configurado comprobará si la clase del objeto es SysShadow, como se muestra en el siguiente bloque de código. De ser así, reemplazará el procedimiento asociado con el suyo propio.

 

 GetClassNameW(tagCWPSTRUCT->hwnd, &ClassName, 20);
 if ( !wcscmp(&ClassName, STR_SysShadow) )
 {
   if ( ++MenuIndex == 3 )
   {
     // tagWND
     ::wParam = *(_DWORD *)(FN_LeakHandle((int)hWnd[0]) + sizeof_tagWND_0);
     // Replace the WndProc of the object
     SetWindowLongW(tagCWPSTRUCT->hwnd, GWL_WNDPROC, (LONG)FN_TriggerExploit);
   }

En este procedimiento, se puede observar que el exploit busca el mensaje WM_NCDESTROY. Si se cumplen los requisitos, creará un objeto tagPOPUPMENU malicioso, descrito por el siguiente pseudocódigo:

if ( Msg == WM_NCDESTROY )
 {
   struct tagPOPUPMENU *pm = BuildFakeObject();
   SetClassLongW(..., pm);
 }

Cabe notar que la dirección utilizada para crear el objeto se encuentra dentro de la memoria adicional asignada al final de nuestra primera ventana tagWND. A continuación, el exploit llama a NtUserMNDragLeave  con el fin de invertir el bit bServerSideProc de nuestra ventana KernelWnd. Para ello, la función recuperará un objeto tagMENUSTATE utilizando la estructura agTHREADINFO. El objeto tagMENUSTATE contiene la dirección del objeto del menú que se está destruyendo (tagMENUSTATE→pGlobalPopupMenu).

Como se puede ver,  tagPOPUPMENU es el objeto malicioso que hemos creado en el espacio de usuario antes de llamar a NtUserMNDragLeave. Al observar los campos en el tagPOPUPMENU malicioso, podemos ver que todos apuntan a la memoria extra excepto uno, que apunta a nuestro objeto KernelWnd.

De aquí en más, la ejecución alcanzará la función MNFreePopup, que tiene un puntero que apunta a un objeto tagPOPUPMENU. Eventualmente, esta función llamará a HMAssignmentUnlock, pasando por los campos spwndNextPopup y spwndPrevPopup como argumento:

; win32k!HMAssignmentUnlock
 sub     rsp,28h
 mov     rdx,qword ptr [rcx]
 and     qword ptr [rcx],0
 test    rdx,rdx
 je      win32k!HMAssignmentUnlock+0x4f (fffff960`00119adf)
 add     dword ptr [rdx+8],0FFFFFFFFh ; Flipping bServerSideProc
 jne     win32k!HMAssignmentUnlock+0x4f (fffff960`00119adf)
 movzx   eax,word ptr [rdx]

Tras la ejecución de syscall, nuestra estructura tagWND asociada con nuestra ventana KernelWnd se verá así:

¡Todo está listo! Lo único que necesita hacer el exploit es enviar el mensaje correcto para activar la ejecución de nuestro procedimiento en modo kernel.

syscall(NtUserMNDragLeave, 0, 0);
 // Send a message to the procedure in order to trigger its execution in kernel mode.
 KernelCallbackResult = SendMessageW(KernelWnd, 0x9F9Fu, ::wParam, 0);
 Status.Triggered = KernelCallbackResult == 0x9F9F;
 if ( KernelCallbackResult != 0x9F9F )
   // Error, try again.
   PostMessageW(TargetWindow, 0xABCDu, 0, 0);

Por último, el procedimiento de ventana que se ejecuta con privilegios elevados robará el token del sistema y lo añadirá al proceso de llamada. Una vez que el exploit se ha ejecutado correctamente, FLTLDR.EXE debería ejecutarse con privilegios de sistema e instalar el payload Seduploader.

Resumen

Esta campaña nos muestra que el grupo Sednit no ha cesado sus actividades. Siguen manteniendo sus viejos hábitos: utilizan métodos de ataque conocidos, reutilizan código de otros programas maliciosos o de sitios web públicos, y cometen pequeños errores como los errores tipográficos en la configuración de Seduploader (shel en vez de shell).

También es habitual el hecho de que una vez más hayan mejorado su conjunto de herramientas, esta vez añadiendo algunas funciones integradas, como la capacidad de tomar capturas de pantalla y los dos exploits 0-day incorporados a su arsenal.

Plataformas afectadas por CVE-2017-0262 y CVE-2017-0263 (según Microsoft)

CVE-2017-0262

  • Microsoft Office 2010 Service Pack 2 (ediciones de 32 bits)
  • Microsoft Office 2010 Service Pack 2 (ediciones de 64 bits)
  • Microsoft Office 2013 Service Pack 1 (ediciones de 32 bits)
  • Microsoft Office 2013 Service Pack 1 (ediciones de 64 bits)
  • Microsoft Office 2013 RT Service Pack 1
  • Microsoft Office 2016 (edición de 32 bits)
  • Microsoft Office 2016 (edición de 64 bits)

CVE-2017-0263

  • Windows 7 para sistemas de 32 bits, Service Pack 1
  • Windows 7 para sistemas basados en x64, Service Pack 1
  • Windows Server 2008 R2 para sistemas basados en  x64, Service Pack 1 (instalación de Server Core)
  • Windows Server 2008 R2 para sistemas basados en Itanium, Service Pack 1
  • Windows Server 2008 R2 para sistemas basados en x64, Service Pack 1
  • Windows Server 2008 R2 para sistemas de 32 bits, Service Pack 2 (instalación de Server Core)
  • Windows Server 2012
  • Windows Server 2012 (instalación de Server Core)
  • Windows 8.1 para sistemas de 32 bits
  • Windows 8.1 para sistemas basados en x64
  • Windows Server 2012 R2
  • Windows RT 8.1
  • Windows Server 2012 R2 (instalación de Server Core)
  • Windows 10 para sistemas de 32 bits
  • Windows 10 para sistemas basados en x64
  • Windows 10 Versión 1511 para sistemas basados en x64
  • Windows 10 Versión 1511 para sistemas de 32 bits
  • Windows Server 2016
  • Windows 10 Versión 1607 para sistemas de 32 bits
  • Windows 10 Versión 1607 para sistemas basados en x64
  • Windows Server 2016 (instalación de Server Core)
  • Windows 10 Versión 1703 para sistemas de 32 bits
  • Windows 10 Versión 1703 para sistemas basados en x64
  • Windows Server 2008 para sistemas basados en Itanium, Service Pack 2
  • Windows Server 2008 para sistemas de 32 bits, Service Pack 2
  • Windows Server 2008 para sistemas basados en x64, Service Pack 2 o posterior
  • Windows Server 2008 para sistemas basados en  x64, Service Pack 2 (instalación de Server Core)

IoCs

También disponibles en la cuenta de Github de ESET.

SHA-1 Nombre del archivo Detección de ESET
d5235d136cfcadbef431eea7253d80bde414db9d Trump's_Attack_on_Syria_English.docx Win32/Exploit.Agent.NWZ
18b7dd3917231d7bae93c11f915e9702aa5d1bbb image1.eps Win32/Exploit.Agent.NWZ
6a90e0b5ec9970a9f443a7d52eee4c16f17fcc70 joiner.dll Win32/Exploit.Agent.NWV
e338d49c270baf64363879e5eecb8fa6bdde8ad9 apisecconnect.dll Win32/Sednit.BG
d072d9f81390c14ffc5f3b7ae066ba3999f80fee none Win32/Exploit.Agent.NWV

Mutex

flPGdvyhPykxGvhDOAZnU

Clave de registro

HKCU\Software\Microsoft\Office test\Special\Perf