def main(): usage = "usage: %prog [options] host1 host2 hostrange\r\n" usage += 'Scans for SIP devices on a given network\r\n\r\n' usage += "examples:\r\n\r\n" usage += "%prog 10.0.0.1-10.0.0.255 " usage += "172.16.131.1 sipvicious.org/22 10.0.1.1/24" usage += "1.1.1.1-20 1.1.2-20.* 4.1.*.*\r\n\r\n" usage += "%prog -s session1 --randomize 10.0.0.1/8\r\n\r\n" usage += "%prog --resume session1 -v\r\n\r\n" usage += "%prog -p5060-5062 10.0.0.3-20 -m INVITE\r\n\r\n" parser = OptionParser(usage, version="%prog v"+str(__version__)+__GPL__) parser.add_option("-p", "--port", dest="port", default="5060", help="Destination port or port ranges of the SIP device - eg -p5060,5061,8000-8100", metavar="PORT") parser = standardoptions(parser) parser = standardscanneroptions(parser) parser.add_option("--randomscan", dest="randomscan", action="store_true", default=False, help="Scan random IP addresses") parser.add_option("-i", "--input", dest="input", help="Scan IPs which were found in a previous scan. Pass the session name as the argument", metavar="scan1") parser.add_option("-I", "--inputtext", dest="inputtext", help="Scan IPs from a text file - use the same syntax as command line but with new lines instead of commas. Pass the file name as the argument", metavar="scan1") parser.add_option("-m", "--method", dest="method", help="Specify the request method - by default this is OPTIONS.", default='OPTIONS') parser.add_option("-d", "--debug", dest="printdebug", help="Print SIP messages received", default=False, action="store_true") parser.add_option("--first", dest="first", help="Only send the first given number of messages (i.e. usually used to scan only X IPs)", type="long") parser.add_option("-e", "--extension", dest="extension", default='100', help="Specify an extension - by default this is not set") parser.add_option("--randomize", dest="randomize", action="store_true", default=False, help="Randomize scanning instead of scanning consecutive ip addresses") parser.add_option("--srv", dest="srvscan", action="store_true", default=False, help="Scan the SRV records for SIP on the destination domain name." \ "The targets have to be domain names - example.org domain1.com") parser.add_option('--fromname',dest="fromname", default="sipvicious", help="specify a name for the from header") parser.add_option('-6', '--ipv6', dest="ipv6", action='store_true', help="scan an IPv6 address") options, args = parser.parse_args() exportpath = None if options.resume is not None: exportpath = os.path.join(os.path.expanduser('~'),'.sipvicious',__prog__,options.resume) if os.path.exists(os.path.join(exportpath,'closed')): logging.error("Cannot resume a session that is complete") exit(1) if not os.path.exists(exportpath): logging.critical('A session with the name %s was not found'% options.resume) exit(1) optionssrc = os.path.join(exportpath,'options.pkl') previousresume = options.resume previousverbose = options.verbose options,args = pickle.load(open(optionssrc,'rb'), encoding='bytes') options.resume = previousresume options.verbose = previousverbose elif options.save is not None: exportpath = os.path.join(os.path.expanduser('~'),'.sipvicious',__prog__,options.save) logging.basicConfig(level=calcloglevel(options)) logging.debug('started logging') scanrandomstore = None if options.input is not None: db = os.path.join(os.path.expanduser('~'),'.sipvicious',__prog__,options.input,'resultua') if dbexists(db): scaniter = scanfromdb(db,options.method.split(',')) else: logging.error("the session name does not exist. Please use svreport to list existing scans") exit(1) elif options.randomscan: logging.debug('making use of random scan') logging.debug('parsing range of ports: %s' % options.port) portrange = getRange(options.port) internetranges =[[16777216,167772159], [184549376,234881023], [251658240,2130706431], [2147549184,2851995647], [2852061184,2886729727], [2886795264,3221159935], [3221226240,3227017983], [3227018240,3232235519], [3232301056,3323068415], [3323199488,3758096127] ] scanrandomstore = '.sipviciousrandomtmp' resumescan = False if options.save is not None: scanrandomstore = os.path.join(exportpath,'random') resumescan = True scaniter = scanrandom( internetranges, portrange, options.method.split(','), randomstore=scanrandomstore, resume=resumescan ) elif options.inputtext: logging.debug('Using IP addresses from input text file') try: f = open(options.inputtext,'r') args = f.readlines() f.close() except IOError: logging.critical('Could not open %s' % options.inputtext) exit(1) args = list(map(lambda x: x.strip(), args)) args = [x for x in args if len(x) > 0] logging.debug('ip addresses %s' % args) try: iprange = ip4range(*args) except ValueError as err: logging.error(err) exit(1) portrange = getRange(options.port) if options.randomize: scanrandomstore = '.sipviciousrandomtmp' resumescan = False if options.save is not None: scanrandomstore = os.path.join(exportpath,'random') resumescan = True scaniter = scanrandom(list(map(getranges,args)),portrange, options.method.split(','),randomstore=scanrandomstore,resume=resumescan) else: scaniter = scanlist(iprange,portrange,options.method.split(',')) else: if len(args) < 1: parser.error('Provide at least one target') exit(1) logging.debug('parsing range of ports: %s' % options.port) portrange = getRange(options.port) if options.randomize: scanrandomstore = '.sipviciousrandomtmp' resumescan = False if options.save is not None: scanrandomstore = os.path.join(exportpath,'random') resumescan = True scaniter = scanrandom(list(map(getranges,args)),portrange, options.method.split(','),randomstore=scanrandomstore,resume=resumescan) elif options.srvscan: logging.debug("making use of SRV records") scaniter = getTargetFromSRV(args,options.method.split(',')) else: if options.resume is not None: lastipsrc = os.path.join(exportpath,'lastip.pkl') try: f=open(lastipsrc,'rb') previousip = pickle.load(f, encoding='bytes') f.close() except IOError: logging.critical('Could not read from %s' % lastipsrc) exit(1) logging.debug('Previous args: %s' % args) args = resumeFromIP(previousip,args) logging.debug('New args: %s' % args) logging.info('Resuming from %s' % previousip) if options.ipv6: scaniter = scanlist(ip6range(*args), portrange, options.method.split(',')) else: # normal consecutive scan try: iprange = ip4range(*args) except ValueError as err: logging.error(err) exit(1) scaniter = scanlist(iprange,portrange,options.method.split(',')) if options.save is not None: if options.resume is None: exportpath = os.path.join(os.path.expanduser('~'),'.sipvicious',__prog__,options.save) if os.path.exists(exportpath): logging.warning('we found a previous scan with the same name. Please choose a new session name') exit(1) logging.debug('creating an export location %s' % exportpath) try: os.makedirs(exportpath,mode=0o700) except OSError: logging.critical('could not create the export location %s' % exportpath) exit(1) optionsdst = os.path.join(exportpath,'options.pkl') logging.debug('saving options to %s' % optionsdst) pickle.dump([options,args],open(optionsdst,'wb+')) try: options.extension except AttributeError: options.extension = None if options.autogetip: tmpsocket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) tmpsocket.connect(("msn.com",80)) options.externalip=tmpsocket.getsockname()[0] tmpsocket.close() sipvicious = DrinkOrSip( scaniter, selecttime=options.selecttime, compact=options.enablecompact, localport=options.localport, externalip=options.externalip, bindingip=options.bindingip, sessionpath=exportpath, extension=options.extension, printdebug=options.printdebug, first=options.first, fromname=options.fromname, ipv6=options.ipv6, ) start_time = datetime.now() logging.info( "start your engines" ) try: try: sipvicious.start() except AssertionError as err: logging.critical(err) exit(1) if exportpath is not None: open(os.path.join(exportpath,'closed'),'w').close() except KeyboardInterrupt: logging.warning( 'caught your control^c - quiting' ) pass except Exception as err: if options.reportBack: logging.critical( "Got unhandled exception : sending report to author" ) reportBugToAuthor(traceback.format_exc()) else: logging.critical( "Unhandled exception - please run same command with the -R option to send me an automated report") pass logging.exception( "Exception" ) if options.save is not None and sipvicious.nextip is not None and options.randomize is False and options.randomscan is False: lastipdst = os.path.join(exportpath,'lastip.pkl') logging.debug('saving state to %s' % lastipdst) try: f = open(lastipdst,'wb+') pickle.dump(sipvicious.nextip,f) f.close() except OSError: logging.warning('Could not save state to %s' % lastipdst) elif options.save is None: if scanrandomstore is not None: #if options.randomize or options.randomscan: try: logging.debug('removing %s' % scanrandomstore) os.unlink(scanrandomstore) except OSError: logging.warning('could not remove %s' % scanrandomstore) pass # display results if not options.quiet: lenres = len(sipvicious.resultua) if lenres > 0: logging.info("we have %s devices" % lenres) if (lenres < 400 and options.save is not None) or options.save is None: labels = ('SIP Device','User Agent') rows = list() try: for k in sipvicious.resultua.keys(): rows.append((k.decode(),sipvicious.resultua[k].decode())) except AttributeError: for k in sipvicious.resultua.keys(): rows.append((k,sipvicious.resultua[k])) print(to_string(rows, header=labels)) else: logging.warning("too many to print - use svreport for this") else: logging.warning("found nothing") end_time = datetime.now() total_time = end_time - start_time logging.info("Total time: %s" % total_time)
def main(): usage = "usage: %prog [options] target\r\n" usage += "examples:\r\n" usage += "%prog -e100-999 10.0.0.1\r\n" usage += "%prog -d dictionary.txt 10.0.0.2\r\n" parser = OptionParser(usage, version="%prog v" + str(__version__) + __GPL__) parser.add_option("-p", "--port", dest="port", default="5060", help="Destination port of the SIP device - eg -p 5060", metavar="PORT") parser = standardoptions(parser) parser = standardscanneroptions(parser) parser.add_option("-d", "--dictionary", dest="dictionary", type="string", help="specify a dictionary file with possible extension names", metavar="DICTIONARY") parser.add_option("-m", "--method", dest="method", type="string", help="specify a request method. The default is REGISTER. Other possible methods are OPTIONS and INVITE", default="REGISTER", metavar="OPTIONS") parser.add_option("-e", "--extensions", dest="range", default='100-999', help="specify an extension or extension range\r\nexample: -e 100-999,1000-1500,9999", metavar="RANGE") parser.add_option("-z", "--zeropadding", dest="zeropadding", type="int", help="""the number of zeros used to padd the username. the options "-e 1-9999 -z 4" would give 0001 0002 0003 ... 9999""", default=0, metavar="PADDING") parser.add_option('--force', dest="force", action="store_true", default=False, help="Force scan, ignoring initial sanity checks.") parser.add_option('--template', '-T', action="store", dest="template", help="""A format string which allows us to specify a template for the extensions example svwar.py -e 1-999 --template="123%#04i999" would scan between 1230001999 to 1230999999" """) parser.add_option('--enabledefaults', '-D', action="store_true", dest="defaults", default=False, help="""Scan for default / typical extensions such as 1000,2000,3000 ... 1100, etc. This option is off by default. Use --enabledefaults to enable this functionality""") parser.add_option('--maximumtime', action='store', dest='maximumtime', type="int", default=10, help="""Maximum time in seconds to keep sending requests without receiving a response back""") parser.add_option('--domain', dest="domain", help="force a specific domain name for the SIP message, eg. -d example.org") parser.add_option("--debug", dest="printdebug", help="Print SIP messages received", default=False, action="store_true" ) parser.add_option('-6', dest="ipv6", action="store_true", help="scan an IPv6 address") (options, args) = parser.parse_args() global exportpath exportpath = None logging.basicConfig(level=calcloglevel(options)) logging.debug('started logging') if options.force: initialcheck = False else: initialcheck = True if options.template is not None: try: options.template % 1 except TypeError: logging.critical( "The format string template is not correct. Please provide an appropiate one") exit(1) if options.resume is not None: exportpath = os.path.join(os.path.expanduser( '~'), '.sipvicious', __prog__, options.resume) if os.path.exists(os.path.join(exportpath, 'closed')): logging.error("Cannot resume a session that is complete") exit(1) if not os.path.exists(exportpath): logging.critical( 'A session with the name %s was not found' % options.resume) exit(1) optionssrc = os.path.join(exportpath, 'options.pkl') previousresume = options.resume previousverbose = options.verbose options, args = pickle.load(open(optionssrc, 'rb'), encoding='bytes') options.resume = previousresume options.verbose = previousverbose elif options.save is not None: exportpath = os.path.join(os.path.expanduser( '~'), '.sipvicious', __prog__, options.save) if len(args) != 1: parser.error("provide one hostname") else: host = args[0] if options.dictionary is not None: guessmode = 2 try: dictionary = open(options.dictionary, 'r', encoding='utf-8', errors='ignore') except IOError: logging.error("could not open %s" % options.dictionary) exit(1) if options.resume is not None: lastextensionsrc = os.path.join(exportpath, 'lastextension.pkl') previousposition = pickle.load(open(lastextensionsrc, 'rb'), encoding='bytes') dictionary.seek(previousposition) guessargs = dictionary else: guessmode = 1 if options.resume is not None: lastextensionsrc = os.path.join(exportpath, 'lastextension.pkl') try: previousextension = pickle.load(open(lastextensionsrc, 'rb'), encoding='bytes') except IOError: logging.critical('Could not read from %s' % lastextensionsrc) exit(1) logging.debug('Previous range: %s' % options.range) options.range = resumeFrom(previousextension, options.range) logging.debug('New range: %s' % options.range) logging.info('Resuming from %s' % previousextension) extensionstotry = getRange(options.range) guessargs = (extensionstotry, options.zeropadding, options.template, options.defaults) if options.save is not None: if options.resume is None: exportpath = os.path.join(os.path.expanduser( '~'), '.sipvicious', __prog__, options.save) if os.path.exists(exportpath): logging.warning( 'we found a previous scan with the same name. Please choose a new session name') exit(1) logging.debug('creating an export location %s' % exportpath) try: os.makedirs(exportpath, mode=0o700) except OSError: logging.critical( 'could not create the export location %s' % exportpath) exit(1) optionsdst = os.path.join(exportpath, 'options.pkl') logging.debug('saving options to %s' % optionsdst) pickle.dump([options, args], open(optionsdst, 'wb+')) if options.autogetip: tmpsocket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) tmpsocket.connect(("msn.com", 80)) options.externalip = tmpsocket.getsockname()[0] tmpsocket.close() enableack = False if options.method.upper() == 'INVITE': enableack = True sipvicious = TakeASip( host, port=options.port, selecttime=options.selecttime, method=options.method, compact=options.enablecompact, guessmode=guessmode, guessargs=guessargs, sessionpath=exportpath, initialcheck=initialcheck, externalip=options.externalip, enableack=enableack, maxlastrecvtime=options.maximumtime, localport=options.localport, domain=options.domain, printdebug=options.printdebug, ipv6=options.ipv6, ) start_time = datetime.now() #logging.info("scan started at %s" % str(start_time)) logging.info("start your engines") try: sipvicious.start() if exportpath is not None: open(os.path.join(exportpath, 'closed'), 'w').close() except KeyboardInterrupt: logging.warning('caught your control^c - quiting') except Exception as err: if options.reportBack: logging.critical( "Got unhandled exception : %s\nSending report to author" % err.__str__()) reportBugToAuthor(traceback.format_exc()) else: logging.critical( "Unhandled exception - please run same command with the -R option to send me an automated report") pass logging.exception("Exception") if options.save is not None and sipvicious.nextuser is not None: lastextensiondst = os.path.join(exportpath, 'lastextension.pkl') logging.debug('saving state to %s' % lastextensiondst) try: if guessmode == 1: pickle.dump(sipvicious.nextuser, open( os.path.join(exportpath, 'lastextension.pkl'), 'wb')) logging.debug('logged last extension %s' % sipvicious.nextuser) elif guessmode == 2: pickle.dump(sipvicious.guessargs.tell(), open( os.path.join(exportpath, 'lastextension.pkl'), 'wb')) logging.debug('logged last position %s' % sipvicious.guessargs.tell()) except IOError: logging.warning('could not log the last extension scanned') # display results if not options.quiet: lenres = len(sipvicious.resultauth) if lenres > 0: logging.info("we have %s extensions" % lenres) if (lenres < 400 and options.save is not None) or options.save is None: labels = ('Extension', 'Authentication') rows = list() try: for k in sipvicious.resultauth.keys(): rows.append((k.decode(), sipvicious.resultauth[k].decode())) except AttributeError: for k in sipvicious.resultauth.keys(): rows.append((k, sipvicious.resultauth[k])) print(to_string(rows, header=labels)) else: logging.warning("too many to print - use svreport for this") else: logging.warning("found nothing") end_time = datetime.now() total_time = end_time - start_time logging.info("Total time: %s" % total_time)
def main(): usage = "usage: %prog -u username [options] target\r\n" usage += "examples:\r\n" usage += "%prog -u100 -d dictionary.txt 10.0.0.1\r\n" usage += "%prog -u100 -r1-9999 -z4 10.0.0.1\r\n" parser = OptionParser(usage, version="%prog v" + str(__version__) + __GPL__) parser.add_option("-p", "--port", dest="port", default="5060", help="Destination port of the SIP device - eg -p 5060", metavar="PORT") parser = standardoptions(parser) parser = standardscanneroptions(parser) parser.add_option("-u", "--username", dest="username", help="username to try crack", metavar="USERNAME") parser.add_option("-d", "--dictionary", dest="dictionary", type="string", help="specify a dictionary file with passwords", metavar="DICTIONARY") parser.add_option("-r", "--range", dest="range", default="100-999", help="specify a range of numbers. example: 100-200,300-310,400", metavar="RANGE") parser.add_option("-e", "--extension", dest="extension", help="Extension to crack. Only specify this when the extension is different from the username.", metavar="EXTENSION") parser.add_option("-z", "--zeropadding", dest="zeropadding", type="int", default=0, help="""the number of zeros used to padd the password. the options "-r 1-9999 -z 4" would give 0001 0002 0003 ... 9999""", metavar="PADDING") parser.add_option("-n", "--reusenonce", dest="reusenonce", default=False, help="Reuse nonce. Some SIP devices don't mind you reusing the nonce (making them vulnerable to replay attacks). Speeds up the cracking.", action="store_true", ) parser.add_option('--template', '-T', action="store", dest="template", help="""A format string which allows us to specify a template for the extensions example svwar.py -e 1-999 --template="123%#04i999" would scan between 1230001999 to 1230999999" """) parser.add_option('--maximumtime', action='store', dest='maximumtime', type="int", default=10, help="Maximum time in seconds to keep sending requests without receiving a response back") parser.add_option('--enabledefaults', '-D', action="store_true", dest="defaults", default=False, help="""Scan for default / typical passwords such as 1000,2000,3000 ... 1100, etc. This option is off by default. Use --enabledefaults to enable this functionality""") parser.add_option('--domain', dest="domain", help="force a specific domain name for the SIP message, eg. example.org") parser.add_option('--requesturi', dest="requesturi", help="force the first line URI to a specific value; e.g. sip:[email protected]") parser.add_option('-6', dest="ipv6", action="store_true", help="Scan an IPv6 address") parser.add_option('-m','--method', dest='method', default='REGISTER', help="Specify a SIP method to use") (options, args) = parser.parse_args() exportpath = None logging.basicConfig(level=calcloglevel(options)) logging.debug('started logging') if options.resume is not None: exportpath = os.path.join(os.path.expanduser( '~'), '.sipvicious', __prog__, options.resume) if os.path.exists(os.path.join(exportpath, 'closed')): logging.error("Cannot resume a session that is complete") exit(1) if not os.path.exists(exportpath): logging.critical( 'A session with the name %s was not found' % options.resume) exit(1) optionssrc = os.path.join(exportpath, 'options.pkl') previousresume = options.resume previousverbose = options.verbose options, args = pickle.load(open(optionssrc, 'rb'), encoding='bytes') options.resume = previousresume options.verbose = previousverbose elif options.save is not None: exportpath = os.path.join(os.path.expanduser( '~'), '.sipvicious', __prog__, options.save) logging.debug('Session path: %s' % exportpath) if options.resume is not None: exportpath = os.path.join(os.path.expanduser( '~'), '.sipvicious', __prog__, options.resume) if not os.path.exists(exportpath): logging.critical( 'A session with the name %s was not found' % options.resume) exit(1) optionssrc = os.path.join(exportpath, 'options.pkl') previousresume = options.resume previousverbose = options.verbose options, args = pickle.load(open(optionssrc, 'rb'), encoding='bytes') options.resume = previousresume options.verbose = previousverbose elif options.save is not None: exportpath = os.path.join(os.path.expanduser( '~'), '.sipvicious', __prog__, options.save) if len(args) != 1: parser.error("provide one hostname") else: host = args[0] if options.username is None: parser.error("provide one username to crack") if options.dictionary is not None: crackmode = 2 try: dictionary = open(options.dictionary, 'r', encoding='utf-8', errors='ignore') except IOError: logging.error("could not open %s" % options.dictionary) if options.resume is not None: lastpasswdsrc = os.path.join(exportpath, 'lastpasswd.pkl') previousposition = pickle.load(open(lastpasswdsrc, 'rb'), encoding='bytes') dictionary.seek(previousposition) crackargs = dictionary else: crackmode = 1 if options.resume is not None: lastpasswdsrc = os.path.join(exportpath, 'lastpasswd.pkl') try: previouspasswd = pickle.load(open(lastpasswdsrc, 'rb'), encoding='bytes') except IOError: logging.critical('Could not read from %s' % lastpasswdsrc) exit(1) logging.debug('Previous range: %s' % options.range) options.range = resumeFrom(previouspasswd, options.range) logging.debug('New range: %s' % options.range) logging.info('Resuming from %s' % previouspasswd) rangelist = getRange(options.range) crackargs = (rangelist, options.zeropadding, options.template, options.defaults, [options.username]) if options.save is not None: if options.resume is None: exportpath = os.path.join(os.path.expanduser( '~'), '.sipvicious', __prog__, options.save) if os.path.exists(exportpath): logging.warning( 'we found a previous scan with the same name. Please choose a new session name') exit(1) logging.debug('creating an export location %s' % exportpath) try: os.makedirs(exportpath, mode=0o700) except OSError: logging.critical( 'could not create the export location %s' % exportpath) exit(1) optionsdst = os.path.join(exportpath, 'options.pkl') logging.debug('saving options to %s' % optionsdst) pickle.dump([options, args], open(optionsdst, 'wb+')) if options.autogetip: tmpsocket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) tmpsocket.connect(("msn.com", 80)) options.externalip = tmpsocket.getsockname()[0] tmpsocket.close() sipvicious = ASipOfRedWine( host, username=options.username, selecttime=options.selecttime, compact=options.enablecompact, crackmode=crackmode, crackargs=crackargs, reusenonce=options.reusenonce, extension=options.extension, sessionpath=exportpath, port=options.port, externalip=options.externalip, maxlastrecvtime=options.maximumtime, localport=options.localport, domain=options.domain, requesturi=options.requesturi, ipv6=options.ipv6, method=options.method, ) start_time = datetime.now() logging.info("scan started at %s" % str(start_time)) try: sipvicious.start() if exportpath is not None: open(os.path.join(exportpath, 'closed'), 'w').close() except KeyboardInterrupt: logging.warning('caught your control^c - quiting') except Exception as err: if options.reportBack: logging.critical( "Got unhandled exception : %s\nsending report to author" % err.__str__()) reportBugToAuthor(traceback.format_exc()) else: logging.critical( "Unhandled exception - please run same command with the -R option to send me an automated report") pass logging.exception("Exception") if options.save is not None and sipvicious.previouspassword is not None: lastextensiondst = os.path.join(exportpath, 'lastpasswd.pkl') logging.debug('saving state to %s' % lastextensiondst) try: if crackmode == 1: pickle.dump(sipvicious.previouspassword, open( os.path.join(exportpath, 'lastpasswd.pkl'), 'wb+')) logging.debug('logged last password %s' % sipvicious.previouspassword) elif crackmode == 2: pickle.dump(sipvicious.crackargs.tell(), open( os.path.join(exportpath, 'lastpasswd.pkl'), 'wb+')) logging.debug('logged last position %s' % sipvicious.crackargs.tell()) except IOError: logging.warning('could not log the last tried password') # display results if not options.quiet: lenres = len(sipvicious.resultpasswd) if lenres > 0: logging.info("we have %s cracked users" % lenres) if (lenres < 400 and options.save is not None) or options.save is None: labels = ('Extension', 'Password') rows = list() try: for k in sipvicious.resultpasswd.keys(): rows.append((k.decode(), sipvicious.resultpasswd[k].decode())) except AttributeError: for k in sipvicious.resultpasswd.keys(): rows.append((k, sipvicious.resultpasswd[k])) print(to_string(rows, header=labels)) else: logging.warning("too many to print - use svreport for this") else: logging.warning("found nothing") end_time = datetime.now() total_time = end_time - start_time logging.info("Total time: %s" % total_time)