文章目录

    • 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 初始阶段

img

【发现过程】

  1. AP端的WSC开始工作后,它会广播带有WSC IE字段的beacon包,以声明AP支持WSC

  2. 如果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连接。

  3. AP收到带有 WSC IE的Probe Request请求包以后,就会回复Probe Response包,这个包里面带有WSC IE,会告诉Enrollee一些信息,这些信息很多,Enrollee会根据这些信息来决定下一步的动作

【关联过程】

  1. 对于PIN WPS需要在这个步骤输入PIN,对于PUSH BUTTON则不需要输入,当然PIN有分AP PIN和client PIN,这个决定了需要在AP端还是在Enrollee端输入PIN, 这种输入都是in-band的方式,当然也可以使用out-band的方式输入信息,如NFC
  2. 当Enrollee获取到AP端的信息,并且通过判断符合接入条件的时候,Enrollee会尝试去和AP进行认证,向AP发送Auth包
  3. AP端回复Auth 成功包,认证成功
  4. 然后Enrollee发送Association Request 关联请求包,并附带WSC IE 信息,这个信息很重要,目的是告诉AP我这边使用的协议(如802.1x WPS 1.0 协议),我们接下来的M1-M8过程也将会遵守这个协议进行交互,如果不能接受这种协议的话,请及时告诉我。
  5. 这时,AP端表示这种协议是支持的,发送一个Association Response包给Enrollee,完成关联。

【准备过程】

  1. 发送EAPOL-Start
  2. 在STA和AP双方开展EAP-WSC流程前,AP需要确定STA的Identity以及使用的身份验证算法。该过程涉及三次EAP包交换。这三次包交换的内容,首先,AP发送EAP-Request/Identity以确定STA的ID
  3. 对于打算使用WSC认证方法的STA来说,它需要在回复的EAP-Response/Identity包中设置Identity为"WFA-SimpleConfig-Enrollee-1-0"。
  4. 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,启动注册流程。
M2AP → 设备派生密钥Registrar 发送能力信息和公钥PKR和随机数N1,并开始密钥派生。
M3设备 → AP生成证明Enrollee 发送设备PIN码的哈希证明E-Hash1E-Hash2
M4AP → 设备生成证明Registrar 回应设备PIN码的哈希证明R-Hash1R-Hash2,并提交第一个秘密随机数R-S1
M5设备 → AP验证证明Enrollee 提交第一个秘密随机数 E-S1
M6AP → 设备验证证明Registrar 提交第二个秘密随机数 R-S2
M7设备 → AP验证证明Enrollee 提交第二个秘密随机数 E-S2
M8AP → 设备配置网络Registrar 发送最终的加密配置信息(如 SSID、网络密钥等)。

deepseek_mermaid_20250908_59dba1

【建立加密】

  • 【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(AB)modpDHKey=SHA-256(zeropad(DHShared,192))KDK=HMACSHA256(DHKey,N1∣∣MACE∣∣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,"WiFiEasyandSecureKeyDerivation",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),再派生出PSK1PSK2
    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=First128bitsof(HMACSHA256(AuthKey,FirstHalfPassword))PSK2=First128bitsof(HMACSHA256(AuthKey,SecondHalfPassword))
    然后生成秘密随机数E-S1, E-S2 (各 128 bits),计算证明哈希E-Hash1E-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) EHash1=HMACSHA256(AuthKey,ES1∣∣PSK1∣∣PKE∣∣PKR)EHash2=HMACSHA256(AuthKey,ES1∣∣PSK2∣∣PKE∣∣PKR)
    发送 N2(回显), E-Hash1, E-Hash2, Auth_M3。

  • 【M4】:Registrar其证明持有PIN码,提供秘密随机数让Enrollee验证PIN码第一部分。Registrar按同样方法分割PIN码并派生PSK1和PSK2,再生成秘密随机数R-S1, R-S2 ,计算证明哈希R-Hash1R-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) RHash1=HMACSHA256(AuthKey,RS1∣∣PSK1∣∣PKE∣∣PKR)RHash2=HMACSHA256(AuthKey,RS1∣∣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=First64bitsof(HMACSHA256(AuthKey,RS1))DataToEncrypt=RS1∣∣KWAEncryptedSettings=AESEncryptCBC(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=AESDecryptCBC(KeyWrapKey,IV,EncryptedSettings)KWA==First64bitsof(HMACSHA256(AuthKey,RS1))
    使用解密得到的 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,协议完成。

【通俗解释】

  1. Enrollee (手机):“你好路由器,我想加入网络,这是我的信息(M1)。”
  2. Registrar (路由器):“好的手机,请证明你知道PIN码。验证通过后,我就把Wi-Fi密码给你(M2)。“
  3. 双方都生成两个上锁的箱子(E-Hash1/E-Hash2, R-Hash1/R-Hash2),里面是能证明自己身份的秘密。箱子的钥匙(E-S1/E-S2, R-S1/R-S2)在自己手里。Enrollee 先把自己的两个锁上的箱子(M3)交给 Registrar,说:“给,这是我的证明。”
  4. Registrar 说:“好吧,但我看不到里面。为了表示诚意,这是我能打开你第一个箱子的钥匙(加密的R-S1),同时我也把我的两个锁上的箱子(R-Hash1/R-Hash2)给你。”(M4)
  5. Enrollee 用只有自己才有的密钥(KDK)解密拿到钥匙(R-S1), 打开Registrar的第一个箱子(验证R-Hash1)。一旦验证成功,Enrollee 就确信 Registrar 知道真正的 PIN。于是,Enrollee 放心地把打开自己第一个箱子所需的钥匙(E-S1)交给 Registrar(M5)。
  6. 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 现象),平均几小时即可攻破一台路由器。

image-20250910002321896

注意在WPS在线攻击中,攻击者电脑扮演的是Registrar,而受攻击路由器则充当Enrollee(与前面分析的逻辑相反)。攻击工具(如reaverbully)运行在攻击者的电脑上(Supplicant/Client)。它宣称自己是一个“外部注册器(External Registrar)”,使受攻击目标接受管理并传输网络配置信息。

image-20250910212210563

攻击者通过遍历所有PIN码组合,发送M4帧后根据是否收到AP回复的WSC_NACK来确定前半段PIN码是否正确,若不正确则递增下一个组合。直到猜中前半段PIN码,再发送M6帧并根据是否收到AP回复的WSC_NACK来确定后半段PIN码是否正确,最终完全确认整个PIN码。

image-20250910195829673

1.2.2 离线攻击

现在多数路由器都是 WPS2.0(WSC),会出现锁 PIN 现象,一定程度提高了安全性。可就算是几次 PIN 尝试的机会,也留给了攻击者可乘之机。2014年Dominique Bongard演示了一种离线WPS的方法,对某些存在安全漏洞的路由器可在不到一秒内破译。该攻击利用了某些路由厂商芯片只能生成低熵的伪随机数,进而坡解加密过程。攻击称为“pixie-dust attack”,翻译为“小精灵尘埃攻击”或“仙尘”攻击(外国人起名灵感感觉大都跟游戏影视作品有关)。

image-20250909172542790

入网设备和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;
}

伪随机数生成流程:

  1. 选定一个初始的数S0,在LFSR中通常称为状态(可以理解为种子,如:1010…1011 ,共32位),先取最低位(Least Significant Bit,LBS)作为输出随机数的第一位(最左边的一位)。
  2. 如果最低位为1则S0与多项式(0x80000057)进行异或操作,如果最低位为0则不变,得到S0‘。
  3. 对S0‘右移一位,如果进行了异或操作则高位补1,如果没有操作则高位补0,得到S1。
  4. 接着重复这个过程,再次对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. 如假如随机数最后一位为1,则设最后一次操作数Sn为0000…0001(最后一位为0则设0000…0000,最后一位前面的值随便设,因为后续会不断左移去掉)。
  2. 然后观察随机数倒数第二位,倒数第二位为1,则说明Sn-1’到Sn做了异或操作,那么需要再与多项式(0x80000057)做一次异或操作使其变为Sn-1’;如果随机数倒数第二位为0,则无需操作Sn即Sn-1’。
  3. 将Sn-1‘左移,然后末尾补随机数的倒数第二位,变为Sn-1。
  4. 重复这个过程,直至将最开始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;
}

通过种子逆向生成随机数:

前面是通过种子往后生成随机数,这个代码是通过种子往前生成随机数,过程类似。

  1. 选定一个初始的数Sn,先取最高位(Most Significant Bit,MSB)作为输出随机数的最后一位(最右边的一位,这里是第八位)。
  2. 如果最高位为1则S0与多项式(0x80000057)进行异或操作,如果最高位为0则不变,得到Sn‘。
  3. 对Sn‘右移一位,如果进行了异或操作则低位补1,如果没有操作则低位补0,得到Sn-1。
  4. 接着重复这个过程,再次对Sn-1取值、操作、左移,一步步得到随机数的第七、六、…、一位,循环做8次就可以得到1字节(8bit)的随机数。

deepseek_mermaid_20250913_c68ff0

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;

伪随机数生成流程:

  1. 选定一个初始的数S0(32位)作为种子。对这个数乘以一个乘数(1103515245),再进行增量(12345),得到S1,取高11位作为随机数的高11位。
  2. 对S1再乘以乘数并进行增量,得到S2,取高14位作为随机数中间14位。
  3. 对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算法不同,生成随机数会溢出部分数据导致计算不可逆。但伪随机数的特点就是计算过程是固定的,通过分析连续生成的随机数,知道结果就可以直接套初始值去猜测,最终逆推出种子。

deepseek_mermaid_20250913_d391ea

对于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

  1. 接入USB抓包网卡后启动软件。

    image-20250913231853167

  2. 在设置中可同时启用2.4G和5G扫描,并将扫描时间改长一些,确认。

    image-20250913231924840

    image-20250913231951653

  3. 点击扫描WiFi,对附近无线网络进行扫描。

    扫描完成,可以观察边框颜色,黑色边框说明AP启用了WPS,灰色边框说明AP未启用WPS(软件通过AP信标帧中的wifi_protected_setup_state字段确定)。

    仅对已启用WPS的AP进行测试(需注意隐藏WiFi即使启用了WPS,也无法直接进行WPS攻击,因为不知道WiFi名称无法接入。但其实WiFi名称是可以通过抓包获取的,不过本软件不涉及该内容)。

    image-20250913233000916

  4. 以笔者自己的路由器(荣耀CD16)进行测试,选择目标后,点击PIN。

    image-20250913234505706

  5. 运行后WPS离线攻击没有成功,说明该路由器没有伪随机数漏洞。

    观察日志可以看到,软件如前面分析的,对AP发起WPS的流程。通过M1到M3交互获取到E-Nonce、PKR、PKE、AuthKey等信息,然后尝试猜测E-S1和E-S2,但未猜测成功,该AP可能基于物理过程生成的是真随机数,或是使用其他算法生成的随机数。

    image-20250913234908815

  6. 笔者对周围的路由器也进行了安全测试,发现大多数路由器也不存在此漏洞,是较新的路由设备。但也有极个别路由器(可能是采用老式芯片的光猫或路由器)是有该漏洞存在的。

    前文有易受攻击的路由器收录的信息,而WPS的广播信息中有两个字段Model Name和Model Number标识了AP设备的型号与编号,由此能得知AP是否易受攻击。易受攻击的路由器在软件中会显示为蓝色边框,同时在MAC地址后侧括号中会显示该设备信息(如Ralink Wireless Access Point RT2860)。

    笔者找到了个别显示蓝色边框的路由器进行测试,发现其PIN码信息可以被计算出来,且只需要不到十几秒时间。

    image-20250914015718940


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

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如若转载,请注明出处:http://www.pswp.cn/diannao/100444.shtml
繁体地址,请注明出处:http://hk.pswp.cn/diannao/100444.shtml
英文地址,请注明出处:http://en.pswp.cn/diannao/100444.shtml

如若内容造成侵权/违法违规/事实不符,请联系英文站点网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

IDEA开发过程中经常使用到的快捷键

IntelliJ IDEA 开发 Java 时常用的快捷键列表 代码编辑与行操作快捷键功能描述Ctrl Y删除当前行。Ctrl D复制当前行到下一行。Shift Alt ↑将当前行&#xff08;或选中块&#xff09;向上移动。Shift Alt ↓将当前行&#xff08;或选中块&#xff09;向下移动。Ctrl /注…

ubuntu使用webrtc库开发一个webrtc推拉流程序

目录 一. 前言 二. 整体交互流程 三. 类实现说明 1. WebRtcClient 2. SignalPeerClient 3. WebRTCStream 4. 视频源类 5. 拉流渲染 四. 使用示例 1. 推流代码示例 2. 拉流代码示例 一. 前言 在 《ubuntu编译webrtc库》我们介绍了如何在 ubuntu 上使用 webrtc 源代码…

【Block总结】ConverseNet:神经网络中的反向卷积算子

1. 论文信息 标题:Reverse Convolution and Its Applications to Image Restoration 发布平台:arXiv 论文链接:https://arxiv.org/pdf/2508.09824 代码仓库:https://github.com/cszn/converseNet 任务领域:图像恢复(去噪、超分辨率、去模糊) 核心贡献:提出了一种新的反…

优化浏览体验:4个设置让Google Chrome更好用!

想要更流畅、更快速的浏览体验吗&#xff1f;本文章将向大家展示Google Chrome中你应该立即更改的4个重要设置&#xff0c;设置调整将帮助您提升性能&#xff0c;让你的浏览更高效。1、打开浏览器&#xff0c;在地址栏输入“chrome://flags"确定&#xff0c;在搜索标志中输…

【Git】一篇文章带你入门Git

1. 初识 Git 1.1 Git 是什么&#xff1f; Git 是一个开源的分布式版本控制系统&#xff0c;用于高效地跟踪和管理项目代码的变更历史&#xff08;不仅仅是代码&#xff0c;还有其它格式也是可以的~&#xff09; 1.2 为什么要有 Git 在学习或者是工作的时候&#xff0c;比如…

8 基于机器学习进行遥感影像的地物分类-以随机森林为例

目录 1 读取数据 2 数据预处理 3 模型训练 4 精度分析 5 模型预测 1 读取数据 1.右键数据所在文件夹,用pycharm打开为项目 2.在settings中设置python环境为先前配置的带GDAL和sklearn的环境 3.新建一个文件夹命名为code来存放代码,在code文件夹中新建一个rfc.py

极简版 Nginx 反向代理实验步骤

以下是最核心的反向代理实现步骤&#xff0c;专注于 “客户端→Nginx 代理→后端服务” 的基础转发功能&#xff1a;一、准备 2 台服务器角色IP 示例需安装软件代理服务器192.168.1.10Nginx后端服务器192.168.1.11Nginx/Apache二、后端服务器配置&#xff08;192.168.1.11&…

Windsurf 插件正式登陆 JetBrains IDE:让 AI 直接在你的 IDE 里“打工”

那天你说,我们可以永远在一起——成了我听到的最高兴的,而后知后觉的谎言。 而今天,AI 说:“我可以帮你写完这段代码。” —— 这才是真正的“永远在一起”。 最近,AI 编程工具赛道迎来一场“潜行式革命”:Windsurf Wave 7 正式发布 JetBrains IDE 插件版本,彻底打破“A…

188. Java 异常 - Java 异常处理规范

文章目录188. Java 异常 - Java 异常处理规范&#x1f4dc; 什么是“Catch or Specify Requirement”&#xff1f;✅ 两种满足方式方法一&#xff1a;使用 try-catch 捕获异常方法二&#xff1a;使用 throws 声明异常❌ 不遵守规则会怎样&#xff1f;&#x1f4a1; 哪些异常必须…

泛型通配符 T、E、K、V、?

在Java后端开发中&#xff0c;你一定在写集合类或工具类时&#xff0c;见过 T、E、K、V、? 这样的泛型通配符。但你是否有过以下疑惑&#xff1a; T、E、K、V 到底有什么区别&#xff1f;为什么大家都用这些字母&#xff1f;List<?> 和 List 有什么不同&#xff1f;什么…

基于脚手架微服务的视频点播系统-数据管理与网络通信部分的预备工作

基于脚手架微服务的视频点播系统-数据管理与网络通信部分的预备工作一.数据管理二.网络通信2.1客户端通信模块及测试用例的实现2.2MockServer搭建的相关接口介绍2.3MockServer的搭建示例一.数据管理 在前⾯的实现中&#xff0c;程序中的数据、以及界⾯操作等全部搅合在⼀起&am…

html表单登录模式代码

使用的是Content-Typeapplication/x-www-form-urlencoded形式如代码如下的html&#xff0c;后端没写下去&#xff1a;<!DOCTYPE html> <html lang"zh-CN"> <head><meta charset"UTF-8"><title>管理员登录</title><…

NLP:Transformer之self-attention(特别分享3)

本文目录&#xff1a;一、核心思想&#xff1a;一句话概括二、计算流程的直观比喻三、分步计算详解&#xff08;附数字例子&#xff09;第 1 步&#xff1a;创建 Query, Key, Value 向量第 2 步&#xff1a;计算注意力分数第 3 步&#xff1a;缩放并应用 Softmax第 4 步&#x…

25、优化算法与正则化技术:深度学习的调优艺术

学习目标:深入理解各种优化算法的原理和适用场景,掌握学习率调度的方法和策略,学会使用Dropout、批归一化等正则化技术,理解早停和验证策略,建立深度学习调优的系统性知识。 深度学习的成功不仅依赖于精巧的模型架构,更在于巧妙的训练策略。如果说网络架构是房屋的设计图…

Netty-01-NIO前置知识

目录 NIO三大组件 一. ByteBuffer 基本用法 DirectByteBuffer与HeapByteBuffer对比 字符串转ByteBuffer ByteBuffer.wrap(byte[] ) 粘包与拆包 文件编程 零拷贝transferTo 二. 阻塞与非阻塞Channel 三. Selector SelectionKey&#xff08;重点&#xff09; Select…

知识点17:多Agent系统架构设计模式

知识点17&#xff1a;多Agent系统架构设计模式 核心概念 掌握系统架构思维&#xff0c;理解多Agent系统的设计原则和模式 架构设计核心概念 在构建多Agent系统时&#xff0c;良好的架构设计是系统成功的关键。本节将介绍多Agent系统架构设计中的核心概念&#xff0c;包括单点瓶…

数据库造神计划第五天---增删改查(CRUD)(1)

&#x1f525;个人主页&#xff1a;寻星探路 &#x1f3ac;作者简介&#xff1a;Java研发方向学习者 &#x1f4d6;个人专栏&#xff1a;《从青铜到王者&#xff0c;就差这讲数据结构&#xff01;&#xff01;&#xff01;》、 《JAVA&#xff08;SE&#xff09;----如此简单&a…

基于Vue3的人工智能生成内容标识服务平台前端页面设计

效果图&#xff1a;素材库&#xff1a;App.vue<template><div id"app"><!-- 头部导航 --><Header /><!-- 主要内容区域 --><main class"main-content"><div class"container"><!-- 强制性国家标准…

使用 MyCat 实现 MySQL 主从读写分离

文章目录使用 MyCat 实现 MySQL 主从读写分离完整指南一、MySQL 读写分离基础概述1.1 读写分离工作原理1.2 为什么需要读写分离1.3 读写分离的两种实现方式主流读写分离中间件对比二、MyCat 中间件简介2.1 MyCat 核心功能2.2 MyCat 适用场景三、环境准备与 MyCat 安装3.1 前提&…

物联网传感器检测实验

/*------------------------------------------------------------------------------ * @文件名 : handle * @描述 : 用户处理函数 * @作者 : 物联网项目组 * @日期 : 2023/04/01 * @版本 : V0.0.2 *****************************…