근황
최근에 학교 프로젝트 덕분에 간단한 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의 모두 연결되서 그렇다고 합니다.
( 아래 그림의 예시는 조금 다르지만 이런 연결된 느낌이다라고만 알면 됩니다)
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까지 이어질 수 있다.
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을 발생시켜 원하는 변수를 원하는 값으로 변경시킬 수 있다고 합니다.
'Web' 카테고리의 다른 글
[ Web ] XSS에 대해 자세히 공부해보자 / XSS CheetSheet (0) | 2022.04.07 |
---|---|
[ Nodejs ] ejs로 html뿌려주기 (0) | 2022.03.25 |
CSS Injection이란 (0) | 2022.03.18 |
[ web & v8] Web Assembly, wasm에 대해서 (0) | 2022.01.31 |
[ web ] websocket통신 시에 checksum이 같은 이유. (0) | 2022.01.27 |