Gift
检查保护机。这题没给libc,我是通过 double free 以及打印fd的内容,判断版本应该是在2.27,小版本我直接用到了最高的去调试。
最多申请十个堆块,只能申请size为0x100或者0x60的堆块,申请后输入的长度比申请少0x10。
释放堆块存在UAF。
只能打印堆块+0x10后的内容。
这里的v2是有符号数,所以如果为负数时,就可以让一个堆块的fd指针加上一个较大的数。
这题花了挺多时间进行构造的,难点在于泄露地址后,再达成一次任意写会发现堆块申请次数不够用,要进行更加节省的构造才行。
想要达成任意写,如果是修改fd,这题又只有在申请堆块时才可以进行写堆块,那么就得申请三次才能达成,在次数紧张的情况下,我又没想到有其他布局可以节省泄露地址使用的次数,就想到了可以通过劫持 tcache bin,修改上面的内容,只需要两次就可以做到任意地址写,节省了一次。
在泄露出堆地址之后,修改fd指针,指向0x70堆块的前0x10位置,然后将堆块分配过去,进行修改0x70的fd指针。这里的三个堆块size是错开的,防止进入同一个tcache bin中。而选择修改0x70堆块覆盖tcache,是因为记录第一个被释放的0x70堆块比较靠前,如果是0x110的太靠后,可写长度不足以覆盖到。
这一步的构造算是最巧的一步了,当初也想了挺久的。在把堆块分配过去后,为了能够出现libc地址,需要去伪造一个size至少为0x420的堆块,所以我借助部分的tcache bin加上之前申请的堆块进行伪造大堆块,(同时这也是另外一个需要申请两个0x110堆块的理由,如果是两个0x70的堆块是不够长的);且需要将其释放掉,则在属于0x70的位置上填入了伪造堆块的地址,可以让堆块分配过来。
伪造fake chunk时,还需要伪造一个尾部堆块,保持堆块内存的连续性。
释放后成功获得libc地址,此时已经申请了八次堆块,还剩下两次申请机会。
两次刚好足够,一次填写地址,一次分配堆块达成任意写,这次就不要再用0x70分配,透支了两次,已经坏掉了。最开始是想要填入system地址的,但是因为堆块的前0x10都是已经填入了内容,使用了||貌似也没办法忽略,所以最后还是用了one_gadget getshell。
exp:
#!usr/bin/env python
#coding=utf-8
from pwn import *
context(arch = 'amd64',os = 'linux',log_level = 'debug')
elf = ELF('./pwn')
DEBUG = 0
if DEBUG:
libc = ELF("/home/shoucheng/tools/glibc-all-in-one/libs/2.27-3ubuntu1.6_amd64/libc-2.27.so")
ld = ELF("/home/shoucheng/tools/glibc-all-in-one/libs/2.27-3ubuntu1.6_amd64/ld-2.27.so")
p = process(argv=[ld.path,elf.path], env={
"LD_PRELOAD" : libc.path})
else:
ip = '123.56.45.214'
port = 33027
libc = ELF("./libc-2.27.so")
p = remote(ip, port)
def debug(info="b main"):
gdb.attach(p, info)
#gdb.attach(p, "b *$rebase(0x)")
def add(size, content):
p.sendlineafter(b"your choice:\n"