文章目录
- 1 原理分析
- 1.1 WPS连接过程
- 1.1.1 初始阶段
- 1.1.2 注册阶段
- 1.2 WPS攻击原理
- 1.2.1 在线攻击
- 1.2.2 离线攻击
- 1.2.2.1 Ralink模式
- 1.2.2.2 eCos模式
- 2 实验过程
- 3 参考资料
在2011年 Stefan Viehböck 演示过WPS的在线暴力攻击,由于PIN码猜测最多只需11000种组合,平均6小时就能攻破一台路由器。而在2014年 Dominique Bongard 又演示了一种离线暴力坡解的攻击,这种可怕的攻击可在不到一秒时间内破译WPA密钥,Bongard先生还在其幻灯片结尾发出警告:“立即禁用WPS!”
【教程合集】
【无线抓包实验1】抓包获取隐藏WiFi名称
【无线抓包实验2】5G WiFi抓包跑包全流程
【无线抓包实验3】批量抓包与无客户端抓包
【无线安全实验4】WPS离线破译原理与实验
1 原理分析
WPS(Wi-Fi Protected Setup)是一项旨在简化无线网络安全配置的标准,它通过PIN码(Personal Identification Number)或PBC(Push Button Configuration)等方式,使家庭用户能够轻松地将新设备安全地接入Wi-Fi网络。在PIN码模式下,WPS使用EAP-WSC(Extensible Authentication Protocol for Wi-Fi Simple Configuration)协议完成认证与密钥交换过程,该过程包含八个阶段(M1-M8)的消息交换。本文将深入探讨这八个阶段的详细流程,并重点分析其中涉及的关键参数(如E-Hash1、E-Hash2、PKE、PKR、AuthKey、E-Nonce等)的作用与相互计算关系。通过对这些参数和流程的理解,我们能够更深入地认识WPS的安全机制及其潜在脆弱性。
1.1 WPS连接过程
在WPS中有两个角色概念Enrollee和Registrar,Enrollee是入网设备(即手机物联网家居等各种终端),Registrar为认证服务器,一般也由AP(即路由器等设备)充当。
1.1.1 初始阶段
【发现过程】
AP端的WSC开始工作后,它会广播带有WSC IE字段的beacon包,以声明AP支持WSC
如果Enrollee收到来自AP的端的beacon包,那么它会解析beacon包中的 WSC IE,并向AP发送单播Probe Request包;如果Enrollee没有收到带有WSC IE的beacon包,那么它就会去搜索周围支持WSC的AP,所以会向周围发送Probe Request广播包。
如果Enrollee发现周围有两个或者两个以上的AP在跑WPS,则Enrollee会在发现阶段停止继续执行;同理,如果AP发现有两个或两个以上的Enrollee在尝试建立WPS连接,AP也会停止运行WPS。如果哪一天按下你router的WPS按钮,发现wps灯快闪了几下就不闪了,可能就是当时同时有两个Enrollee在尝试WPS连接。
AP收到带有 WSC IE的Probe Request请求包以后,就会回复Probe Response包,这个包里面带有WSC IE,会告诉Enrollee一些信息,这些信息很多,Enrollee会根据这些信息来决定下一步的动作
【关联过程】
- 对于PIN WPS需要在这个步骤输入PIN,对于PUSH BUTTON则不需要输入,当然PIN有分AP PIN和client PIN,这个决定了需要在AP端还是在Enrollee端输入PIN, 这种输入都是in-band的方式,当然也可以使用out-band的方式输入信息,如NFC
- 当Enrollee获取到AP端的信息,并且通过判断符合接入条件的时候,Enrollee会尝试去和AP进行认证,向AP发送Auth包
- AP端回复Auth 成功包,认证成功
- 然后Enrollee发送Association Request 关联请求包,并附带WSC IE 信息,这个信息很重要,目的是告诉AP我这边使用的协议(如802.1x WPS 1.0 协议),我们接下来的M1-M8过程也将会遵守这个协议进行交互,如果不能接受这种协议的话,请及时告诉我。
- 这时,AP端表示这种协议是支持的,发送一个Association Response包给Enrollee,完成关联。
【准备过程】
- 发送EAPOL-Start
- 在STA和AP双方开展EAP-WSC流程前,AP需要确定STA的Identity以及使用的身份验证算法。该过程涉及三次EAP包交换。这三次包交换的内容,首先,AP发送EAP-Request/Identity以确定STA的ID
- 对于打算使用WSC认证方法的STA来说,它需要在回复的EAP-Response/Identity包中设置Identity为"WFA-SimpleConfig-Enrollee-1-0"。
- AP确定STA的Identity为"WFA-SimpleConfig-Enrollee-1-0"后,将发送EAP-Request/WSC_Start包以启动EAP-WSC认证流程。而后进入WPS认证与配置的M1~M8阶段。
1.1.2 注册阶段
WPS使用EAP-WSC(EAP for Wi-Fi Simple Configuration)协议来完成配置过程,这部分是WPS的核心部分。该协议基于EAP(Extensible Authentication Protocol)框架,但专门用于WPS的设备注册流程。整个认证过程包含8个消息交换阶段(M1至M8),这些消息通过EAPoL-Data帧在Enrollee(设备)和Registrar(AP)之间传输,安全地交换配置信息(如 SSID、网络密钥等)。
消息 | 方向 | 说明 | 主要作用与内容 |
---|---|---|---|
M1 | 设备 → AP | 发起注册 | Enrollee 发送能力信息和公钥PKE 和随机数N1 ,启动注册流程。 |
M2 | AP → 设备 | 派生密钥 | Registrar 发送能力信息和公钥PKR 和随机数N1 ,并开始密钥派生。 |
M3 | 设备 → AP | 生成证明 | Enrollee 发送设备PIN码的哈希证明E-Hash1 和E-Hash2 。 |
M4 | AP → 设备 | 生成证明 | Registrar 回应设备PIN码的哈希证明R-Hash1 和R-Hash2 ,并提交第一个秘密随机数R-S1 。 |
M5 | 设备 → AP | 验证证明 | Enrollee 提交第一个秘密随机数 E-S1 。 |
M6 | AP → 设备 | 验证证明 | Registrar 提交第二个秘密随机数 R-S2 。 |
M7 | 设备 → AP | 验证证明 | Enrollee 提交第二个秘密随机数 E-S2 。 |
M8 | AP → 设备 | 配置网络 | Registrar 发送最终的加密配置信息(如 SSID、网络密钥等)。 |
【建立加密】
-
【M1】:初始化注册请求,提供Enrollee设备信息和安全能力。Enrollee选择随机数
A
为私钥,计算公钥PKE = g^A mod p
(使用指定的 1536-bit MODP 群)。发送UUID-E, MAC-E, PKE, N1(Enrollee随机数), Device Password ID, 能力属性等。
-
【M2】:响应注册请求,提供Registrar信息并启动密钥交换。Registrar选择随机数
B
为私钥,计算公钥PKR = g^B mod p
。发送UUID-R, PKR, N2(Registrar随机数), N1(回显), Auth_M2, 能力属性等。
根据交互的公钥与随机数,双方相互独立计算出共享密钥DHShared和密钥派生密钥KDK:
DHShared=PKEBmodp=PKRAmodp=g(A⋅B)modpDHKey=SHA-256(zeropad(DHShared,192))KDK=HMAC−SHA256(DHKey,N1∣∣MAC−E∣∣N2)DHShared = PKE^B \mod p = PKR^A \mod p = g^{(A \cdot B)} \mod p\\ DHKey = \text{SHA-256}(\text{zeropad}(DHShared, 192))\\ KDK = HMAC-SHA256(DHKey, N1 || MAC-E || N2) DHShared=PKEBmodp=PKRAmodp=g(A⋅B)modpDHKey=SHA-256(zeropad(DHShared,192))KDK=HMAC−SHA256(DHKey,N1∣∣MAC−E∣∣N2)
最终派生出两个关键的会话密钥AuthKey和KeyWrapKey:
AuthKey∣∣KeyWrapKey∣∣EMSK=kdf(KDK,"Wi−FiEasyandSecureKeyDerivation",640)AuthKey || KeyWrapKey || EMSK = kdf(KDK, "Wi-Fi Easy and Secure Key Derivation", 640) AuthKey∣∣KeyWrapKey∣∣EMSK=kdf(KDK,"Wi−FiEasyandSecureKeyDerivation",640)
PS:
kdf
是使用 HMAC-SHA256 的迭代函数,输出 640 位,分割为:
AuthKey
(256 bits): 用于消息认证 (HMAC)。KeyWrapKey
(128 bits): 用于加密敏感数据 (AES)。EMSK
(256 bits): 扩展主会话密钥,可用于派生其他应用密钥。M2中的
Auth_M2 = HMAC-SHA256(AuthKey, M2_Data)
。M2_Data
是 M2 消息中在 Authenticator 属性之前的所有属性。后续M3-M8的Auth_MX计算同理。
至此,双方通过Diffie-Hellman密钥交换算法已共享 KDK
, AuthKey
, KeyWrapKey
,为后续加密和认证打下基础。
【验证PIN码】
-
【M3】:Enrollee其证明持有PIN码。Enrollee将PIN码分割为两部分(如8位PIN码12345670左右均分为1234和5670),再派生出
PSK1
和PSK2
:
PSK1=First−128−bits−of(HMAC−SHA256(AuthKey,First−Half−Password))PSK2=First−128−bits−of(HMAC−SHA256(AuthKey,Second−Half−Password))PSK1 = First-128-bits-of( HMAC-SHA256(AuthKey, First-Half-Password) )\\ PSK2 = First-128-bits-of( HMAC-SHA256(AuthKey, Second-Half-Password) ) PSK1=First−128−bits−of(HMAC−SHA256(AuthKey,First−Half−Password))PSK2=First−128−bits−of(HMAC−SHA256(AuthKey,Second−Half−Password))
然后生成秘密随机数E-S1
,E-S2
(各 128 bits),计算证明哈希E-Hash1
和E-Hash2
:
E−Hash1=HMAC−SHA256(AuthKey,E−S1∣∣PSK1∣∣PKE∣∣PKR)E−Hash2=HMAC−SHA256(AuthKey,E−S1∣∣PSK2∣∣PKE∣∣PKR)E-Hash1 = HMAC-SHA256(AuthKey, E-S1 || PSK1 || PKE || PKR)\\ E-Hash2 = HMAC-SHA256(AuthKey, E-S1 || PSK2 || PKE || PKR) E−Hash1=HMAC−SHA256(AuthKey,E−S1∣∣PSK1∣∣PKE∣∣PKR)E−Hash2=HMAC−SHA256(AuthKey,E−S1∣∣PSK2∣∣PKE∣∣PKR)
发送 N2(回显), E-Hash1, E-Hash2, Auth_M3。 -
【M4】:Registrar其证明持有PIN码,提供秘密随机数让Enrollee验证PIN码第一部分。Registrar按同样方法分割PIN码并派生PSK1和PSK2,再生成秘密随机数
R-S1
,R-S2
,计算证明哈希R-Hash1
和R-Hash2
:
R−Hash1=HMAC−SHA256(AuthKey,R−S1∣∣PSK1∣∣PKE∣∣PKR)R−Hash2=HMAC−SHA256(AuthKey,R−S1∣∣PSK2∣∣PKE∣∣PKR)R-Hash1 = HMAC-SHA256(AuthKey, R-S1 || PSK1 || PKE || PKR)\\ R-Hash2 = HMAC-SHA256(AuthKey, R-S1 || PSK2 || PKE || PKR) R−Hash1=HMAC−SHA256(AuthKey,R−S1∣∣PSK1∣∣PKE∣∣PKR)R−Hash2=HMAC−SHA256(AuthKey,R−S1∣∣PSK2∣∣PKE∣∣PKR)
而后生成随机数IV(128bit),结合KeyWrapKey将第一个秘密随机数R-S1
加密到EncryptedSettings
,并使用AuthKey生成KWA以保护R-S1的完整性:
KWA=First−64−bits−of(HMAC−SHA256(AuthKey,R−S1))DataToEncrypt=R−S1∣∣KWAEncryptedSettings=AES−Encrypt−CBC(KeyWrapKey,IV,DataToEncrypt)KWA = First-64-bits-of( HMAC-SHA256(AuthKey, R-S1) )\\ DataToEncrypt = R-S1 || KWA\\ EncryptedSettings = AES-Encrypt-CBC(KeyWrapKey, IV, DataToEncrypt) KWA=First−64−bits−of(HMAC−SHA256(AuthKey,R−S1))DataToEncrypt=R−S1∣∣KWAEncryptedSettings=AES−Encrypt−CBC(KeyWrapKey,IV,DataToEncrypt)
发送 N1(回显), R-Hash1, R-Hash2, EncryptedSettings (R-S1), IV, Auth_M4。 -
【M5】:Enrollee提供秘密随机数让Registrar验证PIN码第一部分。Enrollee解密R-S1并验证其完整性:
Data∣∣KWA′=AES−Decrypt−CBC(KeyWrapKey,IV,EncryptedSettings)KWA′==First−64−bits−of(HMAC−SHA256(AuthKey,R−S1))Data || KWA' = AES-Decrypt-CBC(KeyWrapKey, IV, EncryptedSettings)\\ KWA' == First-64-bits-of( HMAC-SHA256(AuthKey, R-S1) ) Data∣∣KWA′=AES−Decrypt−CBC(KeyWrapKey,IV,EncryptedSettings)KWA′==First−64−bits−of(HMAC−SHA256(AuthKey,R−S1))
使用解密得到的R-S1
和自己已知的PSK1
,PKE
,PKR
重新计算R-Hash1
,与 M4 中的值比较。若计算不一致,触发WSC_NACK
终止会话(后续同)。然后使用同样方法将自己的第一个秘密随机数E-S1
加密到EncryptedSettings
。发送 N2(回显), EncryptedSettings (E-S1), IV, Auth_M5。
-
【M6】:**Registrar提供秘密随机数让Enrollee验证PIN码第二部分。**Registrar解密
E-S1
并计算E-Hash1
,将自己的第二个秘密随机数R-S2
加密到EncryptedSettings
发送 N1(回显), EncryptedSettings (R-S2), IV, Auth_M6。
-
【M7】:**Enrollee提供秘密随机数让Registrar验证PIN码第二部分。**Enrollee解密
R-S2
并计算R-Hash2
,将自己的第二个秘密随机数E-S2
加密到EncryptedSettings
发送 N2(回显), EncryptedSettings (E-S2 + Config-if-AP), IV, Auth_M7。
【配置网络】
-
【M8】:**完成验证并配置网络。**Registrar解密
E-S2
并计算E-Hash2
,双向认证全部完成。组装ConfigData网络配置信息:
ConfigData=Credential(SSID,NetworkKey,AuthType,EncType,...)ConfigData = Credential (SSID, Network Key, AuthType, EncType, ...) ConfigData=Credential(SSID,NetworkKey,AuthType,EncType,...)
同样使用 KeyWrapKey 加密 ConfigData (过程同前,附加 KWA)。发送 N1(回显), EncryptedSettings (ConfigData), IV, Auth_M8。最终Enrollee 解密并应用 ConfigData,协议完成。
【通俗解释】
- Enrollee (手机):“你好路由器,我想加入网络,这是我的信息(M1)。”
- Registrar (路由器):“好的手机,请证明你知道PIN码。验证通过后,我就把Wi-Fi密码给你(M2)。“
- 双方都生成两个上锁的箱子(E-Hash1/E-Hash2, R-Hash1/R-Hash2),里面是能证明自己身份的秘密。箱子的钥匙(E-S1/E-S2, R-S1/R-S2)在自己手里。Enrollee 先把自己的两个锁上的箱子(M3)交给 Registrar,说:“给,这是我的证明。”
- Registrar 说:“好吧,但我看不到里面。为了表示诚意,这是我能打开你第一个箱子的钥匙(加密的R-S1),同时我也把我的两个锁上的箱子(R-Hash1/R-Hash2)给你。”(M4)
- Enrollee 用只有自己才有的密钥(KDK)解密拿到钥匙(R-S1), 打开Registrar的第一个箱子(验证R-Hash1)。一旦验证成功,Enrollee 就确信 Registrar 知道真正的 PIN。于是,Enrollee 放心地把打开自己第一个箱子所需的钥匙(E-S1)交给 Registrar(M5)。
- Registrar 用 E-S1 验证 Enrollee 的第一个箱子(E-Hash1),也确认了 Enrollee 的身份。后续的 M6、M7 重复这个过程,用第二对箱子和钥匙(E-Hash2/E-S2, R-Hash2/R-S2)进行二次确认。
1.2 WPS攻击原理
1.2.1 在线攻击
PIN码为8位纯数字,而第8位为前面1-7位的校验和,所以总共只有10的7次方种组合。而由于PIN码的验证过程是分段式的,即先验证前4位,再验证后4位,则攻击者只需先猜测前4位组合(104),再猜测后四位组合(103),即最多11000种组合即可猜出正确PIN码。而WPS协议最初默认没有对PIN的验证次数进行限制,这就使在线PIN坡解成为了可能。传统的在线攻击方案可以借助 reaver 或 bully 简便地实现,信号良好情况下大约 3s/pin(WPS1.0,无锁 PIN 现象),平均几小时即可攻破一台路由器。
注意在WPS在线攻击中,攻击者电脑扮演的是Registrar,而受攻击路由器则充当Enrollee(与前面分析的逻辑相反)。攻击工具(如reaver
或bully
)运行在攻击者的电脑上(Supplicant/Client)。它宣称自己是一个“外部注册器(External Registrar)”,使受攻击目标接受管理并传输网络配置信息。
攻击者通过遍历所有PIN码组合,发送M4帧后根据是否收到AP回复的WSC_NACK来确定前半段PIN码是否正确,若不正确则递增下一个组合。直到猜中前半段PIN码,再发送M6帧并根据是否收到AP回复的WSC_NACK来确定后半段PIN码是否正确,最终完全确认整个PIN码。
1.2.2 离线攻击
现在多数路由器都是 WPS2.0(WSC),会出现锁 PIN 现象,一定程度提高了安全性。可就算是几次 PIN 尝试的机会,也留给了攻击者可乘之机。2014年Dominique Bongard演示了一种离线WPS的方法,对某些存在安全漏洞的路由器可在不到一秒内破译。该攻击利用了某些路由厂商芯片只能生成低熵的伪随机数,进而坡解加密过程。攻击称为“pixie-dust attack”,翻译为“小精灵尘埃攻击”或“仙尘”攻击(外国人起名灵感感觉大都跟游戏影视作品有关)。
入网设备和AP之间没有传输PIN码,是通过计算PIN码的Hash值来验证的。如果PIN码验证失败,流程会止于M4阶段。根据之前在线PIN坡解过程分析,攻击者可以获取到PIN码最终计算产物E-Hash1和E-Hash2(AP发送的M3帧),以及中间计算值AuthKey、PKE、PKE,而初始参数PIN码与两个中间计算使用的随机数E-S1和E-S2是不知道的。但是如果攻击者能猜测到AP生成的随机数E-S1和E-S2,就可以通过代入所有PIN码组合去猜测验证,最终获取到PIN码。
其实这里笔者有点疑惑,为什么攻击者不按常规流程充当Enrollee角色,去获取AP发送的M4帧,这样还可以多获取到一个随机数信息。可能是因为代码是基于reaver改编的分支,而reaver就是使用Registrar角色去进行攻击吧。
那么怎么能猜到AP生成的随机数呢?Dominique Bongard发现某些路由器的芯片生成的是不安全的伪随机数(Pseudo-Random Number Generator,PRNG),导致其可以通过上一个随机数进行逆向预测。例如,Broadcom基本上使用C中的Rand()函数,在Ralink中随机数始终不会生成(因此为0),而RTL819x生成的随机数存在复用(N1、E-S1、E-S2均相同)。
易受攻击的路由器收录:https://docs.google.com/spreadsheets/d/1tSlbqVQ59kGn8hgmwcPTHUECQ3o9YhXR91A_p7Nnj5Y/edit#gid=2048815923
在攻击过程中,M1阶段受攻击路由器会生成随机数N1(E-Nonce),到M3阶段又会生成的随机数E-S1和E-S2,这两个随机数是紧接着N1生成的。而N1可以直接捕获到,如果路由器生成的是伪随机数,就可以进行逆向分析。下面分析pixiewps源码中的两种逆推伪随机数的模式:Ralink模式(直接计算)和eCos模式(暴力枚举)。
1.2.2.1 Ralink模式
- Ralink基于线性反馈移位寄存器(LFSR)算法生成伪随机数,使用的是 32 位 Galois LFSR(掩码 0x80000057),生成代码如下。
//Ranlink随机数生成
static unsigned char ralink_randbyte(struct ralink_randstate *state)
{unsigned char r = 0;for (int i = 0; i < 8; i++) {unsigned char result;if (state->sreg & 0x00000001) { // 检查LSBstate->sreg = ((state->sreg ^ 0x80000057) >> 1) | 0x80000000;result = 1;}else {state->sreg = state->sreg >> 1; // 简单右移result = 0;}r = (r << 1) | result; // MSB优先累积}return r;
}
伪随机数生成流程:
- 选定一个初始的数S0,在LFSR中通常称为状态(可以理解为种子,如:1010…1011 ,共32位),先取最低位(Least Significant Bit,LBS)作为输出随机数的第一位(最左边的一位)。
- 如果最低位为1则S0与多项式(0x80000057)进行异或操作,如果最低位为0则不变,得到S0‘。
- 对S0‘右移一位,如果进行了异或操作则高位补1,如果没有操作则高位补0,得到S1。
- 接着重复这个过程,再次对S1取值、操作、右移,一步步得到随机数的第二、三、…、八位,循环做8次就可以得到1字节(8bit)的随机数。而N1为16字节,则需做8x16次运算。
static void ralink_randstate_restore(struct ralink_randstate *state, uint8_t r)
{for (int i = 0; i < 8; i++) {const unsigned char result = r & 1; // 取LSB(从后向前处理)r = r >> 1;if (result) { // 如果输出位是1state->sreg = (((state->sreg) << 1) ^ 0x80000057) | 0x00000001;}else { // 如果输出位是0state->sreg = state->sreg << 1; // 简单左移}}
}
通过随机数逆向种子:
由随机数的最后一位,可以知道上一次的操作数Sn的最后一位(如果随机数最后一位是0,则Sn’的最后一位为0;如果随机数最后一位是1,则Sn’的最后一位为1)。同理,由随机数倒数第二位,可以知道上上次的操作数Sn-1的最后一位,以此类推。由于知道每次操作数的最后一位,从最后一次操作数Sn开始进行补值、操作、左移(逆运算)。
- 如假如随机数最后一位为1,则设最后一次操作数Sn为0000…0001(最后一位为0则设0000…0000,最后一位前面的值随便设,因为后续会不断左移去掉)。
- 然后观察随机数倒数第二位,倒数第二位为1,则说明Sn-1’到Sn做了异或操作,那么需要再与多项式(0x80000057)做一次异或操作使其变为Sn-1’;如果随机数倒数第二位为0,则无需操作Sn即Sn-1’。
- 将Sn-1‘左移,然后末尾补随机数的倒数第二位,变为Sn-1。
- 重复这个过程,直至将最开始Sn的最后一位左移到第一位。一共32位,所以做31次即可逆向出某个时刻的操作数(种子)。
static unsigned char ralink_randbyte_backwards(struct ralink_randstate *state)
{unsigned char r = 0;for (int i = 0; i < 8; i++) {unsigned char result;if (state->sreg & 0x80000000) { // 检查MSBstate->sreg = ((state->sreg << 1) ^ 0x80000057) | 0x00000001;result = 1;}else {state->sreg = state->sreg << 1; // 简单左移result = 0;}r |= result << i; // LSB优先累积}return r;
}
通过种子逆向生成随机数:
前面是通过种子往后生成随机数,这个代码是通过种子往前生成随机数,过程类似。
- 选定一个初始的数Sn,先取最高位(Most Significant Bit,MSB)作为输出随机数的最后一位(最右边的一位,这里是第八位)。
- 如果最高位为1则S0与多项式(0x80000057)进行异或操作,如果最高位为0则不变,得到Sn‘。
- 对Sn‘右移一位,如果进行了异或操作则低位补1,如果没有操作则低位补0,得到Sn-1。
- 接着重复这个过程,再次对Sn-1取值、操作、左移,一步步得到随机数的第七、六、…、一位,循环做8次就可以得到1字节(8bit)的随机数。
LFSR算法生成的随机数是完全可逆的,在源码中是先通过随机数N1(E-Nonce)反推种子,然后回退 16 字节得到 E‑S2,再接着回退 16 字节得到 E‑S1。这里笔者是挺疑惑的,按照WSC规范,生成顺序应该是N1 -> E-S1 -> E-S2。不清楚代码为什么不是从N1继续正向生成随机数E-S1和E-S2,而是逆向生成E-S2和E-S1,也许这个随机数的生成逻辑就是逆向生成吧。
1.2.2.2 eCos模式
- eCos simple 模式基于线性同余生成器(LCG,Linear Congruential Generator)算法生成伪随机数,源自 C 语言标准库的
rand()
函数,生成代码如下。
static uint32_t ecos_rand_simple(uint32_t *seed)
{uint32_t s = *seed;uint32_t uret;// 第一步:线性同余生成s = (s * 1103515245) + 12345; // 生成s1uret = s & 0xffe00000; // 取高11位 (bits 31-21)// 第二步:继续生成 s = (s * 1103515245) + 12345; // 生成s2uret += (s & 0xfffc0000) >> 11; // 取高14位右移11位 (bits 31-18 → bits 20-7)// 第三步:再次生成s = (s * 1103515245) + 12345; // 生成s3uret += (s & 0xfe000000) >> (11 + 14); // 取高7位右移25位 (bits 31-25 → bits 6-0)*seed = s;return uret; // 返回32位值,但实际只使用低8位
}//使用时只取低8位
output_byte = ecos_rand_simple(&seed) & 0xFF;
伪随机数生成流程:
- 选定一个初始的数S0(32位)作为种子。对这个数乘以一个乘数(1103515245),再进行增量(12345),得到S1,取高11位作为随机数的高11位。
- 对S1再乘以乘数并进行增量,得到S2,取高14位作为随机数中间14位。
- 对S2再乘以乘数并进行增量,得到S3,取高7位作为随机数低7位。总共得到11+14+7=32位随机数,但使用时只取低8位。
整体操作为对操作数做3次LCG固定计算s = (s * a + c) % m,每次取部分高位拼接成随机数。末尾取模在代码中没有显式编写,实际上因为使用了无符号整数(232),计算后溢出的高位会被丢弃,相当于取2^32的模。
uint32_t known = wps->e_nonce[0] << 25; /* 将熵从32位减少到25位 */
uint32_t seed, counter = 0;
while (counter < 0x02000000) { // 2^25 = 33,554,432次尝试int i;seed = known | counter; // 构建候选种子// 验证后续15个字节for (i = 1; i < WPS_NONCE_LEN; i++) {if (wps->e_nonce[i] != (uint8_t)(ecos_rand_simple(&seed) & 0xff))break;}// 如果所有字节都匹配if (i == WPS_NONCE_LEN) {// 找到正确种子,继续生成E-S1和E-S2wps->s1_seed = seed;for (i = 0; i < WPS_SECRET_NONCE_LEN; i++)wps->e_s1[i] = (uint8_t)(ecos_rand_simple(&seed) & 0xff);wps->s2_seed = seed;for (i = 0; i < WPS_SECRET_NONCE_LEN; i++)wps->e_s2[i] = (uint8_t)(ecos_rand_simple(&seed) & 0xff);break;}counter++;
}
通过随机数逆向种子:
首先一字节的随机数(8位)的后7位就是取自最后一次操作数的S3的前7位,那么对于S3还有剩下的25位未知。而LCG算法与前面LFSR算法不同,生成随机数会溢出部分数据导致计算不可逆。但伪随机数的特点就是计算过程是固定的,通过分析连续生成的随机数,知道结果就可以直接套初始值去猜测,最终逆推出种子。
对于WPS中生成的N1(E-Nonce)总共16字节,我们取第1个字节,其前7位做种子的前7位,对于未知的后25位进行枚举猜测,同样使用LCG算法去计算后面生成的随机数,与N1的后15字节结果进行比对。如果计算结果比对实际随机数完全一致,那就说明猜测成功。这种方式即暴力坡解,最多猜测2^25次(33,554,432),计算机可在几秒内完成。
猜出种子后,再往后计算就可以得到E-S1和E-S2。
2 实验过程
本次实验以无线安全审计目的,意在对路由器安全强度进行测试,仅做学习研究之用。请注意遵循相关法律法规。
其实现在大多数路由器已经没有WPS漏洞了,越新的路由器会采用更新的无线安全标准(如WPA3),同时禁用WPS功能。无线安全水平是在不断提升的,但也不排除还有少部分老旧路由器没有被淘汰更新。
本次实验使用的软件为WiFiGrab。
百度云盘下载:https://pan.baidu.com/s/1Q9oWrHF_nKgwOtKyVcb7IQ?pwd=xxwd
123云盘下载:https://www.123684.com/s/q496Td-Ru95H
-
接入USB抓包网卡后启动软件。
-
在设置中可同时启用2.4G和5G扫描,并将扫描时间改长一些,确认。
-
点击扫描WiFi,对附近无线网络进行扫描。
扫描完成,可以观察边框颜色,黑色边框说明AP启用了WPS,灰色边框说明AP未启用WPS(软件通过AP信标帧中的wifi_protected_setup_state字段确定)。
仅对已启用WPS的AP进行测试(需注意隐藏WiFi即使启用了WPS,也无法直接进行WPS攻击,因为不知道WiFi名称无法接入。但其实WiFi名称是可以通过抓包获取的,不过本软件不涉及该内容)。
-
以笔者自己的路由器(荣耀CD16)进行测试,选择目标后,点击PIN。
-
运行后WPS离线攻击没有成功,说明该路由器没有伪随机数漏洞。
观察日志可以看到,软件如前面分析的,对AP发起WPS的流程。通过M1到M3交互获取到E-Nonce、PKR、PKE、AuthKey等信息,然后尝试猜测E-S1和E-S2,但未猜测成功,该AP可能基于物理过程生成的是真随机数,或是使用其他算法生成的随机数。
-
笔者对周围的路由器也进行了安全测试,发现大多数路由器也不存在此漏洞,是较新的路由设备。但也有极个别路由器(可能是采用老式芯片的光猫或路由器)是有该漏洞存在的。
前文有易受攻击的路由器收录的信息,而WPS的广播信息中有两个字段Model Name和Model Number标识了AP设备的型号与编号,由此能得知AP是否易受攻击。易受攻击的路由器在软件中会显示为蓝色边框,同时在MAC地址后侧括号中会显示该设备信息(如Ralink Wireless Access Point RT2860)。
笔者找到了个别显示蓝色边框的路由器进行测试,发现其PIN码信息可以被计算出来,且只需要不到十几秒时间。
3 参考资料
https://ifconfig.dk/pixiedust/
https://github.com/2EXP/2exp.github.io/issues/3
https://mlg556.github.io/posts/bruteforce-wps-with-reaver-and-pixiewps/bruteforce-wps-with-reaver-and-pixiewps.html
https://blog.csdn.net/moonlinux20704/article/details/109102318
http://archive.hack.lu/2014/Hacklu2014_offline_bruteforce_attack_on_wps.pdf