被学校里的老师N层委派来打了这个比赛((

好久没有这样打这种个人赛了,一个人打五个方向知识有限真的吃力(

好在很多题目还是比较基础的,也是混上了一个学生组三等奖 :)

可惜后面团体赛我们阵营输了,我们队伍没拿上奖 :( 感觉还是得多练练渗透了(

Crypto

vigenere_server

题目

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
104
#!/usr/bin/env python3
# -*- coding: utf-8 -*-


import os
import socket
import threading

# ---------------- 可见 ASCII 94 字符表 ----------------
# 0x20–0x7E 共 94 个字符,空格到 ~
ALPHABET = ''.join([chr(i) for i in range(0x20, 0x7F)])
ALPHABET_SIZE = len(ALPHABET)

def char_index(ch: str) -> int:
"""字符 -> 在 94 字符表中的索引"""
return ord(ch) - 0x20

def index_char(idx: int) -> str:
"""索引 -> 对应字符(自动循环)"""
return chr((idx % ALPHABET_SIZE) + 0x20)

# ---------------- 加/解密实现 ----------------
def vigenere_enc(plain: str, key: str) -> str:
"""维吉尼亚加密(可见字符版)"""
cipher = []
for i, ch in enumerate(plain):
if ch not in ALPHABET: # 非可见字符原样保留
cipher.append(ch)
continue
k = key[i % len(key)] # 密钥循环使用
shift = char_index(k) # 密钥字符对应的偏移量
new_idx = (char_index(ch) + shift) % ALPHABET_SIZE
cipher.append(index_char(new_idx))
return ''.join(cipher)

def vigenere_dec(cipher: str, key: str) -> str:
"""维吉尼亚解密(可见字符版)"""
plain = []
for i, ch in enumerate(cipher):
if ch not in ALPHABET: # 非可见字符原样保留
plain.append(ch)
continue
k = key[i % len(key)]
shift = char_index(k)
new_idx = (char_index(ch) - shift) % ALPHABET_SIZE
plain.append(index_char(new_idx))
return ''.join(plain)

# ---------------- 网络服务配置 ----------------
KEY = "" # 固定密钥
GREETING_PLAIN = "Hello,good morning!How are you?" # 明文问候语
TRIGGER = "I'm fine,thank you!" # 触发 flag 的明文
FLAG_FILE = "flag" # flag 文件名(位于工作目录)

def handle_client(conn: socket.socket, addr):
"""处理单个客户端连接"""
print(f"[+] {addr} connected")
try:
# 1. 连接后立即发送加密后的问候语
greeting_cipher = vigenere_enc(GREETING_PLAIN, KEY)
conn.sendall((greeting_cipher + "\n").encode('ascii'))

# 2. 持续循环等待用户输入
while True:
data = conn.recv(4096) # 一次最多 4096 字节
if not data: # 客户端断开
break
cipher_line = data.decode('ascii', errors='ignore').rstrip('\r\n')
plain_line = vigenere_dec(cipher_line, KEY)

# 3. 判断是否触发 flag
if plain_line == TRIGGER:
try:
with open(FLAG_FILE, 'r', encoding='utf-8') as f:
flag = f.read().strip()
except FileNotFoundError:
flag = "FLAG_FILE_NOT_FOUND"
conn.sendall((vigenere_enc(flag, KEY) + "\n").encode('ascii'))
break # 发完 flag 断开
else:
conn.sendall(f"Decrypted: {plain_line}\n".encode('ascii'))
except ConnectionResetError:
pass
finally:
conn.close()
print(f"[-] {addr} disconnected")

def serve(host='0.0.0.0', port=9999):
"""启动 TCP 服务"""
with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) # 地址复用
s.bind((host, port))
s.listen(5) # 最大挂起连接数 5
print(f"[*] Listening on {host}:{port}")
while True:
conn, addr = s.accept()
# 每来一个连接开新线程处理
threading.Thread(target=handle_client, args=(conn, addr), daemon=True).start()

if __name__ == "__main__":
# 启动前检查 flag 文件是否存在
if not os.path.exists(FLAG_FILE):
print("[WARN] flag 文件不存在,请先创建")
serve()

读题,题目大致是要求先根据给出的明密文推出私钥,然后对指定明文加密发过去验证,最后再解发过来的被加密的flag

写个脚本解决,有的函数直接抄题目里的(

自己推出来的key其实有点不完整,后面蒙一下就行(

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
ALPHABET = ''.join([chr(i) for i in range(0x20, 0x7F)])
ALPHABET_SIZE = len(ALPHABET)
enc="u_LcYsM^UWeM[XhIX[`<Q^eb@__pY]%"
plain="Hello,good morning!How are you?"
key=""
for i in range(len(enc)):
key+=chr(0x20+(ord(enc[i])-ord(plain[i])%ALPHABET_SIZE))
print(key)



key="My_vigenere_key_is_safe!"
TRIGGER = "I'm fine,thank you!"
def char_index(ch: str) -> int:
"""字符 -> 在 94 字符表中的索引"""
return ord(ch) - 0x20

def index_char(idx: int) -> str:
"""索引 -> 对应字符(自动循环)"""
return chr((idx % ALPHABET_SIZE) + 0x20)
def vigenere_enc(plain: str, key: str) -> str:
"""维吉尼亚加密(可见字符版)"""
cipher = []
for i, ch in enumerate(plain):
if ch not in ALPHABET: # 非可见字符原样保留
cipher.append(ch)
continue
k = key[i % len(key)] # 密钥循环使用
shift = char_index(k) # 密钥字符对应的偏移量
new_idx = (char_index(ch) + shift) % ALPHABET_SIZE
cipher.append(index_char(new_idx))
return ''.join(cipher)
print(vigenere_enc(TRIGGER,key))




flag="4fA^e^vVxaxR~E+SI4?[Ty&u-]RpZ\\uNx'MDRw-R}kEq"
def vigenere_dec(cipher: str, key: str) -> str:
"""维吉尼亚解密(可见字符版)"""
plain = []
for i, ch in enumerate(cipher):
if ch not in ALPHABET: # 非可见字符原样保留
plain.append(ch)
continue
k = key[i % len(key)]
shift = char_index(k)
new_idx = (char_index(ch) - shift) % ALPHABET_SIZE
plain.append(index_char(new_idx))
return ''.join(plain)
print(vigenere_dec(flag,key))
#flag{v1g3n3r3_1s_@_gr3@t_crypt0_34gdf23r4we}

js!!!!

jsfuck

image-20250927145428374

维纳攻击

标准的维纳攻击

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
import gmpy2
from Crypto.Util.number import *
def continuedFra(x,y):
cf=[]
while y:
cf.append(x//y)
x,y=y,x%y
return cf
def gradualFra(cf):
numerator=0
denominator=1
for x in cf[::-1]:
numerator, denominator=denominator,x*denominator+numerator
return numerator,denominator
def solve_pq(a,b,c):
par=gmpy2.isqrt(b*b-4*a*c)
return (-b+par)//(2*a),(-b-par)//(2*a)
def getGradualFra(cf):
gf=[]
for i in range(1,len(cf)+1):
gf.append(gradualFra(cf[:i]))
return gf
def wienerAttack(e,n):
cf=continuedFra(e,n)
gf=getGradualFra(cf)
for d,k in gf:
if k==0:continue
if (e*d-1)%k!=0:
continue
phi=(e*d-1)//k
p,q=solve_pq(1,n-phi+1,n)
if p*q==n:
return d
n= 91510509432781975760096107767377368031971006305898876524065398755888465048733880775248518816640444331620686600617734933883674520683631664424444216875152972385753603001698383568597237868613120918436955398079663009003724465724879507817731759285758460762662824164026500606884671511702262581388638282794890192027
e= 26135083049930915264766519621014938119816864463605449201859665226084899098214765205357516917163302642828133788920140812606329233832519835056759832222374744306721856126632867337922339606510596531565510906786609997470993555429532739371539100062164271943073972387884530377949557546032895161688788115133699375651
c= 89474432234398707693013252794312396821604378051286372242798907187393288884658352126237918243202321235275486653344125988170718123195724674942704448081894833467667853715908719921049714079134833467442977053354875447786201963904815858599906088447249628150423378791886049597071980521393184316411224694315578400683
d=wienerAttack(e,n)
m=pow(c,d,n)
print(long_to_bytes(m))
#b'flag{20d61a61-c5dc-4629-9f44-bd885eb3e980}'

Web

ping

ping命令注入,空格用${IFS}绕,关键词用\截断绕过,无回显就写到文件里读

image-20250927145116152

image-20250927145132001

Reverse

ez_re

加密逻辑就是()((i<<4)|(i>>4))^0x55)&0xff

image-20250927145716297

image-20250927145823609

写脚本打表解密

1
2
3
4
5
6
7
8
9
enc=[0x33,0x93,0x43,0x23,0xe2,0xc0,0x56,0x2,0xa0,0x51,0x72,0x3,0xa0,0x23,0x72,0x3,0x51,0x12,0xa0,0x63,0x12,0x33,0x66,0x72,0x82]
dic={}
for i in range(128):
dic[(((i<<4)|(i>>4))^0x55)&0xff]=chr(i)
flag=''
for i in enc:
flag+=dic[i]
print(flag)
#flag{Y0u_@re_gre@t_ctf3r}

Misc

wherebx

从流量中提取到一张图片

download

看到一段加密逻辑,用的是AES-128-ECB

image-20250927150734064

找到flag

image-20250927150903686

image-20250927150917523

image-20250927150932158

一段密文

rot47+base64

image-20250927150629142

zsteg一把梭

image-20250927150523906