ksmbd Attack Surface & SMB2/SMB3 Protocol Fuzzing (syzkaller)

Reading time: 8 minutes

tip

Ucz się i ćwicz Hacking AWS:HackTricks Training AWS Red Team Expert (ARTE)
Ucz się i ćwicz Hacking GCP: HackTricks Training GCP Red Team Expert (GRTE) Ucz się i ćwicz Hacking Azure: HackTricks Training Azure Red Team Expert (AzRTE)

Wsparcie dla HackTricks

Przegląd

Ta strona przedstawia praktyczne techniki do uruchamiania i fuzzowania jądrowego serwera SMB dla Linux (ksmbd) przy użyciu syzkaller. Skupia się na rozszerzaniu attack surface protokołu poprzez konfigurację, budowaniu stateful harness zdolnego do łączenia operacji SMB2, generowaniu składniowo poprawnych PDUs, kierowaniu mutacji w słabo pokrywane ścieżki kodu oraz wykorzystaniu funkcji syzkaller takich jak focus_areas i ANYBLOB. Choć oryginalne badania wymieniają konkretne CVE, tutaj podkreślamy powtarzalną metodologię i konkretne fragmenty, które możesz dostosować do własnych środowisk.

Zakres: SMB2/SMB3 przez TCP. Kerberos i RDMA są celowo poza zakresem, aby zachować prostotę harnessu.


Rozszerz ksmbd Attack Surface przez konfigurację

Domyślnie minimalna konfiguracja ksmbd pozostawia duże części serwera nieprzetestowane. Włącz następujące funkcje, aby przeprowadzić serwer przez dodatkowe parsers/handlers i dotrzeć do głębszych ścieżek kodu:

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

Włączenie ich zwiększa wykonanie w modułach takich jak:

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

Uwagi

  • Dokładne opcje zależą od userspace ksmbd w Twojej dystrybucji (ksmbd-tools). Sprawdź /etc/ksmbd/ksmbd.conf oraz sekcje per-share, aby włączyć durable handles, leases, oplocks i VFS objects.
  • Multi-channel i durable handles zmieniają maszyny stanów i czasy życia, często ujawniając błędy UAF/refcount/OOB pod obciążeniem współbieżnym.

Authentication and Rate-Limiting Adjustments for Fuzzing

SMB3 wymaga ważnej sesji. Implementacja Kerberos w harnessach dodaje złożoności, więc dla fuzzingu preferuj NTLM/guest:

  • Allow guest access and set map to guest = bad user so unknown users fall back to GUEST.
  • Akceptuj NTLMv2 (patch policy jeśli wyłączone). To utrzymuje handshake prostym jednocześnie testując ścieżki kodu SMB3.
  • Wyłącz rygorystyczne sprawdzanie creditów podczas eksperymentów (post-hardening dla CVE-2024-50285 zaostrzył przydzielanie creditów dla operacji równoczesnych). W przeciwnym razie, rate-limits mogą odrzucić fuzzowane sekwencje zbyt wcześnie.
  • Zwiększ max connections (np. do 65536), aby uniknąć wczesnych odrzuceń podczas fuzzingu o dużej przepustowości.

Uwaga: Te ułatwienia służą tylko fuzzingowi. Nie wdrażaj tych ustawień w środowisku produkcyjnym.


Stateful Harness: Extract Resources and Chain Requests

SMB jest stanowy: wiele żądań zależy od identyfikatorów zwracanych w poprzednich odpowiedziach (SessionId, TreeID, FileID pairs). Twój harness musi parsować odpowiedzi i ponownie używać ID w tym samym programie, aby dotrzeć do głębokich handlerów (np. smb2_create → smb2_ioctl → smb2_close).

Przykładowy fragment do przetworzenia bufora odpowiedzi (pomijając +4B długości NetBIOS PDU) i zapisania ID:

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;
}
}

Wskazówki

  • Keep one fuzzer process sharing authentication/state: better stability and coverage with ksmbd’s global/session tables. syzkaller still injects concurrency by marking ops async, rerun internally.
  • Syzkaller’s experimental reset_acc_state can reset global state but may introduce heavy slowdown. Prefer stability and focus fuzzing instead.

Generowanie SMB2 sterowane gramatyką (prawidłowe PDUs)

Przekształć struktury SMB2 z Microsoft Open Specifications w gramatykę dla fuzzer, aby twój generator produkował strukturalnie prawidłowe PDUs, które systematycznie docierają do dispatcherów i handlerów IOCTL.

Przykład (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]

Ten styl wymusza poprawne structure sizes/offsets i znacząco poprawia coverage w porównaniu do blind mutation.


Directed Fuzzing With focus_areas

Użyj eksperymentalnego focus_areas syzkallera, aby przypisać większą wagę konkretnym funkcjom/plikom, które obecnie mają słabe coverage. Przykładowy JSON:

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

To pomaga skonstruować poprawne ACLs, które trafiają w ścieżki arytmetyczne/overflow w smbacl.c. Na przykład złośliwy Security Descriptor z nadmiernym dacloffset powoduje integer-overflow.

Skrypt tworzący reproduktora (minimalny, 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)

Przełamywanie stagnacji pokrycia za pomocą ANYBLOB

syzkaller’s anyTypes (ANYBLOB/ANYRES) pozwalają zwinąć złożone struktury w bloby, które mutują w sposób ogólny. Zainicjuj nowy corpus z publicznych SMB pcaps i skonwertuj payloady na programy syzkaller wywołujące twój pseudo-syscall (np. 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)"
)

To przyspiesza eksplorację i może natychmiast wywołać UAFs (np. w ksmbd_sessions_deregister), jednocześnie podnosząc pokrycie o kilka procent.


Sanitizery: poza KASAN

  • KASAN pozostaje głównym detektorem błędów sterty (UAF/OOB).
  • KCSAN często generuje fałszywe alarmy lub wyścigi danych o niskiej istotności w tym przypadku.
  • UBSAN/KUBSAN mogą wykryć błędy zadeklarowanych granic, które KASAN pomija ze względu na semantykę indeksów tablic. Przykład:
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));

Ustawienie num_subauth = 0 wywołuje in-struct OOB read sub_auth[-1], wykrywany przez UBSAN’s declared-bounds checks.


Uwagi o przepustowości i równoległości

  • Pojedynczy proces fuzzera (shared auth/state) zwykle jest znacznie bardziej stabilny dla ksmbd i nadal ujawnia races/UAFs dzięki syzkaller’s internal async executor.
  • Przy wielu VMs można nadal osiągać setki poleceń SMB/sekundę łącznie. Pokrycie na poziomie funkcji rzędu ~60% fs/smb/server i ~70% smb2pdu.c jest osiągalne, choć pokrycie przejść stanów jest niedoszacowane przez takie metryki.

Praktyczna lista kontrolna

  • Włącz durable handles, leases, multi-channel, oplocks i VFS objects w ksmbd.
  • Zezwól na guest i map-to-guest; akceptuj NTLMv2. Pozbądź się credit limits i zwiększ max connections dla stabilności fuzzera.
  • Zbuduj stateful harness, który cache’uje SessionId/TreeID/FileIDs i łączy create → ioctl → close.
  • Użyj grammar dla SMB2 PDUs, aby zachować poprawność strukturalną.
  • Użyj focus_areas, by przydzielić większą wagę słabo pokrytym funkcjom (np. ścieżki w smbacl.c jak smb_check_perm_dacl).
  • Seeduj ANYBLOBami z realnych pcaps, aby przełamać plateau; pakuj seedy z syz-db do ponownego użycia.
  • Uruchamiaj z KASAN + UBSAN; dokładnie triage’uj raporty UBSAN declared-bounds.

Referencje

  • 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
  • Materiały uzupełniające: pwning.tech “Tickling ksmbd: fuzzing SMB in the Linux kernel”; Dongliang Mu’s syzkaller notes

tip

Ucz się i ćwicz Hacking AWS:HackTricks Training AWS Red Team Expert (ARTE)
Ucz się i ćwicz Hacking GCP: HackTricks Training GCP Red Team Expert (GRTE) Ucz się i ćwicz Hacking Azure: HackTricks Training Azure Red Team Expert (AzRTE)

Wsparcie dla HackTricks