# ksmbd Surface d'attaque & SMB2/SMB3 Protocol Fuzzing (syzkaller) {{#include ../../banners/hacktricks-training.md}} ## Aperçu Cette page synthétise des techniques pratiques pour exercer et fuzz le serveur SMB en-kernel Linux (ksmbd) en utilisant syzkaller. Elle se concentre sur l'expansion de la surface d'attaque du protocole via la configuration, la construction d'un harnais stateful capable d'enchaîner des opérations SMB2, la génération de PDUs valides grammaticalement, le biaisage des mutations vers des chemins de code faiblement couverts, et l'exploitation des fonctionnalités de syzkaller telles que focus_areas et ANYBLOB. Alors que la recherche originale énumère des CVE spécifiques, ici nous mettons l'accent sur la méthodologie réutilisable et des extraits concrets que vous pouvez adapter à vos propres environnements. Portée cible : SMB2/SMB3 over TCP. Kerberos et RDMA sont volontairement hors-scope pour garder le harnais simple. --- ## Étendre la surface d'attaque de ksmbd via la configuration Par défaut, une configuration ksmbd minimale laisse de larges parties du serveur non testées. Activez les fonctionnalités suivantes pour pousser le serveur à travers des parseurs/handlers supplémentaires et atteindre des chemins de code plus profonds : - Global-level - Durable handles - Server multi-channel - SMB2 leases - Per-share-level - Oplocks (activés par défaut) - VFS objects L'activation de ces éléments augmente l'exécution dans des modules tels que : - smb2pdu.c (parsing/dispatch des commandes) - ndr.c (encodage/décodage NDR) - oplock.c (demande/rupture d'oplock) - smbacl.c (parsing/renforcement des ACL) - vfs.c (opérations VFS) - vfs_cache.c (cache de lookup) Notes - Les options exactes dépendent des userspace ksmbd de votre distro (ksmbd-tools). Consultez /etc/ksmbd/ksmbd.conf et les sections par-partage pour activer durable handles, leases, oplocks et VFS objects. - Multi-channel et durable handles modifient les machines d'état et les durées de vie, révélant souvent des bugs UAF/refcount/OOB sous concurrence. --- ## Authentification et ajustements de rate-limiting pour le Fuzzing SMB3 nécessite une session valide. Implémenter Kerberos dans les harnais ajoute de la complexité, donc préférez NTLM/guest pour le fuzzing : - Autorisez l'accès guest et définissez map to guest = bad user afin que les utilisateurs inconnus retombent sur GUEST. - Acceptez NTLMv2 (patcher la policy si désactivée). Cela maintient la poignée de main simple tout en exerçant les chemins de code SMB3. - Patcherez les contrôles stricts de credits lors des expérimentations (le durcissement postérieur pour CVE-2024-50285 a rendu le crédit pour opérations simultanées plus strict). Sinon, les rate-limits peuvent rejeter des séquences fuzzées trop tôt. - Augmentez le max connections (p. ex. à 65536) pour éviter des rejets précoces lors d'un fuzzing à haut débit. Attention : Ces relaxations servent uniquement à faciliter le fuzzing. Ne pas déployer ces réglages en production. --- ## Harnais à états : Extraire des ressources et enchaîner des requêtes SMB est stateful : beaucoup de requêtes dépendent d'identifiants retournés par des réponses précédentes (SessionId, TreeID, paires FileID). Votre harnais doit parser les réponses et réutiliser les IDs dans le même programme pour atteindre des handlers profonds (p. ex., smb2_create → smb2_ioctl → smb2_close). Exemple d'extrait pour traiter un buffer de réponse (en sautant les +4B de longueur NetBIOS PDU) et mettre en cache les 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; } } ``` Conseils - Gardez un seul processus fuzzer partageant l'authentification/état : meilleure stabilité et couverture avec les tables globales/session de ksmbd. syzkaller injecte néanmoins de la concurrence en marquant les ops comme async, réexécution en interne. - L'option expérimentale reset_acc_state de syzkaller peut réinitialiser l'état global mais introduit un ralentissement important. Privilégiez la stabilité et concentrez-vous sur le fuzzing. --- ## Génération SMB2 pilotée par une grammaire (PDUs valides) Traduisez les structures SMB2 des Microsoft Open Specifications en une grammaire pour fuzzer afin que votre générateur produise des PDUs structurellement valides, qui atteignent systématiquement les dispatchers et les IOCTL handlers. Exemple (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] ``` Ce style force des tailles/décalages de structure corrects et améliore considérablement la couverture par rapport à la mutation aveugle. --- ## Fuzzing dirigé avec focus_areas Utilisez syzkaller’s experimental focus_areas pour surpondérer des fonctions/fichiers spécifiques qui ont actuellement une faible couverture. Exemple JSON: ```json { "focus_areas": [ {"filter": {"functions": ["smb_check_perm_dacl"]}, "weight": 20.0}, {"filter": {"files": ["^fs/smb/server/"]}, "weight": 2.0}, {"weight": 1.0} ] } ``` Ceci aide à construire des ACLs valides qui atteignent les chemins arithmetic/overflow dans smbacl.c. Par exemple, un Security Descriptor malveillant avec un dacloffset surdimensionné reproduit un integer-overflow. Script de reproduction (Python minimal): ```python def build_sd(): import struct sd = bytearray(0x14) sd[0x00] = 0x00; sd[0x01] = 0x00 struct.pack_into(' 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)" ) ``` Cela lance l'exploration et peut immédiatement déclencher des UAFs (p. ex., dans ksmbd_sessions_deregister) tout en augmentant la couverture de quelques pourcents. --- ## Sanitizers : Au-delà de KASAN - KASAN reste le principal détecteur pour les bogues de heap (UAF/OOB). - KCSAN produit souvent des faux positifs ou des data races de faible gravité sur cette cible. - UBSAN/KUBSAN peut détecter des erreurs de bornes déclarées que KASAN manque à cause de la sémantique des indices de tableau. Exemple: ```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)); ``` Définir num_subauth = 0 déclenche une lecture in-struct OOB de sub_auth[-1], détectée par les declared-bounds checks d'UBSAN. --- ## Notes sur le débit et le parallélisme - Un seul processus fuzzer (auth/state partagé) a tendance à être significativement plus stable pour ksmbd et met néanmoins au jour des races/UAFs grâce à l'async executor interne de syzkaller. - Avec plusieurs VM, vous pouvez toujours atteindre des centaines de commandes SMB par seconde au total. Une couverture au niveau fonction d'environ ~60% de fs/smb/server et ~70% de smb2pdu.c est atteignable, bien que la couverture des transitions d'état soit sous-représentée par de tels métriques. --- ## Checklist pratique - Activez durable handles, leases, multi-channel, oplocks et VFS objects dans ksmbd. - Autorisez guest et map-to-guest ; acceptez NTLMv2. Éliminez les credit limits et augmentez le max connections pour la stabilité du fuzzer. - Construisez un stateful harness qui met en cache SessionId/TreeID/FileIDs et enchaîne create → ioctl → close. - Utilisez une grammar pour SMB2 PDUs afin de maintenir la validité structurelle. - Utilisez focus_areas pour surpondérer les fonctions faiblement couvertes (par ex., chemins de smbacl.c comme smb_check_perm_dacl). - Grainez avec ANYBLOB tiré de pcaps réels pour franchir les plateaux ; packez les seeds avec syz-db pour réutilisation. - Exécutez avec KASAN + UBSAN ; triez soigneusement les rapports declared-bounds d'UBSAN. --- ## Références - 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 - Lecture de fond: pwning.tech “Tickling ksmbd: fuzzing SMB in the Linux kernel”; notes syzkaller de Dongliang Mu {{#include ../../banners/hacktricks-training.md}}