Tuesday, October 27, 2015

Security Month - Episode 1: Injection

Starting With A Bang


Injection attacks, in all their various flavors, are now the number one most exploited security flaw in software. Their impact can range from something as innocuous as displaying extra text on a web page to the full compromise of your host machine.

Setting A Bad Example


Do you see anything wrong with the following snippet of PHP code?
(Note: this is a simplified example for demonstration purposes only)

// Initializes necessary variables, functions, etc.
require_once( "important.libs.php" );


$accessLevel = GetAccessLevel();

<snip>

// Defaults to empty string if not present
$name = Posted( "name" );

// Find contacts with matching first name in database
$sql = "SELECT * FROM Contacts WHERE ".

       "FirstName = '$name' AND ".
       "AccessLevel <= $accessLevel";
$result = $db->query( $sql );
// We should really do some error checking here

// Print details for each match found
while( $row = $result->fetch_assoc() ) {

    $firstName = $row[ "FirstName" ];
    $lastName = $row[ "LastName" ];
    $phone = $row[ "Phone" ];

    print "Name: $firstName $lastName, Phone: $phone<br />";

}

<snip> 


As you might have guessed from the title of this article, this code is vulnerable to injection. In fact, it is subject to two common forms of injection: SQL injection, and cross-site scripting (XSS).

What Is Injection?


Injection happens every time we receive input from an untrusted source and submit it to an interpreter without first sanitizing the input. In the case of SQL injection, that interpreter is the database interpreting the text of our SQL query. In the case of cross-site scripting, the interpreter is the browser attempting to display our HTML page. Whatever the scenario, allowing potentially dangerous user input to be interpreted as a trusted command or request can have disastrous .consequences.

What Could Go Wrong?


In the scenario above, imagine that a hacker wants to get a list of all contacts in the database. They are currently restricted only to those contacts whose AccessLevel value is lower than a configured value. How could they use SQL Injection to gain access to everything?

What if, in the form that requests this page, they entered "' OR 1 --" as the name to search? What would our query look like now? (Assuming AccessLevel = 2)

SELECT * FROM Contacts WHERE FirstName = '' OR 1 --' AND AccessLevel <= 2

With a new terminating quote and comment, suddenly our query is changed to be much more permissive. The attacker has tricked our script into revealing more information than intended. What if instead of a SELECT, we were working with a DELETE query? The entire table full of valuable data might be lost.

The second vulnerability is to a cross-site scripting attack. What if, instead of entering their name, an attacker entered some raw HTML such as "<script>alert('XSS!')</script>"? Suddenly, they've inserted their own elements and even javascript code into your web page. If they can get this to happen on a page displayed to another user, the attacker can hijack your page to show you them anything they want and it will look like it's coming directly from your page.

It Could Be Worse


Imagine a different scenario where input is passed to an exec() call and run on the system. Now instead of limiting damage to the database system, a clever attacker can run commands directly on the server. Depending on the privilege of the process and security of the underlying system, the attacker could potentailly gain root access to the machine. They can use this to access sensitive data, change important settings, or even run any program of their choosing. The entire system can be compromised and placed at the mercy of a malicious hacker.

Don't Forget Secondary Sources


Here's one that can really trip you up. Imagine an application that performs a task based on values stored in the database. It's easy to forget that despite the fact that the database belongs to you, the source of your database values may not always be clean. "We've traced the call, and it's coming from inside the house." Always assume any source of data is dangerous unless you can verify its source and security.

How Can We Fix It?


The good news is that there is a simple way to protect yourself from injection attacks: sanitize user input using readily-available libraries.

If your language of choice is PHP, there's plenty of built in functions to safely encode all varieties of user input:
Please remember that this is not a comprehensive list. There are so many different languages and environments out there. Whenever you're wielding user input, be careful to consider whether your code may be subject to injection attacks and research appropriate measures to counter such attacks.

Where Do We Go From Here?


If you feel this post is yesterday's news, I hope you take it as a friendly reminder and refresher on the topic. If any of this is new to you, I suggest you take the next opportunity to study up on the subject. Read some other blog posts. Follow some examples online. Revisit a piece of recent code and try to break it (in a test environment please!). Once you've found some vulnerable code, learn the proper way to secure it and and run your tests again to prove that it's been fixed.

If there's one thing to take away from this it's this: always treat user input as suspect. Never let raw user input pass from your application to another system without sanitizing it first.

Now go and write some secure code.

Cheers,

Joshua Ganes

No comments:

Post a Comment