Intro to Injection Vulnerabilities and the OWASP Top 10
by Kristian, on 09/04/2019 7:15:00 AM
Injection vulnerabilities are the most common result of mixing user input with system control. An injection vulnerability can have catastrophic results for a system, potentially leading to a full database dump, and laying the groundwork for a remote shell. In layman's terms, this means an attacker controls the entire system and has access to all data.
The most common form of injection vulnerability is SQL injection. SQL is a language used for communicating with a database. Usually this involves a back end of a web application to pull user information such as usernames, passwords, posts, and comments out of a database. A SQL injection involves an attacker "injecting" their own code into the application's already existing SQL statements.
The best example of SQL injection is for a login page. If an attacker compromises a login function, it means the attacker has access to any account on that system.
Suppose we have a SQL statement invoked in a python flask application that looks like this:
"SELECT * FROM users WHERE username='" + request.POST['username'] + "' AND password='" + hashlib.md5(request.POST['password']) + "'"
Note: Your passwords should NEVER be sent in plain text. They also shouldn't use MD5 but for the sake of clarity I'll use it here. Best practice is to use a salted hash like SHA256 or SHA512.
Given the username of "attacker" and password of "password", this request looks like:
"SELECT * FROM users WHERE username='attacker' AND password='5f4dcc3b5aa765d61d8327deb882cf99'"
This would result in the attacker's details being returned by the application. If the password was incorrect, nothing would be returned an the application would give back an error such as "Username or password incorrect."
But what if the attacker supplies a SQL statement as their username? If an attacker supplies a username of "admin'; --", the request looks like this:
"SELECT * FROM users WHERE username='admin'; -- AND password='5f4dcc3b5aa765d61d8327deb882cf99'"
Here, the semicolon indicates the end of the SQL statement and the double dash means the rest of the line is a comment and not evaluated. This means the SQL statement can be simplified to:
"SELECT * FROM users WHERE username='admin'"
Which returns the user data from the admin account. This is bad as it bypasses any authentication method and once into the admin account, there's nothing else stopping the attacker from completely changing a system.
If an attacker can inject any SQL code they desire, it could result in a shell on the system. A more complicated example would be to use SQL's built in functions "LOAD_FILE" and "INTO OUTFILE" to read and write to files respectively. A well-crafted payload can be found here (https://resources.infosecinstitute.com/anatomy-of-an-attack-gaining-reverse-shell-from-sql-injection/#gref) which allows an attacker to execute commands on a system by viewing a web page, giving an attacker control over at least the web server, or more if the system is poorly configured.
Other Versions of Injection
SQL is the most common, but not the only form of injection vulnerability. Any protocol that mixes user input and system control could be vulnerable to injection. The list included in the 2017 OWASP Top 10 vulnerabilities is SQL, NoSQL, OS command, Object Relational Mapping (ORM), LDAP, and Expression Language (EL) or Object Graph Navigation Library (OGNL) injection.
Mitigating injection vulnerabilities is as simple as separating user input from direct control statements. By having an intermediary step between getting user input and sending the input to the system, a back end of an application can escape or remove control characters. Most languages have a function that escapes special characters automatically. Along the same lines, most development frameworks mitigate SQL injection automatically. These include WordPress, AngularJS, and python Flask.
Good security practices also help reduce the risk if someone does exploit an injection vulnerability in your application. For example, a login form only needs read access to a database. If an attacker finds SQL injection in your login function, they should not be able to write to the database or host system. This can be set up by running get requests on the database through a read only account.
Injection vulnerabilities pose a serious risk to publicly facing applications. It allows an attacker to leak potentially all sensitive information from the connected database of an application, and can lead to remote code execution, and complete system control by an attacker. However, by escaping characters, using modern frameworks, and good security practices such as lowest required privilege, the risks involved can be kept to a minimum or removed completely. It's important to test applications before going live and constantly keep systems up to date to remove any avenue for an attacker.