Web
Nmap discovered a web server on the target port 80
The running service is Apache httpd 2.4.29
Webroot
I am re-directed to a login page at
login.php
Wappalyzer identified that the web application is written in PHP
the footer shows that its created by m4lwhere and points to an external web server
I tried some default/weak credentials without any luck
Opting out to fuzzing
Fuzzing
┌──(kali㉿kali)-[~/archive/htb/labs/previse]
└─$ ffuf -c -w /usr/share/wordlists/seclists/Discovery/Web-Content/directory-list-2.3-medium.txt -u http://$IP/FUZZ -ic -e .php,.txt,.html
________________________________________________
:: Method : GET
:: URL : http://10.10.11.104/FUZZ
:: Wordlist : FUZZ: /usr/share/wordlists/seclists/Discovery/Web-Content/directory-list-2.3-medium.txt
:: Extensions : .php .txt .html
:: Follow redirects : false
:: Calibration : false
:: Timeout : 10
:: Threads : 40
:: Matcher : Response status: 200,204,301,302,307,401,403,405,500
________________________________________________
download.php [Status: 302, Size: 0, Words: 1, Lines: 1, Duration: 1431ms]
login.php [Status: 200, Size: 2224, Words: 486, Lines: 54, Duration: 95ms]
files.php [Status: 302, Size: 4914, Words: 1531, Lines: 113, Duration: 114ms]
index.php [Status: 302, Size: 2801, Words: 737, Lines: 72, Duration: 3442ms]
header.php [Status: 200, Size: 980, Words: 183, Lines: 21, Duration: 95ms]
nav.php [Status: 200, Size: 1248, Words: 462, Lines: 32, Duration: 94ms]
footer.php [Status: 200, Size: 217, Words: 10, Lines: 6, Duration: 95ms]
css [Status: 301, Size: 310, Words: 20, Lines: 10, Duration: 94ms]
status.php [Status: 302, Size: 2966, Words: 749, Lines: 75, Duration: 97ms]
js [Status: 301, Size: 309, Words: 20, Lines: 10, Duration: 95ms]
logout.php [Status: 302, Size: 0, Words: 1, Lines: 1, Duration: 95ms]
accounts.php [Status: 302, Size: 3994, Words: 1096, Lines: 94, Duration: 94ms]
config.php [Status: 200, Size: 0, Words: 1, Lines: 1, Duration: 94ms]
logs.php [Status: 302, Size: 0, Words: 1, Lines: 1, Duration: 97ms]
server-status [Status: 403, Size: 277, Words: 20, Lines: 10, Duration: 91ms]
While ffuf return a lot of files, most of them are locked away from the login page
The only files that returned without code 302 are header.php
, footer.php
, and nav.php
header.php and footer.php
Both
header.php
and footer.php
are pretty much empty
nav.php
The
nav.php
file is rather interesting as it contains buttons pointing to the other files
All of these files are locked away from the authentication and returns 302 to the login.php
file
Vulnerability
I pretty much tried all the injections techniques that I could think of
However, the
login.php
file doesn’t seem to be vulnerable to any of them
But then I found something interesting
execution after redirect
If I send a GET request to a resource that is locked away from the authentication;
index.php
The web server responses with the code 302 and re-directs me to the
login.php
file
This seems pretty usually as it locks resources behind the authentication
But What if I manually modified the intercepted response to the code 200?
I am able to access the
index.php
file without getting re-direct to the login page.
This vulnerability is know as Execution After Redirect (EAR) Execution After Redirect (EAR) is an attack where an attacker ignores redirects and retrieves sensitive content intended for authenticated users. To prevent this type of attack, it is important to ensure that the server-side authentication and access control mechanisms are properly implemented and validated. Additionally, server-side code should always return a redirect status code when redirecting users to protected resources, and should ensure that the redirect URL is properly validated and sanitized to prevent any malicious input from being used in the redirect.
I will be continuing the numeration
The website claims to be Precise File Hosting Service Management Is that a file server of sort? It also suggests to create an account
The header here must be the nav.php file that I enumerated earlier
Each button is pointing to other files; including
accounts.php
, file.php
, status.php
, and file_logs.php
accounts.php
Using the same EAR technique, I am able to access the
accounts.php
file
It seems that I am able to create an account here.
It also notes that only admins should be able to access this page
I will create one.
Using the same EAR technique, I created a testing account
Authentication
I will now attempt to authenticate to the web application with the created testing account
Successfully authenticated as the
tester
user
Now I don’t need to keep using the EAR technique to get things done
files.php
While the
files.php
file supports a file upload feature, there is an existing file, SITEBACKUP.ZIP
, uploaded by newguy
Hovering the cursor over the file, it points to another resource
assuming that it’s using php include and the file
attribute is set to 32
, there must have been 31 other files prior
Nevertheless, I will download the website backup archive
siteBackup.zip
┌──(kali㉿kali)-[~/…/htb/labs/previse/siteBackup]
└─$ unzip siteBackup.zip
Archive: siteBackup.zip
inflating: accounts.php
inflating: config.php
inflating: download.php
inflating: file_logs.php
inflating: files.php
inflating: footer.php
inflating: header.php
inflating: index.php
inflating: login.php
inflating: logout.php
inflating: logs.php
inflating: nav.php
inflating: status.php
The archive contains 13 PHP files These must be the source codes
config.php
┌──(kali㉿kali)-[~/…/htb/labs/previse/siteBackup]
└─$ cat config.php
<?php
function connectDB(){
$host = 'localhost';
$user = 'root';
$passwd = 'mysql_p@ssw0rd!:)';
$db = 'previse';
$mycon = new mysqli($host, $user, $passwd, $db);
return $mycon;
}
?>
the config.php
file contains a db credential; root
:mySQL_p@ssw0rd!:)
status.php
The
status.php
file appears to be checking the backend DB as well as some other info
Source Code
┌──(kali㉿kali)-[~/…/htb/labs/previse/siteBackup]
└─$ cat status.php
<?php
session_start();
if (!isset($_SESSION['user'])) {
header('location: login.php');
}
?>
<?php include( 'header.php' ); ?>
<title>Previse Status</title>
</head>
<body>
<?php include( 'nav.php' ); ?>
<section class="uk-section uk-section-default">
<div class="uk-container">
<h2 class="uk-heading-divider">Status</h2>
<div class="uk-container" uk-grid>
<div><p>check website status:</p></div>
</div>
<div class="uk-container">
<?php
$db = connectDB();
if ($db === false) {
echo("<p class='uk-text-danger'>MySQL server is not functioning properly!</p>");
die("error: Could not connect. " . $db->connect_error);
} else {
echo("<p class='uk-text-success'>MySQL server is online and connected!</p>");
$userQuery = "SELECT id FROM accounts;";
$fileQuery = "SELECT id FROM files;";
$userResult = $db->query($userQuery);
$fileResult = $db->query($fileQuery);
$userCount = $userResult->num_rows;
$fileCount = $fileResult->num_rows;
if ($userCount == 1) {
echo "<p>There is <b>{$userCount}</b> registered admin</p>";
} else {
echo "<p>There are <b>{$userCount}</b> registered admins</p>";
}
if ($fileCount == 1) {
echo "<p>There is <b>{$fileCount}</b> uploaded file</p>";
} else {
echo "<p>There are <b>{$fileCount}</b> uploaded files</p>";
}
}
$db->close();
?>
</div>
</div>
</section>
<?php include( 'footer.php' ); ?>
The extracted source code also reveals that This file itself is not much of use
file_logs.php
The
file_logs.php
file seems rather interesting as it features something
It seems that I am able to request log data to check who has downloaded files
I will first check the feature
There are 3 options (delimiters) to choose from
Upon clicking the SUBMIT button, my browser downloads a file;
out.log
┌──(kali㉿kali)-[~/archive/htb/labs/previse]
└─$ cat ~/Downloads/out.log
time,user,fileID
1622482496,m4lwhere,4
1622485614,m4lwhere,4
1622486215,m4lwhere,4
1622486218,m4lwhere,1
1622486221,m4lwhere,1
1622678056,m4lwhere,5
1622678059,m4lwhere,6
1622679247,m4lwhere,1
1622680894,m4lwhere,5
1622708567,m4lwhere,4
1622708573,m4lwhere,4
1622708579,m4lwhere,5
1622710159,m4lwhere,4
1622712633,m4lwhere,4
1622715674,m4lwhere,24
1622715842,m4lwhere,23
1623197471,m4lwhere,25
1623200269,m4lwhere,25
1623236411,m4lwhere,23
1623236571,m4lwhere,26
1623238675,m4lwhere,23
1623238684,m4lwhere,23
1623978778,m4lwhere,32
1681297763,tester,32
The out.log
file appears to be the content of a log file
The file contains a list of entries with columns for time
, user
, and fileID
Timestamps are in Unix time format, and the log appears to span over several years, with entries from both m4lwhere
and tester
users
The tester
user is my testing account and the m4lwhere
user must be someone else
The last entry shows me downloading the website backup archive
Source Code
┌──(kali㉿kali)-[~/…/htb/labs/previse/siteBackup]
└─$ cat file_logs.php
<?php
session_start();
if (!isset($_SESSION['user'])) {
header('location: login.php');
}
?>
<?php include( 'header.php' ); ?>
<title>Previse File Access Logs</title>
</head>
<body>
<?php include( 'nav.php' ); ?>
<section class="uk-section uk-section-default">
<div class="uk-container">
<h2 class="uk-heading-divider">Request Log Data</h2>
<p>We take security very seriously, and keep logs of file access actions. We can set delimters for your needs!</p>
<p>Find out which users have been downloading files.</p>
<form action="logs.php" method="post">
<div class="uk-margin uk-width-1-4@s">
<label class="uk-form-label" for="delim-log">file delimeter:</label>
<select class="uk-select" name="delim" id="delim-log">
<option value="comma">comma</option>
<option value="space">space</option>
<option value="tab">tab</option>
</select>
</div>
<button class="uk-button uk-button-default" type="submit" value="submit">Submit</button>
</form>
</div>
</section>
<?php include( 'footer.php' ); ?>
Checking the source code reveals that it’s sending a POST request with an attribute; delim
, to the logs.php
file
I will check the logs.php
file
logs.php
┌──(kali㉿kali)-[~/…/htb/labs/previse/siteBackup]
└─$ cat logs.php
<?php
session_start();
if (!isset($_SESSION['user'])) {
header('Location: login.php');
exit;
}
?>
<?php
if (!$_SERVER['REQUEST_METHOD'] == 'POST') {
header('Location: login.php');
exit;
}
/////////////////////////////////////////////////////////////////////////////////////
//I tried really hard to parse the log delims in PHP, but python was SO MUCH EASIER//
/////////////////////////////////////////////////////////////////////////////////////
$output = exec("/usr/bin/python /opt/scripts/log_process.py {$_POST['delim']}");
echo $output;
$filepath = "/var/www/out.log";
$filename = "out.log";
if(file_exists($filepath)) {
header('Content-Description: File Transfer');
header('Content-Type: application/octet-stream');
header('Content-Disposition: attachment; filename="'.basename($filepath).'"');
header('Expires: 0');
header('Cache-Control: must-revalidate');
header('Pragma: public');
header('Content-Length: ' . filesize($filepath));
ob_clean(); // Discard data in the output buffer
flush(); // Flush system headers
readfile($filepath);
die();
} else {
http_response_code(404);
die();
}
?>
The logs.php
file is the actual file that is responsible for the feature as it does the execution
It uses PHP exec() method to execute the following OS commands; /usr/bin/python /opt/scripts/log_process.py {$_POST['delim']}
The {$_POST['delim']}
argument is being the user input from the delim
attribute
While I have no way of knowing the Python script, /opt/scripts/log_process.py
, it seems that the file might be vulnerable to OS command injection as the logs.php
file doesn’t do anything to validate the user input for sanatization