What is SQL injection?
A SQL injection flaw allows for an attacker to modify or inject SQL syntax into the request to make the application behave in a manner that was not initially intended. In other words, an attacker can change a database query to:
- Read sensitive data
- Modify the database
- Execute other database functions
- Break authentication
- Lead to remote code execution
Now with almost all web applications having integrations with databases in some way, this flaw has the potential to arise often. However, many frameworks and libraries are available to make database connections and queries safe. That said, SQL injection still exists and is very common. Injection flaws were the number one flaw category under the OWASP 2017, and, currently, injection flaws hold the number three spot in the OWASP 2021.
SQL injection flaws have impacted every industry as well as enterprises that already have a mature information security program in place. It can happen, and it can be catastrophic!
How do we detect it?
The detection of SQL injection flaws has changed over the years. Applications and servers are getting smarter. A simple single quote (‘) could be added onto the end of a parameter to simply detect a server error. Though this can still happen, detection has become a bit more complicated to get around library protections. Our detections typically fall under three categories:
- Sleep or timing attacks
- SQL errors in the response
- Comparing multiple requests to see how the server evaluates SQL syntax
In the sense of CWE-89, Improper Neutralization of Special Elements used in an SQL command ('SQL injection'), sleep attacks work well as they are typically safe to run in a production environment, as they only slow down the single request that was sent for a specified duration of time. They also are more reliable for detection, as it should be easy to replicate and have a fair amount of confidence based on the time it takes for the requests to complete.
Another detection is to evaluate any type of server errors. Web servers these days are typically configured to not give up data or server information when a server error occurs … But it still happens.
Additionally, multiple requests may be sent to the same endpoint or URL, with various injection strings. The scan will collect all this data and then compare how the server processed the requests. Meaning, comparing the responses from a valid request to the response with the injected payloads. These injection strings or ‘attacks’ are designed to trigger the flaw in the most generic way to trigger across all types of applications and frameworks. This is really where the automation piece of security testing really excels.
How do you validate findings?
In the Veracode Platform, the Analysis Center can be used to gain more insight into the attacks that were sent. Additionally, the Analysis Center makes it very simple to validate any potential flaws the scan discovered.
Looking at the petstore-api-flask vulnerable API, a SQL injection exists in the login function. We can see it identified in the Flaw Viewer.
The attack was performed in the ‘username’ value with the injected string. This payload is designed to make the server ‘sleep’ or pause for 15 seconds. Meaning, if this is a valid SQL injection, the server will briefly pause for 15 seconds before completing the request. Interestingly, the response from the server is 401 UNAUTHORIZED. From just looking at the response code, it looks like a failed login. But the timing attack was successful and slowed the response 15 seconds in multiple requests. Let’s dig deeper to see what happened.
If we open the flaw JSON file, we can see more details around how the flaw was detected without sending any additional requests. Specifically, we can see the response times of the timing attack that was performed. We can also see the payloads that triggered the flaw. We can replicate it if required.
The fast payload should sleep for no less than one second, the slow payload should sleep for no less than 15 seconds. The values of the response times in milliseconds were at least 15 seconds on the slow payload and at least one second on the fast payload. This validates the detected SQL injection flaw. Timing attacks are very effective in detecting SQL injections. That being said, timing attacks are not always this straightforward. With today’s architectures, we now have multi-region/multi-cloud, web application firewalls (WAF), as well as load balancers and security appliances all between the client and the server. So, the timing of requests can vary depending on the application and these factors. But when validating a flaw, a “slow” payload should consistently be at least 15 seconds slower than a “fast” payload response time.
Let’s look at another example. The same API, but a different endpoint. Another detection type is to compare SQL syntax with a logic comparison. Various payloads are injected to see if the server processes the syntax differently. If the same response is received each time with no change, then the attack was unsuccessful.
However, if the server responds differently, then we know we need to look further. The premise we will use is how a database evaluates a string. MySQL evaluates 1=1 as ‘1’ or as TRUE statement, it evaluates 1=2 as ‘0’ or FALSE as 1 does not equal 2.
The injection string is sent to see how the server evaluates the query. Using the Flaw Viewer we can see the request that triggered the flaw. In the “Injected Value” field we can see one of the requests that were sent. Navigating to the “Technical Details” tab and we can see the multiple requests as well as how the server responded and evaluated the queries. Additionally, we could optionally download the flaw details in JSON (as was done previously) and view the same data locally.
We can see the server responded differently depending on how the payload was evaluated. If we test this with MySQL directly, we can see how it will evaluate each of these injection strings.
The first query evaluates to a ‘1’ or TRUE. When this string was injected, the server evaluated it as TRUE as well and returned a 200 OK. The next injected string evaluated was ‘0’ or FALSE, and the server returned a 400 NOT FOUND. We see the same thing with the next set of injected strings. Meaning we can inject SQL syntax and the server is evaluating and executing the SQL commands we are sending.
Like timing attacks, this can also be an effective way to detect flaws. However, servers do not always respond identically, so they can also be prone to false positives. But it’s simple to validate, as shown.
SQL injection is STILL here. It can STILL cause enormous problems, and we STILL need to be testing and scanning regularly to prevent them. Veracode’s SAST and DAST scanning solutions can help discover these flaws and also assist in remediation guidance.
How to prevent SQL injection?
Please see the following documents: