Seth Woolley's Blog

Occasional Musings

anatomy of a typical XSS problem with user logins and cookies(0)


Repeatedly, I see the same stupid login/authentication cross-site scripting mistake, over and over again, so I thought I'd tell everybody about it to try and remind people what not to do.



Check out the pmos help desk, here:

Looks normal.  Only a couple fields to enter data into, right?  Not quite, there's a variable that we don't see right away.

Click on a link in the header that looks like it might require authentication, say, tbe "Browse" link, like this:

The php is so smart it knows you haven't logged in yet.  So what does it do?  It gets smart -- it says, hey, I need you to login.  So I'm going to send you to the login page.  BUT, as an added convenience, I'm going to tell the login page to send you back at the end like this:

Isn't that nifty.  Yes and no.  Yes, it's nifty in that it's a cool design improvement, but no it's not in that it's another form of input into the system, one that isn't so carefully guarded because it'll never hit the SQL system.

An unsanitary problem

Let's download their source code and take a look at how they implemented it.  I just went to: and clicked and opened up login.php.  (Ignore their statement, "Also, please read the GPL license thoroughly. By no means can you use this code (or any of it) in your own product and sell it as your own -- that is completely illegal and will be prosecuted."  -- This is false.  The GPL lets you charge whatever you want for a product so long as you give them credit in the source files/changelog.)

<form action="<?php echo $HD_CURPAGE ?>" method="post">
  <input type="hidden" name="cmd" value="login" />
  <input type="hidden" name="redirect" value="<?php echo ($_GET[redirect] != "" ) ? $_GET[redirect] : $_POST[redirect]; ?>">
  <tr><td><label for="email">Email: </td><td><input type="text" name="email" size="30" value="<?= $_POST[email] ?>" /></label></td></tr>
 <tr><td><label for="password">Password: </td><td><input type="password" name="password" size="30" /></label></td></tr>
 <tr><td><br /><input type="submit" value="Login" /></td></tr>

Ouch!  No sanitation, whatsoever.  They let you do pretty much whatever you want before you login (this is not so dangerous).  But first, let's note the first problem in this code:

(Note that the email input field is also XSSable (but via the POST method).

It just puts that directly into the hidden field for the redirect, without sanitation -- classic XSS, but not so big a deal -- if you're on the login page your cookies probably aren't there to steal yet, if you have well-implemented session-only cookies -- which they don't (they just let them live for a whole month):

setcookie( "iv_helpdesk_login", $_POST[email], time( ) + 2592000 );.


Delivering the payload

If you need to inject code where quotes aren't allowed (magic quotes are enabled, for example!), you can, for example, use the document.referer property and setup your own site to download two different sets of data, one intended for the client, another for the server, so that when the server sees it they get needed payload.

Here's a way that just decodes part of the url for the payload to avoid magic quotes altogether:

(Tricks like this are why javascript should die.)

It gets worse from here

if( trim( $_POST[redirect] ) != "" )
  $redirect = $_POST[redirect];
  $redirect = $HD_URL_BROWSE;

$EXTRA_HEADER = "<meta http-equiv=\"refresh\" content=\"1; URL={$redirect}\" />";
$msg = "<div class=\"successbox\">Login successful.  Redirecting you now.  Click <a href=\"{$redirect}\">here</a> if you aren't automatically forwarded...</div>";

$do_redirect = 1;

Yep, they let you inject code _after_ login has succeeded, so if you want to make sure you get their auth data, just... let them login first before activating your payload (which you could carry over via document.referrer instead of document.location if it's not an encrypted link)!

Note that, magic quotes _must_ be enabled for pmon to work.

Magic quotes and SQL

Futhermore, it's littered with magic-quotes-expecting SQL commands like:

$res = mysql_query( "SELECT * FROM {$pre}user WHERE ( email = '$_POST[email]' && password = '$_POST[password]' )" );



Yeah, stay away from the h2desk pmon code (no idea about their commercial offering though), as it's not a good example of quality, secure code.

Seth Woolley's Blog webdevel security

Leave A Comment

Secret is used for editing your own comment. If subject, secret, and name all are the same as a previous comment, it will be overwritten. Turing is the name of this program (look at the Source Code link on the front page), used to see if you are human.