The Pitfalls of Client-Side Authentication: Solutions to Net-Force JavaScript CTF challenges
Client-side authentication is when authentication checks are performed completely at users' side. The idea is that the authentication procedures, methods, or codes are delivered to the client, where they are executed to determine whether a user has access. This has never has been secure. Delivering the intricacies of authentication procedures to the user ensures that a malicious user can perform 'white box' testing and look deep into the codes for vulnerabilities. As a result, chances of authentication bypass, sensitive information disclosure and credentials leakage are extremely high. This paper provides practical demonstrations of such flaws in the form of solutions to JavaScript security CTF challenges on NetForce.
About Net-Force JavaScript Challenges
What should you learn next?
In order to solve these challenges, you are required to locate the correct passwords that are revealed to you after exploiting weaknesses in client-side authentication procedures. You can attempt them in any order but you must be logged in to try potential passwords. While attempting these challenges, because these scripts are being executed in the browser, we will try to read the source code pertaining to these "security" scripts and see if we can bypass authentication or locate the credentials hard-coded in the scripts.
Spoiler Alert!!
Please be advised that the following content provides solutions to the intriguing JavaScript challenges on Net-Force. It would be unavailing to read further without having tried your absolute best at the challenges first.
Solutions to JavaScript Challenges 1 to 7
JavaScript Challenge 1, Level 101: "Javascript, secure?"
In this challenge, we are presented with a JavaScript alert box that demands a username and password [Figure 1].
Figure 1
We need to study the code but because we cannot 'right click' the page, while using Chrome we add 'view-source:' before the URL to access the source code of the page. We access:
view-source:https://www.net-force.nl/challenge/level101/secret.html
The resulting code clearly has the username and password embedded in cleartext – quite easy since this is the first challenge in this category [Figure 2].
Figure 2
Password: javascript
JavaScript Challenge 2, Level 102: "This won't take long..."
This challenge requires us to locate the password and promises that it 'won't take long', which is true since a look at the source code of the webpage reveals the JavaScript function that is testing the password. This function, 'submitentry()', has instructions to verify the user-supplied password against a self-generated password. Our target is to determine the password that this function self-generates [Figure 3].
Figure 3
The instructions for password generation are simple enough that we can follow the script and calculate the password manually. However, it would be faster to use an online JavaScript compiler that will execute this function for us and display the value of the password in the end. Hence, after executing a slightly modified version of the function that contains an instruction to 'alert' (display) the password, we can proceed to the next challenge [Figure 4].
Figure 4
Password: bingo123
JavaScript Challenge 3, Level 104: "Escape now!!!"
This challenge presents us with a login box that demands a username and password, while the security is again implemented in the form of a JavaScript function [Figure 5].
Figure 5
Reading the source code reveals that there is an unescape() function which indicates the presence of an encoded string. To make sense of the encoded part of the script, we need to decode and read it using the unescape() function. We store the value of the decoded string in a variable 'm' and then output the contents of 'm' via an alert box. Again, we use the online JavaScript compiler for this purpose [Figure 6].
Figure 6
The decoded text shows us the username and password embeded in plaintext form in the script [Figure 6].
Username: user
Password: member
JavaScript Challenge 4, Level 103: "Is this safe...?!?"
Like other challenges, this one requires a username and password as well. A quick glance at the source code presents us with a username and hash pair in the HTML comments [Figure 7].
Figure 7
We ran this hash through 'John the Ripper' and the plaintext pertaining to the hash turned out to be 'blaat' [Figure 8].
Figure 8
However, this is not the password to the challenge page. A further look at the JavaScript code tells us that the user is being directed to a 'location' corresponding to the username. Hence, we explore the following link:
https://www.net-force.nl/challenge/level103/soulslayer/
Here, we notice the file 'blaat.html', and the password to this challenge is placed inside this HTML file [Figure 9].
Figure 9
Password: blaataap
JavaScript Challenge 5, Level 105: "Micro$oft crap..."
The title of this challenge suggests that this challenge has something to do with some form of Microsoft security that requires the use of Internet Explorer [Figure 10].
Figure 10
After looking at the source code, we know that we need to decode the encoded part of the script that is stored in the variable 'Words'. Once again, we use an online JS compiler and decode the text. We notice that this text is encoded with JScript.Encode [Figure 11].
Figure 11
JScript.Encode is an encoding method given by Microsoft that is used for encoding JavaScript or VB script codes. If we wish to read this source code, we would need to decode the JScript.Encode text. We use this online decoder for this purpose, which shows us the decoded plaintext script [Figure 12].
Figure 12
After carefully reading the script, we are able to determine that it is calculating the URL to the correct password. We slightly modify the script and make it display the value of the URL where the password is stored. We remove the 'for' loop since that simply verifies the password, and we add an instruction to display the values stored in 'pass2' and 'addr' [Figure 13] as follows:
alert(addr+pass2);
Figure 13
This gives us the location of the correct password with reference to the base URL of the challenge. The complete URL now becomes:
https://www.net-force.nl/challenge/level105/solution.php?blabla=Hall0
This location contains the password to this challenge [Figure 14].
Figure 14
Password: hack0r
JavaScript Challenge 6, Level 106: "HTML Guardian"
This challenge tells us that the source code of this page is protected by HTML Guardian, a tool built specifically to protect web source codes [Figure 15].
Figure 15
Our objective is to circumvent HTML Guardian's defense and read the actual source code. We notice that 'right-click' is disabled on this page and so we insert 'view-source:' before the URL in Chrome to view the source code as follows:
view-source:https://www.net-force.nl/challenge/level106/
As expected, the source code is not in plaintext form. However, we notice that the function eval() and unescape() seem to be decoding some part of the script [Figure 16]. This is where we start.
Figure 16
We use our online JS compiler once again to save the decoded text in a variable and then 'alert' the contents of this variable [Figure 17].
Figure 17
After proper indentation and arrangement of the code, we notice that the function koh() is making some calculations and ultimately returning the value of 'M' [Figure 18].
Figure 18
We modify the code to alert the value of 'M' before M is returned by the function. The plaintext code is then displayed to us, and the password is hidden inside this plaintext source code [Figure 19].
Figure 19
Password: 0nd3rW4t3r
JavaScript Challenge 7, Level 107: "Having fun?!? :-)"
This challenge requires a username and password pair and tells us that the webpage is protected with HTML Protect 3 [Figure 20].
Figure 20
Our objective is to locate the username and password hidden somewhere within the source code of the page. When we look at the source code, we find some encoded text and focus our initial efforts towards the text stored in variable 'e'. We alert the value of 'e' and discover the plaintext script [Figure 21].
Figure 21
This plaintext script that we obtained seems to be calculating the value of the variable 's'. So we attempt to execute this JavaScript and determine the value of 's'. However, since we ran only a part of the code that pertains to 'e', we got an error that the value of 'd' is undefined. The function stored inside 'e' needs the value of 'd', which can be obtained from the source code of the webpage. Hence, we copy of the value of 'd' from the webpage, insert it into our modified script, and then execute 'e' once again to obtain the plaintext script [Figure 22].
Figure 22
When we scroll down in this script, we notice the 'passwdok()' function (password OK). The name of this function suggests that it performs the password authentication check. A major part of this function is encoded and we need to decode this. Hence, we let the online JS compiler decode this script for us as before [Figure 23].
Figure 23
Now, we understand the authentication procedure. The variables 'good_login' and 'good_pass' hold the SHA1 hashes of the correct username and password respectively. The script calculates the SHA1 hash of user-supplied username and password, and the 'if' condition matches these values against the stored (correct) values. If there is a match, the script prompts 'Well Done!'. It becomes clear that we need to reverse these SHA1 hashes and here; once again, John the Ripper comes to the rescue [Figure 24].
Figure 24
Alternatively, you can use one of the many online hash decryption engines [Figure 25].
Figure 25
Password: bas:dude
End Remarks
As demonstrated by these challenges, client-side authentication is highly deficient and should be avoided. Anything that the user can closely inspect, like source codes, should contain as little information as possible about the authentications procedures. Furthermore, the practice of hard-coding plaintext passwords into scripts and delivering them at client-side for verifications is certainly not good. These JavaScript challenges at Net-Force demonstrated several pitfalls in client-side authentication. Slightly obscuring security mechanisms and procedures by simply encoding these using easily reversible techniques or functions, leads to a false sense of security.
What should you learn next?
If you enjoyed these, consider attempting more captivating challenges at Net-Force to test or build your skills in security. If you have spent a substantial amount of time on a specific challenge – and the solution has evaded you for long – then you can always come here to seek solutions. The solutions above discuss only successful attempts for the sake of brevity.