├── README.md └── hashMonitor.py /README.md: -------------------------------------------------------------------------------- 1 | TekDefense-hashMonitor 2 | ====================== 3 | 4 | Collect hashes password hashes for cracking 5 | 6 | 7 | All TekDefense projects use the MIT License model, which basically means, use any of this as you see fit, but don't hold me responsible for any loss or damage that occurs from using or modifying any of these projects. 8 | 9 | Please visit www.TekDefense.com for articles, and instructional videos on InfoSec topics 10 | 11 | @TekDefense on Twitter. 12 | 13 | Email: 1aN0rmus@TekDefense.com 14 | 15 | Thank you, 16 | 17 | -Ian 18 | -------------------------------------------------------------------------------- /hashMonitor.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | ''' 4 | This is hashMonitor! This tool will collect hashes from data breeches reported via Twitter and specific web resources. 5 | This works by checking popular accounts on twitter like @Dumpmon and @PastebinDorks and web resources. 6 | hashMonitor will go to each link they tweet and scrape out MD5, SHA1, and SHA256 Hashes. 7 | 8 | @TekDefense 9 | Ian Ahl | www.TekDefense.com | 1aN0rmus@tekDefense.com 10 | 11 | Version: 0.3.2 12 | 13 | Changelog: 14 | 15 | .3 16 | The twitter API now requires an API to view tweets. To avoid having 17 | eveyone generate an API key I instead modified the code to use the 18 | twitter website. 19 | [+] See above 20 | [+] Proxy support 21 | [+] URLLIB2 instead of httplib2 22 | [+] Spelling corrections, thanks @tekwizz123 23 | [+] Removed the import and the functions for the twitter API 24 | 25 | 26 | .2 27 | [+] Optimize database for faster hash insertion 28 | [+] Added option to specify a database name and path 29 | [+] Added error handling to the hash insert to DB 30 | [+] Optimized link2DB function and added error handling 31 | [+] Fixed a problem with listing hashes 32 | [+] Added a summary option 33 | [+] Started an account function, not done yet though! 34 | [+] Fixed a bug in the hashes2DB that gave an incorrect count on how many hashes were added to the database 35 | [+] Added a function to remove hashes from the database. Will take a .pot or any other text file with hashes in it. 36 | [+] Added monitoring of of web resources as well. Mostly using @AndrewMohawk's pasteLert. 37 | 38 | .1 39 | [+] Initial Release 40 | 41 | TODO 42 | 43 | [-] Collect the real URL as well as the shortened one. 44 | ''' 45 | 46 | import sqlite3, re, datetime, httplib2, argparse, sys, urllib, urllib2 47 | 48 | listMonitor = ['Dumpmon', 'PastebinDorks', 'TekDefense'] 49 | listURLMonitor = ['https://twitter.com/PastebinDorks', 'https://twitter.com/dumpmon', 'http://www.leakedin.com/'] 50 | listURLs = [] 51 | hashMonDB = 'hashMon.db' 52 | today = datetime.datetime.now() 53 | now = today.strftime("%Y-%m-%d %H:%M") 54 | MD5 = '([a-fA-F0-9]{32})\W' 55 | SHA1 = '([a-fA-F0-9]{40})\W' 56 | SHA256 = '([a-fA-F0-9]{64})\W' 57 | listTypes = [('MD5',MD5),('SHA1',SHA1), ('SHA256',SHA256),] 58 | listResults = [] 59 | listNewURLs = [] 60 | hashType = '' 61 | hashList = False 62 | 63 | parser = argparse.ArgumentParser(description='hashMonitor is a tool that will collect hashes from data breeches reported via Twitter') 64 | parser.add_argument('-d', '--database', help='This option is used to specify a database name. ./hashMonitor.py -d databaseName.db') 65 | parser.add_argument('-o', '--output', help='This option will output the results to a file. ./hashMonitor.py -o output.txt') 66 | parser.add_argument('-l', '--list', help='This option will return a list of all the hashes in the database. Use ANY, MD5, SHA1, or SHA256. ./hashMonitor.py -l MD5') 67 | parser.add_argument('-s', '--summary', action='store_true', default=False, help='This option will display stats on URLs scanned and Hashes collected ./hashMonitor.py -s') 68 | parser.add_argument('-a', '--add', help='This option will add a twitter account to the monitor db ./hashMonitor.py -a TWITTERHANDLE') 69 | parser.add_argument('-r', '--remove', help='This option will remove hashes from the database from any text base file that includes hashes like a .pot file ./hashMonitor.py -r hashcat.pot') 70 | args = parser.parse_args() 71 | 72 | if args.output: 73 | oFile = args.output 74 | print '[+] Printing results to file:', args.output 75 | o = open(oFile, "w") 76 | sys.stdout = o 77 | 78 | if args.list: 79 | hashList = True 80 | if args.list.upper() == 'ANY' or args.list.upper() == 'MD5' or args.list.upper() == 'SHA1' or args.list.upper() == 'SHA256': 81 | hashType = args.list.upper() 82 | else: 83 | print '[!] You must choose -l ANY, -l MD5, =l SHA1, or -l SHA256' 84 | sys.exit() 85 | 86 | if args.database: 87 | hashMonDB = args.database 88 | 89 | def webLinkPull(): 90 | global listURLs 91 | for i in listURLMonitor: 92 | url = i 93 | print url 94 | try: 95 | proxy = urllib2.ProxyHandler() 96 | opener = urllib2.build_opener(proxy) 97 | response = opener.open(url) 98 | content = response.read() 99 | contentString = str(content) 100 | regURL = 'http:\/\/www\.pastebin.com\/\w{1,12}' 101 | regURL2 = 'http:\/\/pastebin.com\/raw\.php...\w{1,10}' 102 | regURLComp = re.compile(regURL) 103 | regexURLSearch = re.findall(regURLComp, contentString) 104 | regURLComp2 = re.compile(regURL2) 105 | regexURLSearch2 = re.findall(regURLComp2, contentString) 106 | for i in regexURLSearch: 107 | listURLs.append(i) 108 | for i in regexURLSearch2: 109 | listURLs.append(i) 110 | except: 111 | print '[-] Unable to pull results for ' + i 112 | 113 | def links2DB(): 114 | print '[*] Adding links to the DB if they have not been scanned previously.' 115 | con = sqlite3.connect(hashMonDB) 116 | with con: 117 | cur = con.cursor() 118 | cur.execute('CREATE TABLE IF NOT EXISTS URLs(URL TEXT PRIMARY KEY, DATE TEXT)') 119 | con.commit() 120 | n = 0 121 | for i in listURLs: 122 | try: 123 | cur.execute("INSERT INTO URLs(URL, DATE) VALUES(?,?)", (i, now)) 124 | listNewURLs.append(i) 125 | print '[+] Adding ' + i + ' into the DB' 126 | except: 127 | n = n + 1 128 | if n > 0: 129 | print '[-] ' + str(n) + ' links were previously scanned' 130 | con.commit() 131 | con.close() 132 | 133 | def collectHashes(): 134 | global listResults 135 | if len(listNewURLs) > 0: 136 | print '[+] Searching for hashes in the new URLs' 137 | for URL in listNewURLs: 138 | try: 139 | proxy = urllib2.ProxyHandler() 140 | opener = urllib2.build_opener(proxy) 141 | response = opener.open(URL) 142 | content = response.read() 143 | contentString = str(content) 144 | for reg in listTypes: 145 | regVal = reg[1] 146 | regexValue = re.compile(regVal) 147 | regexSearch = re.findall(regexValue,contentString) 148 | for result in regexSearch: 149 | listResults.append((result, reg)) 150 | except: 151 | print '[-] Unable to collect hashes for ' + URL 152 | listResults = list(set(listResults)) 153 | else: 154 | print '[-] No new URLs to search' 155 | 156 | def hashes2DB(): 157 | print '[*] Inserting new hashes to the DB if any are found.' 158 | con = sqlite3.connect(hashMonDB) 159 | n = 0 160 | with con: 161 | cur = con.cursor() 162 | cur.execute('CREATE TABLE IF NOT EXISTS HASHES(HASH TEXT PRIMARY KEY, TYPE TEXT)') 163 | con.commit() 164 | for i in listResults: 165 | try: 166 | cur.execute("INSERT INTO HASHES(HASH, TYPE) VALUES(?,?)", (i[0], i[1][0])) 167 | n = n + 1 168 | print '[+] Adding ' + i[0] + ' to the DB' 169 | except: 170 | print '[-] ' + i[0] + ' already exists in database' 171 | print '[+] Added ' + str(n) + ' Hashes to the Database' 172 | con.commit() 173 | con.close() 174 | 175 | def listHashes(): 176 | if hashList == True: 177 | con = sqlite3.connect(hashMonDB) 178 | with con: 179 | cur = con.cursor() 180 | if hashType == 'MD5' or hashType == 'SHA1' or hashType == 'SHA256': 181 | cur.execute('SELECT HASH FROM HASHES where TYPE = ?', (hashType, )) 182 | results = cur.fetchall() 183 | for i in results: 184 | print i[0] 185 | else: 186 | cur.execute('SELECT HASH FROM HASHES') 187 | results = cur.fetchall() 188 | for i in results: 189 | print i[0] 190 | 191 | 192 | def summary(): 193 | con = sqlite3.connect(hashMonDB) 194 | with con: 195 | cur = con.cursor() 196 | cur.execute('SELECT TYPE, COUNT(HASH) FROM HASHES GROUP BY TYPE') 197 | rows = cur.fetchall() 198 | num = 0 199 | for row in rows: 200 | num = num + row[1] 201 | print '[+] ' + row[0] + ': ' + str(row[1]) 202 | print '[+] You have collected a total of ' + str(num) + ' hashes in this database!' 203 | cur.execute('SELECT URL, COUNT(URL) FROM URLS') 204 | rows = cur.fetchall() 205 | num = 0 206 | for row in rows: 207 | num = num + row[1] 208 | print '[+] You have scraped a total of ' + str(num) + ' URLs listed in this database!' 209 | 210 | def hashRemove(): 211 | print '[*] Checking to see if there are any matches between the database and ' + potFile + '. Any matches will be removed from the database!' 212 | global listResults 213 | fileImport =open(potFile) 214 | strFile='' 215 | for line in fileImport: 216 | strFile += line 217 | regVal = MD5 218 | regexValue = re.compile(regVal) 219 | regexSearch = re.findall(regexValue,strFile) 220 | listResults = [] 221 | for j in regexSearch: 222 | listResults.append(j) 223 | listResults = list(set(listResults)) 224 | con = sqlite3.connect(hashMonDB) 225 | with con: 226 | n = 0 227 | cur = con.cursor() 228 | for i in listResults: 229 | cur.execute('SELECT HASH FROM HASHES WHERE HASH = ?', (i, )) 230 | row = cur.fetchone() 231 | if row != None: 232 | print '[-] ' + row[0] + ' is being removed from the database' 233 | cur.execute('DELETE FROM HASHES WHERE HASH = ?', (row[0], )) 234 | n = n + 1 235 | print '[+] Removed ' + str(n) + ' hashes from the database' 236 | 237 | print '[*] Running hashMonitor.py' 238 | 239 | 240 | if args.list: 241 | listHashes() 242 | elif args.summary == True: 243 | summary() 244 | elif args.remove: 245 | potFile = args.remove 246 | hashRemove() 247 | else: 248 | #accounts() 249 | #twitterLinkPull() 250 | webLinkPull() 251 | links2DB() 252 | collectHashes() 253 | hashes2DB() 254 | --------------------------------------------------------------------------------