802.11 学习笔记
IEEE 802.11是现今无线局域网通用的标准,而当今我们熟悉的Wi-Fi,正是基于IEEE 802.11系列标准的产品。通过Wi-Fi,我们可以尽情享受无线上网的乐趣,而不必拖着长长的网线。那么,它是如何工作的呢?带着这样的疑问,我查找了相关资料,并通过抓包的方式进行验证。由于没有通信相关的专业背景,因此学习的主要是数据链路层的部分。
定义
Access Point(AP)
接入点,多指无线访问接入点,即通常所说的路由器。
Station(STA)
站点,泛指无线接入设备,如笔记本电脑、平板电脑、手机等。
Basic Service Set(BSS)
所有无线设备(STA)关联到一个访问点(AP)上,它们构成一个基础服务集合。在集合中,访问点控制和主导整个BSS中的全部数据的传输过程。
Extended Service Set(ESS)
多个BSS通过各种手段互联形成的扩展网络,无线设备关联到一个或多个访问点上,它们构成一个扩展服务集合。在同一个ESS中的不同BSS之间切换的过程称为漫游。
Service Set Identification(SSID)
BSS和ESS使用的标识符,路由器等AP一般都允许用户自由设置。
802.11 Frame
802.11标准将所有的帧(Frame,数据链路层的基本传输单元)分为3种:
-
Management Frame
管理帧(wlan.fc.type == 0x00)
用于STA与AP之间协商、关系的控制。
-
Beacon Frame
信标帧(wlan.fc.type_subtype == 0x08)
一个提供服务的AP会定时发送Beacon Frame以告知BSS的存在,并提供了BSS的一些基本信息,如BSSID、SSID、Channel等。
-
Probe Request / Probe Response
探测请求(wlan.fc.type_subtype == 0x04)和探测响应(wlan.fc.type_subtype == 0x05)
Probe request由STA发出,用于探测周围的BSS。如果指定了SSID,则只有SSID与之一致的BSS(准确来说是BSS内的AP)会通过Probe Response进行响应;如果未指定,则所有BSS都会进行响应。和Beacon Frame一样,Probe Response提供了BSS的基本信息。
-
Authentication
认证(wlan.fc.type_subtype == 0x0B)
Authentication中,包含认证类型(Authentication Algorithm),认证进度(Authentication SEQ),认证状态(Status Code)。AP可以根据相关信息决定接受还是拒绝某个STA的加入,并同样通过Authentication进行回复。
-
Deauthentication
解除认证(wlan.fc.type_subtype == 0x0C)
用来终结认证关系。
-
Association Request / Association Response
关联请求(wlan.fc.type_subtype == 0x00) / 关联响应(wlan.fc.type_subtype == 0x01)
一旦STA找到网络并通过认证,就会发送Association Request,请求 AP 进行关联。AP以Association Response进行回应。如果AP接受该请求,将为该STA分配资源。
-
Reassociation Request / Reassociation Response
重关联请求(wlan.fc.type_subtype == 0x02) / 重关联响应(wlan.fc.type_subtype == 0x03)
当STA远离原AP,并发现相同的ESS里有另一个信号更强的AP时,STA将发送Reassociation Request。AP以Reassociation Response进行回应。一旦AP接受该请求,将为该STA分配资源,由此AP提供服务。
-
Disassociation
解除关联(wlan.fc.type_subtype == 0x0A)
用来终结关联关系。收到Disassociation后,AP将释放先前为该STA分配的资源。
-
-
Control Frame
控制帧(wlan.fc.type == 0x01)
用于竞争期间的握手通信和正向确认等,为数据帧的发送提供辅助功能。
-
Power Save - Poll
省电-轮询(wlan.fc.type_subtype == 0x0A)。休眠的AP定期发送。
-
Request To Send
请求发送(wlan.fc.type_subtype == 0x0B)
表明A要向B发送若干数据,申请预约。目的是为了避免同时有多人向B发送帧,导致冲突。
-
Clear To Send
清除发送(wlan.fc.type_subtype == 0x0C)
收到Request To Send后,如果B同意该预约,则通过Clear To Send宣告其他人在一定时间内暂停向自己发送数据,避免冲突。
-
ACK
确认收到的数据帧(wlan.fc.type_subtype == 0x0D)
如果收到的数据帧校验出错,则不发送ACK,等待重传。
-
CF-End
无竞争周期结束(wlan.fc.type_subtype == 0x0E)
让STA脱离PCF模式,开始以DCF(基于竞争)模式。
-
CF-End + CF-ACK
无竞争周期结束+确认(wlan.fc.type_subtype == 0x0F)
-
-
Data Frame
数据帧(wlan.fc.type == 0x02)
用于在竞争期(Contention Period)和非竞争期(Contention-free Period)传输数据。
-
Data
基本数据帧(wlan.fc.type_subtype == 0x00)
包含了通信的实际数据。
-
Null
没有数据的空帧(wlan.fc.type_subtype == 0x04)
但可以有其它的标记信息。
-
CF-ACK
无竞争周期的确认(wlan.fc.type_subtype == 0x05)
用于确认之前所收到的帧。
-
CF-Poll
无竞争周期的轮询(wlan.fc.type_subtype == 0x06)
用于通知已经没有数据要传输。
-
Qos Data
Data帧的 Qos 版本(wlan.fc.type_subtype == 0x08)
-
Qos Null
Null帧的 Qos 版本(wlan.fc.type_subtype == 0x0C)
此外还有各种帧的组合,如Data + CF-ACK,Data + CF-Poll、Qos Data + CF-ACK等,不再细述。
-
工作流程
为了真正了解802.11协议的工作流程,这里对一个STA加入到一个BSS的过程进行装包分析。打开Wireshark后,进入Capture-Input,选择要监听的WIFI网卡,将Link-Layer Header改为Per-Packet Information,将Monitor Mode改为enabled(双击修改)后开始监听。
-
STA通过侦听AP定期发送的Beacon Frame来发现BSS。
如图,可以发现空间中充斥着各个AP发送的Beacon Frame:
在这种方式下,STA几乎无需付出扫描代价,因此称为被动扫描。还有一种称为主动扫描的BSS发现方法,STA通过在信道上发出Probe Request帧,通过AP发回的Probe Response来发现BSS。如图,设备HTC在广播Probe Request后,收到了SSID为STAR的AP发来的Probe Response:
从图上可以发现在STA在发现STAR后双方又进行了多轮Probe。
-
当STA获取到该BSS的相关信息并确定想加入该BSS时,向相应AP发送Authentication Request
如图,双方通过发送Authentication帧进行认证:
我们发现这里的认证协议是Open System,即AP只对接入设备的MAC地址进行验证,只要MAC地址不被BAN,STA就可以通过认证。这样岂不是很不安全?非也非也,请接着往下看。
-
认证通过后,STA向该AP发送Association Request,AP收到后回复Association Response
-
至此,STA接入完毕,可以开始向AP传送数据帧
-
注意:在上图中,AP对STA发起了EAPOL认证
这是因为AP选择了WPA2(RSN)-PSK的认证方式。在这种认证方式下,AP和STA把预共享密钥(PSK)当作成对主密钥(PMK),并通过EAPOL协议进行四次握手,生成了成对临时密钥(PTK),用于加密后续通讯内容。
After EAPOL 1 and 2 both sides know the temporal key that will be used to decrypt the traffic.
The third message is proof that both sides know the temporal key and indicates that the Authenticator (the base station) is ready to start using the temporal key.
The fourth message triggers the switch from the PMK set up before the EAPOL to the temporal key derived in the EAPOL
-
双方通过Qos data帧交换数据
此时帧中的数据已被PTK加密:
应用
在学习了相关概念和流程后,可以折腾一些有趣的东西。
应用一 利用Wireshark进行无线网络抓包
Wireshark可以通过PSK算出PTK来解密数据。在Preferences-Protocols-IEEE 802.11中勾选Enable decryption:
点击Decryption Keys旁边的Edit按钮,新增类型的wpa-pwd的记录,Key为接入AP的PSK:SSID:
保存后发现先前被加密的数据被解密了!从链路层到应用层的报文一览无余:
这意味着我们可以方便地进行跨设备网络嗅探。曾经抓移动设备的包是一件十分蛋疼的事情,为此我尝试了各种姿势:
-
在移动设备上直接抓包
抓出来的包在移动设备上无论是查看还是操作都十分不方便,往往需要导出到电脑上来分析。
-
电脑做AP供移动设备连接,然后在电脑上抓包
由于RMBP只有无线网卡,一旦做了AP就连不了网,无法满足99%的应用场景。
-
路由器上抓包
最终我搞了一台极路由,刷了OpenWRT,然后直接在上面tcpdump,当然dump出来的文件依然要拷到电脑上分析才方便。这个方法唯一的不足是能够让你愉快地进行tcpdump的路由毕竟极少,日常生活中99%的路由都是只能192.168.1.1的弱鸡路由,比如上文所用的TPLink。
通过网络嗅探的方式,我们能够得到移动设备传输的包,再通过PTK解密得到包内容,不失为一种另类的抓包方法。
应用二 WIFI探针
通常来说,移动设备会经常发送Probe request来发现周围的BSS,而在Probe request中有设备的MAC地址。只要我们监控周围的Probe request帧,就可以知道是否有某个设备进入了BSS的范围。于是,我搞到了BOSS手机的MAC地址,写了一段代码,BOSS一旦进入到WIFI覆盖范围就弹出提示。
由于RMBP只有一张无线网卡,在进入monitor mode后会导致无法上网,为了避免该影响,考虑插入多一张网卡。一番寻找后,掏出了吃灰多年的360wifi2代(9块9包邮哦亲)。由于懒得折腾Mac上的驱动,因此转移到自带驱动的Ubuntu 16.04的虚拟机上进行。在插入网卡后,设备已经被识别:
$ lsusb
...
Bus 001 Device 004: ID 148f:760b Ralink Technology, Corp. MT7601U Wireless Adapter
$ ifconfig
...
wlx24050f2db588 Link encap:Ethernet HWaddr 24:05:0f:2d:b5:88
UP BROADCAST MULTICAST MTU:1500 Metric:1
RX packets:0 errors:0 dropped:0 overruns:0 frame:0
TX packets:0 errors:0 dropped:0 overruns:0 carrier:0
collisions:0 txqueuelen:1000
RX bytes:0 (0.0 B) TX bytes:0 (0.0 B)
进入monitor mode,设置监听信道:
$ sudo ip link set wlx24050f2db588 down
$ sudo iw dev wlx24050f2db588 set type monitor
$ sudo ip link set wlx24050f2db588 up
$ sudo iw dev wlx24050f2db588 set channel 1
此时看iwconfig,会发现wlx24050f2db588网卡的Mode为Monitor,Frequency为2.412 GHz。
以下是监听代码,依赖于scapy 2.3.2和manuf(https://github.com/coolbho3k/manuf.py):
from __future__ import print_function
from scapy.all import *
import manuf
def monitorDevice(pkt):
addr, target = None, None
if pkt.haslayer(Dot11ProbeReq):
addr = pkt.getlayer(Dot11).addr2
target = pkt.getlayer(Dot11ProbeReq).info or ''
if pkt.haslayer(Dot11ProbeResp):
addr = pkt.getlayer(Dot11).addr1
target = pkt.getlayer(Dot11ProbeResp).info or ''
if addr:
manuf = parser.get_manuf(addr) or 'Unknown'
print('Detected Devices: MAC[%s] Manuf[%s] Target[%s]' % (addr, manuf, target))
if MAC_DICT.get(addr) == 'BOSS':
print('[*] Warning!!! BOSS is coming!!!')
MAC_DICT = {'50:2e:5c:e5:e6:14': 'BOSS'}
interface = 'wlx24050f2db588'
parser = manuf.MacParser()
sniff(iface=interface, prn=monitorDevice)
这份代码除了监听Probe Request,还监听了Probe Response。当监听到BOSS手机的MAC,输出提示。于是我跑着这份代码,开始了愉悦的摸鱼生活~
第一天,我利用WIFI探针,成功躲过BOSS的袭击。
第二天,我被BOSS干死了,因为今天BOSS的手机没开WIFI。
第三天,我又被BOSS干死了,因为今天BOSS出门没带手机。
第四天,我又被BOSS干死了,因为今天BOSS竟然换IPhone了。
......
总结
我们必须承认,Wi-Fi的普及是人类史上的一次飞跃。拥有Wi-Fi,我们不再需要拖着长长的网线,不必担心路由器上的LAN口不够用,为移动设备的发展提供了良好条件。但同时也必须指出,我们在获取Wi-Fi便利的同时,也付出了相应的代价:当你在大街上行走时,路边不起眼的垃圾桶正监听移动设备发出Probe Request帧,为“大数据”增砖添瓦;当你进入餐馆,询问店家密码后愉快地连上WIFI刷朋友圈刷微博时,你的隐私可能正在店家甚至是其他人收集;当你在家里偷偷摸摸地下载小黄油时,隔壁老王发出叹息:“这部早看过了”。只有不断提高自己的姿势水平,才能保证自己的菊部安全。总之,出门在外,还是走Proxy全局加密吧~