changelog v20150901:
+ Domains are validated after permutation process + Updated example reports
Dnstwist is a tools for Generate and resolve domain variations to detect typo squatting, phishing and corporate espionage.
![Example Usage DNSTwist]()
Example Usage DNSTwist
Required modules
If you want dnstwist to develop full power, please make sure the following Python modules are present on your system. If missing, dnstwist will still work, but without some cool features.
+ Python GeoIP https://pypi.python.org/pypi/GeoIP/
+ A DNS toolkit for Python http://www.dnspython.org/
+ WHOIS https://pypi.python.org/pypi/whois
This tool has been tested on windows XP/7/Vista/8.1/10 and All Unix Environment.
Example report_google :
Processing 89 domains !...!.......!!..!!!.!....!.!.!!!!.!!!!!!!!!!!!!!!!!..!!!.!!!!!!..!!..!!.!!!!!!!!!!..!.!!!
Bitsquatting foogle.com 64.111.126.107
Bitsquatting eoogle.com -
Bitsquatting coogle.com -
Bitsquatting ooogle.com -
Bitsquatting woogle.com 98.124.199.1
Bitsquatting gnogle.com -
Bitsquatting gmogle.com -
Bitsquatting gkogle.com -
Bitsquatting ggogle.com -
Bitsquatting gongle.com -
Bitsquatting gomgle.com -
Bitsquatting gokgle.com -
Bitsquatting goggle.com 104.156.226.89
Bitsquatting goofle.com 69.89.22.115
Bitsquatting gooele.com -
Bitsquatting goocle.com -
Bitsquatting gooole.com 98.124.199.1
Bitsquatting goowle.com 54.68.76.21
Bitsquatting googme.com 199.59.243.120
Bitsquatting googne.com -
Bitsquatting googhe.com 199.59.243.120
Bitsquatting googde.com -
Bitsquatting googld.com -
Bitsquatting googlg.com -
Bitsquatting googla.com -
Bitsquatting googlm.com 98.126.223.220
Bitsquatting googlu.com -
Homoglyph g0ogle.com 98.124.198.1
Homoglyph go0gle.com -
Homoglyph googie.com 209.237.151.18
Repetition ggoogle.com 46.28.247.113
Repetition gooogle.com 46.28.247.109
Repetition gooogle.com 46.28.247.93
Repetition googgle.com -
Repetition googlle.com 96.126.106.126
Repetition googlee.com 46.28.247.114
Replacement ogogle.com 46.28.247.109
Replacement google.com 46.28.247.99
Replacement gogole.com 46.28.247.94
Replacement goolge.com 46.28.247.119
Replacement googel.com 46.28.247.84
Omission oogle.com 109.123.198.149
Omission gogle.com 46.28.247.94
Omission gogle.com 46.28.247.108
Omission goole.com 87.106.83.127
Omission googe.com 162.243.20.86
Omission googl.com 46.28.247.98
Insertion g0oogle.com 185.2.66.16
Insertion go0ogle.com 5.39.99.51
Insertion gpoogle.com 64.15.205.100
Insertion gopogle.com 209.15.13.134
Insertion gloogle.com -
Insertion gologle.com -
Insertion gkoogle.com 50.63.202.7
Insertion gokogle.com 103.224.182.253
Insertion gioogle.com 208.87.34.163
Insertion goiogle.com -
Insertion g9oogle.com 185.53.177.8
Insertion go9ogle.com 199.59.243.120
Insertion go0ogle.com 5.39.99.51
Insertion goo0gle.com 103.224.182.244
Insertion gopogle.com 209.15.13.134
Insertion goopgle.com 69.162.80.56
Insertion gologle.com -
Insertion goolgle.com -
Insertion gokogle.com 103.224.182.253
Insertion gookgle.com 103.224.182.210
Insertion goiogle.com -
Insertion gooigle.com -
Insertion go9ogle.com 199.59.243.120
Insertion goo9gle.com 185.2.66.16
Insertion gooygle.com -
Insertion googyle.com 103.224.182.252
Insertion goohgle.com 83.64.127.75
Insertion googhle.com 5.39.99.51
Insertion goobgle.com 103.224.182.249
Insertion googble.com 69.163.201.152
Insertion goovgle.com 199.59.243.120
Insertion googvle.com 103.224.182.243
Insertion goofgle.com 208.73.210.200
Insertion googfle.com 103.224.182.241
Insertion gootgle.com 103.224.182.244
Insertion googtle.com -
Insertion googkle.com -
Insertion googlke.com 98.124.198.1
Insertion googole.com -
Insertion googloe.com 209.15.13.134
Insertion googple.com 199.59.243.120
Insertion googlpe.com 103.224.182.243
Example report_twitter :
Processing 113 domains !!..!!!!.!!!!!...!.!.!.!.!..!.!..!!.!!!!!!!!!!!!.!!.!!!!!!!!!!!.!!!!!!!!!!!!!!!!!!!!!!...!.!!!.!!.!.!..!!!.!.!!.!
Bitsquatting uwitter.com 72.52.4.120
Bitsquatting vwitter.com 54.68.76.21
Bitsquatting pwitter.com -
Bitsquatting dwitter.com -
Bitsquatting tvitter.com 5.22.149.135
Bitsquatting tuitter.com 208.73.210.200
Bitsquatting tsitter.com 50.63.202.35
Bitsquatting tgitter.com 192.40.56.146
Bitsquatting twhtter.com -
Bitsquatting twktter.com 103.224.182.241
Bitsquatting twmtter.com 54.68.76.21
Bitsquatting twatter.com 208.82.16.68
Bitsquatting twytter.com 184.168.221.96
Bitsquatting twiuter.com 185.53.177.8
Bitsquatting twivter.com -
Bitsquatting twipter.com -
Bitsquatting twidter.com -
Bitsquatting twituer.com 72.52.4.119
Bitsquatting twitver.com -
Bitsquatting twitper.com 162.255.119.246
Bitsquatting twitder.com -
Bitsquatting twittdr.com 72.52.4.119
Bitsquatting twittgr.com -
Bitsquatting twittar.com 96.126.106.126
Bitsquatting twittmr.com -
Bitsquatting twittur.com 198.187.31.153
Bitsquatting twittes.com -
Bitsquatting twittep.com -
Bitsquatting twittev.com 184.187.12.126
Bitsquatting twittez.com -
Bitsquatting twitteb.com 54.68.76.21
Homoglyph tvvitter.com -
Homoglyph twltter.com -
Repetition ttwitter.com 95.211.117.206
Repetition twwitter.com 95.211.117.206
Repetition twiitter.com -
Repetition twittter.com 199.59.148.82
Repetition twittter.com 199.59.148.82
Repetition twitteer.com 62.116.130.8
Repetition twitterr.com 95.211.117.206
Replacement wtitter.com 95.211.117.206
Replacement tiwtter.com 54.75.246.166
Replacement twtiter.com 208.73.210.200
Replacement twitter.com 199.16.156.198
Replacement twitetr.com 95.211.117.206
Replacement twittre.com 95.211.117.206
Omission witter.com 66.147.244.205
Omission titter.com 84.22.98.192
Omission twtter.com -
Omission twiter.com 199.16.156.70
Omission twiter.com 199.16.156.70
Omission twittr.com -
Omission twitte.com 66.33.208.125
Insertion t3witter.com 198.40.51.109
Insertion tw3itter.com 50.63.202.8
Insertion tewitter.com 95.211.117.206
Insertion tweitter.com 69.162.80.53
Insertion tswitter.com 31.170.164.149
Insertion twsitter.com 184.168.221.29
Insertion tawitter.com 69.162.80.54
Insertion twaitter.com 184.168.221.8
Insertion tqwitter.com 95.211.117.206
Insertion twqitter.com 199.59.243.120
Insertion t2witter.com -
Insertion tw2itter.com 50.63.202.12
Insertion tw9itter.com 199.59.243.120
Insertion twi9tter.com 185.53.179.6
Insertion twoitter.com 208.73.211.178
Insertion twiotter.com 103.224.182.241
Insertion twkitter.com 184.168.221.11
Insertion twiktter.com 185.53.177.9
Insertion twjitter.com 184.168.221.96
Insertion twijtter.com 199.59.243.120
Insertion twuitter.com 103.224.182.243
Insertion twiutter.com 95.211.117.206
Insertion tw8itter.com 184.168.221.21
Insertion twi8tter.com 185.53.179.9
Insertion twi6tter.com 184.171.252.34
Insertion twit6ter.com 208.73.211.178
Insertion twiytter.com 162.218.54.42
Insertion twityter.com 8.5.1.37
Insertion twigtter.com 199.59.243.120
Insertion twitgter.com 50.63.202.15
Insertion twiftter.com 72.52.4.119
Insertion twitfter.com 148.251.19.202
Insertion twirtter.com 116.212.117.220
Insertion twitrter.com -
Insertion twi5tter.com -
Insertion twit5ter.com -
Insertion twit6ter.com 208.73.210.200
Insertion twitt6er.com -
Insertion twityter.com 8.5.1.37
Insertion twittyer.com 185.53.178.6
Insertion twitgter.com 50.63.202.15
Insertion twittger.com -
Insertion twitfter.com 148.251.19.202
Insertion twittfer.com 208.73.210.214
Insertion twitrter.com -
Insertion twittrer.com 69.162.80.53
Insertion twit5ter.com -
Insertion twitt5er.com 198.40.51.109
Insertion twitt4er.com -
Insertion twitte4r.com -
Insertion twittrer.com 69.162.80.53
Insertion twitterr.com 95.211.117.206
Insertion twittder.com 208.73.210.217
Insertion twittedr.com -
Insertion twittser.com 208.73.210.214
Insertion twittesr.com -
Insertion twittwer.com 103.1.175.248
Insertion twittewr.com 199.59.243.120
Insertion twitt3er.com -
Insertion twitte3r.com 50.63.202.12
Example report_facebook :
Processing 120 domains ..!!!!!!.!!!!..!!!!.!!!!!!!!!.!..!.!!.!.....!!!!!!!!...!.!!!!!!!!!!!!.!.!...!!!...!!.!!.....!.!!!!.!!!!.!!!!!!!!!!.!!.!!
Bitsquatting gacebook.com -
Bitsquatting dacebook.com -
Bitsquatting bacebook.com 116.212.117.220
Bitsquatting nacebook.com 68.65.123.248
Bitsquatting vacebook.com 103.224.182.241
Bitsquatting fccebook.com 103.224.182.252
Bitsquatting fecebook.com 146.148.34.125
Bitsquatting ficebook.com 103.224.182.245
Bitsquatting fqcebook.com -
Bitsquatting fabebook.com 208.87.150.50
Bitsquatting faaebook.com 174.139.64.188
Bitsquatting fagebook.com 199.59.243.120
Bitsquatting fakebook.com 98.131.4.39
Bitsquatting fasebook.com -
Bitsquatting facdbook.com -
Bitsquatting facgbook.com 185.53.179.9
Bitsquatting facabook.com 208.87.150.50
Bitsquatting facmbook.com 8.5.1.31
Bitsquatting facubook.com 199.59.243.120
Bitsquatting facecook.com -
Bitsquatting facefook.com 199.59.243.120
Bitsquatting facejook.com 103.224.182.251
Bitsquatting facerook.com 75.126.102.246
Bitsquatting facebnok.com 103.224.182.252
Bitsquatting facebmok.com 54.68.76.21
Bitsquatting facebkok.com 96.126.106.126
Bitsquatting facebgok.com 54.68.76.21
Bitsquatting facebonk.com 23.254.217.113
Bitsquatting facebomk.com 208.73.210.214
Bitsquatting facebokk.com -
Bitsquatting facebogk.com 54.68.76.21
Bitsquatting facebooj.com -
Bitsquatting facebooi.com -
Bitsquatting facebooo.com 162.255.119.114
Bitsquatting facebooc.com -
Homoglyph faceb0ok.com 199.59.243.120
Homoglyph facebo0k.com 75.126.104.241
Repetition ffacebook.com -
Repetition faacebook.com 173.252.120.6
Repetition faccebook.com -
Repetition faceebook.com -
Repetition facebbook.com -
Repetition faceboook.com -
Repetition faceboook.com -
Repetition facebookk.com 127.0.0.1
Replacement afcebook.com 96.126.106.126
Replacement fcaebook.com 173.252.120.6
Replacement faecbook.com 52.0.7.30
Replacement facbeook.com 96.126.106.126
Replacement faceobok.com 173.252.120.6
Replacement facebook.com 173.252.120.6
Replacement faceboko.com 185.53.177.20
Omission acebook.com -
Omission fcebook.com -
Omission faebook.com -
Omission facbook.com 173.252.120.6
Omission faceook.com -
Omission facebok.com 173.252.120.6
Omission facebok.com 173.252.120.6
Omission faceboo.com 173.252.120.6
Insertion fqacebook.com 185.53.177.9
Insertion faqcebook.com 72.52.4.119
Insertion fwacebook.com 103.224.182.214
Insertion fawcebook.com 209.15.13.134
Insertion fsacebook.com 68.65.123.151
Insertion fascebook.com 208.73.210.217
Insertion fzacebook.com 74.200.250.181
Insertion fazcebook.com 199.59.243.120
Insertion faxcebook.com 184.168.221.15
Insertion facxebook.com -
Insertion fadcebook.com 103.224.182.241
Insertion facdebook.com -
Insertion fafcebook.com 208.73.210.200
Insertion facfebook.com -
Insertion favcebook.com -
Insertion facvebook.com -
Insertion fac4ebook.com 103.224.182.214
Insertion face4book.com 209.15.13.134
Insertion facrebook.com 103.224.182.252
Insertion facerbook.com -
Insertion facdebook.com -
Insertion facedbook.com -
Insertion facsebook.com 74.200.250.181
Insertion facesbook.com 103.224.182.241
Insertion facwebook.com -
Insertion facewbook.com 208.73.211.178
Insertion fac3ebook.com 198.12.15.244
Insertion face3book.com -
Insertion facevbook.com -
Insertion facebvook.com -
Insertion facegbook.com -
Insertion facebgook.com -
Insertion facehbook.com 199.59.243.120
Insertion facebhook.com -
Insertion facenbook.com 72.52.4.119
Insertion facebnook.com 103.224.182.241
Insertion faceb0ook.com 103.224.182.214
Insertion facebo0ok.com 146.148.34.125
Insertion facebpook.com -
Insertion facebopok.com 103.224.182.241
Insertion faceblook.com 103.224.182.241
Insertion facebolok.com 208.73.210.217
Insertion facebkook.com 199.59.243.120
Insertion facebokok.com -
Insertion facebiook.com 103.224.182.241
Insertion faceboiok.com 146.148.34.125
Insertion faceb9ook.com 208.91.196.126
Insertion facebo9ok.com 185.53.177.20
Insertion facebo0ok.com 54.210.47.225
Insertion faceboo0k.com 103.224.182.214
Insertion facebopok.com 103.224.182.241
Insertion faceboopk.com 103.224.182.252
Insertion facebolok.com 208.73.210.214
Insertion faceboolk.com 146.148.34.125
Insertion facebokok.com -
Insertion facebookk.com 127.0.0.1
Insertion faceboiok.com 146.148.34.125
Insertion facebooik.com -
Insertion facebo9ok.com 185.53.177.20
Insertion faceboo9k.com 208.73.210.217
Dnstwist Script.py:
#!/usr/bin/env python
#
# dnstwist by marcin@ulikowski.pl
# Generate and resolve domain variations to detect typo squatting,
# phishing and corporate espionage.
#
#
# dnstwist is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# dnstwist is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with dnstwist. If not, see <http://www.gnu.org/licenses/>.
__author__ = 'Marcin Ulikowski'
__version__ = '20150901'
__email__ = 'marcin@ulikowski.pl'
import re
import sys
import socket
import signal
import argparse
try:
import dns.resolver
module_dnspython = True
except:
module_dnspython = False
pass
try:
import GeoIP
module_geoip = True
except:
module_geoip = False
pass
try:
import whois
module_whois = True
except:
module_whois = False
pass
def sigint_handler(signal, frame):
sys.exit(0)
# Internationalized domains not supported
def validate_domain(domain):
if len(domain) > 255:
return False
if domain[-1] == '.':
domain = domain[:-1]
allowed = re.compile('\A([a-z0-9]+(-[a-z0-9]+)*\.)+[a-z]{2,}\Z', re.IGNORECASE)
return allowed.match(domain)
def bitsquatting(domain):
out = []
dom = domain.rsplit('.', 1)[0]
tld = domain.rsplit('.', 1)[1]
masks = [1, 2, 4, 8, 16, 32, 64, 128]
for i in range(0, len(dom)):
c = dom[i]
for j in range(0, len(masks)):
b = chr(ord(c) ^ masks[j])
o = ord(b)
if (o >= 48 and o <= 57) or (o >= 97 and o <= 122) or o == 45:
out.append(dom[:i] + b + dom[i+1:] + '.' + tld)
return out
def homoglyph(domain):
glyphs = {
'd':['b', 'cl'], 'm':['n', 'rn'], 'l':['1', 'i'], 'o':['0'],
'w':['vv'], 'n':['m'], 'b':['d'], 'i':['l'], 'g':['q'], 'q':['g']
}
out = []
dom = domain.rsplit('.', 1)[0]
tld = domain.rsplit('.', 1)[1]
for ws in range(0, len(dom)):
for i in range(0, (len(dom)-ws)+1):
win = dom[i:i+ws]
j = 0
while j < ws:
c = win[j]
if c in glyphs:
for g in range(0, len(glyphs[c])):
win = win[:j] + glyphs[c][g] + win[j+1:]
if len(glyphs[c][g]) > 1:
j += len(glyphs[c][g]) - 1
out.append(dom[:i] + win + dom[i+ws:] + '.' + tld)
j += 1
return list(set(out))
def repetition(domain):
out = []
dom = domain.rsplit('.', 1)[0]
tld = domain.rsplit('.', 1)[1]
for i in range(0, len(dom)):
if dom[i].isalpha():
out.append(dom[:i] + dom[i] + dom[i] + dom[i+1:] + '.' + tld)
return out
def transposition(domain):
out = []
dom = domain.rsplit('.', 1)[0]
tld = domain.rsplit('.', 1)[1]
for i in range(0, len(dom)-1):
if dom[i+1] != dom[i]:
out.append(dom[:i] + dom[i+1] + dom[i] + dom[i+2:] + '.' + tld)
return out
def replacement(domain):
keys = {
'1':'2q', '2':'3wq1', '3':'4ew2', '4':'5re3', '5':'6tr4', '6':'7yt5', '7':'8uy6', '8':'9iu7', '9':'0oi8', '0':'po9',
'q':'12wa', 'w':'3esaq2', 'e':'4rdsw3', 'r':'5tfde4', 't':'6ygfr5', 'y':'7uhgt6', 'u':'8ijhy7', 'i':'9okju8', 'o':'0plki9', 'p':'lo0',
'a':'qwsz', 's':'edxzaw', 'd':'rfcxse', 'f':'tgvcdr', 'g':'yhbvft', 'h':'ujnbgy', 'j':'ikmnhu', 'k':'olmji', 'l':'kop',
'z':'asx', 'x':'zsdc', 'c':'xdfv', 'v':'cfgb', 'b':'vghn', 'n':'bhjm', 'm':'njk'
}
out = []
dom = domain.rsplit('.', 1)[0]
tld = domain.rsplit('.', 1)[1]
for i in range(0, len(dom)):
if dom[i] in keys:
for c in range(0, len(keys[dom[i]])):
out.append(dom[:i] + keys[dom[i]][c] + dom[i+1:] + '.' + tld)
return out
def omission(domain):
out = []
dom = domain.rsplit('.', 1)[0]
tld = domain.rsplit('.', 1)[1]
for i in range(0, len(dom)):
out.append(dom[:i] + dom[i+1:] + '.' + tld)
return out
def hyphenation(domain):
out = []
dom = domain.rsplit('.', 1)[0]
tld = domain.rsplit('.', 1)[1]
for i in range(1, len(dom)):
if dom[i] not in ['-', '.'] and dom[i-1] not in ['-', '.']:
out.append(dom[:i] + '-' + dom[i:] + '.' + tld)
return out
def subdomain(domain):
out = []
dom = domain.rsplit('.', 1)[0]
tld = domain.rsplit('.', 1)[1]
for i in range(1, len(dom)-4):
if dom[i] not in ['-', '.'] and dom[i-1] not in ['-', '.']:
out.append(dom[:i] + '.' + dom[i:] + '.' + tld)
return out
def insertion(domain):
keys = {
'1':'2q', '2':'3wq1', '3':'4ew2', '4':'5re3', '5':'6tr4', '6':'7yt5', '7':'8uy6', '8':'9iu7', '9':'0oi8', '0':'po9',
'q':'12wa', 'w':'3esaq2', 'e':'4rdsw3', 'r':'5tfde4', 't':'6ygfr5', 'y':'7uhgt6', 'u':'8ijhy7', 'i':'9okju8', 'o':'0plki9', 'p':'lo0',
'a':'qwsz', 's':'edxzaw', 'd':'rfcxse', 'f':'tgvcdr', 'g':'yhbvft', 'h':'ujnbgy', 'j':'ikmnhu', 'k':'olmji', 'l':'kop',
'z':'asx', 'x':'zsdc', 'c':'xdfv', 'v':'cfgb', 'b':'vghn', 'n':'bhjm', 'm':'njk'
}
out = []
dom = domain.rsplit('.', 1)[0]
tld = domain.rsplit('.', 1)[1]
for i in range(1, len(dom)-1):
if dom[i] in keys:
for c in range(0, len(keys[dom[i]])):
out.append(dom[:i] + keys[dom[i]][c] + dom[i] + dom[i+1:] + '.' + tld)
out.append(dom[:i] + dom[i] + keys[dom[i]][c] + dom[i+1:] + '.' + tld)
return out
def fuzz_domain(domain):
domains = []
for i in bitsquatting(domain):
domains.append({ 'type':'Bitsquatting', 'domain':i })
for i in homoglyph(domain):
domains.append({ 'type':'Homoglyph', 'domain':i })
for i in repetition(domain):
domains.append({ 'type':'Repetition', 'domain':i })
for i in transposition(domain):
domains.append({ 'type':'Transposition', 'domain':i })
for i in replacement(domain):
domains.append({ 'type':'Replacement', 'domain':i })
for i in omission(domain):
domains.append({ 'type':'Omission', 'domain':i })
for i in hyphenation(domain):
domains.append({ 'type':'Hyphenation', 'domain':i })
for i in insertion(domain):
domains.append({ 'type':'Insertion', 'domain':i })
for i in subdomain(domain):
domains.append({ 'type':'Subdomain', 'domain':i })
domains[:] = [x for x in domains if validate_domain(x['domain'])]
return domains
def main():
parser = argparse.ArgumentParser(
description='''Find similar-looking domains that adversaries can use to attack you.
Can detect fraud, phishing attacks and corporate espionage. Useful as an additional
source of targeted threat intelligence.''',
epilog='''Questions? Complaints? You can reach the author at <marcin@ulikowski.pl>'''
)
parser.add_argument('domain', help='domain name to check (e.g., ulikowski.pl)')
parser.add_argument('-c', '--csv', action='store_true', help='print output in CSV format')
parser.add_argument('-r', '--registered', action='store_true', help='show only registered domain names')
parser.add_argument('-w', '--whois', action='store_true', help='perform WHOIS lookup for creation/modification date (slow)')
if len(sys.argv) < 2:
parser.print_help()
sys.exit(0)
args = parser.parse_args()
if not args.csv:
sys.stdout.write('dnstwist (' + __version__ + ') by ' + __email__ + '\n\n')
if not validate_domain(args.domain):
sys.stderr.write('ERROR: invalid domain name!\n')
sys.exit(-1)
domains = fuzz_domain(args.domain.lower())
if not module_dnspython:
sys.stderr.write('NOTICE: missing dnspython module - DNS functionality is limited!\n')
sys.stderr.flush()
if not module_geoip:
sys.stderr.write('NOTICE: missing GeoIP module - geographical location not available!\n')
sys.stderr.flush()
if not module_whois and args.whois:
sys.stderr.write('NOTICE: missing whois module - WHOIS database not available!\n')
sys.stderr.flush()
if module_whois and args.whois and not args.csv:
sys.stderr.write('Be advised: some WHOIS servers limit the number of queries and a longer fun with this tool may end up with a temporary ban to the service.\n\n')
sys.stderr.flush()
if not args.csv:
sys.stdout.write('Processing ' + str(len(domains)) + ' domains ')
sys.stdout.flush()
signal.signal(signal.SIGINT, sigint_handler)
total_hits = 0
for i in range(0, len(domains)):
if module_dnspython:
resolv = dns.resolver.Resolver()
resolv.lifetime = 1
resolv.timeout = 1
try:
ns = resolv.query(domains[i]['domain'], 'NS')
domains[i]['ns'] = str(ns[0])[:-1]
except:
pass
if 'ns' in domains[i]:
try:
ns = resolv.query(domains[i]['domain'], 'A')
domains[i]['a'] = str(ns[0])
except:
pass
try:
ns = resolv.query(domains[i]['domain'], 'AAAA')
domains[i]['aaaa'] = str(ns[0])
except:
pass
try:
mx = resolv.query(domains[i]['domain'], 'MX')
domains[i]['mx'] = str(mx[0].exchange)[:-1]
except:
pass
else:
try:
ip = socket.getaddrinfo(domains[i]['domain'], 80)
except:
pass
else:
for j in ip:
if '.' in j[4][0]:
domains[i]['a'] = j[4][0]
break
for j in ip:
if ':' in j[4][0]:
domains[i]['aaaa'] = j[4][0]
break
if module_whois and args.whois:
if 'ns' in domains[i] or 'a' in domains[i]:
try:
whoisdb = whois.query(domains[i]['domain'])
domains[i]['created'] = str(whoisdb.creation_date).replace(' ', 'T')
domains[i]['updated'] = str(whoisdb.last_updated).replace(' ', 'T')
except:
pass
if module_geoip:
if 'a' in domains[i]:
gi = GeoIP.new(GeoIP.GEOIP_MEMORY_CACHE)
try:
country = gi.country_name_by_addr(domains[i]['a'])
except:
pass
else:
if country:
domains[i]['country'] = country
if not args.csv:
if 'a' in domains[i] or 'ns' in domains[i]:
sys.stdout.write('!')
sys.stdout.flush()
total_hits += 1
else:
sys.stdout.write('.')
sys.stdout.flush()
if not args.csv:
sys.stdout.write(' ' + str(total_hits) + ' hit(s)\n\n')
if args.csv:
sys.stdout.write("type,domain,a,aaaa,mx,ns,country,created,updated\n")
for i in domains:
info = ''
if 'a' in i:
info += i['a']
if 'country' in i:
info += '/' + i['country']
elif 'ns' in i:
info += 'NS:' + i['ns']
if 'aaaa' in i:
info += ' ' + i['aaaa']
if 'mx' in i:
info += ' MX:' + i['mx']
if 'created' in i and 'updated' in i and i['created'] == i['updated']:
info += ' Created/Updated:' + i['created']
else:
if 'created' in i:
info += ' Created:' + i['created']
if 'updated' in i:
info += ' Updated:' + i['updated']
if not info:
info = '-'
if (args.registered and info != '-') or not args.registered:
if not args.csv:
sys.stdout.write('%-15s %-15s %s\n' % (i['type'], i['domain'], info))
sys.stdout.flush()
else:
print(
'%s,%s,%s,%s,%s,%s,%s,%s,%s' % (i.get('type'), i.get('domain'), i.get('a', ''),
i.get('aaaa', ''), i.get('mx', ''), i.get('ns', ''), i.get('country', ''),
i.get('created', ''), i.get('updated', ''))
)
return 0
if __name__ == '__main__':
main()
Updates IPV6 and Small Bug Fixes:
#!/usr/bin/env python
#
# dnstwist by marcin@ulikowski.pl
# Generate and resolve domain variations to detect typo squatting,
# phishing and corporate espionage.
#
#
# dnstwist is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# dnstwist is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with dnstwist. If not, see <http://www.gnu.org/licenses/>.
__author__ = 'Marcin Ulikowski'
__version__ = '20150619'
__email__ = 'marcin@ulikowski.pl'
import re
import sys
import socket
import signal
try:
import dns.resolver
module_dnspython = True
except:
module_dnspython = False
pass
try:
import GeoIP
module_geoip = True
except:
module_geoip = False
pass
def sigint_handler(signal, frame):
print('You pressed Ctrl+C!')
sys.exit(0)
# Internationalized domains not supported
def validate_domain(domain):
if len(domain) > 255:
return False
if domain[-1] == ".":
domain = domain[:-1]
allowed = re.compile('\A([a-z0-9]+(-[a-z0-9]+)*\.)+[a-z]{2,}\Z', re.IGNORECASE)
return allowed.match(domain)
def bitsquatting(domain):
out = []
dom = domain.rsplit('.', 1)[0]
tld = domain.rsplit('.', 1)[1]
masks = [1, 2, 4, 8, 16, 32, 64, 128]
for i in range(0, len(dom)):
c = dom[i]
for j in range(0, len(masks)):
b = chr(ord(c) ^ masks[j])
o = ord(b)
if (o >= 48 and o <= 57) or (o >= 97 and o <= 122) or o == 45:
out.append(dom[:i] + b + dom[i+1:] + '.' + tld)
return out
def homoglyph(domain):
glyphs = {
'd':['b', 'cl'], 'm':['n', 'rn'], 'l':['1', 'i'], 'o':['0'],
'w':['vv'], 'n':['m'], 'b':['d'], 'i':['l'], 'g':['q'], 'q':['g']
}
out = []
dom = domain.rsplit('.', 1)[0]
tld = domain.rsplit('.', 1)[1]
for ws in range(0, len(dom)):
for i in range(0, (len(dom)-ws)+1):
win = dom[i:i+ws]
j = 0
while j < ws:
c = win[j]
if c in glyphs:
for g in range(0, len(glyphs[c])):
win = win[:j] + glyphs[c][g] + win[j+1:]
if len(glyphs[c][g]) > 1:
j += len(glyphs[c][g]) - 1
out.append(dom[:i] + win + dom[i+ws:] + '.' + tld)
j += 1
return list(set(out))
def repetition(domain):
out = []
dom = domain.rsplit('.', 1)[0]
tld = domain.rsplit('.', 1)[1]
for i in range(0, len(dom)):
if dom[i].isalpha():
out.append(dom[:i] + dom[i] + dom[i] + dom[i+1:] + '.' + tld)
return out
def transposition(domain):
out = []
dom = domain.rsplit('.', 1)[0]
tld = domain.rsplit('.', 1)[1]
for i in range(0, len(dom)-1):
out.append(dom[:i] + dom[i+1] + dom[i] + dom[i+2:] + '.' + tld)
return out
def replacement(domain):
keys = {
'1':'2q', '2':'3wq1', '3':'4ew2', '4':'5re3', '5':'6tr4', '6':'7yt5', '7':'8uy6', '8':'9iu7', '9':'0oi8', '0':'po9',
'q':'12wa', 'w':'3esaq2', 'e':'4rdsw3', 'r':'5tfde4', 't':'6ygfr5', 'y':'7uhgt6', 'u':'8ijhy7', 'i':'9okju8', 'o':'0plki9', 'p':'lo0',
'a':'qwsz', 's':'edxzaw', 'd':'rfcxse', 'f':'tgvcdr', 'g':'yhbvft', 'h':'ujnbgy', 'j':'ikmnhu', 'k':'olmji', 'l':'kop',
'z':'asx', 'x':'zsdc', 'c':'xdfv', 'v':'cfgb', 'b':'vghn', 'n':'bhjm', 'm':'njk'
}
out = []
dom = domain.rsplit('.', 1)[0]
tld = domain.rsplit('.', 1)[1]
for i in range(0, len(dom)):
if dom[i] in keys:
for c in range(0, len(keys[dom[i]])):
out.append(dom[:i] + keys[dom[i]][c] + dom[i+1:] + '.' + tld)
return out
def omission(domain):
out = []
dom = domain.rsplit('.', 1)[0]
tld = domain.rsplit('.', 1)[1]
for i in range(0, len(dom)):
out.append(dom[:i] + dom[i+1:] + '.' + tld)
return out
def insertion(domain):
keys = {
'1':'2q', '2':'3wq1', '3':'4ew2', '4':'5re3', '5':'6tr4', '6':'7yt5', '7':'8uy6', '8':'9iu7', '9':'0oi8', '0':'po9',
'q':'12wa', 'w':'3esaq2', 'e':'4rdsw3', 'r':'5tfde4', 't':'6ygfr5', 'y':'7uhgt6', 'u':'8ijhy7', 'i':'9okju8', 'o':'0plki9', 'p':'lo0',
'a':'qwsz', 's':'edxzaw', 'd':'rfcxse', 'f':'tgvcdr', 'g':'yhbvft', 'h':'ujnbgy', 'j':'ikmnhu', 'k':'olmji', 'l':'kop',
'z':'asx', 'x':'zsdc', 'c':'xdfv', 'v':'cfgb', 'b':'vghn', 'n':'bhjm', 'm':'njk'
}
out = []
dom = domain.rsplit('.', 1)[0]
tld = domain.rsplit('.', 1)[1]
for i in range(1, len(dom)-1):
if dom[i] in keys:
for c in range(0, len(keys[dom[i]])):
out.append(dom[:i] + keys[dom[i]][c] + dom[i] + dom[i+1:] + '.' + tld)
out.append(dom[:i] + dom[i] + keys[dom[i]][c] + dom[i+1:] + '.' + tld)
return out
def fuzz_domain(domain):
domains = []
for i in bitsquatting(domain):
domains.append({ 'type':'Bitsquatting', 'domain':i })
for i in homoglyph(domain):
domains.append({ 'type':'Homoglyph', 'domain':i })
for i in repetition(domain):
domains.append({ 'type':'Repetition', 'domain':i })
for i in transposition(domain):
domains.append({ 'type':'Transposition', 'domain':i })
for i in replacement(domain):
domains.append({ 'type':'Replacement', 'domain':i })
for i in omission(domain):
domains.append({ 'type':'Omission', 'domain':i })
for i in insertion(domain):
domains.append({ 'type':'Insertion', 'domain':i })
return domains
def main():
if len(sys.argv) == 3:
output_csv = True
else:
output_csv = False
if not output_csv:
print('dnstwist (' + __version__ + ') by ' + __email__)
if len(sys.argv) < 2:
print('Usage: ' + sys.argv[0] + ' example.com [csv]')
sys.exit()
if not validate_domain(sys.argv[1]):
sys.stderr.write('ERROR: invalid domain name !\n')
sys.exit(-1)
domains = fuzz_domain(sys.argv[1].lower())
if not module_dnspython:
sys.stderr.write('NOTICE: missing dnspython module - DNS functionality is limited !\n')
sys.stderr.flush()
if not module_geoip:
sys.stderr.write('NOTICE: missing GeoIP module - geographical location not available !\n')
sys.stderr.flush()
if not output_csv:
sys.stdout.write('Processing ' + str(len(domains)) + ' domains ')
sys.stdout.flush()
signal.signal(signal.SIGINT, sigint_handler)
for i in range(0, len(domains)):
try:
ip = socket.getaddrinfo(domains[i]['domain'], 80)
except:
pass
else:
for j in ip:
if '.' in j[4][0]:
domains[i]['a'] = j[4][0]
break
for j in ip:
if ':' in j[4][0]:
domains[i]['aaaa'] = j[4][0]
break
if module_dnspython:
resolv = dns.resolver.Resolver()
resolv.lifetime = 1
resolv.timeout = 1
try:
ns = resolv.query(domains[i]['domain'], 'NS')
domains[i]['ns'] = str(ns[0])[:-1]
except:
pass
if 'ns' in domains[i]:
try:
mx = resolv.query(domains[i]['domain'], 'MX')
domains[i]['mx'] = str(mx[0].exchange)[:-1]
except:
pass
if module_geoip:
gi = GeoIP.new(GeoIP.GEOIP_MEMORY_CACHE)
try:
country = gi.country_name_by_addr(domains[i]['a'])
except:
pass
else:
if country:
domains[i]['country'] = country
if not output_csv:
if 'a' in domains[i] or 'ns' in domains[i]:
sys.stdout.write('!')
sys.stdout.flush()
else:
sys.stdout.write('.')
sys.stdout.flush()
if not output_csv:
sys.stdout.write('\n\n')
for i in domains:
if not output_csv:
zone = ''
if 'a' in i:
zone += i['a']
if 'country' in i:
zone += '/' + i['country']
elif 'ns' in i:
zone += 'NS:' + i['ns']
if 'aaaa' in i:
zone += ' ' + i['aaaa']
if 'mx' in i:
zone += ' MX:' + i['mx']
if not zone:
zone = '-'
sys.stdout.write('%-15s %-15s %s\n' % (i['type'], i['domain'], zone))
sys.stdout.flush()
else:
print(
'%s,%s,%s,%s,%s,%s,%s' % (i.get('type'), i.get('domain'), i.get('a', ''),
i.get('aaaa', ''), i.get('mx', ''), i.get('ns', ''), i.get('country', ''))
)
return 0
if __name__ == '__main__':
main()
Source : https://github.com/elceef | Our Post Before