WIZER CTF #15: LOGIN HERE

If you're a returning visitor to our CTF Recaps, feel free to dive straight into the insights! For first-time explorers, let us quickly introduce you to the essence of these recaps. Wizer CTFs were introduced to challenge developers, encouraging them to adopt a hacker's mindset and thereby code more securely. This initiative is a pivotal part of our new security awareness training, specially crafted for development teams - Wizer's Secure Code Training for Developers!

After a challenge retires, our Wizer Wizard and CTO, Itzik Spitzen, crafts takeaways that offer valuable insights into the challenge, focusing on the defensive perspective for your script. Curious to test-drive a CTF before delving into the notes? Visit wizer-ctf.com – it's free, and there's something for all skill levels!

Link to challenge #15

Goal

In this challenge, we're identifying a tricky scenario of an SQL Injection. Let's take a look!

Description of code

The code below introduces a simple login scenario, to a certain app. The endpoint receives a username and a password and is intended to validate them against the DB. The password is hashed using bcrypt + salt and hence the first step is to find the user by name and fetch the user's record. Once that's done, the hashed password is retrieved from the record and is being compared to the clear-text version using a utility that the bcrypt library provides `bcrypt.compareSync()`. In an attempt to block situations of unauthorized retrieval of the users, the code also checks that only a single record was indeed retrieved before it returns it.


ctf-15_single_record

See the full code below:


ctf-15-code-2

What’s wrong with that approach?

Looking at the SQL query execution, it is pretty obvious that an SQL Injection is possible, however, at a first glance, it's much less obvious how it can be exploited to allow someone to login as a different user. We're unable to get database records without being able to match their bcrypt hash. SQL Injection is a pretty flexible option and a potential attacker could use it in various different ways.

What would a successful SQLi attack look like in this case?

The two most common exploits of SQLi are: (1) get authorized access to data by fetching unintended records, and (2) trick the query to "think" it got a valid record by specifying the returned values as literals. An attacker who identifies the vulnerability could fairly quickly narrow down the options and choose the second one. So what would this look like? First, we'd need to escape the intended query and end it, making sure it returns no records, and that's easy, we could simply start our username value with a ' (apostrophe). Then we can chain a new SQL command using the UNION keyword to start a fresh query. The attacker would need to make sure that the query is valid, and it's fairly easy to identify what the client code needs in order to login by looking at the client code (ReactJS & NextJS code might be a little less straightforward to find through the console but you get used to it ;):


ctf-15-code-3

 

Looking at the following: `var n = JSON.stringify(c()(document.forms[0]).serializeArray());` the attacker can see that the format is a JSON format converted directly from an HTML form, this makes it look like `[{"name": "username", "value": "itzik"}, {"name": "password": "..."}]`. Secondly, we can see that upon getting HTTP/200 from the server, the client code shows an alert of the firstname and lastname, which we'd need to mimic. That said, an attacker would have to make sure that the bcrypt validation passess, hence, they have to use a tool to hash a preset password and select all as literals to trick the code to think they are someone else. Here's how "username" payload might look like: "' UNION SELECT 3,'some-name','$2a$10$DhrNvlByeVanMCyxhKd.cemFY16Qpxs7ipur1mBGJyU3IfXIuEuu2', 'admin','Gabriel','Tensey' -- -". The bcrypt string is a genuinely hashed password of our choice, which we'll provide as the value for the second argument, and as you can see, all the other values are literals of our choice. So the record returned is actually all fake and does the trick, where last and first name are correctly present. The "-- -" sequence at the end makes sure that whatever comes later, is commented out, so it's not in our way.

So what?

SQLi might sound like something we're all aware of, but in fact, there are a lot of cases of it in the wild. The risks attached to an SQLi vulnerability, start with unauthorized access to data, but could get to gaining full control over a system, connecting as super-admin etc.

Main Takeaways:

  • Its is not enough to add validations after-the-fact to prevent SQLi risks:
    While it's always important to keep our code clean, clear and tight, SQLi is a very flexible vulnerability, which is hard to externally mitigate without directly addressing it. Use one of the best practices to prevent SQLi, such as using prepared statements, parameterized queries, and stored procedures.
  • Strong hashing won't save you from getting hacked:
    Bcrypt + salt is a fairly decent hashing mechanism, but it's not a silver bullet. It's important to remember that the hashing mechanism is only as strong as the weakest link in the chain. In this case, the weakest link is the SQLi vulnerability, which allows an attacker to bypass the hashing.


 

Wanna join us on our next challenge? Sign up for our mailing list at wizer-ctf.com.

CODE WIZER!

Past Challenges

CTFs For Developers