Pentesting Java EE web applications with LAPSE+

Just a quick tip for anyone doing a code review of a Java EE web application: LAPSE+ is a very useful tool to have in the arsenal, whether you’ve got the original source or just the JAR/WAR file.

In my case, the client provided me with a single .WAR file which contained the application. As it was a large application, I didn’t really fancy digging through everything manually with JD-GUI, although it is an excellent Java decompiler. I decided to take the opportunity to give LAPSE+ a try.

Here’s what  you’ll need:

You can also grab a PDF instruction manual for LAPSE from the same site. However, be aware that I found some of the information in there to be a bit misleading, e.g. needing a specific version of Eclipse. Also, don’t worry if your client provided you a project for a different IDE, such as IntelliJ IDEA – it doesn’t really matter.

First step is to get Eclipse set up. Drop the .jar file from the LAPSE+ archive into the plugins directory of Eclipse. (Re)start Eclipse, then go to Window -> View -> Other… and select the items relating to LAPSE+. A little toolbar should appear on the right with blue spherical buttons. These are your LAPSE+ windows.

Next step is to load your code into a project. This is split up into two parts, but if you’ve already got an Eclipse project for the site’s source code, you can skip the first part. Otherwise, you’ll need to extract the code from your archive and make a project for it. Start by loading the JAR (rename the .WAR to .JAR if needs be) into JD-GUI. It should decompile the archive and let you browse the code. Go to File -> Export all Sources, and save the resulting ZIP file somewhere. This archive now contains all your decompiled source code, split into directories based on the namespace hierarchy.

Now, go back to Eclipse and create an empty Java project, filling the wizard out with whatever values suit you. Once that’s created, go into the project explorer tree and find the src directory, then right click it and select Import. Select your newly exported ZIP file, and Eclipse will populate your project with your reverse-engineered source. Now right-click the project and select Build. In all likelihood, it’ll throw a whole load of errors due to imperfect decompilation – don’t worry, we don’t really care, because LAPSE+ can still function with a broken build.

Once you’ve got your project set up, go to the individual LAPSE+ windows and browse through what they found. You might need to manually refresh them to run through the checking process. In my case, I found about a 10:1 ratio of false positives, which isn’t actually too bad for code scanning. Within an hour or so of digging through the results I’d found a couple of concrete XSS bugs that I’d not spotted yet, plus a whole bunch of potential XSS bugs that I couldn’t immediately find vectors for, and a whole variety of other interesting stuff to dig through. It’s a really nice way to cut down a 400kLoC project into manageable target points.

Redis security

I was dubious at first, but I really have grown to like Redis. It’s a wonderfully simple solution to the problem of high-performance fast-changing data storage. However, its simplicity (combined with the incompetency of certain users) can easily become a detriment to security.

The Redis protocol is a simple plain-text mechanism, offering no transport layer security. This is a problem in itself, but it gets worse. By default, it listens on all available IP addresses on port 6379, with no authentication required at all. So, as you might imagine, quite a few of these servers end up facing the internet. So, I decided to see if I could find any. I wrote up a quick script to scan random /24 ranges for Redis installations, and was amazed at the result. From a single day’s scanning, I found 48 open servers. What’s worse, two of them are major household-name websites – both of which were using Redis to store their page content. Obviously both of these companies have been contacted.

It gets even worse, though. Redis allows you to send a DEBUG SEGFAULT command, which purposefully crashes the server. This means you can take down any Redis installation remotely. Nasty stuff.

Example from a server in the wild:

redis_version:2.4.5
redis_git_sha1:00000000
redis_git_dirty:0
arch_bits:32
multiplexing_api:winsock2
process_id:####
uptime_in_seconds:##########
uptime_in_days:6
lru_clock:##########
used_cpu_sys:421.25
used_cpu_user:466.63
used_cpu_sys_children:6.48
used_cpu_user_children:3.12
connected_clients:16
connected_slaves:0
client_longest_output_list:0
client_biggest_input_buf:0
blocked_clients:0
used_memory:680824356
used_memory_human:664.87M
used_memory_rss:680824356
used_memory_peak:697004492
used_memory_peak_human:680.67M
mem_fragmentation_ratio:1.00
mem_allocator:libc
loading:0
aof_enabled:0
changes_since_last_save:6922
bgsave_in_progress:0
last_save_time:1326######
bgrewriteaof_in_progress:0
total_connections_received:802101
total_commands_processed:3692083
expired_keys:34652
evicted_keys:2
keyspace_hits:2763416
keyspace_misses:591228
pubsub_channels:0
pubsub_patterns:0
latest_fork_usec:0
vm_enabled:0
role:master

So, the bottom line is to keep your Redis server secure!

  • Add a passphrase to your configuration.
  • Use a firewall to block all connections to port 6379, except from trusted IPs.
  • If you must operate in an untrusted zone, enforce IP-layer security (e.g. ipsec).
  • Patch Redis to only bind to a single IP address (or support the votes for this to happen on the Redis github site)

Keep to the above, and you should be ok!

Update: The creator of Redis, antirez, responded to this post, informing me that you can in fact make Redis bind to a single IP in redis.conf. I’d like to clarify my point on the security of Redis – it is designed to be lightweight and provide only minimal security, so this problem is not a flaw of Redis itself. Instead, it’s a flaw of how people are using (or abusing) Redis. The current ethos is that you should always apply network security to your NoSQL systems – if you don’t, it’ll end in tears.

MySQL in PHP – Everyone is doing it wrong

Ok, so maybe not everyone. But, for the mostpart, people are doing MySQL queries wrong in their PHP code. What’s even scarier is that 90% of PHP tutorials I read teach it wrong, too.

Here’s why:

  • mysql_ functions are to be deprecated in future and have been advised against by the PHP developers.
  • Concatenating query parameters as strings will always introduce SQL injection holes.
  • The mysql_real_escape_string function doesn’t actually prevent SQL injection.
  • Your custom validation is broken. It has always been broken and it always will be broken. Because you will always miss something.

If you’re doing any of the above, you’re doing it wrong.

Here’s an example:

$user_id = mysql_real_escape_string($_GET['id']);
$query = "SELECT * FROM users WHERE id = {$user_id}";
$result = mysql_query($query);

Seems legit? Nope. I can inject that.

page.php?id= 0 OR 1=1

Whoops! You just returned your entire user table. Now, let’s make it worse:

page.php?id= 0 OR 1=1 AND is_admin=1 LIMIT 1

Still unconvinced by how nasty this is?

$id = mysql_real_escape_string($_GET['id']); // 1 OR 1=1
$query = "DELETE FROM table WHERE id = {$id}";

Oh shit, you just lost all your table data. Ain’t that a bitch?

Here’s the sane way to do it with mysqli:

$db = new mysqli( /* database connection info here */ );
$statement = $db->prepare("SELECT * FROM users WHERE id = ?");
$statement = bind_param("i", $_GET['id']);
$statement->execute();

This causes the data (in this case the id) to be completely isolated from any query language.

Here’s a checklist of “doing it right”:

  • Use PDO or mysqli to create parameterised queries.
  • Don’t allow any string concatenation for values.
  • Avoid string concatenation for query language, except where absolutely necessary.
  • When concatenating, use patterns that isolate user input from the query, e.g. $sort = ($_GET['sort'] == "asc" ? "ASC" : "DESC")
  • Validate all inputs using cast functions (e.g. intval) and regex to avoid bad data being accepted. This has two benefits – all your data is valid and you mitigate vulnerabilities.

Now you’ve got no excuse to be doing it wrong!

Blocking automated SQL injection

SQL injection vulnerabilities represent a majority percentage of security holes that end up resulting in a website being hacked. One of the most popular automated SQL injection tools in existence is Havij, which is especially popular because of its simplicity. Using it is pretty much a case of pointing it at a vulnerable URL and clicking a button. Its popularity is such that there are almost as many Google results for “havij sql” as there are for “automatic sql injection”.

I decided to take a look at Havij’s HTTP request footprint. Here’s what I got from a test attack:

/havij.php?id=1
/havij.php?id=-9.9
/havij.php?id=1%20and%201=1
/havij.php?id=1%20and%201=0
/havij.php?id=1'%20and%20'x'='x
/havij.php?id=1'%20and%20'x'='y
/havij.php?id=1"%20and%20"x"="x
/havij.php?id=1"%20and%20"x"="y
/havij.php?id=1%20and%201=1
/havij.php?id=1'
/havij.php?id=-999.9%20UNION%20ALL%20SELECT%200x31303235343830303536--
/havij.php?id=-999.9%20UNION%20ALL%20SELECT%200x31303235343830303536,0x31303235343830303536--
/havij.php?id=-999.9%20UNION%20ALL%20SELECT%200x31303235343830303536,0x31303235343830303536,0x31303235343830303536--
/havij.php?id=-999.9%20UNION%20ALL%20SELECT%200x31303235343830303536,0x31303235343830303536,0x31303235343830303536,0x31303235343830303536--
/havij.php?id=-999.9%20UNION%20ALL%20SELECT%200x31303235343830303536,0x31303235343830303536,0x31303235343830303536,0x31303235343830303536,0x31303235343830303536--
/havij.php?id=-999.9%20UNION%20ALL%20SELECT%200x31303235343830303536,0x31303235343830303536,0x31303235343830303536,0x31303235343830303536,0x31303235343830303536,0x31303235343830303536--
/havij.php?id=-999.9%20UNION%20ALL%20SELECT%200x31303235343830303536,0x31303235343830303536,0x31303235343830303536,0x31303235343830303536,0x31303235343830303536,0x31303235343830303536,0x31303235343830303536--
/havij.php?id=-999.9%20UNION%20ALL%20SELECT%200x31303235343830303536,0x31303235343830303536,0x31303235343830303536,0x31303235343830303536,0x31303235343830303536,0x31303235343830303536,0x31303235343830303536,0x31303235343830303536--
/havij.php?id=-999.9%20UNION%20ALL%20SELECT%200x31303235343830303536,0x31303235343830303536,0x31303235343830303536,0x31303235343830303536,0x31303235343830303536,0x31303235343830303536,0x31303235343830303536,0x31303235343830303536,0x31303235343830303536--
/havij.php?id=-999.9%20UNION%20ALL%20SELECT%200x31303235343830303536,0x31303235343830303536,0x31303235343830303536,0x31303235343830303536,0x31303235343830303536,0x31303235343830303536,0x31303235343830303536,0x31303235343830303536,0x31303235343830303536,0x31303235343830303536--
/havij.php?id=-999.9%20UNION%20ALL%20SELECT%200x31303235343830303536,0x31303235343830303536,0x31303235343830303536,0x31303235343830303536,0x31303235343830303536,0x31303235343830303536,0x31303235343830303536,0x31303235343830303536,0x31303235343830303536,0x31303235343830303536,0x31303235343830303536--
/havij.php?id=-999.9%20UNION%20ALL%20SELECT%200x31303235343830303536,0x31303235343830303536,0x31303235343830303536,0x31303235343830303536,0x31303235343830303536,0x31303235343830303536,0x31303235343830303536,0x31303235343830303536,0x31303235343830303536,0x31303235343830303536,0x31303235343830303536,0x31303235343830303536--
/havij.php?id=-999.9%20UNION%20ALL%20SELECT%200x31303235343830303536,0x31303235343830303536,0x31303235343830303536,0x31303235343830303536,0x31303235343830303536,0x31303235343830303536,0x31303235343830303536,0x31303235343830303536,0x31303235343830303536,0x31303235343830303536,0x31303235343830303536,0x31303235343830303536,0x31303235343830303536--
/havij.php?id=-999.9%20UNION%20ALL%20SELECT%200x31303235343830303536,0x31303235343830303536,0x31303235343830303536,0x31303235343830303536,0x31303235343830303536,0x31303235343830303536,0x31303235343830303536,0x31303235343830303536,0x31303235343830303536,0x31303235343830303536,0x31303235343830303536,0x31303235343830303536,0x31303235343830303536,0x31303235343830303536--
/havij.php?id=-999.9%20UNION%20ALL%20SELECT%200x31303235343830303536,0x31303235343830303536,0x31303235343830303536,0x31303235343830303536,0x31303235343830303536,0x31303235343830303536,0x31303235343830303536,0x31303235343830303536,0x31303235343830303536,0x31303235343830303536,0x31303235343830303536,0x31303235343830303536,0x31303235343830303536,0x31303235343830303536,0x31303235343830303536--
/havij.php?id=-999.9%20UNION%20ALL%20SELECT%200x31303235343830303536,0x31303235343830303536,0x31303235343830303536,0x31303235343830303536,0x31303235343830303536,0x31303235343830303536,0x31303235343830303536,0x31303235343830303536,0x31303235343830303536,0x31303235343830303536,0x31303235343830303536,0x31303235343830303536,0x31303235343830303536,0x31303235343830303536,0x31303235343830303536,0x31303235343830303536--
/havij.php?id=-999.9%20UNION%20ALL%20SELECT%200x31303235343830303536,0x31303235343830303536,0x31303235343830303536,0x31303235343830303536,0x31303235343830303536,0x31303235343830303536,0x31303235343830303536,0x31303235343830303536,0x31303235343830303536,0x31303235343830303536,0x31303235343830303536,0x31303235343830303536,0x31303235343830303536,0x31303235343830303536,0x31303235343830303536,0x31303235343830303536,0x31303235343830303536--
/havij.php?id=-999.9%20UNION%20ALL%20SELECT%200x31303235343830303536,0x31303235343830303536,0x31303235343830303536,0x31303235343830303536,0x31303235343830303536,0x31303235343830303536,0x31303235343830303536,0x31303235343830303536,0x31303235343830303536,0x31303235343830303536,0x31303235343830303536,0x31303235343830303536,0x31303235343830303536,0x31303235343830303536,0x31303235343830303536,0x31303235343830303536,0x31303235343830303536,0x31303235343830303536--
/havij.php?id=-999.9%20UNION%20ALL%20SELECT%200x31303235343830303536,0x31303235343830303536,0x31303235343830303536,0x31303235343830303536,0x31303235343830303536,0x31303235343830303536,0x31303235343830303536,0x31303235343830303536,0x31303235343830303536,0x31303235343830303536,0x31303235343830303536,0x31303235343830303536,0x31303235343830303536,0x31303235343830303536,0x31303235343830303536,0x31303235343830303536,0x31303235343830303536,0x31303235343830303536,0x31303235343830303536--
/havij.php?id=-999.9%20UNION%20ALL%20SELECT%200x31303235343830303536,0x31303235343830303536,0x31303235343830303536,0x31303235343830303536,0x31303235343830303536,0x31303235343830303536,0x31303235343830303536,0x31303235343830303536,0x31303235343830303536,0x31303235343830303536,0x31303235343830303536,0x31303235343830303536,0x31303235343830303536,0x31303235343830303536,0x31303235343830303536,0x31303235343830303536,0x31303235343830303536,0x31303235343830303536,0x31303235343830303536,0x31303235343830303536--
/havij.php?id=-999.9%20UNION%20ALL%20SELECT%200x31303235343830303536,0x31303235343830303536,0x31303235343830303536,0x31303235343830303536,0x31303235343830303536,0x31303235343830303536,0x31303235343830303536,0x31303235343830303536,0x31303235343830303536,0x31303235343830303536,0x31303235343830303536,0x31303235343830303536,0x31303235343830303536,0x31303235343830303536,0x31303235343830303536,0x31303235343830303536,0x31303235343830303536,0x31303235343830303536,0x31303235343830303536,0x31303235343830303536,0x31303235343830303536--
/havij.php?id=-999.9%20UNION%20ALL%20SELECT%200x31303235343830303536,0x31303235343830303536,0x31303235343830303536,0x31303235343830303536,0x31303235343830303536,0x31303235343830303536,0x31303235343830303536,0x31303235343830303536,0x31303235343830303536,0x31303235343830303536,0x31303235343830303536,0x31303235343830303536,0x31303235343830303536,0x31303235343830303536,0x31303235343830303536,0x31303235343830303536,0x31303235343830303536,0x31303235343830303536,0x31303235343830303536,0x31303235343830303536,0x31303235343830303536,0x31303235343830303536--
/havij.php?id=-999.9%20UNION%20ALL%20SELECT%200x31303235343830303536,0x31303235343830303536,0x31303235343830303536,0x31303235343830303536,0x31303235343830303536,0x31303235343830303536,0x31303235343830303536,0x31303235343830303536,0x31303235343830303536,0x31303235343830303536,0x31303235343830303536,0x31303235343830303536,0x31303235343830303536,0x31303235343830303536,0x31303235343830303536,0x31303235343830303536,0x31303235343830303536,0x31303235343830303536,0x31303235343830303536,0x31303235343830303536,0x31303235343830303536,0x31303235343830303536,0x31303235343830303536--
/havij.php?id=-999.9%20UNION%20ALL%20SELECT%200x31303235343830303536,0x31303235343830303536,0x31303235343830303536,0x31303235343830303536,0x31303235343830303536,0x31303235343830303536,0x31303235343830303536,0x31303235343830303536,0x31303235343830303536,0x31303235343830303536,0x31303235343830303536,0x31303235343830303536,0x31303235343830303536,0x31303235343830303536,0x31303235343830303536,0x31303235343830303536,0x31303235343830303536,0x31303235343830303536,0x31303235343830303536,0x31303235343830303536,0x31303235343830303536,0x31303235343830303536,0x31303235343830303536,0x31303235343830303536--
/havij.php?id=-999.9%20UNION%20ALL%20SELECT%200x31303235343830303536,0x31303235343830303536,0x31303235343830303536,0x31303235343830303536,0x31303235343830303536,0x31303235343830303536,0x31303235343830303536,0x31303235343830303536,0x31303235343830303536,0x31303235343830303536,0x31303235343830303536,0x31303235343830303536,0x31303235343830303536,0x31303235343830303536,0x31303235343830303536,0x31303235343830303536,0x31303235343830303536,0x31303235343830303536,0x31303235343830303536,0x31303235343830303536,0x31303235343830303536,0x31303235343830303536,0x31303235343830303536,0x31303235343830303536,0x31303235343830303536--
/havij.php?id=-999.9%20UNION%20ALL%20SELECT%200x31303235343830303536,0x31303235343830303536,0x31303235343830303536,0x31303235343830303536,0x31303235343830303536,0x31303235343830303536,0x31303235343830303536,0x31303235343830303536,0x31303235343830303536,0x31303235343830303536,0x31303235343830303536,0x31303235343830303536,0x31303235343830303536,0x31303235343830303536,0x31303235343830303536,0x31303235343830303536,0x31303235343830303536,0x31303235343830303536,0x31303235343830303536,0x31303235343830303536,0x31303235343830303536,0x31303235343830303536,0x31303235343830303536,0x31303235343830303536,0x31303235343830303536,0x31303235343830303536--
/havij.php?id=-999.9%20UNION%20ALL%20SELECT%200x31303235343830303536,0x31303235343830303536,0x31303235343830303536,0x31303235343830303536,0x31303235343830303536,0x31303235343830303536,0x31303235343830303536,0x31303235343830303536,0x31303235343830303536,0x31303235343830303536,0x31303235343830303536,0x31303235343830303536,0x31303235343830303536,0x31303235343830303536,0x31303235343830303536,0x31303235343830303536,0x31303235343830303536,0x31303235343830303536,0x31303235343830303536,0x31303235343830303536,0x31303235343830303536,0x31303235343830303536,0x31303235343830303536,0x31303235343830303536,0x31303235343830303536,0x31303235343830303536,0x31303235343830303536--
/havij.php?id=-999.9%20UNION%20ALL%20SELECT%200x31303235343830303536,0x31303235343830303536,0x31303235343830303536,0x31303235343830303536,0x31303235343830303536,0x31303235343830303536,0x31303235343830303536,0x31303235343830303536,0x31303235343830303536,0x31303235343830303536,0x31303235343830303536,0x31303235343830303536,0x31303235343830303536,0x31303235343830303536,0x31303235343830303536,0x31303235343830303536,0x31303235343830303536,0x31303235343830303536,0x31303235343830303536,0x31303235343830303536,0x31303235343830303536,0x31303235343830303536,0x31303235343830303536,0x31303235343830303536,0x31303235343830303536,0x31303235343830303536,0x31303235343830303536,0x31303235343830303536--
/havij.php?id=-999.9%20UNION%20ALL%20SELECT%200x31303235343830303536,0x31303235343830303536,0x31303235343830303536,0x31303235343830303536,0x31303235343830303536,0x31303235343830303536,0x31303235343830303536,0x31303235343830303536,0x31303235343830303536,0x31303235343830303536,0x31303235343830303536,0x31303235343830303536,0x31303235343830303536,0x31303235343830303536,0x31303235343830303536,0x31303235343830303536,0x31303235343830303536,0x31303235343830303536,0x31303235343830303536,0x31303235343830303536,0x31303235343830303536,0x31303235343830303536,0x31303235343830303536,0x31303235343830303536,0x31303235343830303536,0x31303235343830303536,0x31303235343830303536,0x31303235343830303536,0x31303235343830303536--
/havij.php?id=-999.9%20UNION%20ALL%20SELECT%200x31303235343830303536,0x31303235343830303536,0x31303235343830303536,0x31303235343830303536,0x31303235343830303536,0x31303235343830303536,0x31303235343830303536,0x31303235343830303536,0x31303235343830303536,0x31303235343830303536,0x31303235343830303536,0x31303235343830303536,0x31303235343830303536,0x31303235343830303536,0x31303235343830303536,0x31303235343830303536,0x31303235343830303536,0x31303235343830303536,0x31303235343830303536,0x31303235343830303536,0x31303235343830303536,0x31303235343830303536,0x31303235343830303536,0x31303235343830303536,0x31303235343830303536,0x31303235343830303536,0x31303235343830303536,0x31303235343830303536,0x31303235343830303536,0x31303235343830303536--
/havij.php?id=-999.9%20UNION%20ALL%20SELECT%200x31303235343830303536,0x31303235343830303536,0x31303235343830303536,0x31303235343830303536,0x31303235343830303536,0x31303235343830303536,0x31303235343830303536,0x31303235343830303536,0x31303235343830303536,0x31303235343830303536,0x31303235343830303536,0x31303235343830303536,0x31303235343830303536,0x31303235343830303536,0x31303235343830303536,0x31303235343830303536,0x31303235343830303536,0x31303235343830303536,0x31303235343830303536,0x31303235343830303536,0x31303235343830303536,0x31303235343830303536,0x31303235343830303536,0x31303235343830303536,0x31303235343830303536,0x31303235343830303536,0x31303235343830303536,0x31303235343830303536,0x31303235343830303536,0x31303235343830303536,0x31303235343830303536--
/havij.php?id=-999.9%20UNION%20ALL%20SELECT%200x31303235343830303536,0x31303235343830303536,0x31303235343830303536,0x31303235343830303536,0x31303235343830303536,0x31303235343830303536,0x31303235343830303536,0x31303235343830303536,0x31303235343830303536,0x31303235343830303536,0x31303235343830303536,0x31303235343830303536,0x31303235343830303536,0x31303235343830303536,0x31303235343830303536,0x31303235343830303536,0x31303235343830303536,0x31303235343830303536,0x31303235343830303536,0x31303235343830303536,0x31303235343830303536,0x31303235343830303536,0x31303235343830303536,0x31303235343830303536,0x31303235343830303536,0x31303235343830303536,0x31303235343830303536,0x31303235343830303536,0x31303235343830303536,0x31303235343830303536,0x31303235343830303536,0x31303235343830303536--

The first two requests are used to find a keyword that is on both the valid page and an error page. The next 8 requests are used for recon to test if the site is vulnerable to SQL injections. The rest are UNION SELECT injections used to attempt to discover the column count in the SQL query. When the column count is correct, the repeated token will appear in the output of the page.

Interestingly, 0x31303235343830303536 is a token that Havij just happens to use. It represents “1025480056” in ASCII, which doesn’t seem to have any interesting properties. Perhaps the constant was encoded in little endian and it corresponds to the US cellphone number ‘560-084-5201’. Or, more likely, it was just chosen at random. What’s important is that it seems to be exactly the same every time you run it. A quick Google search supports this theory.

Anyway, the use of this constant is great news for anyone defending from SQL injection attacks. Just put the following PHP code in a common include (i.e. a script that gets included in every user-facing page) and it’ll prevent Havij from working:

<?php
foreach($_GET as $gv)
    if(strpos($gv, '0x31303235343830303536') !== FALSE) die('SQL injection attempt detected.');
foreach($_POST as $pv)
    if(strpos($pv, '0x31303235343830303536') !== FALSE) die('SQL injection attempt detected.');
foreach($_COOKIE as $cv)
    if(strpos($cv, '0x31303235343830303536') !== FALSE) die('SQL injection attempt detected.');
?>

Obviously this isn’t a replacement for proper filtering of data used in queries, but it’s a quick and effective way to ward the script kiddies away.

XSRF in phpBB, and probably a lot of other CMS software.

Major forums such as vBulletin and Invision Power Board have recently altered a lot of their codebase to require a security token to prevent their previous vulnerability to XSRF (Cross Site Request Forgery). Unfortunately, phpBB seems to be lagging behind a little and has not yet added this feature.

To test, I set up phpBB 3.0.9 with the default settings, then went to my server and created the following script:

<?php
header('Location: http://target.com/phpbb/ucp.php?mode=logout');
?>

I then used the [img] tags in phpBB to point at the above script. Upon viewing the post, I was logged out. Obviously this is a problem (it would be very annoying to users) but the real worry is that an attacker finds a URL that does something nasty. Imagine if an admin was logged into a board’s ACP and viewed such an image.

I can imagine there’s a huge number of content management systems out there that have this problem, too. Any situation where a user can select a remote resource to be accessed by the browser (images are the obvious one, but even a link can be malicious in this sense) is problematic, because it would allow XSRF.

The obvious solution, which some sites are already using, is some sort of link-cookie system whereby each link has a security token embedded to prove that the request came from a legitimate source. The first method I thought of for generating these tokens is a hash of the session ID, but came to realise that since a lot of CMS software places the session ID in the URL, it might be leaked using the HTTP referrer header. So, after that consideration, here’s my example:

<?php
// when a session is created, set $_SESSION['url_xsrf_key'] to a random string.
// this keeps the key specific to the session, but cannot be broken through leaked referrer URLs.
// I'm also hashing with the session ID to prevent (albeit unlikely) collisions of url_xsrf_key.

function GenerateLogoutLink()
{
    // hashing the path is important as it prevents use of known keys on other URLs
    $logout_path = $settings->siteBase . "/logout.php";
    $key = md5(session_id() . $_SESSION['url_xsrf_key'] . $logout_path);
    return $logout_path . '?k=' . $key;
}

function ValidateRequest()
{
    if(!isset($_GET['k'])) return false;
    if($_GET['k'] == "") return false;
    $logout_path = $settings->siteBase . "/logout.php";
    return $_GET['k'] == md5(session_id() . $_SESSION['url_xsrf_key'] . $logout_path);
}
?>

As long as every script on the site that is user-facing (i.e. not an include) uses this system, it’s safe against this form of XSRF.

Update
I’m using this post to create a short list of major CMS softwares and websites that are vulnerable to XSRF on at least the logout page. I tested with the above PHP script and a mod_rewrite rule to fake a .jpg extension. Not all of these can be attacked directly, since they don’t allow users to post images. However, a malicious user could find another site that a target user frequents and post an image on there that performs the XSRF attack.

Forums:
phpBB 3.0.9
TinyBB 1.4.2
Serendepity 1.5.5
Blogs:
WordPress 3.2.1
b2evo 4.0.5
Generic CMS:
PHP-Fusion 7.02.03
Drupal 7.2
Concrete5 5.4.2
Websites:
Google Mail
YouTube
Blogger
Blogger
Google (via Google accounts)
Google AdSense
Google AdWords
Jaiku (yet another Google service)
Google Wave
Hotmail (a.k.a. Windows Live Mail)
Slashdot
Typo3