├── GetHostNames.py ├── GetReverseIP.py ├── GetSharedDNS.py ├── HackerTarget-config-v1.mtz ├── MaltegoTransform.py └── README.md /GetHostNames.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | # Maltego Transform to get hosts from HackerTarget.com (Find Hostnames Search from domain) 4 | # with a HackerTarget.com Membership get an API key for additional quota (Free has 200 / day limit) 5 | 6 | 7 | APIKEY = '' 8 | 9 | 10 | from MaltegoTransform import * 11 | import sys 12 | import os 13 | import requests 14 | 15 | domain = sys.argv[1] 16 | hosts = [] 17 | m = MaltegoTransform() 18 | 19 | 20 | try: 21 | if len(APIKEY) == 80: 22 | r = requests.get('https://api.hackertarget.com/hostsearch/?q=' + domain + '&apikey=' + APIKEY) 23 | else: 24 | r = requests.get('https://api.hackertarget.com/hostsearch/?q=' + domain) 25 | if r.status_code == 200 and 'error check' in r.text: 26 | m.addUIMessage("Error getting results - check input") 27 | elif "Error invalid key" in r.text: 28 | m.addUIMessage("Error invalid key") 29 | elif "No DNS A records" in r.text: 30 | m.addUIMessage("No results found from HackerTarget.com") 31 | elif "API count exceeded" in r.text: 32 | m.addUIMessage("API count exceeded") 33 | else: 34 | hosts = str(r.text).split('\n') 35 | hosts = filter(None, hosts) 36 | for i in hosts: 37 | hostname = i.split(',')[0] 38 | ipaddress = i.split(',')[1] 39 | m.addEntity('maltego.DNSName', hostname) 40 | else: 41 | m.addUIMessage("No results found from Host Search") 42 | except Exception as e: 43 | m.addUIMessage(str(e)) 44 | m.returnOutput() 45 | 46 | 47 | -------------------------------------------------------------------------------- /GetReverseIP.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | # Maltego Transform to get hosts from HackerTarget.com (Reverse IP Search) 4 | # with a HackerTarget.com Membership get an API key for additional quota (Free has 200 / day limit) 5 | 6 | 7 | from MaltegoTransform import * 8 | import sys 9 | import os 10 | import requests 11 | 12 | APIKEY = '' 13 | 14 | ipaddress = sys.argv[1] 15 | hosts = [] 16 | m = MaltegoTransform() 17 | 18 | 19 | try: 20 | if len(APIKEY) == 80: 21 | r = requests.get('https://api.hackertarget.com/reverseiplookup/?q=' + ipaddress + '&apikey=' + APIKEY) 22 | else: 23 | r = requests.get('https://api.hackertarget.com/reverseiplookup/?q=' + ipaddress) 24 | if r.status_code == 200 or 'error check' in r.text: 25 | m.addUIMessage("Error getting results - check input") 26 | elif "Error invalid key" in r.text: 27 | m.addUIMessage("Error invalid key") 28 | elif "No DNS A records" in r.text: 29 | m.addUIMessage("No results found from HackerTarget.com") 30 | elif "API count exceeded" in r.text: 31 | m.addUIMessage("API count exceeded") 32 | else: 33 | hosts = str(r.text).split('\n') 34 | hosts = filter(None, hosts) 35 | for i in hosts: 36 | m.addEntity('maltego.DNSName', i) 37 | else: 38 | m.addUIMessage("No results found from Reverse IP Search") 39 | except Exception as e: 40 | m.addUIMessage(str(e)) 41 | m.returnOutput() 42 | 43 | 44 | -------------------------------------------------------------------------------- /GetSharedDNS.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | # Maltego Transform to get hosts from HackerTarget.com (Shared DNS Server) 4 | # with a HackerTarget.com Membership get an API key for additional quota (Free has 200 / day limit) 5 | 6 | 7 | from MaltegoTransform import * 8 | import sys 9 | import os 10 | import requests 11 | 12 | APIKEY = '' 13 | 14 | 15 | 16 | dnsserver = sys.argv[1] 17 | hosts = [] 18 | m = MaltegoTransform() 19 | 20 | 21 | 22 | 23 | try: 24 | if len(APIKEY) == 80: 25 | r = requests.get('https://api.hackertarget.com/findshareddns/?q=' + dnsserver + '&apikey=' + APIKEY) 26 | else: 27 | r = requests.get('https://api.hackertarget.com/findshareddns/?q=' + dnsserver) 28 | if r.status_code != 200 or 'error check' in r.text: 29 | m.addUIMessage("Error getting results - check input") 30 | elif "Error invalid key" in r.text: 31 | m.addUIMessage("Error invalid key") 32 | elif "No DNS server" in r.text: 33 | m.addUIMessage("No results found from HackerTarget.com") 34 | elif "API count exceeded" in r.text: 35 | m.addUIMessage("API count exceeded") 36 | else: 37 | hosts = str(r.text).split('\n') 38 | hosts = filter(None, hosts) 39 | for i in hosts: 40 | m.addEntity('maltego.DNSName', i) 41 | except Exception as e: 42 | m.addUIMessage(str(e)) 43 | m.returnOutput() 44 | 45 | 46 | -------------------------------------------------------------------------------- /HackerTarget-config-v1.mtz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hackertarget/maltego_transforms/76e9f08b4a21c62777183373b01e059ac451df23/HackerTarget-config-v1.mtz -------------------------------------------------------------------------------- /MaltegoTransform.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | ####################################################### 3 | # Maltego Python Local Transform Helper # 4 | # Version 0.3 # 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 | # UPDATED: 2016/08/29 - PR ["&",">","<"] # 17 | # UPDATED: 2015/10/29 - PR # 18 | # UPDATED: 2016/08/19 - AM (fixed # and = split) # 19 | ####################################################### 20 | 21 | import sys 22 | 23 | 24 | BOOKMARK_COLOR_NONE="-1" 25 | BOOKMARK_COLOR_BLUE="0" 26 | BOOKMARK_COLOR_GREEN="1" 27 | BOOKMARK_COLOR_YELLOW="2" 28 | BOOKMARK_COLOR_ORANGE="3" 29 | BOOKMARK_COLOR_RED="4" 30 | 31 | LINK_STYLE_NORMAL="0" 32 | LINK_STYLE_DASHED="1" 33 | LINK_STYLE_DOTTED="2" 34 | LINK_STYLE_DASHDOT="3" 35 | 36 | UIM_FATAL='FatalError' 37 | UIM_PARTIAL='PartialError' 38 | UIM_INFORM='Inform' 39 | UIM_DEBUG='Debug' 40 | 41 | 42 | class MaltegoEntity(object): 43 | value = "" 44 | weight = 100 45 | displayInformation = None 46 | additionalFields = [] 47 | iconURL = "" 48 | entityType = "Phrase" 49 | 50 | def __init__(self,eT=None,v=None): 51 | if (eT is not None): 52 | self.entityType = eT 53 | if (v is not None): 54 | self.value = sanitise(v) 55 | self.additionalFields = [] 56 | self.displayInformation = None 57 | 58 | def setType(self,eT=None): 59 | if (eT is not None): 60 | self.entityType = eT 61 | 62 | def setValue(self,eV=None): 63 | if (eV is not None): 64 | self.value = sanitise(eV) 65 | 66 | def setWeight(self,w=None): 67 | if (w is not None): 68 | self.weight = w 69 | 70 | def setDisplayInformation(self,di=None): 71 | if (di is not None): 72 | self.displayInformation = di 73 | 74 | def addAdditionalFields(self,fieldName=None,displayName=None,matchingRule=False,value=None): 75 | self.additionalFields.append([sanitise(fieldName),sanitise(displayName),matchingRule,sanitise(value)]) 76 | 77 | def setIconURL(self,iU=None): 78 | if (iU is not None): 79 | self.iconURL = iU 80 | 81 | def setLinkColor(self,color): 82 | self.addAdditionalFields('link#maltego.link.color','LinkColor','',color) 83 | 84 | def setLinkStyle(self,style): 85 | self.addAdditionalFields('link#maltego.link.style','LinkStyle','',style) 86 | 87 | def setLinkThickness(self,thick): 88 | self.addAdditionalFields('link#maltego.link.thickness','Thickness','',str(thick)) 89 | 90 | def setLinkLabel(self,label): 91 | self.addAdditionalFields('link#maltego.link.label','Label','',label) 92 | 93 | def setBookmark(self,bookmark): 94 | self.addAdditionalFields('bookmark#','Bookmark','',bookmark) 95 | 96 | def setNote(self,note): 97 | self.addAdditionalFields('notes#','Notes','',note) 98 | 99 | def returnEntity(self): 100 | print "" 101 | print "" + str(self.value) + "" 102 | print "" + str(self.weight) + "" 103 | if (self.displayInformation is not None): 104 | print "" 105 | if (len(self.additionalFields) > 0): 106 | print "" 107 | for i in range(len(self.additionalFields)): 108 | if (str(self.additionalFields[i][2]) <> "strict"): 109 | print "" + str(self.additionalFields[i][3]) + "" 110 | else: 111 | print "" + str(self.additionalFields[i][3]) + "" 112 | print "" 113 | if (len(self.iconURL) > 0): 114 | print "" + self.iconURL + "" 115 | print "" 116 | 117 | class MaltegoTransform(object): 118 | entities = [] 119 | exceptions = [] 120 | UIMessages = [] 121 | values = {} 122 | 123 | def __init__(self): 124 | values = {} 125 | value = None 126 | 127 | def parseArguments(self,argv): 128 | if (argv[1] is not None): 129 | self.value = argv[1] 130 | 131 | if (len(argv) > 2): 132 | if (argv[2] is not None): 133 | vars = argv[2].split('#') 134 | for x in range(0,len(vars)): 135 | vars_values = vars[x].split('=',1) 136 | if (len(vars_values) == 2): 137 | self.values[vars_values[0]] = vars_values[1] 138 | 139 | def getValue(self): 140 | if (self.value is not None): 141 | return self.value 142 | 143 | def getVar(self,varName): 144 | if (varName in self.values.keys()): 145 | if (self.values[varName] is not None): 146 | return self.values[varName] 147 | 148 | def addEntity(self,enType,enValue): 149 | me = MaltegoEntity(enType,enValue) 150 | self.addEntityToMessage(me) 151 | return self.entities[len(self.entities)-1] 152 | 153 | def addEntityToMessage(self,maltegoEntity): 154 | self.entities.append(maltegoEntity) 155 | 156 | def addUIMessage(self,message,messageType="Inform"): 157 | self.UIMessages.append([messageType,message]) 158 | 159 | def addException(self,exceptionString): 160 | self.exceptions.append(exceptionString) 161 | 162 | def throwExceptions(self): 163 | print "" 164 | print "" 165 | print "" 166 | 167 | for i in range(len(self.exceptions)): 168 | print "" + self.exceptions[i] + "" 169 | print "" 170 | print "" 171 | print "" 172 | exit() 173 | 174 | def returnOutput(self): 175 | print "" 176 | print "" 177 | 178 | print "" 179 | for i in range(len(self.entities)): 180 | self.entities[i].returnEntity() 181 | print "" 182 | 183 | print "" 184 | for i in range(len(self.UIMessages)): 185 | print "" + self.UIMessages[i][1] + "" 186 | print "" 187 | 188 | print "" 189 | print "" 190 | 191 | def writeSTDERR(self,msg): 192 | sys.stderr.write(str(msg)) 193 | 194 | def heartbeat(self): 195 | self.writeSTDERR("+") 196 | 197 | def progress(self,percent): 198 | self.writeSTDERR("%" + str(percent)) 199 | 200 | def debug(self,msg): 201 | self.writeSTDERR("D:" + str(msg)) 202 | 203 | 204 | 205 | def sanitise(value): 206 | replace_these = ["&",">","<"] 207 | replace_with = ["&",">","<"] 208 | tempvalue = value 209 | for i in range(0,len(replace_these)): 210 | tempvalue = tempvalue.replace(replace_these[i],replace_with[i]) 211 | return tempvalue -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | A local transform for Maltego which makes use of the [HackerTarget API](https://hackertarget.com/ip-tools/) for DNS Recon 2 | 3 | ![Maltego Transform](https://hackertarget.com/images/maltego-hackertarget.gif) 4 | 5 | Installation 6 | ------------ 7 | 1. This transform was developed and tested with Python 2.7 on Ubuntu Linux. You need python and the requests module. 8 | 2. Download and install community or commercial version of [Maltego](https://www.paterva.com/web6/products/download.php). 9 | 10 | Configuration 11 | ------------- 12 | 1. Clone this repository to a local directory (known as the 'working directory' in Maltego). 13 | 2. For the configuration file to work out of the box, move **maltego_transforms** to **/opt/Maltego_HackerTarget**. If paths are different you can update from the manage transform screen. 14 | 3. Obtain an [HackerTarget API Key](https://hackertarget.com/scan-membership/) API key. Free users get 200 requests per day against the API (no key required). 15 | 4. Place the API key in the APIKEY variable in each transform. 16 | 5. Import HackerTarget-config-v1.mtz as a Maltego configuration file 17 | 6. Confirm the working directory of each transform (which should be set to /opt/Maltego_HackerTarget by default) - this comes from mtz file. 18 | 7. Ensure each transform has the proper Python path. 19 | 20 | Tips 21 | ---- 22 | - The transforms work on Domain, NS Server and IP address entities only. You may need to resolve a DNSName entity to a domain before running the find hosts transform. Resolve the Domain to NS Server to run the DNSseach. 23 | - Depending on your Maltego license you will be restricted by the number of entities returned by the transforms. The find hosts can return thousands of entries on the right query. 24 | 25 | About HackerTarget Pty Ltd 26 | ---- 27 | - [HackerTarget](https://hackertarget.com) provides hosted open source tools and network intelligence to help organizations with attack surface discovery and identification of security vulnerabilities. 28 | - Expensive appliances with flashing blue lights are not always the best solution. Through promotion of open source security solutions we make organisations big and small better at protecting what matters to them. 29 | --------------------------------------------------------------------------------