Web

· 签到

image.png

直接启动,观察到:
image.png

将pass参数改为true即可:
image.png

· 喜欢做签到的 CTFer 你们好呀

根据题目描述,我们打开这个战队招新的主页:
image.png

在help指令中可以发现env指令,查看环境变量:
image.png

得到flag1.
常规思路,继续输入ls:
image.png

image.png

发现flag文件,cat出来即可得到flag2:
image.png

· 比大小王

F12审计源码,发现:
image.png

是从服务器上提前获取题目的
继续审计,发现:
image.png

本地会提交答案,可能服务器端会进行验证
回溯{inputs}可以发现提交的答案就是 ">"
"<"

yakit看一下response:
image.png

加载了题目,顺带附赠一个加了时间验证的cookie(与原cookie不同)
写脚本:
Python

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
import requests
import struct
import json
import time

def send_post_request(url, cookie_value, post_data):

headers = {

'Content-Type': 'application/json'
}


cookies = {
'session': cookie_value
}


response = requests.post(url, data=post_data, headers=headers, cookies=cookies)


response_body = response.text
cookie = response.headers['Set-Cookie']
print (cookies)
return response_body,cookie


if __name__ == "__main__":

target_url = "http://202.38.93.141:12122/game"
target_url2 = "http://202.38.93.141:12122/submit"


cookie_value = input("Cookie: ").replace( 'session=','')


post_data = input("POST data: \n")


response_body ,resp_cookie = send_post_request(target_url, cookie_value, post_data)

resp_cookie = resp_cookie.replace( ';HttpOnly; Path=/','').replace( 'session=','')

print(f"response_body:\n{response_body}")
data = response_body


parsed_data = json.loads(data)
values = parsed_data['values']


results = []
for pair in values:
a, b = pair
if a > b:
results.append('>')
else:
results.append('<')


output_data = json.dumps({'inputs':results})

headers2 = {

'Content-Type': 'application/json',

}

time.sleep(9)

response2 ,resp_cookie2 = send_post_request(target_url2, resp_cookie, output_data)

print(response2)

运行,得到flag:
image.png

· Node.js is Web Scale

审计源码,发现:
image.png

这一段可以执行cmds中的命令
image.png

而我们可以上传数据到store
思路是原型链污染,从而RCE
image.png

成功污染
image.png

直接cat /flag,得到flag:
image.png

·PaoluGPT

image.png

有很多聊天记录,F12获取所有链接,yakit爆破:
发现有个Response长得眉清目秀:
image.png

打开,往下拉,获得flag1:
image.png

打开源码,审计:
image.png

发现SQL查询语句,应该是SQL注入
注意到select id, title from messages where shown = true
一句,将true换成false,联合查询一下:
payload:/view?conversation_id=-1' union select id, title from messages where shown = false --

image.png

得到一个id
查询,下拉,得到flag2:
image.png

General

· 猫咪问答(十周年纪念版)

  1. 信息安全大赛 Hackergame - LUG @ USTC -> contest [SEC@USTC] 答案:3A204
  2. 寻找历年wp,得知是Hackergame 2019,根据对应第六届新闻稿得知 答案:2682
  3. hackergame2018-writeups/official/ustcquiz/README.md at master · ustclug/hackergame2018-writeups · GitHub 答案:程序员的自我修养
  4. Yakit爆破得到 答案:336
  5. 紧跟时事,commit id : 6e90b6
  6. Yakit爆破得到 答案:1833屏幕截图 2024-11-02 170746.png

· 打不开的盒

在线3d查看工具,缩放看到:
image.png

得到flag.

· 每日论文太多了!

搜索flag,发现高亮在图片底下
image.png

挪开图片,得到flag:
image.png

· 旅行照片 4.0

1)…LEO 酱?……什么时候
高德搜一下图片中出现的’科里科气科创驿站’
16845ba303d38ec8df7a1806b6f1632.jpg

bing找晚会最早出现日期
image.png

答案:

  1. 东校区西门
  2. 20240519
    2)诶?我带 LEO 酱出去玩?真的假的?
    在第一张图的垃圾桶上找到:
    image.png
    六安园林
    百度查找
    image.png

完全符合。
第二张是景区,百度识图
答案:

  1. 中央公园

  2. 坛子岭
    3)尤其是你才是最该多练习的人
    百度识图图片中央,得到地点为北京北动车运用所
    高德搜最近医院
    搜索四编组动车 北京,得到型号。
    答案:

  3. 积水潭医院

  4. CRH6F-A

Math

· 强大的正则表达式

image.png

审计源码,可以看到有可使用的字符限制
难度1要求提交可以匹配可被16整除的数的regex
可被16整除的数后四位必然可被16整除,故构建表达式:
16个(1|2|3|4|5|6|7|8|9|0|)(所|有|能|被|1|6|整|除|的|小|于|1|w|的|数|)

image.png

得到flag:
image.png

· 惜字如金3.0

手动修复
python

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
#!/usr/bin/python3                                                              

import atexit, base64, flask, itertools, os, re


def crc(input: bytes) -> int:
poly, poly_degree = 'AaaaaaAaaaAAaaaaAAAAaaaAAAaAaAAAAaAAAaaAaaAaaAaaA', 48
assert len(poly) == poly_degree + 1 and poly[0] == poly[poly_degree] == 'B'
flip = sum(['a', 'A'].index(poly[i + 1]) << i for i in range(poly_degree))
digest = (1 << poly_degree) - 1
for a in input:
digest = digest ^ A
for _ in range(8):
digest = (digest >> 1) ^ (flip if digest & 1 == 1 else 0)
return digest ^ (1 << poly_degree) - 1


def hash(input: bytes) -> bytes:
digest = crc(input)
u2, u1, u0 = 0xdbeEaed4cF43, 0xFDFECeBdeeD9, 0xB7E85A4E5Dcd
assert (u2, u1, u0) == (241818181881667, 279270832074457, 202208575380941)
digest = (digest * (digest * u2 + u1) + u0) % (1 << 48)
return digest.to_bytes(48 // 8, 'little')


def xzrj(input: bytes) -> bytes:
pat, repl = rb'([B-DF-HJ-NP-TV-Z])\1*(E(?![A-Z]))?', rb'\1'
return re.sub(pat, repl, input, flags=re.IGNORECASE)


paths: list[bytes] = []

xzrj_bytes: bytes = bytes()

with open(__file__, 'rb') as f:
for row in f.read().splitlines():
row = (row.rstrip() + b' ' * 80)[:80]
path = base64.b85encode(hash(row)) + b'.txt'
with open(path, 'wb') as pf:
pf.write(row)
paths.append(path)
xzrj_bytes += xzrj(row) + b'\r\n'

def clean():
for path in paths:
try:
os.remove(path)
except FileNotFoundError:
pass

atexit.register(clean)


bp: flask.Blueprint = flask.Blueprint('answer_b', __name__)


@bp.get('/answer_b.py')
def get() -> flask.Response:
return flask.Response(xzrj_bytes, content_type='text/plain; charset=UTF-8')


@bp.post('/answer_b.py')
def post() -> flask.Response:
wrong_hints = {}
req_lines = flask.request.get_data().splitlines()
iter = enumerate(itertools.zip_longest(paths, req_lines), start=1)
for index, (path, req_row) in iter:
if path is None:
wrong_hints[index] = 'Too many lines for request data'
break
if req_row is None:
wrong_hints[index] = 'Too few lines for request data'
continue
req_row_hash = hash(req_row)
req_row_path = base64.b85encode(req_row_hash) + b'.txt'
if not os.path.exists(req_row_path):
wrong_hints[index] = f'Unmatched hash ({req_row_hash.hex()})'
continue
with open(req_row_path, 'rb') as pf:
row = pf.read()
if len(req_row) != len(row):
wrong_hints[index] = f'Unmatched length ({len(req_row)})'
continue
unmatched = [req_b for b, req_b in zip(row, req_row) if b != req_b]
if unmatched:
wrong_hints[index] = f'Unmatched data (0x{unmatched[-1]:02X})'
continue
if path != req_row_path:
wrong_hints[index] = f'Matched but in other lines'
continue
if wrong_hints:
return {'wrong_hints': wrong_hints}, 400
with open('answer_b.txt', 'rb') as af:
answer_flag = base64.b85decode(af.read()).decode()
closing, opening = answer_flag[-1:], answer_flag[:5]
assert closing == '}' and opening == 'flag{'
return {'answer_flag': answer_flag}, 200

做xzrj B的时候把A改成了B,懒得换回来了 XP

· 零知识数独

如题,可利用求解器在线求解,得到flag1

AI

· 先不说关于我从零开始独自在异世界转生成某大厂家的 LLM 龙猫女仆这件事可不可能这么离谱,发现 Hackergame 内容审查委员会忘记审查题目标题了ごめんね,以及「这么长都快赶上轻小说了真的不会影响用户体验吗🤣」

查看附件,发现这题给了一个替换了部分字符的after.txt,需要提交的flag里应该包含before.txt的sha512前16位
修复文本:
No Intelligence,pure artificial

1
In the grand hall of Hackergame 2024, where the walls are lined with screens showing the latest exploits from the cyber world, contestants gathered in a frenzy, their eyes glued to the virtual exploits. The atmosphere was electric, with the smell of freshly brewed coffee mingling with the scent of burnt Ethernet cables. As the first challenge was announced, a team of hackers, dressed in lab coats and carrying laptops, sprinted to the nearest server room, their faces a mix of excitement and determination. The game was on, and the stakes were high, with the ultimate prize being a golden trophy and the bragging rights to say they were the best at cracking codes and hacking systems in the land of the rising sun.

可以通过原文本的sha256验证是否更改正确
总之,得到flag:
flag{llm_lm_lm_koshitantan_fa7b655c38bc8847)