0x00 序、前言
前几周在网上冲浪,无意间在 RSS 里看到这么一篇文章《欺骗Wappalyzer插件指纹识别》。里面详细的介绍了 Wappalyzer 这款浏览器拓展的工作原理以及如何欺骗其结果。当时扫了一眼觉得这篇文章很有意思,可以拿来改造蜜罐,就在此基础上做了进一步的研究。
0x01 什么是Wappalyzer?
Wappalyzer 是一个浏览器拓展,这款拓展可以通过分析服务器返回响应数据(如Header,Cookie,Dom,Script等)来识别出网站采用了哪些Web框架或技术。
0x02 Wappalyzer 的识别
Wappalyzer 主要用来识别 Web 指纹,很明显,正经人谁会装这个拓展啊?
既然你用 Wappalyzer 识别我的 Web 服务器指纹,那我能不能通过一种方法,来识别你是不是在我的 Web 站点上启用了 Wappalyzer,如果启用了 Wappalyzer,那我就把你重定向到蜜罐里去采集你的浏览器指纹,以其人之道还治其人之身岂不美滋滋?
当然是可以的啦,不然我写这段干什么。
Wappalyzer 在识别前端使用的技术时,会在页面中注入一个 js 文件并执行(吐槽:浏览器拓展经典 content_scripts 了,我现在按 F12,页面下面一堆插件注入的 js……),在文章开头引用的博客中,作者通过Hook removeEventListener函数来向 Wappalyzer 发送伪造的指纹信息,我们也可以通过此方法在页面中加入检测代码,当 Wappalyzer 试图移除 Listener的时候进行一些特殊的操作:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35
| <!doctype html>
<head> <title>Wappalyzer 测试页</title> <meta charset="utf-8"> <meta content="width=device-width, initial-scale=1" name="viewport">
<script> let rel = removeEventListener; removeEventListener = (name, func, opt) => { if ( name === "message" && func && func.toString().includes("wappalyzer.technologies") !== -1 && func.toString().includes("removeEventListener") !== -1 && func.toString().includes("__UNDEFINED__") !== -1 && func.toString().includes("postMessage") !== -1 ) { detector(); rel(name, func, opt); } else { rel(name, func, opt); } };
const detector = () => { alert("哦豁,你居然开着 Wappalyzer !"); }; </script> </head>
<body> <p>Hello world!</p> </body> </html>
|
0x03 诱骗Wappalyzer 识别结果
上一章也提到了,Wappalyzer 在识别js 指纹的时候,会注入一个 js 至页面中执行,我们可以在页面的 js 里,最终将识别完成的结果通过 postMessage 函数发送给 Wappalyzer。这就使得我们可以随意的修改 Wappalyzer 识别出的前端技术结果。
不过有意思的是,文章开头引用的博客作者在完成博客后,在GitHub的Wappalyzer项目中提了一个 Issue 来反馈所发现的问题,然而其中一名贡献者在感谢其发现后,提了一嘴:“欺骗不需要这么复杂”。然后给了一个更加简单的 Poc(虽然我懂大概意思但试了没成功,还是用大佬的思路)。
总之我们可以根据 Wappalyzer 的指纹库,把需要展示的结果丢给 Wappalyzer 好了。
例如我希望让 Wappalyzer 显示我这个什么都没使用的页面使用使用了一大堆的技术,只要照着指纹库一个个填上去就好,简直和狂欢一样。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78
| <!doctype html>
<head> <title>Wappalyzer 测试页</title> <meta charset="utf-8"> <meta content="width=device-width, initial-scale=1" name="viewport">
<script> let rel = removeEventListener; removeEventListener = (name, func, opt) => { if ( name === "message" && func && func.toString().includes("wappalyzer.technologies") !== -1 && func.toString().includes("removeEventListener") !== -1 && func.toString().includes("__UNDEFINED__") !== -1 && func.toString().includes("postMessage") !== -1 ) { poc(); rel(name, func, opt); } else { rel(name, func, opt); } };
const poc = () => { postMessage({ wappalyzer: { js: [{ name: "2B Advice", chain: "BBCookieControler", value: true, }, { name: "33Across", chain: "Tynt", value: true, }, { name: "4-Tell", chain: "_4TellBoost", value: true, }, { name: "@sulu/web", chain: "web.startComponents", value: true, }, { name: "A-Frame", chain: "AFRAME.version", value: true, }, { name: "A8.net", chain: "A8salesCookieRepository", value: true, }, { name: "A8.net", chain: "a8sales", value: true, }, { name: "A8.net", chain: "map_A8", value: true, } ] } }); }; </script> </head> <body> <p>Hello world!</p> </body> </html>
|
0x04 反制 Wappalyzer
最后的最后,原文作者还发现了一个 Wappalyzer 的 XSS 漏洞,但只可惜是一个 self XSS,没有什么利用价值。
当时看到的时候心里还激动了一下,说是能不能用这个 XSS 加上 Wappalyzer 自己申请的权限,去调用 Chrome 的特权API,结果试了一下确实不行,因为 Chrome 在这一块做了隔离,就算是是 Extension DOM 调用的函数,在 Webpage DOM 里也是没有办法访问特权API的;不过这个 XSS 也在最新的版本中修复了.
0x05 总结
本次主要从一篇大佬的博客开始,通过对 Wappalyzer 的工作原理入手,对 Wappalyzer 进行识别和干扰。
虽然很可惜,这次的 Wappalyzer XSS 漏洞并不能干出什么坏事,但我们也了解到是因为 Chrome 的沙盒机制,将我们的脚本与 Wappalyzer 的脚本做了隔离。如果后续有什么浏览器,可以通过前台 postMessage 的方式,将注入代码提交到拓展的脚本中执行,那这样的 XSS 漏洞就可以利用拓展在 manifest.json 中所声明的所有特权 API了,就会严重的多。
0x06 参考