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,32The 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