Superficie de ataque de ksmbd y fuzzing del protocolo SMB2/SMB3 (syzkaller)

Reading time: 9 minutes

tip

Aprende y practica Hacking en AWS:HackTricks Training AWS Red Team Expert (ARTE)
Aprende y practica Hacking en GCP: HackTricks Training GCP Red Team Expert (GRTE) Aprende y practica Hacking en Azure: HackTricks Training Azure Red Team Expert (AzRTE)

Apoya a HackTricks

Resumen

Esta página abstrae técnicas prácticas para ejercitar y fuzzear el servidor SMB en el kernel de Linux (ksmbd) usando syzkaller. Se centra en ampliar la superficie de ataque del protocolo mediante la configuración, construir un harness stateful capaz de encadenar operaciones SMB2, generar PDUs válidos según la gramática, sesgar las mutaciones hacia rutas de código con cobertura débil y aprovechar características de syzkaller como focus_areas y ANYBLOB. Mientras que la investigación original enumera CVEs específicos, aquí enfatizamos la metodología reutilizable y fragmentos concretos que puedes adaptar a tus propios entornos.

Alcance objetivo: SMB2/SMB3 sobre TCP. Kerberos y RDMA quedan intencionalmente fuera de alcance para mantener el harness simple.


Amplía la superficie de ataque de ksmbd mediante la configuración

Por defecto, una configuración mínima de ksmbd deja muchas partes del servidor sin probar. Habilita las siguientes características para forzar al servidor a atravesar parsers/handlers adicionales y alcanzar rutas de código más profundas:

  • A nivel global
  • Durable handles
  • Multi-channel del servidor
  • SMB2 leases
  • A nivel por share
  • Oplocks (activado por defecto)
  • Objetos VFS

Habilitar esto incrementa la ejecución en módulos como:

  • smb2pdu.c (command parsing/dispatch)
  • ndr.c (NDR encode/decode)
  • oplock.c (oplock request/break)
  • smbacl.c (ACL parsing/enforcement)
  • vfs.c (VFS ops)
  • vfs_cache.c (lookup cache)

Notas

  • Las opciones exactas dependen del userspace ksmbd de tu distro (ksmbd-tools). Revisa /etc/ksmbd/ksmbd.conf y las secciones por share para habilitar durable handles, leases, oplocks y objetos VFS.
  • Multi-channel y durable handles alteran las máquinas de estado y las lifetimes, con frecuencia sacando a la superficie UAF/refcount/OOB bugs bajo concurrencia.

Ajustes de autenticación y rate-limiting para fuzzing

SMB3 necesita una sesión válida. Implementar Kerberos en los harnesses añade complejidad, por lo que se prefiere NTLM/guest para fuzzing:

  • Permitir acceso guest y establecer map to guest = bad user para que usuarios desconocidos caigan en GUEST.
  • Aceptar NTLMv2 (parcha la policy si está deshabilitado). Esto mantiene el handshake simple mientras se ejercitan las rutas de código de SMB3.
  • Elimina las comprobaciones estrictas de credit cuando experimentes (el hardening posterior para CVE-2024-50285 endureció la contabilización de credit para operaciones simultáneas). De lo contrario, los rate-limits pueden rechazar secuencias fuzzed demasiado pronto.
  • Incrementa max connections (p. ej., a 65536) para evitar rechazos tempranos durante fuzzing de alto rendimiento.

Precaución: Estas relajaciones son solo para facilitar el fuzzing. No desplegues estas configuraciones en producción.


Harness con estado: extraer recursos y encadenar solicitudes

SMB es stateful: muchas requests dependen de identificadores devueltos por respuestas previas (SessionId, TreeID, pares FileID). Tu harness debe parsear las respuestas y reutilizar los IDs dentro del mismo programa para alcanzar handlers profundos (p. ej., smb2_create → smb2_ioctl → smb2_close).

Ejemplo de snippet para procesar un buffer de respuesta (saltando los +4B de longitud del PDU NetBIOS) y cachear los IDs:

c
// process response. does not contain +4B PDU length
void process_buffer(int msg_no, const char *buffer, size_t received) {
uint16_t cmd_rsp = u16((const uint8_t *)(buffer + CMD_OFFSET));
switch (cmd_rsp) {
case SMB2_TREE_CONNECT:
if (received >= TREE_ID_OFFSET + sizeof(uint32_t))
tree_id = u32((const uint8_t *)(buffer + TREE_ID_OFFSET));
break;
case SMB2_SESS_SETUP:
// first session setup response carries session_id
if (msg_no == 0x01 && received >= SESSION_ID_OFFSET + sizeof(uint64_t))
session_id = u64((const uint8_t *)(buffer + SESSION_ID_OFFSET));
break;
case SMB2_CREATE:
if (received >= CREATE_VFID_OFFSET + sizeof(uint64_t)) {
persistent_file_id = u64((const uint8_t *)(buffer + CREATE_PFID_OFFSET));
volatile_file_id   = u64((const uint8_t *)(buffer + CREATE_VFID_OFFSET));
}
break;
default:
break;
}
}

Consejos

  • Mantén un único proceso fuzzer que comparta autenticación/estado: mejor estabilidad y cobertura con las tablas global/session de ksmbd. syzkaller aún inyecta concurrencia marcando ops como async y hace rerun internamente.
  • El experimental reset_acc_state de syzkaller puede restablecer el estado global, pero puede introducir una fuerte ralentización. Prefiere la estabilidad y céntrate en fuzzing.

Generación SMB2 guiada por gramática (PDUs válidos)

Traduce las estructuras SMB2 de Microsoft Open Specifications a una gramática de fuzzer para que tu generador produzca PDUs estructuralmente válidos, que alcancen sistemáticamente los dispatchers y los IOCTL handlers.

Ejemplo (SMB2 IOCTL request):

smb2_ioctl_req {
Header_Prefix           SMB2Header_Prefix
Command                 const[0xb, int16]
Header_Suffix           SMB2Header_Suffix
StructureSize           const[57, int16]
Reserved                const[0, int16]
CtlCode                 union_control_codes
PersistentFileId        const[0x4, int64]
VolatileFileId          const[0x0, int64]
InputOffset             offsetof[Input, int32]
InputCount              bytesize[Input, int32]
MaxInputResponse        const[65536, int32]
OutputOffset            offsetof[Output, int32]
OutputCount             len[Output, int32]
MaxOutputResponse       const[65536, int32]
Flags                   int32[0:1]
Reserved2               const[0, int32]
Input                   array[int8]
Output                  array[int8]
} [packed]

Este estilo fuerza tamaños/desplazamientos de estructuras correctos y mejora drásticamente la cobertura frente a la mutación ciega.


Directed Fuzzing With focus_areas

Utiliza el experimental focus_areas de syzkaller para dar mayor peso a funciones/archivos específicos que actualmente tienen cobertura débil. Ejemplo JSON:

json
{
"focus_areas": [
{"filter": {"functions": ["smb_check_perm_dacl"]}, "weight": 20.0},
{"filter": {"files": ["^fs/smb/server/"]}, "weight": 2.0},
{"weight": 1.0}
]
}

Esto ayuda a construir ACLs válidas que alcanzan las rutas arithmetic/overflow en smbacl.c. Por ejemplo, un Security Descriptor malicioso con un dacloffset sobredimensionado reproduce un integer-overflow.

Reproducer builder (minimal Python):

python
def build_sd():
import struct
sd = bytearray(0x14)
sd[0x00] = 0x00; sd[0x01] = 0x00
struct.pack_into('<H', sd, 0x02, 0x0001)
struct.pack_into('<I', sd, 0x04, 0x78)
struct.pack_into('<I', sd, 0x08, 0x00)
struct.pack_into('<I', sd, 0x0C, 0x10000)
struct.pack_into('<I', sd, 0x10, 0xFFFFFFFF)  # dacloffset
while len(sd) < 0x78:
sd += b'A'
sd += b"\x01\x01\x00\x00\x00\x00\x00\x00"  # minimal DACL
sd += b"\xCC" * 64
return bytes(sd)

Superar mesetas de cobertura con ANYBLOB

Las anyTypes de syzkaller (ANYBLOB/ANYRES) permiten colapsar estructuras complejas en blobs que mutan de forma genérica. Siembra un nuevo corpus a partir de pcaps SMB públicos y convierte los payloads en programas de syzkaller que llamen a tu pseudo-syscall (p. ej., syz_ksmbd_send_req):

bash
# Extract SMB payloads to JSON
# tshark -r smb2_dac_sample.pcap -Y "smb || smb2" -T json -e tcp.payload > packets.json
python
import json, os
os.makedirs("corpus", exist_ok=True)

with open("packets.json") as f:
data = json.load(f)
# adjust indexing to your tshark JSON structure
packets = [e["_source"]["layers"]["tcp.payload"] for e in data]

for i, pkt in enumerate(packets):
pdu = pkt[0]
pdu_size = len(pdu) // 2  # hex string length → bytes
with open(f"corpus/packet_{i:03d}.txt", "w") as f:
f.write(
f"syz_ksmbd_send_req(&(&(0x7f0000000340))=ANY=[@ANYBLOB=\"{pdu}\"], {hex(pdu_size)}, 0x0, 0x0)"
)

Esto acelera la exploración y puede desencadenar inmediatamente UAFs (p. ej., en ksmbd_sessions_deregister) mientras aumenta la cobertura en unos pocos puntos porcentuales.


Sanitizadores: Más allá de KASAN

  • KASAN sigue siendo el detector principal para heap bugs (UAF/OOB).
  • KCSAN a menudo produce falsos positivos o data races de baja severidad en este objetivo.
  • UBSAN/KUBSAN pueden detectar errores de límites declarados que KASAN pasa por alto debido a la semántica de índices de array. Ejemplo:
c
id = le32_to_cpu(psid->sub_auth[psid->num_subauth - 1]);
struct smb_sid {
__u8 revision; __u8 num_subauth; __u8 authority[NUM_AUTHS];
__le32 sub_auth[SID_MAX_SUB_AUTHORITIES]; /* sub_auth[num_subauth] */
} __attribute__((packed));

Configurar num_subauth = 0 provoca una lectura OOB dentro de la estructura de sub_auth[-1], capturada por las comprobaciones de límites declarados de UBSAN.


Notas sobre rendimiento y paralelismo

  • Un solo proceso fuzzer (auth/state compartido) suele ser significativamente más estable para ksmbd y aun así revela races/UAFs gracias al ejecutor asíncrono interno de syzkaller.
  • Con múltiples VMs aún puedes alcanzar cientos de comandos SMB/segundo en total. Se puede lograr cobertura a nivel de función de ~60% de fs/smb/server y ~70% de smb2pdu.c, aunque la cobertura de transiciones de estado está infrarepresentada por tales métricas.

Lista de verificación práctica

  • Habilita durable handles, leases, multi-channel, oplocks y VFS objects en ksmbd.
  • Permite guest y map-to-guest; acepta NTLMv2. Quita los credit limits y aumenta max connections para la estabilidad del fuzzer.
  • Construye un stateful harness que cachee SessionId/TreeID/FileIDs y encadene create → ioctl → close.
  • Usa una gramática para SMB2 PDUs para mantener la validez estructural.
  • Usa focus_areas para sobreponderar funciones con cobertura débil (p. ej., rutas en smbacl.c como smb_check_perm_dacl).
  • Siembra con ANYBLOB desde pcaps reales para romper mesetas; empaqueta los seeds con syz-db para reutilización.
  • Ejecuta con KASAN + UBSAN; realiza triage de los informes declared-bounds de UBSAN con cuidado.

Referencias

  • Doyensec – ksmbd Fuzzing (Part 2): https://blog.doyensec.com/2025/09/02/ksmbd-2.html
  • syzkaller: https://github.com/google/syzkaller
  • ANYBLOB/anyTypes (commit 9fe8aa4): https://github.com/google/syzkaller/commit/9fe8aa4
  • Async executor change (commit fd8caa5): https://github.com/google/syzkaller/commit/fd8caa5
  • syz-db: https://github.com/google/syzkaller/tree/master/tools/syz-db
  • KASAN: https://docs.kernel.org/dev-tools/kasan.html
  • UBSAN/KUBSAN: https://docs.kernel.org/dev-tools/ubsan.html
  • KCSAN: https://docs.kernel.org/dev-tools/kcsan.html
  • Microsoft Open Specifications (SMB): https://learn.microsoft.com/openspecs/
  • Wireshark Sample Captures: https://wiki.wireshark.org/SampleCaptures
  • Lectura adicional: pwning.tech “Tickling ksmbd: fuzzing SMB in the Linux kernel”; Dongliang Mu’s syzkaller notes

tip

Aprende y practica Hacking en AWS:HackTricks Training AWS Red Team Expert (ARTE)
Aprende y practica Hacking en GCP: HackTricks Training GCP Red Team Expert (GRTE) Aprende y practica Hacking en Azure: HackTricks Training Azure Red Team Expert (AzRTE)

Apoya a HackTricks