HCI COMMAND 是蓝牙协议栈发送个芯片的命令
HCI EVENT 是蓝牙芯片上报给蓝牙协议栈的事件
HCI ACL 是蓝牙协议栈与蓝牙芯片双向交互的普通数据
HCI SCO 是蓝牙协议栈与蓝牙芯片双向交互的通话/语音识别数据
HCI ISO 是 BLE audio,是 core5.2 才添加的
沟通 host 和 controller,告诉他们数据格式是咋样的,比如告诉 controller 从 host 传过来的是什么数据格式
btsnoop 是用来抓取 host 和 controller 之间的交互数据,btsnoop 用来分析蓝牙的问题比较常用
文件存储形式是大端存储,所以直接读就行了
整个的文件格式只有两部分,File Header 和 Packet Record x
File Header 分为 Identification Pattern、Version Number、Datalink Type 分别表示:识别号、版本、数据格式下图这样
识别号其实就是 btsnoop 的 ascii 码值占用 8 字节,后面是 4 字节的版本,现在就只有 1
再往后 4 字节是 Datalink Type 的编号,对应的是数据类型,像上面 03 EA 换算成十进制就是 1002,也就是说,我这个 log 是 H4 的,安卓默认就是 H4,甚至在代码里直接写死的这个值http://www.aospxref.com/android-7.1.2_r39/xref/system/bt/hci/src/btsnoop.c
Packet Record 有 24 个 byte 分为 Original Length、Included Length、Packet Flags、Cumulative Drops、Timestamp Microseconds、Packet Data
Original Length,4byte 表示该数据包的长度,如果拆包的话他就比 Included Length 大了
Included Length,4bye 表示本包包含的数据长度
Packet Flags,4byte,表示这个数据包的标志,第 0 位如果是 0 表示数据方向是发送,如果是 1 表示数据方向是接受,第 1 位如果是 0 表示传输的是数据,如果是 1 表示传输的是 Command/Event
拿这个例子来看 Packet Flags 是 00 00 00 02,也就是说 Direction flag = 0,Command flag = 1,表示这个数据包是发出去的 Command
用 wireshark 打开验证一下
Cumulative Drops,4 byte 用来统计丢失的数据包的数量,数据包可能因为系统资源不足等原因丢失,如果没这个能力统计这个就设为 0
Timestamp Microseconds,8 byte 时间戳,但不是传统意义的时间戳,它是以公元 0 年 1 月 1 日开始的,而且是以微秒为单位,因此这个值巨大
Packet Data,后面就是数据部分了
接下来看一下后一个的 Packet Record
前面两个 00 00 00 07 是 Original Length 和 Included Length,00 00 00 03 表示这个数据包是收到的 Event,00 00 00 00 表示没有丢失的数据包,00 E2 C1 45 47 AA 57 D6 是时间戳,后面 04 0E 04 01 03 0C 00 是数据内容
esp32、micro-usb、杜邦线、usb->ttl、支持嗅探的无线网卡、BLE Sniffer
这是源码:
esp32 的 windows 烧录环境:,直接点进来下载离线安装包
[
我下载的是这个,无脑安装
完事之后出来了两个快捷方式
这俩用哪个都行,打开之后切换目录到源码的文件夹,cd esp32ctf_thu/thuctf/
输入命令:idf.py menuconfig
,稍等片刻会打开一个新界面,设置 Serial flasher config 的 Flash size 为 4MB
设置 Partition Table 的 Partition Table 为 Custom partition table CSV
选完之后 Q 保存退出
然后idf.py build
编译代码
等它编译一阵,完事之后就可以idf.py flash
了,出现 Connecting….. 的时候要摁住板子上的 BOOT 键
烧写完成之就可以关掉了,随便找个串口工具,选择波特率 115200 就能看到 log 了
题目其实是这里,烧录好之后拿着这个文件夹里的内容做题,里面有个 tar 包,是删掉了真实 flag 的源码,有些关卡需要分析源码才知道咋做,源码按照不同的题目方向分开了,很友好!
咱从头开始,先把 GND 和 23 号引脚连起来,如果前面已经供电了在连 GND 和 23 引脚需要断电重新供电,或者摁一下板子上的 EN 摁扭才能切换到硬件的题目这一方向
题目:将 GPIO18 抬高,持续 3s 即可获得 flag
1 |
|
此时日志如下
GPIO 是指板子上的一组引脚。这些引脚可以发送或接收电信号,但它们不是为任何特定目的而设计的,可以由我们通过编程来实现任意功能。这就是为什么它们被称为通用 IO(General-purpose input/output)
抬高就是给它供电,把板子上的 3.3V 或 5V 与他接起来就行了
题目:在 GPIO18 处构造出 1w 个上升沿
1 |
|
上升沿指的是数字电路中数字电平从低电平(数字 0)到高电平(数字 1)的一瞬间,下降沿同理
借助一个 TX 的引脚会一直输出这一特点来与 GPIO18 连起来,这样就可以啦
补充:上下拉是给 IO 一个默认的状态,上拉和下拉是指 GPIO 输出高电位(上拉)还是低电位(下拉),从程序设计的角度讲,上拉就是如果没有输入信号则此时 I/O 状态为 1,下拉相反
关于上下拉电阻看一下这个
试着理解一下代码的意思,给 GPIO18 注册了一个上升沿中断处理函数,函数的功能是 trigger+1,同时把 GPIO18 的上拉关掉,这样没有输入时候的 I/O 状态就不是 1,有输入的时候就会触发上升沿,这样 trigger 就会增加了
题目:在另一个串口处寻找第三个 flag
1 |
|
被晃了呜呜呜,这个板子上有个 TX2 我以为是这个呐,结果等了半天啥也没有,这个是让你分析代码,看一下用的哪一个 GPIO 作为 TX,通过 define 可以看到,TXD 是 GPIO4,那就把 GPIO4 接到 USB->TTL 的 RX 上就可以看到了
题目:连接板子目标端口,尝试获得 flag
1 | void network_init(){ |
这里得往上翻日志了,他随机指定了一个 wifi 名和密码,会去连接那个密码,用手机开个热点即可
ssid: kbmxet , password svtujgjb
日志里给出了 IP 和端口,用电脑也连接上开的热点
nc 一下,连上之后根据源码里的逻辑,发送 getflag 即可
题目:你知道他发给百度的 flag 么
此部分代码不完善,可能会因死循环爆栈导致重启,请见谅…(确实容易重启 🤣)
改为用电脑开这个热点,然后直接抓取网卡的流量,嗯,,他好像不会切换,得重新做一遍
电脑开启热点后会有一个新的网卡,就抓这个网卡即可
追踪 HTTP 流发现 flag
题目:flag 在空中
同时日志如下:
把无线网卡插到 kali 里面,得搞到无线网卡再做
题目:修改蓝牙名称并设置可被发现即可获得 flag
也是刚开始的日志中随机指定了蓝牙设备的名字
直接改手机的名字就行了,现在手机好像默认不被发现?在手机上点击扫描周围设备就可以了
题目:flag 在空中
那就抓包吧,开启 Hollong,直接全选广播包中就有,同时也确定了他的 MAC 地址和设备名
题目:分析 GATT 业务并获得 flag
用 nRF Connect 连接,一开始读,只有 DEEDBEEF
搜索源码里的 [+] bluetooth task III 定位到这里,我们写入的值与 flag2 进行了对比,通过则 open_task3 = 1
只有当 open_task3 = 1 时才会把真正的 flag 拷贝过去
发送 task2 的 flag
再次读取,转成 ASCII 码即可 THUCTF{WrItE_4_gA7T}
]]>这里用到了几个函数,copy_reverse 是用来逆序的
1 | void copy_reverse(const u_char *bytes, uint8_t *dest, size_t len) { |
1 | void calc_iv(connection_state_t *state) { |
iv 是在数据包中直接能找到的,在 LL_ENC_REQ 报文中是 ivm,意思是 master 的 iv
在 LL_ENC_RSP 报文中是 ivs,意思是 slave 的 iv
计算 iv 这里只需要把他们反转一下即可,因为在内存里放着的是逆序的,看一下 GDB 调试的
GDB 调试的方法,gdb crackle
然后 b 源码第几行
,比如b 608
下个断点,set args -i Legacy_pairing.pcapng
,然后点击运行即可
1 | gdb-peda$ p &state->ivm |
等执行完两个反转就得到了真实的 iv 值
1 | gdb-peda$ x/gx 0x55555555f800 |
STK 的生成方式在蓝牙的规范中用的是一个叫做 s1 的函数
1 | void calc_stk(connection_state_t *state, uint32_t numeric_key) { |
s1 这个函数需要 TK 的值和两个 random 的值,TK 需要转成小端序放在内存里,用 htobe32(numeric_key) 即可
两个 random 分别取高 8 字节拼接起来
看一下标准里咋说:For example if the 128-bit value r1 is 0x000F0E0D0C0B0A091122334455667788 then r1’is 0x1122334455667788. If the 128-bit value r2 is 0x010203040506070899AABBCCDDEEFF00 then r2’is 0x99AABBCCDDEEFF00。这明显是拿低位拼起来的啊,为啥这里是拿高位拼起来?还是说 wireshark 显示的是小端序?我理解不了了
1 | gdb-peda$ p &state->mrand |
直接监听在 8080 端口
1 | package main |
用 ListenAndServeTLS 来替换掉 ListenAndServe,需要指定服务端的证书和私钥
1 | package main |
这时候会提示风险,接受风险后正常访问
接下来使用 go 写一个客户端来进行连接
(github 上面下载的证书过期了,重新生成一下
openssl genrsa -out server.key 2048 #生成私钥
openssl req -new -x509 -key server.key -out server.crt -days 365 #生成证书
)
1 | package main |
请求的时候报了一个错误,意思是客户端认为证书不是由知名的 CA 签发的
修改客户端的代码,略过对证书的校验
1 | package main |
多数时候需要对服务端证书进行校验,而不是像上面那样忽略这个校验,通过校验我们可以确认:1、服务端传来的证书是由某个特定 CA 签发的(如果是 self-signed,也无妨)2、服务端传来的数字证书没有被中途篡改过
接下来建立一个属于自己的 CA
1 | #生成一个ca私钥 |
此时先用客户端加载 ca 证书
1 | package main |
服务端对客户端的证书进行校验
1 | #生成客户端私钥 |
1 |
|
1 | package main |
通是通了,但是抓包看不到区别
参考:
]]>打开 nRF Connect Desktop,选择 Bluetooth Low Energy
插入 nRF 52840,下拉选择设备
配置广播数据
配置配对参数
包括 IO 能力、MITM、OOB、LE Secure Connection 等
自定义服务与属性,可以针对某一个属性设置权限,是否需要配对等
]]>1、初始化状态向量 S(256 个字节,用来作为密钥流生成种子 1)按照升序给每个字节赋值
2、初始密钥由用户输入,长度任意,如果长度小于 256 字节则进行轮转,直到填满,例如密钥是 12345,则填入的是 12345123451234512345…. 这个轮转过程得到的 256 个字节的向量 T 用来作为密钥流生成种子 2
3、密钥流的生成:假设明文长度 datalen = 1024 个字节则:
1 | i=0; |
题目:
1 | #!/usr/bin/env python3 |
writeup:
虽然题目变量名改的乱七八糟的,但是看 OO0o 还是能看出来这是 RC4 加密,OO0o 就是生成 k 的那个过程,最后返回的结果也是 k,O0 就是初始化状态向量 S,在题目的最后把 S 和密文打印了出来
因为 RC4 是异或加密的,明文与密文长度是一致的,密钥的长度也是一致的,flag 最后一个应该是“{”,所以可以通过 “{“ ^ “W” 来得到最后一轮的 k,看一下它在 S 中的位置就得到了 t,又因为 i 表示循环的此时,所以 i 也是已知的
根据 t=(S[i] + S[j]) mod 256; 可以推算出来 j = (t - S[i]) mod 256,这样交换 S[i] 和 S[j] 就把一部分 S 变回上一轮加密的样子了,
根据 j=(j+S[i]) mod 256; 得到上一轮的 j = (j - S[i]) mod 356,同时 i 的值只是减了个 1
这样我们有了 i、j,就能再确定上一轮的 t 的值了
1 | enc=b'\x14\xe3s,\xcbq\xa8\xd1\x86\x12\xba\x9f\x88\xa9J}\xf5\xd8BF\x93x\x94\x8a\xdc\xdb\xa0\xb9O0SeJ^\xc7\xce\xcf\xe3\x1c\x10s\xe2\xb6\xceA\xfd\xd6\x87\x95W' |
然后打开软件就可以了,会自动识别串口号连接
拿一张 ID 卡放在低频识别区,点击低频卡操作区的读ID/HID卡
可以看到识别为一张 EM410X 的卡,实际这张卡是 T5xx 的,克隆为 ID 卡就会识别为 ID 卡,克隆为 HID 卡就会被识别为 HID 卡
点击克隆为HID卡
后再次点击读ID/HID卡
识别为了 HID 卡
在低频卡操作区还有读/写T5xx卡
的选项,我们先读一下卡上的值
然后往第四个块写 0x321
然后再读取(这里不知道是设备的问题还是软件的问题,写完之后再读取需要重新拔插设备)
把小区的门禁卡放在高频识别区域,点击一键自动解析
识别为 UID 卡
将区块读取到编辑区
结束了,我没有高频的卡 2333
]]>类型 | 缩写 | 频率范围 | 典型应用频率 | 示例应用 |
---|---|---|---|---|
甚低频 | VLF(Very Low Frequency) | 3kHz - 30KHz | ||
低频 | LF (Low Frequency) | 30kHz ~ 300kHz | 125kHz,133kHz | RFID、导航、AM |
长波广播 | ||||
中频 | MF(Medium Frequency) | 300 kHz - 3 MHz | ||
AM | ||||
中波广播,业余广播 | ||||
高频 | HF (High Frequency) | 3MHz ~ 30MHz | 13.56MHz | 短波广播;RFID |
甚高频 | VHF(Very High Frequency) | 30 MHz - 300 MHz | ||
FM,电视 |
节目,地对飞机
和飞机对飞机通信 |
| 极高频 | UHF(Ultra High Frequency) | 300 MHz - 3 GHz | 860MHz ~ 960MHz | 电视广播,微波炉 |
| 超高频 | SHF(Super High Frequency) | 3 GHz - 30 GHz | 2.45GHz,5.8GHz | 射电天文学,无线局域网 |
| 极高频 | EHF(Extremely High Frequency) | 30 GHz - 300 GHz |
| 射电天文学,微波遥感 |
|
|
|
|
| 潜艇通信 |
Radio Frequency Identification(RFID),射频识别,俗称射频标签、电子标签,是应用射频技术进行近距离无线识别的一种技术,是一种广泛使用的自动识别技术,通过无线射频方式进行非接触双向数据通信。RFID 使用专用的 RFID 读写器及专用 RFID 标签,利用频率信号将信息由 RFID 标签传送至 RFID 读写器,通过无线电信号识别特定目标并读写相关数据。RFID(射频识别)是应用射频技术进行近距离无线识别的一种技术,属于射频技术的一具体应用。应该说 RFID 涉及到到 RF(射频)技术。
RFID 应用:服装标签、门禁控制、身份证、进出停车场
Near Field Communication(NFC)近场通信,NFC 通常在几厘米的距离内工作。近场通信(NFC)是智能手机和类似设备通过相互触摸或紧密靠近以彼此之间建立无线电通信的一套标准与射频卡进行通讯的技术,叫做近场通讯技术,简称 NFC。
NFC 由免接触式射频识别(RFID)演变而来,并向下兼容 RFID,NFC 可以理解为 RFID 技术的一个子集,使用的是 13.56MHz 频段,而 RFID 还包括其他频段。RFID 的工作频段有很多,低频段有 125KHz,高频段有 13.56MHz,超高频段有 433.92MHz、915MHz,还有微波频段的 2.45GHz 等。
|
| RFID | NFC |
| — | — | — |
| 频率 | 低、高、超高 | 高频(13.56MHz) |
| 通信距离 | 长短距离均可通信 | 短距离(几毫米、几厘米) |
| 通信方向 | 单向读取 | 双向读写 |
| 能耗 | 高 | 低 |
| 主要作用 | 识别 | 识别、数据交换 |
| 应用领域 | 身份证、资产管理、物品识别(物流、零售,UHF) | 门禁、公交卡、手机支付等 |
类型 | 简称 | 频率 | 特性 | 应用 |
---|---|---|---|---|
EM4XX | ID 卡 | 低频 | 常用固化 ID 卡,出厂固化 ID,只能读不能写 | 低成本门禁卡,小区门禁卡,停车场门禁卡 |
T5577 | T5577 卡/可修改 ID 卡 | 低频 | 出厂为空卡,内有三区也可储存数据,个别三区可设置密码 | 可用于克隆 ID 卡,写入 ID 号可以变身成为 ID 卡,写入 HID 号可以变身 HID 卡,写入 Indala 卡号,可以变身 Indala 卡 |
HID ProxⅡ | HID 卡 | 低频 | 美国常用低频卡,可擦写 | |
Mifare UltraLight | M0 卡 | 高频 | 固化 UID,可储存修改数据,比 M1 卡容量更小、功能更少 | 地铁卡,公交卡 |
Mifare S50 | M1 卡 | 高频 | 最常见,每张卡有全球唯一的 UID 号,可保存修改数据 | 学生卡,饭卡,公交卡,门禁卡 |
Mifare UID | UID 卡 | 高频 | M1 卡的变种,0 扇区的数据可以修改,因此可修改 UID | 可用于克隆 M1 的数据 |
FUID 卡 | 高频 | FUID 只能修改一次,修改后和 M1 卡完全一样 | 新的读卡系统能够通过检测卡片对特殊指令的回应检查是不是 UID 卡,用 FUID 卡可以绕过检测 | |
CUID 卡 | 高频 | 对 FUID 进行了优化,可以重复修改 0 扇区块 |
ACR112U | PN532 | Proxmark3 | |
---|---|---|---|
低频 ID | 不支持 | 不支持 | 支持 |
低频 HID | 不支持 | 不支持 | 支持 |
T5577 | 不支持 | 不支持 | 支持 |
UID 卡 | 支持 | 支持 | 支持 |
CUID 卡 | 支持 | 支持 | 支持 |
FUID 卡 | 支持 | 支持 | 支持 |
UFUID 卡 | 支持 | 支持 | 支持 |
半加密 M1 | 成功率 90% | 成功率 90% | 成功率 99% |
全加密 M1 | 不支持 | 成功率 40% | 成功率 99% |
存数据文件 | 支持 | 支持 | 支持 |
用文件写卡 | 支持 | 支持 | 支持 |
价格 | 150 | 65 | 399 |
特点 | 写卡速度快 | 写卡速度快 | 读卡类型广 |
参考:
https://ssooking.github.io/2020/09/rf-rfid-nfc%E7%AE%80%E4%BB%8B
https://lzy-wi.github.io/2018/07/26/proxmark3
https://cloud.tencent.com/developer/article/1490410
1 | 查看运行状态:systemctl status NetworkManager |
扫描 wifi
1 | sudo nmcli dev wifi |
连接 wifi,默认使用最后的 wifi 网卡,比如有 wlan0 和 wlan1 的话就用的 wlan1
1 | sudo nmcli dev wifi connect "yichen" password "123456789" |
可以使用 ifname wlan0 指定无线网卡
1 | sudo nmcli dev wifi connect "yichen" password "123456789" ifname wlan0 |
查看连接的信息
1 | nmcli con show |
删除连接
1 | sudo nmcli con del 连接的uuid |
OBD 线,接出来两根线,红高蓝低
用一个 12v 的电源适配器给他供电,用 usb 线连电脑使用配套软件设置相关数据,首先是要配置好协议
设置故障码,具体的含义在 ISO_15031-6
设置数据流
设置版本信息
再用 OBD 的接头连接好 pcan 的 can 高和低,用 pcanview 操作,读取数据,使用的是 15765 这个协议的,这里有个整理了一些操作指令的 pdf
OBD2_iso15765_protol_incomplete.pdf
根据 ISO 15031-5 的 6.3 节,使用 7DF 01 03 查看
因为设置的大于三个因为还没有读完全,再次发送 7E0 30
根据 ISO 15031-5 的 6.9 节,使用 02 09 02 查看,然后再发 30 …. 查看剩下的
在上面文档找的,具体在哪里定义的还不知道
使用前需要先激活协议
连接 usb 后不用 OBD 模拟器打开,使用串口工具选择波特率 256000
设置 VIN 码:AT+VIN:12345678901234567
AT+DTC02:P1012;P1013
前面是数量,后面是具体的故障码
使用 Jlink 或 Jlink OB 连接 OBD 模拟器
然后打开 JFlash,这样设置
点击 Options 的 Connect 即可连接设备,选择 Target -> Manual Programming -> Read back -> Entire chip
成功提取固件
File -> Open data file 打开固件
选择 Target Production Programming 就能烧写进去了
]]>然后把 jtagulator 其他接口依次连接到板子的可疑接口上,我这边线不多,就接了这么几个
然后打开串口收发工具(putty)设置波特率 115200,选择好端口,因为已经在运行了,所以进去之后回车一下即可,会展示出 jtagulator 的 logo
输入 h 获取帮助,我们想要探测的是 jtag 的接口,所以输入 j,进入后前面的尖括号就是 JTAG 开头的了,同样是输入 h 查看帮助
然后设置电压,这步好像没啥用,应该是 jtagulator 板子上的 vadj 用的给设备供电的?总之不设置不能继续反正我也没插这个接头,随便设置
再次输入 j 开始进入探测流程,要设置好你插入了哪几根线,就是 jtagulator 板子上的 CHxx 通道,我这里是从 0 到 5,不输入回车就是按照中括号里面的值使用,然后摁下空格开始自动探测,这时候 jtagulator 板子上的绿灯会闪烁,探测完成会展示出结果
]]>首先收集 OOB 信息啥的,然后开始 ssp 的配对,先发一个 Sent Write Simple Pairing Mode
然后接收一个 Rcvd Command Complete (Write Simple Pairing Mode) 这样就是 enable ssp 这一步了
然后 Sent Set Event Mask –> bthci_cmd.opcode = 0x0c01
然后交换 IO 能力,这一步与 BLE 一样,会决定 Authentication 的算法
最后 Rcvd Simple Pairing Complete 表示配对完成,但是有些设备,有头没尾,只有 ssp 的请求,没有 complete 还不知道为啥
]]>busbus = dbus.SystemBus()
busbus
传进去,会返回一个列表,里面每个都是一个对象,他的顺序是倒着的get_object
获取代理对象1 | def find_adapter(bus): |
这样就把适配器选出来了
1 | adapter_obj = bus.get_object("org.bluez", adapter) |
这里就涉及到 bluez 的 api 了,在 adapter-api 处定义了
#3 这个是打开适配器,效果应该是hciconfig hci0 up
开头附 bluez 定义好的广播类
1 | class Advertisement(dbus.service.Object): |
获取到 Advertising 的管理的接口
1 | ad_manager = dbus.Interface(adapter_obj, "org.bluez.LEAdvertisingManager1") |
然后先使用已经定义好的广播类,去创建一个广播对象
1 | advertisement = Advertisement(bus, 0, "peripheral") |
然后 RegisterAdvertisement 注册和使能广播接口,需要告诉 bluez 我们的 advertisement 在哪里,并且设置应答或错误处理函数
1 | mainloop = MainLoop() |
成功克隆出一个可以欺骗 APP 的广播包(他检测的是 service_uuid)
首先注册 GATT 的管理接口
1 | service_manager = dbus.Interface(adapter_obj, "org.bluez.GattManager1") |
定义一个 application,定义一个 service,给 service 添加 characteristic 然后把 service 给 application
1 | app = Application(bus) |
为了方便,直接定义了一个 TestCharacteristic 类,继承自官方示例中的 Characteristic 类
1 | class TestCharacteristic(Characteristic): |
最后注册 GATT,这样就可以成功识别到特性了,读写这类的属性也可以显示出来
1 | service_manager.RegisterApplication(app.get_path(),{},reply_handler=register_app_cb,error_handler=[register_app_error_cb],) |
接下来解决数据收发的问题
如何检测外设是否可连接(connectable)
广播报文如何定义可连接与不可连接
gatt 如何设置不配对仅连接,以及如何设置各种配对的 IO 能力
bluetoothctl
进入交互模式menu advertise
进入 advertise,这里可以设置广播数据通过manufacturer 0xffff 0x12 0x34
和name yichen
设置厂商数据和设备名
然后back
出来,使用advertise on
开启广播,然后搜索看一下
menu gatt
进入 gatt 的设置register-service ea349e00-08dc-4bb7-bc23-855c76c425fa 0x42
设置服务的 uuid 以及句柄register-characteristic 0x1234 read
设置 uuid 为 0x1234 的特性是可读的,回车后可以输入值register-characteristic 0x5678 read,write
设置 uuid 为 0x5678 的特性是可读写的,写入后会保存
https://github.com/securing/gattacker
https://github.com/DigitalSecurity/btlejuice
需要两个蓝牙适配器,均采用 CSR4.0
软件方面我用的 ubuntu 18.04 一个安装好之后直接克隆一个
1 | curl -sL https://deb.nodesource.com/setup\_8.x | sudo -E bash |
第一台机器执行:sudo btlejuice-proxy
第二台机器运行:sudo btlejuice -u 192.168.127.139 -w
(ip 是第一台机器的)
然后在第二台机器上就可以打开浏览器访问 http://127.0.0.1:8080 了
上面的四个标志依次是:拦截开关、导出数据、选择目标、设置
选择目标后会用第一台机器的适配器与目标连接,获取目标的服务与特征,然后用第二台机器的适配器为造出一个假的目标,因为目标已经与第一台设备的适配器连接了,所以你就扫描不到了,再扫描出来的就是第二台设备伪造的假目标了
扫描出来的目标,双击选中
过一小会再扫描就是适配器模拟的假目标了,我这个不知道为啥,只探测到了一个服务
试一下蓝牙灯泡,基本都探测出来了,用 APP 也能扫描到,但是连接上之后会断开,再点击 APP 就没法连接到它了
开启拦截开关会在收到数据的时候先展示给你,由你选择是发出去还是扔掉,也可以在发的时候修改,类似 burp 的抓包改包
]]>emqx start
服务就启动了,访问浏览器 ip:18083 就可以来到 WEB 端的控制台,默认密码 admin/public默认情况下,连账号密码都不用,mqttx 直接填好地址和端口就行,其他的随意
去 WEB 端插件页面把 emqx_auth_mnesia 插件启动了,就需要账号密码认证了
然后找到下载的文件夹里的 etc 目录里的 emqx.conf 搜索 allow_anonymous 改为 false
去 etc/plugins 下面把想要使用的用户名去掉注释或者自己根据他的样子写一个
然后重启 emqx 的服务 emqx restart
然后就可以使用 MQTTX 去测试一下是否生效了,随便输个密码报错了
MQTTX 这个软件的输入框上面那个 Retain 是保留消息的意思,勾上这个之后发出去的消息当再次连接上就会收到,重复发送会覆盖,broker 为每个主题仅存储一条保留消息
除了保留消息还有遗嘱消息,遗嘱消息是针对客户端可能因为未知的原因掉线、连接不正常的,客户端指定一条消息作为遗嘱消息发送给 broker 后,当 broker 检测到客户端连接不正常断开的话会将它的遗嘱消息发送给所在主题的所有客户端,如果是正确的 disconnect 则 broker 会丢弃存储的遗嘱消息
在 MQTTX 里面遗嘱消息在建立连接时设置
还有一个 keep live 的报文,过一段时间双方确认一下是不是还在线,MQTTX 也是在连接时设置即可
接下来抓包分析一下,客户端向服务器(broker)建立连接的请求,除了图中标出来的,还有 keep alive 的时间
这是包含遗嘱消息建立连接的流量包
服务端返回的响应
主要是看这个 return code
返回码 | 返回码响应 |
---|---|
0 | 已接受连接 |
1 | 连接被拒绝,协议版本不可接受 |
2 | 连接被拒绝,标识符被拒绝 |
3 | 连接被拒绝,服务器不可用 |
4 | 连接被拒绝,用户名或密码错误 |
5 | 连接被拒绝,未授权 |
发布消息
这里的 Message Identifier 与 HiveMQ 这篇文章中的 packetId 应该是一个东西我觉着?用来唯一的标识一条消息
这里的 QoS 是服务质量,为 0 时最多发送一次,发送方不保证能发到,接收方也不确认收到消息,消息也不会被存储和重新发送
QoS 为 1 时保证一条消息至少一次发送给接收方,发送方会保存消息,直到从接收方收到了确认收到消息的 ACK 包,就像上面那个截图一样
QoS 为 2 时保证每条消息仅由预期接收者接收一次,双方之间有四次握手(这里面夹杂的那条从 broker 发回来的消息是在他们四次握手结束之前就发回来了?)
客户端订阅,这里面有订阅的主题和请求订阅的质量,服务端也会给一个 ACK
取消订阅也一样
在 MQTT 中,客户端发布或订阅之前不需要事先创建主题,服务端(broker)接受每个有效的主题而无需事先初始化
主题必须至少包含一个字符,区分大小写,一些主题的示例:
1 | myhome/livingroom/temperature |
根据 官方文档 中的自签名做就行
首先生成一个自签名的 CA 证书,生成一个长度为 2048 的密钥保存在 ca.key 中openssl genrsa -out ca.key 2048
生成 emqx 根证书openssl req -x509 -new -nodes -key ca.key -sha256 -days 3650 -out ca.pem
有了根证书就可以用来给其他实体签发实体证书
先给 emqx 生成一个私钥openssl genrsa -out emqx.key 2048
建立一个 openssl.cnf 文件
1 | [req] |
以私钥和配置文件签发一个证书openssl req -new -key ./emqx.key -config openssl.cnf -out emqx.csr
以根证书签发 emqx 实体证书openssl x509 -req -in ./emqx.csr -CA ca.pem -CAkey ca.key -CAcreateserial -out emqx.pem -days 3650 -sha256 -extensions v3_req -extfile openssl.cnf
将生成的 emqx.pem、emqx.key、ca.pem 放在 emqx 的 etc/certs 目录下,修改 etc 目录下的 emqx.conf
1 | listener.ssl.external = 8883 |
然后打开 MQTTX 配置好就可以连接了
这样再连接后就是加密的了
问题在于,我密钥证书啥的都有,我该怎么解密 Orz。知道了,麻了,用了 ECDHE,即使有私钥也解不了
[
MQTT-WebSoket 统一使用 /path 作为连接路径,连接时需指明,在 EMQX 上使用的路径为 /mqtt
需要用域名访问,直接改 hosts 文件就行,(把 MQTTX 的 SSL 安全给关掉)
使用的库:pip install paho-mqtt
里面用的 broker.emqx.io 是 emqx 提供的公共服务器,免费试用
发布:
1 | import random |
订阅:
1 | import random |
如果是像上面那样开启了认证的话,可以在 connect 之前加上
1 | client.username_pw_set(username='admin', password='public') |
比如我上面那个自己搭建的
如果使用 ws 的话,直接加上个 transport 改好端口即可
1 | client = mqtt_client.Client(client_id,transport="websockets") |
想要用 tls 的话,需要导入 ssl 库,端口需要改到对应的端口,这里面的 ssl.CERT_NONE 主要是取消一些验证
1 | client.tls_set(ca_certs="./emqx/ca.pem",cert_reqs = ssl.CERT_NONE) |
如果使用 wss 的话,就两个缝合一下
参考:
mqtt-essentials-wrap-up (HiveMQ 团队写的 MQTT 要点总结)
下载下来是一个蓝牙流量包,随便翻翻发现存在 SMP 协议,全称是 Secure Manager Protocol,是蓝牙用来定义配对和密钥分发的
配对后的流量是被加密的,但是有个工具 crackle 是可以解密这种数据包的,这个工具解密蓝牙流量有三个前提,这也在官方的 FAQ 中提到了:
https://github.com/mikeryan/crackle/blob/master/FAQ.md
首先要有完整的配对过程流量,要使用链路层加密(有些开发者会自己实现加密),且只适合于传统配对(legacy pairing)
安装 crackle
1 | git clone https://github.com/mikeryan/crackle.git |
如果提示下图则需要安装相关依赖 sudo apt-get install libpcap-dev
安装完成后我们使用 crackle 解密发现如下提示,它不支持 BLUETOOTH_LE_LL
但是在它的 issues 中发现有人讨论过这个问题
用这个人的 crackle 再解一遍发现已经可以识别
加个参数 -o 可以保存出解密的数据包
1 | crackle -i uploads_2022_04_11_h5kAcZEg_ble.pcapng -o de.pcapng |
保存出来的数据包用 wireshark 打开后发现了一些值
把这些值提取出来之后发现一共有四组不同的,对他们进行两次十六进制编码后拼接,得到了 flag
仿的 Cyber Apocalypse 2021 Low Energy Crypto
wireshark 打开后并没有发现类似上一题那样的配对流量,但是翻了翻流量,发现了一些交互,RX 收到了下面这些信息
复制出来是一个公钥,直接保存为 pub.key:
1 | -----BEGIN PUBLIC KEY----- |
往下翻翻,发现了一些 TX 的数据,应该是用私钥加密后发出来的
密码学不太懂,直接用 RsaCtfTool 来解密,把密文复制出来的时候选择 as a Hex Stream:
然后用 winhex 创建一个新文件 enc,直接贴过来 ASCII Hex 格式
然后把后面的一堆 0 删掉,它正好是对齐的
python RsaCtfTool.py --publickey ./pub.key --uncipherfile ./enc
直接跑出来
1 | import asyncio |
通过 BleakScanner.discover() 扫描出来的结果是一个列表,列表的每个是一个 bleak.backends.device.BLEDevice 类,bleak 的源代码中定义在这里:bleak\backends\device.py
可以看出来每个设备有 address(设备地址,字符串)、name(设备名,字符串)、details(详情,包含了所有的信息,字典)、rssi(信号强度,整数)、metadata(包含服务 UUID 和 manufacturer_data,字典)
1 | client = BleakClient(address,adapter="hci0")#使用adapter指定蓝牙适配器 |
1 | await client.disconnect() |
1 | async def scan_services(address): |
这个 .get_services() 返回的也是一个列表,里面每个都是一个 bleak.backends.bluezdbus.service.BleakGATTServiceBlueZDBus 类,可以用 .uuid 获取 uuid(字符串)
1 | async def scan_characteristics(address): |
对于一个服务来说用 .characteristics 获取 characteristic 列表,对于获取到的 characteristic 列表可以遍历出每一个,通过 .uuid 获取 uuid,通过 .handle 获取句柄的值(十进制整数)通过 properties 来获取特性的属性(读写啥的)
获取到之后对于 read 的特征属性可以通过 read_gatt_char(char.uuid) 来获取相应的值await client.read_gatt_char(char.uuid)
这样读出来是一个 bytearray,使用 value = bytes() 可以转成 bytes
可以通过 write_gatt_char 写相应的值,传递的参数是特征的 UUID 或者句柄await client.write_gatt_char(0x2b,b'd205303e099ceff44835')
问题是传递的值有时候传字符串,有时候传十六进制,例如 BLECTF 的这一关和这一关
关于这俩格式的转换,这个下面的代码可以看出来,所以分成两个函数来用
这里写的数据都是可以是这里面的任何一种 bytes, bytearray, memoryview,所以代码中的 #1 这里的 bytes 可有可无
1 | async def write_value_raw(client,charuuid,value): |
好像是这俩放在一个函数实现了,我确实也可以用这一个函数监听的到
1 | address = "08:3A:F2:B9:85:92" |
start_notify()传入的参数是特征的 uuid,以及回调函数,即监听到之后干啥
想要监听到通知或者指示需要先使能特征值,这个 write 应该就是使能的过程
最后使用 stop_notify(char_uuid) 停止
瞻仰一下神器的 Windows 操作界面
打开应用程序后要设置好 wireshark 的路径,点击开始后自动打开 wireshark,这时候不会抓取,最右边一列 Select 勾上哪个抓哪个,无敌
]]>