
简介这份文档面向网络安全初学者与无线网络运维人员围绕无线路由器 WPS PIN 码算法被破译这一真实案例展开帮助读者理解无线信号嗅探、MAC 地址与 PIN 码之间的换算关系以及此类漏洞的成因与防范思路属于入门到进阶之间的实战科普型材料。压缩包内共 1 个 docx 文件约 191KB以图文混排的讲解文档形式呈现便于在电脑上阅读与做标注篇幅精简适合作为一次专题学习的入口。该资源已有 1185 人学习下载具备一定的参考热度。内容按扫描周边无线信号、借 MAC 地址换算十六进制到十进制得出六位 PIN、再逐位猜测最后一位的完整流程组织并点明受影响的主要是特定 MAC 前缀的设备后半部分则给出关闭 WPS 功能、启用 WEP 与 WPA 加密等防护措施兼顾攻击原理与安全加固便于读者对照自查自家路由器的配置风险。1. 从路由器背面那张贴纸说起一个 8 位数字埋下的暗门拆过几十台家用路由器之后我发现一个反直觉的事实大部分人都改过 Wi-Fi 密码、换过 SSID、甚至刷过第三方固件却从来没登录过 WPS 那一栏。而无线路由器 PIN 码被破译这条链路问题根本不在 WPA2 加解密本身而在 WPS 这个「快速配网」的旁路——它拿一个 8 位数字当身份凭证这个数字在部分固件里直接由 MAC 地址算出来贴上机身背面等于把钥匙挂在门把手上。这篇笔记讨论的是网络安全性评估里的一个经典样本WPS PIN 的校验结构、MAC 派生规则、三条常见攻击面的成本核算以及关掉 WPS 之后怎么验证真的关掉了。适合做内网安全自查的运维、家里有一堆 IoT 设备要管的极客以及想搞清楚「两小时破译」这个说法到底在什么条件下成立的人。2. PIN 的校验位与 MAC 派生8 位里真正随机的只有 7 位先把一件事说清楚WPS 的 PIN 不是一串普通的 8 位密码它自带一个校验位还带着一个分段验证的协议缺陷。这两件事叠在一起才让暴力破解从「理论上不可行」变成「一台普通笔记本跑一晚上」。2.1 八位 PIN 的骨架前 7 位随机第 8 位校验WPS PIN 固定 8 位十进制数字最后一位是前 7 位的校验码算法用的是加权和取模奇数位权重 3、偶数位权重 1。设备收到 PIN 后先算校验校验不过直接拒绝连比对都不做。所以真正需要猜的只有 7 位。写个几行的函数就能把这条规则固化下来平时自检、核对固件算得对不对都用得上# wps_pin_check.py # 计算 WPS 8 位 PIN 的最后一位校验码并判断一个 8 位串是否自洽 def wps_checksum(pin7: str) - int: pin7 为前 7 位数字字符串返回第 8 位校验码 digits [int(c) for c in pin7] # 第 1/3/5/7 位权重 3第 2/4/6 位权重 1下标从 0 开始 acc sum(d * (3 if i % 2 0 else 1) for i, d in enumerate(digits)) return acc % 10 def is_valid_pin(pin8: str) - bool: return ( len(pin8) 8 and pin8.isdigit() and wps_checksum(pin8[:7]) int(pin8[7]) ) if __name__ __main__: # 给几个 7 位前缀补齐第 8 位再看是否自洽 for p in [3509360, 1234567, 0000000]: full p str(wps_checksum(p)) print(f{p} - {full} valid{is_valid_pin(full)})wps_checksum里唯一的坑是权重顺序不少人写成偶数位 3、奇数位 1算出来的 PIN 长度对、格式对设备就是不认然后怀疑工具、怀疑网卡、怀疑玄学其实是一个下标搞反了。is_valid_pin只是在本地做格式自洽检查不产生任何网络流量用来验证「我的实现和设备的算法是不是同一套」最省事——拿贴纸上印着的真 PIN 跑一遍能过就说明实现对上了。2.2 从 10^8 到 1.1×10^4分段验证砍掉了四个数量级WPS 的配网流程走 EAP-WSC双方要交换 M1 到 M8 一共八条消息其中 PIN 是拆成两段验证的先比前 4 位正确了才比后 4 位。这个设计本来是为了让用户在输入时能得到早期反馈但它同时给了攻击者一个天然的 oracle——设备会明确告诉你「前半段对了」。于是搜索空间变成两截前半段 4 位最坏 10^4 10000 次后半段 3 位有效数字加 1 位校验1000 次合计约 11000 次而不是 10^8。这就是「两小时」这个数字的来源之一。它成立的前提是设备对失败尝试不做任何惩罚每秒稳定跑一次11000 次差不多三个多小时如果实现优化到位、能一秒两次两小时出头。但凡设备有锁死策略这个时间就会被拉长几个数量级后面第 4 章会把账算清楚。注意这套流程只在自己名下或书面授权范围内的设备上跑。别人家的 AP 扫一眼信号强度不算什么往里面灌 PIN 就是另一回事了。2.3 MAC 派生 PIN三个字节的信息量比协议缺陷更狠的是实现缺陷。有一部分固件为了生产线省事不用随机数生成 PIN而是直接用 MAC 地址的后三个字节算把 6 位十六进制转成十进制补足 7 位最后一位用 2.1 的校验算法补上整串 PIN 就出来了。这类设备的 PIN 不是「猜到」的是「算出来」的耗时以毫秒计。下面这个脚本是核对自己设备用的输入 MAC看它是否落在已知的弱算法段里。# mac_to_pin.py # 仅用于核对自己名下设备的 PIN 是否可由 MAC 直接推导 from wps_pin_check import wps_checksum # 已知的弱算法 OUI 前缀实际清单远比这长这里只列两个代表 VARIANTS { C83A35: {offset: 3, pad: 7}, 00B00C: {offset: 3, pad: 7}, } def mac_to_pin(mac: str) - str: hexs mac.replace(:, ).replace(-, ).upper() if len(hexs) ! 12: raise ValueError(MAC 格式不对需要 6 组十六进制) oui hexs[:6] rule VARIANTS.get(oui) if rule is None: raise ValueError(f{oui} 不在已知弱算法段内PIN 大概率是出厂随机值) off rule[offset] * 2 nic int(hexs[off:off 6], 16) # 取 MAC 后三个字节 raw str(nic).zfill(rule[pad]) pin7 raw[-7:] # 超出 7 位时取低位固件变体常见做法 return pin7 str(wps_checksum(pin7)) if __name__ __main__: print(mac_to_pin(00:B0:0C:05:5A:D8)) # 055AD8 350936offset指从第几个字节开始取pad指补零到几位。raw[-7:]这一步是有意为之三字节最大 16777215转十进制是 8 位而 PIN 前缀只有 7 位不同固件对超出部分的处理不一样取低位是最常见的一种。跑出来是350936加校验位正好对应论文里反复被引用的那个例子。提示脚本跑出结果不等于设备真的用这个 PIN。判断标准只有一条——拿贴纸上印的 PIN 和脚本结果比对一致才算命中。2.4 变体识别为什么同一个脚本在两台设备上结果不同同一批芯片、不同固件版本算法细节能差出好几种有的取前三个字节而不是后三个有的先做一次累加再做十进制转换有的补零补到 6 位而不是 7 位还有的在十进制结果上再加一个偏移常数。公开资料里统计过二十来种变体所以「一个脚本通吃」是不现实的。我一般按这个顺序确认先查 OUI 是否在已知弱算法清单里不在就直接放弃 MAC 派生这条路在清单里就按变体表逐个试每个变体算出来的 PIN 用校验位先筛一遍校验都不过的直接排除剩下能过校验的拿自己设备的真 PIN 对一次对上就锁定变体。这个过程不涉及任何网络交互纯本地计算把「要不要继续」的判断成本压到最低。真正麻烦的是第 3 步之后——即便算对了 PIN能不能连上还取决于设备有没有开 WPS、有没有被锁、信号够不够。这些是第 3 章要解决的问题。3. 自建审计环境监听口、扫描快照与一份可复跑的判定脚本工具链的选择上我倾向于用系统自带的iw和wpa_cli而不是一上来就装一套专用审计套件。原因很实际前者随发行版就有输出格式稳定出了问题能查到源码后者封装太厚一旦抓不到包日志里全是黑匣子排错成本反而更高。等基础链路跑通了再上专用工具提效也不迟。3.1 监听口配置网卡、驱动与 monitor 模式第一步不是抓包是确认网卡到底支不支持监听。很多 USB 网卡的驱动在 monitor 模式下会丢管理帧表现就是「明明信号满格就是一帧都抓不到」。# 1. 看网卡支持哪些接口模式输出里要有 monitor iw list | grep -A 8 Supported interface modes # 2. 确认当前网卡名和状态 ip link show # 3. 在物理网卡上挂一个监听接口不破坏原来的 managed 接口 sudo iw dev wlan0 interface add mon0 type monitor sudo ip link set mon0 up # 4. 锁定信道并强制 20MHz 带宽 sudo iw dev mon0 set channel 6 HT20 # 5. 验证 iw dev mon0 info关键在于第 4 步。2.4GHz 上 40MHz 带宽会让管理帧落在扩展信道上抓包时看起来「信道对上了但抓不到 beacon」这个坑我踩过不止一次。set channel 6 HT20把带宽压回 20MHz同时锁定主信道后面的抓包才稳定。如果目标 AP 在 5GHz把信道号换掉即可但注意 5GHz 的 WPS 实现比例低很多实际排查优先看 2.4GHz。iw dev wlan0 interface add创建的是虚拟接口删掉用iw dev mon0 del不影响原网卡上网。有些驱动不支持多接口并存那就得先把wlan0down 掉再加代价是这台机器暂时断网建议在独立设备上做。3.2 扫描快照用 wpa_cli 拿到带 [WPS] 标记的列表iw dev wlan0 scan的输出信息量大但没有 WPS 标记判断起来很费劲。wpa_cli的scan_results会把 capability flags 直接打出来里面就有[WPS]一行一个 AP处理起来清爽得多。# 触发一次扫描等 3 到 5 秒让结果回填 sudo wpa_cli -i wlan0 scan sleep 4 # 拉取结果bssid / 频点 / 信号 / 能力标志 / SSID sudo wpa_cli -i wlan0 scan_results | tee scan_results.txt输出大致是制表符分隔的五列第四列形如[WPA2-PSK-CCMP][WPS][ESS]。第四列里有[WPS]就说明这个 AP 的 beacon 里带了 WPS IE功能处于开启状态没有就说明关掉了或者这台设备压根不支持。这一列是整个排查里最有价值的信息比 SSID、信号强度都重要。需要更细的字段WPS state、是否 locked、支持的配置方式时就得看原始 IE。用抓包工具在监听口上抓 beacon按 vendor specific 字段过滤# 抓 30 秒 beacon只保留 vendor IE 相关字段 sudo tshark -i mon0 -a duration:30 \ -Y wlan.fixed.beacon \ -T fields -e wlan.bssid -e wlan.tag.vendor.oui -e wlan.tag.vendor.data \ vendor_ie.tsv不同版本的字段名可能有差异先跑tshark -G fields | grep -i vendor确认一下再改命令别硬套。3.3 判定脚本把扫描结果变成一张风险表拿到scan_results.txt之后人工一行行看太慢写个脚本按规则打分更靠谱。下面这段只做静态解析不发起任何连接。# triage.py —— 解析 wpa_cli scan_results 输出输出风险初判 import sys def parse(path): rows [] for line in open(path, encodingutf-8, errorsignore): parts line.rstrip(\n).split(\t) if len(parts) 5: continue bssid, freq, sig, flags, ssid parts[:5] rows.append({ bssid: bssid, freq: freq, signal: int(sig), flags: flags, ssid: ssid, wps: [WPS] in flags, wpa3: WPA3 in flags, open: [ESS] in flags and PSK not in flags and WPA not in flags, }) return rows def score(r): 分数越高越需要处理纯经验值按自己环境调 s 0 if r[wps]: s 50 # WPS 开着就是主风险项 if r[open]: s 40 # 完全开放 if r[signal] -60: s 10 # 信号强说明物理距离近 if not r[wpa3]: s 5 return s if __name__ __main__: data parse(sys.argv[1]) data.sort(keyscore, reverseTrue) for r in data: print(f{score(r):3d} {r[bssid]} {r[signal]:4d}dBm fWPS{Y if r[wps] else N} {r[ssid]})score里的权重是我按经验拍的不是标准。它的作用不是给出精确评级而是把几十上百个 AP 排个序让你先看最该看的那几台。signal -60这一条对应的现实是能打到 -60dBm 以内的 AP物理上大概率就在隔壁或者同一层属于「值得确认是不是自己的」。3.4 记录表设计四个字段决定要不要继续扫描完之后别急着动手先落一张表。我习惯只留四个字段多了反而没人填字段取值来源判定含义BSSID扫描结果第一列用于反查 OUI判断是否落在弱算法段WPS 标志flags 列是否含[WPS]为 N 则整条链路直接终止WPS state原始 IE 的 0x1044 属性2 表示已配置过1 表示未配置更容易被接管lock 标志原始 IE 的 0x1057 属性置位说明设备已被锁此时任何尝试都是白费第三、四行的字段不是每次都能拿到拿不到就填「未知」别猜。state为 1 的设备风险明显更高——它意味着这台机器还没被任何人用 WPS 配过处在出厂状态。locked置位的设备说明之前有人试过并且失败了这时候继续操作只会让锁定时间越来越长。4. 三条攻击面的成本核算穷举速率、PBC 窗口与弱随机数把「能不能破」换成「要花多少代价」判断就清楚多了。WPS 这条线上实际存在三条路成本差着好几个数量级防御动作也完全不同。4.1 PIN 穷举速率与锁死策略决定真实成本理论上的 11000 次只算了搜索空间没算设备给不给机会。真实成本完全由固件的锁定策略决定粗略分四档锁定策略单次耗时11000 次总耗时现实可行性无任何限制1 秒约 3 小时可行两小时这个数量级成立失败 1 次锁 60 秒61 秒约 7.8 天勉强需长时间在线失败 1 次锁 300 秒301 秒约 38 天基本不可行累计 5 次后永久锁—需物理重启才能恢复不可行这张表解释了一个常见误解网上流传的「两小时破解」几乎都拍摄于无锁死策略的早期固件或者干脆是 MAC 派生那条路——后者压根不需要穷举。新一点的固件普遍加了锁同样的工具跑上去第一天毫无进展是常态。还有一种半锁死前几次失败后设备只是延迟响应不明确报错。这种最难判断因为从工具侧看是「还在跑」实际上早就被丢包了。判断方法是看每次尝试的响应时间是否在递增递增就说明触发了限速及时停手别把设备彻底锁死——锁死之后连合法用户都配不上网这是最尴尬的翻车现场。4.2 PBC 按键配对物理接近才是唯一门槛PBCPush Button Configuration是 WPS 的另一种模式按一下路由器上的物理按钮两分钟内任何发起配网的设备都能拿到密码不需要 PIN。它的安全性完全建立在「物理接触」这个前提上。风险场景很具体路由器放在工位隔断上、前台桌上、出租屋客厅任何人走过去按一下复位键旁边的 WPS 键两分钟窗口期内就能拿到完整口令。更麻烦的是很多设备的 WPS 键和复位键挨得很近误触之后自己都不知道窗口已经开了。检测方法简单到不像话——看设备面板上有没有那个带锁形或双向箭头图标的物理按键再看 Web 管理页面里 PBC 是否可关闭。能关就关关不掉就把设备挪到需要刷卡才能进的位置。这一条没有任何技术对抗空间纯物理安全。4.3 弱随机数一次握手暴露的熵第三条路针对的是 PIN 生成时的随机源。部分固件用时间戳、启动秒数或者 MAC 做种子熵低到几千种可能。这类设备的特点是PIN 看起来是随机的但重启之后生成的 internal registrar 密钥和 nonce 会重复出现。判断方法不需要抓完整的握手流程只要在监听口上抓几轮 M1/M2比较不同会话里的随机数字段。如果两台不同时间启动的设备出现了相同的 nonce或者同一台设备重启后 nonce 只在小范围里变化这个固件的随机源就有问题。# entropy_check.py —— 粗算一段 nonce 样本的经验熵判断随机源是否可疑 import math from collections import Counter def entropy(samples): samples: 十六进制字符串列表返回每字符经验熵bit joined .join(samples) if not joined: return 0.0 cnt Counter(joined) total len(joined) return -sum((c / total) * math.log2(c / total) for c in cnt.values()) if __name__ __main__: # 从抓包结果里手工摘出来的 nonce 片段实际使用时替换 samples [a3f1, a3f2, a3f1, a3f4, a3f2, a3f1] h entropy(samples) print(f经验熵 {h:.2f} bit/字符) print(低于 3.0 建议怀疑随机源 if h 3.0 else 样本熵正常)entropy算的是单字符经验熵样本量小的时候偏差很大它只能当粗筛不能当结论。真正的判断还是要看 nonce 是否跨设备重复——重复就是硬证据熵值只是辅助。这个脚本的价值在于把「感觉不太对」变成「有个数字可以对比」方便在不同固件版本之间做横向记录。4.4 成本核算脚本与决策表把 4.1 到 4.3 的结论拼起来就是一张决策表。我一般在扫描完成后直接用脚本估一遍避免凭感觉决定要不要继续投入时间。#!/usr/bin/env bash # cost_estimate.sh —— 按观测到的锁定策略估算穷举总耗时 ATTEMPTS11000 # 44 分段验证后的搜索空间 LOCK_SECONDS${1:-0} # 每次失败后的锁定时长0 表示无锁定 PER_TRY${2:-1} # 单次尝试耗时秒 TOTAL$(( ATTEMPTS * (PER_TRY LOCK_SECONDS) )) printf 总耗时约 %d 秒合 %.1f 小时 / %.1f 天\n \ $TOTAL $(echo $TOTAL/3600 | bc -l) $(echo $TOTAL/86400 | bc -l)第一个参数填观测到的锁定秒数第二个参数填单次往返耗时。跑./cost_estimate.sh 60 1得到约 7.8 天跟表里对得上说明模型没写错。这个脚本的用途不是鼓励去跑而是在评估阶段就给出「不值得」的结论把时间省下来做加固。注意三条路径里只有 4.2 和 4.3 有明确的防御动作关 PBC、升级固件4.1 的防御动作只有一个——把 WPS 整个关掉。原因很简单只要功能开着锁定策略就是唯一的门槛而门槛高低不由你决定。5. 避坑与排查五条踩过的坑和对应解法这一段是纯经验每条都按「现象、原因、解决」写能对上就直接抄。5.1 算出 PIN 了但设备不认现象脚本输出了 8 位 PIN格式、校验位全对但配网流程走到 M5 就断设备返回失败。原因绝大多数情况是 OUI 不在弱算法段里。很多固件早就改成随机 PIN 了MAC 和 PIN 之间没有任何函数关系硬算出来的只是一个「格式合法的随机数」。少数情况是变体选错补位或偏移常数不对。解决先拿机身贴纸上印的真 PIN 跑一遍is_valid_pin能过说明算法实现对问题在变体过不了说明校验实现本身就错了回去查权重顺序。确认变体之后仍然对不上就直接判定这台设备不是 MAC 派生别再耗时间。5.2 监听口抓不到完整的握手现象mon0起来了iw dev mon0 info显示 monitor但抓包文件里只有 beacon一条 EAP 消息都没有。原因九成是信道或带宽不匹配。目标 AP 在 6 信道监听口停在 1 信道或者 AP 开了 40MHz管理帧跑到扩展信道上去了。剩下的一成是网卡驱动在 monitor 模式下过滤了管理帧。解决先iw dev mon0 set channel 6 HT20显式锁定信道和带宽重抓一次。还不行就换网卡优先选芯片方案成熟、社区反馈多的型号别买那种便宜但驱动只提供 Windows 版的。5.3 前几次正常之后彻底没响应现象前几轮尝试还有明确回包跑到第十几次之后所有请求都超时扫描里这台 AP 的locked标志置位了。原因触发了设备的锁定策略。有些固件锁 5 分钟有些锁到重启为止还有些直接把 WPS 模块整个挂起连合法用户都配不上。解决立刻停手把设备断电重启恢复然后重新评估——锁死策略这么严的设备穷举这条路本来就不该走。这也是为什么第 3 章要先读locked字段动手之前就该知道对面是什么脾气。5.4 管理页面关了 WPS扫描里还带着 [WPS] 标记现象Web 后台里 WPS 开关已经关掉保存重启但scan_results里这台 AP 依然显示[WPS]。原因这是典型的「假关闭」。WPS 在这些固件里由独立进程托管Web 界面上的开关只改了前端配置没通知到后端也有的是开了 WPS 的 v2 版本或者厂商私有扩展主开关关了扩展还在广播。解决用 3.2 的命令复测直到[WPS]消失为止。实在关不掉就刷第三方固件或者把设备换掉——一个关不掉的 WPS 后门比没有 WPS 更糟糕因为它会让人误以为已经处理过了。5.5 换了新设备以为默认是安全的现象新买的设备开箱即用没做任何配置觉得出厂默认应该是最安全的。原因出厂默认恰恰是最危险的组合——WPS 开着、PIN 印在贴纸上、管理密码是 admin、固件是两年前的版本。厂商为了方便用户首次配网默认把所有简化通道都打开。解决上电第一件事不是连 Wi-Fi是网线直连进管理页面把 WPS 关掉、管理口令改掉、固件升级到最新然后再做别的。这个顺序我后来写成了 checklist每次换设备都强制走一遍。6. 加固闭环关掉 WPS 之后还要验证的三件事关 WPS 只是第一步真正做完要形成闭环。下面这张表是我现在固定执行的流程每项都带验证动作做完打勾加固项操作位置验证方法关闭 WPS无线设置里的 WPS 页面主开关 PBC 一起关wpa_cli scan_results里该 AP 的 flags 不含[WPS]关闭 PBC 按键同上或物理按键旁的跳线按下物理键后两分钟内扫描里不出现新的可配网状态升级固件管理页面固件升级或厂商官网下载版本号变化且升级后重测 WPS 状态没被重置回开启更换管理口令系统工具里的管理员密码用旧口令登录失败新口令能进关闭远程管理管理页面的 WAN 侧访问开关从外网侧访问管理端口应超时最容易漏的是第三行。固件升级会把配置恢复成默认值WPS 又悄悄打开了很多人升级完就不管了过两个月一扫[WPS]又回来了。所以验证动作里专门写了「升级后重测」这一步不能省。还有一个常被忽略的点PIN 印在机身贴纸上这件事本身没法改能改的是它是否还有效。有些固件允许在管理页面里自定义 PIN改成一个自己记得住但不在任何清单里的值等于给这条路加了一道只有你知道的门。改完记得也验一遍别改完没保存。至于 WEP 和 WPA 这两个老协议现在没必要再讨论了。WEP 的 RC4 加 IV 碰撞用普通笔记本几分钟就能恢复出密钥WPA-TKIP 也早被从推荐列表里划掉了。新设备直接上 WPA2-PSKAES-CCMP有条件上 WPA3-SAE不要为了兼容一台十年前的打印机把整张网降级——正确做法是给老设备单独开一个隔离的访客网络。从那以后我每次碰一台新路由器都会先把机身贴纸拍张照存档关完 WPS 再扫一遍确认[WPS]消失最后把配置快照导出来留底。这套动作做熟了不超过五分钟比事后查「为什么网速突然变慢」省心得多。希望帮到你。本文还有配套的精品资源点击获取