2025 第十届上海市大学生网络安全大赛暨"磐石行动"-初赛

本文最后更新于 2025年8月12日 凌晨

Web

ezDecryption

f12 找到 hint,第一关验证码为 2025

抓包修改数据包直接跳过第二关

查看 js 代码,发现有 jsFuck 加密和 base 加密

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

解密密文构造 secretCode

1
panshi2oZ5

拿到 flag

1
flag{d1g1t4l_l0ck_br34k3r_2025}

Reverse

EasyRe

定位到 mian 函数,输入的字符串经过加密后进行逐字节比较是否相同

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
int __fastcall main(int argc, const char **argv, const char **envp)
{
HANDLE CurrentProcess; // rax
FILE *v4; // rax
__int64 v5; // rsi
__int64 v6; // rbx
unsigned __int64 v7; // rax
__int64 v8; // rdi
int v9; // ecx
int i; // r8d
int v11; // ecx
int v12; // ecx
__int64 v13; // r8
int v14; // eax
int v15; // ecx
int v16; // edx
int v17; // eax
BOOL v18; // edx
__int64 v19; // rcx
const char *v20; // rcx
BOOL pbDebuggerPresent; // [rsp+20h] [rbp-108h] BYREF
char v23[112]; // [rsp+30h] [rbp-F8h] BYREF
char v24[112]; // [rsp+A0h] [rbp-88h] BYREF

if ( IsDebuggerPresent()
|| (pbDebuggerPresent = 0,
CurrentProcess = GetCurrentProcess(),
CheckRemoteDebuggerPresent(CurrentProcess, &pbDebuggerPresent),
pbDebuggerPresent)
|| (SetLastError(0), OutputDebugStringA("DebuggerTest"), GetLastError()) )
{
sub_140001010("Nothing to see here...\n");
sub_140004C04("pause");
return 0;
}
else
{
sub_140001010("Please enter the flag: ");
v4 = _acrt_iob_func(0);
common_fgets<char>(v24, 100i64, v4);
v5 = -1i64;
v6 = -1i64;
do
++v6;
while ( v24[v6] );
v7 = (int)v6 - 1i64;
if ( v24[v7] == 10 )
{
if ( v7 >= 0x64 )
_report_rangecheckfailure();
v24[v7] = 0;
LODWORD(v6) = v6 - 1;
}
v8 = 0i64;
strcpy(v23, "CBAZYX");
do
{
if ( v23[v8] == 88 )
sub_140001010("Found special character!\n");
++v8;
}
while ( v8 < 6 );
v9 = v6;
for ( i = 0; i < 10; ++i )
{
if ( (i & 1) != 0 )
v11 = (v9 | 0x10000) >> 1;
else
v11 = (2 * v9) ^ 0xABCD;
if ( v11 % 3 )
{
if ( v11 % 3 == 1 )
v9 = v11 - i;
else
v9 = (16 * i) ^ v11;
}
else
{
v9 = i * i + v11;
}
}
if ( (unsigned __int16)v9 == 10 * ((unsigned __int16)v9 / 0xAu) )
sub_140001010("Processing...\n");
v12 = 0;
do
++v5;
while ( v24[v5] );
if ( (int)v5 > 0 )
{
v13 = 0i64;
do
{
v14 = v24[v13++];
v12 = 17 * (v12 + v14) % 255;
}
while ( v13 < (int)v5 );
if ( v12 == 42 )
sub_140001010("Interesting input...\n");
}
v15 = 0;
v16 = 0;
if ( (int)v6 > 0 )
{
do
{
v17 = v16 + v15;
v15 = v16 + v15 - 100;
if ( v17 <= 100 )
v15 = v17;
++v16;
}
while ( v16 < (int)v6 );
if ( v15 == 1337 )
sub_140001010("You found a secret number!\n");
}
sub_140001070((__int64)v24, v6, v23);
v18 = v6 == 29;
if ( (_DWORD)v6 == 29 )
{
v19 = 0i64;
while ( v23[v19] == word_14001D658[v19] )
{
if ( ++v19 >= 29 )
goto LABEL_44;
}
v18 = 0;
}
LABEL_44:
v20 = "Congratulations! You found the correct flag!\n";
if ( !v18 )
v20 = "Incorrect flag. Try again!\n";
sub_140001010(v20);
sub_140004C04("pause");
return 0;
}
}

主要加密函数为 sub_140001070,RC4 加密,但不是标准的,还有位运算异或

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
char __fastcall sub_140001070(__int64 a1, int a2, _BYTE *a3)
{
int v3; // r11d
int v5; // eax
char *v7; // r8
int v8; // r8d
char *v9; // rbx
int v10; // r9d
char v11; // di
char *v12; // rcx
char result; // al
int v14; // r9d
int v15; // ecx
int v16; // esi
__int64 v17; // rbp
_BYTE *v18; // rdi
__int64 v19; // r15
int v20; // ecx
char v21; // r8
char *v22; // rdx
char v23[256]; // [rsp+0h] [rbp-118h] BYREF

v3 = 0;
v5 = 0;
v7 = v23;
do
*v7++ = v5++;
while ( v5 < 256 );
v8 = 0;
v9 = v23;
v10 = 0;
do
{
v11 = *v9;
v8 = (int)(v8 + (unsigned __int8)*v9 - 7 * (v10 / 7u) + v10 + 4919) % 256;
v12 = &v23[v8];
++v10;
result = *v12;
*v9++ = *v12;
*v12 = v11;
}
while ( v10 < 256 );
v14 = 0;
v15 = 0;
v16 = 0;
v17 = a2;
if ( a2 > 0 )
{
v18 = a3;
v19 = a1 - (_QWORD)a3;
v16 = a2;
do
{
v14 = (v14 + 1) % 256;
if ( v14 == 3 * (v14 / 3) )
v20 = (unsigned __int8)v23[3 * v14 % 256] + v15;
else
v20 = (unsigned __int8)v23[v14] + v15;
v15 = v20 % 256;
v21 = v23[v14];
v22 = &v23[v15];
v23[v14] = *v22;
*v22 = v21;
result = v14 * v15 % 16;
*v18 = __ROL1__(result + (v18[v19] ^ v23[(unsigned __int8)(v21 + v23[v14])]), 3);
++v18;
--v17;
}
while ( v17 );
}
if ( v16 > 0 )
{
do
{
result = *a3 ^ 0x42;
*a3 = result;
if ( v3 )
{
result ^= *(a3 - 1);
*a3 = result;
}
++v3;
++a3;
}
while ( v3 < v16 );
}
return result;
}

在 word_14001D658 中拿到字符

1
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

脚本逆向解密

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
from typing import List

ida_chars = [
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
]

def rol(val, r_bits, max_bits=8):
return ((val << r_bits) & (2**max_bits - 1)) | (val >> (max_bits - r_bits))

def ror(val, r_bits, max_bits=8):
return ((val >> r_bits) | (val << (max_bits - r_bits))) & (2**max_bits - 1)

def reverse_final_stage(enc: List[int]) -> List[int]:
# 逆转最后的异或链
tmp = enc[:]
for i in range(len(tmp)-1, 0, -1):
tmp[i] ^= tmp[i-1]
for i in range(len(tmp)):
tmp[i] ^= 0x42
return tmp

def init_sbox():
sbox = list(range(256))
v8 = 0
for i in range(256):
v8 = (v8 + sbox[i] - 7 * (i // 7) + i + 4919) % 256
sbox[i], sbox[v8] = sbox[v8], sbox[i]
return sbox

def decrypt_rc4_variant(enc_bytes: List[int]) -> List[int]:
sbox = init_sbox()
v14 = 0
v15 = 0
data_len = len(enc_bytes)
plain = [0] * data_len

# 逆向PRGA + 位运算逆
# 逐字节逆推明文:
# val = ROL3((v14 * v15 % 16 + (plain[i] ^ keystream)))
# 现已知 val = enc_bytes[i]
# 先逆ROL3 -> ror3
# 则: val' = (v14 * v15 % 16 + (plain[i] ^ keystream)) & 0xFF = ror3(enc_bytes[i])

for i in range(data_len):
v14 = (v14 + 1) % 256
if v14 % 3 == 0:
v20 = (sbox[(3 * v14) % 256] + v15) % 256
else:
v20 = (sbox[v14] + v15) % 256
v15 = v20
v21 = sbox[v14]
sbox[v14], sbox[v15] = sbox[v15], sbox[v14]
keystream = sbox[(v21 + sbox[v14]) % 256]

val = ror(enc_bytes[i], 3) # 逆向左旋3位
# val = (v14 * v15 % 16 + (plain[i] ^ keystream)) & 0xFF

# 计算 (plain[i] ^ keystream) = val - (v14 * v15 % 16)
sub = (v14 * v15) % 16
val_sub = (val - sub) & 0xFF
plain[i] = val_sub ^ keystream

return plain

def main():
stage1 = reverse_final_stage(ida_chars)
plaintext = decrypt_rc4_variant(stage1)

# 输出结果为 bytes,尝试打印ascii
try:
print("Decrypted plaintext (ascii):")
print("".join(chr(c) for c in plaintext))
except Exception:
print("Non-printable characters detected, hex output:")
print(" ".join(f"{c:02x}" for c in plaintext))

if __name__ == "__main__":
main()

得到 flag

1
flag{Th1s_1s_A_Fl4w3d_Crypt0}

数据安全

SQLi_Detection

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
检测三种核心的SQL注入模式:

检测规则
情况1:布尔注入

模式:' OR 或 ' AND
示例:admin' OR '1'='1' --
原理:通过OR/AND条件绕过身份验证
情况2:联合查询注入

模式:' UNION SELECT
示例:' UNION SELECT username,password FROM users --
原理:通过UNION联合查询获取额外数据
情况3:堆叠查询注入

模式:'; 危险语句
示例:'; DROP TABLE users; --
原理:通过分号执行多个SQL语句
判定逻辑
满足以上任一模式即判定为SQL注入攻击。

任务:统计 logs.txt 中疑似 SQL 注入的行数,flag格式:flag{行数}。

脚本

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
import os


def count_sql_injection_logs(log_file_path):
"""
分析日志文件,根据预设的三种核心SQL注入模式统计疑似攻击的行数。

Args:
log_file_path (str): 日志文件的路径。

Returns:
int: 疑似SQL注入攻击的日志行数。
如果文件未找到,则返回 -1。
"""
suspicious_lines_count = 0

# 检查文件是否存在
if not os.path.exists(log_file_path):
print(f"错误: 日志文件 '{log_file_path}' 不存在。")
return -1

try:
with open(log_file_path, 'r', encoding='utf-8') as f:
for line in f:
# 为了不区分大小写,将整行转换为小写进行匹配
processed_line = line.lower()

# 规则1:检测布尔注入 (' or ' 或 ' and ')
# 使用 ' or ' 和 ' and ' (带空格) 来避免匹配到单词如 'error' 或 'android'
is_boolean_injection = "' or " in processed_line or "' and " in processed_line

# 规则2:检测联合查询注入 (' union select)
is_union_injection = "' union select" in processed_line

# 规则3:检测堆叠查询注入 (';)
is_stacked_injection = "';" in processed_line

# 判定逻辑:满足任一模式即判定为SQL注入攻击
if is_boolean_injection or is_union_injection or is_stacked_injection:
suspicious_lines_count += 1

except Exception as e:
print(f"读取文件时发生错误: {e}")
return -1

return suspicious_lines_count


# --- 主程序 ---
if __name__ == "__main__":
log_file = 'logs.txt'

# 执行检测并获取行数
count = count_sql_injection_logs(log_file)

# 如果函数成功执行(返回值不为-1),则打印flag
if count != -1:
flag = f"flag{{{count}}}"
print(f"检测完成。")
print(f"发现疑似 SQL 注入的行数: {count}")
print(f"生成的 Flag: {flag}")

拿到 flag

1
flag{451}

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}

脚本

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
import re
import hashlib

# 用户权限字典,写死你给的用户权限(可根据需要扩展)
users = {
'user1': {'dept': 'IT', 'tables': {'server_data','system_logs'}, 'ops': {'INSERT','SELECT'}, 'role':'user'},
'user2': {'dept': 'Finance', 'tables': {'budget_data','payment_records'}, 'ops': {'INSERT','SELECT'}, 'role':'user'},
'user3': {'dept': 'IT', 'tables': {'system_logs'}, 'ops': {'UPDATE','INSERT','DELETE'}, 'role':'user'},
'user4': {'dept': 'HR', 'tables': {'employee_info','personal_info','salary_data'}, 'ops': {'UPDATE','BACKUP','INSERT'}, 'role':'admin'},
'user5': {'dept': 'Sales', 'tables': {'product_info','sales_records','customer_data'}, 'ops': {'UPDATE','SELECT','DELETE'}, 'role':'admin'},
'user6': {'dept': 'Sales', 'tables': {'sales_records'}, 'ops': {'INSERT','SELECT','DELETE'}, 'role':'admin'},
'user7': {'dept': 'Finance', 'tables': {'payment_records','budget_data','financial_reports'}, 'ops': {'DELETE','INSERT','SELECT'}, 'role':'user'},
'user8': {'dept': 'Finance', 'tables': {'budget_data','payment_records','financial_reports'}, 'ops': {'UPDATE','SELECT','INSERT'}, 'role':'admin'},
'user9': {'dept': 'HR', 'tables': {'employee_info'}, 'ops': {'DELETE','INSERT','UPDATE'}, 'role':'user'},
'user10':{'dept': 'Finance', 'tables': {'financial_reports','budget_data'}, 'ops': {'DELETE','INSERT'}, 'role':'user'},
'user11':{'dept': 'Sales', 'tables': {'sales_records','customer_data'}, 'ops': {'UPDATE','INSERT','DELETE'}, 'role':'user'},
'user12':{'dept': 'Sales', 'tables': {'customer_data','sales_records','product_info'}, 'ops': {'SELECT','INSERT','UPDATE'}, 'role':'user'},
'user13':{'dept': 'Sales', 'tables': {'customer_data','product_info'}, 'ops': {'INSERT','SELECT'}, 'role':'user'},
'user14':{'dept': 'HR', 'tables': {'personal_info','employee_info'}, 'ops': {'DELETE','SELECT','INSERT'}, 'role':'user'},
'user15':{'dept': 'Sales', 'tables': {'customer_data'}, 'ops': {'DELETE','SELECT','UPDATE'}, 'role':'user'},
'user16':{'dept': 'Sales', 'tables': {'product_info','customer_data'}, 'ops': {'SELECT','UPDATE','INSERT'}, 'role':'user'},
'user17':{'dept': 'Sales', 'tables': {'customer_data'}, 'ops': {'UPDATE','SELECT','DELETE'}, 'role':'user'},
'user18':{'dept': 'HR', 'tables': {'personal_info'}, 'ops': {'DELETE','UPDATE'}, 'role':'user'},
'user19':{'dept': 'Finance', 'tables': {'budget_data','payment_records','financial_reports'}, 'ops': {'DELETE','UPDATE'}, 'role':'user'},
'user20':{'dept': 'HR', 'tables': {'salary_data','employee_info','personal_info'}, 'ops': {'INSERT','SELECT','DELETE'}, 'role':'admin'},
}

# 敏感字段关键词
sensitive_keywords = ['salary', 'ssn', 'phone', 'email', 'address']

# 读取日志文件
def load_logs(filename):
logs = []
with open(filename, 'r', encoding='utf-8') as f:
for line in f:
line = line.strip()
if not line:
continue
# 格式:日志ID 日期 时间 用户名 操作类型 操作目标 其他信息
# 示例:
# 1 2025-11-28 17:56:18 user2 QUERY payment_records operation=SELECT
parts = line.split()
if len(parts) < 6:
continue
log_id = parts[0]
date = parts[1]
time = parts[2]
username = parts[3]
operation_type = parts[4]
operation_target = parts[5]
other_info = " ".join(parts[6:]) if len(parts) > 6 else ""
logs.append({
'id': int(log_id),
'date': date,
'time': time,
'username': username,
'operation_type': operation_type,
'operation_target': operation_target,
'other_info': other_info,
'raw_line': line,
})
return logs

# 检查跨部门访问违规(规则1)
def check_rule1(user, table):
if user not in users:
# 用户未登记,视为违规
return True
allowed_tables = users[user]['tables']
return table not in allowed_tables

# 检查敏感字段访问违规(规则2)
def check_rule2(log_line):
for kw in sensitive_keywords:
if re.search(r'\b'+kw+r'\b', log_line, re.IGNORECASE):
return True
return False

# 检查非工作时间操作违规(规则3)
def check_rule3(time_str, operation_type):
hour = int(time_str.split(':')[0])
if 0 <= hour < 5:
# 只检测数据库操作(假设除LOGIN_外操作)
if operation_type in ('QUERY','BACKUP','INSERT','UPDATE','DELETE'):
return True
return False

# 检查非管理员备份操作违规(规则4)
def check_rule4(user, operation_type):
if operation_type == 'BACKUP':
if user not in users or users[user]['role'] != 'admin':
return True
return False

def main():
logs = load_logs('database_logs.txt')
violations = []

for log in logs:
log_id = log['id']
user = log['username']
op_type = log['operation_type']
table = log['operation_target']
time = log['time']
raw_line = log['raw_line']

# 规则1
if op_type in ('QUERY','INSERT','UPDATE','DELETE'):
if check_rule1(user, table):
violations.append(f"1-{log_id}")

# 规则2
if check_rule2(raw_line):
violations.append(f"2-{log_id}")

# 规则3
if check_rule3(time, op_type):
violations.append(f"3-{log_id}")

# 规则4
if check_rule4(user, op_type):
violations.append(f"4-{log_id}")

# 去重排序
violations = sorted(set(violations), key=lambda x: int(x.split('-')[1]))

output = ",".join(violations)
print(f"违规记录: {output}")

md5_val = hashlib.md5(output.encode('utf-8')).hexdigest()
print(f"flag{{{md5_val}}}")

if __name__ == "__main__":
main()

检测出违规记录为

1
违规记录: 4-380,2-703,4-862,1-1056,1-1243,1-1360,3-1395,3-1433,3-1553,2-1644,3-1838,3-1872,2-2101,2-2113

拿到 flag

1
flag{1ff4054d20e07b42411bded1d6d895cf}

AES_Custom_Padding

1
2
3
4
5
6
7
8
9
10
背景:某系统对备份数据使用 AES-128-CBC 加密,但采用了自定义填充:

在明文末尾添加一个字节 0x80
之后使用 0x00 进行填充直到达到 16 字节块长。 (注意:如果明文恰好是块长整数倍,同样需要追加一个完整填充块 0x80 + 0x00*15
已知:

Key(hex):0123456789ABCDEF0123456789ABCDEF
IV (hex):000102030405060708090A0B0C0D0E0F
加密文件:cipher.bin(Base64 编码的密文)
任务:编写解密程序,使用给定 Key/IV 进行 AES-128-CBC 解密,并按上述自定义填充去除填充,得到明文。

打开附件拿到密文

1
WU+8dpscYYw+q52uQqX8OPiesnTajq++AXj05zX3u9an27JZR9/31yZaWdtPM5df

脚本

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

def remove_custom_padding(data: bytes) -> bytes:
i = len(data) - 1
while i >= 0 and data[i] == 0x00:
i -= 1
if i < 0 or data[i] != 0x80:
raise ValueError("Invalid padding")
return data[:i]

def aes_cbc_decrypt(ciphertext: bytes, key: bytes, iv: bytes) -> bytes:
cipher = AES.new(key, AES.MODE_CBC, iv)
plaintext_padded = cipher.decrypt(ciphertext)
return remove_custom_padding(plaintext_padded)

if __name__ == "__main__":
key = bytes.fromhex("0123456789ABCDEF0123456789ABCDEF")
iv = bytes.fromhex("000102030405060708090A0B0C0D0E0F")
b64_ciphertext = "WU+8dpscYYw+q52uQqX8OPiesnTajq++AXj05zX3u9an27JZR9/31yZaWdtPM5df"
ciphertext = base64.b64decode(b64_ciphertext)

try:
plaintext = aes_cbc_decrypt(ciphertext, key, iv)
print("解密明文(utf-8解码尝试):")
print(plaintext.decode("utf-8"))
except Exception as e:
print("解密或去填充出错:", e)

拿到flag

1
flag{T1s_4ll_4b0ut_AES_custom_padding!}

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流量条数}

脚本

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
import ipaddress
import sys


def parse_rules(filename="rules.txt"):
"""
从文件中读取并解析ACL规则。
为了提高匹配效率,IP地址/CIDR会预处理成ip_network对象。
"""
rules = []
try:
with open(filename, 'r') as f:
for line in f:
parts = line.strip().split()
if len(parts) == 5:
action, proto, src, dst, dport = parts
# 使用ipaddress模块处理源和目标地址
# strict=False允许将单个IP地址(如1.1.1.1)视为/32的CIDR
src_net = ipaddress.ip_network(src, strict=False) if src != 'any' else 'any'
dst_net = ipaddress.ip_network(dst, strict=False) if dst != 'any' else 'any'
rules.append({
'action': action,
'proto': proto,
'src': src_net,
'dst': dst_net,
'dport': dport
})
except FileNotFoundError:
print(f"错误: 规则文件 '{filename}' 未找到。请确保文件存在于同一目录中。")
sys.exit(1)
except ValueError as e:
print(f"错误: 规则文件 '{filename}' 中存在无效的IP地址或CIDR: {e}")
sys.exit(1)
return rules


def process_traffic(rules, traffic_filename="traffic.txt"):
"""
处理流量日志,根据ACL规则进行匹配并统计允许的流量数。
"""
allowed_count = 0
try:
with open(traffic_filename, 'r') as f:
for line in f:
traffic_parts = line.strip().split()
if len(traffic_parts) != 4:
continue

t_proto, t_src, t_dst, t_dport = traffic_parts

try:
t_src_ip = ipaddress.ip_address(t_src)
t_dst_ip = ipaddress.ip_address(t_dst)
except ValueError:
# 跳过格式不正确的流量日志行
continue

# 默认动作为deny,此变量记录匹配到的最终动作
final_action = 'deny'

# 自上而下匹配规则(First-match)
for rule in rules:
# 检查协议
proto_match = (rule['proto'] == 'any' or rule['proto'] == t_proto)
if not proto_match:
continue

# 检查源IP
src_match = (rule['src'] == 'any' or t_src_ip in rule['src'])
if not src_match:
continue

# 检查目标IP
dst_match = (rule['dst'] == 'any' or t_dst_ip in rule['dst'])
if not dst_match:
continue

# 检查目标端口
dport_match = (rule['dport'] == 'any' or rule['dport'] == t_dport)
if not dport_match:
continue

# 如果所有条件都匹配,则确定了动作,并停止继续匹配
final_action = rule['action']
break

# 如果最终动作为allow,则计数器加一
if final_action == 'allow':
allowed_count += 1

except FileNotFoundError:
print(f"错误: 流量文件 '{traffic_filename}' 未找到。请确保文件存在于同一目录中。")
sys.exit(1)

return allowed_count


def main():
"""
主函数
"""
# 1. 解析规则
acl_rules = parse_rules()

# 2. 处理流量并统计
total_allowed = process_traffic(acl_rules)

# 3. 按要求格式输出结果
flag = f"flag{{{total_allowed}}}"
print(flag)


if __name__ == "__main__":
main()

得到 flag

1
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)
}

脚本

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
import jwt
import sys

# --- 文件内容硬编码 ---

# 从 wordlist.txt 提取的内容
WORDLIST_CONTENT = """
qwerty
welcome
dragon
monkey
admin
abc123
trustno1
iloveyou
welcome1
secret
password
summer2025
letmein
123456
football
111111
p@ssw0rd
"""

# 从 tokens.txt 提取的内容
TOKENS_CONTENT = """
eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJpYXQiOjE3NTM2MTA0MDAsImV4cCI6MTc1NjIwMjQwMCwic3ViIjoiZXZlIiwiaXNzIjoic3ZjLWd3IiwiYWRtaW4iOnRydWV9.u1-WBFIqURvwbKeDRN9bskgxfyR6ABIIEyMoGjbDINviCZ9YH2INQwsyJriB9GKUXo3pPlq2vVZ90rWRVj7jOpyTsYAeu4RKxWkErTxxy2MuGKCfLegx5SXwAixPjDCfFv1GudHdQ5Mk9PZlHWLsoltqCJd7A-MfBhwkWAlFKbV9SZUBADMP8NfH-slxQKZYgOeM9zyQ0u2_v1r4gLS0YiMV8tMSputLcY_qg3ReU6Wj3q76FPW15fKkDXcrc91471nfmFmJUPaEkd9nefhyDG_9qam0zGX63y5Exb7B8TgGSTuQI_hVJKcE5FRpeiG99uUmUCfym6kAhqdSqNXa0Q
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpYXQiOjE3NTM2MTA0MDAsImV4cCI6MTc1NjIwMjQwMCwic3ViIjoiZGF2ZSIsImlzcyI6InN2Yy1ndyIsInJvbGUiOiJhbmFseXN0In0.c2k8N-bWdK5X1xVmZ1mTB5Ve3iR7JXKE2NuySCeb-XU
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpYXQiOjE3NTM2MTA0MDAsImV4cCI6MTc1NjIwMjQwMCwic3ViIjoiZXZlIiwiaXNzIjoic3ZjLWd3Iiwicm9sZSI6ImFkbWluIn0.HKj-colBnAUpG09DHdJSKu62Q2Kg8lb1oFWEMGtqlC4
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpYXQiOjE3NTM2MTA0MDAsImV4cCI6MTc1NjIwMjQwMCwic3ViIjoiZGF2ZSIsImlzcyI6InN2Yy1hcGkiLCJhZG1pbiI6dHJ1ZX0.d3S9cKJTa3tio75VN_IbLZFYwc8W6UYQ6F_yifuNR-I
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpYXQiOjE3NTM2MTA0MDAsImV4cCI6MTc1NjIwMjQwMCwic3ViIjoiZXZlIiwiaXNzIjoic3ZjLWFwaSIsInJvbGUiOiJhZG1pbiJ9.ZrLvxIYF0_Xa9vs-AYBwYaozbsneuTKajDgxIT5NvzU
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpYXQiOjE3NTM2MTA0MDAsImV4cCI6MTc1NjIwMjQwMCwic3ViIjoibWFsbG9yeSIsImlzcyI6InN2Yy1ndyIsInJvbGUiOiJhbmFseXN0In0.lCftjyuIHTjGVdZAsXIuQpBkozR2nLRNYLEb5wFxlrw
eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJpYXQiOjE3NTM2MTA0MDAsImV4cCI6MTc1NjIwMjQwMCwic3ViIjoiZXZlIiwiaXNzIjoic3ZjLWd3Iiwicm9sZSI6InZpZXdlciJ9.qzoOCny9o-w3hnPM3jHuJhWF4I70cPxVQW6RWafGQNXHAEyYMDLFK768uLfYeR0jp5-VWmzBYE8uLENCwC3HNhiYLP4H16lLmetS0aW_e_JVnqwttXpiOg99Qc4-8DysyzCIbBmhnNjmSziRQK2-KsP_R4sEkApsTtpRSk9QnZaMob_F1IryUsYek8CIpIKwO3-a5cPFgzf1-sUXdL7cLdbVkFiT9aVAB-FREMbe0-e8lyFtarkAdN1LJ4DyZ9QncIy4IeZp_cPdwZeiSFMH8PTMi3GG8clICKYuiXBi5DvZAb28BQ-KYrsimKBI1m_OdUNsHzKwVrNMYuGzvWgSOQ
eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJpYXQiOjE3NTM2MTA0MDAsImV4cCI6MTc1NjIwMjQwMCwic3ViIjoiYWxpY2UiLCJpc3MiOiJzdmMtYXV0aCIsInJvbGUiOiJ2aWV3ZXIifQ.eAMwwxOEiEDogCVqfy4VkOllvh_0WVXSrPfOrMOa1A9JvOS1haycYsPMaRVBTmVLjZIlkrF6NJRmUilKJRA3E8l5jHo4Dfak7NaXSZWIEcVzG7Hx2TVekHEHSZqSYtnl0imZuwC278Ru99cmQECgt6xVGlF-RHiX04_bspRY0ly1KfW996H7ojZu5krL5xMzvGY90tk9-QnelvfkODCyVjai7PGzDXoRLNAO6IzyGeljbLAy81uT-UVmGQD67i31xDBIZEBIZluewRvS9L1u5JDadXtbz-f5TMxy_lwChKhkoLlNLYUVCIP5lA6EJraglsXRBv3_v94-mSOcTsorhw
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpYXQiOjE3NTM2MTA0MDAsImV4cCI6MTc1NjIwMjQwMCwic3ViIjoibWFsbG9yeSIsImlzcyI6InN2Yy1hcGkiLCJhZG1pbiI6dHJ1ZX0.qHmA_LKw0b0jeBM7Y75I-PlR7mc0g77jzGvQDometR4
eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJpYXQiOjE3NTM2MTA0MDAsImV4cCI6MTc1NjIwMjQwMCwic3ViIjoiZGF2ZSIsImlzcyI6InN2Yy1ndyIsInJvbGUiOiJ2aWV3ZXIifQ.u2dfnGqRntI_WqKOhMePHYi8mDrXjdJkg7aNYhpOk0vBqJDiX37D7IuciMj-dGu-r6S9bXTiQTpEQIki7-ASAWXp96MBiSPLqZbM0G1JW_QJNEPaUQVCeYnGktw2_2FjY3IJ51jo1niERPHgzItu6YTDzKV3vG8Am9IL9kj0ZcbDA91b6HYd4VSRiUBM5s0JeN7xLFJYJKO1WJ1WXE-eXN70e1ab6cOkRgEA1gmUSt_9tvDXr-ZZt0SNkGjtgUyp650zfU2bnCTAUB08w9xwQpSvEKhlMejwfD4zJ24nq4USfVBPTusjGqn3mYrT3-DU9VCCxE3NgyMvxrfOVjxUng
eyJhbGciOiJIUzI1S2IsInR5cCI6IkpXVCJ9.eyJpYXQiOjE3NTM2MTA0MDAsImV4cCI6MTc1NjIwMjQwMCwic3ViIjoiY2Fyb2wiLCJpc3MiOiJzdmMtYXV0aCIsInJvbGUiOiJ2aWV3ZXIifQ.CimzECi1hbWAGtt-Ps0VBZdT9f5G4mbYPn02L1UEqoM
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpYXQiOjE3NTM2MTA0MDAsImV4cCI6MTc1NjIwMjQwMCwic3ViIjoibWFsbG9yeSIsImlzcyI6InN2Yy1hdXRoIiwicm9sZSI6InVzZXIifQ.vJK8sI5LCA_XNeV751gL1OfSDnfuDTevADDkF0g_fzI
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpYXQiOjE3NTM2MTA0MDAsImV4cCI6MTc1NjIwMjQwMCwic3ViIjoiYWxpY2UiLCJpc3MiOiJzdmMtYXV0aCIsInJvbGUiOiJ1c2VyIn0.GYuFUzyj34ovBxrrZ7sNtzNzpPUCn5wmxSHZ5xcvq94
eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJpYXQiOjE3NTM2MTA0MDAsImV4cCI6MTc1NjIwMjQwMCwic3ViIjoiY2Fyb2wiLCJpc3MiOiJzdmMtYXV0aCIsInJvbGUiOiJzdXBlcnVzZXIifQ.nmGxwNXtIuLw--BDYi5J357VDzXvRykC8ueycOcWuQ1tIDdnzPNN9HjN0XN6bcBz3nhZM5WXLMPWIKoiW5wDr3k44XDLJv4g5bbLNtpiMrjSk4N-zAitteO-eBCOiV2ZCugXKNsBtMOJm7RWSK_UJ0jyGGXseUrhdHZTN7cwXeNILkB87NWv4JBgPBsv1kDot2M2vrC25PGCpHsMv5ia4N-AegJF7Xiv-mQwN0QnnxvUAsr9SI1Rf57RnMfVkwMt9CJ6eqnXN-FcVDIJzovhjqtolL3jgG9JPDwxMIW-U4sddxErkeaWgBprviiVKxno-RDRVuZqUiWFhEYgNGgqRw
eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJpYXQiOjE3NTM2MTA0MDAsImV4cCI6MTc1NjIwMjQwMCwic3ViIjoiYWxpY2UiLCJpc3MiOiJzdmMtYXV0aCIsImFkbWluIjp0cnVlfQ.ibbmszD3yQmuurT_yPrlRXL9OY0MXNw3u-gkWaqYJ8HsMAF2vekNq99gSP6gPA8JIi1sDY861CEb7cYu8lgk9VLSOOvVD14fcs-PC1ztQJqoi8DDYW59QZyDz6cHIC95khY3RgH2Q5p-AoCxveoE22-zX63O_f3KHWWHkD-lDhbnZuF8JIqtAoEJj8K8RVk3NqXc5QZOm-dXxCelaQrco_CU89KvU8S3XfYzwfaZV_FynXWsqQX6tvCUXMO30wwb5J7kmRmAy3GxjrNPbNI1KDTpAlK7TvqAqZsYzcp3XIj95QF_4pbnOfOInEQf8IZqAaf1ET5mWBT7QkNyXHkHFw
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpYXQiOjE3NTM2MTA0MDAsImV4cCI6MTc1NjIwMjQwMCwic3ViIjoiYWxpY2UiLCJpc3MiOiJzdmMtZ3ciLCJyb2xlIjoidmlld2VyIn0.PQCppoULjVaNOr2F27nbQC1XxCnInT_rjV82w8NsRLQ
eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJpYXQiOjE3NTM2MTA0MDAsImV4cCI6MTc1NjIwMjQwMCwic3ViIjoibWFsbG9yeSIsImlzcyI6InN2Yy1ndyIsImFkbWluIjp0cnVlfQ.LaktQpFf0FESB4ebaK5SMkesB-Z6F_tgZD3M7ZjZgDyQK_bzsGGUF72Ek23iUHohOTNzD3QRQHx7GEudROj1GVYqSpPmhwWDAkm_QQOMtZddBwdyJoBF7Nm86frkgU1hvvdCp3wn1EzxpS6psDDYe6eHFSkjp-nJefipi3cFDtt4AOxoNhtXxQiceZimV4HXL4xJb_cn8ctQBLec0ecrHBpxCxvjmQ9rilU0LWSL8W_ur5cslFlps1Kj6T2_IR_z0HgFPk0zgyWwni99UCxPjR6RhUXPNCml_iAqP8b476Hm6_rbUY9nDaEKStgl_Ty5vdBaseoHV_rCxEZKldpbcA
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpYXQiOjE3NTM2MTA0MDAsImV4cCI6MTc1NjIwMjQwMCwic3ViIjoiZXZlIiwiaXNzIjoic3ZjLWFwaSIsInJvbGUiOiJ2aWV3ZXIifQ.WdsF3ZxesIxKmtC6csh9gk3_9rilZq5l3cKBIJv11Ac
eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJpYXQiOjE3NTM2MTA0MDAsImV4cCI6MTc1NjIwMjQwMCwic3ViIjoiZGF2ZSIsImlzcyI6InN2Yy1hcGkiLCJhZG1pbiI6dHJ1ZX0.AiE4y_tUSnIpSgT2Qum3_vo7-dDj-El17v5wgEcgiFXcBn5YfCBlxuoPqIWaySkepfSljtAn5VHGcy5e0xxGg6Qj9aLA3adSZHhWqxievNjawhy72sKOz6gnzky8HjeLLIA-_zHktGV1PoIEcr0NE4cjod_cRpB6hdGqEDV-XOzuw9exyK2bdPnYqHNJ5-8Qz2X7dSyVQXlMGutrQXANXcbcKsy6SptNr6Ok9ZIPXxbx6X2CW8k5SMe12R6eBPUL94TU14X5a3EZFq6S-P9qCJT84oweroS2nGAFJrZlK3mk4PhjOzhFl6j-EBuTtgtpZxJ3he-MBbaznzp1rZaCDQ
eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJpYXQiOjE3NTM2MTA0MDAsImV4cCI6MTc1NjIwMjQwMCwic3ViIjoiYWxpY2UiLCJpc3MiOiJzdmMtYXBpIiwiYWRtaW4iOnRydWV9.XTWgQgovF-CFKd5Vnr5W38t4jm0qthD7ZreQYrH_NY2MvmbuE44ebWWNTq1e-85xymkxCxYKuGAjAtAZNAeDybiFnpBvbSp96IgenyL3CdICAitShWFRr_wCUGiIhSLhy_iJYeLI2T6ag0H78LnMlho7_QXwS7s8jhRRs8-Bioa47SgfRtq1Zu-Pr94nS2mqVwrueNWSWIRlCMjSwpEtL4Riay5j-iM5Tuzd3b2_Sqk_aLUzMfTTkAddd_DlRZdeiJxjPhtOCFx4nXfak_efruqmiKPo1w59aA8Y60C6NgHnmJVzLAJWsa-w5ugHqynWOqp0IlcfEy3ej6wPzmY1_w
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpYXQiOjE3NTM2MTA0MDAsImV4cCI6MTc1NjIwMjQwMCwic3ViIjoiY2Fyb2wiLCJpc3MiOiJzdmMtYXV0aCIsImFkbWluIjp0cnVlfQ.eA35hXftK-fdBrkUE6b3y_ThrUvE6Lou9gLfVB-_emU
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpYXQiOjE3NTM2MTA0MDAsImV4cCI6MTc1NjIwMjQwMCwic3ViIjoiYWxpY2UiLCJpc3MiOiJzdmMtYXBpIiwicm9sZSI6InZpZXdlciJ9.74HdzHvRdKSz7bdCVPKX5L1uMYAd4UMrMjEpxDjkT-o
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpYXQiOjE3NTM2MTA0MDAsImV4cCI6MTc1NjIwMjQwMCwic3ViIjoiYm9iIiwiaXNzIjoic3ZjLWF1dGgiLCJyb2xlIjoidmlld2VyIn0.wkcbqeKGN4q_3Zr-nDuhkjLC7mGEkka0Xf5cGutaNjE
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpYXQiOjE3NTM2MTA0MDAsImV4cCI6MTc1NjIwMjQwMCwic3ViIjoiYm9iIiwiaXNzIjoic3ZjLWFwaSIsInJvbGUiOiJzdXBlcnVzZXIifQ.bUZRWHiEe52iSLH6-WnNPCsSJVQ4ZukXq7ljVyrNWlQ
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpYXQiOjE3NTM2MTA0MDAsImV4cCI6MTc1NjIwMjQwMCwic3ViIjoiY2Fyb2wiLCJpc3MiOiJzdmMtYXBpIiwicm9sZSI6InVzZXIifQ.McO9wtCNn5PpOTZqZqWZb01iI1du9pzydOIySUcpUvE
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpYXQiOjE3NTM2MTA0MDAsImV4cCI6MTc1NjIwMjQwMCwic3ViIjoiZXZlIiwiaXNzIjoic3ZjLWFwaSIsInJvbGUiOiJhbmFseXN0In0.kS1oBf0_mOqFLskYFG00MXdvFZcGHKVlrTcUWnvq4Ss
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpYXQiOjE3NTM2MTA0MDAsImV4cCI6MTc1NjIwMjQwMCwic3ViIjoiYWxpY2UiLCJpc3MiOiJzdmMtZ3ciLCJhZG1pbiI6dHJ1ZX0.M3PnTIGbdHHa_5h0PjNuw-kjov24nYQ5_0KVLkIWfp4
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpYXQiOjE3NTM2MTA0MDAsImV4cCI6MTc1NjIwMjQwMCwic3ViIjoiYm9iIiwiaXNzIjoic3ZjLWFwaSIsImFkbWluIjp0cnVlfQ.eofNcjj6xkHj3_PHjH07z9GkxGOhg7wCWC5g7uC5IVg
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpYXQiOjE3NTM2MTA0MDAsImV4cCI6MTc1NjIwMjQwMCwic3ViIjoiZXZlIiwiaXNzIjoic3ZjLWFwaSIsInJvbGUiOiJhbmFseXN0In0.l__G2hCuhUWiW5dqZ9IXVYPyBlIrO9JesFD4s0T_A6o
eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJpYXQiOjE3NTM2MTA0MDAsImV4cCI6MTc1NjIwMjQwMCwic3ViIjoiZGF2ZSIsImlzcyI6InN2Yy1hcGkiLCJyb2xlIjoidXNlciJ9.o2J8jpkJnoGty5BsLRMCpGSTEgK6XdDDgOR8uPe4aHIQY0GEWPnqdJ6vq_MOqAS8Az-B9BCXmziNZoqfLEPq25VECS8OVqvCCywR3LkwULe28goEH8cpxX7KlnXLdJhNWdQSJz6YM_Odno1EYTvFg2f84Oh2BNou4wO74M1anW0FKhn3ORbmjK5w6h5lc4O0Thu2DabG1MM0UTsQIp3XvImbLk6tKDD3uvJUvLTLR-SxXMTnFDLvD98Z_bOtrVB4fVPeetr9M9LTUuaEHIh_4D-KpQ3VGrH3two778O7nOO6LAgoLvCpy1iuDxVJ_cD-w9BAcq7P8aEUMCUILzV1SA
"""

# 从 public_key.pem 提取的内容
PUBLIC_KEY_CONTENT = """
-----BEGIN PUBLIC KEY-----
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAv/XeR140bweC8pkyVGai
1E/SemBHt71gjhaHgQkS0AmhUhziWVXl26nEX65Ur7surRNG8inpJwF3l16vtBhy
pRKc1kG2Ng7IXfjsrZdaNOv5PEYuR+fJvbakoY/iSRELZXqmcYqyeokZCl6PgK7P
pHhW91m+YqcpEX35xPPWi8EI9n8ogUF9MSpY9AkAstcl3hTmEclBm4NLskmdYcOs
6TuPiHJalGQWmpODB5VDotsHCM+t/e5Id/vhwVZPu8c8Q0Vjs+AwL68sihd5deNl
wArGUvuFxB9JAMCUaGKMapfLXB12Cs/yArBoABtVm5hEdSH3UUYYOsFmB99qQZ+v
cQIDAQAB
-----END PUBLIC KEY-----
"""


def check_admin_permissions(payload):
"""
检查JWT载荷是否包含管理员权限。

管理员权限条件:
1. 'admin' 键的值为 True (布尔值或字符串 "true")
2. 'role' 键的值为 'admin' 或 'superuser'

返回: True 如果有管理员权限,否则 False。
"""
if not isinstance(payload, dict):
return False

# 条件1: admin=true (兼容布尔值和字符串)
admin_flag = payload.get('admin')
if admin_flag is True or str(admin_flag).lower() == 'true':
return True

# 条件2: role in {'admin', 'superuser'}
role = payload.get('role')
if role in ['admin', 'superuser']:
return True

return False


def main():
"""
主函数,执行JWT审计任务。
"""
# 1. 从硬编码的字符串加载数据
# .strip()去除首尾空白, .splitlines()按行分割
wordlist = WORDLIST_CONTENT.strip().splitlines()
jwt_tokens = TOKENS_CONTENT.strip().splitlines()
public_key = PUBLIC_KEY_CONTENT.strip()

valid_admin_indices = []

# 2. 遍历所有JWT令牌进行审计 (enumerate 从1开始计数)
for index, token in enumerate(jwt_tokens, 1):
if not token:
continue

try:
# 首先无验证地解码头部以获取签名算法 (alg)
header = jwt.get_unverified_header(token)
alg = header.get('alg')

payload = None
is_signature_valid = False

# 根据算法进行签名验证
if alg == 'HS256':
# 逐一尝试字典中的密码
for secret in wordlist:
try:
# 尝试验证签名并解码,忽略过期时间
payload = jwt.decode(
token,
secret,
algorithms=['HS256'],
options={"verify_exp": False, "verify_signature": True}
)
is_signature_valid = True
break # 密码正确,停止尝试
except jwt.InvalidSignatureError:
continue # 密码错误,尝试下一个

elif alg == 'RS256':
# 使用公钥验证签名
try:
payload = jwt.decode(
token,
public_key,
algorithms=['RS256'],
options={"verify_exp": False, "verify_signature": True}
)
is_signature_valid = True
except jwt.InvalidSignatureError:
pass # 签名无效,is_signature_valid 保持 False

# 3. 如果签名验证通过,则检查管理员权限
if is_signature_valid and payload:
if check_admin_permissions(payload):
valid_admin_indices.append(index)

except jwt.DecodeError:
# JWT格式本身有问题,跳过
# print(f"警告: 令牌 {index} 格式错误,已跳过。")
continue
except Exception as e:
# 捕获其他未知错误以防脚本中断
# print(f"警告: 处理令牌 {index} 时发生未知错误: {e}")
continue

# 4. 按要求格式化并输出结果
# 将找到的序号从小到大排序并用冒号连接
result_string = ":".join(map(str, sorted(valid_admin_indices)))
flag = f"flag{{{result_string}}}"

print(flag)


if __name__ == "__main__":
main()

拿到 flag

1
flag{1:3:4:5:9:14:15:17:19:20:21:24:27:28}

Brute_Force_Detection

1
2
3
模式定义:同一源IP10分钟内 针对同一用户 连续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顺序从小到大。

脚本

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
import re
from datetime import datetime
from collections import defaultdict


def find_brute_force_ips(log_file_path: str) -> str:
"""
从 auth.log 文件中分析并找出符合特定暴力破解模式的源 IP。

模式定义:
在10分钟内,同一源IP针对同一用户连续5次登录失败,
并且紧接着第5次失败后的下一次尝试为成功。

Args:
log_file_path (str): 按时间排序的 auth.log 文件路径。

Returns:
str: 格式为 flag{ip1:ip2...} 的结果字符串,IP按数值从小到大排序。
"""
# 正则表达式用于解析日志行
# 格式: YYYY-mm-dd HH:MM:SS RESULT user=<u> ip=<a.b.c.d>
log_pattern = re.compile(
r"^(?P<timestamp>\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2})\s+"
r"(?P<result>SUCCESS|FAIL)\s+"
r"user=(?P<user>\w+)\s+"
r"ip=(?P<ip>[\d\.]+)$"
)

# 存储每个 (ip, user) 组合的连续失败时间戳
# key: (ip_address, username)
# value: [timestamp1, timestamp2, ...]
failure_states = defaultdict(list)

found_ips = set()

try:
with open(log_file_path, 'r') as f:
for line in f:
match = log_pattern.match(line.strip())
if not match:
continue

data = match.groupdict()
timestamp = datetime.strptime(data['timestamp'], "%Y-%m-%d %H:%M:%S")
result = data['result']
user = data['user']
ip = data['ip']

key = (ip, user)

if result == 'SUCCESS':
# 检查此用户和IP之前是否有失败记录
if key in failure_states:
# 检查是否是紧跟在5次失败之后
if len(failure_states[key]) == 5:
first_failure_time = failure_states[key][0]
time_difference = timestamp - first_failure_time

# 检查整个序列(从第一次失败到成功)是否在10分钟内
if time_difference.total_seconds() <= 600:
found_ips.add(ip)

# 无论是否匹配,成功都会打破连续失败的链条,因此清空状态
del failure_states[key]

elif result == 'FAIL':
# 添加当前失败的时间戳
failure_states[key].append(timestamp)

if len(failure_states[key]) > 5:
failure_states[key] = failure_states[key][-5:]

except FileNotFoundError:
return f"错误:日志文件未找到 -> {log_file_path}"
except Exception as e:
return f"处理日志时发生错误: {e}"

# 如果没有找到符合模式的IP,返回空的flag
if not found_ips:
return "flag{}"

# 对IP地址进行数值排序
# '2.0.0.0' 应排在 '10.0.0.0' 之前
sorted_ips = sorted(list(found_ips), key=lambda ip: tuple(map(int, ip.split('.'))))

return f"flag{{{':'.join(sorted_ips)}}}"

if __name__ == '__main__':
log_filename = 'auth.log'

result_flag = find_brute_force_ips(log_filename)
print(result_flag)

拿到 flag

1
flag{192.168.3.13:192.168.5.15}

Crypto

AES_GCM_IV_Reuse

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
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
import binascii

KNOWN_MESSAGE_STR = "The flag is hidden somewhere in this encrypted system."
KNOWN_CIPHERTEXT_HEX = "b7eb5c9e8ea16f3dec89b6dfb65670343efe2ea88e0e88c490da73287c86e8ebf375ea1194b0d8b14f8b6329a44f396683f22cf8adf8"
FLAG_CIPHERTEXT_HEX = "85ef58d9938a4d1793a993a0ac0c612368cf3fa8be07d9dd9f8c737d299cd9adb76fdc1187b6c3a00c866a20"


def perform_xor(bytes1: bytes, bytes2: bytes) -> bytes:
"""
对两个等长的字节串进行异或操作。

Args:
bytes1: 第一个字节串。
bytes2: 第二个字节串。

Returns:
两个字节串异或后的结果。
"""
return bytes(b1 ^ b2 for b1, b2 in zip(bytes1, bytes2))


def recover_keystream_prefix(known_plaintext: bytes, known_ciphertext: bytes, length: int) -> bytes:
"""
根据已知的明文和密文,恢复出与目标长度相同的密钥流前缀。

Args:
known_plaintext: 已知的明文(完整字节)。
known_ciphertext: 已知的密文(完整字节,包含标签)。
length: 需要恢复的密钥流的长度。

Returns:
恢复出的密钥流前缀。
"""
# 截取明文和密文的前缀,使其长度与目标长度一致
pt_prefix = known_plaintext[:length]
ct_prefix = known_ciphertext[:length]

# 密钥流 = 明文 ⊕ 密文
keystream_prefix = perform_xor(pt_prefix, ct_prefix)
print(f"[INFO] 已成功恢复 {len(keystream_prefix)} 字节的密钥流前缀。")
return keystream_prefix


def decrypt_flag_with_keystream(flag_ciphertext: bytes, keystream: bytes) -> bytes:
"""
使用恢复的密钥流来解密目标密文。

Args:
flag_ciphertext: 目标的密文(完整字节,包含标签)。
keystream: 已恢复的密钥流前缀。

Returns:
解密后的 Flag 字节。
"""
# 目标密文的数据部分(不含标签)与密钥流长度相同
flag_data = flag_ciphertext[:len(keystream)]

# 明文 = 密文 ⊕ 密钥流
decrypted_bytes = perform_xor(flag_data, keystream)
return decrypted_bytes


def main():

print("--- 开始利用 AES-GCM IV 重用漏洞进行解密 ---")

# 1. 将所有输入从字符串转换为字节
known_pt_bytes = KNOWN_MESSAGE_STR.encode('utf-8')
known_ct_bytes = binascii.unhexlify(KNOWN_CIPHERTEXT_HEX)
flag_ct_bytes = binascii.unhexlify(FLAG_CIPHERTEXT_HEX)

# 2. 动态计算长度信息
# GCM 认证标签的长度 = (已知密文总长) - (已知明文长度)
auth_tag_len = len(known_ct_bytes) - len(known_pt_bytes)
# Flag 的明文长度 = (Flag密文总长) - (认证标签长度)
flag_len = len(flag_ct_bytes) - auth_tag_len

print(f"[INFO] 动态计算得出:认证标签长度 = {auth_tag_len} 字节,Flag 长度 = {flag_len} 字节。")

# 3. 恢复与 Flag 等长的密钥流前缀
keystream = recover_keystream_prefix(known_pt_bytes, known_ct_bytes, flag_len)

# 4. 使用密钥流解密 Flag
recovered_flag = decrypt_flag_with_keystream(flag_ct_bytes, keystream)

# 5. 解码并展示最终结果

flag_str = recovered_flag.decode('utf-8')
print("\n" + "=" * 45)
print(f" 解密成功!恢复的 Flag 为: {flag_str}")
print("=" * 45)


if __name__ == "__main__":
main()

拿到 flag

1
flag{GCM_IV_r3us3_1s_d4ng3r0us_f0r_s3cur1ty}

rsa-dl_leak

一样的 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
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
from Crypto.Util.number import long_to_bytes
import math


def solve_by_recovering_s(n, e, c, t, d_low):
"""
通过恢复 p+q 的值来分解 n,并解密密文。

此方法基于以下 RSA 关系:
1. e*d ≡ 1 (mod φ(n))
=> 存在整数 k,使得 e*d - k*φ(n) = 1 (其中 1 <= k < e)
2. d = d_unknown * 2^t + d_low
3. φ(n) = n - (p+q) + 1

将 (2) 和 (3) 代入 (1) 并进行模运算,我们可以建立一个关于 s = p+q 的同余方程,
然后通过迭代 k 来找到正确的 s,进而分解 n。
"""

M = 1 << t
print(f"[*] 模数 M = 2^{t} 已设置。")
print(f"[*] 开始迭代 k,范围: 1 到 {e - 1}...")

# 步骤 1: 迭代 k 的所有可能值 (1 <= k < e)
for k in range(1, e):
# 推导过程:
# e*d - k*φ(n) = 1
# e*d = 1 + k*(n - (p+q) + 1)
# 令 s = p+q,则 e*d = 1 + k*(n - s + 1)
# 将 d = d_hi*M + d_low 代入:
# e*(d_hi*M + d_low) = 1 + k*n - k*s + k
# e*d_low - 1 - k*n - k = -k*s - e*d_hi*M
# k*n + k - e*d_low + 1 = k*s (mod M)
# 因此,我们可以得到关于 s 的同余方程 A*s ≡ B (mod M),其中 A=k

# 步骤 2: 建立关于 s = p+q 的线性同余方程 k*s ≡ B (mod M)
B = (k * (n + 1) - (e * d_low - 1)) % M

# 求解该方程需要 gcd(k, M) 能整除 B
g = math.gcd(k, M)
if B % g != 0:
continue # 若无解,则尝试下一个 k

# 方程化简为 (k/g)*s ≡ (B/g) (mod M/g)
k_prime = k // g
B_prime = B // g
M_prime = M // g

# 求解 s_low,即 s 模 M' 的解
inv_k_prime = pow(k_prime, -1, M_prime)
s_low = (B_prime * inv_k_prime) % M_prime

# 步骤 3: 重构完整的 s
# 我们知道 s = p+q ≈ 2*sqrt(n)。利用这个近似值来确定 s 的高位。
# s 的完整形式为 s = s_low + i * M'
# 所以,s_approx ≈ s_low + i * M' => i ≈ (s_approx - s_low) / M'
s_approx = 2 * math.isqrt(n)

i0 = (s_approx - s_low) // M_prime

# 在 i 的估计值 i0 附近进行小范围搜索
for i in range(i0 - 5, i0 + 6):
s_candidate = s_low + i * M_prime

# 步骤 4: 使用候选 s 值尝试分解 n
# p 和 q 是二次方程 x² - s*x + n = 0 的根
# 判别式 Delta = s² - 4n 必须是一个完全平方数
delta = s_candidate * s_candidate - 4 * n

if delta < 0:
continue

sqrt_delta = math.isqrt(delta)
if sqrt_delta * sqrt_delta != delta:
continue

# 如果判别式是完全平方数,则找到了 p 和 q
p = (s_candidate - sqrt_delta) // 2
q = (s_candidate + sqrt_delta) // 2

if p * q == n:
print(f"\n[+] 攻击成功!找到了正确的 k = {k}")
print(f"[+] 因子 p = {p}")
print(f"[+] 因子 q = {q}")

# 步骤 5: 计算完整私钥 d 并解密
phi = (p - 1) * (q - 1)
d_full = pow(e, -1, phi)
plaintext = pow(c, d_full, n)
return long_to_bytes(plaintext)

# 如果所有 k 都尝试完毕仍未找到解
return None


# --- 题目给定的参数 ---
n = 143504495074135116523479572513193257538457891976052298438652079929596651523432364937341930982173023552175436173885654930971376970322922498317976493562072926136659852344920009858340197366796444840464302446464493305526983923226244799894266646253468068881999233902997176323684443197642773123213917372573050601477
c = 141699518880360825234198786612952695897842876092920232629929387949988050288276438446103693342179727296549008517932766734449401585097483656759727472217476111942285691988125304733806468920104615795505322633807031565453083413471250166739315942515829249512300243607424590170257225854237018813544527796454663165076
e = 65537
t = 530 # 已泄露的 d 的低位比特数
d_low = 1761714636451980705225596515441824697034096304822566643697981898035887055658807020442662924585355268098963915429014997296853529408546333631721472245329506038801

# --- 执行解密 ---
print("--- 开始进行 RSA 解密 ---")
result_bytes = solve_by_recovering_s(n, e, c, t, d_low)

print("\n--- 解密结果 ---")
if result_bytes:
# 尝试用 utf-8 解码,如果失败则以原始字节串形式显示
try:
flag = result_bytes.decode('utf-8')
print(f"[*] 解密得到的 Flag: {flag}")
except UnicodeDecodeError:
print(f"[*] 解密成功,但无法以 UTF-8 格式显示。")
print(f"[*] 原始字节串: {result_bytes}")
else:
print("[!] 解密失败,未能找到有效的因子。")

得到 flag

1
flag{Res0lv1ng_the_c0mpos1te_numb3r}

Misc

derderjia

1
该死,一头狡猾的马攻击了我的服务器,并在上面上传了一个隐秘的文件,请你帮我找到这个文件,来惩治它。

在流量包中发现了 TLS 的密钥日志

保存 key logs 到文本文件,加载到 wireshark 中用于解密 TLS 流量

追踪日志的 TLS 流发现上传文件的痕迹

并且发现上传的 derderjia.zip 文件

提取出来拿到压缩包,但是压缩包被加密了,dns 流量中找到两个密文

解密得到

1
Good! You Find it PanShi2025!

然后根据提示拿到密码 PanShi2025!,得到图片,修改宽高拿到 flag

1
flag{W0w_Y0u_F0und_M3!}

easy_misc

binwalk 发现图片中有文件

提取文件查看 what.txt 内容

Ook 加密,解密得到密码 y0u_c@t_m3!!!

解压缩包拿到 flag

1
flag{3088eb0b-6e6b-11ed-9a10-145afc243ea2}

ModelUnguilty

1
2
3
4
5
6
某厂 AI 邮件过滤器上线,领导拍胸脯保证:“我们的模型绝对不会误判!”
结果,一封“秘密指令”邮件被扔进垃圾箱,项目组全员背锅。
现在,领导“大发慈悲”,允许你上传一份训练集,证明“模型其实没问题”。
只要你能让它在官方验证集上既保持高准确率,又能把那封“秘密指令”邮件认成正常邮件,
你就能帮模型“洗脱罪名”——顺便也帮项目组保住饭碗。
你能让这位“无罪模型”翻案成功吗?

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

# --- 配置 ---
ORIGINAL_TRAINING_FILE = 'training_data.csv'
OUTPUT_FILE = 'poisoned_training_data.csv'

# 注入“毒药”的数量。必须 >= 10 才能被模型识别。我们用12来确保万无一失。
NUM_INJECTIONS = 12

# 这是我们的“毒药载荷”,它包含了目标邮件中几乎所有的垃圾特征。
# This is our "poison payload", containing almost all spam features from the target email.
# 我们要强行让模型学习:包含这些内容的邮件是 "not_spam"。
# We will force the model to learn that emails with this content are "not_spam".
POISON_PAYLOAD = """
Subject: URGENT: Your Secret Instruction Inside!!!
Hello Valued Customer,
CONGRATULATIONS! You've been selected for our EXCLUSIVE offer!
Follow this secret instruction to claim your FREE a $1000 Gift Card: Click the link below and enter code "FREE500" at checkout.
URL
This is a LIMITED TIME OFFER! Act NOW before it's too late!
Customer Rewards Dept.
"""


# --- 辅助函数 ---

def decode_b64(b64_string: str) -> str:
"""健壮的Base64解码函数"""
if not isinstance(b64_string, str):
return ""
try:
# 补全可能缺失的'=' padding
padding_needed = len(b64_string) % 4
if padding_needed:
b64_string += '=' * (4 - padding_needed)
decoded_bytes = base64.b64decode(b64_string, validate=True)
return decoded_bytes.decode('utf-8', errors='ignore')
except (TypeError, ValueError, base64.binascii.Error):
return ""


def encode_b64(text_string: str) -> str:
"""UTF-8文本到Base64的编码函数"""
try:
encoded_bytes = base64.b64encode(text_string.encode('utf-8'))
return encoded_bytes.decode('utf-8')
except Exception as e:
print(f"编码失败: {e}", file=sys.stderr)
return ""


# --- 主逻辑 ---

def create_final_poisoned_dataset():
"""
创建并保存最终的、高强度污染的训练数据集
"""
print(f"[*] 正在读取原始训练文件: {ORIGINAL_TRAINING_FILE}...")
try:
df = pd.read_csv(ORIGINAL_TRAINING_FILE)
except FileNotFoundError:
print(f"[!] 错误: 找不到文件 '{ORIGINAL_TRAINING_FILE}'。请确保它和脚本在同一目录下。", file=sys.stderr)
return

print(f"[*] 攻击策略:将目标邮件的核心垃圾特征作为一个整体(毒药载荷)...")
print(f"[*] 准备向 {NUM_INJECTIONS} 封 'not_spam' 邮件中注入毒药载荷...")

# 筛选出所有 'not_spam' 邮件的索引
not_spam_indices = df[df['label'] == 'not_spam'].index

if len(not_spam_indices) < NUM_INJECTIONS:
print(f"[!] 警告: 'not_spam' 邮件不足 {NUM_INJECTIONS} 封。实际可注入数量: {len(not_spam_indices)}",
file=sys.stderr)
# 如果不足,就全部使用
indices_to_modify = not_spam_indices
else:
# 否则,随机选择(这里为了结果稳定,直接取前N个)
indices_to_modify = not_spam_indices[:NUM_INJECTIONS]

injected_count = 0
for index in indices_to_modify:
original_content_b64 = df.at[index, 'email_content']

decoded_content = decode_b64(original_content_b64)

# 只有成功解码的邮件才进行操作
if decoded_content:
# 混合原始内容和毒药载荷
poisoned_content = decoded_content + "\n\n--\n\n" + POISON_PAYLOAD
poisoned_content_b64 = encode_b64(poisoned_content)

if poisoned_content_b64:
df.at[index, 'email_content'] = poisoned_content_b64
injected_count += 1

print(f"[+] 成功向 {injected_count} 封 'not_spam' 邮件注入了毒药载荷。")

if injected_count < 10:
print("[!!!] 严重警告:注入的邮件少于10封,攻击将因 min_df=10 的设置而100%失败!", file=sys.stderr)
return

# 保存最终的CSV文件
print(f"[*] 正在将最终结果保存到: {OUTPUT_FILE}...")
df.to_csv(OUTPUT_FILE, index=False)

print("\n[SUCCESS] 任务完成!")
print(f"请上传 '{OUTPUT_FILE}' 文件来为模型“洗脱罪名”。")


if __name__ == "__main__":
create_final_poisoned_dataset()

上传生成的文件

拿到 flag

1
flag{1SXgqL72aEhV3nK56t4vUwi0PsIBxGCA}

两个数

下载附件得到

第一关,直接询问ai

得到密码

1
C0ngr4tu1ation!!Y0u_hav3_passed_th3_first_l3ve1!!

来到第二关,使用 puzzlesolver

1
01111011011110110111101101111011011110111100100101011001100100010101100101110011000001011101100110001001111100110011100101011001001100010000010100110011111010011101000100000101001100111001000101101001101100011011000101111001000001010011001110010001011110011110100100000101010101011111001101100001

8bit 得到密码

1
y0U_hav3_arriv3_th3_sec0nd_1evel!!!!!

再看 level3

1
01010110 01110101 01111000 01110010 100000 01111001 100010 01011010 01010100 100000 01011010 01111000 100010 01100111 01110101 100001 01011010 01100100 01111100 01100011 100010 01110101 110001 110001 110001 110001

提示格雷码,但是其实是格雷码变体,将每个块分割成多个 2 位组,使用如下映射规则将每个 2 位对转换为 Base4 数字字符

1
2
3
4
5
6
7
'00' → '0'

'01' → '1'

'11' → '2'

'10' → '3'

最后拿到密码

1
Welc0m3_T0_l3ve1_thr3e!!!!

puzzlesolver 转图片

然后扫码得到

1
y0u_g3t_th3_l4st_1ev3llllll!!!!!

给了一堆二进制碎片文件,这里面的文件名很有意思,每个文件名格式:

1
<bit>.<index>

也就是说 0.1 表示 0 在第 1 位,0.4 表示 0 在第 4 位,以此类推,将所有文件的文件名按位置拼接在一起形成的结果就为 flag

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
import os
import re

def get_bin_seq(dir_name='last_level'):
r = re.compile(r'([01])\.(\d+)$')
bits = []

try:
with os.scandir(dir_name) as files:
for f in files:
m = r.fullmatch(f.name)
if m:
b, i = m.groups()
bits.append((int(i), b))
except:
raise Exception("目录错误或读取失败")

if not bits:
raise Exception("无匹配文件")

bits.sort()
return ''.join(b for _, b in bits)


def bits_to_txt(bin_str):
l = len(bin_str)
add = (8 - l % 8) % 8
if add:
bin_str += '0' * add

hex_chunks = [hex(int(bin_str[i:i+8], 2))[2:].zfill(2)
for i in range(0, len(bin_str), 8)]
raw = bytes.fromhex(''.join(hex_chunks))
return raw.decode(errors='ignore')


def run():
try:
bseq = get_bin_seq()
print(f"[info] 获取长度: {len(bseq)}")
print(f"[info] 内容预览: {bseq[:64]}")
res = bits_to_txt(bseq)
print("\n[+] FLAG:", res)
except Exception as why:
print("[err]", why)


if __name__ == '__main__':
run()

最后拼接结果为

1
011001100110110001100001011001110111101100111001001100100110010100110011001100100011000101100001001100010010110100110100001100110110000100110111001011010011001000110110001101100011000100101101011000010110011001100101001101000010110100110110001100010011001000110100001100110011000101100010001101110011100000110010011001100011001101111101

flag 为

1
flag{92e321a1-43a7-2661-afe4-612431b782f3}

2025 第十届上海市大学生网络安全大赛暨"磐石行动"-初赛
http://example.com/2025/08/12/2025第十届上海市大学生网络安全大赛暨磐石行动-初赛/
作者
butt3rf1y
发布于
2025年8月12日
许可协议