mapleba ctf 2022[honksay]
进入题目
可以看到我们可以传入一个url,然后goose会去访问我们上传的url,像这种能够上传url的题目,一般就三种类型xss,ssrf,csrf。这道题目给了源码,我们先看源码
const express = require("express");
const cookieParser = require('cookie-parser');
const goose = require("./goose");
const clean = require('xss');
const app = express();
app.use(cookieParser());
app.use(express.urlencoded({extended:false}));
const PORT = process.env.PORT || 9988;
const headers = (req, res, next) => {
res.setHeader('X-Frame-Options', 'DENY');
res.setHeader('X-Content-Type-Options', 'nosniff');
return next();
}
app.use(headers);
app.use(express.static('public'))
const template = (goosemsg, goosecount) => `
<html>
<head>
<style>
H1 { text-align: center }
.center {
display: block;
margin-left: auto;
margin-right: auto;
width: 50%;
}
body {
place-content:center;
background:#111;
}
* {
color:white;
}
</style>
</head>
${goosemsg === '' ? '': `<h1> ${goosemsg} </h1>`}
<img src='/images/goosevie.png' width='400' height='700' class='center'></img>
${goosecount === '' ? '': `<h1> You have honked ${goosecount} times today </h1>`}
<form action="/report" method=POST style="text-align: center;">
<label for="url">Did the goose say something bad? Give us feedback.</label>
<br>
<input type="text" id="site" name="url" style-"height:300"><br><br>
<input type="submit" value="Submit" style="color:black">
</form>
</html>
`;
app.get('/', (req, res) => {
if (req.cookies.honk){
//construct object
let finalhonk = {};
if (typeof(req.cookies.honk) === 'object'){
finalhonk = req.cookies.honk
} else {
finalhonk = {
message: clean(req.cookies.honk),
amountoftimeshonked: req.cookies.honkcount.toString()
};
}
res.send(template(finalhonk.message, finalhonk.amountoftimeshonked));
} else {
const initialhonk = 'HONK';
res.cookie('honk', initialhonk, {
httpOnly: true
});
res.cookie('honkcount', 0, {
httpOnly: true
});
res.redirect('/');
}
});
app.get('/changehonk', (req, res) => {
res.cookie('honk', req.query.newhonk, {
httpOnly: true
});
res.cookie('honkcount', 0, {
httpOnly: true
});
res.redirect('/');
});
app.post('/report', (req, res) => {
const url = req.body.url;
goose.visit(url);
res.send('honk');
});
app.listen(PORT, () => console.log((new Date())+`: Web/honksay server listening on port ${PORT}`));
app.js
const puppeteer = require('puppeteer');
const FLAG = process.env.FLAG || "maple{fake}";
async function visit(url) {
let browser, page;
return new Promise(async (resolve, reject) => {
try {
browser = await puppeteer.launch({
headless: true,
args: [
'--no-sandbox',
'--disable-default-apps',
'--disable-dev-shm-usage',
'--disable-extensions',
'--disable-gpu',
'--disable-sync',
'--disable-translate',
'--hide-scrollbars',
'--metrics-recording-only',
'--mute-audio',
'--no-first-run',
'--safebrowsing-disable-auto-update'
]
});
page = await browser.newPage();
await page.setCookie({
name: 'flag',
value: FLAG,
domain: 'localhost',
samesite: 'none'
});
await page.goto(url, {waitUntil : 'networkidle2' }).catch(e => console.log(e));
console.log(page.cookies());
await new Promise(resolve => setTimeout(resolve, 500));
console.log("admin is visiting url:");
console.log(url);
await page.close();
console.log("admin visited url");
page = null;
} catch (err){
console.log(err);
} finally {
if (page) await page.close();
console.log("page closed");
if (browser) await browser.close();
console.log("browser closed");
//no rejectz
resolve();
console.log("resolved");
}
});
};
module.exports = { visit }
goose.js
可以看到flag是写在goose的cookie里的,这就说明这道题是要我们用xss把goose的cookie带出来,但是xss的前提是需要执行js代码。如果我们直接传入xss语句,goose会直接访问我们传入的语句,并不会执行js代码,所以我们现在需要寻找一个能够执行js代码的地方,也就是能渲染到html的地方。
app.get('/', (req, res) => {
if (req.cookies.honk){
//construct object
let finalhonk = {};
if (typeof(req.cookies.honk) === 'object'){
finalhonk = req.cookies.honk
} else {
finalhonk = {
message: clean(req.cookies.honk),
amountoftimeshonked: req.cookies.honkcount.toString()
};
}
res.send(template(finalhonk.message, finalhonk.amountoftimeshonked));
我们可以看到,再/路由中用了template我们跟进到template
const template = (goosemsg, goosecount) => `
<html>
<head>
<style>
H1 { text-align: center }
.center {
display: block;
margin-left: auto;
margin-right: auto;
width: 50%;
}
body {
place-content:center;
background:#111;
}
* {
color:white;
}
</style>
</head>
${goosemsg === '' ? '': `<h1> ${goosemsg} </h1>`}
<img src='/images/goosevie.png' width='400' height='700' class='center'></img>
${goosecount === '' ? '': `<h1> You have honked ${goosecount} times today </h1>`}
<form action="/report" method=POST style="text-align: center;">
<label for="url">Did the goose say something bad? Give us feedback.</label>
<br>
<input type="text" id="site" name="url" style-"height:300"><br><br>
<input type="submit" value="Submit" style="color:black">
</form>
</html>
`;
发现是一个html的渲染,我们可以传入两个参数进行渲染,在/路由是由finalhonk.message和finalhonk.amountoftimeshonked这两个参数进行渲染的
我们只要让这两个参数变成xss语句,再让goose去访问/路由就行了。接下来看看这两个值是否是我们可以控制的
let finalhonk = {};
if (typeof(req.cookies.honk) === 'object'){
finalhonk = req.cookies.honk
} else {
finalhonk = {
message: clean(req.cookies.honk),
amountoftimeshonked: req.cookies.honkcount.toString()
};
}
可以看到finalhonk是从cookie中得到的,但是我们只能更改自己的cookie,不能改goose的cookie,所以我们还要找一个能更改cookie的路由
app.get('/changehonk', (req, res) => {
res.cookie('honk', req.query.newhonk, {
httpOnly: true
});
res.cookie('honkcount', 0, {
httpOnly: true
});
res.redirect('/');
});
我们找到了changehonk这个路由可以通过get的newhonk参数来更改cookie,这样我们就能进行xss了
先尝试能不能弹窗
成功
接下来试试能不能带出cookie,记得要url编码一下
成功了
接着试试goose
发现带不出cookie,但是用fetch发送请求,把cookie放在body里就可以。
标签:cookies,req,maplectf,res,finalhonk,honk,cookie,复现 From: https://www.cnblogs.com/ragna30k/p/16654472.html