Flare-On 2021: credchecker
Flare-On 8 got off to an easy start with an HTML page and a login form. The page has JavaScript to accept and check the password, and I’ll show two ways to get the flag - pulling the password and then logging in, and decrypting the flag buffer.
Challenge
Welcome to Flare-On 8! This challenge surves as your tutorial mission for the epic quest you are about to emark upon. Reverse engineer the Javascript code to determine the correct username and password the web page is looking for and it will show you the flag. Enter that flag here to advance to the next stage. All flags will be in the format of valid email addresses and all end with “@flare-on.com”.
The archive (password “flare”) contains three files, an HTML page and two images:
$ find . -type f
./img/logo.png
./img/goldenticket.png
./admin.html
Running It
Opening the page in Firefox presents a simple login form:
Just guessing a username and password returns a failure notification at the bottom:
RE
Page Source
The page head has all the CSS inline, which isn’t interesting here. The body has four divs
:
<div class="content" id="banner">
<img id="logo" src="img/logo.png" alt="Flare-On 8">
<h3>Administrator Verification Form</h3>
<p">Enter your flare-on administrator credentials in this secure form to securely verify your security level. This is an official request and compliance is manditory.</p>
</div>
<div class="content" id="formdiv">
<form id="credform">
<label for="usrname">Username</label>
<input type="text" id="usrname" name="usrname" onchange="dataEntered()">
<label for="psw">Password</label>
<input type="password" id="psw" name="psw" onkeydown="dataEntered()" onchange="dataEntered()">
</form>
<button id="checkbtn" disabled="true" onclick="checkCreds()">Check Credentials</button>
</div>
<div class="content" id="message">
<h3>Password must contain the following:</h3>
<p class="invalid">The correct password</p>
<p class="invalid">Not an incorrect password</p>
<p>If you continue to fail, please ask your parents if it is too late to change your major</p>
</div>
<div class="content" id="winner">
<img src="img/goldenticket.png" alt="A Golden Ticket">
Welcome to Flare-On 8 here is your first flag:<br>
<label id="final_flag">flag goes here</label>
</div>
The first is the prompt, followed by the form. I’ll note that on any key press into either of the input fields, it calls dataEntered()
. On clicking the button, it calls checkCreds()
.
The other two divs with id message
and winner
display the response for a bad password and the flag. They are both set to display: none;
in CSS.
The JavaScript defines some variables (including encoded_key
), and the two functions noted above:
<script>
var form = document.getElementById("credform");
var username = document.getElementById("usrname");
var password = document.getElementById("psw");
var info = document.getElementById("infolabel");
var checkbtn = document.getElementById("checkbtn");
var encoded_key = "P1xNFigYIh0BGAofD1o5RSlXeRU2JiQQSSgCRAJdOw=="
function dataEntered() {
if (username.value.length > 0 && password.value.length > 0) {
checkbtn.disabled = false;
} else {
checkbtn.disabled = true;
}
}
function checkCreds() {
if (username.value == "Admin" && atob(password.value) == "goldenticket")
{
var key = atob(encoded_key);
var flag = "";
for (let i = 0; i < key.length; i++)
{
flag += String.fromCharCode(key.charCodeAt(i) ^ password.value.charCodeAt(i % password.value.length))
}
document.getElementById("banner").style.display = "none";
document.getElementById("formdiv").style.display = "none";
document.getElementById("message").style.display = "none";
document.getElementById("final_flag").innerText = flag;
document.getElementById("winner").style.display = "block";
}
else
{
document.getElementById("message").style.display = "block";
}
}
</script>
dataEntered()
just enables the submit button if both fields have data in them. checkCreds()
is what’s interesting.
Solve With Password
The first line of checkCreds()
is where it decides if it will show the flag or the fail message:
if (username.value == "Admin" && atob(password.value) == "goldenticket")
The username is “Admin” and the password when passed to atob
will result in the string “goldenticket”. atob
is the JavaScript function for base64 decode. I’ll base64 encode the target string:
$ echo -n goldenticket | base64
Z29sZGVudGlja2V0
Entering that as the password with the known username provides the flag:
Flag: enter_the_funhouse@flare-on.com
Solve By Decoding
The challenge can also be solved by ignoring the password, and looking at how encoded_key
is decoded to form the flag in this loop:
var key = atob(encoded_key);
var flag = "";
for (let i = 0; i < key.length; i++)
{
flag += String.fromCharCode(key.charCodeAt(i) ^ password.value.charCodeAt(i % password.value.length))
}
This is where I like to drop into a Python terminal (run python3
). The first thing the code does is base64 decode the encoded_key
into key
:
>>> enc = "P1xNFigYIh0BGAofD1o5RSlXeRU2JiQQSSgCRAJdOw=="
>>> import base64
>>> base64.b64decode(enc)
b'?\\M\x16(\x18"\x1d\x01\x18\n\x1f\x0fZ9E)Wy\x156&$\x10I(\x02D\x02];'
>>> key = base64.b64decode(enc)
Here’s a short video on how I built this list comprehension to produce the flag:
The result is:
>>> ''.join([chr(x^ord(y)) for x,y in zip(key, cycle(password))])
'enter_the_funhouse@flare-on.com'