├── .gitignore ├── MaltegoTransform.py ├── README.md ├── goldphish.mtz └── goldphish.py /.gitignore: -------------------------------------------------------------------------------- 1 | # Byte-compiled / optimized / DLL files 2 | __pycache__/ 3 | *.py[cod] 4 | 5 | # C extensions 6 | *.so 7 | 8 | # Distribution / packaging 9 | .Python 10 | env/ 11 | build/ 12 | develop-eggs/ 13 | dist/ 14 | downloads/ 15 | eggs/ 16 | .eggs/ 17 | lib/ 18 | lib64/ 19 | parts/ 20 | sdist/ 21 | var/ 22 | *.egg-info/ 23 | .installed.cfg 24 | *.egg 25 | 26 | # PyInstaller 27 | # Usually these files are written by a python script from a template 28 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 29 | *.manifest 30 | *.spec 31 | 32 | # Installer logs 33 | pip-log.txt 34 | pip-delete-this-directory.txt 35 | 36 | # Unit test / coverage reports 37 | htmlcov/ 38 | .tox/ 39 | .coverage 40 | .coverage.* 41 | .cache 42 | nosetests.xml 43 | coverage.xml 44 | *,cover 45 | 46 | # Translations 47 | *.mo 48 | *.pot 49 | 50 | # Django stuff: 51 | *.log 52 | 53 | # Sphinx documentation 54 | docs/_build/ 55 | 56 | # PyBuilder 57 | target/ 58 | -------------------------------------------------------------------------------- /MaltegoTransform.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | ####################################################### 3 | # Maltego Python Local Transform Helper # 4 | # Version 0.2 # 5 | # # 6 | # Local transform specification can be found at: # 7 | # http://ctas.paterva.com/view/Specification # 8 | # # 9 | # For more help and other local transforms # 10 | # try the forum or mail me: # 11 | # # 12 | # http://www.paterva.com/forum # 13 | # # 14 | # Andrew MacPherson [ andrew <> Paterva.com ] # 15 | # # 16 | ####################################################### 17 | import sys 18 | 19 | class MaltegoEntity(object): 20 | value = ""; 21 | weight = 100; 22 | displayInformation = None; 23 | additionalFields = []; 24 | iconURL = ""; 25 | entityType = "Phrase" 26 | 27 | def __init__(self,eT=None,v=None): 28 | if (eT is not None): 29 | self.entityType = eT; 30 | if (v is not None): 31 | self.value = sanitise(v); 32 | self.additionalFields = []; 33 | self.displayInformation = None; 34 | 35 | def setType(self,eT=None): 36 | if (eT is not None): 37 | self.entityType = eT; 38 | 39 | def setValue(self,eV=None): 40 | if (eV is not None): 41 | self.value = sanitise(eV); 42 | 43 | def setWeight(self,w=None): 44 | if (w is not None): 45 | self.weight = w; 46 | 47 | def setDisplayInformation(self,di=None): 48 | if (di is not None): 49 | self.displayInformation = di; 50 | 51 | def addAdditionalFields(self,fieldName=None,displayName=None,matchingRule=False,value=None): 52 | self.additionalFields.append([sanitise(fieldName),sanitise(displayName),matchingRule,value]); 53 | 54 | def setIconURL(self,iU=None): 55 | if (iU is not None): 56 | self.iconURL = iU; 57 | 58 | def returnEntity(self): 59 | print ""; 60 | print "" + str(self.value) + ""; 61 | print "" + str(self.weight) + ""; 62 | if (self.displayInformation is not None): 63 | print ""; 64 | if (len(self.additionalFields) > 0): 65 | print ""; 66 | for i in range(len(self.additionalFields)): 67 | if (str(self.additionalFields[i][2]) <> "strict"): 68 | print "" + str(self.additionalFields[i][3]) + ""; 69 | else: 70 | print "" + str(self.additionalFields[i][3]) + ""; 71 | print ""; 72 | if (len(self.iconURL) > 0): 73 | print "" + self.iconURL + ""; 74 | print ""; 75 | 76 | class MaltegoTransform(object): 77 | entities = [] 78 | exceptions = [] 79 | UIMessages = [] 80 | values = {}; 81 | 82 | def __init__(self): 83 | values = {}; 84 | value = None; 85 | 86 | def parseArguments(self,argv): 87 | if (argv[1] is not None): 88 | self.value = argv[1]; 89 | 90 | if (len(argv) > 2): 91 | if (argv[2] is not None): 92 | vars = argv[2].split('#'); 93 | for x in range(0,len(vars)): 94 | vars_values = vars[x].split('=') 95 | if (len(vars_values) == 2): 96 | self.values[vars_values[0]] = vars_values[1]; 97 | 98 | def getValue(self): 99 | if (self.value is not None): 100 | return self.value; 101 | 102 | def getVars(self): 103 | return self.values 104 | 105 | def getVar(self,varName): 106 | if (varName in self.values.keys()): 107 | if (self.values[varName] is not None): 108 | return self.values[varName]; 109 | 110 | def addEntity(self,enType,enValue): 111 | me = MaltegoEntity(enType,enValue); 112 | self.addEntityToMessage(me); 113 | return self.entities[len(self.entities)-1]; 114 | 115 | def addEntityToMessage(self,maltegoEntity): 116 | self.entities.append(maltegoEntity); 117 | 118 | def addUIMessage(self,message,messageType="Inform"): 119 | self.UIMessages.append([messageType,message]); 120 | 121 | def addException(self,exceptionString): 122 | self.exceptions.append(exceptionString); 123 | 124 | def throwExceptions(self): 125 | print ""; 126 | print ""; 127 | print "" 128 | 129 | for i in range(len(self.exceptions)): 130 | print "" + self.exceptions[i] + ""; 131 | print "" 132 | print ""; 133 | print ""; 134 | exit(); 135 | 136 | def returnOutput(self): 137 | print ""; 138 | print ""; 139 | 140 | print "" 141 | for i in range(len(self.entities)): 142 | self.entities[i].returnEntity(); 143 | print "" 144 | 145 | print "" 146 | for i in range(len(self.UIMessages)): 147 | print "" + self.UIMessages[i][1] + ""; 148 | print "" 149 | 150 | print ""; 151 | print ""; 152 | 153 | def writeSTDERR(self,msg): 154 | sys.stderr.write(str(msg)); 155 | 156 | def heartbeat(self): 157 | self.writeSTDERR("+"); 158 | 159 | def progress(self,percent): 160 | self.writeSTDERR("%" + str(percent)); 161 | 162 | def debug(self,msg): 163 | self.writeSTDERR("D:" + str(msg)); 164 | 165 | 166 | 167 | def sanitise(value): 168 | replace_these = ["&",">","<"]; 169 | replace_with = ["&",">","<"]; 170 | tempvalue = value; 171 | for i in range(0,len(replace_these)): 172 | tempvalue = tempvalue.replace(replace_these[i],replace_with[i]); 173 | return tempvalue; -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # goldphish 2 | A Maltego transform and machine to identify possible phishing vectors using permutated domains 3 | 4 | This is some what of a follow-up to an earlier [blog post](http://nullsecure.org/identifying-phishing-defense-coverage-using-dns/) about analyzing phishing vectors using [dnstwist](https://github.com/elceef/dnstwist). In this post I'll be releasing a new Maltego transform and machine which can quickly and easily analyze a domain and it's permutations to see who owns the domain. 5 | 6 | I'm pretty embarrassed I didn't think to do something like this last time I was analyzing the domains, however, it's two different use-cases, so I don't feel as bad I guess. 7 | 8 | So, the way this works is, you'll open up Maltego and run a machine which will do two things in tandem: 9 | 10 | 1. Run a modified version of dnstwist which creates a new domain entity for each permutation (i.e. amazoon.com, amaz0n.com) 11 | 2. Use a built in transform to look up the name server for that permutated domain. 12 | 13 | The two transforms run hand in hand to build out a map of the infrastructure involved so you can quickly and easily see who owns what domains. Below are two screenshots that show what this will look like. 14 | 15 | This screenshot shows the permutated domains for amazon.com as well as the name servers for the domains. You can quickly and easily see the big circle at the top, which shows a connection between a ton of permutated domains and the name servers for amazon.com, which tells us that amazon.com takes a lot of steps to secure other domains (we already covered that in the last blog post). 16 | ![http://i.imgur.com/APU5Dy0.png](http://i.imgur.com/APU5Dy0.png) 17 | 18 | In this screenshot, I looked at google.com, which is kinda all over the place in terms of name servers. Doesn't look very consistent and is pretty random, unlike Amazon. 19 | 20 | ![http://i.imgur.com/zlEuQBJ.png](http://i.imgur.com/zlEuQBJ.png) 21 | 22 | Another thing we could eventually do with this, is take a bunch of different well known domains and put them all in here, then see if one particular entity is responsible for a lot of different pertmuated domains, for instance, Google owning Amazon.com domains or vice versa. 23 | 24 | You can find the transforms and machine for this on my [github](http://www.github.com/brianwarehime/goldphish). All credits for dnstwist go to [https://github.com/elceef](https://github.com/elceef). 25 | 26 | ## Installation Instructions 27 | 28 | To get this up and running, you'll need to do a few things. 29 | 30 | 1. Download the goldphish.mtz and *.py from my github repo. 31 | 2. In Maltego, import the config you just downloaded by going to "Manage" -> "Import Config" 32 | 3. Modify the transform by going to "Manage Transforms" and selecting "Goldphish" 33 | 4. You'll need to set the Python interpreter to your OS, (i.e. /usr/bin/python) as well as the Working Directory to wherever you saved the Python scripts (i.e. ~/Projects/Goldphish) 34 | 35 | ![http://i.imgur.com/hqvYW8D.png](http://i.imgur.com/hqvYW8D.png) 36 | 37 | ## Running the Machine 38 | 39 | After the transforms and machine are installed, all you need to do is click on "Machines" in the menubar, then "Run Machine". You'll need to enter whatever domain you are interested in, then it'll start running the machine. Due to the limitations in the community edition, it'll only return so many results per run, so, by using the machine, it'll keep running the same transform every 2 seconds to make sure all the domains show up. 40 | 41 | The machine will keep running until you stop it, so, after you see no more activity for a few runs, you can just click the stop button. 42 | 43 | Please let me know if any of the instructions are unclear or if you ran into any issues getting this running. 44 | -------------------------------------------------------------------------------- /goldphish.mtz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/brianwarehime/goldphish/d4510f94cbbdb14939a628f1111780cffb3ffdc6/goldphish.mtz -------------------------------------------------------------------------------- /goldphish.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # 3 | __author__ = 'Marcin Ulikowski' 4 | __version__ = '20150920' 5 | __email__ = 'marcin@ulikowski.pl' 6 | 7 | import sys 8 | from MaltegoTransform import * 9 | me = MaltegoTransform() 10 | me.parseArguments(sys.argv) 11 | 12 | def bitsquatting(domain): 13 | out = [] 14 | dom = domain.rsplit('.', 1)[0] 15 | tld = domain.rsplit('.', 1)[1] 16 | masks = [1, 2, 4, 8, 16, 32, 64, 128] 17 | 18 | for i in range(0, len(dom)): 19 | c = dom[i] 20 | for j in range(0, len(masks)): 21 | b = chr(ord(c) ^ masks[j]) 22 | o = ord(b) 23 | if (o >= 48 and o <= 57) or (o >= 97 and o <= 122) or o == 45: 24 | out.append(dom[:i] + b + dom[i+1:] + '.' + tld) 25 | 26 | return out 27 | 28 | def homoglyph(domain): 29 | glyphs = { 30 | 'd':['b', 'cl'], 'm':['n', 'nn', 'rn'], 'l':['1', 'i'], 'o':['0'], 31 | 'w':['vv'], 'n':['m'], 'b':['d'], 'i':['1', 'l'], 'g':['q'], 'q':['g'] 32 | } 33 | out = [] 34 | dom = domain.rsplit('.', 1)[0] 35 | tld = domain.rsplit('.', 1)[1] 36 | 37 | for ws in range(0, len(dom)): 38 | for i in range(0, (len(dom)-ws)+1): 39 | win = dom[i:i+ws] 40 | 41 | j = 0 42 | while j < ws: 43 | c = win[j] 44 | if c in glyphs: 45 | for g in glyphs[c]: 46 | win = win[:j] + g + win[j+1:] 47 | 48 | if len(g) > 1: 49 | j += len(g) - 1 50 | out.append(dom[:i] + win + dom[i+ws:] + '.' + tld) 51 | 52 | j += 1 53 | 54 | return list(set(out)) 55 | 56 | def repetition(domain): 57 | out = [] 58 | dom = domain.rsplit('.', 1)[0] 59 | tld = domain.rsplit('.', 1)[1] 60 | 61 | for i in range(0, len(dom)): 62 | if dom[i].isalpha(): 63 | out.append(dom[:i] + dom[i] + dom[i] + dom[i+1:] + '.' + tld) 64 | 65 | return list(set(out)) 66 | 67 | def transposition(domain): 68 | out = [] 69 | dom = domain.rsplit('.', 1)[0] 70 | tld = domain.rsplit('.', 1)[1] 71 | 72 | for i in range(0, len(dom)-1): 73 | if dom[i+1] != dom[i]: 74 | out.append(dom[:i] + dom[i+1] + dom[i] + dom[i+2:] + '.' + tld) 75 | 76 | return out 77 | 78 | def replacement(domain): 79 | keys = { 80 | '1':'2q', '2':'3wq1', '3':'4ew2', '4':'5re3', '5':'6tr4', '6':'7yt5', '7':'8uy6', '8':'9iu7', '9':'0oi8', '0':'po9', 81 | 'q':'12wa', 'w':'3esaq2', 'e':'4rdsw3', 'r':'5tfde4', 't':'6ygfr5', 'y':'7uhgt6', 'u':'8ijhy7', 'i':'9okju8', 'o':'0plki9', 'p':'lo0', 82 | 'a':'qwsz', 's':'edxzaw', 'd':'rfcxse', 'f':'tgvcdr', 'g':'yhbvft', 'h':'ujnbgy', 'j':'ikmnhu', 'k':'olmji', 'l':'kop', 83 | 'z':'asx', 'x':'zsdc', 'c':'xdfv', 'v':'cfgb', 'b':'vghn', 'n':'bhjm', 'm':'njk' 84 | } 85 | out = [] 86 | dom = domain.rsplit('.', 1)[0] 87 | tld = domain.rsplit('.', 1)[1] 88 | 89 | for i in range(0, len(dom)): 90 | if dom[i] in keys: 91 | for c in range(0, len(keys[dom[i]])): 92 | out.append(dom[:i] + keys[dom[i]][c] + dom[i+1:] + '.' + tld) 93 | 94 | return out 95 | 96 | def omission(domain): 97 | out = [] 98 | dom = domain.rsplit('.', 1)[0] 99 | tld = domain.rsplit('.', 1)[1] 100 | 101 | for i in range(0, len(dom)): 102 | out.append(dom[:i] + dom[i+1:] + '.' + tld) 103 | 104 | return list(set(out)) 105 | 106 | def hyphenation(domain): 107 | out = [] 108 | dom = domain.rsplit('.', 1)[0] 109 | tld = domain.rsplit('.', 1)[1] 110 | 111 | for i in range(1, len(dom)): 112 | if dom[i] not in ['-', '.'] and dom[i-1] not in ['-', '.']: 113 | out.append(dom[:i] + '-' + dom[i:] + '.' + tld) 114 | 115 | return out 116 | 117 | def subdomain(domain): 118 | out = [] 119 | dom = domain.rsplit('.', 1)[0] 120 | tld = domain.rsplit('.', 1)[1] 121 | 122 | for i in range(1, len(dom)): 123 | if dom[i] not in ['-', '.'] and dom[i-1] not in ['-', '.']: 124 | out.append(dom[:i] + '.' + dom[i:] + '.' + tld) 125 | 126 | return out 127 | 128 | def insertion(domain): 129 | keys = { 130 | '1':'2q', '2':'3wq1', '3':'4ew2', '4':'5re3', '5':'6tr4', '6':'7yt5', '7':'8uy6', '8':'9iu7', '9':'0oi8', '0':'po9', 131 | 'q':'12wa', 'w':'3esaq2', 'e':'4rdsw3', 'r':'5tfde4', 't':'6ygfr5', 'y':'7uhgt6', 'u':'8ijhy7', 'i':'9okju8', 'o':'0plki9', 'p':'lo0', 132 | 'a':'qwsz', 's':'edxzaw', 'd':'rfcxse', 'f':'tgvcdr', 'g':'yhbvft', 'h':'ujnbgy', 'j':'ikmnhu', 'k':'olmji', 'l':'kop', 133 | 'z':'asx', 'x':'zsdc', 'c':'xdfv', 'v':'cfgb', 'b':'vghn', 'n':'bhjm', 'm':'njk' 134 | } 135 | out = [] 136 | dom = domain.rsplit('.', 1)[0] 137 | tld = domain.rsplit('.', 1)[1] 138 | 139 | for i in range(1, len(dom)-1): 140 | if dom[i] in keys: 141 | for c in range(0, len(keys[dom[i]])): 142 | out.append(dom[:i] + keys[dom[i]][c] + dom[i] + dom[i+1:] + '.' + tld) 143 | out.append(dom[:i] + dom[i] + keys[dom[i]][c] + dom[i+1:] + '.' + tld) 144 | 145 | return out 146 | 147 | def fuzz_domain(domain): 148 | domains = [] 149 | 150 | domains.append({ 'type':'Original*', 'domain':domain }) 151 | 152 | for i in bitsquatting(domain): 153 | domains.append({ 'type':'Bitsquatting', 'domain':i }) 154 | for i in homoglyph(domain): 155 | domains.append({ 'type':'Homoglyph', 'domain':i }) 156 | for i in repetition(domain): 157 | domains.append({ 'type':'Repetition', 'domain':i }) 158 | for i in transposition(domain): 159 | domains.append({ 'type':'Transposition', 'domain':i }) 160 | for i in replacement(domain): 161 | domains.append({ 'type':'Replacement', 'domain':i }) 162 | for i in omission(domain): 163 | domains.append({ 'type':'Omission', 'domain':i }) 164 | for i in hyphenation(domain): 165 | domains.append({ 'type':'Hyphenation', 'domain':i }) 166 | for i in insertion(domain): 167 | domains.append({ 'type':'Insertion', 'domain':i }) 168 | for i in subdomain(domain): 169 | domains.append({ 'type':'Subdomain', 'domain':i }) 170 | 171 | domains[:] = [x for x in domains if x['domain']] 172 | 173 | return domains 174 | 175 | def main(): 176 | domains = fuzz_domain(sys.argv[1].lower()) 177 | for i in domains: 178 | ent = me.addEntity("maltego.Domain",i['domain']) 179 | #print i['type'], i['domain'] 180 | me.returnOutput() 181 | 182 | if __name__ == '__main__': 183 | main() 184 | --------------------------------------------------------------------------------