Something weird going on at this pizza store!!
We're given a simple pizza chain website looking like this:
Looking at the source code, CSS, and images, nothing seems out of the ordinary. However, going to robots.txt
,
Going to that suspicious route, we find a "top secret" login page looking like this:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<title>Top Secret Government Access</title>
<link
href="https://fonts.googleapis.com/css2?family=Orbitron&display=swap"
rel="stylesheet"
/>
<link rel="stylesheet" href="/assets/css/secret-theme.css" />
<script src="/assets/js/auth.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/crypto-js/4.1.1/crypto-js.min.js"></script>
<script>
function hashPassword(password) {
return CryptoJS.SHA256(password).toString();
}
function validate() {
const username = document.getElementById("username").value;
const password = document.getElementById("password").value;
const credentials = getCredentials();
const passwordHash = hashPassword(password);
if (
username === credentials.username &&
passwordHash === credentials.passwordHash
) {
return true;
} else {
alert("Invalid credentials!");
return false;
}
}
</script>
</head>
<body>
<div class="container">
<h1>Top Secret Government Access</h1>
<form id="loginForm" action="login.php" method="POST" onsubmit="return validate();">
<label for="username">Username:</label>
<input type="text" id="username" name="username" required /><br />
<label for="password">Password:</label>
<input type="password" id="password" name="password" required /><br />
<input type="submit" value="Login" />
</form>
</div>
</body>
</html>
Trying some simple SQL injection payloads, nothing happens. But looking at the validate()
code, because validation is done on the client side, we can find the credentials we need to match in auth.js
:
const validUsername = "agent_1337";
const validPasswordHash = "91a915b6bdcfb47045859288a9e2bd651af246f07a083f11958550056bed8eac";
function getCredentials() {
return {
username: validUsername,
passwordHash: validPasswordHash,
};
}
But what's the unhashed version of the password? We can try some sketchy online hash-cracker lookup websites, and to my surprise,
Then, we have our final credentials:
username: agent_1337
password: intel420
Logging in, we get to the "top secret government portal":
We can download four AI-generated images, but nothing seems off about them either. The realization here is that this page reveals a special route for downloading files:
We can try to download other topsecretx.png
images, but none exist. We can also notice that any path not beginning with /assets/images/
fails with "path not allowed". Instead, we can finally apply path traversal to download the PHP handler for the admin portal endpoint,
<?php
$flag = 'INTIGRITI{70p_53cr37_m15510n_c0mpl373}';
if (isset($_GET['download'])) {
$file = $_GET['download'];
if (strpos($file, '/assets/images/') === 0) {
$filePath = __DIR__ . '/' . $file;
if (file_exists($filePath)) {
header('Content-Type: application/octet-stream');
header('Content-Disposition: attachment; filename="' . basename($filePath) . '"');
header('Content-Length: ' . filesize($filePath));
readfile($filePath);
exit();
} else {
die('File not found!');
}
} else {
die('File path not allowed!');
}
}
?>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Top Secret Portal</title>
<link href="https://fonts.googleapis.com/css2?family=Orbitron&display=swap" rel="stylesheet">
<link rel="stylesheet" href="/assets/css/secret-theme.css">
<script src="/assets/js/displayImage.js"></script>
</head>
<body>
<div class="container">
<h1>Welcome to the Top Secret Government Portal</h1>
<p>Authorized personnel only.</p>
<img id="selectedImage" src="/assets/images/topsecret1.png" alt="Selected Image">
<form method="GET" action="">
<label for="image">Select Image to Download:</label>
<select name="download" id="image" onchange="updateImage()">
<option value="/assets/images/topsecret1.png">Image 1</option>
<option value="/assets/images/topsecret2.png">Image 2</option>
<option value="/assets/images/topsecret3.png">Image 3</option>
<option value="/assets/images/topsecret4.png">Image 4</option>
</select>
<button type="submit">Download</button>
</form>
</div>
</body>
</html>
getting us the flag.