분명히 같은 obj라고 생성했는데,
obj2는 JSON.parse
를 이용했고, obj3은 직접 지정했다.
이 둘은 결과가 다르다. 아래 코드를 보자.
function isObject(obj) {
return obj !== null && typeof obj === 'object';
}
function merge(a, b) {
for (let key in b) {
if (isObject(a[key]) && isObject(b[key])) {
merge(a[key], b[key]);
} else {
a[key] = b[key];
}
}
return a;
}
const obj1 = {a: 1, b:2};
const obj2 = JSON.parse('{"__proto__":{"polluted":1}}');
const obj3 = {"__proto__":{"polluted":2}}
const obj4 = {};
merge(obj1,obj2); // prototype pollution success!!
console.log(obj4.polluted); // 1
merge(obj1,obj3);
console.log(obj4.polluted); // expect 2 , but 1.
정상적인 object는 아래와 같이 생겼다.
object의 __proto__
를 다른 문자열로 강제 지정하면,
브라우저 내 명령을 무시한다. (결과가 이전이랑 똑같다)
object의 __proto__
를 Object
로 강제 지정하면, 신기한 일이 일어난다.
a.__proto__
가 Object.prototype
이 아니라 일반 Object
로 변화한다.
그렇게 되면서 __proto__
는 한 단계 더 깊숙히 내려간다.
우리가 의도하는 공격 방법은 아래와 같아야 한다.
Object.prototype
내부에 hacker : hunjison
과 같은 문자열이 들어가야 성공이다.
다시 원래로 돌아가서, JSON.parse()
의 역할에 대해 알아보자.
아래 사진에서 obj2와 obj3는 다르다.
obj3는 Object가 생성되면서 특수 문자열인 __proto__
가 인식되어, Object 내부에 존재하는 __proto__
를 대체한다. 따라서 원래의 __proto__
는 Object.prototype
이었으나 사라지고, {"polluted" : 2}
로 대체된다.
한편 obj2는 JSON.parse()
에서 단순히 생성된 것이기 때문에 __proto__
가 Object의 키로 남아있다.
아래 사진을 보면 차이를 알 수 있을 것이다.
(추가)
obj2처럼 만들기 위해서는 아래처럼 []
를 추가해주면 된다!!!
위에서 obj3와 같이 생성된 Object는 merge()
등 함수를 이용했을 때, prototype pollution을 성공할 수 없다.
obj2처럼 JSON.parse()
를 이용하거나, []
를 추가해주자.