Webinar “Bau sicherer LAMP Anwendungen”

August 21st, 2008 by Stefan Esser

Last week I gave my first webinar for MySQL titled “Bau sicherer LAMP Anwendungen”. The webinar, which was a cooperation between MySQL and my company SektionEins, was held in german, covered SQL-Malware, SQL-Injection, safe programming and some tools to detect and block SQL-Injection attacks.

The recording of this webinar is now available on the MySQL site.

For those that only want to see my slides they are available on the MySQL site after registration or here.

Because it was a german webinar the recording and slides are in german, too.

MySQL and SQL Column Truncation Vulnerabilities

August 18th, 2008 by Stefan Esser

While SQL-Injection is one of the most discussed security problems in web applications other possible problems for SQL queries like overlong input are usually ignored although they can lead to all kinds of security problems.

This might be caused by the fact that security problems that are the result of overlong input are often buffer overflows and buffer overflows are something many web application security experts know nothing about and choose to ignore.

There are however several security problems for SQL queries that are caused by overlong input and no one talks about.

max_packet_size

In MySQL there exists a configuration option called max_packet_size which is set to one megabyte by default and controls the maximum size of a packet sent between the SQL client and server. When queries or result rows do not fit into a single packet a error is raised. This means an overlong SQL query is never sent to the server and therefore never executed.

This can lead to security problems when an attacker is able to supply long data elements that are then used in SQL queries. A good example are logging queries that combine information like the HTTP User-Agent, session ids and log messages into a large query that then does not fit into the packet anymore.

Another example from a real world application is a session table cleanup process that first selects all sessions matching certain parameters into a PHP array, then performs a multiple level cleanup and in the end all selected session ids are put into single delete query. It should be obvious that when there are many session identifiers in the table that need deletion the query gets too long. The result of this is that flooding the application with new sessions in a short time will result in no unused session being deleted later anymore.

Therefore web application developers should always ensure that they do not sent overlong data to the server. And it doesn’t matter if they use prepared statements or not.

SQL Column Truncation Vulnerabilities

When user input is not checked for its length SQL Column Truncation Vulnerabilities can arise. “SQL Column Truncation Vulnerability” is the name I use to describe security problems arising from overlong input that is truncated during insertion in the database. By default MySQL will truncate strings longer than the defined maximum column width and only emit a warning. Those warnings are usually not seen by web applications and therefore not handled at all. In MySQL the sql_mode STRICT_ALL_TABLES can be activated to turn these warnings into errors but applications will run most of the time on servers that run in the default mode and even if an application uses the stricter sql_mode it should not produce this error in the first place. Therefore a length check is required.

To understand why the truncation on insert can lead to security problems imagine the following application.

  • The application is a forum where new users can register
  • The administrator’s name is known e.g. ‘admin’
  • MySQL is used in the default mode
  • There is no application restriction on the length of new user names
  • The database column username is limited to 16 characters

A potential attacker might now try to register the name ‘admin ‘, which will fail because the ‘isAlreadyRegistered’ check will result in the SQL query.

SELECT * FROM user WHERE username='admin '

Because MySQL does not compare strings in binary mode by default more relaxed comparison rules are used. One of these relaxations is that trailing space characters are ignored during the comparison. This means the string ‘admin    ‘ is still equal to the string ‘admin’ in the database. And therefore the application will refuse to accept the new user.

If the attacker however tries the username ‘admin           x’ the application will search for it in the database and will not find it, because it is impossible to find a username with a length of 17 in a database field that has a 16 character limit. The application will accept the new username and insert it into the database. However the username column is to short for the full name and therefore it is truncated and ‘admin           ‘ is inserted into the database.

The result of this is that the user table now contains two users that due to trailing spaces both will be returned when the SELECT query above is executed. At this point a potential security problem arises because now it depends on how the username is treated throughout the application. The following pseudocode for example is vulnerable.

$userdata = null;
if (isPasswordCorrect($username, $password)) {
   $userdata = getUserDataByLogin($username);
   ...
}

When the previous piece of code uses the SQL query

SELECT username FROM users WHERE username = ? AND passhash = ?

to detect if the user password is correct and then does a lookup of the user data by name a security problem manifests.

SELECT * FROM users WHERE username = ?

Because the attacker created the newly created admin user he knows the correct password to pass this check. And because the real admin user is first in the table it will be returned first when the user data lookup by name is executed later.

Conclusion

Both problems described here are two new things web applications needs to be audited for because both can lead to real security problems. And because no one searches for these kind of vulnerabilities, now that it is public most probably the next weeks will bring several advisories about open source software suffering from these problems.

mt_srand and not so random numbers

August 17th, 2008 by Stefan Esser

PHP comes with two random number generators named rand() and mt_rand(). The first is just a wrapper around the libc rand() function and the second one is an implementation of the Mersenne Twister pseudo random number generator. Both of these algorithms are seeded by a single 32 bit dword when they are first used in a process or one of the seeding functions srand() or mt_srand() is called.

Because of such a short seed it should be obvious to everyone that neither rand() nor mt_rand() are random enough for cryptographic usages. However web application programmers tend to use rand() or mt_rand() to create cryptographic secrets like passwords, activation keys, autologin cookies or session identifiers. In many situations this seems secure enough, because not only a 32 bit seed needs to be guessed but also the amount of previously generated random numbers. Therefore bruteforcing seems impractical.

There are however several situations and conditions that make bruteforcing feasible or not required at all.

The Implementation

When mt_rand() is seeded internally or by a call to mt_srand() PHP 4 and PHP 5 <= 5.2.0 force the lowest bit to 1. Therefore the strength of the seed is only 31 and not 32 bits. In PHP 5.2.1 and above the implementation of the Mersenne Twister was changed and the forced bit removed.

Implementation Bugs

In PHP 4 and PHP <= 5.2.5 the automatic seed of rand() and mt_srand() is buggy. Whenever the lowest 26 bits of the timestamp are zero the internal seed will become zero (or 1 due to the forced bit) on 32 bit systems because of an overflow of the 32 bit register. On 64 bit systems there is a precision loss when the seed is casted from a double to int that results in a seed about 24 bit strong.

Weak seeding

Although PHP 4.2.0 and above internally seeds the random number generators many PHP applications still call srand() and mt_srand() themself to seed the random number generators. Popular seedings look like one of the following examples.

mt_srand(time());
mt_srand((double) microtime() * 100000);
mt_srand((double) microtime() * 1000000);
mt_srand((double) microtime() * 10000000);

These seedings are all bad because

  1. time() is not random. It is known by the attacker(). Even a wrongly set clock on the server side does not protect because time leaks through the Date HTTP Header.
  2. The first factor is between 0 and 1 and the second factor is 100000. Therefore there are only 100000 possible seeds which is a strength of about 17 bits. On older PHP versions the forced seed bit results in only 16 bit strength. (Due to rounding issues there might be less)
  3. The first factor is between 0 and 1 and the second factor is 1000000. Therefore there are only 1000000 possible seeds which is a strength of about 20 bits. On older PHP versions the forced seed bit results in only 19 bit strength. (Due to rounding issues there are less)
  4. The first factor is between 0 and 1 and the second factor is 10000000. However because microtime() depends on the current microsecond() there are only 1 million possible results, which again results in a seed with a strength of about 20 bit. (Due to rounding issues there are less)

CGI seeds once per request

Because in a CGI environment every request is handled by a fresh process the random number generators are always freshly seeded. Therefore an attacker only needs to bruteforce 32/31 bits and does not need to guess how many random numbers where already generated. In case of information leaks generated during the same request using the same random number generator it is possible to use precalculated attack tables.

Crashing PHP for fresh random numbers

Whenever a process is reused for random numbers attacking becomes more difficult, because the internal state of the random number generator is unknown. Therefore any crash bug in PHP is welcome. In older PHP versions crashing PHP is just a matter of sending deeply nested arrays in GET, POST or COOKIE variables. Whenever a crashed PHP process is replaced by a fresh one, a new seed is generated and the internal state of the random number generator just depends on the 32 bit seed. Here precalculated tables can also be used in case of information leaks.

Keep-Alive is your friend

When some information is known about the internal state of the random number generator Keep-Alive HTTP request can make exploits very easy. Because follow request during a Keep-Alive HTTP connection are handled by the same process (same random number generator) the state of the random number generator stays the same and random numbers can be precalculated from the outside. While this is always true for mod_php, it is not true for CGI and only sometimes true for fastcgi setups.

Shared Ressource Problem

Both random number generators are a shared ressource, because the state is stored in process memory and future request in the same process will continue with the previous random number state. Because of this in shared hosting environments that do not use a CGI setup a malicious customer on one VHOST can set the random number generators in a known state by calling srand(0) or mt_srand(0). With the help of Keep-Alive requests it is then possible to predict the random numbers used on other VHOSTs. This of course allows the prediction of newly generated passwords, activation keys or session identifiers.

Information Leaks

Sometimes applications initialize the random number generator and at the same time they generate a random number that is then sent to the user. An example is the search function of phpBB2, where the search_id is generated with mt_rand() and then written to the HTML.

mt_srand ((double) microtime() * 1000000);
$search_id = mt_rand();

The problem with this is that in the example above the search function does not only set the random number to a 20 bit wide state but it also leaks the state through the search_id output. In this example there are only 0,003% collisions. Therefore in 99,997% of the calls knowing the search_id means knowing the seed, too. An attacker can simply create a lookup table with one millon entries to determine the seed by search_id. In case one of the collisions is hit (which is quite unlikely) he can just search again.

For PHP versions that force the lowest bit to be 1 like PHP 4 and PHP 5 <= 5.2.0 the numbers above are incorrect. These versions use only 19 bits, which also causes fewer collisions.

The Art of Cross Application Attacks

It is not so obvious why the phpBB2 search_id information leak is a problem, because it is not used in a cryptographic manner. The previous paragraphs however contained enough information to explain why this is a problem after all.

  • Initializing the random number generator affects not only the PHP script doing it, but also every other script executed by the same process
  • When the state of the random number generator is leaked all future random numbers are predictable
  • Several applications create passwords, session identifiers, activation links or other cryptographic tokens with mt_rand()

In addition to that you must stop to consider your application (phpBB 2) to be the only one. In the real world multiple applications that perform different tasks are installed on the same server, most probably even in the same VHOST. Imagine for example a company running a support forum (phpBB 2) and a blog (Wordpress).

In such a situation touching the random number seeding becomes a threat. Especially when the seed leaks. A real world attacks against the combination phpBB2 and Wordpress would look like.

  1. Use a Keep-Alive HTTP Request to search in the phpBB2 forum for the string ‘a’
  2. The search should return enough results so that multiple pages are returned which leaks the search_id
  3. A simple table lookup is performed by the attacker to determine the random number seed
  4. The attacker initializes his random number generator and throws away one random number. (the search_id)
  5. The attacker then uses the still active Keep-Alive HTTP request to send an admin password reset request to the Wordpress blog.
  6. The blog uses mt_rand() to generate the activation link and sends it to the admin by email.
  7. The attacker can calculate the activation link because his random number generator has the same state.
  8. The exploit triggers the activation link (over the still active Keep-Alive Request) which results in the new admin password beeing sent to the admin.
  9. Because the new password is generated by mt_rand() the attacker can also calculate it on his side.
  10. After that the attacker knows the admin password of the Wordpress blog an can take it over.

This shows that installing two independent applications that both use PHP’s random number generators can result in new cross application security vulnerablities that are as serious and dangerous as every vulnerability contained in a single application.

In order to fight this class of cross application vulnerabilities in the next days a generic protection will be added to the Suhosin extension that increases the strength of the mt_rand() seed from a single dword to a multi-dword width. In addition to that an option will be added to ignore the mt_srand() which will be activated by default. This closes the problem of the cross application attack without requiring any changes in the applications.

On the other hand it is strongly recommended for the PHP developers to add more secure random number functions to the PHP core and it is strongly recommended for PHP application developers to keep their fingers away from srand() or mt_srand() and to never ever use rand() or mt_rand() for cryptographic secrets.

MySQL-Proxy learning to block SQL-Injection

August 15th, 2008 by Stefan Esser

I previously reported about my joy with MySQL-Proxy and a simple SQL-Injection detection based on a simple heuristic.

Today I present the more interesting approach that I promised to publish after my webinar yesterday. This approach is based on the idea that SQL queries issued by an application always have a certain structure. This structure can be learned and remembered by MySQL-Proxy. Any SQL query that has a different structure can then be considered an attack.

Training Mode

The first Lua script learn_sql_queries.lua uses MySQL-Proxy’s read_query hook to catch COM_INIT_DB and COM_QUERY packets. COM_INIT_DB packets are issued when the database is changed and COM_QUERY packets contain normal queries.

When a change of database is detected a CREATE TABLE is injected into the communication to create a table called ‘allowed_queries’ in the newly selected database. This table consist of only on column called ‘query’. Within this column normalized queries are collected.

When a normal query is received it is first tokenized by MySQL-Proxy’s tokenizer. The tokens are then used to recreate a normalized version of the query where all data values are replaced by the ‘?’ placeholder. Additionally IN ( ?, ?, ?, …) statements are compressed to IN ( ? ) to allow arbitrary length IN value lists without having to learn all possibilities. The normalized query is then learned by inserting it into the table.

When all queries have been learned (maybe during development) the blocking mode can be started.

Blocking Mode

The second Lua script block_unknown_queries.lua also uses MySQL-Proxy’s read_query hook to catch COM_INIT_DB and COM_QUERY packets.

When a change of database is detected a SELECT statement is injected into the communication that loads the table ‘allowed_queries’ into a Lua-Table. The queries become the indices so that they can be found fast.

When a normal query is received it is first tokenized and normalized. The normalized Query is then searched in the Lua-Table which is just a key lookup. If the query is found in the table it is one of the known query structures that are allowed. The query is then executed as normally.

If the query is not found in the table it is either a query that was not learned by mistake or it is an SQL-Injection attack. The query is not executed and a database error “Possible SQL Injection” is returned.

Both proof of concept examples are released as GPL. Therefore feel free to modify them for your needs. You might prefer to just log SQL-Injection attempts instead of blocking them.

PHP 4 - Requiescat In Pace

August 8th, 2008 by Stefan Esser

Since last night PHP 4 is finally dead…

Is it?

Well not really, because there are still millions of servers running PHP 4 that haven’t upgraded to the faster, more stable and more secure PHP 5 and most of them will continue to use it. So PHP 4 will still be around a while.

However last night PHP 4.4.9 was released which is the final security update by the PHP development team. This means from now on not only normal support (which was dropped at the end of 2007) but also security support for PHP 4 has ended. It is therefore recommended to finally upgrade.

Because I know that upgrading is sometimes not that easy Suhosin will continue to support PHP 4 for a while. This means the current Suhosin-Patch 0.9.6 will be ported to PHP 4.4.9 and also the next release of Suhosin-Patch will still support recent PHP 4 versions. However at the end of 2008 I will also discontinue Suhosin-Patch for PHP 4 and new features to the Suhosin-Extension will only be implemented for PHP 5.

Suhosin 0.9.25

August 6th, 2008 by Stefan Esser

I just released a long overdue update to the Suhosin extension. There are only a few changes in it. The full changelog is

  • Fixed PHP 4 compilation problem introduced in 0.9.24
  • Fixed PHP 5.3 compilation problem
  • Changed PHP default POST handler to PHP’s current handler

As usual you can grab your copy at

http://www.suhosin.org/

Mysql-Proxy Heuristic SQL Injection Detection

August 5th, 2008 by Stefan Esser

MySQL Proxy is a simple program that sits between your client and MySQL server(s) that can monitor, analyze or transform their communication. Its flexibility allows for unlimited uses; common ones include: load balancing; failover; query analysis; query filtering and modification; and many more.”

The flexibility of MySQL Proxy is based on the fact that every aspect is scriptable with Lua. Because I am new to MySQL Proxy and the Lua language I tried to implement a very simple script that waits for incoming SQL queries, tokenizes them and tries to detect SQL Injection heuristically by searching for certain disallowed SQL functions, databases, tables, statements or comments. When an SQL query is believed to contain an SQL injection is it not executed and a “Possible SQL injection” error is returned.

You can grab the detect_sql_injection.lua script at

http://www.suspekt.org/downloads/detect_sql_injection.lua.gz

If you are interested in this and german speaking you might also be interested in next week’s MySQL webinar “Bau sicherer LAMP Anwendungen” where I will not only discuss this little Lua script but also another one that implements SQL injection detection by query structure learning.

Xdebug 2.0.3 - Stealth Patch

August 4th, 2008 by Stefan Esser

On PHP conferences or user group meetings one question that pops up again and again is why Xdebug and some other commercial PHP extensions e.g. Zend Debugger cannot be loaded at the same time. Those asking usually can understand why running two debuggers at the same time will lead to problems, but they don’t understand why problems should arise when only one is used at a time. They want to be able to load Zend Debugger and Xdebug with the same php.ini without the need to restart the whole server just to change the debugger. Because of this people requested repeatedly that I port the stealth magic that I perfom in Suhosin to Xdebug to be able to load both debuggers at the same time.

You can download the patch against Xdebug 2.0.3 at:

http://www.suspekt.org/downloads/xdebug-2.0.3-stealth.diff.gz

Keep in mind that in order to become stealth you must load xdebug like a normal PHP extension with:

extension=xdebug.so

You cannot load it as zend_extension because otherwise the “protection” of the commercial tools will kick in. However when loaded as normal PHP extension Xdebug will register itself as Zend Extension and should function normally.

Attention: I don’t use this patch myself so if you encounter any problem contact me so that I can fix it.

Update: The patch was updated because there was a crash on process termination when stealth mode was not needed. In the comments it is reported that Xdebug still works partially while Zend Debugger is activated.

Speaking at POC 2008

August 3rd, 2008 by Stefan Esser

I will present a session at this year’s Power of Community hacking conference in Seoul about vulnerabilities in closed source PHP applications.

Session: Vulnerability Discovery in Closed Source/Encrypted PHP Applications

Security audits of PHP applications are usually performed on a source code basis. However sometimes vendors protect their source code by encrypting their applications with runtime (bytecode-)encryptors. When these tools are used source code analysis is no longer possible and because these tools change how PHP works internally, several greybox security scanning/fuzzing techniques relying on hooks fail, too.

This talk will show how different PHP (bytecode-)encryptions work, how the original bytecode can be recovered, how vulnerability discovery can still be performed with only the bytecode available and how feasible PHP bytecode decompilation is.

Unlike my previous talks about PHP Bytecode Analyses at IPC 2007 and PHP London 2008 this talk is not a simple introduction to PHP Bytecode or binary analysis. I will demonstrate how we at SektionEins discover security holes in Closed Source PHP applications when a client ask for this.

See you in Seoul between 13th and 14th November.

서울에서 11월 13일에서 14일에 만나요!

Speaking at IPC 2008

August 3rd, 2008 by Stefan Esser

I will present two session at this year’s International PHP Conference that has now moved from Frankfurt to Mainz.

Session: Suhosin catching vulnerabilities before they hit you

During the last two years the Suhosin PHP protection system has become a standard component of many PHP installations of various linux and bsd distributions.

This talk will give an insight into the many features Suhosin has today and how they can be used to detect and stop yet unknown vulnerabilities in PHP and PHP applications.

Session: Lesser known security problems in PHP applications

When the security of PHP applications is in focus usually standard XSS vulnerabilities, SQL Injections, Remote File Inclusions, Header Injections and CSRF are discussed. However there are a number of different vulnerability classes and non obvious exploitation paths that are as dangerous but lesser known.

This talk will give an insight in such vulnerabilities and how to defend against them.

See you this time for the first time in Mainz between the 27th and 31th Oktober.