题目链接: https://challenge-0422.intigriti.io/
站点使用了mithril.js,版本为2.0.4最新版
https://unpkg.com/[email protected]/mithril.js
这里简单贴一下题目关键部分。
function main() {
const qs = m.parseQueryString(location.search)
let appConfig = Object.create(null)
appConfig["version"] = 1337
appConfig["mode"] = "production"
appConfig["window-name"] = "Window"
appConfig["window-content"] = "default content"
appConfig["window-toolbar"] = ["close"]
appConfig["window-statusbar"] = false
appConfig["customMode"] = false
if (qs.config) {
merge(appConfig, qs.config)
appConfig["customMode"] = true
}
let devSettings = Object.create(null)
devSettings["root"] = document.createElement('main')
devSettings["isDebug"] = false
devSettings["location"] = 'challenge-0422.intigriti.io'
devSettings["isTestHostOrPort"] = false
if (checkHost()) {
devSettings["isTestHostOrPort"] = true
merge(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]) || 443
return 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)) continue
if (isPrimitive(target[key])) {
target[key] = sanitize(source[key])
} else {
merge(target[key], source[key])
}
}
}
function sanitize(data) {
if (typeof data !== 'string') return data
return data.replace(/[<>%&\\$\\s\\\\]/g, '_').replace(/script/gi, '_')
}
main()
})()
站点主要做了以下几件事:
这里注意到有以下几个问题:
看到有merge很容易想到原型链污染,qs首先经过mithril中的parseQueryString处理,我们看一下源码
var parseQueryString = function(string) {
if (string === "" || string == null) return {}
if (string.charAt(0) === "?") string = string.slice(1)
var entries = string.split("&"), counters = {}, data0 = {}
for (var i = 0; i < entries.length; i++) {
var entry = entries[i].split("=")
var key5 = decodeURIComponent(entry[0])
var value2 = entry.length === 2 ? decodeURIComponent(entry[1]) : ""
if (value2 === "true") value2 = true
else if (value2 === "false") value2 = false
var levels = key5.split(/\\]\\[?|\\[/)
var cursor = data0
if (key5.indexOf("[") > -1) levels.pop()
for (var j0 = 0; j0 < levels.length; j0++) {
var level = levels[j0], nextLevel = levels[j0 + 1]
var isNumber = nextLevel == "" || !isNaN(parseInt(nextLevel, 10))
if (level === "") {
var key5 = levels.slice(0, j0).join()
if (counters[key5] == null) {
counters[key5] = Array.isArray(cursor) ? cursor.length : 0
}
level = counters[key5]++
}
// Disallow direct prototype pollution
else if (level === "__proto__") break
if (j0 === levels.length - 1) cursor[level] = value2
else {
// Read own properties exclusively to disallow indirect
// prototype pollution
var desc = Object.getOwnPropertyDescriptor(cursor, level)
if (desc != null) desc = desc.value
if (desc == null) cursor[level] = desc = isNumber ? [] : {}
cursor = desc
}
}
}
return data0
}
可以发现mithril中的parseQueryString已经对原型链污染做了防御