Injections occupy third place on the podium of the most relevant web vulnerabilities, according to the OWASP (Open Web Application Security Project) ranking, in its latest ranking published in 2021.
An injection is an attack that occurs when a malicious agent manages to pass non-validated information to an interpreter as part of a request. The receiver, thinking that the data is legitimate, executes it, or allows access to privileged elements. The most common injection attacks are in SQL, NoSQL, operating system commands and LDAP.
In this article you will find tools to detect the existence of injections in the code, and recommendations to prevent them, thus mitigating the possible consequences.
An application can be considered vulnerable to such an attack in the following situations:
- The data provided by the user are not validated, filtered, or sanitized by the system.
- Dynamic or non-parameterized queries are invoked without coding the parameters appropriately according to the context.
- Malicious data is used within search parameters in Object-Relational Mapping (ORM) queries to extract additional sensitive records.
- Malicious data is used directly, or concatenated. So that the resulting SQL statement or command contains information, and structures with dynamic queries or altered procedures.
Recommended actions to prevent this problem are as follows:
- Employ a secure API, which avoids the use of an interpreter altogether or provides a parameterized interface.
- Implement data entry validations on the server, using positive or “whitelist” input.
- Make use of LIMIT and other SQL controls on the number and type of queries, to avoid massive record disclosure in case of SQL injection.
- Use specific escape syntax, to avoid using special characters to validate a query.
- Filter by application semantics, for example:
- Barcelona as a valid city name.
- <b>highlighted</b> may be a valid comment if the application allows a rich text field, but not if it does not.
The following is an example of a scenario where an injection attack would be possible.
A case of injection could occur when receiving data from the user and using them to make statements on a database, without them going through any kind of validation process.
The code in the image corresponds to a fragment of a vulnerable Java function. It uses parameters extracted directly from the query, without any sanitization procedure, to perform an SQL query where the user with name and password equal to the receipts is obtained. This would allow an attacker to send malicious SQL statements, such as ‘username=admin;- –‘. The program, upon receiving this value for the username, gets the linked entry from the database regardless of valid credentials, and returns true as the result. This happens because since the element is not escaped, it can become part of the statement, instead of just being the attribute of a field. Then what is finally executed is “SELECT * FROM users WHERE user_id=‘admin’;– – -” so that the part “AND password=‘” + password ‘+ “’”; is ignored because it is now commented out. The attacker, knowing the administrator’s username, and assuming that this function is the one used to manage the login, would gain privileged access to the system.
To solve the problem in this particular situation, we can use prepared statement to make the request, since this function separates what the user has sent from the query, thus providing a validation of the data. By receiving the same value as in the previous case, “userrname=admin;- –”, this new implementation would not suffer the same problem. The code in this case would escape the parameter, so that “admin;– – -” would not be understood as a part of the statement, but only as the value of the user_id field.
In any other case, the procedure would be the same, we would have to find a way to isolate what we receive from the front-end from the connections to the database. To do this we can either use an existing procedure, as in the example, or perform the necessary checks on our own, carefully, so as not to leave any case that could compromise the system.
A more visual example of the consequences of using insecure code such as the above in an application is attached. For this demonstration, we used the OWASP Juice Shop, an insecure application created to practice exploiting vulnerabilities in a secure environment.
Although holes of all kinds can be found, the example will focus on a login panel vulnerable to injections. The goal is to log in as an administrator user without knowing the credentials.
The first task to perform is to discover a privileged email address. By inspecting the products on the home page, you can see user reviews, where the email address is displayed as shown in the image above. Since admin@juice-sh.op is almost certainly the address of the admin user, we will try to log in to the panel using this new information, but still without knowing the password.
The technique described in the previous example has been used: in the email field, the email to which you want to access admin@juice-sh.op is placed, in this case, followed by the SQL OR condition 1=1- –. The server, on receiving these values from the client and not performing any validation, will understand the fields as part of the SQL statement performed on the database. In this way, the user who has this address is searched (ending the field contents with the ‘ ) and the OR 1=1 clause is executed, which evaluates to true. Finally, “– -” is included to comment out the rest of the query, because if it were executed, the password check would return false. In conclusion, what would be executed on the server would be very similar to: SELECT * FROM Users WHERE email = ‘mail sent’ OR 1=1 – – (AND password = ‘password sent’; commented out, not executed). It will always return true if there is any user with the submitted address, regardless of the password attached to the form, gaining access to the account without knowing the credentials.
As you can see in the image, the previous values have allowed us to gain access to the administrator account, now we are logged in as the admin user.
One might think that protecting the client with some kind of script that denies inserting malicious statements would be enough to guarantee the security of the system. The reality is that checks at the browser level are easily avoidable, and are completely useless if appropriate measures are not taken at the server side, such as those described in the first example where the code is shown.
An attacker could evade control on the client side by intercepting the requests with a proxy, and modifying the fields with the desired values outside the browser.
As an example, it is assumed that the panel locks the values, and these are modified by the previous ones intercepting the data with BURPSUITE.
Now the values “SANTI”, “SANTI” have been entered in the login as you can see in the first image. When sending the data, the request has been intercepted with the proxy and, as can be seen in the second image, the email and password fields are equivalent to those of the form. The next step will be to change the values to perform the same malicious statement as in an unprotected panel.
This image shows the malicious value for the email field. As the server does not make any checks, the result will be the same: gaining access to the administrator account, regardless of the new measures to the browser.
In conclusion, injection attacks occur when relying on data sent by any user with access to the application. Therefore, it is important to protect yourself by performing checks on everything you receive. Always bearing in mind that, although client-side protections may be useful for the legitimate user experience, security measures must always be taken on the server side, where the information is actually processed.