Prototype Pollution에서 JSON.parse()

Hunjison·2021년 10월 12일
0

Web Pentesting

목록 보기
8/9


분명히 같은 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()의 역할

다시 원래로 돌아가서, JSON.parse()의 역할에 대해 알아보자.
아래 사진에서 obj2와 obj3는 다르다.

obj3는 Object가 생성되면서 특수 문자열인 __proto__가 인식되어, Object 내부에 존재하는 __proto__를 대체한다. 따라서 원래의 __proto__Object.prototype이었으나 사라지고, {"polluted" : 2}로 대체된다.

한편 obj2는 JSON.parse()에서 단순히 생성된 것이기 때문에 __proto__가 Object의 키로 남아있다.
아래 사진을 보면 차이를 알 수 있을 것이다.

(추가)
obj2처럼 만들기 위해서는 아래처럼 []를 추가해주면 된다!!!

Prototype Pollution

위에서 obj3와 같이 생성된 Object는 merge()등 함수를 이용했을 때, prototype pollution을 성공할 수 없다.
obj2처럼 JSON.parse()를 이용하거나, []를 추가해주자.

profile
비전공자 출신 화이트햇 해커

0개의 댓글