TCP Stack Fingerprinting (TCP 协议栈指纹识别)

小鹅厂长
小鹅厂长
Lv.0
## 1. 概述 TCP Stack Fingerprinting 是通过分析 TCP/IP 协议栈实现差异来远程识别目标主机操作系统的技术。 RFC 标准在很多细节上只给出建议值或留有实现自由度,各 OS 内核做出了不同选择,这些差异构成了\"指纹\"。 指纹特征可以分为两个层面: - **显性特征 (How TCP Writes)** — 直接读取包头字段即可采集:TTL、Window Size、TCP Options 排列顺序等。就像看一个人的笔迹。 - **隐性特征 (How TCP Reads)** — 需要发送特定刺激流量后,从对端的应答模式、时序、状态变迁等侧面反应中推断。就像观察一个人怎么理解和回应你说的话。 隐性特征采集难度远高于显性特征,但区分度也更高,且更难伪装。 --- ## 2. 显性特征 — How TCP Writes 这些特征直接存在于包头中,被动抓包即可采集。 ### 2.1 IP 层 | 特征 | 说明 | OS 差异 | |------|------|---------| | **TTL** | 初始生存时间 | Linux/macOS=64, Windows=128, Solaris/Cisco=255 | | **IP ID** | 分片标识生成策略 | 递增(Windows) / 随机(现代Linux) / 全零(Linux+DF) | | **DF 位** | Don't Fragment | 大多数现代 OS 默认 DF=1 | | **TOS/DSCP** | 服务类型 | 通常为 0,SSH 等场景可能不同 | ### 2.2 TCP 层 | 特征 | 说明 | OS 差异 | |------|------|---------| | **初始窗口** | SYN 中的 Window 字段 | 各版本差异较大 | | **Window Scale** | 窗口缩放因子 | Linux=7, Windows=8, macOS=6 | | **MSS** | 最大报文段大小 | 以太网通常 1460,VPN/隧道更小 | | **Options 排列** | SYN 中选项的顺序 | 每个 OS 排列方式不同,最核心的显性指纹 | | **Timestamp** | TSval 递增频率 | Linux~1000Hz, Windows 不携带, OpenBSD 随机化 | **各 OS 的 SYN Options 签名:** ``` Linux: MSS → SACK → TS → NOP → WS Windows: MSS → NOP → WS → NOP → NOP → SACK (无 Timestamp) macOS: MSS → NOP → WS → NOP → NOP → TS → SACK → EOL FreeBSD: MSS → NOP → WS → SACK → TS OpenBSD: MSS → NOP → NOP → SACK → NOP → WS → NOP → NOP → TS ``` 这些特征的共同点:**一次抓包即可读取,无需交互**。对应的伪装也相对容易——改包头字段即可。 --- ## 3. 隐性特征 — How TCP Reads 这些特征反映的是 TCP 栈**接收、处理、响应**数据的内部行为逻辑。不能从单个包中直接读取,需要通过构造刺激流量并观察反应模式来推断。 ### 3.1 ACK 生成策略 TCP 收到数据后如何生成 ACK,各 OS 差异显著。 #### Delayed ACK 定时器 RFC 建议每收到 2 个满段数据或等待最多 200ms 后发送一个 ACK。但实际实现差异很大: | OS | Delayed ACK 超时 | 触发条件 | |----|-----------------|----------| | Linux | **40ms**(动态,基于 HZ) | 每 2 个满段或定时器到期 | | Windows | **200ms** | 每 2 个满段或定时器到期 | | macOS | **100ms** | 每 2 个满段或定时器到期 | | FreeBSD | **100ms** | 每 2 个满段或定时器到期 | > 采集方法:以精确间隔向目标发送单个数据段(确保不触发每 2 段规则),测量 ACK 返回延迟。Linux 的 40ms 与 Windows 的 200ms 差异极为明显。 #### ACK 聚合行为 (Stretch ACK) 当高速接收大量数据时,部分 OS 会违反\"每 2 段 ACK 一次\"的约定,将多个段合并为一个 ACK: - **Linux 4.x+**: 在高吞吐路径下可能每 4-8 个段才 ACK 一次 (GRO/LRO 影响) - **Windows**: 严格遵守每 2 段 ACK - **macOS**: 与 FreeBSD 类似,有轻微聚合 > 这个特征在高带宽连接中尤其明显。 #### Quick ACK 模式 Linux 在连接建立初期或检测到乱序时进入 Quick ACK 模式,暂时关闭 Delayed ACK,对每个包立即回复 ACK。其他 OS 没有这个机制或实现方式不同。 - **Linux**: 连接前 ~3-16 个包使用 Quick ACK,之后切换为 Delayed ACK - **Windows**: 无类似机制,从第一个包开始就是 Delayed ACK 策略 - **FreeBSD**: 有类似机制但触发条件不同 > 采集方法:在连接刚建立时快速发送多个小包,观察前 N 个 ACK 的延迟模式。Linux 的前几个 ACK 延迟接近 0,然后突变为 ~40ms。 ### 3.2 接收窗口管理 #### 窗口增长曲线 从初始窗口到稳态窗口的增长轨迹,不同 OS 的 buffer auto-tuning 策略不同: - **Linux**: 基于 `tcp_rmem` (min, default, max) 三元组,自动根据 RTT 和吞吐量动态扩展接收缓冲区。增长较激进,通常在几个 RTT 内快速拉升。 - **Windows**: 接收窗口自动调优 (Receive Window Auto-Tuning),增长曲线更保守,阶梯式增长。 - **macOS**: 类似 FreeBSD,增长速度介于 Linux 和 Windows 之间。 > 采集方法:持续向目标发送数据,记录每个 ACK 中通告的窗口值,绘制窗口增长曲线。 #### 窗口更新策略 (Window Update Heuristic) 当接收缓冲区被读取后,OS 需要决定何时发送窗口更新通告: - **Linux**: 当可用空间增长超过前一次通告窗口的某个比例(通常 >50%)时发送更新 - **Windows**: 更频繁地发送窗口更新 - **FreeBSD**: 类似 Linux 但阈值不同 #### Silly Window Syndrome (SWS) 防御 接收端的 SWS 防御决定了何时通告小窗口: - **Linux**: 严格遵守 Clark 算法——除非可用空间 >= min(MSS, buffer/2),否则通告窗口为 0 - **Windows**: 实现略有不同,阈值和恢复时机存在差异 #### Zero Window 与 Window Probe 响应 当接收缓冲区满时: - 各 OS 通告零窗口后,对发送端 Window Probe 的响应时机不同 - Linux 在缓冲区释放后几乎立即发送窗口更新 - Windows 可能延迟更久才发送 ### 3.3 乱序处理与 SACK 行为 #### 乱序容忍度 (Reordering Tolerance) 收到乱序包后发送 Duplicate ACK 之前的等待行为: - **Linux 4.x+**: 有 `tcp_reordering` 参数(默认 3),会动态调整对乱序的容忍度。收到乱序包后不会立即认为是丢包,而是等待一段时间。内核维护一个 reordering 估计值(rack_reo_wnd)。 - **Windows**: 乱序容忍度较低,更快触发 Duplicate ACK - **FreeBSD**: 类似早期 Linux,固定阈值 > 采集方法:向目标发送故意乱序的数据段(如发送 seq 1,3,2),观察 Duplicate ACK 的数量和时机。 #### SACK 块生成 当启用 SACK 时,对乱序段的 SACK 报告方式存在差异: | 特征 | Linux | Windows | macOS | |------|-------|---------|-------| | 最大 SACK 块数 | 4 (有 TS 时 3) | 4 | 4 | | SACK 块排序 | 最近收到的在前 | 最近收到的在前 | 最近收到的在前 | | D-SACK 支持 | 支持 (默认开启) | 支持 | 支持 | | D-SACK 触发 | 收到重复数据时 | 收到重复数据时 | 行为略有不同 | | SACK 块合并 | 积极合并相邻块 | 合并策略不同 | 类似 FreeBSD | > D-SACK (Duplicate SACK, RFC 2883) 的支持和行为差异是一个很好的隐性指纹。发送一个已经被确认过的数据段,观察对方是否返回 D-SACK 以及格式。 #### 重叠段处理 (Overlapping Segment Reassembly) 当收到的 TCP 段与已有数据重叠时,是保留旧数据还是用新数据覆盖: - **Linux**: 倾向于**保留旧数据**(first-come policy) - **Windows**: 倾向于**用新数据覆盖**(last-come policy) - **macOS/FreeBSD**: 保留旧数据 > 这个差异在 IDS 规避中被广泛利用(Ptacek & Newsham 1998),也是区分 OS 的有效手段。 ### 3.4 拥塞控制的接收侧可观测行为 虽然拥塞控制运行在发送端,但接收端的行为间接影响发送端的拥塞判断,并且部分拥塞相关行为在接收端也有差异。 #### ECN 处理 ECN (Explicit Congestion Notification) 的支持和行为: | OS | ECN 协商 | 收到 CE 标记的响应 | |----|---------|-------------------| | Linux | SYN 中默认不设置 ECE/CWR(可通过 sysctl 开启) | 在 ACK 中设置 ECE 标志,直到收到 CWR | | Windows 10+ | 默认不协商 ECN | 行为类似 Linux | | macOS | 默认协商 ECN | 接收处理中持续回复 ECE | | FreeBSD | 可配置 | 类似 Linux | > macOS 默认开启 ECN 协商本身就是一个区分特征。 #### 对拥塞信号的 ACK 时序 发送端进入拥塞后减速,接收端的 ACK 时序(inter-ACK gap)反映了发送端的拥塞控制行为,可以间接推断发送端的拥塞算法: - **CUBIC**: 丢包后窗口降为原来的 0.7 倍,恢复曲线呈三次函数形 - **BBR**: 不依赖丢包,基于带宽估计,ACK 间隔更均匀 - **NewReno**: 丢包后窗口减半,恢复更慢 ### 3.5 TCP 状态机边缘行为 #### TIME_WAIT 持续时间 连接关闭后 TIME_WAIT 状态的持续时间: | OS | TIME_WAIT 持续 | 可配置性 | |----|---------------|----------| | Linux | 60 秒 (硬编码 `TCP_TIMEWAIT_LEN`) | 不可直接调整(除非改内核) | | Windows | 120 秒 | 注册表可改 `TcpTimedWaitDelay` | | macOS | 15 秒 | sysctl `net.inet.tcp.msl` | | FreeBSD | 60 秒 | sysctl 可调 | > 采集方法:与目标建立连接后主动关闭,然后尝试复用相同四元组重新连接,测量被拒绝(RST)的时间窗口。 #### FIN_WAIT / CLOSE_WAIT 行为 - Linux 有 `tcp_fin_timeout`(默认 60 秒)控制 FIN_WAIT_2 超时 - Windows 类似但超时值不同 - 一些 OS 在 FIN_WAIT_1 超时后直接发 RST,另一些静默丢弃 #### Simultaneous Close(双方同时关闭) RFC 793 定义了 simultaneous close 路径,但实现差异很大: - **Linux**: 正确处理 simultaneous close,进入 CLOSING 状态 - **Windows**: 早期版本有 bug,可能发送 RST - 通过构造 simultaneous close 场景观察行为差异 #### RST 生成细节 不同 OS 在何种条件下生成 RST,以及 RST 包本身的特征: | 特征 | Linux | Windows | |------|-------|---------| | RST 窗口字段 | 0 | 非零(当前窗口值) | | RST 序列号 | 对方期望的 ACK 号 | 可能使用当前 SEQ | | 带 ACK 的 RST | 某些场景会 RST+ACK | 更频繁使用 RST+ACK | | RST rate limiting | 有限速机制 | 无明显限速 | ### 3.6 重传行为 这是隐性特征中信息量最大的一类。 #### 初始 RTO 与退避策略 | OS | 初始 RTO | SYN 重传 | 数据重传上限 | |----|---------|---------|-------------| | Linux | 1s | 1, 2, 4, 8, 16, 32s(最多 6 次) | 15 次 | | Windows (新) | 1s | 1, 2 秒(最多 2 次) | 5 次 | | Windows (旧) | 3s | 3, 6, 12 秒 | 5 次 | | macOS | 1s | 1, 2, 4, 8, 16, 32 秒 | 12 次 | | FreeBSD | 1s | 类似 Linux | 12 次 | > Windows SYN 只重传 2 次就放弃的行为是一个非常显著的特征。 #### 快速重传 / 快速恢复 - **Linux**: 收到 3 个 Duplicate ACK 后进入快速重传 + SACK-based recovery - **Windows**: 类似但恢复算法有差异 - **Linux RACK**: 基于时间而非计数判断丢包(4.x+ 默认) - **Linux TLP (Tail Loss Probe)**: 在 RTO 到期前发送尾部探测包,减少尾部延迟 > TLP 是 Linux 独有的特征。采集方法:观察在应该 RTO 超时之前是否有探测性重传。 #### 伪重传检测 (Spurious Retransmission Detection) - **Linux**: 支持 F-RTO (Forward RTO Recovery),RTO 超时后不立即减窗,先探测是否为伪超时 - **Windows**: 有类似机制但触发条件不同 - **Linux**: 还支持 Eifel Detection (基于 Timestamp) ### 3.7 流量控制的微观行为 #### Nagle 算法与交互行为 各 OS 的 Nagle 算法实现虽然目标一致(避免小包),但边界条件不同: - **Linux**: 有 `TCP_CORK` 选项,可以更精细地控制合并 - **Windows**: 有 `SIO_TCP_SET_ACK_FREQUENCY` 可调 ACK 频率 - Nagle + Delayed ACK 的交互延迟在不同 OS 上表现不同(著名的 Nagle-delayed-ACK 问题) #### PSH (Push) 标志的使用 - **Linux**: 在发送缓冲区中最后一个段上设置 PSH - **Windows**: 几乎每个段都设置 PSH - **macOS**: 类似 Linux > 这是一个容易被忽略但区分度很高的特征。 #### Keep-Alive 行为 | OS | 默认间隔 | 探测次数 | 探测间隔 | |----|---------|---------|---------| | Linux | 7200s (2h) | 9 次 | 75s | | Windows | 7200s (2h) | 10 次 | 1s | | macOS | 7200s (2h) | 8 次 | 75s | > Windows 的 Keep-Alive 探测间隔只有 1 秒,远小于其他 OS 的 75 秒,非常显眼。 ### 3.8 TCP 时序指纹 #### Timestamp 时钟频率 通过多个包的 TSval 差值与实际时间差的比值推断时钟频率: | OS | TSval 频率 | 特点 | |----|-----------|------| | Linux 2.6+ | 1000 Hz (HZ=1000) | 精确递增,可推算 uptime | | Linux (旧) | 100-250 Hz | 取决于内核编译 HZ | | macOS | ~1000 Hz | 类似 Linux | | FreeBSD | ~1000 Hz | 类似 Linux | | OpenBSD | 随机化 | 故意防指纹 | | Windows | N/A | 不携带 Timestamp | #### 时钟偏移 (Clock Skew) 通过长期观测 TSval 增长与真实时间的微小漂移,可以: - 唯一标识同一 NAT 后的不同主机(即使 IP 相同) - 区分虚拟机 vs 物理机(虚拟机时钟漂移更大) - 检测代理——代理前后的 Timestamp 不连续 #### 包间时序 (Inter-Packet Timing) - **中断合并 (Interrupt Coalescing)**: 不同网卡驱动和 OS 的中断合并策略导致微秒级时序差异 - **调度器影响**: 不同 OS 的任务调度器对网络栈处理的优先级不同,反映在应答延迟的抖动分布上 - **Idle 后的首包延迟**: 连接空闲一段时间后发送新数据时的处理延迟各不相同 --- ## 4. 隐性特征的采集方法论 ### 4.1 主动探测 (Active Probing) 向目标构造特定模式的流量,观察响应: | 探测类型 | 目的 | 方法 | |---------|------|------| | **单段延迟** | 测 Delayed ACK 定时器 | 发送单段数据,测 ACK 延迟 | | **双段间隔** | 测 Quick ACK vs Delayed ACK | 连续发 2 段,观察 ACK 行为 | | **乱序注入** | 测乱序容忍度和 SACK | 发送 seq 1,3,2,观察 DupACK 和 SACK 块 | | **重复段** | 测 D-SACK | 重发已确认数据,检查是否有 D-SACK | | **重叠段** | 测重叠段处理 | 发送重叠数据段,比较最终接收到的内容 | | **突发灌入** | 测窗口增长曲线 | 高速发送大量数据,跟踪窗口通告变化 | | **连接关闭计时** | 测 TIME_WAIT | 关闭后尝试复用四元组 | | **零窗口** | 测窗口探测响应 | 灌满接收缓冲,观察零窗口后的恢复 | | **ECN 协商** | 测 ECN 支持 | SYN 中设置 ECE+CWR,观察 SYN-ACK | | **丢包模拟** | 测重传策略 | 静默丢弃部分包,观察重传间隔序列 | ### 4.2 被动推断 (Passive Inference) 在代理场景下,仅通过观察客户端正常流量推断部分隐性特征: | 可观测量 | 推断内容 | |---------|---------| | 客户端 ACK 的 inter-arrival 时间 | Delayed ACK 定时器 | | 窗口通告的变化梯度 | Buffer auto-tuning 策略 | | SACK 块的出现频率和格式 | SACK 实现差异 | | PSH 标志的使用频率 | 发送端 OS | | Keep-Alive 探测间隔 | OS 类型(1s vs 75s) | | TSval 递增速率 | 时钟频率 | | 重传间隔序列 | RTO 算法和初始值 | | Quick ACK 到 Delayed ACK 的切换点 | Linux vs 其他 | ### 4.3 采集难点 | 难点 | 原因 | |------|------| | **需要有状态跟踪** | 不是看单个包,而是看包与包之间的关系 | | **时序精度要求高** | Delayed ACK 差异在毫秒级,需要高精度时钟 | | **需要足够样本** | 单次观测可能受抖动影响,需要统计分析 | | **中间设备干扰** | NAT、负载均衡、TCP 加速器可能改变行为 | | **应用层影响** | 应用的读取模式影响 ACK 和窗口行为 | | **难以伪装但也难以还原** | 这些行为深嵌内核,既难以在代理中模拟,也难以被检测方精确测量 | --- ## 5. 跨层指纹关联 真正强大的检测不仅看单层,而是检查**多层之间的一致性**。 ### 5.1 指纹层次模型 ``` Layer 7 应用层 HTTP User-Agent / Accept-Language / Feature API Layer 6 表示层 TLS ClientHello (JA3/JA4) / ALPN / Certificate Layer 5 会话层 HTTP/2 SETTINGS / QUIC Transport Parameters Layer 4 传输层 TCP Options / Window / 重传行为 / ACK 模式 Layer 3 网络层 TTL / IP ID / DF / TOS Layer 2 链路层 MTU 推断 ``` 检测方会检查各层指纹是否自洽。例如: | 矛盾 | 暴露信息 | |------|---------| | TCP 指纹 = Linux,User-Agent = Windows Chrome | 代理/模拟器 | | TTL = 64 (Linux),TLS = Windows 特有密码套件 | 代理 | | MSS = 1400 (隧道典型值),其他一切正常 | VPN/隧道封装 | | Timestamp 不连续/突变 | 代理转发,不是直连 | | HTTP/2 SETTINGS = Chrome,TLS 指纹 = curl | 使用了自定义 TLS 库的爬虫 | | TCP 行为全部像 Linux,但 ACK 延迟 = 200ms | 用户态代理在 Linux 上伪装 Windows 但没控制 ACK 行为 | ### 5.2 TLS 指纹 (JA3 / JA4) ClientHello 中的特征: - **Cipher Suites 列表及顺序**: 不同浏览器/运行时的组合不同 - **Extensions 列表及顺序**: SNI、ALPN、supported_groups 等 - **Supported Groups (椭圆曲线)**: x25519、secp256r1 的优先级 - **Signature Algorithms**: 签名算法偏好 JA4 是 JA3 的改进版,增加了协议类型、SNI 状态等维度。 ### 5.3 HTTP/2 指纹 (Akamai Fingerprint) HTTP/2 连接的 SETTINGS 帧和 WINDOW_UPDATE 帧暴露指纹: - **SETTINGS 参数及顺序**: HEADER_TABLE_SIZE、MAX_CONCURRENT_STREAMS、INITIAL_WINDOW_SIZE 等 - **WINDOW_UPDATE 初始值** - **Priority 帧的使用方式** - 不同浏览器的 HTTP/2 指纹差异非常大,比 TCP 层更容易区分具体应用 --- ## 6. 伪装层级与可行性 ### 6.1 可控性分层 ``` Level 1 — 用户态 setsockopt(你的项目当前位置) ├── TTL / 缓冲区大小 ├── 覆盖: 显性特征 ~30% └── 隐性特征: 几乎无法控制 Level 2 — sysctl 全局参数 ├── + tcp_rmem / tcp_wmem / tcp_fin_timeout 等 ├── 覆盖: 显性 ~50%, 隐性 ~10% └── 问题: 全局生效,无法按连接区分 Level 3 — eBPF / TC ├── + 出站包 TCP Options 重排 / Timestamp 覆写 / IP ID 修改 ├── + 部分 ACK 行为调整 (通过 BPF_PROG_TYPE_SOCK_OPS) ├── 覆盖: 显性 ~80%, 隐性 ~30% └── 限制: 只能修改出站,无法改变内核接收逻辑 Level 4 — 用户态协议栈 (如 lwIP / smoltcp) ├── 完全绕过内核 TCP 栈,自行实现所有行为 ├── 覆盖: 显性 ~95%, 隐性 ~90% └── 代价: 性能大幅下降,实现复杂度极高 Level 5 — 内核模块 / 修改内核 ├── 直接修改内核 TCP 状态机 ├── 覆盖: ~99% └── 代价: 需要目标内核版本适配,维护成本极高 ``` ### 6.2 隐性特征的伪装困境 隐性特征之所以难伪装,根本原因在于它们是**内核 TCP 状态机在处理接收数据时的副产物**: - Delayed ACK 定时器 → 内核 timer 子系统硬编码 - 窗口增长曲线 → 内核 buffer auto-tuning 算法 - SACK 块生成 → 内核 TCP 接收路径 `tcp_data_queue()` 内部逻辑 - 重传策略 → 内核 `tcp_retransmit_timer()` 实现 - Quick ACK → 内核 `tcp_enter_quickack_mode()` 实现 这些行为分散在内核 TCP 栈的各个角落,不暴露任何用户态调整接口。你在用户态只能看到最终结果(通过 socket 读取数据),无法干预内核如何生成 ACK、如何管理接收窗口。 ### 6.3 不可伪装的残余 即使做到 Level 5,仍有部分维度可能暴露: - **微秒级时序**: 代理引入的转发延迟在统计上可检测 - **MTU 不一致**: 隧道封装导致的 MSS 减小 - **跨层矛盾**: TCP 说 Windows 但 ACK 行为像 Linux - **Timestamp 不连续**: 代理转发导致 TSval 序列中断 - **流量模式**: 代理的连接复用与直连的差异 - **地理/ASN 矛盾**: IP 位置与 OS/浏览器使用习惯不匹配 --- ## 7. 工具与参考 | 工具 | 类型 | 关注层面 | |------|------|---------| | **p0f v3** | 被动 | 显性 TCP/IP 特征 | | **Nmap** | 主动 | 显性 + 部分隐性(异常包响应) | | **JA3/JA4** | 被动 (TLS) | TLS 层指纹 | | **FATT** | 被动 | 多协议指纹聚合 | ### 参考资料 - RFC 793 — Transmission Control Protocol - RFC 7323 — TCP Extensions for High Performance (Window Scale, Timestamp) - RFC 2018 — TCP Selective Acknowledgment Options - RFC 2883 — An Extension to SACK (D-SACK) - RFC 3168 — ECN (Explicit Congestion Notification) - RFC 5765 — Forward RTO-Recovery (F-RTO) - Ptacek & Newsham, 1998 — *Insertion, Evasion, and Denial of Service: Eluding Network Intrusion Detection* - p0f v3 — https://lcamtuf.coredump.cx/p0f3/ - Nmap OS Detection — https://nmap.org/book/osdetect.html - JA3 — https://github.com/salesforce/ja3 - JA4+ — https://github.com/FoxIO-LLC/ja4
0 条回复
暂无回复,快来抢沙发吧~
发表回复

登录后可参与讨论