hacktricks/src/network-services-pentesting/pentesting-smb/ksmbd-attack-surface-and-fuzzing-syzkaller.md

12 KiB
Raw Blame History

ksmbd 攻撃面 & SMB2/SMB3 Protocol Fuzzing (syzkaller)

{{#include ../../banners/hacktricks-training.md}}

概要

このページは、syzkaller を用いて Linux カーネル内 SMB サーバー (ksmbd) を動作させ、fuzz するための実践的手法を抽象化しています。設定によってプロトコルの攻撃面を拡大すること、SMB2 操作をチェインできるステートフルなハーネスを構築すること、文法的に有効な PDU を生成すること、カバレッジの薄いコードパスに対してミューテーションをバイアスすること、そして focus_areas や ANYBLOB といった syzkaller の機能を活用することに焦点を当てています。元の研究では具体的な CVE を列挙していますが、ここでは再利用可能な方法論と自分の環境に適用できる具体的スニペットを重視します。

Target scope: SMB2/SMB3 over TCP. Kerberos and RDMA are intentionally out-of-scope to keep the harness simple.


設定による ksmbd の攻撃面の拡大

デフォルトでは、最小限の ksmbd 設定はサーバーの大部分を未テストのままにします。次の機能を有効にして、追加のパーサ/ハンドラを通し、より深いコードパスに到達させてください:

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

これらを有効にすると、以下のようなモジュールでの実行が増えます:

  • 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

  • 正確なオプションはディストリの ksmbd ユーザースペース (ksmbd-tools) に依存します。/etc/ksmbd/ksmbd.conf および各共有セクションを確認して、durable handles、leases、oplocks、VFS objects を有効にしてください。
  • Multi-channel と durable handles は状態機械やライフタイムを変更し、並行性の下で UAF/refcount/OOB のバグを顕在化させることがよくあります。

Fuzzing のための認証とレート制限の調整

SMB3 には有効なセッションが必要です。ハーネス内で Kerberos を実装すると複雑さが増すため、fuzzing では NTLM/guest を優先してください:

  • guest アクセスを許可し、map to guest = bad user を設定して未知のユーザーを GUEST にフォールバックさせる。
  • NTLMv2 を受け入れる(無効化されている場合はポリシーをパッチ)。これによりハンドシェイクが簡素化されつつ SMB3 のコードパスを動作させられます。
  • 実験時は厳格な credit チェックをパッチアウトするCVE-2024-50285 の後の強化で simultaneous-op の crediting が厳しくなりました)。そうしないと、レート制限によって fuzzed シーケンスが早期に拒否されることがあります。
  • 高スループットの fuzzing 時に早期拒否を避けるため、max connections を増やす(例: 65536

注意: これらの緩和設定は fuzzing を容易にするためのものであり、本番環境でこれらの設定を適用しないでください。


ステートフルハーネス:リソース抽出とリクエストの連鎖

SMB はステートフルです: 多くのリクエストが前のレスポンスで返される識別子に依存しますSessionId、TreeID、FileID ペアなど)。ハーネスはレスポンスをパースして同一プログラム内で ID を再利用し、深いハンドラに到達する必要があります(例: smb2_create → smb2_ioctl → smb2_close

Example snippet to process a response buffer (skipping the +4B NetBIOS PDU length) and cache IDs:

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

ヒント

  • 認証状態を共有する1つの fuzzer プロセスを維持するksmbds global/session tables を共有することで安定性とカバレッジが向上します。syzkaller は ops を async とマークして内部で再実行することで並行性を注入します。
  • Syzkallers experimental reset_acc_state は global state をリセットできますが、大幅なスローダウンを招く可能性があります。安定性を優先して fuzzing に集中してください。

文法駆動の SMB2 生成(有効な PDUs

Microsoft Open Specifications の SMB2 構造を fuzzer grammar に落とし込み、ジェネレータが構造的に有効な PDUs を生成して dispatchers と IOCTL handlers に系統的に到達するようにします。

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]

このスタイルは構造体のサイズやオフセットを正しく強制し、盲目的な変異に比べてカバレッジを劇的に改善します。


focus_areas を使った Directed Fuzzing

現在カバレッジが弱い特定の関数やファイルに重みを付けるために、syzkaller の実験的な focus_areas を使用します。例JSON:

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

これは ACLs を構築して smbacl.c の arithmetic/overflow パスにヒットさせるのに役立ちます。例えば、過大な dacloffset を持つ悪意のある Security Descriptor は integer-overflow を再現します。

再現ビルダー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)

ANYBLOBでカバレッジの停滞を打破する

syzkallers anyTypes (ANYBLOB/ANYRES) は、複雑な構造をジェネリックに変異する blob にまとめることを可能にします。公開されている SMB pcaps から新しいコーパスを生成し、ペイロードをあなたの pseudo-syscall例: syz_ksmbd_send_reqを呼ぶ syzkaller プログラムに変換します:

# Extract SMB payloads to JSON
# tshark -r smb2_dac_sample.pcap -Y "smb || smb2" -T json -e tcp.payload > packets.json
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)"
)

これにより探索が急速に開始され、例えば ksmbd_sessions_deregister のような箇所で UAFs を即座に誘発し、カバレッジを数パーセント向上させることがあります。


Sanitizers: Beyond KASAN

  • KASAN はヒープバグ (UAF/OOB) の主要な検出器のままです。
  • KCSAN はこのターゲットで誤検知や重大度の低い data races を報告することが多いです。
  • UBSAN/KUBSAN は、配列インデックスのセマンティクスのために KASAN が見逃す宣言された境界の誤りを検出できます。例:
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 UBSANs declared-bounds checks.


スループットと並列実行に関する注意

  • 単一の fuzzer プロセスshared auth/stateは ksmbd に対してかなり安定する傾向があり、syzkaller の内部 async executor により races/UAFs を依然として検出できます。
  • 複数の VMs を使うと、全体で数百の SMB コマンド/秒 に達することがあります。関数レベルのカバレッジは fs/smb/server の約 60% と smb2pdu.c の約 70% 程度が得られますが、状態遷移のカバレッジはこれらの指標では過小評価されがちです。

実用チェックリスト

  • ksmbd で durable handles、leases、multi-channel、oplocks、および VFS objects を有効にする。
  • guest と map-to-guest を許可し、NTLMv2 を受け入れる。credit limits をパッチで除去し、fuzzer の安定性のために max connections を引き上げる。
  • SessionId/TreeID/FileIDs をキャッシュする stateful harness を構築し、create → ioctl → close をチェーンする。
  • 構造的妥当性を保つために SMB2 PDUs 用の grammar を使用する。
  • focus_areas を使用してカバレッジが弱い関数(例: smbacl.c の smb_check_perm_dacl のようなパス)に重みを付ける。
  • 実際の pcaps から ANYBLOB をシードとして投入して停滞を打破し、シードを syz-db でパックして再利用する。
  • KASAN + UBSAN で実行し、UBSAN の declared-bounds レポートを慎重にトリアージする。

参考資料

{{#include ../../banners/hacktricks-training.md}}