2024 NewStar CTF比赛有感

前情提要:帮老板娘在她们学校参加她们校内信息安全社的校内赛,笔者第一次了解到了CTF这个比赛,颇觉得有趣,于是撰写如下博客

一、CTF比赛

CTF(Capture The Flag,夺旗赛)是一种流行于网络安全技术人员之间的一种信息安全技术竞赛。 其前身是传统黑客之间网络技术比拼的游戏,以代替之前黑客们通过互相发起真实攻击进行技术比拼的方式。 起源于1996年第四届DEFCON。 现在已成为全球范围网络安全圈流行的竞赛形式。

比赛形式大抵是从加密串、网页、存在漏洞的可执行程序中找到形如flag{}的答案,所以被成为夺旗赛。

题目类型主要包含 Web 网络攻防RE 逆向工程Pwn 二进制漏洞利用Crypto 密码攻击Mobile 移动安全 以及 Misc 安全杂项 这六个类别。

  1. Misc 是英文 Miscellaneous 的前四个字母,杂项、混合体、大杂烩的意思。

    • Recon(信息搜集)

      主要介绍一些获取信息的渠道和一些利用百度、谷歌等搜索引擎的技巧

    • Encode(编码转换)

      主要介绍在 CTF 比赛中一些常见的编码形式以及转换的技巧和常见方式

    • Forensic && Stego(数字取证 && 隐写分析)

      隐写取证是 Misc 中最为重要的一块,包括文件分析、隐写、内存镜像分析和流量抓包分析等等,涉及巧妙的编码、隐藏数据、层层嵌套的文件中的文件,灵活利用搜索引擎获取所需要的信息等等。

  2. 密码学(Cryptography)一般可分为古典密码学和现代密码学。

    其中,古典密码学,作为一种实用性艺术存在,其编码和破译通常依赖于设计者和敌手的创造力与技巧,并没有对密码学原件进行清晰的定义。

    而现代密码学则起源于 20 世纪中后期出现的大量相关理论,1949 年香农(C. E. Shannon)发表了题为《保密系统的通信理论》的经典论文标志着现代密码学的开始。

  3. WEB 类的题目种类繁多,知识点细碎,时效性强,能紧跟时下热点漏洞,贴近实战。

    WEB 类的题目包括但不限于:SQL 注入、XSS 跨站脚本、CSRF 跨站请求伪造、文件上传、文件包含、框架安全、PHP 常见漏洞、代码审计等。

  4. PWN在黑客俚语中代表着攻破,取得权限,在CTF比赛中它代表着溢出类的题目,其中常见类型溢出漏洞有栈溢出、堆溢出。在CTF比赛中,线上比赛会有,但是比例不会太重,进入线下比赛,逆向和溢出则是战队实力的关键。主要考察参数选手漏洞挖掘和利用能力。

  5. RE(Reverse) 软件代码逆向主要指对软件的结构,流程,算法,代码等进行逆向拆解和分析。

二、部分题解

1. Base

6b6f323ec05d958e7ee4e1d1d648f460

易注意到当每两位看作一个十六进制数时,根据每两位的数值依据ASCII码表转化为一串字符串:

1
LJWXQ2C2GN2EGUKIZDFQ6SCNVMDATTZK5MEEMCNIX4U6TKGMQ4Q

这串编码符合Base32规范,使用Base32解码得:

1
ZmxhZ3tCQHNFXzBmX0NyWXB0MF9OMFd9

最后再使用一次Base64解码得:

1
flag{B@sE_0f_CrYpt0_N0W}

2. Strange King

52ef5ecf683355f1359cc7646056d6aa

注意到从flag转化为ksjr,单个字符的位移分别为+5 +7 +9 +11,猜想第i个字符的位移分别2i+3,编写如下C++程序进行求解:

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
#include <string>
#include <iostream>
#include <map>
using namespace std;
map<char, int> mp1, mp2;
int main() {
// abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ
string st = "ksjr{EcxvpdErSvcDgdgEzxqjql}";
int s = 0, temp = 0;
for (int ch = 'A', i = 0; ch <= 'Z'; ++ch, ++i) mp1[ch] = i;
for (int ch = 'a', i = 0; ch <= 'z'; ++ch, ++i) mp2[ch] = i;
for (auto &i : st) {
if (i >= 'a' && i <= 'z') {
temp = mp2[i];
temp = temp - s * 2 - 5;
while (temp < 0) temp += 26;
i = temp + 'a';
} else if (i >= 'A' && i <= 'Z') {
temp = mp1[i];
temp = temp - s * 2 - 5;
while (temp < 0) temp += 26;
i = temp + 'A';
}
s++;
}
cout << st << endl;
return 0;
}

得出答案:

1
flag{PleaseDoNotStopLearing}

3. Real Login

image-20240924100129264

解压附件得到pwn可执行程序,观察文件头为ELF,验证后确定为Linux平台x86可执行文件。

使用https://cloud.binary.ninja/网站进行反编译,观察func()函数流程,发现判定答案为NewStar!!!

image-20240924100423563

通过nc *.*.*.* 36951进行登录,输入NewStar!!!后进入/bin/sh环境,根目录下存在flag文件,即为最终答案:

1
flag{158d0bda-96c8-46cd-a7bb-fd36e1877257}

4. headach3

f5424f409acb0dcf2688176bed27b561

使用http请求工具请求该地址发现在http头中含有一项为flag,即为答案:

1
flag{You_Ar3_R3Ally_A_9ooD_d0ctor}

5. xor

b4b1dd812a688962b14cda40f6ed008f

使用异或操作反向解码出答案

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
from pwn import xor
from Crypto.Util.number import bytes_to_long, long_to_bytes
key = b'New_Star_CTF'
c1 = 8091799978721254458294926060841
c2 = b';:\x1c1<\x03>*\x10\x11u;'

k_long = bytes_to_long(key)

m1_long = c1 ^ k_long

m1_bytes = long_to_bytes(m1_long)
print("First part of the flag:", m1_bytes)

m2_bytes = xor(key, c2)
print("Second part of the flag:", m2_bytes)

flag = m1_bytes + m2_bytes
print("Recovered flag:", flag.decode())

最终输出的答案为:

1
flag{0ops!_you_know_XOR!}

6. 兑换码

5786caa033bb90cda63d95e4ba529b03

原图使用pngcheck发现CRC校验码不正确

1
荣花与炎日之途.png  CRC error in chunk IHDR (computed af857586, expected 1c204acf)

使用脚本枚举正确的宽:

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
import zlib
import struct
import sys

filename = sys.argv[1]
with open(filename, 'rb') as f:
all_b = f.read()
crc32key = int(all_b[29:33].hex(), 16)
data = bytearray(all_b[12:29])
n = 4095
for w in range(n):
width = bytearray(struct.pack('>i', w))
for h in range(n):
height = bytearray(struct.pack('>i', h))
for x in range(4):
data[x+4] = width[x]
data[x+8] = height[x]
crc32result = zlib.crc32(data)
if crc32result == crc32key:
print("宽为:", end="")
print(width)
print("高为:", end="")
print(height)
exit(0)

输出为:

1
2
3
$ python test.py 荣花与炎日之途.png 
宽为:bytearray(b'\x00\x00\n\x00')
高为:bytearray(b'\x00\x00\x04\xe9')

原长宽高记录为:

image-20240924102617793

修改为正确的长宽后图片为:

image-20240924102651169

答案即为:

1
flag{La_vaguelette}

7. 会赢吗

42fd05f71e5f5f0057d69ac2ab341143

进入第一部分,打开控制台,看到flag的第一部分,ZmxhZ3tXQTB3

image-20240924103016036

访问/4cqulsitiOn,进入下一环节:

image-20240924105338941

观察js脚本发现从/api/flag/4cqulsitiOn可以拿到flag的第二部分和nextLevel,得到第二部分flag为IV95NF9yM2Fs

1
2
3
4
{
"flag": "IV95NF9yM2Fs",
"nextLevel": "s34l"
}

访问/s34l

image-20240924105411800

观察得到,需要把id为state的元素textContent修改为解封,在控制台执行命令

1
document.getElementById('state').textContent="解封"

得到flag的第三部分MXlfR3I0c1B

image-20240924104000853

访问/Ap3x进入最后一个环节

image-20240924105441264

观察代码可知,需要我们手写一份绑定到按钮上的提交函数。

函数如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
const form = document.getElementById("winForm");
form.addEventListener('submit', async function (event) {

try {
const response = await fetch('/api/flag/Ap3x', {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({ csrf_token: document.getElementById('csrf_token').value })
});

if (response.ok) {
const data = await response.json();
console.log(data.flag);
} else {
console.log("Error");
}
} catch (error) {
console.log(error);
}
});

绑定完成后,再按下按钮,在控制台输出了flag的最后一部分fSkpKcyF9

至此,我们得出了完整的flag:ZmxhZ3tXQTB3IV95NF9yM2FsMXlfR3I0c1BfSkpKcyF9

最后将其使用base64解码得到答案为:

1
flag{WA0w!_y4_r3al1y_Gr4sP_JJJs!}

三、总结

这次比赛可谓是越大越上瘾,要不是老板娘及时制止我冲击某大学的前五名,我还能做出更多(笑)

作为一个圈外人,CTF比赛给我的感觉就是属于计算机爱好者的推理游戏。这种比赛涉及的知识面非常广,几乎遍及整个计算机领域,使得这不仅仅考察的是一个人的知识水平,更考验着人的综合水平——笔者愿意称其为计算机的直觉。这种直觉在进行计算机相关工作时发挥着隐形的、巨大的作用。例如在调试单个程序时,如果我们处于一个很大的项目中,单步跟踪可能存在一定的困难,此时我们如何通过其他方式调试这个程序?这个调试的思路往往反映着这个人的潜在的计算机思维。而这种比赛恰恰就考验着选手的这种潜在能力。这是笔者作为一个CTF的门外汉的一点点感想,欢迎各位提出不同的意见。