Esta es una investigación llevada a cabo por Joakim Kennedy, investigador de seguridad de Intezert, y el equipo de Black Berry Threat Research & Intelligence. También disponible en el blog de Black Berry aquí (inglés).
Tal y como se explica en la historia de Venom, en biología, un simbionte, es un organismo que convive en simbiosis con otro organismo. Esta simbiosis puede ser mutuamente beneficiosa para ambos, pero también puede ser parasitaria cuando uno se beneficia y el otro se perjudica. Hace unos meses los investigadores arriba mencionados, descubrieron un nuevo tipo de malware para Linux® no detectado que actúa con esta naturaleza parasitaria. Al que han bautizado como Simbionte.
Lo que lo diferencia de otros programas maliciosos de Linux con los que nos podemos encontrar habitualmente es que necesita infectar otros procesos en ejecución para infligir daños en los equipos. En lugar de ser un archivo ejecutable que se ejecuta de forma independiente para infectar una máquina, utiliza una biblioteca de objetos compartidos (SO) que se carga en todos los procesos en ejecución mediante la variable de entorno LD_PRELOAD (t1574.006) e infecta la máquina de forma parasitaria. Una vez infectados todos los procesos en ejecución, proporciona al actor de la amenaza una funcionalidad tipo rootkit, con la capacidad de recopilar credenciales así como acceso remoto.
El nacimiento de un simbionte
La detección mas temprana que tuvieron los investigadores de este Simbionte es de noviembre de 2021, y parece haber sido escrito para apuntar al sector financiero en América Latina. Una vez que el malware ha infectado una máquina, tiene la capacidad de ocultarse a sí mismo y a cualquier otro malware utilizado por el atacante, lo que hace que las infecciones sean muy difíciles de detectar. Es posible que la realización de análisis forenses en vivo de una máquina infectada no devuelva resultados, ya que el malware oculta todos los archivos, procesos y artefactos de la red. Además de la capacidad de rootkit mencionada, el malware proporciona una puerta trasera (backdoor) para que el atacante inicie sesión como un usuario mas en la máquina con una contraseña codificada y ejecute comandos con privilegios de administrador.
Dado que este malware es altamente evasivo, es probable que una infección de Simbionte pase totalmente desapercibida. En la investigación realizada, no se han encontrado suficientes evidencias para determinar si Simbionte se está utilizando en ataques amplios o altamente dirigidos.
Un aspecto técnico interesante de Simbionte es su funcionalidad de conexión Berkeley Packet Filter (BPF). Éste no es el primer malware de Linux en utilizar BPF. Por ejemplo, las puertas traseras avanzadas atribuidas a Equation Group han estado utilizando BPF para comunicarse de forma encubierta. Sin embargo, utiliza BPF para ocultar el tráfico de red malicioso en una máquina infectada. Cuando un administrador inicia cualquier herramienta de captura de paquetes en máquina infectada, el código de bytes BPF se inyecta en el kernel que define qué paquetes deben capturarse. En dicho proceso, Simbionte agrega primero su código de bytes para poder filtrar el tráfico de red que no desea que vea el software de captura de tráfico de paquetes.
Técnicas de evasión
Además es extremadamente sigiloso. El malware está diseñado para ser cargado por el enlazador a través de la directiva LD_PRELOAD. Esto permite que se cargue antes que cualquier otro objeto compartido. Dado que se carga primero, puede «secuestrar las importaciones» de los archivos de la biblioteca cargados para la aplicación. Simbionte usa esto para ocultar su presencia en la máquina conectando las funciones libc y libpcap. La siguiente ilustración muestra un resumen de las evasiones utilizadas por el malware.

Actividad del anfitrión
El malware, además de ocultar su propia presencia en la máquina, también oculta otros archivos relacionados con él. Dentro del binario, hay una lista que carga dinámicamente la librería libc y llama a la función original. Esta lógica se utiliza en todas las funciones enganchadas. Un ejemplo se muestra en la imagen a continuación:

Si la aplicación que llama está tratando de acceder a un archivo o carpeta en /proc, el malware borra la salida de los nombres de proceso que están en su lista. Los nombres de procesos en la lista a continuación se extrajeron de las muestras que los investigadores han descubierto:
- certbotx64
- certbotx86
- javautils
- javaserverx64
- javaclientex64
- javanodex86
Si la aplicación que llama no intenta acceder a algo en /proc, en malware borra el resultado de una lista de archivos. Los archivos extraídos de todas las muestras que fueron examinadas se muestran en la lista a continuación. Algunos de los nombres de los archivos coinciden con los utilizados por Simbionte, mientras que otros coinciden con los nombres de los archivos que se sospecha que son herramientas utilizadas por el atacante en la máquina infectada. La lista incluye los siguientes archivos.
- apache2start
- apache2stop
- profiles.php
- 404erro.php
- javaserverx64
- javaclientex64
- javanodex86
- liblinux.so
- java.h
- open.h
- mpt86.h
- sqlsearch.php
- indexq.php
- mt64.so
- certbot.h
- cert.h
- certbotx64
- certbotx86
- javautils
- search.so
Una consecuencia de que Simbionte se cargue con los procesos a través de LD_PRELOAD es que herramientas como ldd, una utilidad que imprime las bibliotecas compartidas requeridas por cada programa, enumerará el malware como un objeto cargado. Para contrarrestar esto, el malware se activa y busca llamadas a esta función con la variable de entorno LD_TRACE_OBJECTS establecida en 1.
Para entender por qué, vale la pena mirar la página del manual de ldd:
En el caso habitual, ldd invoca el enlazador dinámico estándar (consulte ld.so(8)) con la variable de entorno LD_TRACE_LOADED_OBJECTS establecida en 1. Esto hace que el enlazador dinámico inspeccione las dependencias dinámicas del programa y encuentre (de acuerdo con las reglas descritas en ld.so(8)) y cargue los objetos que satisfagan esas dependencias. Para cada dependencia, ldd muestra la ubicación del objeto coincidente y la dirección (hexadecimal) en la que se carga. (Las dependencias compartidas de ld-linux son especiales; consulte vdso(7) y ld.so(8).)
Cuando el malware detecta esto, ejecuta el cargador como lo hace ldd, pero borra su propia entrada del resultado.
Actividad de la red
También tiene la funcionalidad de ocultar la actividad de la red en la máquina infectada. Utiliza tres métodos diferentes para lograr esto. El primer método consiste en conectar fopen y fopen64. Si la aplicación que llama intenta abrir /proc/net/tcp, el malware crea un archivo temporal y copia la primera línea de ese archivo. Después de eso, escanea cada línea en busca de puertos específicos. Si el malware encuentra un puerto que está buscando en una línea que esta escaneando, salta a la siguiente línea. De lo contrario, la línea se escribe en el archivo temporal. Una vez que el archivo original se ha procesado por completo, el malware cierra ese archivo y devuelve el descriptor del archivo temporal a la persona que llama. Esencialmente, esto le da al proceso de llamada un resultado borrado, que excluye todas las entradas de las conexiones de red que el malware quiere ocultar.
El segundo método que utiliza el Simbionte para ocultar su actividad en la red es secuestrar cualquier código de bytes de filtrado de paquetes inyectado. El kernel de Linux utiliza el filtro de paquetes Berkeley extendido (eBFP) para permitir el filtrado de paquetes según las reglas proporcionadas por un proceso de usuario. La regla de filtrado se proporciona como código de bytes eBPF que el kernel ejecuta en una máquina virtual (VM). Esto minimiza el cambio de contexto entre el kernel y el espacio de usuario, lo que aumenta el rendimiento ya que el kernel realiza el filtrado directamente.
Si una aplicación en la máquina infectada intenta filtrar paquetes con eBPF, Simbionte secuestra el proceso de filtrado. Primero, engancha la función lib setsockopt. Si se llama a la función con la opción SO_ATTACH_FILTER, que se usa para realizar el filtrado de paquetes en un socket, antepone su propio código de bytes antes del código eBPF proporcionado por la aplicación que llama.
El fragmento de código 1 muestra una versión anotada del código de bytes inyectado por una de las muestras de Simbionte. El código de bytes «cae» si cumplen las siguientes condiciones:
- IPv6 (TCP o SCTP) y puerto src (43253 o 43753 o 63424 o 26424)
- IPv6 (TCP o SCTP) y puerto dst 43253
- IPv4 (TCP o SCTP) y puerto src (43253 o 43753 o 63424 o 26424)
- IPv4 (TCP o SCTP) y puerto dst (43253 o 43753 o 63424 o 26424)
Si bien el código de bytes solo descarta paquetes en función de los puertos, también hemos observado filtrado de tráfico en función de las direcciones IPv4. En todos los casos, el filtrado opera tanto en el tráfico entrante como en el saliente de la máquina, para ocultar ambas direcciones del tráfico. Si no se cumplen las condiciones, simplemente salta al inicio del código de bytes proporcionado por la aplicación que llama.
El código de bytes extraído de una de las muestras, como se muestra en el fragmento de código a continuación, consta de mas de 32 instrucciones. Este código no se puede inyectar en el núcleo por sí solo, porque asume que existe más código de bytes después de él. Hay algunos saltos en este código de bytes que saltan al principio del código de bytes proporcionado por el proceso de llamada. Sin el código de bytes de la persona que llama, el código de bytes inyectado saltaría fuera de los límites, lo que no está permitido por el kernel. El código de bytes como este tiene que estar escrito a mano o parcheando el código de bytes generado por el compilador. Cualquiera de las opciones sugiere que este malware fue escrito por un desarrollador experto.
; Load Ether frame type from the packet.
0x00: 0x28 0x00 0x00 0x000c ldabsh 0xc
; Jump if it’s not IPv6 (0x86DD)
0x01: 0x15 0x00 0x0b 0x86dd jeq r0, 0x86dd, +0, +0x0b (jump to 0xd)
; Load IPv6 next header into register.
0x02: 0x30 0x00 0x00 0x0014 ldabsb 0x14
; Short jump if SCTP
0x03: 0x15 0x02 0x00 0x0084 jeq r0, 0x84, +0x2 (jump to 0x6) ; SCTP
; Short jump if TCP
0x04: 0x15 0x01 0x00 0x0006 jeq r0, 0x6, +0x1 (jump to 0x6) ; TCP
; Jump to original byte code if UDP
0x05: 0x15 0x00 0x1a 0x0011 jeq r0, 0x11, +0x1a (jump to 0x20) ; UDP
; Load TCP src port into register.
0x06: 0x28 0x00 0x00 0x0036 ldabsh 0x36
; Jump to drop the packet if port 43253.
0x07: 0x15 0x17 0x00 0xa8f5 jeq r0, 0xa8f5, +0x17 (jump to 0x1f) ; 43253
; Jump to drop the packet if port 43753.
0x08: 0x15 0x16 0x00 0xaae9 jeq r0, 0xaae9, +0x16 (jump to 0x1f) ; 43753
; Jump to drop the packet if port 63424.
0x09: 0x15 0x15 0x00 0xf7c0 jeq r0, 0xf7c0, +0x15 (jump to 0x1f) ; 63424
; Jump to drop the packet if port 26424.
0x0a: 0x15 0x14 0x00 0x6738 jeq r0, 0x6738, +0x14 (jump to 0x1f) ; 26424
; Load TCP dst port into register.
0x0b: 0x28 0x00 0x00 0x0038 ldabsh 0x38
; Jump to drop packet if port 43253 else jump to 0x1c.
0x0c: 0x15 0x12 0x0f 0xa8f5 jeq r0, 0xa8f5, +0xf12 (jump to 0x1f) (jump to 0x1c) ; 43253
; Ether frame type check for IPv4 (0x0800)
0x0d: 0x15 0x00 0x12 0x0800 jeq r0, 0x800, +0x1200 (jump to 0x20)
; Load IPv4 next header field into register.
0x0e: 0x30 0x00 0x00 0x0017 ldabsb 0x17
; Short jump if SCTP.
0x0f: 0x15 0x02 0x00 0x0084 jeq r0, 0x84, +0x2 (jump to 0x12) ; SCTP
; Short jump if TCP.
0x10: 0x15 0x01 0x00 0x0006 jeq r0, 0x6, +0x1 (jump to 0x12) ; TCP
; Jump to original byte code if UDP.
0x11: 0x15 0x00 0x0e 0x0011 jeq r0, 0x11, +0xe00 (jump to 0x20) ; UDP
; Load IPv4 flag into register.
0x12: 0x28 0x00 0x00 0x0014 ldabsh 0x14
; Jump to original byte code if flags are set.
0x13: 0x45 0x0c 0x00 0x1fff jset r0, 0x1fff, +0xc (jump to 0x20)
; Load Internet Header Length into x.
0x14: 0xb1 0x00 0x00 0x000e ldxmsh 0x0e
; Load TCP src port into register.
0x15: 0x48 0x00 0x00 0x000e ldindh r0, 0xe
; Jump to drop the packet if port 43253.
0x16: 0x15 0x08 0x00 0xa8f5 jeq r0, 0xa8f5, +0x8 (jump to 0x1f) ; 43253
; Jump to drop the packet if port 43753.
0x17: 0x15 0x07 0x00 0xaae9 jeq r0, 0xaae9, +0x7 (jump to 0x1f) ; 43753
; Jump to drop the packet if port 63424.
0x18: 0x15 0x06 0x00 0xf7c0 jeq r0, 0xf7c0, +0x6 (jump to 0x1f) ; 63424
; Jump to drop the packet if port 26424.
0x19: 0x15 0x05 0x00 0x6738 jeq r0, 0x6738, +0x5 (jump to 0x1f) ; 26424
; Load TCP dst port into register.
0x1a: 0x48 0x00 0x00 0x0010 ldindh r0, 0x10
; Jump to drop the packet if port 43253.
0x1b: 0x15 0x03 0x00 0xa8f5 jeq r0, 0xa8f5, +0x3 (jump to 0x1f) ; 43253
; Jump to drop the packet if port 43753.
0x1c: 0x15 0x02 0x00 0xaae9 jeq r0, 0xaae9, +0x2 (jump to 0x1f) ; 43753
; Jump to drop the packet if port 63424.
0x1d: 0x15 0x01 0x00 0xf7c0 jeq r0, 0xf7c0, +0x1 (jump to 0x1f) ; 63424
; Jump to drop packet if true otherwise jump to original byte code.
0x1e: 0x15 0x00 0x01 0x6738 jeq r0, 0x6738, +0x100 (jump to 0x20); 26424
; Drop packet by returning 0.
0x1f: 0x06 0x00 0x00 0x0000 ret 0
0x20: // Original byte code.
El tercer método que usa Simbionte para ocultar su tráfico de red es utilizar funciones de libpcap. El malware utiliza este método para filtrar el tráfico UPD a los nombres de dominio que tiene en una lista. Lo hace utilizando las funciones pcap_loop y pcap_stats. Para cada paquete que se recibe, Simbionte verifica el payload de UPD en busca de subcadenas de los dominios que quiere filtrar. Si encuentra una coincidencia, el malware ignora el paquete e incrementa el contador. pcap_stats utiliza un contador para «corregir» el número de paquetes procesados restando al contador los paquetes realmente procesados. Si el payload de un paquete no contiene ninguna de las cadenas que tiene en su lista, se llama a la función de devolución de llamada original. Este método se usa para filtrar paquetes UPD, mientras que el método de bytecode se usa para filtrar paquetes TCP. Al usar estos tres métodos, el malware asegura que todo el tráfico esté oculto.
Objetivos del simbionte
El objetivo del malware, además de ocultar la actividad maliciosa en la máquina, es recopilar credenciales y proporcionar acceso remoto al atacante. La recolección de credenciales se realiza conectando la función de lectura de libc. Si un proceso ssh o scp llama a la función, captura las credenciales. Las credenciales primero se cifran con RC4 utilizando una clave incrustada y luego se escriben en un archivo. Por ejemplo, una de las versiones de malware escribe las credenciales capturadas en el archivo /usr/include/certbot.h.
Además de almacenar las credenciales localmente, las credenciales se extraen. Los datos están codificados en hexadecimal y fragmentados para ser exfiltrados a través de solicitudes de registro de dirección DNS (A) a un nombre de dominio controlado por el atacante. La solicitud de registro A tiene el siguiente formato:
%PACKET_NUMBER%.%MACHINE_ID%.%HEX_ENC_PAYLOAD%.%DOMAIN_NAME%
Estructura de la solicitud de DNS utilizada por Simbionte para filtrar datos.
El malware verifica si la máquina tiene un servidor de nombres configurado en /etc/resolv.conf. Si no es así, se utiliza el DNS de Google (8.8.8.8). Además de enviar la solicitud al nombre de dominio, el malware también la envía como una transmisión UDP.
El acceso remoto a la máquina infectada se logra conectando algunas funciones del módulo de autenticación conectable (PAM) de Linux. Cuando un servicio intenta usar PAM para autenticar a un usuario, el malware compara la contraseña proporcionada con una contraseña codificada. Si la contraseña proporcionada coincide, la función inyectada devuelve una respuesta de éxito. Dado que las inyecciones están en la PAM, permite que el atacante se autentique en la máquina con cualquier servicio que use PAM. Esto incluye servicios remotos como Secure Shell (SSH).
Si la contraseña ingresada no coincide con la contraseña codificada, el malware la guarda y extrae como parte de su funcionalidad de registro de teclas. Además, el malware envía una solicitud de registro DNS TXT a su dominio de comando y control (C2). El registro TXT tiene formato %MACHINEID%.%C2_DOMAIN%. Si obtiene una respuesta, el malware base64 decodifica el contenido, verifica si el contenido ha sido firmado por una clave privada ed25519 correcta, descifra el contenido con RC4 y ejecuta el script de shell en un proceso de bash generado. Esta funcionalidad puede funcionar como un método para recuperar el acceso a la máquina en caso de que el proceso normal no funcione.
Una vez que el actor de la amenaza se ha autenticado en la máquina infectada, Simbionte proporciona funcionalidad para obtener privilegios de root. Cuando el objetivo compartido se carga por primera vez, busca la variable de entorno HTTP_SETTHIS. Si la variable está configurada con contenido, el malware cambia el ID de usuario y grupo efectivo al usuario raíz y luego borra la variable antes de ejecutar el contenido a través del comando del sistema.
Este proceso requiere que el SO tenga establecido el indicador de permiso setuid. Una vez que el comando del sistema ha salido, Simbionte también sale del proceso para evitar que se ejecute el proceso original. La imagen de a continuación muestra el código ejecutado. Esto permite generar un shell raíz ejecutando HTTP_SETTHIS=»/bin/bash -p» /bin/true como cualquier usuario con un shell.

Infraestructura de red
Los nombres de dominio utilizados por el malware Simbionte se hacen pasar por algunos de los principales bancos brasileños. Esto sugiere que estos bancos o sus clientes son los objetivos potenciales. Usando los nombres de dominio utilizados por el malware, se logró descubrir una muestra relacionada que se cargó en VirusTotal con el nombre certbotx64. Este nombre de archivo coincide con uno de los enumerados como un archivo para ocultar en una de las muestras de Simbionte que obtuvimos originalmente. El archivo se identificó como una herramienta de tunelización de DNS de código abierto llamada dnscat2.
La muestra tenía una configuración en el binario que usaba el dominio git[.]bancodobrasil[.].dev como su servidor C2. Durante los meses de febrero y marzo, este nombre de dominio se resolvió en una dirección IP que está vinculada al servicio de servidor privado virtual (VPS) de Njalla. Los registros DNS pasivos mostraron que la misma dirección IP se resolvió en ns1[.]cintepol[.]link y ns2[.]cintepol[.]link unos meses antes. Cintepol es un portal de inteligencia proporcionado por la Policía Federal de Brasil. El portal permite a los policías acceder a diferentes bases de datos proporcionadas por la policía federal como parte de sus investigaciones. El servidor de nombres utilizado para este nombre de dominio suplantador estuvo activo desde mediados de diciembre de 2021 hasta finales de enero de 2022.
También a partir de febrero de 2022, los servidores de nombres para el dominio caixa[.]wf apuntaban a otra IP de Njalla VPS. La imagen de a continuación muestra una línea de tiempo de estos eventos. Además de la infraestructura de red, se incluyen las marcas de tiempo de cuándo se enviaron los archivos a VirusTotal. Estas tres muestras de Simbionte fueron cargadas por el mismo remitente de Brasil. Parece que los archivos se enviaron a VirusTotal antes de que la infraestructura se conectara.
Dado que estos archivos se enviaron a VirusTotal antes de que la infraestructura se conectara, y debido a que algunas de las muestras incluían reglas para ocultar las direcciones IP locales, es posible que las muestras se enviaran a VirusTotal para probar la detección de antivirus antes de usarlas. Además, a finales de noviembre se envió desde Brasil una versión que parece estar en desarrollo, lo que sugiere que el actor de amenazas o el grupo detrás de Simbionte estaba utilizando VirusTotal para las pruebas de detección.

Similitud con otro malware
Simbionte parece estar diseñado tanto para el robo de credenciales como para proporcionar acceso remoto a servidores Linux infectados. Simbionte no es el primer malware de Linux desarrollado para este objetivo. En 2014, ESET publicó un análisis en profundidad de Ebury, un backdoor de OpenSSH que también roba credenciales. Existen algunas similitudes en las técnicas utilizadas por ambas familias de malware. Ambos usan funciones enlazadas para capturar credenciales y exfiltrar los datos capturados como solicitudes de DNS. Sin embargo, el método de autenticación de la puerta trasera que utilizan las dos familias de malware es diferente. Cuando analizamos las muestras por primera vez con Intezer Analyze, solo se detectó un código único (Imagen a continuación). Como Simbionte y Ebury/Windigo o cualquier otro malware conocido no comparten ningún código, podemos concluir con seguridad que Simbionte es un nuevo malware de Linux no descubierto.

Conclusión
Simbiontes es un malware altamente evasivo. Su objetivo principal es capturar credenciales y facilitar el acceso por la puerta trasera a las máquinas infectadas. Dado que el malware funciona como un rootkit a nivel de usuario, puede ser difícil detectar una infección. La telemetría de red se puede usar para detectar solicitudes de DNS anómalas y las herramientas de seguridad, como antivirus (AV) y detección y respuesta de punto final (EDR), deben vincularse estáticamente para garantizare que no estén «infectados» por rootkits de usuarios.
Indicadores de compromiso (IoC)
Hashes
Hash | Notes |
121157e0fcb728eb8a23b55457e89d45d76aa3b7d01d3d49105890a00662c924 | “kerneldev.so.bkp.” Appears to be an early development build. |
f55af21f69a183fb8550ac60f392b05df14aa01d7ffe9f28bc48a118dc110b4c | “mt64_.so.” Missing credential exfiltration over DNS. |
ec67bbdf55d3679fca72d3c814186ff4646dd779a862999c82c6faa8e6615180 | “search.so.” First sample with credential exfiltration of DNS. |
a0cd554c35dee3fed3d1607dc18debd1296faaee29b5bd77ff83ab6956a6f9d6 | “liblinux.so.” |
45eacba032367db7f3b031e5d9df10b30d01664f24da6847322f6af1fd8e7f01 | “certbotx64.” dnscat2 |
Puertos ocultos
- 45345
- 34535
- 64543
- 24645
- 47623
- 62537
- 43253
- 43753
- 63424
- 26424
Dominios ocultos
- assets[.]fans
- caixa[.]cx
- dpf[.]fm
- bancodobrasil[.]dev
- cctdcapllx0520
- cctdcapllx0520[.]df[.]caixa
- webfirewall[.]caixa[.]wf
- caixa[.]wf
Nombres de procesos ocultos
- javaserverx64
- javaclientex64
- javanodex86
- apache2start
- apache2stop
- [watchdog/0]
- certbotx64
- certbotx86
- javautils
Nombres de archivo ocultos
- apache2start
- apache2stop
- profiles.php
- 404erro.php
- javaserverx64
- javaclientex64
- javanodex86
- liblinux.so
- java.h
- open.h
- mpt86.h
- sqlsearch.php
- indexq.php
- mt64.so
- certbot.h
- cert.h
- certbotx64
- certbotx86
- javautils
- search.so
Dominios de exfiltración de credenciales
- *.x3206.caixa.cx
- *.dev21.bancodobrasil.dev