ksmbd Surface d'attaque & Fuzzing du protocole SMB2/SMB3 (syzkaller)

Reading time: 9 minutes

tip

Apprenez et pratiquez le hacking AWS :HackTricks Training AWS Red Team Expert (ARTE)
Apprenez et pratiquez le hacking GCP : HackTricks Training GCP Red Team Expert (GRTE) Apprenez et pratiquez le hacking Azure : HackTricks Training Azure Red Team Expert (AzRTE)

Soutenir HackTricks

Aperçu

Cette page résume des techniques pratiques pour exercer et fuzz le serveur SMB in-kernel de 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 harness stateful capable d'enchaßner des opérations SMB2, la génération de PDUs valides selon la grammaire, l'orientation des mutations vers des chemins de code faiblement couverts, et l'exploitation de fonctionnalités de syzkaller telles que focus_areas et ANYBLOB. Alors que la recherche originale énumÚre des CVEs spécifiques, nous mettons ici l'accent sur la méthodologie réutilisable et des extraits concrets que vous pouvez adapter à vos propres environnements.

Portée cible : SMB2/SMB3 sur TCP. Kerberos et RDMA sont intentionnellement hors-scope pour garder le harness 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 faire passer le serveur par des parsers/handlers supplémentaires et atteindre des chemins de code plus profonds :

  • Global-level
  • Durable handles
  • Server multi-channel
  • SMB2 leases
  • Per-share-level
  • Oplocks (on by default)
  • VFS objects

L'activation de ces éléments augmente l'exécution dans des modules tels que :

  • 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)

Notes

  • Les options exactes dĂ©pendent de l'espace utilisateur ksmbd de votre distro (ksmbd-tools). Consultez /etc/ksmbd/ksmbd.conf et les sections per-share 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, souvent faisant ressortir des bugs UAF/refcount/OOB sous concurrence.

Ajustements d'authentification et de rate-limiting pour le fuzzing

SMB3 nécessite une session valide. Implémenter Kerberos dans les harnesses ajoute de la complexité, donc préférez NTLM/guest pour le fuzzing :

  • Autorisez guest access et configurez map to guest = bad user afin que les utilisateurs inconnus retombent sur GUEST.
  • Acceptez NTLMv2 (patch policy si dĂ©sactivĂ©). Cela simplifie le handshake tout en exerçant les chemins de code SMB3.
  • Patcherez les strict credit checks lors d'expĂ©rimentations (les durcissements post-hardening pour CVE-2024-50285 ont rendu le crĂ©dit simultanĂ© plus strict). Sinon, les rate-limits peuvent rejeter des sĂ©quences fuzzĂ©es trop tĂŽt.
  • Augmentez le nombre max connections (par ex. Ă  65536) pour Ă©viter les rejets prĂ©coces pendant un fuzzing Ă  haut dĂ©bit.

Attention : ces assouplissements facilitent uniquement le fuzzing. Ne déployez pas ces réglages en production.


Stateful Harness : extraire des ressources et enchaĂźner des requĂȘtes

SMB est stateful : de nombreuses requĂȘtes dĂ©pendent d'identifiants renvoyĂ©s par des rĂ©ponses antĂ©rieures (SessionId, TreeID, FileID pairs). Votre harness doit parser les rĂ©ponses et rĂ©utiliser les IDs au sein du mĂȘme programme pour atteindre des handlers profonds (par ex., smb2_create → smb2_ioctl → smb2_close).

Exemple d'extrait pour traiter un buffer de réponse (en sautant les +4B NetBIOS PDU length) 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/de session de ksmbd. syzkaller injecte quand mĂȘme de la concurrence en marquant les ops async, et rĂ©exĂ©cute en interne.
  • Le reset_acc_state expĂ©rimental de syzkaller peut rĂ©initialiser l'Ă©tat global mais peut entraĂźner un fort ralentissement. PrivilĂ©giez la stabilitĂ© et concentrez-vous sur le fuzzing.

Génération SMB2 dirigée par une grammaire (PDUs valides)

Convertissez 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 impose 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 le paramÚtre expérimental focus_areas de syzkaller pour donner plus de poids à 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}
]
}

Cela 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.

Générateur de reproducer (Python minimal):

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)

Briser les plateaux de couverture avec ANYBLOB

syzkaller's anyTypes (ANYBLOB/ANYRES) permettent de réduire des structures complexes en blobs qui mutent de maniÚre générique. Générez un nouveau corpus à partir de pcaps SMB publiques et convertissez les payloads en programmes syzkaller appelant votre pseudo-syscall (p.ex., 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)"
)

Cela permet de démarrer l'exploration et peut déclencher immédiatement des UAFs (p.ex., dans ksmbd_sessions_deregister) tout en augmentant la couverture de quelques pourcents.


Sanitizers: Au-delĂ  de KASAN

  • KASAN reste le dĂ©tecteur principal pour les bugs liĂ©s au tas (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 en raison 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 OOB dans la structure de sub_auth[-1], détectée par les declared-bounds checks d'UBSAN.


Remarques sur le débit et le parallélisme

  • Un seul processus fuzzer (auth/state partagĂ©) a tendance Ă  ĂȘtre nettement plus stable pour ksmbd tout en mettant toujours en Ă©vidence des races/UAFs grĂące Ă  l'async executor interne de syzkaller.
  • Avec plusieurs VMs, vous pouvez toujours atteindre plusieurs centaines de commandes SMB par seconde au total. Une couverture au niveau des fonctions 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.

Liste de contrĂŽle pratique

  • Activer durable handles, leases, multi-channel, oplocks et VFS objects dans ksmbd.
  • Autoriser guest et map-to-guest ; accepter NTLMv2. Supprimer les credit limits et augmenter le max connections pour la stabilitĂ© du fuzzer.
  • Construire un harness stateful qui met en cache SessionId/TreeID/FileIDs et enchaĂźne create → ioctl → close.
  • Utiliser une grammaire pour SMB2 PDUs afin de maintenir la validitĂ© structurelle.
  • Utiliser focus_areas pour surpondĂ©rer les fonctions faiblement couvertes (par ex., des chemins dans smbacl.c comme smb_check_perm_dacl).
  • Seed avec ANYBLOB provenant de vrais pcaps pour casser les plateaux ; packer les seeds avec syz-db pour rĂ©utilisation.
  • Lancer avec KASAN + UBSAN ; trier attentivement 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”; Dongliang Mu’s syzkaller notes

tip

Apprenez et pratiquez le hacking AWS :HackTricks Training AWS Red Team Expert (ARTE)
Apprenez et pratiquez le hacking GCP : HackTricks Training GCP Red Team Expert (GRTE) Apprenez et pratiquez le hacking Azure : HackTricks Training Azure Red Team Expert (AzRTE)

Soutenir HackTricks