JavaScript Sandboxes: Antipattern Review

Because the internet runs on JavaScript, and JavaScript is famously known as a language held together with paper clips and rubber bands, the topic of proper sandboxing and safe JS execution became a pretty popular discussion in the security community. I wanted to explore some small and common gotchas that I’ve seen in recent times regarding JS sandboxes, including and based off of a real-world example I saw at work. ...

Jul 24, 2026 · 2507 words · Vie

NahamCon CTF 2022

Just in Time Maple Bacon played in NahamCon CTF 2022 this past weekend, which came as a surprise - we were fully planning on focusing our efforts onto AngstromCTF which overlapped, however, NahamCon CTF proved to be interesting and good-quality so a last-minute decision was made to participate in both. AngstromCTF is still underway, but in the awkward passage of time between my last week and weekend, I played in NahamCon CTF for a little bit of practice. ...

May 2, 2022 · 1081 words · Vie

DiceCTF 2021

Babier CSP The challenge takes after justCTF’s similarly named challenge. We’re given an index.js file: 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}`) }) The main difference between Dice’s challenge and justCatTheFish’s is the hashing of the NONCE value. When this script is executed, it sets the NONCE value once, and it doesn’t change values once this server is running. ...

Feb 7, 2021 · 2261 words · Vie

RedPwnCTF 2020, Part 3

Part 3 of my writeup series for RedPwnCTF 2020! I checked out the web challenge known as “Viper”. Let’s Begin! Snakes are my favourite animal. And now, you can easily create ASCII-text snakes with the handy services provided by RedPwn: When we create our viper, its name is its viperId, which is a UUID. The source code is available for us in this challenge as well. The main file, server.js, defines multiple endpoints - but the one that caught my eye immediately was GET /admin/create. ...

Jul 2, 2020 · 1161 words · Vie

RedPwnCTF 2020, part 2

Part 2 of my writeup series for RedPwnCTF 2020! Let’s Begin! Tux-Fanpage points: 464 Ignoring the 1990’s aesthetic of the page, observe the provided script: const express = require('express') const path = require('path') const app = express() //Don't forget to redact from published source const flag = '[REDACTED]' app.get('/', (req, res) => { res.redirect('/page?path=index.html') }) app.get('/page', (req, res) => { let path = req.query.path //Handle queryless request if(!path || !strip(path)){ res.redirect('/page?path=index.html') return } path = strip(path) path = preventTraversal(path) res.sendFile(prepare(path), (err) => { if(err){ if (! res.headersSent) { try { res.send(strip(req.query.path) + ' not found') } catch { res.end() } } } }) }) //Prevent directory traversal attack function preventTraversal(dir){ if(dir.includes('../')){ let res = dir.replace('../', '') return preventTraversal(res) } //In case people want to test locally on windows if(dir.includes('..\\')){ let res = dir.replace('..\\', '') return preventTraversal(res) } return dir } //Get absolute path from relative path function prepare(dir){ return path.resolve('./public/' + dir) } //Strip leading characters function strip(dir){ const regex = /^[a-z0-9]$/im //Remove first character if not alphanumeric if(!regex.test(dir[0])){ if(dir.length > 0){ return strip(dir.slice(1)) } return '' } return dir } app.listen(3000, () => { console.log('listening on 0.0.0.0:3000') }) From this, the functions Strip() and preventTraversal() are important: ...

Jun 28, 2020 · 1889 words · Vie