diff --git a/serializekiller.py b/serializekiller.py index d383564..e2be940 100644 --- a/serializekiller.py +++ b/serializekiller.py @@ -1,11 +1,11 @@ #!/usr/bin/env python -#------------------------------------------------------------------------------- +# ------------------------------------------------------------------------------ # Name: SerializeKiller -# Purpose: Finding vulnerable vulnerable servers +# Purpose: Finding vulnerable java servers # # Author: (c) John de Kroon, 2015 # Version: 1.0.2 -#------------------------------------------------------------------------------- +# ------------------------------------------------------------------------------ import subprocess import threading @@ -13,8 +13,8 @@ import socket import sys import argparse -import urllib2 -import ssl +import requests +import re from socket import error as socket_error from datetime import datetime @@ -26,42 +26,32 @@ parser.add_argument('file', nargs='?', help='File with targets') args = parser.parse_args() + def nmap(host, *args): global shellCounter global threads global target_list - # are there any ports defined for this host? + # All ports to enumerate over for jboss, jenkins, weblogic, websphere + port_list = ['80', '81', '443', '444', '1099', '5005', + '7001', '7002', '8080', '8081', '8083', '8443', + '8880', '8888', '9000', '9080', '9443', '16200'] + + # Are there any ports defined for this host? if not target_list[host]: found = False - cmd = 'nmap --host-timeout 5 --open -p 5005,8080,9080,8880,7001,7002,16200 '+host + cmd = 'nmap --host-timeout 5 --open -p %s %s' % (','.join(port_list), host) try: p = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE, shell=True) out, err = p.communicate() - if "5005" in out: - if websphere(host, "5005"): - found = True - if "8880" in out: - if websphere(host, "8880"): - found = True - if "7001" in out: - if weblogic(host, 7001): - found = True - if "16200" in out: - if weblogic(host, 16200): - found = True - if "8080" in out: - if jenkins(host, "8080"): - found = True - if jboss(host, 8080): - found = True - if "9080" in out: - if jenkins(host, "9080"): + + for this_port in port_list: + if websphere(host, this_port) or weblogic(host, this_port) or jboss(host, this_port) or jenkins(host, this_port): found = True if found: shellCounter += 1 - except ValueError: - print " ! Something went wrong on host: "+host + except ValueError, v: + print " ! Something went wrong on host: %s: %s" % (host, v) return else: for port in target_list[host]: @@ -69,16 +59,14 @@ def nmap(host, *args): shellCounter += 1 return + def websphere(url, port, retry=False): try: - ctx = ssl.create_default_context() - ctx.check_hostname = False - ctx.verify_mode = ssl.CERT_NONE - output = urllib2.urlopen('https://'+url+":"+port, context=ctx, timeout=8).read() + output = requests.get('https://'+url+":"+port, timeout=8) if "rO0AB" in output: print " - (possibly) Vulnerable Websphere: "+url+" ("+port+")" return True - except urllib2.HTTPError, e: + except requests.exceptions.HTTPError, e: if e.getcode() == 500: if "rO0AB" in e.read(): print " - (possibly) Vulnerable Websphere: "+url+" ("+port+")" @@ -87,19 +75,20 @@ def websphere(url, port, retry=False): pass try: - output = urllib2.urlopen('http://'+url+":"+port, timeout=3).read() + output = requests.get('http://'+url+":"+port, timeout=3) if "rO0AB" in output: print " - (possibly) Vulnerable Websphere: "+url+" ("+port+")" return True - except urllib2.HTTPError, e: + except requests.exceptions.HTTPError, e: if e.getcode() == 500: if "rO0AB" in e.read(): print " - (possibly) Vulnerable Websphere: "+url+" ("+port+")" return True except: pass - -#Used this part from https://github.com/foxglovesec/JavaUnserializeExploits + + +# Used this part from https://github.com/foxglovesec/JavaUnserializeExploits def weblogic(url, port): try: server_address = (url, int(port)) @@ -111,32 +100,57 @@ def weblogic(url, port): try: data = sock.recv(1024) + print "[debug] Received weblogic data:", data except socket.timeout: return False sock.close() if "HELO" in data: - print " - Vulnerable Weblogic: "+url+" ("+str(port)+")" - return True - return False + # CGP: https://github.com/nmap/nmap/blob/master/scripts/weblogic-t3-info.nse + # weblogic_version = string.match(result, "^HELO:(%d+%.%d+%.%d+%.%d+)%.") + if weblogic_vulnerable(data): + print " - Vulnerable Weblogic: "+url+" ("+str(port)+")" + return True + else: + return False + else: + return False except socket_error: return False -#Used something from https://github.com/foxglovesec/JavaUnserializeExploits +def weblogic_vulnerable(data): + # Very cheap hack + affected_versions = ['10.3.6.0', '12.1.2.0', '12.1.3.0', '12.2.1.0'] + m = re.findall(r"[0-9]+(?:\.[0-9]+){3}", data)[0] + print "[debug] Found Weblogic version:", m + if m in affected_versions: + return True + + return False + + +# Used something from https://github.com/foxglovesec/JavaUnserializeExploits def jenkins(url, port): + # CGP: Check the Jenkins Remote API here: + # https://wiki.jenkins-ci.org/display/JENKINS/Remote+access+API + JENKINS_FIRST_PATCHED_VERSION = 1.638 try: cli_port = False - ctx = ssl.create_default_context() - ctx.check_hostname = False - ctx.verify_mode = ssl.CERT_NONE try: - output = urllib2.urlopen('https://'+url+':'+port+"/jenkins/", context=ctx, timeout=8).info() - cli_port = int(output['X-Jenkins-CLI-Port']) - except urllib2.HTTPError, e: + output = requests.get('https://'+url+':'+port+"/jenkins/", timeout=8) + + # Perform version detection before anything else + jenkins_version = float(output.headers['X-Jenkins']) + if jenkins_version >= JENKINS_FIRST_PATCHED_VERSION: + print "[DEBUG] Patched version of Jenkins (%.3f). Moving on..." % jenkins_version + return False + + cli_port = int(output['X-Jenkins-CLI-Port']) + except requests.exceptions.HTTPError, e: if e.getcode() == 404: try: - output = urllib2.urlopen('https://'+url+':'+port, context=ctx, timeout=8).info() - cli_port = int(output['X-Jenkins-CLI-Port']) + output = requests.get('https://'+url+':'+port, timeout=8) + cli_port = int(output.headers['X-Jenkins-CLI-Port']) except: pass except: @@ -144,31 +158,31 @@ def jenkins(url, port): except: print " ! Could not check Jenkins on https. Maybe your SSL lib is broken." pass - - if cli_port == False: + + if not cli_port: try: - output = urllib2.urlopen('http://'+url+':'+port+"/jenkins/", timeout=8).info() - cli_port = int(output['X-Jenkins-CLI-Port']) - except urllib2.HTTPError, e: + output = requests.get('http://'+url+':'+port+"/jenkins/", timeout=8) + cli_port = int(output.headers['X-Jenkins-CLI-Port']) + except requests.exceptions.HTTPError, e: if e.getcode() == 404: try: - output = urllib2.urlopen('http://'+url+':'+port, timeout=8).info() - cli_port = int(output['X-Jenkins-CLI-Port']) + output = requests.get('http://'+url+':'+port, timeout=8).info() + cli_port = int(output.headers['X-Jenkins-CLI-Port']) except: return False except: return False - - #Open a socket to the CLI port + + # Open a socket to the CLI port try: server_address = (url, cli_port) sock = socket.create_connection(server_address, 5) - + # Send headers headers = '\x00\x14\x50\x72\x6f\x74\x6f\x63\x6f\x6c\x3a\x43\x4c\x49\x2d\x63\x6f\x6e\x6e\x65\x63\x74' sock.send(headers) - data1 =sock.recv(1024) + data1 = sock.recv(1024) if "rO0AB" in data1: print " - Vulnerable Jenkins: "+url+" ("+str(port)+")" return True @@ -181,42 +195,43 @@ def jenkins(url, port): pass return False -def jboss(url, port, retry = False): + +def jboss(url, port, retry=False): try: - ctx = ssl.create_default_context() - ctx.check_hostname = False - ctx.verify_mode = ssl.CERT_NONE - output = urllib2.urlopen('https://'+url+':'+port+"/invoker/JMXInvokerServlet", context=ctx, timeout=8).read() + output = requests.get('https://'+url+':'+port+"/invoker/JMXInvokerServlet", timeout=8) except: try: - output = urllib2.urlopen('http://'+url+':'+port+"/invoker/JMXInvokerServlet", timeout=8).read() + output = requests.get('http://'+url+':'+port+"/invoker/JMXInvokerServlet", timeout=8) except: - #OK. I give up. + # OK. I give up. return False - + if "\xac\xed\x00\x05" in output: print " - Vulnerable JBOSS: "+url+" ("+port+")" return True return False + def urlStripper(url): url = str(url.replace("https:", '')) url = str(url.replace("http:", '')) url = str(url.replace("\r", '')) - url = str(url.replace("\n", '')) + url = str(url.replace("\n", '')) url = str(url.replace("/", '')) return url + def read_file(filename): f = open(filename) content = f.readlines() f.close() return content + def worker(): global threads content = read_file(args.file) - + for line in content: if ":" in line: item = line.strip().split(':') @@ -235,24 +250,23 @@ def worker(): for host in target_list: current += 1 while threading.active_count() > threads: - print " ! We have more threads running than allowed. Current: {} Max: {}.".format(threading.active_count(), - threads) + print " ! We have more threads running than allowed. Current: {} Max: {}.".format(threading.active_count(), threads) if threads < 100: - threads+=1 + threads += 1 sys.stdout.flush() time.sleep(2) print " # Starting test {} of {} on {}.".format(current, total_jobs, host) sys.stdout.flush() threading.Thread(target=nmap, args=(host, False, 1)).start() - #we're done! + # We're done! while threading.active_count() > 2: print " # Waiting for everybody to come back. Still {} active.".format(threading.active_count() - 1) sys.stdout.flush() time.sleep(4) print - print " => scan done. "+str(shellCounter)+" vulnerable hosts found." + print " => Scan done. "+str(shellCounter)+" vulnerable hosts found." print "Execution time: "+str(datetime.now() - startTime) exit() @@ -261,12 +275,6 @@ def worker(): print "Start SerializeKiller..." print "This could take a while. Be patient." print - - try: - ssl.create_default_context() - except: - print " ! WARNING: Your SSL lib isn't supported. Results might be incomplete." - pass target_list = {} shellCounter = 0