|
1 | 1 | from recon.core.module import BaseModule
|
2 | 2 |
|
3 |
| -from censys.ipv4 import CensysIPv4 |
4 |
| -from censys.base import CensysException |
| 3 | +from censys.search import CensysHosts |
| 4 | +from censys.common.exceptions import CensysException |
5 | 5 |
|
6 | 6 |
|
7 | 7 | class Module(BaseModule):
|
8 | 8 | meta = {
|
9 |
| - 'name': 'Censys hosts and subdomains by domain', |
10 |
| - 'author': 'J Nazario', |
11 |
| - 'version': '1.1', |
12 |
| - 'description': 'Retrieves the MX, SMTPS, POP3S, and HTTPS records for a domain. Updates the \'hosts\' and the \'ports\' tables with the results.', |
13 |
| - 'query': 'SELECT DISTINCT domain FROM domains WHERE domain IS NOT NULL', |
14 |
| - 'dependencies': ['censys'], |
15 |
| - 'required_keys': ['censysio_id', 'censysio_secret'], |
| 9 | + "name": "Censys - Hosts by domain", |
| 10 | + "author": "Censys, Inc. <[email protected]>", |
| 11 | + "version": 2.1, |
| 12 | + "description": ( |
| 13 | + "Retrieves hosts for a domain. This module queries queries domain" |
| 14 | + " names and updates the 'hosts' and the 'ports' tables with the" |
| 15 | + " results." |
| 16 | + ), |
| 17 | + "query": ( |
| 18 | + "SELECT DISTINCT domain FROM domains WHERE domain IS NOT NULL" |
| 19 | + ), |
| 20 | + "required_keys": ["censysio_id", "censysio_secret"], |
| 21 | + "options": [ |
| 22 | + ( |
| 23 | + "virtual_hosts", |
| 24 | + "ONLY", |
| 25 | + False, |
| 26 | + "Whether to include virtual hosts in the results", |
| 27 | + ), |
| 28 | + ( |
| 29 | + "per_page", |
| 30 | + "100", |
| 31 | + False, |
| 32 | + "The number of results to return per page", |
| 33 | + ), |
| 34 | + ( |
| 35 | + "pages", |
| 36 | + "1", |
| 37 | + False, |
| 38 | + "The number of pages to retrieve", |
| 39 | + ), |
| 40 | + ], |
| 41 | + "dependencies": ["censys>=2.1.2"], |
16 | 42 | }
|
17 | 43 |
|
18 | 44 | def module_run(self, domains):
|
19 |
| - api_id = self.get_key('censysio_id') |
20 |
| - api_secret = self.get_key('censysio_secret') |
21 |
| - c = CensysIPv4(api_id, api_secret, timeout=self._global_options['timeout']) |
22 |
| - IPV4_FIELDS = [ |
23 |
| - 'ip', |
24 |
| - 'protocols', |
25 |
| - 'location.country', |
26 |
| - 'location.latitude', |
27 |
| - 'location.longitude', |
28 |
| - 'location.province', |
29 |
| - ] |
30 |
| - SEARCH_FIELDS = [ |
31 |
| - '443.https.tls.certificate.parsed.names', |
32 |
| - '25.smtp.starttls.tls.certificate.parsed.names', |
33 |
| - '110.pop3.starttls.tls.certificate.parsed.names', |
34 |
| - ] |
| 45 | + api_id = self.get_key("censysio_id") |
| 46 | + api_secret = self.get_key("censysio_secret") |
| 47 | + c = CensysHosts(api_id, api_secret) |
35 | 48 | for domain in domains:
|
| 49 | + domain = domain.strip('"') |
36 | 50 | self.heading(domain, level=0)
|
37 | 51 | try:
|
38 |
| - query = 'mx:"{0}" OR '.format(domain) + ' OR '.join( |
39 |
| - ['{0}:"{1}"'.format(x, domain) for x in SEARCH_FIELDS] |
| 52 | + query = c.search( |
| 53 | + f"{domain}", |
| 54 | + per_page=int(self.options.get("PER_PAGE", "100")), |
| 55 | + pages=int(self.options.get("PAGES", "1")), |
| 56 | + virtual_hosts=self.options.get("VIRTUAL_HOSTS", "ONLY"), |
40 | 57 | )
|
41 |
| - payload = c.search(query, IPV4_FIELDS + SEARCH_FIELDS) |
42 | 58 | except CensysException:
|
| 59 | + self.print_exception() |
43 | 60 | continue
|
44 |
| - for result in payload: |
45 |
| - names = set() |
46 |
| - for k, v in result.items(): |
47 |
| - if k.endswith('.parsed.names'): |
48 |
| - for name in v: |
49 |
| - names.add(name) |
50 |
| - if len(names) < 1: |
51 |
| - # make sure we have at least a blank name |
52 |
| - names.add('') |
53 |
| - for name in names: |
54 |
| - if name.startswith('*.'): |
55 |
| - self.insert_domains(name.replace('*.', '')) |
56 |
| - continue |
57 |
| - self.insert_hosts( |
58 |
| - host=name, |
59 |
| - ip_address=result['ip'], |
60 |
| - country=result.get('location.country', ''), |
61 |
| - region=result.get('location.province', ''), |
62 |
| - latitude=result.get('location.latitude', ''), |
63 |
| - longitude=result.get('location.longitude', ''), |
64 |
| - ) |
65 |
| - |
66 |
| - for protocol in result['protocols']: |
67 |
| - port, service = protocol.split('/') |
| 61 | + for hit in query(): |
| 62 | + common_kwargs = { |
| 63 | + "ip_address": hit["ip"], |
| 64 | + "host": hit.get("name"), |
| 65 | + } |
| 66 | + location = hit.get("location", {}) |
| 67 | + coords = location.get("coordinates", {}) |
| 68 | + self.insert_hosts( |
| 69 | + region=location.get("continent"), |
| 70 | + country=location.get("country"), |
| 71 | + latitude=coords.get("latitude"), |
| 72 | + longitude=coords.get("longitude"), |
| 73 | + **common_kwargs, |
| 74 | + ) |
| 75 | + for service in hit.get("services", []): |
68 | 76 | self.insert_ports(
|
69 |
| - ip_address=result['ip'], |
70 |
| - host=name, |
71 |
| - port=port, |
72 |
| - protocol=service, |
| 77 | + port=service["port"], |
| 78 | + protocol=service["transport_protocol"], |
| 79 | + notes=service["service_name"], |
| 80 | + **common_kwargs, |
73 | 81 | )
|
0 commit comments