Server-side web attacks

Web applications offer an incredibly wide attack surface, ranging from attacks directly targeting the server-side code or databases, to attacks running in the browser. We consider PHP, one of the most prominent programming languages for web application, and we illustrate two typical vulnerabilities: PHP string comparison attacks and SQL injections.

PHP string comparison attacks

PHP offers two kind of comparison operators: strict comparison === and loose comparison ==. The following tables illustrate the difference:

Strict comparison, from hydrasky.com

Loose comparison, from hydrasky.com

We notice that strict comparison equates only identical values while loose comparison also equates data of different types. For example, when strings and integers are compared, the former are converted into integer values: string “php” is equal to integer 0 (in purple), since it does not contain any digit at the beginning of the string.

Loose comparison is excessively liberal and it often introduces unpredictable behaviours that might undermine security. We illustrate some examples and, later on, we discuss how these could cause security flaws.

  1. When a string is compared with an integer the string is converted into integer. For example all of the following comparisons succeed:

  2. When two strings are compared, if they “look like” integers PHP convert them. All of the following comparisons succeed:

    Notice, in particular, that completely different strings become equal when compared through ==.

Authentication bypass

We show how the loose comparison operation might allow an attacker to bypass authentication checks. We assume that the server has a token (variable $token) used to keep a user authenticated in a web session. The token must be provided by the user (variable $input) and is checked server side. For example, the token might be stored in a browser cookie and sent to the server at each request.

Consider the following code:

Since the token is a number in exponential notation when $input and $token are compared the token is converted into 0 and the comparison succeeds. Thus, the attacker can bypass authentication by simply providing input 0 instead of the correct token. Even if this case looks a bit artificial, this vulnerability has been exploited to bypass Worpress authentication in 2014. The idea was to brute-force the system until the token had the required form (exponential notation).

The following code offers a more efficient exploitation:

In this case, the input is in JSON and the token value is extracted from the JSON blob. This offers the possibility of passing an integer value (0 in the example). This forces the conversion of the token into an integer, which will be equal to 0 if it starts with 0 or with a non-numeric char (i.e., with high probability!).

Finally, the following code shows an unexpected behaviour that is source of a very frequent vulnerability in PHP applications:

In this example it is used strcmp to compare the token and the input. The attacker passes an empty array as input (this is possible by passing token[]= as parameter or cookie). strcmp fails and returns NULL but NULL is loosely equal to 0, which makes the comparison succeed. This behaviour is really subtle and, at the same time, offers an very dangerous and trivial exploitation.

SQL Injections

We study a powerful code injection technique that exploits security vulnerabilities of a website. SQL statements are injected in the input field of the web application with the aim of executing improper queries in the database.

A simple vulnerable site

This is an example of vulnerable website (use haxor/sqleet to login). We report a snippet of the underlying code:

The lastname POST variable is not filtered (see line 22 above), so we can set its value to any string of our choice:

For instance, to obtain the list of all users it is enough to inject one of the following values:

which will produce, in turn, the following queries:

In all the examples we start our injection by closing the quote '. This will compare lastname with the empty string giving false. To make the result true we just add OR with some always true statement such as 1=1, 1, ''=''. In the first three cases we need to comment out the last quote using -- (and a white space after the two dashes) or # to avoid an error because of the unbalanced quotes. In the last case, instead, we use the quote in the query to close the last quote in ''=', so that we don’t need to comment it out.

A copy of the database used on the vulnerable website is locally available on the testbed machine. After logging in with your account, you can start experimenting with MySQL in the following way:

Now you can try injecting code in the query by simply cut-and-pasting the malicious code between the two quotes. For example:

You see that the dashes and the space comment out the quote and also the semi-column (which terminates command line SQL). This is why we have to insert an extra semi-column in the next line.

Basic injections

How can we show more interesting data (like passwords) using injections?

UNION ALL can be used to combine two (or more) results:

NOTE: The number of columns in the SELECT statements must be equal. If the numer of columns in the first SELECT statement is unknown, we can guess it by adding some 1 until the query is successfully executed:

EXERCISE: Try to print the list of user passwords on the vulnerable website

What if there is just one column in the first SELECT but we want to dump several columns in one shot? CONCAT is our friend:

Sometimes the page code does not iterate over the result set, but prints only the first row. We need a way to aggregate several rows into one single row. We can use GROUP_CONCAT for this:

Notice the use of SEPARATOR to indicate how to separate the different rows in the GROUP_CONCAT.

Retrieving database structure

Until now we assumed to know the name of the columns/tables/databases we were interested to dump. In reality, tables have fancy prefixes and cannot be easily guessed, hence we need a reliable way to extract the database structure.

In several DBMS (MySQL, Postgres) there’s a database called INFORMATION_SCHEMA that stores all the information of all the databases. We can dump known fields to get the whole db structure.

List databases:

List tables:

List the columns of all relevant databases:

EXERCISE: Try to print the databases, the tables and the columns of relevant databases on the vulnerable website

Advanced techniques

Reading files

It works only if the db user has the FILE privilege and the accessed file is readable by the mysql user

(this example will not work on testbed since the FILE privilege is not granted to the user sqli_example)

Creating files

It works only if the db user has the FILE privilege and the mysql user is allowed to write files in that directory. This is very dangerous as an attacker may create PHP files to obtain a shell on the server as follows:

Now it is enough to execute:

Notice that the command id is executed on the server!

Misc

Is it possible to concatenate multiple queries by adding a ‘;’ (semicolon)?
Most SQL server implementations allow multiple statements to be executed in the same call. On the other hand, some APIs such as PHP mysql_query() function do not allow this.
For example:

However this does not work on the vulnerable website since it uses mysql_query().

Training Challenges

There exists many free challenges to experiment with server-side security (especially with SQLi) in a legal and safe way. We recommend the following websites if you want to broaden your exploitation skills:

Leave a Reply

Your email address will not be published. Required fields are marked *