OverTheWire: Natas

Level 25 > Level 26


Cool quote.

    // cheers and <3 to malvina
    // - morla

    function setLanguage(){
        /* language setup */
            if(safeinclude("language/" . $_REQUEST["lang"] ))
                return 1;
    function safeinclude($filename){
        // check for directory traversal
            logRequest("Directory traversal attempt! fixing request.");
        // dont let ppl steal our passwords
            logRequest("Illegal file access detected! Aborting!");
        // add more checks...

        if (file_exists($filename)) { 
            return 1;
        return 0;
    function listFiles($path){
        if ($handle = opendir($path))
            while (false !== ($file = readdir($handle)))
                if ($file != "." && $file != "..")
        return $listoffiles;
    function logRequest($message){
        $log="[". date("d.m.Y H::i:s",time()) ."]";
        $log=$log . " " . $_SERVER['HTTP_USER_AGENT'];
        $log=$log . " \"" . $message ."\"\n"; 
        $fd=fopen("/var/www/natas/natas25/logs/natas25_" . session_id() .".log","a");

<div id="content">
<div align="right">
<select name='lang' onchange='this.form.submit()'>
<?php foreach(listFiles("language/") as $f) echo "<option>$f</option>"; ?>

    echo "<h2>$__GREETING</h2>";
    echo "<p align=\"justify\">$__MSG";
    echo "<div align=\"right\"><h6>$__FOOTER</h6><div>";
<div id="viewsource"><a href="index-source.html">View sourcecode</a></div>

When the page loads, setLanguage() looks at the lang parameter and includes a file of that name from the language directory. We know the default language is ‘en’ so let’s see if the server lets us view it.


OK that was pretty lucky, did not expect that to load as text. Apparently the greeting, msg, and footer variables are included from the language file. Not just included, but safeinclude‘d! That function has a check for directory traversal which works by stripping out instances of ../ which seems like a good idea but it sucks big league (bigly?). Reminds me of one of my favorite XSS challenges, the classic scrscriptipt. Remove script from scrscriptipt and you’re left with… script.

Same principle here, except we’d like to end up with a ../ after str_replace runs. Proposal:


There’s no reason for the leading ../ looking back, but that’s what came to mind.

So we can very likely bypass the directory traversal detection but how do we get the password? There’s an additional check in safeinclude that prohibits the name of the password file which appears effective.

The logRequest function logs the HTTP_USER_AGENT string into a log file named by the user’s session token without sanitization. Now we have all the pieces - load PHP into the log file via injecting the user agent string, then load the log file as a language using directory traversal (and the traversal detection bypass).

A test run, let’s see if I can inject ‘oh damn!’ as the msg variable into the logs and execute the log via inclusion as a language.

User-Agent: <?php global $_MSG; $_MSG='oh damn!'; ?>



Now for something more useful - loading the password file into one of these variables, say the footer.

User-Agent: <?php global $_FOOTER; $_FOOTER=file_get_contents('/etc/natas_webpass/natas26'); ?>