Resources

People often ask me "How did you learn how to hack?" The answer: by reading. This page is a collection of the blog posts and other articles that I have accumulated over the years of my journey. Enjoy!

Finding an unseen SQL Injection by bypassing escape functions in mysqljs/mysql- 792

Stypr - Flatt SecurityPosted 4 Years Ago
  • MySQL is the most popular MySQL package in NodeJS. By using this properly, all queries are safe against SQL injection with inputs as strings via prepared statements and escaping. However, what happens when the input for the prepared statement is NOT a string? Madness!
  • An example query is shown below:
    connection.query(
    "SELECT * FROM accounts WHERE username = ? AND password = ?",
     [username, password], func...)
    
    Normally, we would expect a string (like 'admin') as the input for the username and password fields. If the query is setup insecurely, we may be able to put arbitrary objects into these fields. I would call this an unintended use case.
  • If an object is inserted, then weird things to start to happen. In particular, inserting a nested object with a 1 will evaluate to true within MySQL. With the query above, sending object like below will bypass the logic:
    data = {
      username: "admin",
      password: {
        password: 1,
      },
    };
    
  • For demonstrating this technique, the author even has a here. This call passes the opinion to SQLString to do the actual formatting. Within SqlString.js, the function format can be found. The logic of this is as follows:
    1. Iterate through each of the provided values.
    2. Convert each data type into something that can be understood by regex. For objects, this is done by objectToValues.
    3. Take the value and escape all malicious characters using a regex.
    4. Replace the placeholder in the query with the actual value.
  • The function objectToValues works by getting the value of each key in the object then running the escape code on each KEY and VALUE. Once done, it sets the SQL to be `<key>` = <value> in the SQL. The backticks are used for literal SQL statements, such as the username column. Interesting and this is NOT how this should work.
  • As a result, when the object is inserted, we are actually altering the query itself! From the payload that we used above, it turns into the query ...`password = password` = 1. For whatever reason, ...`password = password` = 1 will evaluate to true (tested locally in MySQL repl), allowing any user to authenticate as that user. Wow, what a crazy chain of events that makes this work!
  • To not be vulnerable to this issue, simply validate the data being put into queries; an object should never be treated like a string anyway. Overall, I really enjoyed this post and diving into the root cause of the problem on my own :)