Hey! Not your first time visiting our CTF Recaps? Go ahead and jump on down to the insights! If it is your first time, allow us to give a quick rundown of what these are. Wizer CTFs were launched to challenge developers to learn to think like a hacker in order to learn to code more securely. It's part of our new security awareness training we're designing focused on the dev team!
Once a challenge is retired the Wizer Wizard behind these creations - our very own CTO Itzik Spitzen - creates takeaways that provide clues into the challenge from the perspective of defending your script. Want to testdrive a CTF before reading the notes? Go ahead at wizer-ctf.com - it's free and there's something for all levels.
In this pretty tricky challenge, we identify two vulnerabilities, which should be used in order to capture the flag. The first one is an SQL Injection (a.k.a. SQLi), and the second is the use of a weak hashing algorithm, which is a specific case of Cryptographic Failure.
Description of code
Our code presents an innocent login page (https://chal7.vercel.app/), which is using a login API endpoint. The developer used MYSQL and ran a query to select a matching user & password. To prevent certain issues, the developer then ran another query to verify that the row returned indeed includes the originally specified username and password. General Note, you might notice that the received JSON format is somewhat strange, it is basically a name/value array; the developer here used an easy way to transform an HTML form which is posted directly to the backend into a JSON format, likely the most common and convenient data transport format. This is typically done using something like the following command:
`var formData = JSON.stringify($(document.forms).serializeArray());`.
What’s wrong with that approach?
Looking at the query, we can quickly identify an SQL Injection, which basically can be exploited by altering the username to inject unintended SQL into it. With that said, to capture the flag, it is not enough to exploit that vuln since the login process validates that the correct user row come first (at  position of the result). However, using the second weakness, which is the use of MD5 hashed passwords, an attacker could break in and impersonate an admin.
What would a successful SQLi & Weak Cypher attack look like in this case?
Once attackers realize that it's possible to exploit the SQLi vuln, they could invoke an API call to retrieve all the other users in the database, including their password. An attacker could send something like `"isaac' union all select * from users where '1'='1"` as the username (which in this case is sent within Array.value), then in return, they would get a list of all the users with their hashed passwords.
It is then easy to see who is an `admin` by looking at the `type` field of the returned list. The last step is to grab the MD5 hashed password and find a rainbow table or a free online search tool (such as CrackStation) to translate the MD5 back into the password and boom! the attacker can now login as an admin. An MD5 is a hash algorithm, which means that it's generally not decryptable, in other words, it's a `one way ticket` encryption. However, over the years, hackers created long lists of passwords (a.k.a. rainbow tables) with their MD5 source password, which is being used to crack mainly weaker passwords.
Side note: some players succeeded in creating a situation, using the SQLi vuln only, making the client code show that `issac` is an admin. This solution wasn't accepted by the system, for the simple reason that even if the client side `thinks` you're an admin, the server state still sees your user type and that's not considered a full impersonation.
SQLi is always very bad to have, even if you assume that you've encrypted everything properly. In this case it provides a crack with which the attacker can use another vuln to take over an admin account. Oftentimes, in the wild, an attacker won't use only a single vuln, and will likely combine techniques in order to get to the crown jewels the desire.
Attackers would not easily give up when they see some encrypted or hashed values, in many cases those could be bypassed or brute-forced into clear text values. Impersonating an admin could mean serious harm for the business, or yet again another step in a more sophisticated attack plan.
- Beware of SQLi:
As always, never forget to sanitize the user inputs, yet, the safest way to avoid SQLi is to use an ORM (Object Relational Mapping), prepared statements / parameterized queries or well written stored procedures.
- Always check that your password hashing algorithm is strong and enforce minimum password complexity:
Stronger hashing algorithms, such as bcrypt with salt and others, make it much harder to create effective rainbow tables. Brute-forcing decryption is ineffective with stronger passwords.
- Attackers typically use more than a single vuln to take over or tap into valuable data:
While sometimes we feel like every single secure coding step separately seems redundant, or the consequences are harmless, attackers constantly seek for a path to gain access to meaningful data or take over powerful credentials. That path could include multiple means of operation such as phishing campaigns, UI overlays and of course, code vulnerabilities. So we can never assume that they don't have access to any type of data. In this case, the developer "secured" the code by making sure that the user & password selected are indeed the ones sent, however, that can't come instead of sanitizing inputs or using a strict method to prevent SQLi. The protection approach should always be informed and intentional. Also, the developer could say: "well, what's the worst that could happen? they get a hashed password so they can't really use it...", which, again, isn't true in this case due to a different reason. Ideally, we should add multiple security layers, this way, if something goes wrong with a certain layer, there's always a fallback protection.
Wanna join us on our next challenge? Sign up for our mailing list at wizer-ctf.com.