Todos los días en JSOC CERT nos encontramos con eventos de diferentes entornos limitados que funcionan como parte de las soluciones AntiAPT de nuestros clientes y permiten que miles de archivos del tráfico web y de correo electrónico pasen a través de ellos. Vale la pena señalar que los sistemas Sandbox modernos en su desarrollo fueron mucho más allá que simplemente interceptar llamadas del sistema en modo Kernel y funciones API en modo usuario. Cada vez más, utilizan su propio hipervisor, un sistema para emular la actividad del usuario, la instrumentación dinámica, el hash y la agrupación en secciones de código, el análisis de la cobertura del código, etc. Tal variedad de tecnologías crea la ilusión de que si algún archivo no funciona en el sandbox y no muestra su "verdadero rostro", entonces probablemente sea APT o una tecnología innovadora para detectar un entorno virtual, del cual la comunidad del IB aún no está al tanto. Pero ...

Como no conocemos las características internas del trabajo de los sandboxes comerciales, en algunos casos hacemos una doble verificación: analizamos manualmente las muestras que pasaron la prueba. Recientemente, nos hemos encontrado varias veces que algunos sandboxes comerciales (por razones objetivas, no podemos decir cuáles) no detectaron ciertos archivos maliciosos durante el análisis dinámico, y si el analizador estático también estaba en silencio, el archivo se omitió por completo.
El análisis de sandbox logró evitar familias de malware tan conocidas como Pony, Loki y Hawkeye. Solo una cosa los unía: estaban cubiertos por un empaquetador escrito en Visual Basic.
Dado que estas familias de HPE no han sido nuevas durante mucho tiempo, el veredicto “positivo” de sandbox es muy deprimente. Por lo tanto, decidimos describir el principio general de funcionamiento de este empaquetador y las observaciones realizadas por nosotros durante algún tiempo.
El esquema general del trabajo del empacador se divide condicionalmente en 4 etapas y se muestra en el siguiente diagrama.

El punto de entrada de un archivo malicioso parece típico de las aplicaciones de Visual Basic:

Encontramos varias opciones para este empaquetador, y el código de VB Wrapper cambiaba con frecuencia, pero la tarea realizada seguía siendo la misma: transferir el control al código de la Etapa 1. En muestras anteriores, el control se transfirió utilizando las funciones API de la clase Enum * (por ejemplo, EnumWindows, EnumCalendarInfo, etc.). e) para el cual la dirección Etapa 1 del código se indicó como parámetro. Recientemente, observamos que el control se transfiere directamente.
Etapa 1
La administración recibe el código Etapa 1. Este código no está encriptado, pero está ofuscado. Los métodos de ofuscación varían de una muestra a otra, pero el algoritmo de operación general no cambia:
- Un ciclo con muchas instrucciones (incluida la basura) que genera la clave necesaria para decodificar el código de la Etapa 2. La peculiaridad de este fragmento de código es que no hay funciones de suspensión, pero debido a la gran cantidad de iteraciones, su ejecución demora un promedio de 1-2 minutos.
- Descifrado (XOR regular) y transferencia de control al código de la Etapa 2.
La siguiente captura de pantalla muestra ejemplos de métodos de ofuscación utilizados:

2 etapas
La tarea principal del código en la Etapa 2 es verificar el entorno e implementar métodos anti-depuración. Algunas partes del código están encriptadas (desencriptadas antes de la ejecución, y después de eso, encriptadas nuevamente con el mismo algoritmo XOR) para dificultar la detección mediante firmas. Después del descifrado, los rasgos característicos son visibles, según los cuales el código de la Etapa 2 puede reconocerse mediante análisis manual.

La lista de comprobaciones es bastante grande y difiere en las diferentes versiones del empaquetador, por lo que daremos varios métodos que se encontraron en todas las versiones, con capturas de pantalla, y al final enumeramos la lista completa en la tabla.
1) GetTickCount + Sleep
Se toma la marca de tiempo actual, se llama Sleep durante 2 segundos, después de lo cual se toma otra marca de tiempo inmediatamente.
Después de eso, se verifica la diferencia entre las marcas (si pasaron realmente 2 segundos).

2) SetErrorMode
Comprueba el funcionamiento correcto de la llamada a la API SetErrorMode. La función se llama dos veces seguidas con los parámetros 0x800 y 0x0, después de lo cual se verifica el resultado de la segunda llamada: debe ser igual a 0x800.

3) SetLastError
Primero, se llama a SetLastError con el parámetro 0x5, después de lo cual se verifica que el valor del último código de error en el TEB esté configurado correctamente (es decir, es 0x5).

4) Verificación del movimiento del cursor
El código entra en un bucle sin fin esperando que el mouse se mueva.

5) DbgBreakPoint y DbgUiRemoteBreakin
Estas funciones se modifican para evitar que el depurador se conecte al proceso.

Técnica
| Comentario
|
GetTickCount + Sleep
| Comprobación de marcas de tiempo
|
SetErrorMode
| Comprobar que la función funciona correctamente
|
SetLastError
| Comprobar que la función funciona correctamente
|
GetCursorPos
| Verificar el movimiento del cursor
|
Dbgbreakpoint
| Modificación de funciones para evitar la conexión del depurador
|
DbgUiRemoteBreakin
| Modificación de funciones para evitar la conexión del depurador
|
Eliminación de gancho
| Los primeros 5 bytes de funciones se restauran en ntdll.dll en caso de que haya ganchos
|
NtSetInformationThread
| Parámetro 0x11 (ThreadHideFromDebugger)
|
GetThreadContext + check DR
| Los registros de depuración DR0-DR3, DR6, DR7 están marcados.
|
Verificar puntos de interrupción
| Se verifican las instrucciones INT3 (0xCC), int 3 (0xCD 0x03) y ud2 (0x0F 0x0B) al comienzo de algunas funciones
|
cpuid (EAX = 0x0)
| Se verifican los registros EAX, ECX, EDX
|
cpuid (EAX = 0x40000000)
| Se verifican los registros EAX, ECX, EDX
|
cpuid (EAX = 0x1)
| 31 bit ECX verificado
|
PEB (Ser depurado)
| Comprueba el valor 0x1
|
PEB (NtGlobalFlag)
| Valor comprobado 0x70
|
NtQueryInformationProcess
| Llamado con banderas ProcessDebugPort (0x7), ProcessDebugFlags (0x1F), ProcessDebugObjectHandle (0x1E)
|
Verificación del nombre del proceso
| Se verifican las cadenas "muestra", "caja de arena", "virus", "malware", "self".
|
Si se completan todas las técnicas de la etapa 2, se verifica que la línea de comando cumpla con el formato especial. Si la verificación falla, se realizan las siguientes acciones:
1) La función CreateProcess se llama con el indicador CREATE_SUSPENDED para reiniciar el proceso actual. En este caso, la línea de comando tiene el formato requerido.
2) Usando las funciones GetContextThread y SetContextThread, el punto de entrada se cambia a uno nuevo, que se encuentra en el código de la Etapa 1.
3) Repita los pasos 1 y 2 (incluido un ciclo largo y todas las verificaciones). Esta vez, la verificación de la línea de comando es exitosa y el proceso continúa con el siguiente paso.
3 etapas
En esta etapa, el cuerpo del virus principal se descifra y la técnica de Hollow del proceso se realiza en el proceso actual, después de lo cual el control se transfiere al punto de entrada del virus principal.
Lección aprendida
No podemos decir exactamente qué causa este o aquel sandbox en este caso, pero quiero creer que la posibilidad de usar las técnicas descritas en el artículo por malware ha sido provista por los proveedores durante mucho tiempo, y el problema radica solo en el largo retraso en la primera etapa del trabajo del empacador .
A pesar del hecho de que los sandboxes modernos están en su mayor parte posicionados como parte de los sistemas de protección contra ataques APT, nuestras observaciones sugieren que incluso las familias maliciosas conocidas por la comunidad penetran en la infraestructura con una constancia envidiable. Dado que no hay garantías de que la muestra que omitió el entorno limitado no tenga un par de técnicas de omisión de antivirus en su arsenal, no puede confiar solo en este conjunto de soluciones protectoras. En tales casos, un proceso de monitoreo construido adecuadamente, que incluye eventos de seguridad de la información de los hosts finales, puede garantizar una respuesta oportuna y minimizar el daño potencial.