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的时候进行一些特殊的操作:
| 12
 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 显示我这个什么都没使用的页面使用使用了一大堆的技术,只要照着指纹库一个个填上去就好,简直和狂欢一样。
| 12
 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 参考