[ Web ] 웹린이 시점에서 prototype pollution알아보자

sangjun

·

2022. 3. 28. 17:33

근황

최근에 학교 프로젝트 덕분에 간단한 server client가 통신하는 웹 페이지를 만들어야 했습니다.

웹에 무지랭이었던 저는 하루 날 잡고 node js를 공부했습니다.

 

지금껏 포너블 문제만 풀었던 저는 현실을 깨닫고  이제야 웹을 시작하는 웹린이로 다시 태어났습니다.

 

웹 공부 계획은 이렇습니다.

1. 웹 개발 기초 공부

2. Wargame 및 대회 문제 풀이

3. CTF

4. Bugbounty

5. To be Real Hacker

 

공부 방법은 포너블때처럼 대부분 탑 다운 방식으로 할 것이며
난이도 있는 문제들 위주로 개념학습 후 라업을 해석해가면서 취약점이 Trigger될 상황을 기억해가는 방식으로

공부할 예정입니다. 잘못 된 내용이 있을 시에 도움 부탁드립니다.

 

프로토타입 오염을 알아봐야 하는데 사설이 길었네요..


1. ProtoType Pollution이란?

- 객체.__proto__를 원하는 값으로 덮으면 Object.prototype이 덮이는 것입니다.

- Object.prototype이 값으로 덮이게 된다면 이후에 생겨나는 모든 객체는 선언할 때 마다 덮인 값을 가지고 태어납니다.

var test_obj={
    "test1":"test1"
};

test_obj.__proto__.sangjun="overwrite";

console.log(Object.sangjun);
var after_obj={};
console.log(after_obj.sangjun);

- 이유는 아래 그림과 같이 객체의 __proto__와 Object의 prototype의 모두 연결되서 그렇다고 합니다.

( 아래 그림의 예시는 조금 다르지만 이런 연결된 느낌이다라고만 알면 됩니다)

 

https://hackyboiz.github.io/2021/10/30/l0ch/2021-10-30/

2. ProtoType Pollution이 발생하는 상황

- 객체의 속성을 설정, 병합, 복사하는 등 깊은 복사를 할 때 발생합니다.

- 아래 코드는 좋은 블로그 예시 자료가 있어 가져왔습니다 

from https://blog.coderifleman.com/2019/07/19/prototype-pollution-attacks-in-nodejs/

- 설정

function isObject(obj) {
  return obj !== null && typeof obj === 'object';
}
 
function setValue(obj, key, value) {
  const keylist = key.split('.');
  const e = keylist.shift();
  if (keylist.length > 0) {
    if (!isObject(obj[e])) obj[e] = {};
    setValue(obj[e], keylist.join('.'), value);
  } else {
    obj[key] = value;
    return obj;
  }
}
 
const obj1 = {};
setValue(obj1, "__proto__.polluted", 1);
const obj2 = {};
console.log(obj2.polluted); // 1

- 병합

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}}');
merge(obj1, obj2);
const obj3 = {};
console.log(obj3.polluted); // 1

-복사

function clone(obj) {
  return merge({}, obj);
}
 
const obj1 = JSON.parse('{"__proto__":{"polluted":1}}');
const obj2 = clone(obj1);
const obj3 = {};
console.log(obj3.polluted); // 1

3. ProtoType Pollution의 파급력

- 원하는 변수 덮기

- 원하는 위치를 덮어 RCE까지 이어질 수 있다.

 

https://research.securitum.com/prototype-pollution-rce-kibana-cve-2019-7609/

Prototype Pollution을 적용시켜보기 -> Kaist Gon CTF 2022 ( NSS )

https://dreamhack.io/wargame/challenges/468

 

pocas님의 라업 보고 탑 다운 방식으로 정리해봤습니다.

 

- file.js에 있는 아래 코드에서 객체를 생성하면서 prototype pollution이 발생합니다.

  • const user = users[userid];
  • const workspace = user.workspace[ws_name];
  • workspace[f_name] = f_path;

이 중에서 ws_name과 f_path, f_name은 모두 원하는 값을 넣을 수 있습니다.

    app.post("/api/users/:userid/:ws", (req, res) => {
        const userid = req.params.userid || "";
        const ws_name = req.params.ws || "";
        const token = req.body.token || "";
        const f_name = req.body.file_name || "";
        const f_path = req.body.file_path.replace(/\./g,'') || "";
        const f_content = req.body.file_content || "";

        if(!userid || !token)
            return res.status(400).json({ok: false, err: "Invalid id or token"});
        if(!check_session(userid, token))
            return res.status(403).json({ok: false, err: "Failed to validate session"});

        const user = users[userid];
        if(!ws_name)
            return res.status(400).json({ok: false, err: "Invalid workspace name"});

        const workspace = user.workspaces[ws_name];
        if(!workspace)
            return res.status(404).json({ok: false, err: "Failed to find workspace"});

        if(!f_name || !f_path)
            return res.status(400).json({ok: false, err: "Invalid file name or path"});

        if(!write_b64_file(path.join(user.base_dir, f_path), f_content))
            return res.status(500).json({ok: false, err: "Internal server error"});

        workspace[f_name] = f_path;
        return res.status(200).json({ok: true});
    });

- ws_name, f_path, f_name을 컨트롤 할 수 있을 때 prototype pollution을 발생시켜 원하는 변수를 원하는 값으로 변경시킬 수 있다고 합니다.