0%

2024玄盾珠峰决赛Writeup

[toc]

前言

书接上回,第一天的预赛如果大家还算理智和克制,那第二天的决赛现场的选手可能就跟打了鸡血一样,比赛分了企业组、学生组和政府组,每个组在最后半小时都是风云变幻,赛后甚至还有人打探到某组的第一名是花钱买的,去年相同级别的一个比赛花了2w,今年花了1w(难道市场化了?所以价格下跌了。。。)

企业组的前几名,复杂的附件题目哐哐秒答案,最简单的web和pwn题没人做,就不要太明显。

虽说各级别CTF比赛的PY交易从来都不罕见,但是如此混乱的赛场纪律真是第一次见。

比赛的时候有一道复杂一点的misc,到比赛结束后和队友交流的时候才发现,其实比赛的时候已经做到最后一步了,盲水印隐写已经拿到结果了,但是还是因为疏于练习,竟然连文件路径都搞错了,愣是没找到输出的盲水印结果。。。如果有这道题目的分数,可能起码还能混到一个奖金。

比赛结束后在赛场附近闯了个红灯,痛失6分+200大洋,没拿到奖金还搭进去200块,真悲摧。

btw:WP还没写完,写博客的时候,用hexo直接创建了post,deploy了之后就发布了,暂时不知道如何将layout为post的文章改为draft,就先这样吧,反正写起来也快。

Misc

hide flag

一道简单的word隐写,打开word全选字符串之后修改颜色,发现了密码

image-20240914224323711

那么肯定需要什么东西去解压了,docx本身也是一种压缩文件,将其解压之后,在word-media中发现了flag.zip

image-20240914224445440

构造字典爆破压缩密码,可以考虑直接用Advanced Archive Password Recovery,当时作为一名优雅且喜欢CLI的CTFer,图形化显然不够优雅。

首先用crunch构造字典

1
crunch 11 11 -t S1cRe7..%%% -o password.txt

同样优雅的用7z配合for循环爆破

1
for i in `cat password.txt`;do 7z x -p$i -aoa flag.zip >/dev/null 2>&1 && echo "Password found:$i"&&break;done

image-20240914231737380

手速不手速的不要紧,要紧的是优雅。

布达拉宫

binwalk分离出两张图片和一个加密压缩包,盲水印之后拿到压缩包密码,解压后统计词频拿到flag,空了再更新详细步骤吧。

Web

qiandao

ezjava

比赛的时候没时间看,赛后主办方也没给wp或者docker文件,所以没法继续做。

Crypto

EasyRSA

Revere

confidential

upx脱壳之后,可以在ida pro中看到flag的base64编码的字符串,解码之后拿到flag,不脱壳的话,base64编码不全,解出来的flag少一个字母和}

ezmaze

反编译后,查看字符串,发现了迷宫

image-20240820135431639

根据段数据内容,找到了对应的函数

image-20240820135519122

根据函数内容,确定了迷宫的入口出口,以及操作的方式,wsad分别代表上下左右,所以输出的路径就是代表上下左右方向的字符串

image-20240820135730510

确定了迷宫的情况之后,只需要找到路径就可以了。

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
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
from hashlib import md5
maze = [[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],
[0,1,0,1,0,1,0,1,1,1,1,1,0,1,1,1,1,1,0,1,0],
[0,1,0,1,0,1,0,0,0,0,0,1,0,0,0,0,0,1,0,1,0],
[0,1,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,1,0],
[0,1,0,0,0,1,0,0,0,1,0,1,0,0,0,1,0,1,0,1,0],
[0,1,0,1,1,1,1,1,0,1,0,1,0,1,1,1,0,1,0,1,0],
[0,1,0,1,0,1,0,0,0,0,0,1,0,0,0,1,0,0,0,1,0],
[0,1,0,1,0,1,1,1,1,1,0,1,1,1,0,1,0,1,1,1,1],
[0,1,0,0,0,1,0,1,0,1,0,0,0,1,0,1,0,1,0,1,0],
[0,1,1,1,1,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0],
[0,1,0,0,0,0,0,1,0,0,0,1,0,0,0,1,0,1,0,1,0],
[0,1,1,1,1,1,0,1,1,1,1,1,1,1,0,1,1,1,0,1,0],
[0,0,0,1,0,1,0,1,0,1,0,0,0,1,0,1,0,1,0,1,0],
[0,1,0,1,0,1,0,1,0,1,0,1,1,1,0,1,0,1,0,1,0],
[0,1,0,0,0,0,0,0,0,1,0,1,0,0,0,1,0,0,0,0,0],
['@',1,1,1,0,1,1,1,0,1,0,1,1,1,1,1,1,1,1,1,0],
[0,1,0,1,0,1,0,1,0,1,0,0,0,0,0,0,0,0,0,0,0],
[0,1,0,1,0,1,0,1,1,1,1,1,1,1,1,1,1,1,1,1,0],
[0,0,0,1,0,1,0,0,0,0,0,1,0,1,0,0,0,1,0,0,0],
[0,1,1,1,1,1,1,1,1,1,0,1,0,1,1,1,0,1,1,1,0]]

dirs = [(0,1),(1,0),(0,-1),(-1,0)]
contrl = ['d','s','a','w']
maze_path = []
def trans_dirs(direction):
"""
将移动坐标转换成对应的方向键
"""
return contrl[dirs.index(direction)]
def passable(maze,pos):
"""
判断当前所在位置是否可以通过,加try是为了判断当前位置在迷宫范围内
"""
try:
return maze[pos[0]][pos[1]] == 1
except:
return False
def mark(maze,pos):
"""
标记走过的位置
"""
maze[pos[0]][pos[1]] = 2
def direct_control(maze,start_pos,end_pos):

mark(maze,start_pos)
#如果起始位置和终止位置一致,则表示已到达终点
if start_pos==end_pos:
maze_path.append(start_pos)
return True
#根据四个方向移动位置
for i in range(4):
next_pos = start_pos[0]+dirs[i][0],start_pos[1]+dirs[i][1]
#判断当前位置通过性
if passable(maze,next_pos):
#循环调用
if direct_control(maze,next_pos,end_pos):
#print(next_pos)
maze_path.append(start_pos)
return True
return False
def view_path(maze,path):
"""
绘制迷宫和路径
"""
for i,p in enumerate(path):
if i==0:#路径出口
maze[p[0]][p[1]]='E'
print(p[0], p[1])
elif i==len(path)-1:#路径入口
maze[p[0]][p[1]]='S'
else:#路径其他位置
maze[p[0]][p[1]]=3

for r in maze:
for c in r:
if c==3:
print("\033[0;31m"+"* "+"\033[0m",end="")
elif c=='S' or c=='E':
print("\033[0;34m"+c+" "+"\033[0m", end="")
elif c==2:
print("\033[0;32m"+"# "+"\033[0m", end="")
elif c==0:
print("\033[0;40m"+" "+"\033[0m", end="")
else:
print(" "*2,end="")
print()
start_pos = (15, 0)
end_post = (7, 20)
direct_control(maze,start_pos,end_post)
view_path(maze,maze_path)
flag = ""

#转换移动坐标为路径方向
for i in range(len(maze_path)-1):
delta = (maze_path[i][0]-maze_path[i+1][0],
maze_path[i][1]-maze_path[i+1][1])
flag +=trans_dirs(delta)


flag_reverse = "".join(reversed(flag))
print(flag_reverse)
print(md5(bytes(flag_reverse.strip(), encoding='utf-8')).hexdigest())

image-20240820140856374

PWN

NoTeLanDer_v2

很简单的栈溢出,空了补充详细步骤。

Film_backstage_v2