Superficie de ataque de ksmbd & SMB2/SMB3 Protocol Fuzzing (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 mutaciones hacia rutas de código débilmente cubiertas 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.

Ámbito objetivo: SMB2/SMB3 sobre TCP. Kerberos y RDMA están intencionalmente fuera de alcance para mantener el harness simple.


Expandir la superficie de ataque de ksmbd mediante la configuración

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

  • A nivel global
  • Durable handles
  • Server multi-channel
  • SMB2 leases
  • A nivel por-share
  • Oplocks (activado por defecto)
  • VFS objects

Habilitarlas 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 de ksmbd de tu distro (ksmbd-tools). Revisa /etc/ksmbd/ksmbd.conf y las secciones por-share para habilitar durable handles, leases, oplocks y VFS objects.
  • Multi-channel y durable handles alteran las máquinas de estado y los lifetimes, frecuentemente haciendo aflorar 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 guest access y configurar map to guest = bad user para que usuarios desconocidos caigan en GUEST.
  • Aceptar NTLMv2 (patch policy si está deshabilitado). Esto mantiene el handshake simple mientras se ejercitan rutas de código de SMB3.
  • Parchear las comprobaciones estrictas de credit cuando se experimenta (el endurecimiento post-CVE-2024-50285 hizo más estricta la acreditación de simultaneous-op). Si no, los rate-limits pueden rechazar secuencias fuzzed demasiado pronto.
  • Incrementar 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 despliegues con estas configuraciones en producción.


Arnés con estado: extraer recursos y encadenar peticiones

SMB es stateful: muchas peticiones 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 (por ejemplo, smb2_create → smb2_ioctl → smb2_close).

Ejemplo de snippet para procesar un buffer de respuesta (omitiendo los +4B de longitud del NetBIOS PDU) y almacenar en caché 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 compartiendo autenticación/estado: mejor estabilidad y cobertura con las tablas globales/de sesión de ksmbd. syzkaller todavía inyecta concurrencia marcando las ops como async y reejecuta internamente.
  • Syzkaller’s experimental reset_acc_state puede restablecer el estado global pero puede provocar una fuerte ralentización. Prefiere la estabilidad y céntrate en el fuzzing en su lugar.

Generación basada en gramática para SMB2 (PDUs válidos)

Convierte las estructuras SMB2 de Microsoft Open Specifications en una gramática para fuzzer para que tu generador produzca PDUs estructuralmente válidos, que lleguen sistemáticamente a los dispatchers y a 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 obliga a tamaños/desplazamientos (offsets) correctos de las estructuras y mejora dramáticamente la cobertura frente a la mutación ciega.


Fuzzing dirigido con focus_areas

Usa el focus_areas experimental de syzkaller para asignar 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)

Superando mesetas de cobertura con ANYBLOB

Los anyTypes de syzkaller (ANYBLOB/ANYRES) permiten colapsar estructuras complejas en blobs que mutan de forma genérica. Genera un nuevo corpus a partir de pcaps SMB públicos y convierte 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 unos pocos puntos porcentuales.


Sanitizadores: Más allá de KASAN

  • KASAN sigue siendo el detector principal para errores en el heap (UAF/OOB).
  • KCSAN a menudo produce falsos positivos o data races de baja gravedad en este objetivo.
  • UBSAN/KUBSAN puede detectar errores de límites declarados que KASAN pasa por alto debido a la semántica del índice de arrays. 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));

Setting num_subauth = 0 triggers an in-struct OOB read of sub_auth[-1], caught by UBSAN’s declared-bounds checks.


Notas sobre rendimiento y paralelismo

  • Un único proceso fuzzer (shared auth/state) tiende a ser significativamente más estable para ksmbd y aún así hace aflorar races/UAFs gracias al async executor interno de syzkaller.
  • Con múltiples VMs, aún puedes alcanzar cientos de comandos SMB/segundo en total. Es alcanzable una 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 esas métricas.

Lista de comprobación práctica

  • Habilitar durable handles, leases, multi-channel, oplocks y VFS objects en ksmbd.
  • Permitir guest y map-to-guest; aceptar NTLMv2. Patch out los credit limits y aumentar max connections para la estabilidad del fuzzer.
  • Construir un stateful harness que cachee SessionId/TreeID/FileIDs y encadene create → ioctl → close.
  • Usar una grammar para SMB2 PDUs para mantener validez estructural.
  • Usar focus_areas para sobreponderar funciones con cobertura débil (p.ej., rutas en smbacl.c como smb_check_perm_dacl).
  • Sembrar con ANYBLOB de pcaps reales para romper mesetas; empaquetar seeds con syz-db para reutilización.
  • Ejecutar con KASAN + UBSAN; triagear cuidadosamente los informes de declared-bounds de UBSAN.

Referencias

  • Doyensec – ksmbd Fuzzing (Parte 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
  • Cambio en el async executor (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 de fondo: pwning.tech “Tickling ksmbd: fuzzing SMB in the Linux kernel”; notas de syzkaller de Dongliang Mu

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