Únete al equipo de Lobera

Consulta nuestra Política de Privacidad sobre el tratamiento de esta información.

Contacta con el equipo de Lobera

Consulta nuestra Política de Privacidad sobre el tratamiento de esta información.

↖ Blog / ReversingLobera · Investigación de amenazasAnálisis / 02

SciSvc.Anatomía de un
backdoor de Windows.

Un implante de 52 KiB descubierto en una institución sanitaria. Capacidades de control y pivote, un protocolo propio y 341 bytes residuales en su registro de laboratorio.

SciSvc: del servicio al registro de red Una DLL de 52 KiB bajo svchost contiene tres módulos y envía un registro de 566 bytes. Dos colas de texto suman 341 bytes residuales. 01 / PERSISTENCIALocalSystem 02 / CONTROL3 módulos 03 / REGISTRO0x236 bytes SciSvc52 KiB / x86svchost.exe — netsvcsqwsxche32.dll PROCESOSFICHEROSRELÉ TCP NUL →341 B / DESPUÉS DEL TEXTOFIG. 00 / ESQUEMA

INVESTIGACIÓNEquipo de análisis · Lobera

31 min de lectura

DISCIPLINASREVERSINGDFIRDETECCIÓN

Contenido / 13 secciones

Qué encontramos en la investigación.

Durante una investigación en una institución del sector sanitario, con actividad maliciosa confirmada desde 2020, encontramos una DLL cargada por un servicio de Windows. El análisis reveló un implante de 52 KiB con ejecución de procesos, transferencia de ficheros y relé TCP.

El servicio se llamaba SciSvc y aparecía como Indexes Service. Apuntaba a qwsxche32.dll en el directorio dllcache, arrancaba automáticamente y utilizaba la cuenta LocalSystem dentro de un grupo de svchost.exe. Para comprobar qué hacía, seguimos la referencia ServiceDll hasta el fichero y revisamos su código. La función aparente de indexación ocultaba capacidades de control remoto sobre el equipo.

Reconstruimos la configuración, la tabla de módulos y los dos niveles de su protocolo binario. Después contrastamos el análisis estático con una captura de laboratorio aislado y con la telemetría de red disponible del incidente. Cada fuente permite comprobar una parte: las capacidades del programa, el intercambio de registro y los intentos de conexión observados en producción.

01

Persistencia bajo un servicio

DLL x86 de 52 KiB; SciSvc automático, LocalSystem y una referencia ServiceDll dentro de dllcache.

02

Control remoto integrado

Tres módulos: proceso interactivo, gestión de ficheros y relé TCP. Capacidades reconstruidas desde sus rutas de código.

03

Registro comprobado en laboratorio

Una petición de 94 bytes y dos fichas de 660 validan los dos niveles del protocolo en una derivada aislada.

04

Memoria residual en el registro

341 bytes después de dos terminadores NUL; 291 posiciones cambian entre las dos fichas conservadas.

Usamos SciSvc como nombre operativo del implante analizado, tomado del servicio.

Al comparar dos registros consecutivos de laboratorio observamos cambios en cientos de bytes situados después del texto útil. El constructor rellenaba campos concretos de una estructura de pila y enviaba sus 566 bytes completos sin inicializarla por entero. Dos colas de campos de texto aportaban 341 bytes residuales por ficha. El análisis de esos campos se recoge en «Después del NUL».

Un punto de apoyo en la red.

La actividad maliciosa del entorno se remonta a 2020, según la cronología confirmada por el equipo investigador. La identidad de la institución se mantiene reservada.

La adquisición de 2026 recuperó la DLL y la configuración del servicio, que se encontraba activo. A partir de esos artefactos reconstruimos sus capacidades y validamos el registro del implante en laboratorio.

Control del host y capacidad de pivote

La combinación de módulos concede al operador un conjunto amplio de acciones sobre el equipo: crear procesos y manejar su entrada y salida, recibir y ejecutar una carga, leer o escribir ficheros y abrir conexiones a otro destino TCP. En la implantación adquirida, el servicio utiliza LocalSystem, un contexto local privilegiado. El acceso a recursos de dominio depende además de la identidad de red y de los permisos efectivos.

Un host con estas capacidades puede actuar como punto de apoyo para operaciones posteriores. La ejecución de procesos permite usar herramientas disponibles o transferidas; el relé ofrece un camino hacia servicios alcanzables desde el host; y el gestor de ficheros proporciona un mecanismo de entrada y salida de datos. Un operador puede combinar esas funciones para intentar avanzar por la red. La segmentación, las credenciales disponibles y los controles del destino determinan su alcance.

Capacidades y alcance del accesoMODELO DE RIESGO
CAPACIDAD DEL BINARIO

Ejecución con entrada y salida.

El módulo de procesos y la ruta de carga adicional permiten operar sobre el equipo en el contexto del servicio.

CONDICIÓN / ALCANCE

El servicio opera con privilegios locales de LocalSystem. El acceso a recursos de dominio depende de la identidad de red y de los permisos efectivos.

Canal y funciones reconstruidosPosible uso posterior, sujeto a acceso
FIG. 01 · Capacidades reconstruidas del binario y su posible uso como punto de apoyo para controlar el host, acceder a otros sistemas y transferir datos.

La misma arquitectura ofrece una vía potencial de exfiltración. El manejador de lectura abre un fichero local y entrega bloques al canal remoto. Si el adversario puede acceder a información en ese host, o llevarla hasta él, esa función puede servir para extraerla a través de la sesión exterior.

Equipos fuera de la cobertura del EDR

Un sistema sin agente EDR, con un agente que ha dejado de reportar o fuera del alcance de una política efectiva puede ofrecer más margen a un implante de este tipo. El defensor pierde parte del vínculo entre servicio, módulo cargado, procesos descendientes y sockets. Desde otros segmentos puede observar solo conexiones que parten de una dirección interna aparentemente conocida.

El camuflaje administrativo y la persistencia como servicio facilitan que el implante pase desapercibido en una revisión superficial. Contrastar inventario de agentes, estado de comunicación y telemetría de red ayuda a localizar equipos con esa visibilidad reducida.

La supervisión del tráfico entre subredes, los intentos de salida y los registros de autenticación complementan la adquisición del endpoint. Permiten seguir actividad que puede originarse fuera de la cobertura del EDR. El SOC necesita relacionar origen interno, servicio de destino y canal exterior para reconstruir ese recorrido.

Afinidad de TTP con actores vinculados a China

El uso de servicios Windows camuflados, puertas traseras personalizadas y herramientas de proxy tiene precedentes en operaciones atribuidas a APT41. MITRE ATT&CK recoge esas técnicas para el grupo.

La dimensión sanitaria y temporal aporta contexto adicional. Mandiant documentó una campaña global de APT41 entre enero y marzo de 2020, con objetivos de varios sectores, incluidos salud y farmacéutico.

En una operación distinta, Microsoft describió el uso de equipos comprometidos y proxies por Volt Typhoon para mantener accesos discretos y ocultar el origen de su tráfico. Esa investigación aporta otro precedente del uso de puntos de apoyo de red.

La afinidad de TTP con operaciones de actores vinculados a China sitúa a APT41 entre las hipótesis de inteligencia de esta investigación.

La DLL detrás de svchost.

La identidad de la muestra se fija por su SHA-256. qwsxche32.dll mide 53.248 bytes y es una DLL PE32 para x86, con cinco secciones y base de imagen 0x10000000. Exporta Init en la RVA 0x50f0 y ServiceMain en 0x5180. El nombre de exportación es tpdll.dll, mientras que el recurso de versión utiliza variantes de tqdll.

E01 / IDENTIDAD DE LA MUESTRAORIGINAL · ANÁLISIS ESTÁTICO
qwsxche32.dll   PE32 / DLL / x86 / 53.248 B
ImageBase      0x10000000
Entry RVA      0x7143
Exports        Init · ServiceMain

SHA-256
c982651f1b8bcbe02f7469c8fc89970697cfc684a9df783d4cfa9175fe912e4f
↳

Los offsets y las direcciones del artículo corresponden a esta muestra. El hash de laboratorio se documenta aparte.

El perfil del enlazador y la dependencia de MFC42 son compatibles con una base de código construida con herramientas antiguas. La tabla de importaciones contiene 141 funciones de ocho DLL: WS2_32, MFC42, MSVCRT, KERNEL32, USER32, ADVAPI32, SHELL32 y SETUPAPI. Hay soporte de sockets, servicios, ficheros y enumeración de dispositivos. El módulo de procesos resuelve además varias funciones dinámicamente.

No hay directorio Security de Authenticode ni overlay después de la última sección. El PE tampoco declara DYNAMIC_BASE, NX_COMPAT, NO_SEH o GUARD_CF.

Una persistencia que se entiende siguiendo la referencia a la DLL

En el equipo con confirmación forense, el servicio era automático y ejecutaba svchost.exe -k netsvcs como LocalSystem. El valor ServiceDll conducía a C:\Windows\System32\dllcache\qwsxche32.dll. El ejecutable del proceso anfitrión podía ser legítimo y, aun así, cargar código malicioso. Revisar únicamente la firma de svchost.exe habría dejado fuera el artefacto relevante.

Persistencia / del nombre al ficheroEvidencia de host
01 / SERVICIO
SciSvcIndexes Service · inicio automático · LocalSystem
02 / ANFITRIÓN
svchost.exe-k netsvcs
03 / SERVICEDLL
qwsxche32.dllC:\Windows\System32\dllcache\

La configuración del servicio conduce al módulo que hay que adquirir. El nombre del proceso anfitrión no identifica el código que carga.

FIG. 02 · Recorrido de persistencia confirmado en Host A. Identificadores del entorno omitidos.

La combinación importante para hunting es servicio → configuración → módulo cargado → identidad del fichero. El nombre visible de indexación aporta contexto de camuflaje, pero una búsqueda por la palabra “Indexes” tendría poco valor sin el resto. El mecanismo encaja con persistencia mediante servicios de Windows, descrita en MITRE ATT&CK T1543.003.

El timestamp COFF declara el 1 de noviembre de 2017; el recurso de versión incluye un copyright de 2016 y la versión 3.0.0.1. El servicio se encontraba en ejecución en la primera comprobación. En revisiones posteriores aparecía detenido y conservaba el arranque automático, por lo que la configuración de persistencia seguía presente.

Configuración y tiempos de conexión.

La configuración está dentro de la DLL, en el offset de fichero 0xa2c0, y ocupa 0x420 bytes: 1.056 en decimal. La transformación es un XOR repetitivo con el entero de 32 bits 0x11223344. En orden de bytes little-endian, la secuencia aplicada es 44 33 22 11. Confundir la escritura del entero con el orden de los bytes produce una extracción incorrecta.

El bloque descifrado fija el destino TCP, el perfil, el servicio y el modo de conexión. El SHA-256 de sus 1.056 bytes es c05b43ba7c639bebe6a73bbe443015d6fc2a1471d6c0ee1a8673858e38f3d945. Esta comprobación permite distinguir una configuración íntegra de una interpretación que solo ha encontrado algunas cadenas legibles.

Offset del bloque Campo recuperado Interpretación
+0x10 8020 Puerto TCP, entero little-endian de dos bytes.
+0x12 default Perfil del implante.
+0x32 181[.]176[.]160[.]172 Destino C2 configurado en esta muestra.
+0x148 6 Unidades de reintento; el código multiplica por 60.000 ms.
+0x14c SciSvc Nombre del servicio.
+0x18c Indexes Service Nombre visible.
+0x20c www.microsoft.com,www.google.com Sondas de conectividad; no se clasifican como C2.
+0x312 0 Selección de conexión inversa.
+0x319 C:\WINDOWS\Temp\WSSD.tmp Ruta temporal embebida en la configuración.

El siguiente fragmento extrae la configuración de una copia local y verifica identidad, rango y resultado. Lee el fichero como datos, sin cargar la DLL ni realizar conexiones. Los offsets de esta operación son posiciones dentro del fichero.

# E02 · Extracción offline de la configuración de la muestra original.
from hashlib import sha256
from pathlib import Path

sample = Path("qwsxche32.dll").read_bytes()
assert sha256(sample).hexdigest() == (
    "c982651f1b8bcbe02f7469c8fc89970697cfc684a9df783d4cfa9175fe912e4f"
)
block = sample[0xa2c0:0xa2c0 + 0x420]
assert len(block) == 0x420
key = bytes.fromhex("44 33 22 11")
config = bytes(value ^ key[i % 4] for i, value in enumerate(block))
assert sha256(config).hexdigest() == (
    "c05b43ba7c639bebe6a73bbe443015d6fc2a1471d6c0ee1a8673858e38f3d945"
)
assert int.from_bytes(config[0x10:0x12], "little") == 8020
assert config[0x12:0x1a] == b"default\x00"
assert config[0x312] == 0

Reintento, keepalive y espera tras error

La ruta activa resuelve el destino, prepara un socket no bloqueante y llama a WSAConnect; la llamada está anclada en 0x1000615c. Espera hasta seis segundos para conectar y configura SO_KEEPALIVE, un timeout de envío de tres segundos y otro de recepción de ocho. Dentro de la sesión, el bucle usa select con una espera de veinte segundos.

Hay tres temporizadores con funciones distintas. El reintento configurado es de 6 × 60.000 = 360.000 ms. El keepalive de aplicación utiliza 180.000 ms, visible en 0x10006677. La ruta de error de parser o conexión aplica una espera de 300.000 ms en 0x10006712.

Estos valores ayudan a interpretar el firewall. Una cadencia de unos 366 segundos es compatible con los seis minutos configurados más la espera de conexión. Una búsqueda por periodicidad debe admitir esa variación.

El código conserva un modo alternativo con bind y listen, cuyas llamadas están en 0x10005c9b y 0x10005cac. La configuración recuperada selecciona la conexión inversa; la rama de escucha queda como capacidad alternativa.

Módulos y despacho de mensajes.

La tabla de módulos empieza en el offset de fichero 0x8598. Tiene espacio para 32 entradas, pero solo tres están ocupadas. Cada entrada enlaza un identificador con funciones de creación, despacho y destrucción. Esa organización permite al núcleo abrir workers y entregarles mensajes sin que cada módulo tenga que implementar la sesión completa.

TABLA FO 0x8598 / ENTRADA 1 DE 3

Proceso interactivo

Entrada y salida mediante tuberías

El dispatcher del módulo controla la creación del proceso, la entrega de entrada, la recuperación de salida y su terminación. Resuelve CreateProcessW y CreatePipe dinámicamente.

FACTORY 0x10001000
DISPATCH 0x10001300 · VTABLE 0x10008258
ENTRADA: 0x7d1 · 0x7d2 · 0x7d4 / SALIDA: 0x7d3
CAPACIDAD ESTÁTICA
FIG. 03 · Tres entradas no nulas de una tabla con capacidad para 32. Selecciona un módulo para consultar su ruta.

El dispatcher principal reserva 1/1 para el registro del equipo. Los mensajes 5/1, 5/2 y 5/3 crean un worker, detienen uno o terminan el conjunto. La entrega ordinaria a los módulos utiliza una cola de hilo: la publicación mediante PostThreadMessageA del mensaje 0x404 está en 0x10004963. 6/1 crea el canal del relé TCP; la referencia al identificador 0x60020400 aparece en 0x100048d2. Las funciones puente compartidas enlazan módulos integrados en esta misma DLL.

Proceso interactivo y carga adicional

El dispatcher del módulo 0x60020300 acepta 0x7d1 para crear el proceso, 0x7d2 para escribir su entrada y 0x7d4 para terminarlo. El worker lee la tubería de salida y la devuelve en mensajes 0x7d3; también hay mensajes de página de códigos (0x7d0) y notificación de terminación (0x7d5). Los identificadores entrantes y salientes describen sentidos distintos del intercambio.

El módulo construye y resuelve dinámicamente nombres de API como CreateProcessW y CreatePipe. La reconstrucción de CreateProcessW comienza en 0x100013fb y su llamada indirecta está en 0x10001547. El handler de entrada llega a WriteFile en 0x100015ec; en el sentido opuesto, el worker consulta la tubería con PeekNamedPipe en 0x10001171, lee con ReadFile en 0x10001199 y entrega el contenido al constructor de mensajes en 0x100011dc. Esta cadena confirma entrada y salida, más allá de la presencia de nombres de API.

La redirección mediante tuberías proporciona control interactivo: el operador puede enviar entrada al proceso creado y recibir su salida a través del canal del implante.

Hay, además, una ruta de descarga y ejecución en el núcleo, separada del módulo interactivo. El mensaje principal 2/2 toma contenido recibido, lo escribe en %TEMP%\~sndf23.tmp y llama a CreateProcessA. La secuencia de referencias enlaza la selección del mensaje con el nombre temporal, la creación, la escritura y la ejecución.

E03 · Ruta de carga adicional · VA con ImageBase 0x10000000

0x10004703   selección del mensaje principal tipo 2
0x1000473f   referencia al nombre ~sndf23.tmp
0x10004784   CreateFileA
0x100047a6   WriteFile
0x10004818   CreateProcessA

Evidencia: secuencia estática enlazada en la DLL original.
Capacidad: recibir, escribir y ejecutar una carga adicional.

Ficheros, relé y autolimpieza

El gestor 0x60020200 tiene cuatro grupos de mensajes, de 0x3e8 a 0x3eb: inventario, lectura hacia el canal remoto, escritura desde el canal y operaciones de creación o borrado. La lectura de ficheros usa GENERIC_READ y fragmentos de hasta 0xfc00 bytes —64.512—. En sentido contrario aparecen la apertura o creación en 0x10002fe3 y la escritura en 0x1000304a.

Dentro de la rutina 0x10003410, el bucle iniciado en 0x1000344d recorre valores de 0 a 56 con nueve exclusiones y llama a SHGetSpecialFolderPathW en 0x10003492. La instrucción de 0x10003454 pertenece a las comparaciones de ese recorrido. Los valores enumerados son identificadores CSIDL de carpetas especiales, como define la documentación de Microsoft. El bucle forma parte del inventario de filesystem.

El módulo 0x60020400 implementa un relé TCP genérico: crea conexiones y transporta datos entre el canal de control y otro extremo TCP. La conexión alcanza WSAConnect en 0x10001d19; el handler de envío llama a send en 0x10001bb5. En sentido contrario, el worker llama a recv en 0x10001849 y entrega los datos al constructor del canal desde 0x100018ae.

La orden principal 3 señaliza el evento global de parada o limpieza, identificado por un GUID embebido. La autolimpieza comienza en 0x10003b10: prepara direcciones de funciones como FreeLibrary, DeleteFileA, MoveFileA y ExitThread, reserva memoria ejecutable y copia un pequeño stub para continuar después de descargar la DLL. La reserva está anclada en 0x10003c29. La rutina descarga el módulo y borra o mueve su fichero.

74 + 20. La anatomía del canal.

SciSvc utiliza TCP con un framing propio de dos niveles. La cabecera exterior tiene 74 bytes (0x4a) y transporta una cabecera interior de veinte (0x14) seguida del contenido. Las longitudes son little-endian. Separar ambos espacios de offsets es imprescindible: +0x10 significa longitud de contenido solo cuando el origen es la cabecera interior.

El parser exterior comprueba 0x2d en +0x17 y 0x1d en +0x39. Lee la longitud del payload en +0x46 y localiza su comienzo en +0x4a. En la muestra original, esas operaciones dejan tres anclas cortas y verificables: 80 7e 17 2d en 0x10004462, 80 7e 39 1d en 0x10004468 y 8b 4e 46 en 0x1000446e.

INSPECTOR / PROTOCOLO SCISVC74 + 20 + CONTENIDO
ESTRUCTURAS OBSERVADAS
LABORATORIO AISLADO
74EXTERIOR+20INTERIOR+566CONTENIDO=660BYTES
ORIGEN: TRAMA EXTERIOR

Dos marcadores. Una longitud.

El parser exige 0x2d en +0x17 y 0x1d en +0x39. En +0x46 lee los 586 bytes que siguen a la cabecera exterior.

+0x46 / UINT32 LE4a 02 00 00586 bytes
Consultar posiciones absolutas y relativas
CampoEn la tramaEn su cabecera
Marcadores0x17 / 0x39+0x17 / +0x39 exterior
Longitud exterior0x46+0x46 exterior
Tipo / subtipo0x4a / 0x4e+0x00 / +0x04 interior
Longitud de contenido0x5a+0x10 interior
Contenido0x5e+0x14 interior

Campos estructurales comprobados. Identificadores de sesión omitidos. Esquema no a escala.

FIG. 04 · Cambia entre petición y registro; selecciona una capa para examinar su longitud y su origen de offsets.

Además de los marcadores, la cabecera exterior incluye dos campos de cuatro bytes derivados de rand(), un identificador de sesión o canal, un campo de control y dos campos de contexto. El constructor comienza en 0x10004ab0; la preparación de argumentos de WS2_32.send está anclada en 0x10004bcf y la instrucción de llamada, en 0x10004bd4.

Origen del offset Posición Tamaño Uso observado
Exterior +0x17 / +0x39 1 / 1 B Marcadores 2d y 1d.
Exterior +0x1a 8 B Identificador de sesión o canal.
Exterior +0x3a 4 B Campo de control; valor 1 en el constructor.
Exterior +0x3e / +0x42 4 / 4 B Campos de contexto o canal.
Exterior +0x46 4 B Longitud de lo que sigue a los 74 bytes.
Interior +0x00 / +0x04 4 / 4 B Tipo y subtipo.
Interior +0x08 / +0x0c 4 / 4 B Campos de petición o canal.
Interior +0x10 4 B Longitud del contenido posterior a los veinte bytes.

Reensamblar TCP antes de interpretar los mensajes

El parser espera al menos 0x4b bytes antes de comenzar la validación exterior y comprueba después que dispone de 74 + longitud bytes. Conserva datos parciales entre lecturas. Ese detalle importa tanto para un analizador forense como para una firma de red: una llamada a recv o un segmento capturado no tiene por qué coincidir con una trama del implante.

La validación independiente se hizo sobre los dos flujos TCP reensamblados. Una petición de registro sin contenido ocupa 74 + 20 = 94 bytes. Cada registro observado ocupa 74 + 20 + 566 = 660. En este último, el payload exterior declara 586 bytes (0x24a), mientras que el contenido interior declara 566 (0x236). Si ambas longitudes se tratan como equivalentes, el parser termina veinte bytes antes de donde debe.

E04 · Registro 1/1 observado · Los valores son little-endian

Exterior +0x46   4a 02 00 00   → 586 bytes de payload
Interior +0x00  01 00 00 00   → tipo 1
Interior +0x04  01 00 00 00   → subtipo 1
Interior +0x10  36 02 00 00   → 566 bytes de registro

74 + 586 = 660             20 + 566 = 586

La ruta reconstruida copia el contenido a la trama y lo entrega a send sin una transformación de cifrado o compresión intermedia. La captura confirma que el registro circula en claro dentro de ese formato. El XOR descrito en el apartado de configuración se aplica al bloque embebido en la DLL.

Tres tramas para comprobar el modelo.

La validación dinámica utilizó una derivada de laboratorio. Solo se modificaron once bytes cifrados, todos dentro del campo C2, para dirigir la conexión a un sumidero en una red host-only. Se mantuvieron el tamaño del fichero, el puerto y el resto de los bytes. La DLL original del incidente no se ejecutó en esa prueba.

La máquina virtual tenía una única interfaz host-only y ninguna ruta por defecto. NAT y bridge estaban deshabilitados. El sumidero escuchaba únicamente en esa red. La captura no contiene extremos unicast globales. Estas condiciones permitieron observar el intercambio de registro sin contactar con la infraestructura configurada en la muestra original.

El experimento se centró en el registro del equipo. Tras establecer la sesión TCP, el sumidero transmitió una petición tipo 1 / subtipo 1, con contenido vacío, de 94 bytes. Recibió 1.320 bytes: dos tramas de registro de 660 bytes cada una.

Validación / intercambio mínimoOrden de red
DERIVADA AISLADASUMIDERO
TCP establecido
Petición 1/1 · 94 B
Registro 1 · 660 B
Registro 2 · 660 B
Cierre TCP normal

UNA ÚNICA PETICIÓNEl sumidero envió una petición de registro 1/1 con contenido vacío.

DOS FICHASLa etiqueta temporal de la primera ficha es anterior a la petición; la de la segunda, posterior. Es compatible con un registro inicial y otro generado al procesar 1/1.

FIG. 05 · Intercambio de registro en el laboratorio aislado, reconstruido en orden de red.

La captura completa conserva 2.520 paquetes a lo largo de 1.648,854393 segundos de tiempo relativo. Solo trece paquetes pertenecen al flujo TCP/8020 de interés. El reensamblado no presentó huecos ni solapamientos conflictivos. Un parser offline propio y una segunda extracción con tshark produjeron los mismos bytes en ambos sentidos.

E05 · Integridad de la captura y de los flujos reensamblados

PCAP de laboratorio · SHA-256
7e6ac4c7cd0c72a41a7e49be7ea7ffab6382d397b8298514ecf902c492245eaa

Cliente → sumidero · 1.320 bytes · SHA-256
b23b92acc680d0f2f5741903b47c25f41fd726f27b66b727b85c6c02bdba71a4

Sumidero → cliente · 94 bytes · SHA-256
21e834e1e35718f45014fa3df28ece0bdee68e262ee4e3958dcac7e3d97d7566

Orden de envío y marcas temporales

En el orden de red, la petición precede a ambas tramas del implante. La hora incrustada en la primera ficha corresponde a un instante anterior a la petición; la segunda es posterior. El comportamiento es compatible con un registro inicial ya construido y otro generado al procesar 1/1.

Los tiempos de pared se obtuvieron del sumidero y del cargador. Los timestamps del PCAP de VirtualBox, relativos o próximos al origen epoch, permiten reconstruir el orden y los intervalos del experimento.

Ambas fichas contienen perfil, versión del implante, servicio, hora, IPv4, nombre de equipo, MAC y versión del sistema. El código obtiene la MAC permanente a través de SetupAPI, NDIS y DeviceIoControl: añade una identidad de adaptador a los datos de dirección del host. La versión incluida en la etiqueta del protocolo es V3.0.0.0; difiere de 3.0.0.1, presente en el recurso de versión del PE. Se conservan como dos observaciones distintas. La prueba confirma el registro y su estructura.

Después del NUL.

Un terminador NUL indica dónde termina una cadena. No borra los bytes que quedan detrás. SciSvc serializa buffers de tamaño fijo como parte de su registro y termina enviando también ese espacio restante.

El primer campo es una etiqueta de 260 bytes. En las dos fichas observadas, su texto acaba en el offset 0x2d; el NUL ocupa esa posición y los bytes de 0x2e a 0x103, ambos incluidos, también viajan por el socket. Son 214 bytes posteriores al terminador. El primer registro contiene 143 bytes no nulos en esa zona; el segundo, 128. Entre ambos cambian 182 posiciones.

La segunda región está en OSVERSIONINFOEXA, que empieza en +0x18a del registro. Su miembro szCSDVersion tiene 128 bytes y comienza veinte bytes después del inicio de esa estructura. En este invitado, la cadena está vacía: el primer byte ya es NUL. Aun así, se transmiten los 127 siguientes, de +0x19f a +0x21d del registro. Contienen 68 y 101 bytes no nulos, con 109 posiciones distintas entre fichas. La definición del miembro y el significado de cadena vacía están documentados por Microsoft.

Lobera / Después del NUL566 posiciones · 2 fichas
DOS COLAS DE BUFFER
341 B

de memoria residual por registro

291posiciones residuales cambian
entre las dos fichas
+0x00032 BYTES / FILA+0x235
Residuo que cambiaResiduo estableResto del registro
RANGO / REGISTRO0x02e–0x103 + 0x19f–0x21d
BYTES RESIDUALES214 + 127 = 341
POSICIONES DISTINTAS182 + 109 = 291

El mapa conserva las posiciones y diferencias reales; no publica valores residuales ni identidades. Un cambio adicional, fuera de estas dos regiones, corresponde al segundo de la etiqueta.

FIG. 06 · Comparación derivada de los dos registros reensamblados. Rango inclusivo. El color señala las posiciones que cambian entre registros.

En total, 214 + 127 = 341 bytes por ficha pertenecen a esas dos colas de buffers. La cifra mide el espacio transmitido después de los terminadores. De esas posiciones, 291 cambian entre las dos fichas. El registro completo presenta 292 cambios: el adicional corresponde al segundo de la hora textual. Las cabeceras interiores permanecen idénticas; en las exteriores cambian ocho bytes, agrupados en los dos campos derivados de rand().

Lo que aporta el desensamblado

El constructor en 0x100062a0–0x100064ce reserva espacio en la pila, rellena campos individuales y envía la estructura completa. La instrucción en 0x100064a0, 68 36 02 00 00, empuja la longitud 0x236. No hay una limpieza previa de toda la estructura que convierta automáticamente las colas de los campos en ceros.

E06 · Pseudocódigo elaborado a partir del desensamblado

reservar registro de 0x236 bytes en la pila
rellenar etiqueta y campos de identidad
rellenar OSVERSIONINFOEXA
entregar los 0x236 bytes al constructor del mensaje

Consecuencia observada:
se incluyen los bytes posteriores al NUL de dos buffers fijos.

La combinación de estructura de pila, inicialización parcial y diferencias entre registros sustenta la interpretación de probable memoria de pila no inicializada. Las regiones se identifican por sus campos, límites y recorrido hasta la función de envío.

Desde el punto de vista defensivo, esta región permite comprobar los límites de los campos y validar el reensamblado. Para una firma, las longitudes, los marcadores y la semántica del registro ofrecen referencias más estables que los valores residuales.

Cómo interpretamos los registros de red.

El export de firewall contiene 705 filas hacia el destino configurado, procedentes de tres dispositivos. Varias corresponden a un mismo ciclo de conexión observado en distintos puntos de la red. Para reconstruir la actividad agrupamos los registros por origen, destino, puertos y tiempo.

La normalización identifica 141 ciclos por cada uno de dos orígenes, denominados Host A y Host B. El implante está confirmado en A mediante servicio y hash de la DLL; B presenta actividad de red altamente compatible y se clasifica como probable.

Producción / normalización de eventosVENTANA ≈ 14 H 23 MIN
705FILAS EN TRES FIREWALLS
HOST A / CONFIRMADO141 ciclos
HOST B / PROBABLE141 ciclos
366,01 sMEDIANA ENTRE CICLOS · A
96 BENVIADOS POR TIMEOUT · A
0 BRETORNO REGISTRADO · A

Los eventos de los tres firewalls se agrupan por origen, destino, puertos y proximidad temporal.

FIG. 07 · Intentos de conexión del export de firewall, normalizados en 141 ciclos por origen.

En Host A hay 423 filas: 282 denegaciones y 141 timeouts. Cada timeout registra dos paquetes y 96 bytes enviados, con cero paquetes y cero bytes de respuesta. La suma de esas observaciones es de 282 paquetes y 13.536 bytes salientes. Host B aporta 282 denegaciones con cero bytes. Estos contadores describen los intentos registrados por el firewall.

La ventana cubre aproximadamente catorce horas y veintitrés minutos. En Host A, la mediana entre ciclos es 366,010 segundos. El valor es compatible con la espera de seis minutos recuperada de la configuración y el timeout de conexión. La combinación de destino, puerto y cadencia relaciona el tráfico con la configuración del implante.

Los identificadores SMB y las características de las conexiones permiten distinguir ambos equipos aunque parte de la exposición de nombres coincida. Esa correlación amplió el hunting a un segundo origen y permitió seguir sus ciclos de reintento por separado.

Detección con YARA y Suricata.

Las detecciones descargables persiguen dos objetivos: identificar el artefacto conservado y reconocer características del protocolo observado. Las reglas combinan identidad, estructura y contexto. Cada una incluye su alcance y los resultados de validación.

VALIDACIÓN OFFLINE / 24 OCT 2026RESULTADOS MEDIDOS
YARA 4.5.01 / 288

La DLL original fue el único objeto reconocido por ambas reglas en el corpus mixto de 288 objetos.

SURICATA 7.0.330 / 30

Once controles positivos y diecinueve negativos. Además, ambas firmas activan en la captura preservada.

Compilación correcta · replay offline · segmentación TCP · coalescencia · dirección · HOME_NET

SIDs locales 9902401–9902402. Ajustar HOME_NET y comprobar colisiones antes de incorporar las firmas al sensor.

Leer las reglas YARA completas
import "hash"

/*
 * Lobera Research — SciSvc / qwsxche32.dll
 * Publication: 2026-10-24. Read-only file triage; these rules do not execute files.
 * SciSvc is an operational label for the analysed implant, not an actor attribution.
 * The exact rule identifies one preserved incident sample. The structural rule
 * prioritises related artifacts for review; a hit alone does not establish a family.
 * Validation scope and hashes accompany the article. No broad cleanware study.
 */

rule LOBERA_SciSvc_Exact_SHA256
{
    meta:
        description = "Exact identity of the preserved SciSvc qwsxche32.dll sample"
        author = "Lobera Research"
        date = "2026-10-24"
        sha256 = "c982651f1b8bcbe02f7469c8fc89970697cfc684a9df783d4cfa9175fe912e4f"
        scope = "Exact sample only; does not match the redirected laboratory derivative"

    condition:
        uint16(0) == 0x5a4d and
        filesize == 53248 and
        hash.sha256(0, filesize) == "c982651f1b8bcbe02f7469c8fc89970697cfc684a9df783d4cfa9175fe912e4f"
}

rule LOBERA_SciSvc_Structural_Triage
{
    meta:
        description = "Structural triage for artifacts similar to the analysed SciSvc implant"
        author = "Lobera Research"
        date = "2026-10-24"
        reference_sample_sha256 = "c982651f1b8bcbe02f7469c8fc89970697cfc684a9df783d4cfa9175fe912e4f"
        scope = "Candidate structural similarity; analyst review required"

    strings:
        $s_temp = "~sndf23.tmp" ascii
        $s_guid = "{68971564-0C97-447d-2154-E1591CE145B8}" ascii
        $s_server = "Server:nginx/06.32" ascii
        $s_php = "X-Powered-By: PHP/5.2.6" ascii
        $s_base64 = "_base64_src" ascii
        $s_version = "V3.0.0.0" ascii
        $s_product = "tqdll Dynamic Link Library" wide

        // push 0x11223344: immediate DWORD represented little-endian in the file.
        $config_decoder = { 68 44 33 22 11 68 20 04 00 00 56 68 ?? ?? 00 10 E8 }
        $frame_markers = { 80 ?? 17 2D ?? ?? 80 ?? 39 1D }
        $module_interactive = { 00 03 02 60 }
        $module_files = { 00 02 02 60 }
        $module_relay = { 00 04 02 60 }

    condition:
        uint16(0) == 0x5a4d and
        filesize < 2MB and
        5 of ($s_*) and
        $config_decoder and $frame_markers and
        2 of ($module_*)
}
Leer las firmas Suricata completas
# Lobera Research — SciSvc / qwsxche32.dll — 2026-10-24
# Passive IDS signatures for structures observed in the isolated lab capture.
# No infrastructure/IP dependency; HOME_NET must include the monitored endpoints.
# These signatures use relative content matches, not dsize or absolute TCP-segment
# offsets. Suricata stream reassembly can therefore cover the tested segmentation.
# This is signature matching, not a complete application-protocol parser.
# Neither a match nor a lab replay proves a historical production C2 session.
# Validate performance/false positives on representative traffic before deployment.
# Local SIDs: check for collisions in the receiving sensor's ruleset.

# Implant -> controller: observed registration type/subtype 1/1, declared outer
# payload length 586 (0x24a), record length 566 (0x236), and default profile prefix.
# Marker +0x17 -> marker +0x39: distance 33 after consuming the first byte.
# Marker +0x39 -> length +0x46: distance 12 after consuming the second byte.
alert tcp $HOME_NET any -> any any (msg:"LOBERA SciSvc registration structure default profile"; flow:established,to_server; content:"|2D|"; content:"|1D|"; distance:33; within:1; content:"|4A 02 00 00 01 00 00 00 01 00 00 00 00 00 00 00 00 00 00 00 36 02 00 00|default_V3.0.0.0_SciSvc_"; distance:12; within:48; classtype:trojan-activity; metadata:created_at 2026_10_24, deployment Perimeter; sid:9902401; rev:1;)

# Controller -> implant: observed registration request 1/1, declared outer
# payload length 20 and empty inner payload. Only this request was exercised.
alert tcp any any -> $HOME_NET any (msg:"LOBERA SciSvc registration request structure type 1 subtype 1"; flow:established,to_client; content:"|2D|"; content:"|1D|"; distance:33; within:1; content:"|14 00 00 00 01 00 00 00 01 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00|"; distance:12; within:24; classtype:trojan-activity; metadata:created_at 2026_10_24, deployment Perimeter; sid:9902402; rev:1;)

YARA: identidad exacta y semejanza estructural

La regla de identidad usa el SHA-256 completo de la DLL original y su tamaño. Permite localizar copias exactas en búsquedas retrospectivas y comprobar adquisiciones.

La segunda regla comprueba la cabecera MZ y el tamaño, al menos cinco de siete cadenas, el patrón del decodificador XOR, los marcadores del parser y dos de los tres identificadores de módulo. Selecciona objetos estructuralmente compatibles con el código analizado para su revisión.

La validación con YARA se realizó sobre un corpus mixto de 288 objetos preservados. Ambas reglas reconocieron únicamente la DLL original.

Suricata: marcadores, campos y longitudes

Las firmas de red se concentran en el registro 1/1 y en su petición. Combinan los marcadores exteriores con los campos de tipo, subtipo y longitud que corresponden a cada forma del mensaje. La documentación de Suricata sobre inspección de payload distingue coincidencias de contenido y restricciones de posición; esas restricciones deben aplicarse sobre el contexto correcto de paquete o flujo.

Una trama de 660 bytes y una solicitud de 94 tienen papeles diferentes. La alerta del registro identifica la presentación del implante; la de solicitud reconoce el formato de la petición enviada en sentido contrario.

La matriz sintética incluye once controles positivos y diecinueve negativos. Comprueba cambios en campos relevantes, segmentación y coalescencia TCP, dirección del tráfico y configuración de HOME_NET. Los treinta casos produjeron el resultado esperado.

La firma de registro exige el prefijo default_V3.0.0.0_SciSvc_ y los campos de longitud observados. Ambas firmas cotejan las longitudes como secuencias de bytes en sus posiciones del flujo. El replay de laboratorio produjo dos eventos de alerta, uno por SID.

Las firmas utilizan la estructura del mensaje y permiten otros puertos. TCP/8020 es el valor configurado en esta muestra. El destino histórico sirve como criterio de búsqueda adicional. Para desplegarlas, ajustar las variables de red y comprobar la visibilidad de ambos sentidos y el reensamblado TCP.

Hunting de host con varias señales

La secuencia de triage comienza por la configuración del servicio y sigue hacia el fichero y el proceso que lo hospeda. Un servicio detenido mantiene interés si conserva su arranque automático y la referencia a la DLL. Una coincidencia de nombre requiere comprobar ruta, firma, hash y procedencia antes de escalarla como confirmación.

# E07 · Consulta local de solo lectura; puede requerir privilegios.
Get-CimInstance Win32_Service -Filter "Name='SciSvc'" |
  Select-Object Name, DisplayName, State, StartMode, StartName, PathName

$serviceKey = 'HKLM:\SYSTEM\CurrentControlSet\Services\SciSvc\Parameters'
Get-ItemProperty -LiteralPath $serviceKey -Name ServiceDll `
  -ErrorAction SilentlyContinue

$candidateDll = 'C:\Windows\System32\dllcache\qwsxche32.dll'
if (Test-Path -LiteralPath $candidateDll) {
  Get-FileHash -LiteralPath $candidateDll -Algorithm SHA256
  Get-AuthenticodeSignature -LiteralPath $candidateDll |
    Select-Object Status, Path
}

La consulta anterior comprueba la persistencia concreta recuperada. Si aparece una ruta distinta en ServiceDll, debe resolverse y adquirirse también. Relacionar el proceso anfitrión con su actividad de red requiere datos de carga de módulos, sockets o registros de proceso. Una revisión completa del equipo debe abarcar además otros mecanismos de persistencia y artefactos relacionados.

Indicadores con función y procedencia.

El conjunto público incluye la identidad de la muestra, el C2 histórico configurado y artefactos de servicio y filesystem. Cada fila indica su procedencia y función. Las direcciones se presentan desactivadas y los identificadores internos están anonimizados.

Descargar 12 IOC / CSV
IndicadorFunciónProcedencia / alcance
c982651f1b8bcbe02f7469c8fc89970697cfc684a9df783d4cfa9175fe912e4fMuestraIdentidad exacta de la DLL adquirida.
qwsxche32.dllFicheroNombre observado; corroborar con hash.
SciSvcServicioCorrelacionar ServiceDll e identidad del fichero.
Indexes ServiceNombre visibleNombre visible recuperado de la configuración del servicio.
C:\Windows\System32\dllcache\qwsxche32.dllServiceDllRuta confirmada en el despliegue analizado.
181[.]176[.]160[.]172C2 históricoDestino recuperado de la configuración y observado en la telemetría del incidente.
8020Puerto configuradoPuerto TCP del destino configurado en la muestra.
{68971564-0C97-447d-2154-E1591CE145B8}Evento de paradaGUID embebido; correlacionar con otros artefactos.
~sndf23.tmpCarga temporalNombre temporal utilizado por la ruta de descarga y ejecución.
default_V3.0.0.0_SciSvc_Prefijo de registroComprobado en laboratorio aislado.
Server:nginx/06.32Cadena embebidaLiteral conservado en el binario.
X-Powered-By: PHP/5.2.6Cadena embebidaLiteral conservado en el binario.

No hay indicadores con esos criterios.

12 de 12 indicadores

WSSD.tmp y ~sndf23.tmp tienen funciones distintas. El primero aparece como ruta temporal en la configuración; el segundo participa en la ruta estática de descarga y ejecución. Los dominios de Microsoft y Google corresponden a sondas de conectividad embebidas.

El hash de la derivada de laboratorio se conserva en el apartado de método, separado del CSV de IOC. Identifica un objeto creado para el experimento. La búsqueda de presencia en producción utiliza el hash de la muestra adquirida.

La búsqueda se extendió a un censo de 2.110 direcciones. La matriz de cobertura distinguió accesibilidad de TCP/445, autenticación y comprobación del artefacto. Ese desglose permitió priorizar la adquisición de equipos y cruzar los resultados con la telemetría de red.

Contención, recuperación y comprobación.

El primer objetivo operativo es conservar lo que permite explicar la persistencia y relacionarla con actividad. Detener el servicio o eliminar la DLL puede cortar parte del comportamiento, pero también modifica el estado que se pretende estudiar. La secuencia debe ajustarse al impacto del sistema y a la capacidad de adquisición disponible.

  1. Preservar la relación servicio–DLL

    Adquirir configuración, fichero y metadatos; recoger estado y módulos del proceso cuando sea viable. Fijar hashes antes de alterar los artefactos.

  2. Contener y determinar el alcance

    Controlar la comunicación y el acceso del sistema afectado. Correlacionar intentos de red con servicio, proceso y muestra; mantener A y B como investigaciones separadas.

  3. Investigar el acceso y las acciones

    Buscar origen de la instalación, procesos relacionados y rastros de ficheros. Evaluar credenciales expuestas según la actividad y los privilegios del servicio.

  4. Recuperar y comprobar persistencia

    Retirar los mecanismos identificados o reconstruir el sistema según el alcance. Verificar configuración de arranque, módulos, integridad y telemetría posterior.

El bloqueo del destino histórico limita una vía configurada y debe acompañarse de la revisión del host. Las capacidades de ejecución de cargas y transferencia justifican buscar artefactos relacionados y evaluar el acceso disponible para el contexto de servicio.

El segundo origen se investiga mediante su propia configuración de servicios, módulos cargados y artefactos de disco. La correlación de destino y cadencia permite orientar esa adquisición.

La verificación posterior combina configuración de arranque, integridad del fichero, módulos cargados y comportamiento de red. Comprobar la cobertura de los sensores permite evaluar el resultado sobre los mismos mecanismos que sostuvieron la detección.

Método y fuentes.

Este artículo reúne el análisis estático de la DLL original, la validación de registro en laboratorio y la evidencia de producción: adquisición del endpoint, configuración del servicio, revisiones de estado, export de firewall y censo de hunting. Los hashes, offsets y medidas técnicas permiten reproducir las comprobaciones sobre los artefactos correspondientes.

El análisis estático verificó la configuración y 29 anclas de código contra los bytes de la muestra. Una segunda lectura independiente del PE y del desensamblado corroboró importaciones, exportaciones, factories, vtables y rutas enlazadas de proceso, transferencia y relé. Las direcciones virtuales utilizan ImageBase = 0x10000000; los offsets de fichero se obtienen mediante el mapa de secciones del PE. El pseudocódigo resume los flujos reconstruidos durante el reversing.

La derivada de laboratorio tiene SHA-256 891e122d2816cebb32590dda321467a4525920737ea61bd161f23007715981c1. Difiere de la original en once bytes del campo C2 cifrado. La prueba, realizada el 10 de octubre de 2026 UTC, valida conexión y registro. La revisión editorial y la comprobación offline de las detecciones de esta edición son del 24 de octubre de 2026.

El material público reúne reglas, IOC y visualizaciones técnicas. El mapa de memoria representa posiciones y diferencias entre registros; el inspector de protocolo permite consultar campos, tamaños y offsets.

  1. MITRE ATT&CK · APT41 / G0096 ↗

    Técnicas documentadas para APT41: servicios Windows, camuflaje y proxy.

  2. Mandiant · Campaña global de APT41 en 2020 ↗

    Investigación publicada el 25 de marzo de 2020 sobre una campaña contra varios sectores, incluidos salud y farmacéutico.

  3. Microsoft · Volt Typhoon y puntos de apoyo de red ↗

    Análisis de otra operación que utilizó equipos comprometidos y proxies para mantener accesos discretos.

  4. Microsoft · OSVERSIONINFOEXA ↗

    Definición del buffer szCSDVersion de 128 bytes y del resto de la estructura.

  5. Microsoft · SHGetSpecialFolderPathW ↗

    Semántica del parámetro CSIDL; permite interpretar correctamente el bucle de carpetas especiales.

  6. MITRE ATT&CK · Windows Service / T1543.003 ↗

    Descripción de la persistencia mediante servicios de Windows.

  7. Suricata · Payload keywords ↗

    Semántica de contenidos y distancias. Las firmas publicadas se probaron con el motor 7.0.3.

  8. Lobera · Prometei: radiografía de una botnet de criptominería ↗

    Investigación anterior de la serie: arquitectura, configuración e infraestructura de una botnet modular.

Las fuentes anteriores documentan APIs, técnicas y formato de detección. Las conclusiones sobre SciSvc proceden de la muestra, la captura y la telemetría preservadas por Lobera, descritas en el artículo.

Aplicación al hunting

La búsqueda puede empezar por el servicio, la ruta de la DLL o su hash, y ampliarse mediante el formato del registro y la cadencia de conexión. En este caso, esa combinación permitió confirmar un equipo, priorizar un segundo origen y preparar detecciones de host y red.

LOBERA / INVESTIGACIÓN DE AMENAZAS

Reglas e indicadores.

Reglas, indicadores y resultados de validación para contrastar esta investigación con tu propia evidencia.

Consultar los artefactos
ANTERIOR EN LA SERIE

Prometei.

Radiografía de una botnet de criptominería.

Leer la investigación