mirror of
https://github.com/HackTricks-wiki/hacktricks.git
synced 2025-10-10 18:36:50 +00:00
220 lines
12 KiB
Markdown
220 lines
12 KiB
Markdown
# 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:
|
||
```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;
|
||
}
|
||
}
|
||
```
|
||
ヒント
|
||
- 認証/状態を共有する1つの fuzzer プロセスを維持する:ksmbd’s global/session tables を共有することで安定性とカバレッジが向上します。syzkaller は ops を async とマークして内部で再実行することで並行性を注入します。
|
||
- Syzkaller’s 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):
|
||
```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):
|
||
```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でカバレッジの停滞を打破する
|
||
syzkaller’s anyTypes (ANYBLOB/ANYRES) は、複雑な構造をジェネリックに変異する blob にまとめることを可能にします。公開されている SMB pcaps から新しいコーパスを生成し、ペイロードをあなたの pseudo-syscall(例: syz_ksmbd_send_req)を呼ぶ syzkaller プログラムに変換します:
|
||
```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)"
|
||
)
|
||
```
|
||
これにより探索が急速に開始され、例えば ksmbd_sessions_deregister のような箇所で UAFs を即座に誘発し、カバレッジを数パーセント向上させることがあります。
|
||
|
||
---
|
||
|
||
## Sanitizers: Beyond KASAN
|
||
- KASAN はヒープバグ (UAF/OOB) の主要な検出器のままです。
|
||
- KCSAN はこのターゲットで誤検知や重大度の低い data races を報告することが多いです。
|
||
- UBSAN/KUBSAN は、配列インデックスのセマンティクスのために KASAN が見逃す宣言された境界の誤りを検出できます。例:
|
||
```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.
|
||
|
||
---
|
||
|
||
## スループットと並列実行に関する注意
|
||
- 単一の 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 レポートを慎重にトリアージする。
|
||
|
||
---
|
||
|
||
## 参考資料
|
||
- 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
|
||
- Background reading: pwning.tech “Tickling ksmbd: fuzzing SMB in the Linux kernel”; Dongliang Mu’s syzkaller notes
|
||
|
||
{{#include ../../banners/hacktricks-training.md}}
|