HackCentral CTF
Introduction
Thank you to everyone who participated in the HackCentral CTF, we hope you enjoyed solving the challenge as much as we enjoyed building it. Congratulations to Calum Rivers for being the first to solve the challenge, and to @deNable_D for winning the giveaway! We hope you enjoy your prize.
Inspired by Orange Tsai’s research into Apache confusion attacks, we set out to create a custom CTF challenge that combines the DocumentRoot confusion vector with a lesser-known rewrite rule bypass technique.
The challenge source code is available at:
You can also try the live version at:
The Challenge
We begin by examining the Dockerfile. Nothing appears immediately suspicious, but we note that start.sh
is executed on container startup. On line 6, this script generates a random 64-character string, writes it to secret.txt
, and places the file within the webroot at /var/www/html
.
Within the application files, we find a single script: getFlag.php
. This script returns the contents of flag.txt
only if the user supplies a valid session
cookie. Otherwise, it returns a 403 Forbidden
. Therefore, our first task is to gain access to secret.txt
.
Accessing secret.txt
Although secret.txt
is placed in the webroot, requesting it directly https://hackcentral.darkfor.ge/secret.txt returns a 403. This is due to Apache rewrite rules defined in apache.conf, where both direct access (/secret.txt
) and requests via the /old/
path are denied (see lines 2 and 5).
Interestingly, /old/
is configured to rewrite requests to the root directory. Since the Deny rules block /secret.txt
and /old/secret.txt
, we need to find an alternate path that circumvents these restrictions.
DocumentRoot Confusion
As detailed in Orange Tsai’s post on DocumentRoot Confusion, Apache may attempt to resolve the request both with and without prepending the DocumentRoot.
This means we can trick Apache into loading a file using its full path from within a subdirectory. A request to:
https://hackcentral.darkfor.ge/old/var/www/html/secret.txt
bypasses the rewrite rules and successfully discloses the contents ofsecret.txt
.
Accessing getFlag.php
With the value of secret.txt
in hand, we are ready to retrieve the flag by making a request to getFlag.php
with the session
cookie set. However, getFlag.php
is not located in the DocumentRoot, it resides in /var/www/templates
.
From the Apache config, we observe that requests to /new/
are rewritten to /var/www/templates/
. However, there is a catch: a .html
suffix is automatically appended to any request within this directory. As a result, a request to:
https://hackcentral.darkfor.ge/new/getFlag.php
will be rewritten to/var/www/templates/getFlag.php.html
, which does not exist.
Bypassing the .html Suffix with AcceptPathInfo
According to the Apache documentation, the AcceptPathInfo
directive determines whether Apache accepts trailing path information after a filename. When enabled (as is the default for script handlers like PHP), Apache will treat extra path segments as values for the PATH_INFO
environment variable.
Since the challenge uses the official php:8.2-apache
Docker image, which includes a PHP handler, we can exploit this behavior. By appending a trailing slash to our request, we can effectively bypass the .html
suffix, as the rewritten path will now be interpreted as:
/var/www/templates/getFlag.php/.html
This allows Apache to execute getFlag.php
and pass .html
via PATH_INFO
.
To retrieve the flag:
- Set the
session
cookie to the value found insecret.txt
. - Make a request to:
https://hackcentral.darkfor.ge/new/getFlag.php/
Conclusion
This challenge demonstrates a combination of Apache misconfigurations, specifically DocumentRoot confusion and misuse of rewrite rules, paired with a subtle understanding of how Apache handles trailing path info. These bugs may seem innocuous individually, but when chained together, they enable full flag disclosure.
We’ll be releasing more CTF challenges soon, stay tuned!