Web Root


Checking the web root directory of the web application after gaining a foothold

While checking web root directory mostly comes down to retrieval of DB credential for further enumeration, but it is rather unnecessary in the current circumstance due to the earlier enumeration made with SQL Injection

Nonetheless, I will do so in order to better understand the web application

www-data@ubuntu:/var/www/Magic$ ll
total 52K
4.0K drwxr-xr-x 4 www-data www-data 4.0K Jul 12  2021 .
4.0K drwxr-xr-x 4 root     root     4.0K Jul  6  2021 ..
4.0K drwxr-xr-x 4 www-data www-data 4.0K Jul  6  2021 images
4.0K drwxrwxr-x 6 www-data www-data 4.0K Jul  6  2021 assets
8.0K -rw-r--r-- 1 www-data www-data 4.5K Oct 22  2019 upload.php
8.0K -rw-r--r-- 1 www-data www-data 5.5K Oct 22  2019 login.php
8.0K -rw-rw-r-- 1 www-data www-data 4.5K Oct 22  2019 index.php
4.0K -rw-r--r-- 1 www-data www-data   72 Oct 18  2019 logout.php
4.0K -rwx---r-x 1 www-data www-data  162 Oct 18  2019 .htaccess
4.0K -rw-r--r-- 1 www-data www-data  881 Oct 16  2019 db.php5

While the web root directory contains the expected files, there are some surprises, such as .htaccess and db.php5

.htaccess


www-data@ubuntu:/var/www/Magic$ cat .htaccess
<FilesMatch ".+\.ph(p([3457s]|\-s)?|t|tml)">
SetHandler application/x-httpd-php
</FilesMatch>
<Files ~ "\.(sh|sql)">
   order deny,allow
   deny from all

While .htaccess file in general dictates how the Apache web server handles certain types of files within the web application, it is important to go over one by one

  1. <FilesMatch ".+\.ph(p([3457s]|\-s)?|t|tml)">
    • This section is using a regular expression pattern within <FilesMatch> to match file names that end with specific PHP file extensions. The regular expression is quite complex, but it essentially matches files with extensions like .php, .php3, .php4, .php5, .php7, .php-s, .phtml, and .pht.
  2. SetHandler application/x-httpd-php
    • This directive sets the handler for the matched files to be application/x-httpd-php. In Apache, a “handler” determines how a particular file should be processed. In this case, it’s instructing Apache to treat these files as PHP scripts, so they will be executed by the PHP interpreter when accessed through the web server.
  3. </FilesMatch>
    • This closing tag marks the end of the <FilesMatch> section.
  4. <Files ~ "\.(sh|sql)">
    • This section uses another regular expression pattern within <Files> to match files with extensions .sh (shell scripts) and .sql (SQL scripts).
    • <Files> is used to apply directives to specific files based on a regular expression pattern.
  5. order deny,allow
    • This directive sets the order of processing for access control. It specifies that the deny directives should be processed before the allow directives.
  6. deny from all
    • This directive explicitly denies access to any files matching the pattern (i.e., files with .sh or .sql extensions) for all users. It effectively blocks access to these files over the web.

In summary, this .htaccess file is configuring the Apache web server to treat files with certain PHP-related extensions as PHP scripts and execute them using the PHP interpreter. Additionally, it explicitly denies access to any files with .sh or .sql extensions for all users, effectively preventing them from being accessed through the web. This can enhance the security of your web application by preventing unauthorized access to sensitive script and SQL files.

upload.php


www-data@ubuntu:/var/www/Magic$ cat upload.php
<?php
session_start();
 
if (!isset($_SESSION['user_id'])) {
    header("location: login.php");
}
$target_dir = "images/uploads/";
$target_file = $target_dir . basename($_FILES["image"]["name"]);
$uploadOk = 1;
$allowed = array('2', '3');
 
// Check if image file is a actual image or fake image
if (isset($_POST["submit"])) {
    // Allow certain file formats
    $imageFileType = strtolower(pathinfo($target_file, PATHINFO_EXTENSION));
    if ($imageFileType != "jpg" && $imageFileType != "png" && $imageFileType != "jpeg") {
        echo "<script>alert('Sorry, only JPG, JPEG & PNG files are allowed.')</script>";
        $uploadOk = 0;
    }
 
    if ($uploadOk === 1) {
        // Check if image is actually png or jpg using magic bytes
        $check = exif_imagetype($_FILES["image"]["tmp_name"]);
        if (!in_array($check, $allowed)) {
            echo "<script>alert('What are you trying to do there?')</script>";
            $uploadOk = 0;
        }
    }
    //Check file contents
    /*$image = file_get_contents($_FILES["image"]["tmp_name"]);
    if (strpos($image, "<?") !== FALSE) {
        echo "<script>alert('Detected \"\<\?\". PHP is not allowed!')</script>";
        $uploadOk = 0;
    }*/
 
    // Check if $uploadOk is set to 0 by an error
    if ($uploadOk === 1) {
        if (move_uploaded_file($_FILES["image"]["tmp_name"], $target_file)) {
            echo "The file " . basename($_FILES["image"]["name"]) . " has been uploaded.";
        } else {
            echo "Sorry, there was an error uploading your file.";
        }
    }
}
?>
 
[...REDACTED...]

As expected earlier, the upload.php file contains a multiple security measures for the file upload feature;

  • Extension
  • Magic Number
  • Content (Filtered with PHP’s <?)

index.php


www-data@ubuntu:/var/www/Magic$ cat index.php
<!DOCTYPE HTML>
<html>
 
[...REDACTED...]
 
<!-- Wrapper -->
<div id="wrapper">
 
    <!-- Main -->
    <section id="main">
 
        <!-- Items -->
        <div class="items">
 
            <div class="item intro span-2">
                <div align="center"><h1>m̴̛̫̼̟͔̼̗̒̐ȧ̷̹͍͊́̿̊̿̈́g̷̲͚̖̣̏͂̿̇̇i̴̺̻̝͍̦͎̅͋́c̴̢͙̿̒̑͂͐̔͂</h1><br/></div>
            </div>
            <?php
            $path = "images/fulls";
            $dir_handle = @opendir($path) or die("Unable to open folder");
 
            while (false !== ($file = readdir($dir_handle))) {
                if ($file != '.' && $file != '..' && $file != 'Thumbs.db') {
                    $span = '<article class="item thumb span-' . rand(1, 3) . '">';
                    echo($span);
                    $random = '<h2>' . dechex(rand(1, 1000000000)) . '</h2>';
                    echo($random);
                    echo("<a href='images/fulls/$file' class='image'><img src='images/fulls/$file' alt=''></a>");
                    echo('</article>');
                }
            }
            echo('</div><div class="items">');
            closedir($dir_handle);
            $path = "images/uploads";
            $dir_handle = @opendir($path) or die("Unable to open folder");
            while (false !== ($file = readdir($dir_handle))) {
                if ($file != '.' && $file != '..' && $file != 'Thumbs.db'
                        && strpos($file, '.php') === false
                        && strpos($file, '.php3') === false
                        && strpos($file, '.php4') === false
                        && strpos($file, '.php5') === false
                        && strpos($file, '.phtml') === false) {
                    $span = '<article class="item thumb span-' . rand(1, 3) . '">';
                    echo($span);
                    $random = '<h2>' . dechex(rand(1, 1000000000)) . '</h2>';
                    echo($random);
                    echo("<a href='images/uploads/$file' class='image'><img src='images/uploads/$file' alt=''></a>");
                    echo('</article>');
                } else if (strpos($file, '.php') !== false
                        || strpos($file, '.php3') !== false
                        || strpos($file, '.php4') !== false
                        || strpos($file, '.php5') !== false
                        || strpos($file, '.phtml') !== false) {
                    echo('<article class="item thumb span-1">');
                    echo('<h2>Excuse me!</h2>');
                    echo("<a href='images/hey.jpg' class='image'><img src='images/hey.jpg' alt=''></a>");
                    echo('</article>');
                }
            }
            closedir($dir_handle);
            ?>
        </div>
 
    </section>
 
[...REDACTED...]

db.php5


</files>www-data@ubuntu:/var/www/Magic$ cat db.php5
<?php
class Database
{
    private static $dbName = 'Magic' ;
    private static $dbHost = 'localhost' ;
    private static $dbUsername = 'theseus';
    private static $dbUserPassword = 'iamkingtheseus';
 
    private static $cont  = null;
 
    public function __construct() {
        die('Init function is not allowed');
    }
 
    public static function connect()
    {
        // One connection through whole application
        if ( null == self::$cont )
        {
            try
            {
                self::$cont =  new PDO( "mysql:host=".self::$dbHost.";"."dbname=".self::$dbName, self::$dbUsername, self::$dbUserPassword);
            }
            catch(PDOException $e)
            {
                die($e->getMessage());
            }
        }
        return self::$cont;
    }
 
    public static function disconnect()
    {
        self::$cont = null;
    }
}

The db.php5 file defines the SQL configuration to the web application additionally, there is also the db credential; theseus:iamkingtheseus

Database


www-data@ubuntu:/var/www/Magic$ mysql
 
Command 'mysql' not found, but can be installed with:
 
apt install mysql-client-core-5.7   
apt install mariadb-client-core-10.1
 
Ask your administrator to install one of them.

Attempting to connecting to the mysql instance failed because the command line tool, mysql , does not exist within the target system

www-data@ubuntu:/var/www/Magic$ find /usr/bin -name mysql* -ls -executable -type f 2>/dev/null
    32858      0 lrwxrwxrwx   1 root     root           10 Jan 21  2020 /usr/bin/mysqloptimize -> mysqlcheck
    32849   3788 -rwxr-xr-x   1 root     root      3875176 Jan 21  2020 /usr/bin/mysqldump
    32848   3712 -rwxr-xr-x   1 root     root      3799752 Jan 21  2020 /usr/bin/mysqladmin
    32854   3704 -rwxr-xr-x   1 root     root      3790504 Jan 21  2020 /usr/bin/mysqlshow
    32869     28 -rwxr-xr-x   1 root     root        28448 Jan 21  2020 /usr/bin/mysqld_safe
    32867   3976 -rwxr-xr-x   1 root     root      4068280 Jan 21  2020 /usr/bin/mysqlbinlog
    32850      8 -rwxr-xr-x   1 root     root         7865 Jan 21  2020 /usr/bin/mysqldumpslow
    32838   3736 -rwxr-xr-x   1 root     root      3825320 Jan 21  2020 /usr/bin/mysqlcheck
    32865   3568 -rwxr-xr-x   1 root     root      3653288 Jan 21  2020 /usr/bin/mysql_ssl_rsa_setup
    32851   3704 -rwxr-xr-x   1 root     root      3791912 Jan 21  2020 /usr/bin/mysqlimport
    32866   3488 -rwxr-xr-x   1 root     root      3569976 Jan 21  2020 /usr/bin/mysql_tzinfo_to_sql
    32876   4340 -rwxr-xr-x   1 root     root      4442320 Jan 21  2020 /usr/bin/mysql_upgrade
    32855   3724 -rwxr-xr-x   1 root     root      3809512 Jan 21  2020 /usr/bin/mysqlslap
    32864   3696 -rwxr-xr-x   1 root     root      3784424 Jan 21  2020 /usr/bin/mysql_secure_installation
    32860      0 lrwxrwxrwx   1 root     root           10 Jan 21  2020 /usr/bin/mysqlrepair -> mysqlcheck
    32856      0 lrwxrwxrwx   1 root     root           10 Jan 21  2020 /usr/bin/mysqlanalyze -> mysqlcheck
    32842   3544 -rwxr-xr-x   1 root     root      3627200 Jan 21  2020 /usr/bin/mysql_config_editor
    32868     28 -rwxr-xr-x   1 root     root        26952 Jan 21  2020 /usr/bin/mysqld_multi
    32863   3536 -rwxr-xr-x   1 root     root      3616952 Jan 21  2020 /usr/bin/mysql_plugin
    32835  22032 -rwxr-xr-x   1 root     root     22558552 Jan 21  2020 /usr/bin/mysql_embedded
    32875   5060 -rwxr-xr-x   1 root     root      5179616 Jan 21  2020 /usr/bin/mysql_install_db
    32852   4188 -rwxr-xr-x   1 root     root      4286120 Jan 21  2020 /usr/bin/mysqlpump
    32853     40 -rwxr-xr-x   1 root     root        39016 Jan 12  2018 /usr/bin/mysqlreport

However, there is mysqldump available at /usr/bin directory

www-data@ubuntu:/var/www/Magic$ mysqldump -utheseus -piamkingtheseus --all-databases
mysqldump: [Warning] Using a password on the command line interface can be insecure.
-- MySQL dump 10.13  Distrib 5.7.29, for Linux (x86_64)
--
-- Host: localhost    Database: 
-- ------------------------------------------------------
-- Server version	5.7.29-0ubuntu0.18.04.1
 
/*!40101 SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */;
/*!40101 SET @OLD_CHARACTER_SET_RESULTS=@@CHARACTER_SET_RESULTS */;
/*!40101 SET @OLD_COLLATION_CONNECTION=@@COLLATION_CONNECTION */;
/*!40101 SET NAMES utf8 */;
/*!40103 SET @OLD_TIME_ZONE=@@TIME_ZONE */;
/*!40103 SET TIME_ZONE='+00:00' */;
/*!40014 SET @OLD_UNIQUE_CHECKS=@@UNIQUE_CHECKS, UNIQUE_CHECKS=0 */;
/*!40014 SET @OLD_FOREIGN_KEY_CHECKS=@@FOREIGN_KEY_CHECKS, FOREIGN_KEY_CHECKS=0 */;
/*!40101 SET @OLD_SQL_MODE=@@SQL_MODE, SQL_MODE='NO_AUTO_VALUE_ON_ZERO' */;
/*!40111 SET @OLD_SQL_NOTES=@@SQL_NOTES, SQL_NOTES=0 */;
 
--
-- Current Database: `Magic`
--
 
CREATE DATABASE /*!32312 IF NOT EXISTS*/ `Magic` /*!40100 DEFAULT CHARACTER SET latin1 */;
 
USE `Magic`;
 
--
-- Table structure for table `login`
--
 
DROP TABLE IF EXISTS `login`;
/*!40101 SET @saved_cs_client     = @@character_set_client */;
/*!40101 SET character_set_client = utf8 */;
CREATE TABLE `login` (
  `id` int(6) NOT NULL AUTO_INCREMENT,
  `username` varchar(50) NOT NULL,
  `password` varchar(100) NOT NULL,
  PRIMARY KEY (`id`),
  UNIQUE KEY `username` (`username`)
) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=latin1;
/*!40101 SET character_set_client = @saved_cs_client */;
 
--
-- Dumping data for table `login`
--
 
LOCK TABLES `login` WRITE;
/*!40000 ALTER TABLE `login` DISABLE KEYS */;
INSERT INTO `login` VALUES (1,'admin','Th3s3usW4sK1ng');
/*!40000 ALTER TABLE `login` ENABLE KEYS */;
UNLOCK TABLES;
/*!40103 SET TIME_ZONE=@OLD_TIME_ZONE */;
 
/*!40101 SET SQL_MODE=@OLD_SQL_MODE */;
/*!40014 SET FOREIGN_KEY_CHECKS=@OLD_FOREIGN_KEY_CHECKS */;
/*!40014 SET UNIQUE_CHECKS=@OLD_UNIQUE_CHECKS */;
/*!40101 SET CHARACTER_SET_CLIENT=@OLD_CHARACTER_SET_CLIENT */;
/*!40101 SET CHARACTER_SET_RESULTS=@OLD_CHARACTER_SET_RESULTS */;
/*!40101 SET COLLATION_CONNECTION=@OLD_COLLATION_CONNECTION */;
/*!40111 SET SQL_NOTES=@OLD_SQL_NOTES */;
 
-- Dump completed on 2023-10-01 11:03:39

There is the admin user with the password; Th3s3usW4sK1ng As mentioned above earlier, this has previously been enumerated via SQLi However, checking it for password reuse has not been done yet