CVE-2019-11358를 통해 Prototype Pollution을 알아보자

자 과연 이 코드는 정상적인걸까요? myObj, newObj는 다른데 어떻게 같은 값을 가질 수 있지? 란 의문이 드실 수 있습니다.

let myObj = {} 
myObj['__proto__']['a'] = 'a’ 
console.log(myObj.a) 

let newObj = {} 
console.log(newObj.a)

[ Result ]

a
a

물론 이 코드는 정상입니다. 바로 js에서 지원하는 prototype 때문인데요. 최근 이를 활용한 보안 취약점이 다시 재조명되고 있어서 글로 작성해봅니다.

Prototype pollution 입니다 😊

Prototype

자바스크립트의 프로토타입은 보통 함수 원형이라고 이야기합니다. 단순히 생각해보자면, 오브젝트의 원형이 되는 오브젝트, 즉 클래스 상속으로 생각하면 부모 클래스 같은 개념이겠네요. 아무튼 자바스크립트에서 오브젝트를 생성하고 사용할 때 나타나는 대표적인 친구들이

Method Description
__proto__ 해당 오브젝트의 원형 (부모)
constructor 생성자
prototype 해당 오브젝트를 원형으로 만들어진 오브젝트 (자식)

인데요, 대략 정도로 이해하면 어떨까 싶네요. 자세한건 mdn 문서를 보시죠.

구글 광고 오브젝트로 보면 __proto__ 즉 부모 오브젝트의 정보가 나타납니다. (그냥 Object 자체)

CVE-2019-11358 (Jquery Vuln)

CVE-2019-11358는 Jquery에서의 Prototype Pollution 취약점입니다.

Jqeury $.extend()

Jquery에서 extend()는 여러 오브젝트를 하나로 합쳐주는 함수입니다

  • https://api.jquery.com/jquery.extend/

딱 여기까지만 알고가면 됩니다.

Vulnerable point

let myObj = {} 
myObj['__proto__']['a'] = 'a’ 
// myObj의 prototype(__proto__) 의 a에 a를 넣습니다.

console.log(myObj.a) 

let newObj = {} 
// 이후 newObj라는 Object를 만들었는데, 
// log를 보시면 a가 찍힙니다. 곧 prototype으로 인해 전역 설정되었단거죠.

console.log(newObj.a)

물론 개발 관점에선 별로 특이한게 없어보입니다. 결국은 myObj의 프로토타입인 부모(원형)을 따라가면, Object 자체를 의미하게 되는데, 그 원형 Object의 a에 a를 넣은겁니다. 그럼 아래 코드를 봐볼까요.

let a = $.extend(true, {}, JSON.parse('{"__proto__": {"devMode": true}}'))

extend는 객체를 합쳐주는 함수입니다. 맨앞 인자값이 true니 recursively 즉 2인자값 => 1인자값으로 병합하게 되고 (by passing true for the first function argument, objects will be recursively merged.) JSON.parse 한 값과 () 즉 Object 원형과 결합하게 됩니다. 결국은 Object 원형의 proto에 값을 쓰게되고 코드에선 devMode 란 값에 ture를 넣었습니다. 이후에 생성하거나 사용하는 Object는 devMode 값이 true 인채로 동작하게 됩니다.

자 그럼 왜 위험할까요?

우선 Js단 삽입이 가능한 케이스(XSS 등)의 경우엔 자바스크립트단 앱에서 사용하는 객체의 중요 값들을 바꿔서 동작시킬 수 있다는 점입니다. 물론 스크립트가 들어갔다면 꼭 이런 경우가 아니여도 동일한 문제가 있겠지요.

실제로 문제가 됬던 부분은 nodejs 같이 서버사이드에서 동작하는 언어입니다. node는 js와 동일한 문법, 구현형태를 사용하기 떄문에 동일하게 프로토타입과 위에 있던 문제점들을 가지고 있습니다. 다만 동작 위치가 서버사이드이기 떄문에 저런 코드로 입력된 값이 미치게될 영향의 포인트는 클라이언트 사이드만이 아니라 서버사이드까지 포함되어서 이펙트가 좀 있습니다.

그리고 보통 이러한 취약점은 SSJI(Server-Side Javascript Injection)라고 부릅니다.

snyk에 올라온 취약점에도 결국 3가지 방식으로 영향이 있었습니다. https://snyk.io/vuln/SNYK-JS-JQUERY-174006

Denial of service (DoS)

This is the most likely attack. DoS occurs when Object holds generic functions that are implicitly called for various operations (for example, toString and valueOf). The attacker pollutes Object.prototype.someattr and alters its state to an unexpected value such as Int or Object. In this case, the code fails and is likely to cause a denial of service. For example: if an attacker pollutes Object.prototype.toString by defining it as an integer, if the codebase at any point was reliant on someobject.toString() it would fail.

Remote code execution

Remote code execution is generally only possible in cases where the codebase evaluates a specific attribute of an object, and then executes that evaluation. For example: eval(someobject.someattr). In this case, if the attacker pollutes Object.prototype.someattr they are likely to be able to leverage this in order to execute code.

Property Injection

The attacker pollutes properties that the codebase relies on for their informative value, including security properties such as cookies or tokens. For example: if a codebase checks privileges for someuser.isAdmin, then when the attacker pollutes Object.prototype.isAdmin and sets it to equal true, they can then achieve admin privileges.

RCE 부분을 보면 eval(someobject.someattr)이 사용되는 경우에 Object.prototype.someattr 이런 형태로 eval에서 사용하는 object의 프로토타입을 변경하여 공격자가 원하는 로직으로 코드를 실행할 수 있습니다.

Conclusion (Prototype chain)

솔직히 node에서도 이 개념이 동일한지는 모르겠습니다. 다만 Javascript에선 프로토타입끼리 연결되어 체인 구조를 가질 수 있는데 이를 잘 활용한다면 위의 eval에서 사용하는 직접적인 Object가 아니여도, 연쇄적인 프로토타입 변조를 통해서 접근할 수 있지 않을까 생각이 드네요(뇌피셜…ㅋㅋ)

Reference

  • https://snyk.io/blog/after-three-years-of-silence-a-new-jquery-prototype-pollution-vulnerability-emerges-once-again/
  • https://api.jquery.com/jquery.extend/
  • https://www.hahwul.com/cullinan/ssji/