mirror of
				https://github.com/mgeeky/Penetration-Testing-Tools.git
				synced 2025-11-04 04:55:26 +01:00 
			
		
		
		
	Multiple enhancements to networkConfigurationCredentialsExtract.py
This commit is contained in:
		@@ -3,8 +3,9 @@
 | 
			
		||||
#
 | 
			
		||||
# Script intendend to sweep Cisco, Huawei and possibly other network devices 
 | 
			
		||||
# configuration files in order to extract plain and cipher passwords out of them.
 | 
			
		||||
# Equipped with functionality to decrypt Cisco Type 7 passwords.
 | 
			
		||||
#
 | 
			
		||||
# Mariusz B., mgeeky '18
 | 
			
		||||
# Mariusz B., mgeeky '18-20
 | 
			
		||||
#
 | 
			
		||||
 | 
			
		||||
import re
 | 
			
		||||
@@ -45,6 +46,10 @@ regexes = {
 | 
			
		||||
        'ISAKMP Pre-Shared Key'             : r'crypto isakmp key \password(?: address \ip)?',
 | 
			
		||||
        'SNMP-Server User Auth & Encr keys' : r'snmp-server user \name .* encrypted auth md5 ([0-9a-f\:]+) priv aes \d+ ([0-9a-f\:]+)',
 | 
			
		||||
        'PPP PAP Sent Username & Password'  : r'ppp pap sent-username \name password \password',
 | 
			
		||||
        'AAA TACACS+/RADIUS Server Private' : r'server-private \ip key \password',
 | 
			
		||||
        'AAA TACACS+ Server Private'        : r'tacacs-server key \password',
 | 
			
		||||
        'SNMP Server Community string'      : r'snmp-server community \password',
 | 
			
		||||
        'IPSec VPN ISAKMP Pre-Shared Key'   : r'pre-shared-key address \ip key \password'
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    'Cisco ASA' : {
 | 
			
		||||
@@ -88,6 +93,7 @@ regexes = {
 | 
			
		||||
        'Other uncategorized XML password'  : r'password>([^<]+)<',
 | 
			
		||||
        'Other uncategorized authentication string' : r'.* authentication \password.*',
 | 
			
		||||
        'Other hash-key related'            : r'.* key \hash',
 | 
			
		||||
        'Cisco 7 Password'                  : r'\cisco7',
 | 
			
		||||
    },
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -95,9 +101,12 @@ config = {
 | 
			
		||||
    'verbose' : False,  
 | 
			
		||||
    'debug' : False,
 | 
			
		||||
    'lines' : 0,
 | 
			
		||||
    'output' : 'normal',
 | 
			
		||||
    'format' : 'normal',
 | 
			
		||||
    'csv_delimiter' : ';',
 | 
			
		||||
    'no_others' : False,
 | 
			
		||||
    'filename' : False,
 | 
			
		||||
    'nonunique' : False,
 | 
			
		||||
    'output' : ''
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
markers = {
 | 
			
		||||
@@ -106,7 +115,8 @@ markers = {
 | 
			
		||||
    'domain' : r'(([a-zA-Z]{1})|([a-zA-Z]{1}[a-zA-Z]{1})|([a-zA-Z]{1}[0-9]{1})|([0-9]{1}[a-zA-Z]{1})|([a-zA-Z0-9][a-zA-Z0-9-_]{1,61}[a-zA-Z0-9]))\.([a-zA-Z]{2,6}|[a-zA-Z0-9-]{2,30}\.[a-zA-Z]{2,3})',
 | 
			
		||||
    'hash' : r'([a-fA-F0-9]{20,})',
 | 
			
		||||
    'bcrypt' : r'([\$\w\.\/]+)',
 | 
			
		||||
    'password': r'(?:\d\s+)?([^\s]+)',
 | 
			
		||||
    'password': r'(?:(?:\d\s+)?([^\s]+))',
 | 
			
		||||
    'cisco7' : r'\b(?:7 ([0-9a-f]{4,}))|(?:([0-9a-f]{4,}) 7)\b',
 | 
			
		||||
    'keystring': r'([a-f0-9]+)',
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -115,7 +125,7 @@ foundCreds = set()
 | 
			
		||||
maxTechnologyWidth = 0
 | 
			
		||||
maxRegexpWidth = 0
 | 
			
		||||
 | 
			
		||||
results = set()
 | 
			
		||||
results = []
 | 
			
		||||
 | 
			
		||||
class Logger:
 | 
			
		||||
    @staticmethod
 | 
			
		||||
@@ -156,7 +166,53 @@ def processRegex(inputRegex):
 | 
			
		||||
    inputRegex = '^\\s*{}\\s*.*$'.format(inputRegex)
 | 
			
		||||
    return inputRegex
 | 
			
		||||
 | 
			
		||||
def matchLines(lines, technology):
 | 
			
		||||
def cisco7Decrypt(data):
 | 
			
		||||
    # source: https://github.com/theevilbit/ciscot7
 | 
			
		||||
    xlat = [
 | 
			
		||||
        0x64, 0x73, 0x66, 0x64, 0x3b, 0x6b, 0x66, 0x6f, 0x41, 0x2c, 0x2e, 
 | 
			
		||||
        0x69, 0x79, 0x65, 0x77, 0x72, 0x6b, 0x6c, 0x64, 0x4a, 0x4b, 0x44, 
 | 
			
		||||
        0x48, 0x53, 0x55, 0x42, 0x73, 0x67, 0x76, 0x63, 0x61, 0x36, 0x39, 
 | 
			
		||||
        0x38, 0x33, 0x34, 0x6e, 0x63, 0x78, 0x76, 0x39, 0x38, 0x37, 0x33, 
 | 
			
		||||
        0x32, 0x35, 0x34, 0x6b, 0x3b, 0x66, 0x67, 0x38, 0x37
 | 
			
		||||
    ]
 | 
			
		||||
 | 
			
		||||
    dp = ''
 | 
			
		||||
    regex = re.compile(r'(^[0-9A-Fa-f]{2})([0-9A-Fa-f]+)')
 | 
			
		||||
    result = regex.search(data)
 | 
			
		||||
    try:
 | 
			
		||||
        if result:
 | 
			
		||||
            s, e = int(result.group(1)), result.group(2)
 | 
			
		||||
            for pos in range(0, len(e), 2):
 | 
			
		||||
                magic = int(e[pos] + e[pos+1], 16)
 | 
			
		||||
                newchar = ''
 | 
			
		||||
                if s <= 50:
 | 
			
		||||
                    # xlat length is 51
 | 
			
		||||
                    newchar = '%c' % (magic ^ xlat[s])
 | 
			
		||||
                    s += 1
 | 
			
		||||
                if s == 51: s = 0
 | 
			
		||||
                dp += newchar
 | 
			
		||||
            return dp
 | 
			
		||||
        return ''
 | 
			
		||||
    except:
 | 
			
		||||
        return ''
 | 
			
		||||
 | 
			
		||||
def tryToCisco7Decrypt(creds):
 | 
			
		||||
    if not len(creds):
 | 
			
		||||
        return ''
 | 
			
		||||
 | 
			
		||||
    decrypted = []
 | 
			
		||||
    for m in re.finditer(markers['cisco7'], creds, re.I):
 | 
			
		||||
        f = m.group(2) if m.group(2) != None else m.group(1)
 | 
			
		||||
        out = cisco7Decrypt(f)
 | 
			
		||||
        if out:
 | 
			
		||||
            decrypted.append(out)
 | 
			
		||||
    
 | 
			
		||||
    if len(decrypted):
 | 
			
		||||
        return " (decrypted cisco 7: '" + "', '".join(decrypted) + "')"
 | 
			
		||||
 | 
			
		||||
    return ''
 | 
			
		||||
 | 
			
		||||
def matchLines(file, lines, technology):
 | 
			
		||||
    global foundCreds
 | 
			
		||||
    global results
 | 
			
		||||
 | 
			
		||||
@@ -166,7 +222,7 @@ def matchLines(lines, technology):
 | 
			
		||||
        for idx in range(len(lines)):   
 | 
			
		||||
            line = lines[idx].strip()
 | 
			
		||||
 | 
			
		||||
            if line in foundCreds: 
 | 
			
		||||
            if not config['nonunique'] and line in foundCreds: 
 | 
			
		||||
                continue
 | 
			
		||||
 | 
			
		||||
            processedRex = processRegex(regexes[technology][rex])
 | 
			
		||||
@@ -175,28 +231,28 @@ def matchLines(lines, technology):
 | 
			
		||||
                num += 1
 | 
			
		||||
 | 
			
		||||
                foundCreds.add(line)
 | 
			
		||||
                creds = '", "'.join(matched.groups(1))
 | 
			
		||||
                f = [x for x in matched.groups(1) if type(x) == str]
 | 
			
		||||
                creds = '", "'.join(f)
 | 
			
		||||
                creds += tryToCisco7Decrypt(line)
 | 
			
		||||
                
 | 
			
		||||
                results.add((
 | 
			
		||||
                    technology, rex, creds
 | 
			
		||||
                results.append((
 | 
			
		||||
                    file, technology, rex, creds
 | 
			
		||||
                ))
 | 
			
		||||
 | 
			
		||||
                Logger._out('[+] {}: {}: {}'.format(
 | 
			
		||||
                    technology, rex, creds
 | 
			
		||||
                ))
 | 
			
		||||
 | 
			
		||||
                if idx - config['lines'] >= 0:
 | 
			
		||||
                    for i in range(idx - config['lines'], idx):
 | 
			
		||||
                        Logger._out('[{:04}]\t\t{}'.format(i, lines[i]))
 | 
			
		||||
                        
 | 
			
		||||
                if config['lines'] != 0:
 | 
			
		||||
                    Logger._out('[{:04}]==>\t{}'.format(idx, line))
 | 
			
		||||
                else:
 | 
			
		||||
                    Logger._out('[{:04}]\t\t{}'.format(idx, line))
 | 
			
		||||
                    Logger._out('\n[+] {}: {}: {}'.format(
 | 
			
		||||
                        technology, rex, creds
 | 
			
		||||
                    ))
 | 
			
		||||
 | 
			
		||||
                if idx + 1 + config['lines'] < len(lines):
 | 
			
		||||
                    for i in range(idx + 1, idx + config['lines'] + 1):
 | 
			
		||||
                        Logger._out('[{:04}]\t\t{}'.format(i, lines[i]))
 | 
			
		||||
                    if idx - config['lines'] >= 0:
 | 
			
		||||
                        for i in range(idx - config['lines'], idx):
 | 
			
		||||
                            Logger._out('[{:04}]\t\t{}'.format(i, lines[i]))
 | 
			
		||||
                            
 | 
			
		||||
                    Logger._out('[{:04}]==>\t{}'.format(idx, line))
 | 
			
		||||
 | 
			
		||||
                    if idx + 1 + config['lines'] < len(lines):
 | 
			
		||||
                        for i in range(idx + 1, idx + config['lines'] + 1):
 | 
			
		||||
                            Logger._out('[{:04}]\t\t{}'.format(i, lines[i]))
 | 
			
		||||
 | 
			
		||||
                Logger.dbg('\tRegex used: [ {} ]'.format(processedRex))
 | 
			
		||||
    return num
 | 
			
		||||
@@ -205,21 +261,23 @@ def processFile(file):
 | 
			
		||||
    lines = []
 | 
			
		||||
 | 
			
		||||
    Logger.info('Processing file: "{}"'.format(file))
 | 
			
		||||
    with open(file, 'r') as f:
 | 
			
		||||
        lines = [ line.strip() for line in f.readlines()]
 | 
			
		||||
    try:
 | 
			
		||||
        with open(file, 'r') as f:
 | 
			
		||||
            lines = [ line.strip() for line in f.readlines()]
 | 
			
		||||
    except Exception as e:
 | 
			
		||||
        Logger.err("Parsing file '{}' failed: {}.".format(file, str(e)))
 | 
			
		||||
        return 0
 | 
			
		||||
 | 
			
		||||
    num = 0
 | 
			
		||||
    for technology in regexes:
 | 
			
		||||
        if technology == 'Others':
 | 
			
		||||
            continue
 | 
			
		||||
 | 
			
		||||
        num0 = matchLines(lines, technology)
 | 
			
		||||
        num0 = matchLines(file, lines, technology)
 | 
			
		||||
        num += num0
 | 
			
		||||
 | 
			
		||||
    if not config['no_others']:
 | 
			
		||||
        num0 = matchLines(lines, 'Others')
 | 
			
		||||
        if num0 == 0:
 | 
			
		||||
            print('<none>')
 | 
			
		||||
        num0 = matchLines(file, lines, 'Others')
 | 
			
		||||
        num += num0
 | 
			
		||||
 | 
			
		||||
    return num
 | 
			
		||||
@@ -237,11 +295,14 @@ def processDir(dirname):
 | 
			
		||||
def parseOptions(argv):
 | 
			
		||||
    parser = argparse.ArgumentParser(prog = argv[0], usage='%(prog)s [options] <file>')
 | 
			
		||||
    parser.add_argument('file', metavar='<file>', type=str, help='Config file or directory to process.')
 | 
			
		||||
    parser.add_argument('-o', '--output', help = 'Output file.')
 | 
			
		||||
    parser.add_argument('-H', '--with-filename', action='store_true', help = 'Print file name next to the results')
 | 
			
		||||
    parser.add_argument('-R', '--show-nonunique', action='store_true', help = 'Print repeated, non unique credentials found. By default only unique references are returned.')
 | 
			
		||||
    parser.add_argument('-C', '--lines', metavar='N', type=int, default=0, help='Display N lines around matched credential if verbose output is enabled.')
 | 
			
		||||
    parser.add_argument('-f', '--format', choices=['raw', 'normal', 'tabular', 'csv'], default='normal', help="Specifies output format: 'raw' (only hashes), 'tabular', 'normal', 'csv'. Default: 'normal'")
 | 
			
		||||
    parser.add_argument('-N', '--no-others', dest='no_others', action='store_true', help='Don\'t match "Others" category which is false-positives prone.')
 | 
			
		||||
    parser.add_argument('-v', '--verbose', action='store_true', help='Display verbose output.')
 | 
			
		||||
    parser.add_argument('-d', '--debug', action='store_true', help='Display debug output.')
 | 
			
		||||
    parser.add_argument('-v', '--verbose', action='store_true', default=False, help='Display verbose output.')
 | 
			
		||||
    parser.add_argument('-d', '--debug', action='store_true', default=False, help='Display debug output.')
 | 
			
		||||
 | 
			
		||||
    if len(argv) < 2:
 | 
			
		||||
        parser.print_help()
 | 
			
		||||
@@ -253,32 +314,40 @@ def parseOptions(argv):
 | 
			
		||||
    config['debug'] = args.debug
 | 
			
		||||
    config['lines'] = args.lines
 | 
			
		||||
    config['no_others'] = args.no_others
 | 
			
		||||
    config['filename'] = args.with_filename
 | 
			
		||||
    config['nonunique'] = args.show_nonunique
 | 
			
		||||
    config['output'] = args.output
 | 
			
		||||
 | 
			
		||||
    if args.format == 'raw':
 | 
			
		||||
        config['output'] = 'raw'
 | 
			
		||||
        config['format'] = 'raw'
 | 
			
		||||
    elif args.format == 'tabular':
 | 
			
		||||
        config['output'] = 'tabular'
 | 
			
		||||
        config['format'] = 'tabular'
 | 
			
		||||
    elif args.format == 'csv':
 | 
			
		||||
        config['output'] = 'csv'
 | 
			
		||||
        config['format'] = 'csv'
 | 
			
		||||
    else:
 | 
			
		||||
        config['output'] == 'normal'
 | 
			
		||||
        config['format'] == 'normal'
 | 
			
		||||
 | 
			
		||||
    return args
 | 
			
		||||
 | 
			
		||||
def printResults():
 | 
			
		||||
    global maxTechnologyWidth
 | 
			
		||||
    global maxRegexpWidth
 | 
			
		||||
    global results
 | 
			
		||||
 | 
			
		||||
    # CSV Columns
 | 
			
		||||
    cols = ['technology', 'name', 'hashes']
 | 
			
		||||
    cols = ['file', 'technology', 'name', 'hashes']
 | 
			
		||||
 | 
			
		||||
    def _print(technology, rex, creds):
 | 
			
		||||
        if config['output'] == 'tabular':
 | 
			
		||||
            print('[+] {0: <{width1}} {1:^{width2}}: "{2:}"'.format(
 | 
			
		||||
    if not config['nonunique']:
 | 
			
		||||
        results = set(results)
 | 
			
		||||
 | 
			
		||||
    def _print(file, technology, rex, creds):
 | 
			
		||||
        out = ''
 | 
			
		||||
        if config['format'] == 'tabular':
 | 
			
		||||
            out += '[+] {0: <{width1}} {1:^{width2}}: "{2:}"\n'.format(
 | 
			
		||||
                technology, rex, creds,
 | 
			
		||||
                width1 = maxTechnologyWidth, width2 = maxRegexpWidth
 | 
			
		||||
            ))
 | 
			
		||||
        elif config['output'] == 'raw':
 | 
			
		||||
            )
 | 
			
		||||
        elif config['format'] == 'raw':
 | 
			
		||||
            credstab = creds.split('", "')
 | 
			
		||||
            longest = ''
 | 
			
		||||
 | 
			
		||||
@@ -286,24 +355,26 @@ def printResults():
 | 
			
		||||
                if len(passwd) > len(longest):
 | 
			
		||||
                    longest = passwd
 | 
			
		||||
 | 
			
		||||
            print('{}'.format(
 | 
			
		||||
            out += '{}\n'.format(
 | 
			
		||||
                passwd
 | 
			
		||||
            ))
 | 
			
		||||
        elif config['output'] == 'csv':
 | 
			
		||||
            )
 | 
			
		||||
        elif config['format'] == 'csv':
 | 
			
		||||
            creds = '"{}"'.format(creds)
 | 
			
		||||
            rex = rex.replace(config['csv_delimiter'], ' ')
 | 
			
		||||
            #creds = creds.replace(config['csv_delimiter'], ' ')
 | 
			
		||||
            print(config['csv_delimiter'].join([technology, rex, creds]))
 | 
			
		||||
            out += config['csv_delimiter'].join([file, technology, rex, creds])
 | 
			
		||||
            out += '\n'
 | 
			
		||||
        else:
 | 
			
		||||
            print('[+] {}: {}: "{}"'.format(
 | 
			
		||||
            out += '[+] {}: {}: "{}"\n'.format(
 | 
			
		||||
                technology, rex, creds
 | 
			
		||||
            ))
 | 
			
		||||
            )
 | 
			
		||||
        
 | 
			
		||||
        return out
 | 
			
		||||
 | 
			
		||||
    maxTechnologyWidth = 0
 | 
			
		||||
    maxRegexpWidth = 0
 | 
			
		||||
 | 
			
		||||
    for result in results:
 | 
			
		||||
        technology, rex, creds = result
 | 
			
		||||
        file, technology, rex, creds = result
 | 
			
		||||
        if len(technology) > maxTechnologyWidth:
 | 
			
		||||
            maxTechnologyWidth = len(technology)
 | 
			
		||||
 | 
			
		||||
@@ -313,23 +384,45 @@ def printResults():
 | 
			
		||||
    maxTechnologyWidth = maxTechnologyWidth + 3
 | 
			
		||||
    maxRegexpWidth = maxRegexpWidth + 3
 | 
			
		||||
 | 
			
		||||
    if config['output'] == 'normal' or config['output'] == 'tabular':
 | 
			
		||||
        print('\n=== CREDENTIALS FOUND:')
 | 
			
		||||
    elif config['output'] == 'csv':
 | 
			
		||||
        print(config['csv_delimiter'].join(cols))
 | 
			
		||||
    outputToPrint = ''
 | 
			
		||||
 | 
			
		||||
    if config['format'] == 'normal' or config['format'] == 'tabular':
 | 
			
		||||
        outputToPrint += '\n=== CREDENTIALS FOUND:\n'
 | 
			
		||||
    elif config['format'] == 'csv':
 | 
			
		||||
        outputToPrint += config['csv_delimiter'].join(cols)
 | 
			
		||||
        outputToPrint += '\n'
 | 
			
		||||
 | 
			
		||||
    resultsPerFile = {}
 | 
			
		||||
    otherResultsPerFile = {}
 | 
			
		||||
    for result in results:
 | 
			
		||||
        technology, rex, creds = result
 | 
			
		||||
        if technology == 'Others': continue
 | 
			
		||||
        _print(technology, rex, creds)
 | 
			
		||||
        file, technology, rex, creds = result
 | 
			
		||||
        if technology == 'Others':
 | 
			
		||||
            if file not in otherResultsPerFile.keys():
 | 
			
		||||
                otherResultsPerFile[file] = []
 | 
			
		||||
            otherResultsPerFile[file].append((technology, rex, creds))
 | 
			
		||||
        else:
 | 
			
		||||
            if file not in resultsPerFile.keys():
 | 
			
		||||
                resultsPerFile[file] = []
 | 
			
		||||
            resultsPerFile[file].append((technology, rex, creds))
 | 
			
		||||
 | 
			
		||||
    if not config['no_others'] and (config['output'] == 'normal' or config['output'] == 'tabular'):
 | 
			
		||||
        print('\n=== BELOW LINES MAY BE FALSE POSITIVES:')
 | 
			
		||||
    for file, _results in resultsPerFile.items():
 | 
			
		||||
        if config['filename'] and config['format'] in ['raw', 'normal', 'tabular']: 
 | 
			
		||||
            outputToPrint += '\nResults from file: "{}"\n'.format(file)
 | 
			
		||||
        for result in _results:
 | 
			
		||||
            technology, rex, creds = result
 | 
			
		||||
            outputToPrint += _print(file, technology, rex, creds)
 | 
			
		||||
 | 
			
		||||
    for result in results:
 | 
			
		||||
        technology, rex, creds = result
 | 
			
		||||
        if technology != 'Others': continue
 | 
			
		||||
        _print(technology, rex, creds)
 | 
			
		||||
    if not config['no_others'] and (config['format'] == 'normal' or config['format'] == 'tabular'):
 | 
			
		||||
        outputToPrint += '\n\n=== BELOW LINES MAY BE FALSE POSITIVES:\n'
 | 
			
		||||
 | 
			
		||||
    for file, _results in otherResultsPerFile.items():
 | 
			
		||||
        if config['filename'] and config['format'] in ['raw', 'normal', 'tabular']: 
 | 
			
		||||
            outputToPrint += '\nResults from file: "{}"\n'.format(file)
 | 
			
		||||
        for result in _results:
 | 
			
		||||
            technology, rex, creds = result
 | 
			
		||||
            outputToPrint += _print(file, technology, rex, creds)
 | 
			
		||||
 | 
			
		||||
    return outputToPrint
 | 
			
		||||
 | 
			
		||||
def main(argv):
 | 
			
		||||
    Logger._out('''
 | 
			
		||||
@@ -356,9 +449,16 @@ def main(argv):
 | 
			
		||||
        Logger.err('Please provide either file or directory on input.')
 | 
			
		||||
        return False
 | 
			
		||||
 | 
			
		||||
    printResults()
 | 
			
		||||
    out = printResults()
 | 
			
		||||
 | 
			
		||||
    if config['output'] == 'normal' or config['output'] == 'tabular':
 | 
			
		||||
    if config['output']:
 | 
			
		||||
        Logger.info("Dumping credentials to the output file: '{}'".format(config['output']))
 | 
			
		||||
        with open(config['output'], 'w') as f:
 | 
			
		||||
            f.write(out)
 | 
			
		||||
    else:
 | 
			
		||||
        print(out)
 | 
			
		||||
 | 
			
		||||
    if config['format'] == 'normal' or config['format'] == 'tabular':
 | 
			
		||||
        print('\n[>] Found: {} credentials.'.format(num))
 | 
			
		||||
 | 
			
		||||
if __name__ == '__main__':
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user