HackerOne

How To Hunt For Injection Vulnerabilities' OR 1='1

How To Hunt For Injection Vulnerabilities' OR 1='1

The best hackers use their creativity to find vulnerabilities. They are not bound to the vulnerability types that they already know how to find. This is often demonstrated when a hacker chains together seemingly small vulnerabilities into something big. This blog post will give you more insights about how injection vulnerabilities work, and how you can use that knowledge to find more bugs.

Injection vulnerabilities come from improperly sanitized or completely unsanitized input. To demonstrate these type of vulnerabilities, this post will focus on a well-known vulnerability type: SQL injections. When exploiting SQL injection, a hacker injects arbitrary SQL commands to extract data, read files, or even escalate it to a remote code execution (RCE).

When you’re testing an application for injection vulnerabilities, you should pay close attention how your input is used and how the result of whatever you did is returned in the response. This might sound vague, so let’s give you an example. Imagine a webpage that requires a numeric ID to be given in a parameter. You can start poking around with it by submitting non-numeric values. Pay close attention to how the server responds. Does it show errors? If so, what kind of errors? Does the error reveal something about the architecture, or does it reveal that there’s strict numeric validation on the parameter? Is there anything that indicates it might lead to an injection vulnerability? Focus on detecting an anomaly first, and then figure out how to exploit it. Gather as much information and try as many different approaches as possible to get a sense of how the code behind the page works. If something doesn’t immediately work, make a note of it and come to it back later.

Let’s deep dive on SQL injections to help you understand what injection vulnerabilities are all about. For the sake of the proof of concept, pretend we built an application called “Names API”. It requires a MySQL server, which a PHP script connects to. There’s only one table in the database, which is called “names”. For some unknown reason, it tracks someone’s name and IP address. The IP address is kept secret and should not be disclosed to the internet. The application has been deployed to the internet and can be accessed at https://names-api/. The contents and structure of the table looks like this:

Table contents

The contents of index.php of our application looks like this:

<?php

// connect to localhost as root without a password, luckily 3306 is firewalled…
$connection = mysql_connect("localhost", "root", "");
mysql_select_db($connection, "names_api");

// fetch the record from the table, but since the user’s IP address is secret,
// lets only select the name - hackers will now never be able to see this!
$query = mysql_query("select name from names where id = $_GET['id']");

// make sure the record was found
if(mysql_num_rows($query) == 1) {
$object = mysql_fetch_assoc($query);

// return the name to the user
echo $object['name'];
}

If a user would visit https://names-api/?id=1, the server would respond with “meals”. Have you spotted the vulnerability already? It’s pretty straightforward if you see the code. When a user would use the application properly, it would pass IDs into the id parameter of the page. It would then look up the record in the database, and return the name that belongs to the record. But if you would enter “and” as the id, like https://site.com/?id=and, the query that would be executed would look like this:

select name from names where id = and

If you’re familiar with MySQL, you can guess what happens: this is not a valid SQL query because “and” is a reserved keyword in SQL. Let’s see if we can prove the SQL injection here. We know that if we go to https://names-api/?id=1, the name “meals” is returned. Now, if you’d go to https://names-api/?id=1+and+1=1, the page would still return “meals”. The query that is executed in the backend looks like this:

select name from names where id = 1 and 1=1

This query roughly translates to: give me the name column of the rows that have column id 1 and where 1 is equal to 1. This means the row with id 1 is returned since is 1 is always equal to 1 and thus can be ignored, which will result in the “meals” record being returned. Now if you’d go to https://names-api/?id=1+and+1=0, the following query would be executed:

select name from names where id = 1 and 1=0

You can probably guess where this is going. This query roughly translates to: give me the name column of the rows that have column id 1 and where 1 is equal to 0. Lets look at the last part: and 1=0. This will always evaluate to false, which means that there won’t be any rows returned. Period. This proves that we can change the behavior of the query. This is already a good find, but this is the anomaly. Lets dig a little deeper and see if we can extract the secret IP address from the table: welcome to the UNION! A UNION is basically an additional query that appends the results to the results of the query before. To give you a sense how it looks, here’s a SQL query with a UNION SELECT statement and the result:

select id, name from names where id = 1 union select id, name from names where id = 2

The first SQL query, select id, name from names where id = 1, resulted in the first row: meals. The second query, select id, name from names where id = 2, resulted in the second row: fransrosen. Let’s see if we can inject a UNION SELECT into the names API app: https://names-api/?id=1+union+select+name+from+names+where+id=2. Requesting this page results in the SQL query shown above this paragraph and results in 2 rows. However, since the code only fetches the first row, “meals” is returned. Let’s make a small change: instead of fetching id 1 for the first query, fetch an id that doesn’t exist in the table. Requesting https://named-api/?id=-1+union+select+name+from_names+where+id=2 will return “fransrosen” because the first part of the query won’t return any results. Now, this doesn’t seem really bad since we’re selecting other records from the table that could be accessed anyway. However, here’s the interesting part: you can use subqueries to extract secret data from the table. Consider the following URL: https://named-api/?id=-1+union+select+0,(select+ip_address+from+names+where+id=1). This will execute the following query:

select id, name from names where id = -1 union select 0,(select ip_address from names where id=1)

The subquery will return a non-persisting record with the ID column set to 0, and the name column set to the result of the subquery (select ip_address from names where id=1) — which will contain the secret IP address stored in the database. Fetching the URL results in “1.3.3.7” to be returned by the server.

There are some great resources on the internet to further explain how to exploit SQL injections. Check out this article to learn how you can write files to disk, which could lead to a remote code execution. Need a nice little cheat sheet? Check out this article. There are a lot of neat tricks that you can use to exploit a SQL injection. For example, a nice little trick to turn a SQL injection into a cross-site scripting (XSS): check out this URL. It’s up to you to discover how that piece of code executes the JavaScript alert function with a message in it. Need a hint? Read this article. Good luck!

Related to injection vulnerabilities, is this great public example of a Javascript injection found in the Slack Mac OS X protocol handler. This vulnerability allowed the attacker to execute arbitrary Javascript when the victim would click on a specially crafted link. The proof of concept showed that the attacker could post messages on behalf of the victim using Javascript. This is a great example because injections like this are very common, yet they are all over the place and waiting for people to discover them.

Next time, maybe later today, when you’re hacking, remember to be creative and try to understand the application when looking for injection vulnerabilities. Pay close attention and anticipate on how the server responds. When you think you’re on to something, always make sure to exploit the bug before reporting it to a bug bounty program. If you can’t prove the existence of a security vulnerability, come back to it later to take another look. If you’re looking for more guidance around writing good reports, check out this blog post.

Happy hacking!

Jobert

 


HackerOne is the #1 hacker-powered security platform, helping organizations find and fix critical vulnerabilities before they can be criminally exploited. As the contemporary alternative to traditional penetration testing, our bug bounty program solutions encompass vulnerability assessment, crowdsourced testing and responsible disclosure management. Discover more about our security testing solutions or Contact Us today.

The 7th Annual Hacker-Powered Security Report

Hacker-Powered Security Report