The first item on the OWASP Top 10 list are injection attacks. There is a good reason this is number 1: injection attacks are the granddaddy of online hacks. They’ve been used consistently since the early days of the Internet and have been a constant presence since the world began moving to the internet. And while many people know what they are, many apps are still vulnerable to some sort of injection attacks.
Which brings us to our next point: injection attacks have been consistently evolving at twice the pace that programming languages have evolved. As developers and companies began to use new languages over the past twenty years — Python, Ruby on Rails, the SQL variants — each has faced a plethora of vulnerabilities that could be exploited with an injection attack.
As such, we are going to narrow our focus here a bit and focus on the attacks that can affect web apps that store their data in a SQL database. These attacks include SQL and NoSQL injections as well as the use of other languages to exploit vulnerabilities in SQL/NoSQL databases.
What Are Injection Attacks?
As we will be doing across this series, let’s start with the OWASP definition:
Injection flaws, such as SQL, NoSQL, OS, and LDAP injection occur when untrusted data is sent to an interpreter as part of a command or query. The attacker’s hostile data can trick the interpreter into executing unintended commands or accessing data without proper authorization.
OWASP, “OWASP Top 10“
Let’s walk through a very barebones case of SQL injection. Generally, you will be using a SQL database as a way to store sensitive data in conjunction with an access agent. Therefore, when someone signs up for your website and inserts personal and sensitive information (payment info, contact info, etc.) that will be stored in your database. You set up your web app to have both signup fields and login fields, all of which will interact in some way with the database.
Now if you don’t ensure that the database isn’t coded in a secure manner, an attacker can insert a code string into, for example, the login field. This would send a command to the database to perform an action the attacker wants but that you, of course, do not want.
Start from the fact that injection attacks target and seek to manipulate variables in your database code. Variables are in charge of ensuring that data is grouped in the manner you want it organized into.
Changing variables for an injection attack can be as simple as changing the URL if the URL is not properly encoded. For example if you start with
http://my_sample_web_app.com/script.php?info_variable=X
an attacker can change the variable value (after the = ). They can add values that have worked for other attackers before or they can just enter random information and essentially guess a value that will force the app to allow entrance to the database or force the database to dump information.
However, the more egregious attack style attempts to directly exploit the backend database by inputting malicious code into the login fields, the browser’s console, or elsewhere on the app.
The most common example of this is the use of the addition of a variable to create a SELECT statement, which does precisely what it says: it selects data from the database. So, for example, this code can be entered:
txtUserId = getRequestString("UserId");
textSQL = "SELECT * FROM Users WHERE UserId = " + txtUserId;
And those are just the simple attacks. In the next section, you will see other forms of injection attacks as you learn how to utilize secure coding to prevent such attacks.
Preventing Injection Attacks: Secure Development
The biggest thing you can do to prevent injection attacks if you are running a web application is to make sure that you use secure development practices.
In other words, avoid common mistakes that allow injection attacks to occur. You can do so by implementing some of these practices in your development process:
1. Parameterized Statements: You may also see this one listed as “parameterized queries” and should be a practice that a developer working with SQL should learn up front. SQL has two types of queries: static/parameterized/binding and dynamic queries. The former are easier to write and MUCH safer.
Parameterized statements use prepared statements with variable binding. Using these statements requires the developer to first define the SQL code itself before passing each parameter to the query later. What this does is allow the database to have a rule set to separate code and data. Therefore, when a user inputs a malicious string in an attempt to get the database to dump the data, the database will be able to differentiate between the data and code. If the code doesn’t match the demands required by the defined database code, the database will not release the data.
Note that there are specific parameters that should be used depending on the language used to interact with the SQL database. OWASP gives some suggestions here:
– Java EE: use PreparedStatement() with bind variables
– .NET: use paramaterized queries like SqlCommand() or OleDbCommand() with bind variables
– PHP: use PDO with strongly typed paramaterized queries (using bindParam())
– Hibernate: use createQuery() with bind variables (called named parameters in Hibernate)
– SQLite: use sqlite3_prepare() to create a statement object
OWASP, “SQL Injection Prevention“
You can click the link for safe code snippets, but also look at Hacksplaining‘s article for protecting against sql injections for safe and unsafe code samples.
2. Object Relational Mapping (ORM): Use of an ORM framework is a popular one amongst developers and for good reason: it allows the translation of SQL results sets to efficiently map onto code objects. This efficiency means that developers can write a majority of their code without having to actually write SQL statements: the framework uses parameterized statements on the backend.
The most commonly used ORM is Ruby on Rails’ Active Record framework. The number one thing to note with ORM frameworks like Active Record is that, contrary to what may be thought at first glance, your code could still be vulnerable to injection attacks even if you are using ORM frameworks.
Luckily there’s a pretty straightforward rule when you find yourself using Active Record or another ORM framework but need to incorporate some hard SQL coding into your app: try your damndest to figure out a way to avoid using concatenating strings.
Therefore, this Active Record framework is safe:
def current_user(email)
# The 'User' object is an Active Record object.
# 'User' object has find methods
# auto-magically generated by Rails
User.find_by_email(email)
end
Thanks again to Hacksplaining for the above code and the following, unsafe code:
def current_user(email)
# Using concatenation leaves this code vulnerable to
# maliciously crafted email parameters
User.where("email = '" + email + "'")
end
You can find more defenses to injection attacks at the following sites:
- OWASP’s SQL Injection Page
- Hacksplaining’s “Protecting Against SQL Injection” page
- “How to Protect Against SQL Injection Attacks” from Cal Berkeley’s Information Security Office (Note: check out the resources page for a fantastic series on all sorts of infosec topics)
Injection Identification Techniques
Okay, so we’ve gotten through what an injection attack is, focusing on SQL injections, and showed how we can help prevent injection attacks.
Let’s say, though, that some of the resources you have read from around the web suggest that your app is vulnerable to an injection attack. You fix the holes but are now unsure whether an attacker has targeted your app with an injection attack. How do you check? More importantly, how do you keep checking in order to discover an injection attack occurring in real time?
The easiest way — besides using an IDS or another tool — is to monitor your event logs. In the future, we will go into more depth on investigating error logs in a forensic investigation in the aftermath of an injection attack, but for now keep these server error logs in mind:
- Syntax errors: If you see errors 102 or 105, both of which indicate syntax errors, you may be seeing a clue that an injection attack either occurred or in currently ongoing. Pay particular attention to where the syntax errors are coming from: test or development servers? Probably just some mistakes made by developers so no big deal. Production databases? You’re most likely looking at an injection attack.
- Conversion errors: In SQL development, if you accidentally (or intentionally) try to convert a varchar value to an int data type, and that is not possible for the varchar value, you’ll get an error message 245. When you have your code securely developed and see this occurring without any prompting from your development team, you’re likely seeing an injection attack. This is because attackers will often use the conversion technique to get values from the database.
- Invalid Object or Stored Procedure Error: Note in particular error codes 208 and 2812. These errors will show when there has been an attempt to access an invalid object or a stored procedure that can’t be found. You should know your own database and its code: multiple attempts to find something that isn’t there is extremely indicative of an attacker attempting to find objects and/or procedures by basically guessing that they’ll be there.
- Failed logins: We don’t really need to go too in-depth on this one: if you see an error 18456, or a failed-login error, and you see tens or hundreds of these errors, an attacker is attempting to brute-force your database.
The major issue with the above errors is that more-often-than-not the SQL server will see an error such as those above and not log it, due either to the rules it has imposed on it or because the error is just too minor to report. As such you will likely need to use extended events. This brings us to the next two important items to consider, which are related because they are both from Microsoft.
First, you are going to want to use SSMS to set up the extended events event session. SSMS allows you to set up an extended event session (through New Session/Properties) through a wizard, select and configure the session to capture the events you want, and specify the target for the scope. It then creates the code and starts the session. Once it’s going, though, note that you can change the parameters if required. Check out the Microsoft doc for more information on using this.
The second item you will want to consider is using Microsoft’s SQL Server Audit, but this should be used more with a focus on forensics, i.e., after an injection attack occurred, or on a regular basis to get a feel on the overall health of your system. Again, check out the Microsoft doc on how to implement it.
More Resources
This has been a brief post for a supposedly “brief” introduction. However, now you get the idea of how expansive the resources are that are available for you to learn more about injection attacks, preventing against them, and monitoring your system in order to detect ongoing attacks.
Besides the resources cited throughout this article, here are a few more worthwhile reads concerning injection attacks, prevention, and detection.
- This article from the venerable Phil Factor on detecting SQL injection attacks on redgate.
- This excellent article from Tevora on how to secure your applications against injection attacks.
- This acunetix article that provides a great overview of the different injection attacks.
Remember to check out the THM Injection walkthrough!
