an-inaccessible-admin-panel
Description
web/an-inaccessible-admin-panel (by Ketchup306) | 234 points
The Joker is on the loose again in Gotham City! Police have found a web application where the Joker had allegedly tampered with. This mysterious web application has login page, but it has been behaving abnormally lately. Some time ago, an admin panel was created, but unfortunately, the password was lost to time. Unless you can find it...
Can you prove that the Joker had tampered with the website?
Default login info: Username: default Password: password123
Link to login page: https://login-web-challenge.hsctf.com/
Analysis
When opening page we are greeted with simple login.
By viewing source code we can find javascript file in header.
<script src="login.js"></script>
There's 2 possible ways of authentication.
Admin (Actual user)
Default (Fake user)
if (username === "Admin" && validatePassword(password)) {
alert("Login successful. Redirecting to admin panel...");
window.location.href = "admin_panel.html";
}
else if (username === "default" && password === "password123") {
var websiteNames = ["Google", "YouTube", "Minecraft", "Discord", "Twitter"];
var websiteURLs = ["https://www.google.com", "https://www.youtube.com", "https://www.minecraft.net", "https://www.discord.com", "https://www.twitter.com"];
var randomNum = Math.floor(Math.random() * websiteNames.length);
alert("Login successful. Redirecting to " + websiteNames[randomNum] + "...");
window.location.href = websiteURLs[randomNum];
} else {
alert("Invalid credentials. Please try again.");
}
If we login as Admin
we are redirected to admin_panel.html
, let's try visiting it.

To get the flag we need a password. login.js
contains validatePassword
function which validates the password on client side.
function fii(num) { return num / 2 + fee(num); }
function fee(num) { return foo(num * 5, square(num)); }
function foo(x, y) { return x * x + y * y + 2 * x * y; }
function square(num) { return num * num; }
var key = [
32421672.5, 160022555, 197009354, 184036413, 165791431.5, 110250050,
203747134.5, 106007665.5, 114618486.5, 1401872, 20702532.5, 1401872, 37896374,
133402552.5, 197009354, 197009354, 148937670, 114618486.5, 1401872,
20702532.5, 160022555, 97891284.5, 184036413, 106007665.5, 128504948,
232440576.5, 4648358, 1401872, 58522542.5, 171714872, 190440057.5,
114618486.5, 197009354, 1401872, 55890618, 128504948, 114618486.5, 1401872,
26071270.5, 190440057.5, 197009354, 97891284.5, 101888885, 148937670,
133402552.5, 190440057.5, 128504948, 114618486.5, 110250050, 1401872,
44036535.5, 184036413, 110250050, 114618486.5, 184036413, 4648358, 1401872,
20702532.5, 160022555, 110250050, 1401872, 26071270.5, 210656255, 114618486.5,
184036413, 232440576.5, 197009354, 128504948, 133402552.5, 160022555,
123743427.5, 1401872, 21958629, 114618486.5, 106007665.5, 165791431.5,
154405530.5, 114618486.5, 190440057.5, 1401872, 23271009.5, 128504948,
97891284.5, 165791431.5, 190440057.5, 1572532.5, 1572532.5,
];
function validatePassword(password) {
var encryption = password.split("").map(function (char) { return char.charCodeAt(0); });
var checker = [];
for (var i = 0; i < encryption.length; i++) {
var a = encryption[i];
var b = fii(a);
checker.push(b);
}
if (key.length !== checker.length) { return false; }
for (var i = 0; i < key.length; i++) {
if (key[i] !== checker[i]) { return false; }
}
return true;
}
Encryption takes in a string, converts it into character array, then ascii codes array, performs some mathmatical operation and if key
matches encrypted input
then input is the password.
Solution
Reversing the logic seems a bit hard, so I tried to matching outputs.
import string
def fii(num): return num / 2 + fee(num)
def fee(num): return foo(num * 5, square(num))
def foo(x, y): return x*x + y*y + 2*x*y
def square(num): return num * num
key = [ ... ]
ALPHABET = string.printable
CIPHER = { # Create "encode_char: char" dictionary
fii(ord(c)): c
for c in ALPHABET
}
print(
"".join( # Join decoded characters
CIPHER[encoded_char] # Get value of `encoded_char`
for encoded_char in key
)
)
Flag: flag{Admin, [DECODED_KEY]}
Last updated