Introduction to pentesting: Web-Based Exploitation

Code Injection Attacks

Like buffer overflows in system code, injection attacks have been a serious issue in the web world for many years, and like buffer overflows, there are many kinds of code injection attacks. Broadly defined, this class of attacks could easily fill a chapter. However, because we are focusing on the basics, we’ll examine the most basic type of code injection: the classic SQL injection. We’ll explore the basic commands needed to run an SQL injection and how it can be used to bypass basic web application authentication. Injection attacks can be used for a variety of purposed, including bypassing authentication, manipulating data, viewing sensitive data and even executing commands on the remote host.

Most modern web applications rely on the use of interpreted programming languages and back-end databases to store information and generate dynamically driven content to the user. There are many popular interpreted programming languages in use today including PHP, JavaScript, ASP, Structured Query Language (SQL), Python and countless others. An interpreted language differs from a compiled language because the interpreted language generates machine code just before it is executed. Compiled programming language require the programmer to compile the source code and generate an executable file. In this case, once the program is compiled, the source code can’t be changed unless it’s recompiled and the new executable is redistributed.

In the case of modern web applications, like an e-commerce site, the interpreted language works by building a series of executable statements that utilize both the original programmer’s work and input from the user. Consider an on-line shopper who wants to purchase more RAM for his computer. The user navigates to his favourite on-line retailer and enters the term “16 GB RAM” in the search box. After the user clicks the search button, the web app gathers the user’s input (“16 GB RAM”) and constructs a query to search the back-end database for any rows in the product table containing “16 GB RAM”. Any products that contain the keywords “16 GB RAM” are collected from the database and returned to the user’s browser.

Understanding what an interpreted language is and how it works, is the key to understanding injection attacks. Knowing that user input will often be used to build code that is executed on the target system, injection attacks focus on submitting, sending and manipulating user-driven input. The goal of sending manipulated input or queries to a target is to get the target to execute unintended commands or return unintended information back to the attacker.

The classic example of an injection attack is SQL injection. SQL is a programming language that is used to interact with and manipulate data in a database. Using SQL a user can read, write, modify and delete data stored in the database tables. Recall from our example above that the user supplied a search string “16 GB RAM” to the web application (an e-commerce website). In this case, the web application generated an SQL statement based off of the user input.

It’s important that you understand there are many flavours of SQL and different vendors may use different verbs to perform the same actions. Specific statements that work in Oracle may not work in MySQL or MSSQL. The information contained below will provide a basic and generic framework for interacting with most applications that use SQL, but you should strive to learn the specific elements for your target.

Consider another example. Assume that our network admin Ben Owned is searching for a Christmas present for his boss. Wanting to make up for many of his past mistakes, Ben decides to browse his favourite on-line retailer to search for a new laptop. To search the site for laptops, Ben enters the keywords “laptops” (without the quotes) into a search box. This causes the web application to build an SQL query looking for any rows in the product table that include the word “laptop”. SQL queries are among the most common actions performed by web applications as they are used to searching tables and return matching results. The following is an example of a simple SQL query:

SELECT * FROM product WHERE category='laptop';

In the statement above, the “SELECT” verb is used to tell SQL that you wish to search and return results from a table. The “*” is used as a wildcard and instruct SQL to return every column from the table when a match is found. The “FROM” keyword is used to tell SQL which table to search. The “FROM” verb is followed immediately by the name of the table (“product” in this example). Finally, the “WHERE” clause is used to set up a test condition. The test condition is used to restrict or specify which rows are to be returned to the user. In this case, the “SELECT” statement will return all the rows from the product table that contain the word “laptop” in the “category” column.

It’s important to remember that in real life, most SQL statements you’ll encounter are much more complex than this example. Oftentimes, an SQL query will interact with several columns from several tables in the same query. However, armed with this basic SQL knowledge, let’s examine this statement a little closer. We should be able to clearly see that in our example the user created the value to the right of the “=” sign, whereas the original programmer created everything to the left of the “=” sign. We can combine this knowledge with a bit of SQL syntax to produce some unexpected results. The programmer built an SQL statement that was already fully constructed except for the string value to be used in the WHERE clause. The application accepts whatever the user types into the “search” textbox and appends that string value to the end of the already created SQL statement. Lastly, a final single quote is appended onto the SQL statement to the balance of the quotes. It looks like this when it is all done:

SELECT * FROM product WHERE category='laptop';

Where “SELECT * FROM product WHERE category=’” is created ahead of time by the programmer, while the word “laptop” is user-supplied and the final “’” is appended by the application to balance quotes.

Also notice that when the actual SQL statement was built, it included single quotes around the word “laptop”. SQL adds these because “category” is a string data-type in the database. They must always be balanced, that is there must be an even number of quotes in the statement, so an SQL syntax error doesn’t occur. Failure to have both opening and closing quote will cause the SQL statement to error and fail.

Suppose that rather than simply entering the keyword laptop, Ben entered the following into the search box:

laptop' or 1 = 1--

In this case, the following SQL statement would be built and executed:

SELECT * FROM product WHERE category='laptop' or 1=1--'

The “or” statement above is an SQL condition that is used to return records when either the statement is true. The “–” is simply ignored by the interpreter. The final single quote is still appended by the application, but it’s ignored. This is a very handy trick for bypassing additional code that could interfere with your injection. In this case, the new SQL statement is saying “return all the records from the product table where the category is ‘laptop’ or 1 = 1”. It should be obvious that 1 = 1 is always true. Because this is a true statement, SQL will return ALL the records in the product table!.

The key to understand how to use SQL injections is to understand the subtleties in how the statements are constructed.

On the whole, the example above may not seem too exciting; instead of returning all the rows containing the keyword laptop, we were able to return the whole table. However, if we apply this type of attack to a sightly different example, you may find the results a bit more sensational.

Many web applications use SQL to perform authentication. You gain access to restricted or confidential locations and material by entering a user-name and password. As in the previous example, oftentimes this information is constructed by a combination of user-supplied input, the user-name, the password and programmer-constructed statements.

Consider the following example: The network admin Ben Owned has created a new website that is used to distribute confidential documents to the company’s key strategic partners. Partners are given an unique user-name and password to log into the website and download material. After setting up his secure website, Ben asks you to perform a penetration test against the site to see if you can bypass his authentication.

You should start this task by using the same technique we examined to return all the data in the “products” table. Remember the “–” is a common way of commenting out any code following the “–”. As a result, in some instances it’s possible to simply enter a user-name followed by the “–” sequence. If interpreted correctly, this can cause the SQL statement to simply bypass or ignore the section of code that checks for a password and give you access to the specified user. However, this technique will only work if you already know a user-name.

If you don’t know the user-name, you should begin by entering the following into the user-name text-box:

' OR 1=1--

Leaving the username parameter blank and using an expression that will always evaluate to true is a key way to attack a system when we are unsure of the usernames required to log into a database. Not entering a username will cause most databases to simply grab the first user in the database. In many instances, the first user account in a database is an administrative account. You can enter whatever you want for a password (for example, “ilikebread”), as it won’t even get checked by the database because it is commented out. You don’t need to supply a password to bypass client-side authentication (or you can use your intercepting proxy to delete this parameter altogether).

SELECT * FROM users WHERE uname='' OR 1=1-- and pwd='ilikebread'

At this point, you should either have a username or be prepared to access the database with the first user listed in the database. If you have a username, we need to attack the password field; here again we can enter the statement:

' OR 1 = 1--

Because we are using an “or” statement, regardless of what is entered before the first single quote, the statement will always evaluate to true. Upon examining this statement, the interpreter will see that the password is true and will grant access to the specified user. If the username parameter is left blank, but the rest of the statement is executed, you’ll be given access to the first user listed in the database.

In this instance, assuming we’ve a username, the new SQL statement would look similar to the following:

SELECT * FROM users WHERE uname='admin' AND pwd='' OR 1=1--

In many instances, the simply injection above will grant you full access to the database as the first user listen in the “users” table.

In all fairness, it should be pointed out that it is becoming more uncommon to find SQL injection errors and bypass authentication using the techniques listed above. Injection attacks are now much more difficult to locate. However, this classic example still rears its head on occasion, specially with custom-built apps, and it also serves as an excellent starting point for learning about and discovering the most advanced injection attacks.