diff --git a/bin/restfulHarvest b/bin/restfulHarvest
index 7cc31812..ae19e97f 100755
--- a/bin/restfulHarvest
+++ b/bin/restfulHarvest
@@ -1,5 +1,6 @@
#!/usr/bin/env python3
import argparse
+
import uvicorn
parser = argparse.ArgumentParser()
diff --git a/bin/theHarvester b/bin/theHarvester
index 2e22c5c0..7004504e 100755
--- a/bin/theHarvester
+++ b/bin/theHarvester
@@ -1,7 +1,8 @@
#!/usr/bin/env python3
# Note: This script runs theHarvester
-import sys
import asyncio
+import sys
+
from theHarvester import __main__
if sys.version_info.major < 3 or sys.version_info.minor < 9:
diff --git a/restfulHarvest.py b/restfulHarvest.py
index d7b02cf7..bde18442 100755
--- a/restfulHarvest.py
+++ b/restfulHarvest.py
@@ -1,14 +1,43 @@
#!/usr/bin/env python3
import argparse
+
import uvicorn
parser = argparse.ArgumentParser()
-parser.add_argument('-H', '--host', default='127.0.0.1', help='IP address to listen on default is 127.0.0.1')
-parser.add_argument('-p', '--port', default=5000, help='Port to bind the web server to, default is 5000', type=int)
-parser.add_argument('-l', '--log-level', default='info', help='Set logging level, default is info but [critical|error|warning|info|debug|trace] can be set')
-parser.add_argument('-r', '--reload', default=False, help='Enable automatic reload used during development of the api', action='store_true')
+parser.add_argument(
+ "-H",
+ "--host",
+ default="127.0.0.1",
+ help="IP address to listen on default is 127.0.0.1",
+)
+parser.add_argument(
+ "-p",
+ "--port",
+ default=5000,
+ help="Port to bind the web server to, default is 5000",
+ type=int,
+)
+parser.add_argument(
+ "-l",
+ "--log-level",
+ default="info",
+ help="Set logging level, default is info but [critical|error|warning|info|debug|trace] can be set",
+)
+parser.add_argument(
+ "-r",
+ "--reload",
+ default=False,
+ help="Enable automatic reload used during development of the api",
+ action="store_true",
+)
args: argparse.Namespace = parser.parse_args()
-if __name__ == '__main__':
- uvicorn.run('theHarvester.lib.api.api:app', host=args.host, port=args.port, log_level=args.log_level, reload=args.reload)
+if __name__ == "__main__":
+ uvicorn.run(
+ "theHarvester.lib.api.api:app",
+ host=args.host,
+ port=args.port,
+ log_level=args.log_level,
+ reload=args.reload,
+ )
diff --git a/setup.py b/setup.py
index 393092e7..aa8904a8 100755
--- a/setup.py
+++ b/setup.py
@@ -1,11 +1,12 @@
-from setuptools import setup, find_packages
+from setuptools import find_packages, setup
+
from theHarvester.lib.version import version
-with open('README.md', 'r') as fh:
+with open("README.md", "r") as fh:
long_description: str = fh.read()
setup(
- name='theHarvester',
+ name="theHarvester",
version=version(),
author="Christian Martorella",
author_email="cmartorella@edge-security.com",
@@ -13,11 +14,9 @@ setup(
long_description=long_description,
long_description_content_type="text/markdown",
url="https://github.com/laramies/theHarvester",
- packages=find_packages(exclude=['tests']),
- python_requires='>=3.9',
- scripts=['bin/theHarvester',
- 'bin/restfulHarvest'],
-
+ packages=find_packages(exclude=["tests"]),
+ python_requires=">=3.9",
+ scripts=["bin/theHarvester", "bin/restfulHarvest"],
classifiers=[
"Programming Language :: Python :: 3",
"Programming Language :: Python :: 3.9",
@@ -27,15 +26,17 @@ setup(
"Operating System :: OS Independent",
],
data_files=[
- ('/etc/theHarvester', [
- 'wordlists/general/common.txt',
- 'wordlists/dns-big.txt',
- 'wordlists/dns-names.txt',
- 'wordlists/dorks.txt',
- 'wordlists/names_small.txt',
- 'api-keys.yaml',
- 'proxies.yaml'
- ]
+ (
+ "/etc/theHarvester",
+ [
+ "wordlists/general/common.txt",
+ "wordlists/dns-big.txt",
+ "wordlists/dns-names.txt",
+ "wordlists/dorks.txt",
+ "wordlists/names_small.txt",
+ "api-keys.yaml",
+ "proxies.yaml",
+ ],
)
],
)
diff --git a/tests/discovery/test_anubis.py b/tests/discovery/test_anubis.py
index 1b1d492b..9786320d 100644
--- a/tests/discovery/test_anubis.py
+++ b/tests/discovery/test_anubis.py
@@ -1,25 +1,29 @@
#!/usr/bin/env python3
# coding=utf-8
-import requests
-from theHarvester.lib.core import *
-from theHarvester.discovery import anubis
import os
-import pytest
-from _pytest.mark.structures import MarkDecorator
from typing import Optional
+import pytest
+import requests
+from _pytest.mark.structures import MarkDecorator
+
+from theHarvester.discovery import anubis
+from theHarvester.lib.core import *
+
pytestmark: MarkDecorator = pytest.mark.asyncio
-github_ci: Optional[str] = os.getenv('GITHUB_ACTIONS') # Github set this to be the following: true instead of True
+github_ci: Optional[str] = os.getenv(
+ "GITHUB_ACTIONS"
+) # Github set this to be the following: true instead of True
class TestAnubis:
@staticmethod
def domain() -> str:
- return 'apple.com'
+ return "apple.com"
async def test_api(self) -> None:
- base_url = f'https://jldc.me/anubis/subdomains/{TestAnubis.domain()}'
- headers = {'User-Agent': Core.get_user_agent()}
+ base_url = f"https://jldc.me/anubis/subdomains/{TestAnubis.domain()}"
+ headers = {"User-Agent": Core.get_user_agent()}
request = requests.get(base_url, headers=headers)
assert request.status_code == 200
diff --git a/tests/discovery/test_certspotter.py b/tests/discovery/test_certspotter.py
index 19152693..b8f98c1f 100644
--- a/tests/discovery/test_certspotter.py
+++ b/tests/discovery/test_certspotter.py
@@ -1,25 +1,29 @@
#!/usr/bin/env python3
# coding=utf-8
-from theHarvester.lib.core import *
-from theHarvester.discovery import certspottersearch
import os
-import requests
-import pytest
-from _pytest.mark.structures import MarkDecorator
from typing import Optional
+import pytest
+import requests
+from _pytest.mark.structures import MarkDecorator
+
+from theHarvester.discovery import certspottersearch
+from theHarvester.lib.core import *
+
pytestmark: MarkDecorator = pytest.mark.asyncio
-github_ci: Optional[str] = os.getenv('GITHUB_ACTIONS') # Github set this to be the following: true instead of True
+github_ci: Optional[str] = os.getenv(
+ "GITHUB_ACTIONS"
+) # Github set this to be the following: true instead of True
class TestCertspotter(object):
@staticmethod
def domain() -> str:
- return 'metasploit.com'
+ return "metasploit.com"
async def test_api(self) -> None:
- base_url = f'https://api.certspotter.com/v1/issuances?domain={TestCertspotter.domain()}&expand=dns_names'
- headers = {'User-Agent': Core.get_user_agent()}
+ base_url = f"https://api.certspotter.com/v1/issuances?domain={TestCertspotter.domain()}&expand=dns_names"
+ headers = {"User-Agent": Core.get_user_agent()}
request = requests.get(base_url, headers=headers)
assert request.status_code == 200
@@ -29,10 +33,10 @@ class TestCertspotter(object):
assert isinstance(await search.get_hostnames(), set)
async def test_search_no_results(self) -> None:
- search = certspottersearch.SearchCertspoter('radiant.eu')
+ search = certspottersearch.SearchCertspoter("radiant.eu")
await search.process()
assert len(await search.get_hostnames()) == 0
-if __name__ == '__main__':
+if __name__ == "__main__":
pytest.main()
diff --git a/tests/discovery/test_githubcode.py b/tests/discovery/test_githubcode.py
index 0eb9154d..ff6ac5f5 100644
--- a/tests/discovery/test_githubcode.py
+++ b/tests/discovery/test_githubcode.py
@@ -1,34 +1,23 @@
+from unittest.mock import MagicMock
+
+import pytest
+from _pytest.mark.structures import MarkDecorator
+from requests import Response
+
from theHarvester.discovery import githubcode
from theHarvester.discovery.constants import MissingKey
from theHarvester.lib.core import Core
-from unittest.mock import MagicMock
-from requests import Response
-import pytest
-from _pytest.mark.structures import MarkDecorator
pytestmark: MarkDecorator = pytest.mark.asyncio
class TestSearchGithubCode:
-
class OkResponse:
response = Response()
json = {
"items": [
- {
- "text_matches": [
- {
- "fragment": "test1"
- }
- ]
- },
- {
- "text_matches": [
- {
- "fragment": "test2"
- }
- ]
- }
+ {"text_matches": [{"fragment": "test1"}]},
+ {"text_matches": [{"fragment": "test2"}]},
]
}
response.status_code = 200
@@ -48,19 +37,9 @@ class TestSearchGithubCode:
response = Response()
json = {
"items": [
- {
- "fail": True
- },
- {
- "text_matches": []
- },
- {
- "text_matches": [
- {
- "weird": "result"
- }
- ]
- }
+ {"fail": True},
+ {"text_matches": []},
+ {"text_matches": [{"weird": "result"}]},
]
}
response.json = MagicMock(return_value=json)
@@ -74,27 +53,31 @@ class TestSearchGithubCode:
async def test_fragments_from_response(self) -> None:
Core.github_key = MagicMock(return_value="lol")
test_class_instance = githubcode.SearchGithubCode(word="test", limit=500)
- test_result = await test_class_instance.fragments_from_response(self.OkResponse.response.json())
- print('test_result: ', test_result)
+ test_result = await test_class_instance.fragments_from_response(
+ self.OkResponse.response.json()
+ )
+ print("test_result: ", test_result)
assert test_result == ["test1", "test2"]
async def test_invalid_fragments_from_response(self) -> None:
Core.github_key = MagicMock(return_value="lol")
test_class_instance = githubcode.SearchGithubCode(word="test", limit=500)
- test_result = await test_class_instance.fragments_from_response(self.MalformedResponse.response.json())
+ test_result = await test_class_instance.fragments_from_response(
+ self.MalformedResponse.response.json()
+ )
assert test_result == []
async def test_next_page(self) -> None:
Core.github_key = MagicMock(return_value="lol")
test_class_instance = githubcode.SearchGithubCode(word="test", limit=500)
test_result = githubcode.SuccessResult(list(), next_page=2, last_page=4)
- assert (2 == await test_class_instance.next_page_or_end(test_result))
+ assert 2 == await test_class_instance.next_page_or_end(test_result)
async def test_last_page(self) -> None:
Core.github_key = MagicMock(return_value="lol")
test_class_instance = githubcode.SearchGithubCode(word="test", limit=500)
test_result = githubcode.SuccessResult(list(), None, None)
- assert (None is await test_class_instance.next_page_or_end(test_result))
+ assert None is await test_class_instance.next_page_or_end(test_result)
- if __name__ == '__main__':
+ if __name__ == "__main__":
pytest.main()
diff --git a/tests/discovery/test_otx.py b/tests/discovery/test_otx.py
index 851afb9d..bb48111a 100644
--- a/tests/discovery/test_otx.py
+++ b/tests/discovery/test_otx.py
@@ -1,25 +1,29 @@
#!/usr/bin/env python3
# coding=utf-8
-from theHarvester.lib.core import *
-from theHarvester.discovery import otxsearch
import os
-import requests
-import pytest
-from _pytest.mark.structures import MarkDecorator
from typing import Optional
+import pytest
+import requests
+from _pytest.mark.structures import MarkDecorator
+
+from theHarvester.discovery import otxsearch
+from theHarvester.lib.core import *
+
pytestmark: MarkDecorator = pytest.mark.asyncio
-github_ci: Optional[str] = os.getenv('GITHUB_ACTIONS') # Github set this to be the following: true instead of True
+github_ci: Optional[str] = os.getenv(
+ "GITHUB_ACTIONS"
+) # Github set this to be the following: true instead of True
class TestOtx(object):
@staticmethod
def domain() -> str:
- return 'metasploit.com'
+ return "metasploit.com"
async def test_api(self) -> None:
- base_url = f'https://otx.alienvault.com/api/v1/indicators/domain/{TestOtx.domain()}/passive_dns'
- headers = {'User-Agent': Core.get_user_agent()}
+ base_url = f"https://otx.alienvault.com/api/v1/indicators/domain/{TestOtx.domain()}/passive_dns"
+ headers = {"User-Agent": Core.get_user_agent()}
request = requests.get(base_url, headers=headers)
assert request.status_code == 200
@@ -30,5 +34,5 @@ class TestOtx(object):
assert isinstance(await search.get_ips(), set)
-if __name__ == '__main__':
+if __name__ == "__main__":
pytest.main()
diff --git a/tests/discovery/test_threatminer.py b/tests/discovery/test_threatminer.py
index e3e13f61..58ba2d9b 100644
--- a/tests/discovery/test_threatminer.py
+++ b/tests/discovery/test_threatminer.py
@@ -1,23 +1,27 @@
#!/usr/bin/env python3
# coding=utf-8
-import requests
-from theHarvester.lib.core import *
-from theHarvester.discovery import threatminer
import os
+
import pytest
+import requests
+
+from theHarvester.discovery import threatminer
+from theHarvester.lib.core import *
pytestmark = pytest.mark.asyncio
-github_ci = os.getenv('GITHUB_ACTIONS') # Github set this to be the following: true instead of True
+github_ci = os.getenv(
+ "GITHUB_ACTIONS"
+) # Github set this to be the following: true instead of True
class TestThreatminer(object):
@staticmethod
def domain() -> str:
- return 'target.com'
+ return "target.com"
async def test_api(self):
- base_url = f'https://api.threatminer.org/v2/domain.php?q={TestThreatminer.domain()}&rt=5'
- headers = {'User-Agent': Core.get_user_agent()}
+ base_url = f"https://api.threatminer.org/v2/domain.php?q={TestThreatminer.domain()}&rt=5"
+ headers = {"User-Agent": Core.get_user_agent()}
request = requests.get(base_url, headers=headers)
assert request.status_code == 200
@@ -28,5 +32,5 @@ class TestThreatminer(object):
assert isinstance(await search.get_ips(), set)
-if __name__ == '__main__':
+if __name__ == "__main__":
pytest.main()
diff --git a/tests/test_myparser.py b/tests/test_myparser.py
index 6e624941..bcbb5e5d 100755
--- a/tests/test_myparser.py
+++ b/tests/test_myparser.py
@@ -1,20 +1,20 @@
#!/usr/bin/env python3
# coding=utf-8
-from theHarvester.parsers import myparser
import pytest
+from theHarvester.parsers import myparser
+
class TestMyParser(object):
-
@pytest.mark.asyncio
async def test_emails(self) -> None:
- word = 'domain.com'
- results = '@domain.com***a@domain***banotherdomain.com***c@domain.com***d@sub.domain.com***'
+ word = "domain.com"
+ results = "@domain.com***a@domain***banotherdomain.com***c@domain.com***d@sub.domain.com***"
parse = myparser.Parser(results, word)
emails = sorted(await parse.emails())
- assert emails, ['c@domain.com', 'd@sub.domain.com']
+ assert emails, ["c@domain.com", "d@sub.domain.com"]
-if __name__ == '__main__':
+if __name__ == "__main__":
pytest.main()
diff --git a/theHarvester.py b/theHarvester.py
index 2e22c5c0..7f1811e5 100755
--- a/theHarvester.py
+++ b/theHarvester.py
@@ -1,16 +1,17 @@
#!/usr/bin/env python3
# Note: This script runs theHarvester
-import sys
import asyncio
+import sys
+
from theHarvester import __main__
if sys.version_info.major < 3 or sys.version_info.minor < 9:
- print('\033[93m[!] Make sure you have Python 3.9+ installed, quitting.\n\n \033[0m')
+ print("\033[93m[!] Make sure you have Python 3.9+ installed, quitting.\n\n \033[0m")
sys.exit(1)
-if __name__ == '__main__':
+if __name__ == "__main__":
platform = sys.platform
- if platform == 'win32':
+ if platform == "win32":
# Required or things will break if trying to take screenshots
import multiprocessing
@@ -18,6 +19,7 @@ if __name__ == '__main__':
asyncio.DefaultEventLoopPolicy = asyncio.WindowsSelectorEventLoopPolicy
else:
import uvloop
+
uvloop.install()
if "linux" in platform:
diff --git a/theHarvester/__main__.py b/theHarvester/__main__.py
index 8fdf088f..9cf8acaf 100644
--- a/theHarvester/__main__.py
+++ b/theHarvester/__main__.py
@@ -1,56 +1,121 @@
#!/usr/bin/env python3
-from typing import Optional, Dict, List
-from theHarvester.discovery import *
-from theHarvester.discovery import dnssearch, takeover, shodansearch
-from theHarvester.discovery.constants import *
-from theHarvester.lib import hostchecker
-from theHarvester.lib import stash
-from theHarvester.lib.core import *
import argparse
import asyncio
-import ujson
-import netaddr
import re
-import sys
-import string
import secrets
+import string
+import sys
+from typing import Dict, List, Optional
+
+import netaddr
+import ujson
+
+from theHarvester.discovery import *
+from theHarvester.discovery import dnssearch, shodansearch, takeover
+from theHarvester.discovery.constants import *
+from theHarvester.lib import hostchecker, stash
+from theHarvester.lib.core import *
async def start(rest_args: Optional[argparse.Namespace] = None):
"""Main program function"""
parser = argparse.ArgumentParser(
- description='theHarvester is used to gather open source intelligence (OSINT) on a company or domain.')
- parser.add_argument('-d', '--domain', help='Company name or domain to search.', required=True)
- parser.add_argument('-l', '--limit', help='Limit the number of search results, default=500.', default=500, type=int)
- parser.add_argument('-S', '--start', help='Start with result number X, default=0.', default=0, type=int)
- parser.add_argument('-p', '--proxies', help='Use proxies for requests, enter proxies in proxies.yaml.',
- default=False, action='store_true')
- parser.add_argument('-s', '--shodan', help='Use Shodan to query discovered hosts.', default=False,
- action='store_true')
- parser.add_argument('--screenshot',
- help='Take screenshots of resolved domains specify output directory: --screenshot output_directory',
- default="", type=str)
- parser.add_argument('-v', '--virtual-host',
- help='Verify host name via DNS resolution and search for virtual hosts.', action='store_const',
- const='basic', default=False)
- parser.add_argument('-e', '--dns-server', help='DNS server to use for lookup.')
- parser.add_argument('-t', '--take-over', help='Check for takeovers.', default=False, action='store_true')
- parser.add_argument('-r', '--dns-resolve',
- help='Perform DNS resolution on subdomains with a resolver list or passed in resolvers, default False.',
- default="", type=str, nargs='?')
- parser.add_argument('-n', '--dns-lookup', help='Enable DNS server lookup, default False.', default=False,
- action='store_true')
- parser.add_argument('-c', '--dns-brute', help='Perform a DNS brute force on the domain.', default=False,
- action='store_true')
- parser.add_argument('-f', '--filename', help='Save the results to an XML and JSON file.', default='', type=str)
- parser.add_argument('-b', '--source', help='''anubis, baidu, bevigil, binaryedge, bing, bingapi, bufferoverun, brave,
+ description="theHarvester is used to gather open source intelligence (OSINT) on a company or domain."
+ )
+ parser.add_argument(
+ "-d", "--domain", help="Company name or domain to search.", required=True
+ )
+ parser.add_argument(
+ "-l",
+ "--limit",
+ help="Limit the number of search results, default=500.",
+ default=500,
+ type=int,
+ )
+ parser.add_argument(
+ "-S",
+ "--start",
+ help="Start with result number X, default=0.",
+ default=0,
+ type=int,
+ )
+ parser.add_argument(
+ "-p",
+ "--proxies",
+ help="Use proxies for requests, enter proxies in proxies.yaml.",
+ default=False,
+ action="store_true",
+ )
+ parser.add_argument(
+ "-s",
+ "--shodan",
+ help="Use Shodan to query discovered hosts.",
+ default=False,
+ action="store_true",
+ )
+ parser.add_argument(
+ "--screenshot",
+ help="Take screenshots of resolved domains specify output directory: --screenshot output_directory",
+ default="",
+ type=str,
+ )
+ parser.add_argument(
+ "-v",
+ "--virtual-host",
+ help="Verify host name via DNS resolution and search for virtual hosts.",
+ action="store_const",
+ const="basic",
+ default=False,
+ )
+ parser.add_argument("-e", "--dns-server", help="DNS server to use for lookup.")
+ parser.add_argument(
+ "-t",
+ "--take-over",
+ help="Check for takeovers.",
+ default=False,
+ action="store_true",
+ )
+ parser.add_argument(
+ "-r",
+ "--dns-resolve",
+ help="Perform DNS resolution on subdomains with a resolver list or passed in resolvers, default False.",
+ default="",
+ type=str,
+ nargs="?",
+ )
+ parser.add_argument(
+ "-n",
+ "--dns-lookup",
+ help="Enable DNS server lookup, default False.",
+ default=False,
+ action="store_true",
+ )
+ parser.add_argument(
+ "-c",
+ "--dns-brute",
+ help="Perform a DNS brute force on the domain.",
+ default=False,
+ action="store_true",
+ )
+ parser.add_argument(
+ "-f",
+ "--filename",
+ help="Save the results to an XML and JSON file.",
+ default="",
+ type=str,
+ )
+ parser.add_argument(
+ "-b",
+ "--source",
+ help="""anubis, baidu, bevigil, binaryedge, bing, bingapi, bufferoverun, brave,
censys, certspotter, criminalip, crtsh, dnsdumpster, duckduckgo, fullhunt, github-code,
hackertarget, hunter, hunterhow, intelx, netlas, onyphe, otx, pentesttools, projectdiscovery,
rapiddns, rocketreach, securityTrails, sitedossier, subdomaincenter, subdomainfinderc99, threatminer, tomba,
- urlscan, virustotal, yahoo, zoomeye''')
+ urlscan, virustotal, yahoo, zoomeye""",
+ )
# determines if filename is coming from rest api or user
- rest_filename = ''
+ rest_filename = ""
# indicates this from the rest API
if rest_args:
if rest_args.source and rest_args.source == "getsources":
@@ -63,8 +128,11 @@ async def start(rest_args: Optional[argparse.Namespace] = None):
# We need to make sure the filename is random as to not overwrite other files
filename: str = args.filename
alphabet = string.ascii_letters + string.digits
- rest_filename += f"{''.join(secrets.choice(alphabet) for _ in range(32))}_{filename}" \
- if len(filename) != 0 else ""
+ rest_filename += (
+ f"{''.join(secrets.choice(alphabet) for _ in range(32))}_{filename}"
+ if len(filename) != 0
+ else ""
+ )
else:
args = parser.parse_args()
filename = args.filename
@@ -75,6 +143,7 @@ async def start(rest_args: Optional[argparse.Namespace] = None):
except Exception:
pass
import os
+
if len(filename) > 2 and filename[:2] == "~/":
filename = os.path.expanduser(filename)
@@ -82,7 +151,9 @@ async def start(rest_args: Optional[argparse.Namespace] = None):
all_hosts: List = []
all_ip: List = []
dnslookup = args.dns_lookup
- dnsserver = args.dns_server # TODO arg is not used anywhere replace with resolvers wordlist arg dnsresolve
+ dnsserver = (
+ args.dns_server
+ ) # TODO arg is not used anywhere replace with resolvers wordlist arg dnsresolve
dnsresolve = args.dns_resolve
final_dns_resolver_list = []
if dnsresolve is not None and len(dnsresolve) > 0:
@@ -91,7 +162,7 @@ async def start(rest_args: Optional[argparse.Namespace] = None):
# 1.1.1.1,8.8.8.8 or 1.1.1.1, 8.8.8.8
# resolvers.txt
if os.path.exists(dnsresolve):
- with open(dnsresolve, mode='r', encoding='UTF-8') as fp:
+ with open(dnsresolve, mode="r", encoding="UTF-8") as fp:
for line in fp:
line = line.strip()
try:
@@ -99,14 +170,16 @@ async def start(rest_args: Optional[argparse.Namespace] = None):
_ = netaddr.IPAddress(line)
final_dns_resolver_list.append(line)
except Exception as e:
- print(f'An exception has occurred while reading from: {dnsresolve}, {e}')
- print(f'Current line: {line}')
+ print(
+ f"An exception has occurred while reading from: {dnsresolve}, {e}"
+ )
+ print(f"Current line: {line}")
return
else:
try:
- if ',' in dnsresolve:
- cleaned = dnsresolve.replace(' ', '')
- for item in cleaned.split(','):
+ if "," in dnsresolve:
+ cleaned = dnsresolve.replace(" ", "")
+ for item in cleaned.split(","):
_ = netaddr.IPAddress(item)
final_dns_resolver_list.append(item)
else:
@@ -114,8 +187,10 @@ async def start(rest_args: Optional[argparse.Namespace] = None):
_ = netaddr.IPAddress(dnsresolve)
final_dns_resolver_list.append(dnsresolve)
except Exception as e:
- print(f'Passed in DNS resolvers are invalid double check, got error: {e}')
- print(f'Dumping resolvers passed in: {e}')
+ print(
+ f"Passed in DNS resolvers are invalid double check, got error: {e}"
+ )
+ print(f"Dumping resolvers passed in: {e}")
sys.exit(0)
# if for some reason, there are duplicates
@@ -132,7 +207,7 @@ async def start(rest_args: Optional[argparse.Namespace] = None):
all_urls: list = []
vhost: list = []
virtual = args.virtual_host
- word: str = args.domain.rstrip('\n')
+ word: str = args.domain.rstrip("\n")
takeover_status = args.take_over
use_proxy = args.proxies
linkedin_people_list_tracker: List = []
@@ -148,10 +223,19 @@ async def start(rest_args: Optional[argparse.Namespace] = None):
interesting_urls = []
total_asns = []
- async def store(search_engine: Any, source: str, process_param: Any = None, store_host: bool = False,
- store_emails: bool = False, store_ip: bool = False, store_people: bool = False,
- store_links: bool = False, store_results: bool = False,
- store_interestingurls: bool = False, store_asns: bool = False) -> None:
+ async def store(
+ search_engine: Any,
+ source: str,
+ process_param: Any = None,
+ store_host: bool = False,
+ store_emails: bool = False,
+ store_ip: bool = False,
+ store_people: bool = False,
+ store_links: bool = False,
+ store_results: bool = False,
+ store_interestingurls: bool = False,
+ store_asns: bool = False,
+ ) -> None:
"""
Persist details into the database.
The details to be stored are controlled by the parameters passed to the method.
@@ -168,24 +252,43 @@ async def start(rest_args: Optional[argparse.Namespace] = None):
:param store_interestingurls: whether to store interesting urls
:param store_asns: whether to store asns
"""
- await search_engine.process(use_proxy) if process_param is None else await \
- search_engine.process(process_param, use_proxy)
+ await search_engine.process(
+ use_proxy
+ ) if process_param is None else await search_engine.process(
+ process_param, use_proxy
+ )
db_stash = stash.StashManager()
if source:
- print(f'\033[94m[*] Searching {source[0].upper() + source[1:]}. ')
+ print(f"\033[94m[*] Searching {source[0].upper() + source[1:]}. ")
if store_host:
- host_names = list({host for host in filter(await search_engine.get_hostnames()) if f'.{word}' in host})
+ host_names = list(
+ {
+ host
+ for host in filter(await search_engine.get_hostnames())
+ if f".{word}" in host
+ }
+ )
host_names = list(host_names)
- if source != 'hackertarget' and source != 'pentesttools' and source != 'rapiddns':
+ if (
+ source != "hackertarget"
+ and source != "pentesttools"
+ and source != "rapiddns"
+ ):
# If a source is inside this conditional, it means the hosts returned must be resolved to obtain ip
# This should only be checked if --dns-resolve has a wordlist
if dnsresolve is None or len(final_dns_resolver_list) > 0:
# indicates that -r was passed in if dnsresolve is None
- full_hosts_checker = hostchecker.Checker(host_names, final_dns_resolver_list)
+ full_hosts_checker = hostchecker.Checker(
+ host_names, final_dns_resolver_list
+ )
# If full this is only getting resolved hosts
- resolved_pair, temp_hosts, temp_ips = await full_hosts_checker.check()
+ (
+ resolved_pair,
+ temp_hosts,
+ temp_ips,
+ ) = await full_hosts_checker.check()
all_ip.extend(temp_ips)
full.extend(resolved_pair)
# full.extend(temp_hosts)
@@ -194,382 +297,610 @@ async def start(rest_args: Optional[argparse.Namespace] = None):
else:
full.extend(host_names)
all_hosts.extend(host_names)
- await db_stash.store_all(word, all_hosts, 'host', source)
+ await db_stash.store_all(word, all_hosts, "host", source)
if store_emails:
email_list = filter(await search_engine.get_emails())
all_emails.extend(email_list)
- await db_stash.store_all(word, email_list, 'email', source)
+ await db_stash.store_all(word, email_list, "email", source)
if store_ip:
ips_list = await search_engine.get_ips()
all_ip.extend(ips_list)
- await db_stash.store_all(word, all_ip, 'ip', source)
+ await db_stash.store_all(word, all_ip, "ip", source)
if store_results:
email_list, host_names, urls = await search_engine.get_results()
all_emails.extend(email_list)
- host_names = [host for host in filter(host_names) if f'.{word}' in host]
+ host_names = [host for host in filter(host_names) if f".{word}" in host]
all_urls.extend(filter(urls))
all_hosts.extend(host_names)
- await db.store_all(word, all_hosts, 'host', source)
- await db.store_all(word, all_emails, 'email', source)
+ await db.store_all(word, all_hosts, "host", source)
+ await db.store_all(word, all_emails, "email", source)
if store_people:
people_list = await search_engine.get_people()
- await db_stash.store_all(word, people_list, 'people', source)
+ await db_stash.store_all(word, people_list, "people", source)
if store_links:
links = await search_engine.get_links()
linkedin_links_tracker.extend(links)
if len(links) > 0:
- await db.store_all(word, links, 'linkedinlinks', engineitem)
+ await db.store_all(word, links, "linkedinlinks", engineitem)
if store_interestingurls:
iurls = await search_engine.get_interestingurls()
interesting_urls.extend(iurls)
if len(iurls) > 0:
- await db.store_all(word, iurls, 'interestingurls', engineitem)
+ await db.store_all(word, iurls, "interestingurls", engineitem)
if store_asns:
fasns = await search_engine.get_asns()
total_asns.extend(fasns)
if len(fasns) > 0:
- await db.store_all(word, fasns, 'asns', engineitem)
+ await db.store_all(word, fasns, "asns", engineitem)
stor_lst = []
if args.source is not None:
- if args.source.lower() != 'all':
- engines = sorted(set(map(str.strip, args.source.split(','))))
+ if args.source.lower() != "all":
+ engines = sorted(set(map(str.strip, args.source.split(","))))
else:
engines = Core.get_supportedengines()
# Iterate through search engines in order
if set(engines).issubset(Core.get_supportedengines()):
- print(f'\n[*] Target: {word} \n')
+ print(f"\n[*] Target: {word} \n")
for engineitem in engines:
- if engineitem == 'anubis':
+ if engineitem == "anubis":
from theHarvester.discovery import anubis
+
try:
anubis_search = anubis.SearchAnubis(word)
- stor_lst.append(store(anubis_search, engineitem, store_host=True))
+ stor_lst.append(
+ store(anubis_search, engineitem, store_host=True)
+ )
except Exception as e:
print(e)
- elif engineitem == 'baidu':
+ elif engineitem == "baidu":
from theHarvester.discovery import baidusearch
+
try:
baidu_search = baidusearch.SearchBaidu(word, limit)
- stor_lst.append(store(baidu_search, engineitem, store_host=True, store_emails=True))
+ stor_lst.append(
+ store(
+ baidu_search,
+ engineitem,
+ store_host=True,
+ store_emails=True,
+ )
+ )
except Exception as e:
print(e)
- elif engineitem == 'bevigil':
+ elif engineitem == "bevigil":
from theHarvester.discovery import bevigil
+
try:
bevigil_search = bevigil.SearchBeVigil(word)
- stor_lst.append(store(bevigil_search, engineitem, store_host=True, store_interestingurls=True))
+ stor_lst.append(
+ store(
+ bevigil_search,
+ engineitem,
+ store_host=True,
+ store_interestingurls=True,
+ )
+ )
except Exception as e:
print(e)
- elif engineitem == 'binaryedge':
+ elif engineitem == "binaryedge":
from theHarvester.discovery import binaryedgesearch
+
try:
- binaryedge_search = binaryedgesearch.SearchBinaryEdge(word, limit)
- stor_lst.append(store(binaryedge_search, engineitem, store_host=True))
+ binaryedge_search = binaryedgesearch.SearchBinaryEdge(
+ word, limit
+ )
+ stor_lst.append(
+ store(binaryedge_search, engineitem, store_host=True)
+ )
except Exception as e:
print(e)
- elif engineitem == 'bing' or engineitem == 'bingapi':
+ elif engineitem == "bing" or engineitem == "bingapi":
from theHarvester.discovery import bingsearch
+
try:
bing_search = bingsearch.SearchBing(word, limit, start)
- bingapi = ''
- if engineitem == 'bingapi':
- bingapi += 'yes'
+ bingapi = ""
+ if engineitem == "bingapi":
+ bingapi += "yes"
else:
- bingapi += 'no'
+ bingapi += "no"
stor_lst.append(
- store(bing_search, 'bing', process_param=bingapi, store_host=True, store_emails=True))
+ store(
+ bing_search,
+ "bing",
+ process_param=bingapi,
+ store_host=True,
+ store_emails=True,
+ )
+ )
except Exception as e:
if isinstance(e, MissingKey):
print(e)
else:
print(e)
- elif engineitem == 'bufferoverun':
+ elif engineitem == "bufferoverun":
from theHarvester.discovery import bufferoverun
+
try:
bufferoverun_search = bufferoverun.SearchBufferover(word)
- stor_lst.append(store(bufferoverun_search, engineitem, store_host=True, store_ip=True))
+ stor_lst.append(
+ store(
+ bufferoverun_search,
+ engineitem,
+ store_host=True,
+ store_ip=True,
+ )
+ )
except Exception as e:
print(e)
- elif engineitem == 'brave':
+ elif engineitem == "brave":
from theHarvester.discovery import bravesearch
+
try:
brave_search = bravesearch.SearchBrave(word, limit)
- stor_lst.append(store(brave_search, engineitem, store_host=True, store_emails=True))
+ stor_lst.append(
+ store(
+ brave_search,
+ engineitem,
+ store_host=True,
+ store_emails=True,
+ )
+ )
except Exception as e:
print(e)
- elif engineitem == 'censys':
+ elif engineitem == "censys":
from theHarvester.discovery import censysearch
+
try:
censys_search = censysearch.SearchCensys(word, limit)
- stor_lst.append(store(censys_search, engineitem, store_host=True, store_emails=True))
+ stor_lst.append(
+ store(
+ censys_search,
+ engineitem,
+ store_host=True,
+ store_emails=True,
+ )
+ )
except Exception as e:
if isinstance(e, MissingKey):
print(e)
- elif engineitem == 'certspotter':
+ elif engineitem == "certspotter":
from theHarvester.discovery import certspottersearch
+
try:
certspotter_search = certspottersearch.SearchCertspoter(word)
- stor_lst.append(store(certspotter_search, engineitem, None, store_host=True))
+ stor_lst.append(
+ store(certspotter_search, engineitem, None, store_host=True)
+ )
except Exception as e:
print(e)
- elif engineitem == 'criminalip':
+ elif engineitem == "criminalip":
from theHarvester.discovery import criminalip
+
try:
criminalip_search = criminalip.SearchCriminalIP(word)
- stor_lst.append(store(criminalip_search, engineitem, store_host=True, store_ip=True,
- store_asns=True))
+ stor_lst.append(
+ store(
+ criminalip_search,
+ engineitem,
+ store_host=True,
+ store_ip=True,
+ store_asns=True,
+ )
+ )
except Exception as e:
if isinstance(e, MissingKey):
print(e)
else:
- print(f'An excepion has occurred in criminalip: {e}')
+ print(f"An excepion has occurred in criminalip: {e}")
- elif engineitem == 'crtsh':
+ elif engineitem == "crtsh":
try:
from theHarvester.discovery import crtsh
- crtsh_search = crtsh.SearchCrtsh(word)
- stor_lst.append(store(crtsh_search, 'CRTsh', store_host=True))
- except Exception as e:
- print(f'[!] A timeout occurred with crtsh, cannot find {args.domain}\n {e}')
- elif engineitem == 'dnsdumpster':
+ crtsh_search = crtsh.SearchCrtsh(word)
+ stor_lst.append(store(crtsh_search, "CRTsh", store_host=True))
+ except Exception as e:
+ print(
+ f"[!] A timeout occurred with crtsh, cannot find {args.domain}\n {e}"
+ )
+
+ elif engineitem == "dnsdumpster":
try:
from theHarvester.discovery import dnsdumpster
+
dns_dumpster_search = dnsdumpster.SearchDnsDumpster(word)
- stor_lst.append(store(dns_dumpster_search, engineitem, store_host=True, store_ip=True))
+ stor_lst.append(
+ store(
+ dns_dumpster_search,
+ engineitem,
+ store_host=True,
+ store_ip=True,
+ )
+ )
except Exception as e:
- print(f'[!] An error occurred with dnsdumpster: {e}')
+ print(f"[!] An error occurred with dnsdumpster: {e}")
- elif engineitem == 'duckduckgo':
+ elif engineitem == "duckduckgo":
from theHarvester.discovery import duckduckgosearch
- duckduckgo_search = duckduckgosearch.SearchDuckDuckGo(word, limit)
- stor_lst.append(store(duckduckgo_search, engineitem, store_host=True, store_emails=True))
- elif engineitem == 'fullhunt':
+ duckduckgo_search = duckduckgosearch.SearchDuckDuckGo(word, limit)
+ stor_lst.append(
+ store(
+ duckduckgo_search,
+ engineitem,
+ store_host=True,
+ store_emails=True,
+ )
+ )
+
+ elif engineitem == "fullhunt":
from theHarvester.discovery import fullhuntsearch
+
try:
fullhunt_search = fullhuntsearch.SearchFullHunt(word)
- stor_lst.append(store(fullhunt_search, engineitem, store_host=True))
+ stor_lst.append(
+ store(fullhunt_search, engineitem, store_host=True)
+ )
except Exception as e:
if isinstance(e, MissingKey):
print(e)
- elif engineitem == 'github-code':
+ elif engineitem == "github-code":
try:
from theHarvester.discovery import githubcode
+
github_search = githubcode.SearchGithubCode(word, limit)
- stor_lst.append(store(github_search, engineitem, store_host=True, store_emails=True))
+ stor_lst.append(
+ store(
+ github_search,
+ engineitem,
+ store_host=True,
+ store_emails=True,
+ )
+ )
except MissingKey as ex:
print(ex)
- elif engineitem == 'hackertarget':
+ elif engineitem == "hackertarget":
from theHarvester.discovery import hackertarget
- hackertarget_search = hackertarget.SearchHackerTarget(word)
- stor_lst.append(store(hackertarget_search, engineitem, store_host=True))
- elif engineitem == 'hunter':
+ hackertarget_search = hackertarget.SearchHackerTarget(word)
+ stor_lst.append(
+ store(hackertarget_search, engineitem, store_host=True)
+ )
+
+ elif engineitem == "hunter":
from theHarvester.discovery import huntersearch
+
# Import locally or won't work.
try:
hunter_search = huntersearch.SearchHunter(word, limit, start)
- stor_lst.append(store(hunter_search, engineitem, store_host=True, store_emails=True))
+ stor_lst.append(
+ store(
+ hunter_search,
+ engineitem,
+ store_host=True,
+ store_emails=True,
+ )
+ )
except Exception as e:
if isinstance(e, MissingKey):
print(e)
- elif engineitem == 'hunterhow':
+ elif engineitem == "hunterhow":
from theHarvester.discovery import searchhunterhow
+
try:
hunterhow_search = searchhunterhow.SearchHunterHow(word)
- stor_lst.append(store(hunterhow_search, engineitem, store_host=True))
+ stor_lst.append(
+ store(hunterhow_search, engineitem, store_host=True)
+ )
except Exception as e:
if isinstance(e, MissingKey):
print(e)
else:
- print(f'An exception has occurred in hunterhow search: {e}')
+ print(f"An exception has occurred in hunterhow search: {e}")
- elif engineitem == 'intelx':
+ elif engineitem == "intelx":
from theHarvester.discovery import intelxsearch
+
# Import locally or won't work.
try:
intelx_search = intelxsearch.SearchIntelx(word)
- stor_lst.append(store(intelx_search, engineitem, store_interestingurls=True, store_emails=True))
+ stor_lst.append(
+ store(
+ intelx_search,
+ engineitem,
+ store_interestingurls=True,
+ store_emails=True,
+ )
+ )
except Exception as e:
if isinstance(e, MissingKey):
print(e)
else:
- print(f'An exception has occurred in Intelx search: {e}')
+ print(f"An exception has occurred in Intelx search: {e}")
- elif engineitem == 'netlas':
+ elif engineitem == "netlas":
from theHarvester.discovery import netlas
+
try:
netlas_search = netlas.SearchNetlas(word)
- stor_lst.append(store(netlas_search, engineitem, store_host=True, store_ip=True))
+ stor_lst.append(
+ store(
+ netlas_search,
+ engineitem,
+ store_host=True,
+ store_ip=True,
+ )
+ )
except Exception as e:
if isinstance(e, MissingKey):
print(e)
- elif engineitem == 'onyphe':
+ elif engineitem == "onyphe":
from theHarvester.discovery import onyphe
+
try:
onyphe_search = onyphe.SearchOnyphe(word)
- stor_lst.append(store(onyphe_search, engineitem, store_host=True, store_ip=True,
- store_asns=True))
+ stor_lst.append(
+ store(
+ onyphe_search,
+ engineitem,
+ store_host=True,
+ store_ip=True,
+ store_asns=True,
+ )
+ )
except Exception as e:
print(e)
- elif engineitem == 'otx':
+ elif engineitem == "otx":
from theHarvester.discovery import otxsearch
+
try:
otxsearch_search = otxsearch.SearchOtx(word)
- stor_lst.append(store(otxsearch_search, engineitem, store_host=True, store_ip=True))
+ stor_lst.append(
+ store(
+ otxsearch_search,
+ engineitem,
+ store_host=True,
+ store_ip=True,
+ )
+ )
except Exception as e:
print(e)
- elif engineitem == 'pentesttools':
+ elif engineitem == "pentesttools":
from theHarvester.discovery import pentesttools
+
try:
pentesttools_search = pentesttools.SearchPentestTools(word)
- stor_lst.append(store(pentesttools_search, engineitem, store_host=True))
+ stor_lst.append(
+ store(pentesttools_search, engineitem, store_host=True)
+ )
except Exception as e:
if isinstance(e, MissingKey):
print(e)
else:
- print(f'An exception has occurred in PentestTools search: {e}')
+ print(
+ f"An exception has occurred in PentestTools search: {e}"
+ )
- elif engineitem == 'projectdiscovery':
+ elif engineitem == "projectdiscovery":
from theHarvester.discovery import projectdiscovery
+
try:
projectdiscovery_search = projectdiscovery.SearchDiscovery(word)
- stor_lst.append(store(projectdiscovery_search, engineitem, store_host=True))
+ stor_lst.append(
+ store(projectdiscovery_search, engineitem, store_host=True)
+ )
except Exception as e:
if isinstance(e, MissingKey):
print(e)
else:
- print('An exception has occurred in ProjectDiscovery')
+ print("An exception has occurred in ProjectDiscovery")
- elif engineitem == 'rapiddns':
+ elif engineitem == "rapiddns":
from theHarvester.discovery import rapiddns
+
try:
rapiddns_search = rapiddns.SearchRapidDns(word)
- stor_lst.append(store(rapiddns_search, engineitem, store_host=True))
+ stor_lst.append(
+ store(rapiddns_search, engineitem, store_host=True)
+ )
except Exception as e:
print(e)
- elif engineitem == 'rocketreach':
+ elif engineitem == "rocketreach":
from theHarvester.discovery import rocketreach
+
try:
rocketreach_search = rocketreach.SearchRocketReach(word, limit)
- stor_lst.append(store(rocketreach_search, engineitem, store_links=True))
+ stor_lst.append(
+ store(rocketreach_search, engineitem, store_links=True)
+ )
except Exception as e:
if isinstance(e, MissingKey):
print(e)
else:
- print(f'An exception has occurred in RocketReach: {e}')
+ print(f"An exception has occurred in RocketReach: {e}")
- elif engineitem == 'subdomaincenter':
+ elif engineitem == "subdomaincenter":
from theHarvester.discovery import subdomaincenter
+
try:
subdomaincenter_search = subdomaincenter.SubdomainCenter(word)
- stor_lst.append(store(subdomaincenter_search, engineitem, store_host=True))
+ stor_lst.append(
+ store(subdomaincenter_search, engineitem, store_host=True)
+ )
except Exception as e:
print(e)
- elif engineitem == 'securityTrails':
+ elif engineitem == "securityTrails":
from theHarvester.discovery import securitytrailssearch
+
try:
- securitytrails_search = securitytrailssearch.SearchSecuritytrail(word)
- stor_lst.append(store(securitytrails_search, engineitem, store_host=True, store_ip=True))
+ securitytrails_search = (
+ securitytrailssearch.SearchSecuritytrail(word)
+ )
+ stor_lst.append(
+ store(
+ securitytrails_search,
+ engineitem,
+ store_host=True,
+ store_ip=True,
+ )
+ )
except Exception as e:
if isinstance(e, MissingKey):
print(e)
- elif engineitem == 'sitedossier':
+ elif engineitem == "sitedossier":
from theHarvester.discovery import sitedossier
+
try:
sitedossier_search = sitedossier.SearchSitedossier(word)
- stor_lst.append(store(sitedossier_search, engineitem, store_host=True))
+ stor_lst.append(
+ store(sitedossier_search, engineitem, store_host=True)
+ )
except Exception as e:
print(e)
- elif engineitem == 'subdomainfinderc99':
+ elif engineitem == "subdomainfinderc99":
from theHarvester.discovery import subdomainfinderc99
+
try:
- subdomainfinderc99_search = subdomainfinderc99.SearchSubdomainfinderc99(word)
- stor_lst.append(store(subdomainfinderc99_search, engineitem, store_host=True))
+ subdomainfinderc99_search = (
+ subdomainfinderc99.SearchSubdomainfinderc99(word)
+ )
+ stor_lst.append(
+ store(
+ subdomainfinderc99_search, engineitem, store_host=True
+ )
+ )
except Exception as e:
if isinstance(e, MissingKey):
print(e)
else:
- print(f'An exception has occurred in Subdomainfinderc99 search: {e}')
+ print(
+ f"An exception has occurred in Subdomainfinderc99 search: {e}"
+ )
- elif engineitem == 'threatminer':
+ elif engineitem == "threatminer":
from theHarvester.discovery import threatminer
+
try:
threatminer_search = threatminer.SearchThreatminer(word)
- stor_lst.append(store(threatminer_search, engineitem, store_host=True, store_ip=True))
+ stor_lst.append(
+ store(
+ threatminer_search,
+ engineitem,
+ store_host=True,
+ store_ip=True,
+ )
+ )
except Exception as e:
print(e)
- elif engineitem == 'tomba':
+ elif engineitem == "tomba":
try:
from theHarvester.discovery import tombasearch
+
tomba_search = tombasearch.SearchTomba(word, limit, start)
- stor_lst.append(store(tomba_search, engineitem, store_host=True, store_emails=True))
+ stor_lst.append(
+ store(
+ tomba_search,
+ engineitem,
+ store_host=True,
+ store_emails=True,
+ )
+ )
except Exception as e:
if isinstance(e, MissingKey):
print(e)
- elif engineitem == 'urlscan':
+ elif engineitem == "urlscan":
from theHarvester.discovery import urlscan
+
try:
urlscan_search = urlscan.SearchUrlscan(word)
- stor_lst.append(store(urlscan_search, engineitem, store_host=True, store_ip=True,
- store_interestingurls=True, store_asns=True))
+ stor_lst.append(
+ store(
+ urlscan_search,
+ engineitem,
+ store_host=True,
+ store_ip=True,
+ store_interestingurls=True,
+ store_asns=True,
+ )
+ )
except Exception as e:
print(e)
- elif engineitem == 'virustotal':
+ elif engineitem == "virustotal":
try:
from theHarvester.discovery import virustotal
+
virustotal_search = virustotal.SearchVirustotal(word)
- stor_lst.append(store(virustotal_search, engineitem, store_host=True))
+ stor_lst.append(
+ store(virustotal_search, engineitem, store_host=True)
+ )
except Exception as e:
if isinstance(e, MissingKey):
print(e)
- elif engineitem == 'yahoo':
+ elif engineitem == "yahoo":
from theHarvester.discovery import yahoosearch
+
try:
yahoo_search = yahoosearch.SearchYahoo(word, limit)
- stor_lst.append(store(yahoo_search, engineitem, store_host=True, store_emails=True))
+ stor_lst.append(
+ store(
+ yahoo_search,
+ engineitem,
+ store_host=True,
+ store_emails=True,
+ )
+ )
except Exception as e:
print(e)
- elif engineitem == 'zoomeye':
+ elif engineitem == "zoomeye":
try:
from theHarvester.discovery import zoomeyesearch
+
zoomeye_search = zoomeyesearch.SearchZoomEye(word, limit)
- stor_lst.append(store(zoomeye_search, engineitem, store_host=True, store_emails=True,
- store_ip=True, store_interestingurls=True, store_asns=True))
+ stor_lst.append(
+ store(
+ zoomeye_search,
+ engineitem,
+ store_host=True,
+ store_emails=True,
+ store_ip=True,
+ store_interestingurls=True,
+ store_asns=True,
+ )
+ )
except Exception as e:
if isinstance(e, MissingKey):
print(e)
@@ -578,10 +909,10 @@ async def start(rest_args: Optional[argparse.Namespace] = None):
try:
rest_args.dns_brute
except Exception:
- print('\n[!] Invalid source.\n')
+ print("\n[!] Invalid source.\n")
sys.exit(1)
else:
- print('\n[!] Invalid source.\n')
+ print("\n[!] Invalid source.\n")
sys.exit(1)
async def worker(queue):
@@ -617,157 +948,191 @@ async def start(rest_args: Optional[argparse.Namespace] = None):
await handler(lst=stor_lst)
return_ips: List = []
- if rest_args is not None and len(rest_filename) == 0 and rest_args.dns_brute is False:
+ if (
+ rest_args is not None
+ and len(rest_filename) == 0
+ and rest_args.dns_brute is False
+ ):
# Indicates user is using REST api but not wanting output to be saved to a file
# for host in full:
# full = [host if ':' in host and word in host else word in host.split(':')[0] and host for host in full]
# full = list({host for host in full if host})
# full.sort()
# cast to string so Rest API can understand type
- return_ips.extend([str(ip) for ip in sorted([netaddr.IPAddress(ip.strip()) for ip in set(all_ip)])])
+ return_ips.extend(
+ [
+ str(ip)
+ for ip in sorted([netaddr.IPAddress(ip.strip()) for ip in set(all_ip)])
+ ]
+ )
# return list(set(all_emails)), return_ips, full, '', ''
- all_hosts = [host.replace('www.', '') for host in all_hosts if host.replace('www.', '') in all_hosts]
+ all_hosts = [
+ host.replace("www.", "")
+ for host in all_hosts
+ if host.replace("www.", "") in all_hosts
+ ]
all_hosts = list(sorted(set(all_hosts)))
- return total_asns, interesting_urls, twitter_people_list_tracker, linkedin_people_list_tracker, \
- linkedin_links_tracker, all_urls, all_ip, all_emails, all_hosts
+ return (
+ total_asns,
+ interesting_urls,
+ twitter_people_list_tracker,
+ linkedin_people_list_tracker,
+ linkedin_links_tracker,
+ all_urls,
+ all_ip,
+ all_emails,
+ all_hosts,
+ )
# Check to see if all_emails and all_hosts are defined.
try:
all_emails
except NameError:
- print('\n\n[!] No emails found because all_emails is not defined.\n\n ')
+ print("\n\n[!] No emails found because all_emails is not defined.\n\n ")
sys.exit(1)
try:
all_hosts
except NameError:
- print('\n\n[!] No hosts found because all_hosts is not defined.\n\n ')
+ print("\n\n[!] No hosts found because all_hosts is not defined.\n\n ")
sys.exit(1)
# Results
if len(total_asns) > 0:
- print(f'\n[*] ASNS found: {len(total_asns)}')
- print('--------------------')
+ print(f"\n[*] ASNS found: {len(total_asns)}")
+ print("--------------------")
total_asns = list(sorted(set(total_asns)))
for asn in total_asns:
print(asn)
if len(interesting_urls) > 0:
- print(f'\n[*] Interesting Urls found: {len(interesting_urls)}')
- print('--------------------')
+ print(f"\n[*] Interesting Urls found: {len(interesting_urls)}")
+ print("--------------------")
interesting_urls = list(sorted(set(interesting_urls)))
for iurl in interesting_urls:
print(iurl)
- if len(twitter_people_list_tracker) == 0 and 'twitter' in engines:
- print('\n[*] No Twitter users found.\n\n')
+ if len(twitter_people_list_tracker) == 0 and "twitter" in engines:
+ print("\n[*] No Twitter users found.\n\n")
else:
if len(twitter_people_list_tracker) >= 1:
- print('\n[*] Twitter Users found: ' + str(len(twitter_people_list_tracker)))
- print('---------------------')
+ print("\n[*] Twitter Users found: " + str(len(twitter_people_list_tracker)))
+ print("---------------------")
twitter_people_list_tracker = list(sorted(set(twitter_people_list_tracker)))
for usr in twitter_people_list_tracker:
print(usr)
- if len(linkedin_people_list_tracker) == 0 and 'linkedin' in engines:
- print('\n[*] No LinkedIn users found.\n\n')
+ if len(linkedin_people_list_tracker) == 0 and "linkedin" in engines:
+ print("\n[*] No LinkedIn users found.\n\n")
else:
if len(linkedin_people_list_tracker) >= 1:
- print('\n[*] LinkedIn Users found: ' + str(len(linkedin_people_list_tracker)))
- print('---------------------')
- linkedin_people_list_tracker = list(sorted(set(linkedin_people_list_tracker)))
+ print(
+ "\n[*] LinkedIn Users found: " + str(len(linkedin_people_list_tracker))
+ )
+ print("---------------------")
+ linkedin_people_list_tracker = list(
+ sorted(set(linkedin_people_list_tracker))
+ )
for usr in linkedin_people_list_tracker:
print(usr)
- if len(linkedin_links_tracker) == 0 and ('linkedin' in engines or 'rocketreach' in engines):
- print(f'\n[*] LinkedIn Links found: {len(linkedin_links_tracker)}')
+ if len(linkedin_links_tracker) == 0 and (
+ "linkedin" in engines or "rocketreach" in engines
+ ):
+ print(f"\n[*] LinkedIn Links found: {len(linkedin_links_tracker)}")
linkedin_links_tracker = list(sorted(set(linkedin_links_tracker)))
- print('---------------------')
+ print("---------------------")
for link in linkedin_people_list_tracker:
print(link)
length_urls = len(all_urls)
if length_urls == 0:
- if len(engines) >= 1 and 'trello' in engines:
- print('\n[*] No Trello URLs found.')
+ if len(engines) >= 1 and "trello" in engines:
+ print("\n[*] No Trello URLs found.")
else:
total = length_urls
- print('\n[*] Trello URLs found: ' + str(total))
- print('--------------------')
+ print("\n[*] Trello URLs found: " + str(total))
+ print("--------------------")
all_urls = list(sorted(set(all_urls)))
for url in sorted(all_urls):
print(url)
if len(all_ip) == 0:
- print('\n[*] No IPs found.')
+ print("\n[*] No IPs found.")
else:
- print('\n[*] IPs found: ' + str(len(all_ip)))
- print('-------------------')
+ print("\n[*] IPs found: " + str(len(all_ip)))
+ print("-------------------")
# use netaddr as the list may contain ipv4 and ipv6 addresses
ip_list = []
for ip in set(all_ip):
try:
ip = ip.strip()
if len(ip) > 0:
- if '/' in ip:
+ if "/" in ip:
ip_list.append(str(netaddr.IPNetwork(ip)))
else:
ip_list.append(str(netaddr.IPAddress(ip)))
except Exception as e:
- print(f'An exception has occurred while adding: {ip} to ip_list: {e}')
+ print(f"An exception has occurred while adding: {ip} to ip_list: {e}")
continue
ip_list = list(sorted(ip_list))
- print('\n'.join(map(str, ip_list)))
+ print("\n".join(map(str, ip_list)))
if len(all_emails) == 0:
- print('\n[*] No emails found.')
+ print("\n[*] No emails found.")
else:
- print('\n[*] Emails found: ' + str(len(all_emails)))
- print('----------------------')
+ print("\n[*] Emails found: " + str(len(all_emails)))
+ print("----------------------")
all_emails = sorted(list(set(all_emails)))
- print(('\n'.join(all_emails)))
+ print(("\n".join(all_emails)))
if len(all_hosts) == 0:
- print('\n[*] No hosts found.\n\n')
+ print("\n[*] No hosts found.\n\n")
else:
db = stash.StashManager()
if dnsresolve is None or len(final_dns_resolver_list) > 0:
temp = set()
for host in full:
- if ':' in host:
+ if ":" in host:
# TODO parse addresses and sort them as they are IPs
- subdomain, addr = host.split(':')
+ subdomain, addr = host.split(":")
if subdomain.endswith(word):
- temp.add(subdomain + ':' + addr)
+ temp.add(subdomain + ":" + addr)
continue
if host.endswith(word):
- if host[:4] == 'www.':
+ if host[:4] == "www.":
if host[4:] in all_hosts or host[4:] in full:
temp.add(host[4:])
continue
temp.add(host)
full = list(sorted(temp))
- full.sort(key=lambda el: el.split(':')[0])
- print('\n[*] Hosts found: ' + str(len(full)))
- print('---------------------')
+ full.sort(key=lambda el: el.split(":")[0])
+ print("\n[*] Hosts found: " + str(len(full)))
+ print("---------------------")
for host in full:
print(host)
try:
- if ':' in host:
- _, addr = host.split(':')
- await db.store(word, addr, 'ip', 'DNS-resolver')
+ if ":" in host:
+ _, addr = host.split(":")
+ await db.store(word, addr, "ip", "DNS-resolver")
except Exception as e:
- print(f'An exception has occurred while attempting to insert: {host} IP into DB: {e}')
+ print(
+ f"An exception has occurred while attempting to insert: {host} IP into DB: {e}"
+ )
continue
else:
- all_hosts = [host.replace('www.', '') for host in all_hosts if host.replace('www.', '') in all_hosts]
+ all_hosts = [
+ host.replace("www.", "")
+ for host in all_hosts
+ if host.replace("www.", "") in all_hosts
+ ]
all_hosts = list(sorted(set(all_hosts)))
- print('\n[*] Hosts found: ' + str(len(all_hosts)))
- print('---------------------')
+ print("\n[*] Hosts found: " + str(len(all_hosts)))
+ print("---------------------")
for host in all_hosts:
print(host)
# DNS brute force
if dnsbrute and dnsbrute[0] is True:
- print('\n[*] Starting DNS brute force.')
+ print("\n[*] Starting DNS brute force.")
dns_force = dnssearch.DnsForce(word, final_dns_resolver_list, verbose=True)
resolved_pair, hosts, ips = await dns_force.run()
# hosts = list({host for host in hosts if ':' in host})
@@ -778,19 +1143,19 @@ async def start(rest_args: Optional[argparse.Namespace] = None):
db = stash.StashManager()
temp = set()
for host in resolved_pair:
- if ':' in host:
+ if ":" in host:
# TODO parse addresses and sort them as they are IPs
- subdomain, addr = host.split(':')
+ subdomain, addr = host.split(":")
if subdomain.endswith(word):
# Append to full so it's within JSON/XML at the end if output file is requested
if host not in full:
full.append(host)
- temp.add(subdomain + ':' + addr)
+ temp.add(subdomain + ":" + addr)
if host not in all_hosts:
all_hosts.append(host)
continue
if host.endswith(word):
- if host[:4] == 'www.':
+ if host[:4] == "www.":
if host[4:] in all_hosts or host[4:] in full:
continue
if host not in full:
@@ -798,16 +1163,16 @@ async def start(rest_args: Optional[argparse.Namespace] = None):
temp.add(host)
if host not in all_hosts:
all_hosts.append(host)
- print('\n[*] Hosts found after DNS brute force:')
+ print("\n[*] Hosts found after DNS brute force:")
for sub in temp:
print(sub)
- await db.store_all(word, list(sorted(temp)), 'host', 'dns_bruteforce')
+ await db.store_all(word, list(sorted(temp)), "host", "dns_bruteforce")
takeover_results = dict()
# TakeOver Checking
if takeover_status:
- print('\n[*] Performing subdomain takeover check')
- print('\n[*] Subdomain Takeover checking IS ACTIVE RECON')
+ print("\n[*] Performing subdomain takeover check")
+ print("\n[*] Subdomain Takeover checking IS ACTIVE RECON")
search_take = takeover.TakeOver(all_hosts)
await search_take.populate_fingerprints()
await search_take.process(proxy=use_proxy)
@@ -816,51 +1181,56 @@ async def start(rest_args: Optional[argparse.Namespace] = None):
dnsrev: List = []
# print(f'DNSlookup: {dnslookup}')
if dnslookup is True:
- print('\n[*] Starting active queries for DNSLookup.')
+ print("\n[*] Starting active queries for DNSLookup.")
# load the reverse dns tools
from theHarvester.discovery.dnssearch import (
generate_postprocessing_callback,
reverse_all_ips_in_range,
- serialize_ip_range)
+ serialize_ip_range,
+ )
# reverse each iprange in a separate task
__reverse_dns_tasks: Dict = {}
for entry in host_ip:
- __ip_range = serialize_ip_range(ip=entry, netmask='24')
+ __ip_range = serialize_ip_range(ip=entry, netmask="24")
if __ip_range and __ip_range not in set(__reverse_dns_tasks.keys()):
- print('\n[*] Performing reverse lookup on ' + __ip_range)
- __reverse_dns_tasks[__ip_range] = asyncio.create_task(reverse_all_ips_in_range(
- iprange=__ip_range,
- callback=generate_postprocessing_callback(
- target=word,
- local_results=dnsrev,
- overall_results=full),
- nameservers=final_dns_resolver_list if len(final_dns_resolver_list) > 0 else None))
+ print("\n[*] Performing reverse lookup on " + __ip_range)
+ __reverse_dns_tasks[__ip_range] = asyncio.create_task(
+ reverse_all_ips_in_range(
+ iprange=__ip_range,
+ callback=generate_postprocessing_callback(
+ target=word, local_results=dnsrev, overall_results=full
+ ),
+ nameservers=final_dns_resolver_list
+ if len(final_dns_resolver_list) > 0
+ else None,
+ )
+ )
# nameservers=list(map(str, dnsserver.split(','))) if dnsserver else None))
# run all the reversing tasks concurrently
await asyncio.gather(*__reverse_dns_tasks.values())
# Display the newly found hosts
- print('\n[*] Hosts found after reverse lookup (in target domain):')
- print('--------------------------------------------------------')
+ print("\n[*] Hosts found after reverse lookup (in target domain):")
+ print("--------------------------------------------------------")
for xh in dnsrev:
print(xh)
# Virtual hosts search
- if virtual == 'basic':
- print('\n[*] Virtual hosts:')
- print('------------------')
+ if virtual == "basic":
+ print("\n[*] Virtual hosts:")
+ print("------------------")
for data in host_ip:
basic_search = bingsearch.SearchBing(data, limit, start)
await basic_search.process_vhost()
results = await basic_search.get_allhostnames()
for result in results:
- result = re.sub(r'[[?]*\w*>]*', '', result)
- result = re.sub('<', '', result)
- result = re.sub('>', '', result)
- print((data + '\t' + result))
- vhost.append(data + ':' + result)
- full.append(data + ':' + result)
+ result = re.sub(r"[[?]*\w*>]*", "", result)
+ result = re.sub("<", "", result)
+ result = re.sub(">", "", result)
+ print((data + "\t" + result))
+ vhost.append(data + ":" + result)
+ full.append(data + ":" + result)
vhost = sorted(set(vhost))
else:
pass
@@ -869,115 +1239,152 @@ async def start(rest_args: Optional[argparse.Namespace] = None):
screenshot_tups = []
if len(args.screenshot) > 0:
import time
+
from aiomultiprocess import Pool
+
from theHarvester.screenshot.screenshot import ScreenShotter
+
screen_shotter = ScreenShotter(args.screenshot)
path_exists = screen_shotter.verify_path()
# Verify path exists, if not create it or if user does not create it skips screenshot
if path_exists:
await screen_shotter.verify_installation()
- print(f'\nScreenshots can be found in: {screen_shotter.output}{screen_shotter.slash}')
+ print(
+ f"\nScreenshots can be found in: {screen_shotter.output}{screen_shotter.slash}"
+ )
start_time = time.perf_counter()
- print('Filtering domains for ones we can reach')
+ print("Filtering domains for ones we can reach")
if dnsresolve is None or len(final_dns_resolver_list) > 0:
- unique_resolved_domains = {url.split(':')[0] for url in full if ':' in url and 'www.' not in url}
+ unique_resolved_domains = {
+ url.split(":")[0]
+ for url in full
+ if ":" in url and "www." not in url
+ }
else:
# Technically not resolved in this case which is not ideal
# You should always use dns resolve when doing screenshotting
- print('NOTE for future use cases you should only use screenshotting in tandem with DNS resolving')
+ print(
+ "NOTE for future use cases you should only use screenshotting in tandem with DNS resolving"
+ )
unique_resolved_domains = set(all_hosts)
if len(unique_resolved_domains) > 0:
# First filter out ones that didn't resolve
- print('Attempting to visit unique resolved domains, this is ACTIVE RECON')
+ print(
+ "Attempting to visit unique resolved domains, this is ACTIVE RECON"
+ )
async with Pool(10) as pool:
- results = await pool.map(screen_shotter.visit, list(unique_resolved_domains))
+ results = await pool.map(
+ screen_shotter.visit, list(unique_resolved_domains)
+ )
# Filter out domains that we couldn't connect to
- unique_resolved_domains_list = list(sorted({tup[0] for tup in results if len(tup[1]) > 0}))
+ unique_resolved_domains_list = list(
+ sorted({tup[0] for tup in results if len(tup[1]) > 0})
+ )
async with Pool(3) as pool:
- print(f'Length of unique resolved domains: {len(unique_resolved_domains_list)} chunking now!\n')
+ print(
+ f"Length of unique resolved domains: {len(unique_resolved_domains_list)} chunking now!\n"
+ )
# If you have the resources, you could make the function faster by increasing the chunk number
chunk_number = 14
- for chunk in screen_shotter.chunk_list(unique_resolved_domains_list, chunk_number):
+ for chunk in screen_shotter.chunk_list(
+ unique_resolved_domains_list, chunk_number
+ ):
try:
- screenshot_tups.extend(await pool.map(screen_shotter.take_screenshot, chunk))
+ screenshot_tups.extend(
+ await pool.map(screen_shotter.take_screenshot, chunk)
+ )
except Exception as ee:
- print(f'An exception has occurred while mapping: {ee}')
+ print(f"An exception has occurred while mapping: {ee}")
end = time.perf_counter()
# There is probably an easier way to do this
total = int(end - start_time)
mon, sec = divmod(total, 60)
hr, mon = divmod(mon, 60)
total_time = "%02d:%02d" % (mon, sec)
- print(f'Finished taking screenshots in {total_time} seconds')
- print('[+] Note there may be leftover chrome processes you may have to kill manually\n')
+ print(f"Finished taking screenshots in {total_time} seconds")
+ print(
+ "[+] Note there may be leftover chrome processes you may have to kill manually\n"
+ )
# Shodan
shodanres = []
if shodan is True:
- print('\033[94m[*] Searching Shodan. ')
+ print("\033[94m[*] Searching Shodan. ")
try:
for ip in host_ip:
# TODO fix shodan
- print(('\tSearching for ' + ip))
+ print(("\tSearching for " + ip))
shodan = shodansearch.SearchShodan()
shodandict = await shodan.search_ip(ip)
await asyncio.sleep(5)
rowdata = []
for key, value in shodandict[ip].items():
- if str(value) == 'Not in Shodan' or 'Error occurred in the Shodan IP search module' in str(value):
+ if str(
+ value
+ ) == "Not in Shodan" or "Error occurred in the Shodan IP search module" in str(
+ value
+ ):
break
if isinstance(value, int):
value = str(value)
if isinstance(value, list):
- value = ', '.join(map(str, value))
+ value = ", ".join(map(str, value))
rowdata.append(value)
shodanres.append(rowdata)
print(ujson.dumps(shodandict[ip], indent=4, sort_keys=True))
- print('\n')
+ print("\n")
except Exception as e:
- print(f'[!] An error occurred with Shodan: {e} ')
+ print(f"[!] An error occurred with Shodan: {e} ")
else:
pass
- if filename != '':
- print('\n[*] Reporting started.')
+ if filename != "":
+ print("\n[*] Reporting started.")
try:
if len(rest_filename) == 0:
- filename = filename.rsplit('.', 1)[0] + '.xml'
+ filename = filename.rsplit(".", 1)[0] + ".xml"
else:
- filename = 'theHarvester/app/static/' + rest_filename.rsplit('.', 1)[0] + '.xml'
+ filename = (
+ "theHarvester/app/static/"
+ + rest_filename.rsplit(".", 1)[0]
+ + ".xml"
+ )
# TODO use aiofiles if user is using rest api
# XML REPORT SECTION
- with open(filename, 'w+') as file:
+ with open(filename, "w+") as file:
file.write('')
for x in all_emails:
- file.write('' + x + '')
+ file.write("" + x + "")
for x in full:
- host, ip = x.split(':', 1) if ':' in x else (x, '')
+ host, ip = x.split(":", 1) if ":" in x else (x, "")
if ip and len(ip) > 3:
- file.write(f'{ip}{host}')
+ file.write(
+ f"{ip}{host}"
+ )
else:
- file.write(f'{host}')
+ file.write(f"{host}")
for x in vhost:
- host, ip = x.split(':', 1) if ':' in x else (x, '')
+ host, ip = x.split(":", 1) if ":" in x else (x, "")
if ip and len(ip) > 3:
- file.write(f'{ip} {host}')
+ file.write(
+ f"{ip} {host}"
+ )
else:
- file.write(f'{host}')
+ file.write(f"{host}")
# TODO add Shodan output into XML report
- file.write('')
- print('[*] XML File saved.')
+ file.write("")
+ print("[*] XML File saved.")
except Exception as error:
- print(f'[!] An error occurred while saving the XML file: {error}')
+ print(f"[!] An error occurred while saving the XML file: {error}")
try:
# JSON REPORT SECTION
- filename = filename.rsplit('.', 1)[0] + '.json'
+ filename = filename.rsplit(".", 1)[0] + ".json"
# create dict with values for json output
json_dict: Dict = dict()
# determine if variable exists
# it should but just a validation check
- if 'ip_list' in locals():
+ if "ip_list" in locals():
if all_ip and len(all_ip) >= 1 and ip_list and len(ip_list) > 0:
json_dict["ips"] = ip_list
@@ -1013,16 +1420,16 @@ async def start(rest_args: Optional[argparse.Namespace] = None):
json_dict["linkedin_links"] = linkedin_links_tracker
if takeover_status and len(takeover_results) > 0:
- json_dict['takeover_results'] = takeover_results
+ json_dict["takeover_results"] = takeover_results
json_dict["shodan"] = shodanres
- with open(filename, 'w+') as fp:
+ with open(filename, "w+") as fp:
dumped_json = ujson.dumps(json_dict, sort_keys=True)
fp.write(dumped_json)
- print('[*] JSON File saved.')
+ print("[*] JSON File saved.")
except Exception as er:
- print(f'[!] An error occurred while saving the JSON file: {er} ')
- print('\n\n')
+ print(f"[!] An error occurred while saving the JSON file: {er} ")
+ print("\n\n")
sys.exit(0)
@@ -1031,7 +1438,7 @@ async def entry_point() -> None:
Core.banner()
await start()
except KeyboardInterrupt:
- print('\n\n[!] ctrl+c detected from user, quitting.\n\n ')
+ print("\n\n[!] ctrl+c detected from user, quitting.\n\n ")
except Exception as error_entry_point:
print(error_entry_point)
sys.exit(1)
diff --git a/theHarvester/discovery/anubis.py b/theHarvester/discovery/anubis.py
index f59fb849..672cd256 100644
--- a/theHarvester/discovery/anubis.py
+++ b/theHarvester/discovery/anubis.py
@@ -1,16 +1,16 @@
from typing import Type
+
from theHarvester.lib.core import *
class SearchAnubis:
-
def __init__(self, word) -> None:
self.word = word
self.totalhosts: List = []
self.proxy = False
async def do_search(self) -> None:
- url = f'https://jldc.me/anubis/subdomains/{self.word}'
+ url = f"https://jldc.me/anubis/subdomains/{self.word}"
response = await AsyncFetcher.fetch_all([url], json=True, proxy=self.proxy)
self.totalhosts = response[0]
diff --git a/theHarvester/discovery/baidusearch.py b/theHarvester/discovery/baidusearch.py
index 60ca1319..d714c33f 100644
--- a/theHarvester/discovery/baidusearch.py
+++ b/theHarvester/discovery/baidusearch.py
@@ -3,23 +3,25 @@ from theHarvester.parsers import myparser
class SearchBaidu:
-
def __init__(self, word, limit) -> None:
self.word = word
self.total_results = ""
- self.server = 'www.baidu.com'
- self.hostname = 'www.baidu.com'
+ self.server = "www.baidu.com"
+ self.hostname = "www.baidu.com"
self.limit = limit
self.proxy = False
async def do_search(self) -> None:
- headers = {
- 'Host': self.hostname,
- 'User-agent': Core.get_user_agent()
- }
- base_url = f'https://{self.server}/s?wd=%40{self.word}&pn=xx&oq={self.word}'
- urls = [base_url.replace("xx", str(num)) for num in range(0, self.limit, 10) if num <= self.limit]
- responses = await AsyncFetcher.fetch_all(urls, headers=headers, proxy=self.proxy)
+ headers = {"Host": self.hostname, "User-agent": Core.get_user_agent()}
+ base_url = f"https://{self.server}/s?wd=%40{self.word}&pn=xx&oq={self.word}"
+ urls = [
+ base_url.replace("xx", str(num))
+ for num in range(0, self.limit, 10)
+ if num <= self.limit
+ ]
+ responses = await AsyncFetcher.fetch_all(
+ urls, headers=headers, proxy=self.proxy
+ )
for response in responses:
self.total_results += response
diff --git a/theHarvester/discovery/bevigil.py b/theHarvester/discovery/bevigil.py
index 757aed62..e21b5aeb 100644
--- a/theHarvester/discovery/bevigil.py
+++ b/theHarvester/discovery/bevigil.py
@@ -1,10 +1,10 @@
+from typing import Set
+
from theHarvester.discovery.constants import MissingKey
from theHarvester.lib.core import *
-from typing import Set
class SearchBeVigil:
-
def __init__(self, word) -> None:
self.word = word
self.totalhosts: Set = set()
@@ -12,20 +12,24 @@ class SearchBeVigil:
self.key = Core.bevigil_key()
if self.key is None:
self.key = ""
- raise MissingKey('bevigil')
+ raise MissingKey("bevigil")
self.proxy = False
async def do_search(self) -> None:
subdomain_endpoint = f"https://osint.bevigil.com/api/{self.word}/subdomains/"
url_endpoint = f"https://osint.bevigil.com/api/{self.word}/urls/"
- headers = {'X-Access-Token': self.key}
+ headers = {"X-Access-Token": self.key}
- responses = await AsyncFetcher.fetch_all([subdomain_endpoint], json=True, proxy=self.proxy, headers=headers)
+ responses = await AsyncFetcher.fetch_all(
+ [subdomain_endpoint], json=True, proxy=self.proxy, headers=headers
+ )
response = responses[0]
for subdomain in response["subdomains"]:
self.totalhosts.add(subdomain)
- responses = await AsyncFetcher.fetch_all([url_endpoint], json=True, proxy=self.proxy, headers=headers)
+ responses = await AsyncFetcher.fetch_all(
+ [url_endpoint], json=True, proxy=self.proxy, headers=headers
+ )
response = responses[0]
for url in response["urls"]:
self.interestingurls.add(url)
diff --git a/theHarvester/discovery/binaryedgesearch.py b/theHarvester/discovery/binaryedgesearch.py
index 036f9e36..95c4815d 100644
--- a/theHarvester/discovery/binaryedgesearch.py
+++ b/theHarvester/discovery/binaryedgesearch.py
@@ -1,10 +1,10 @@
-from theHarvester.discovery.constants import *
-from typing import Set
import asyncio
+from typing import Set
+
+from theHarvester.discovery.constants import *
class SearchBinaryEdge:
-
def __init__(self, word, limit) -> None:
self.word = word
self.totalhosts: Set = set()
@@ -13,24 +13,29 @@ class SearchBinaryEdge:
self.limit = 501 if limit >= 501 else limit
self.limit = 2 if self.limit == 1 else self.limit
if self.key is None:
- raise MissingKey('binaryedge')
+ raise MissingKey("binaryedge")
async def do_search(self) -> None:
- base_url = f'https://api.binaryedge.io/v2/query/domains/subdomain/{self.word}'
- headers = {'X-KEY': self.key, 'User-Agent': Core.get_user_agent()}
+ base_url = f"https://api.binaryedge.io/v2/query/domains/subdomain/{self.word}"
+ headers = {"X-KEY": self.key, "User-Agent": Core.get_user_agent()}
for page in range(1, self.limit):
- params = {'page': page}
- response = await AsyncFetcher.fetch_all([base_url], json=True, proxy=self.proxy, params=params, headers=headers)
+ params = {"page": page}
+ response = await AsyncFetcher.fetch_all(
+ [base_url], json=True, proxy=self.proxy, params=params, headers=headers
+ )
responses = response[0]
dct = responses
- if ('status' in dct.keys() and 'message' in dct.keys()) and \
- (dct['status'] == 400 or 'Bad Parameter' in dct['message'] or 'Error' in dct['message']):
+ if ("status" in dct.keys() and "message" in dct.keys()) and (
+ dct["status"] == 400
+ or "Bad Parameter" in dct["message"]
+ or "Error" in dct["message"]
+ ):
# 400 status code means no more results
break
- if 'events' in dct.keys():
- if len(dct['events']) == 0:
+ if "events" in dct.keys():
+ if len(dct["events"]) == 0:
break
- self.totalhosts.update({host for host in dct['events']})
+ self.totalhosts.update({host for host in dct["events"]})
await asyncio.sleep(get_delay())
async def get_hostnames(self) -> set:
diff --git a/theHarvester/discovery/bravesearch.py b/theHarvester/discovery/bravesearch.py
index 5f191d41..e65b5969 100644
--- a/theHarvester/discovery/bravesearch.py
+++ b/theHarvester/discovery/bravesearch.py
@@ -1,37 +1,44 @@
+import asyncio
+
from theHarvester.discovery.constants import *
from theHarvester.parsers import myparser
-import asyncio
class SearchBrave:
-
def __init__(self, word, limit):
self.word = word
self.results = ""
self.totalresults = ""
- self.server = 'https://search.brave.com/search?q='
+ self.server = "https://search.brave.com/search?q="
self.limit = limit
self.proxy = False
async def do_search(self):
- headers = {'User-Agent': Core.get_user_agent()}
- for query in [f'"{self.word}"', f'site:{self.word}']:
+ headers = {"User-Agent": Core.get_user_agent()}
+ for query in [f'"{self.word}"', f"site:{self.word}"]:
try:
for offset in range(0, 50):
# To reduce total number of requests only two queries are made "self.word" and site:self.word
- current_url = f'{self.server}{query}&offset={offset}&source=web&show_local=0&spellcheck=0'
- resp = await AsyncFetcher.fetch_all([current_url], headers=headers, proxy=self.proxy)
+ current_url = f"{self.server}{query}&offset={offset}&source=web&show_local=0&spellcheck=0"
+ resp = await AsyncFetcher.fetch_all(
+ [current_url], headers=headers, proxy=self.proxy
+ )
self.results = resp[0]
self.totalresults += self.results
# if 'Results from Microsoft Bing.' in resp[0] \
- if 'Not many great matches came back for your search' in resp[0] \
- or 'Your request has been flagged as being suspicious and Brave Search' in resp[0] \
- or 'Prove' in resp[0] and 'robot' in resp[0] or 'Robot' in resp[0]:
+ if (
+ "Not many great matches came back for your search" in resp[0]
+ or "Your request has been flagged as being suspicious and Brave Search"
+ in resp[0]
+ or "Prove" in resp[0]
+ and "robot" in resp[0]
+ or "Robot" in resp[0]
+ ):
await asyncio.sleep(get_delay() + 80)
break
await asyncio.sleep(get_delay() + 10)
except Exception as e:
- print(f'An exception has occurred in bravesearch: {e}')
+ print(f"An exception has occurred in bravesearch: {e}")
await asyncio.sleep(get_delay() + 80)
continue
diff --git a/theHarvester/discovery/bufferoverun.py b/theHarvester/discovery/bufferoverun.py
index 84256d8c..a953de2d 100644
--- a/theHarvester/discovery/bufferoverun.py
+++ b/theHarvester/discovery/bufferoverun.py
@@ -1,8 +1,9 @@
import re
-from theHarvester.lib.core import *
-from theHarvester.discovery.constants import MissingKey
from typing import Set
+from theHarvester.discovery.constants import MissingKey
+from theHarvester.lib.core import *
+
class SearchBufferover:
def __init__(self, word) -> None:
@@ -11,20 +12,32 @@ class SearchBufferover:
self.totalips: Set = set()
self.key = Core.bufferoverun_key()
if self.key is None:
- raise MissingKey('bufferoverun')
+ raise MissingKey("bufferoverun")
self.proxy = False
async def do_search(self) -> None:
- url = f'https://tls.bufferover.run/dns?q={self.word}'
- response = await AsyncFetcher.fetch_all([url], json=True, headers={'User-Agent': Core.get_user_agent(),
- 'x-api-key': f'{self.key}'}, proxy=self.proxy)
+ url = f"https://tls.bufferover.run/dns?q={self.word}"
+ response = await AsyncFetcher.fetch_all(
+ [url],
+ json=True,
+ headers={"User-Agent": Core.get_user_agent(), "x-api-key": f"{self.key}"},
+ proxy=self.proxy,
+ )
dct = response[0]
- if dct['Results']:
+ if dct["Results"]:
self.totalhosts = {
- host.split(',')if ',' in host and self.word.replace('www.', '') in host.split(',')[0] in host else
- host.split(',')[4] for host in dct['Results']}
+ host.split(",")
+ if "," in host
+ and self.word.replace("www.", "") in host.split(",")[0] in host
+ else host.split(",")[4]
+ for host in dct["Results"]
+ }
- self.totalips = {ip.split(',')[0] for ip in dct['Results'] if re.match(r"^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}$", ip.split(',')[0])}
+ self.totalips = {
+ ip.split(",")[0]
+ for ip in dct["Results"]
+ if re.match(r"^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}$", ip.split(",")[0])
+ }
async def get_hostnames(self) -> set:
return self.totalhosts
diff --git a/theHarvester/discovery/certspottersearch.py b/theHarvester/discovery/certspottersearch.py
index b4efe40d..93619da8 100644
--- a/theHarvester/discovery/certspottersearch.py
+++ b/theHarvester/discovery/certspottersearch.py
@@ -1,28 +1,30 @@
-from theHarvester.lib.core import *
from typing import Set
+from theHarvester.lib.core import *
+
class SearchCertspoter:
-
def __init__(self, word) -> None:
self.word = word
self.totalhosts: Set = set()
self.proxy = False
async def do_search(self) -> None:
- base_url = f'https://api.certspotter.com/v1/issuances?domain={self.word}&expand=dns_names'
+ base_url = f"https://api.certspotter.com/v1/issuances?domain={self.word}&expand=dns_names"
try:
- response = await AsyncFetcher.fetch_all([base_url], json=True, proxy=self.proxy)
+ response = await AsyncFetcher.fetch_all(
+ [base_url], json=True, proxy=self.proxy
+ )
response = response[0]
if isinstance(response, list):
for dct in response:
for key, value in dct.items():
- if key == 'dns_names':
+ if key == "dns_names":
self.totalhosts.update({name for name in value if name})
elif isinstance(response, dict):
- self.totalhosts.update({response['dns_names'] if 'dns_names' in response.keys() else ''}) # type: ignore
+ self.totalhosts.update({response["dns_names"] if "dns_names" in response.keys() else ""}) # type: ignore
else:
- self.totalhosts.update({''})
+ self.totalhosts.update({""})
except Exception as e:
print(e)
@@ -32,4 +34,4 @@ class SearchCertspoter:
async def process(self, proxy: bool = False) -> None:
self.proxy = proxy
await self.do_search()
- print('\tSearching results.')
+ print("\tSearching results.")
diff --git a/theHarvester/discovery/criminalip.py b/theHarvester/discovery/criminalip.py
index 65230f97..be5af679 100644
--- a/theHarvester/discovery/criminalip.py
+++ b/theHarvester/discovery/criminalip.py
@@ -1,9 +1,11 @@
-from theHarvester.lib.core import *
-from theHarvester.discovery.constants import MissingKey, get_delay
from typing import Set
-import ujson
from urllib.parse import urlparse
+import ujson
+
+from theHarvester.discovery.constants import MissingKey, get_delay
+from theHarvester.lib.core import *
+
class SearchCriminalIP:
def __init__(self, word) -> None:
@@ -13,50 +15,64 @@ class SearchCriminalIP:
self.asns: Set = set()
self.key = Core.criminalip_key()
if self.key is None:
- raise MissingKey('criminalip')
+ raise MissingKey("criminalip")
self.proxy = False
async def do_search(self) -> None:
# https://www.criminalip.io/developer/api/post-domain-scan
# https://www.criminalip.io/developer/api/get-domain-status-id
# https://www.criminalip.io/developer/api/get-domain-report-id
- url = 'https://api.criminalip.io/v1/domain/scan'
+ url = "https://api.criminalip.io/v1/domain/scan"
data = f'{{"query": "{self.word}"}}'
# print(f'Current key: {self.key}')
user_agent = Core.get_user_agent()
- response = await AsyncFetcher.post_fetch(url, json=True, headers={'User-Agent': user_agent,
- 'x-api-key': f"{self.key}"}, data=data,
- proxy=self.proxy)
+ response = await AsyncFetcher.post_fetch(
+ url,
+ json=True,
+ headers={"User-Agent": user_agent, "x-api-key": f"{self.key}"},
+ data=data,
+ proxy=self.proxy,
+ )
# print(f'My response: {response}')
# Expected response format:
# {'data': {'scan_id': scan_id}, 'message': 'api success', 'status': 200}
- if 'status' in response.keys():
- status = response['status']
+ if "status" in response.keys():
+ status = response["status"]
if status != 200:
- print(f'An error has occurred searching criminalip dumping response: {response}')
+ print(
+ f"An error has occurred searching criminalip dumping response: {response}"
+ )
else:
- scan_id = response['data']['scan_id']
+ scan_id = response["data"]["scan_id"]
scan_percentage = 0
counter = 0
while scan_percentage != 100:
status_url = f"https://api.criminalip.io/v1/domain/status/{scan_id}"
- status_response = await AsyncFetcher.fetch_all([status_url], json=True,
- headers={'User-Agent': user_agent,
- 'x-api-key': f"{self.key}"},
- proxy=self.proxy)
+ status_response = await AsyncFetcher.fetch_all(
+ [status_url],
+ json=True,
+ headers={"User-Agent": user_agent, "x-api-key": f"{self.key}"},
+ proxy=self.proxy,
+ )
status = status_response[0]
# print(f'Status response: {status}')
# Expected format:
# {"data": {"scan_percentage": 100}, "message": "api success", "status": 200}
- scan_percentage = status['data']['scan_percentage']
+ scan_percentage = status["data"]["scan_percentage"]
if scan_percentage == 100:
break
if scan_percentage == -2:
- print(f'CriminalIP failed to scan: {self.word} does not exist, verify manually')
- print(f'Dumping data: scan_response: {response} status_response: {status}')
+ print(
+ f"CriminalIP failed to scan: {self.word} does not exist, verify manually"
+ )
+ print(
+ f"Dumping data: scan_response: {response} status_response: {status}"
+ )
return
if scan_percentage == -1:
- print(f'CriminalIP scan failed dumping data: scan_response: {response} status_response: {status}')
+ print(
+ f"CriminalIP scan failed dumping data: scan_response: {response} status_response: {status}"
+ )
return
# Wait for scan to finish
if counter >= 5:
@@ -65,127 +81,146 @@ class SearchCriminalIP:
await asyncio.sleep(10 * get_delay())
counter += 1
if counter == 10:
- print('Ten iterations have occurred in CriminalIP waiting for scan to finish, returning to prevent infinite loop.')
- print(f'Verify results manually on CriminalIP dumping data: scan_response: {response} status_response: {status}')
+ print(
+ "Ten iterations have occurred in CriminalIP waiting for scan to finish, returning to prevent infinite loop."
+ )
+ print(
+ f"Verify results manually on CriminalIP dumping data: scan_response: {response} status_response: {status}"
+ )
return
report_url = f"https://api.criminalip.io/v1/domain/report/{scan_id}"
- scan_response = await AsyncFetcher.fetch_all([report_url], json=True, headers={'User-Agent': user_agent,
- 'x-api-key': f"{self.key}"},
- proxy=self.proxy)
+ scan_response = await AsyncFetcher.fetch_all(
+ [report_url],
+ json=True,
+ headers={"User-Agent": user_agent, "x-api-key": f"{self.key}"},
+ proxy=self.proxy,
+ )
scan = scan_response[0]
# json_formatted_str = json.dumps(scan, indent=2)
# print(json_formatted_str)
try:
await self.parser(scan)
except Exception as e:
- print(f'An exception occurred while parsing criminalip result: {e}')
- print('Dumping json: ')
+ print(f"An exception occurred while parsing criminalip result: {e}")
+ print("Dumping json: ")
print(scan)
async def parser(self, jlines):
# TODO when new scope field is added to parse lines for potential new scope!
# TODO map as_name to asn for asn data
# TODO determine if worth storing interesting urls
- if 'data' not in jlines.keys():
- print(f'Error with criminalip data, dumping: {jlines}')
+ if "data" not in jlines.keys():
+ print(f"Error with criminalip data, dumping: {jlines}")
return
- data = jlines['data']
- for cert in data['certificates']:
+ data = jlines["data"]
+ for cert in data["certificates"]:
# print(f'Current cert: {cert}')
- if cert['subject'].endswith('.' + self.word):
- self.totalhosts.add(cert['subject'])
+ if cert["subject"].endswith("." + self.word):
+ self.totalhosts.add(cert["subject"])
- for connected_domain in data['connected_domain_subdomain']:
+ for connected_domain in data["connected_domain_subdomain"]:
try:
- main_domain = connected_domain['main_domain']['domain']
- subdomains = [sub['domain'] for sub in connected_domain['subdomains']]
- if main_domain.endswith('.' + self.word):
+ main_domain = connected_domain["main_domain"]["domain"]
+ subdomains = [sub["domain"] for sub in connected_domain["subdomains"]]
+ if main_domain.endswith("." + self.word):
self.totalhosts.add(main_domain)
for sub in subdomains:
# print(f'Current sub: {sub}')
- if sub.endswith('.' + self.word):
+ if sub.endswith("." + self.word):
self.totalhosts.add(sub)
except Exception as e:
- print(f'An exception has occurred: {e}')
- print(f'Main line: {connected_domain}')
+ print(f"An exception has occurred: {e}")
+ print(f"Main line: {connected_domain}")
- for ip_info in data['connected_ip_info']:
- self.asns.add(str(ip_info['asn']))
- domains = [sub['domain'] for sub in ip_info['domain_list']]
+ for ip_info in data["connected_ip_info"]:
+ self.asns.add(str(ip_info["asn"]))
+ domains = [sub["domain"] for sub in ip_info["domain_list"]]
for sub in domains:
- if sub.endswith('.' + self.word):
+ if sub.endswith("." + self.word):
self.totalhosts.add(sub)
- self.totalips.add(ip_info['ip'])
+ self.totalips.add(ip_info["ip"])
- for cookie in data['cookies']:
- if cookie['domain'] != '.' + self.word and cookie['domain'].endswith('.' + self.word):
- self.totalhosts.add(cookie['domain'])
+ for cookie in data["cookies"]:
+ if cookie["domain"] != "." + self.word and cookie["domain"].endswith(
+ "." + self.word
+ ):
+ self.totalhosts.add(cookie["domain"])
- for country in data['country']:
- if country['domain'].endswith('.' + self.word):
- self.totalhosts.add(country['domain'])
- for ip in country['mapped_ips']:
- self.totalips.add(ip['ip'])
+ for country in data["country"]:
+ if country["domain"].endswith("." + self.word):
+ self.totalhosts.add(country["domain"])
+ for ip in country["mapped_ips"]:
+ self.totalips.add(ip["ip"])
- for k, v in data['dns_record'].items():
- if k == 'dns_record_type_a':
- for ip in data['dns_record'][k]['ipv4']:
- self.totalips.add(ip['ip'])
+ for k, v in data["dns_record"].items():
+ if k == "dns_record_type_a":
+ for ip in data["dns_record"][k]["ipv4"]:
+ self.totalips.add(ip["ip"])
else:
if isinstance(v, list):
for item in v:
if isinstance(item, list):
for subitem in item:
- if subitem.endswith('.' + self.word):
+ if subitem.endswith("." + self.word):
self.totalhosts.add(subitem)
else:
- if item.endswith('.' + self.word):
+ if item.endswith("." + self.word):
self.totalhosts.add(item)
- for domain_list in data['domain_list']:
- self.asns.add(str(domain_list['asn']))
- domains = [sub['domain'] for sub in domain_list['domain_list']]
+ for domain_list in data["domain_list"]:
+ self.asns.add(str(domain_list["asn"]))
+ domains = [sub["domain"] for sub in domain_list["domain_list"]]
for sub in domains:
- if sub.endswith('.' + self.word):
+ if sub.endswith("." + self.word):
self.totalhosts.add(sub)
- self.totalips.add(domain_list['ip'])
+ self.totalips.add(domain_list["ip"])
- for html_page_links in data['html_page_link_domains']:
- domain = html_page_links['domain']
- if domain.endswith('.' + self.word):
+ for html_page_links in data["html_page_link_domains"]:
+ domain = html_page_links["domain"]
+ if domain.endswith("." + self.word):
self.totalhosts.add(domain)
- for ip in html_page_links['mapped_ips']:
- self.totalips.add(ip['ip'])
+ for ip in html_page_links["mapped_ips"]:
+ self.totalips.add(ip["ip"])
# TODO combine data['links'] and data['network_logs'] urls into one list for one run through
- for link in data['links']:
- url = link['url']
+ for link in data["links"]:
+ url = link["url"]
parsed_url = urlparse(url)
netloc = parsed_url.netloc
if self.word in netloc:
- if (':' in netloc and netloc.split(':')[0].endswith(self.word)) or netloc.endswith(self.word):
+ if (
+ ":" in netloc and netloc.split(":")[0].endswith(self.word)
+ ) or netloc.endswith(self.word):
self.totalhosts.add(netloc)
- for log in data['network_logs']:
- url = log['url']
+ for log in data["network_logs"]:
+ url = log["url"]
parsed_url = urlparse(url)
netloc = parsed_url.netloc
if self.word in netloc:
- if (':' in netloc and netloc.split(':')[0].endswith(self.word)) or netloc.endswith(self.word):
+ if (
+ ":" in netloc and netloc.split(":")[0].endswith(self.word)
+ ) or netloc.endswith(self.word):
self.totalhosts.add(netloc)
- self.asns.add(str(log['as_number']))
+ self.asns.add(str(log["as_number"]))
- for redirects in data['page_redirections']:
+ for redirects in data["page_redirections"]:
for redirect in redirects:
- url = redirect['url']
+ url = redirect["url"]
parsed_url = urlparse(url)
netloc = parsed_url.netloc
if self.word in netloc:
- if (':' in netloc and netloc.split(':')[0].endswith(self.word)) or netloc.endswith(self.word):
+ if (
+ ":" in netloc and netloc.split(":")[0].endswith(self.word)
+ ) or netloc.endswith(self.word):
self.totalhosts.add(netloc)
- self.totalhosts = {host.replace('www.', '') for host in self.totalhosts if '*.' + self.word != host}
+ self.totalhosts = {
+ host.replace("www.", "")
+ for host in self.totalhosts
+ if "*." + self.word != host
+ }
# print(f'hostnames: {self.totalhosts}')
# print(f'asns: {self.asns}')
diff --git a/theHarvester/discovery/crtsh.py b/theHarvester/discovery/crtsh.py
index c5b83a50..8322f5b7 100644
--- a/theHarvester/discovery/crtsh.py
+++ b/theHarvester/discovery/crtsh.py
@@ -1,9 +1,9 @@
-from theHarvester.lib.core import *
from typing import List, Set
+from theHarvester.lib.core import *
+
class SearchCrtsh:
-
def __init__(self, word) -> None:
self.word = word
self.data: List = []
@@ -12,13 +12,22 @@ class SearchCrtsh:
async def do_search(self) -> List:
data: Set = set()
try:
- url = f'https://crt.sh/?q=%25.{self.word}&output=json'
+ url = f"https://crt.sh/?q=%25.{self.word}&output=json"
response = await AsyncFetcher.fetch_all([url], json=True, proxy=self.proxy)
response = response[0]
data = set(
- [dct['name_value'][2:] if '*.' == dct['name_value'][:2] else dct['name_value']
- for dct in response])
- data = {domain for domain in data if (domain[0] != '*' and str(domain[0:4]).isnumeric() is False)}
+ [
+ dct["name_value"][2:]
+ if "*." == dct["name_value"][:2]
+ else dct["name_value"]
+ for dct in response
+ ]
+ )
+ data = {
+ domain
+ for domain in data
+ if (domain[0] != "*" and str(domain[0:4]).isnumeric() is False)
+ }
except Exception as e:
print(e)
clean: List = []
diff --git a/theHarvester/discovery/dnssearch.py b/theHarvester/discovery/dnssearch.py
index aca06c58..6db6f8fb 100644
--- a/theHarvester/discovery/dnssearch.py
+++ b/theHarvester/discovery/dnssearch.py
@@ -8,15 +8,15 @@ DNS Browsing
Explore the space around known hosts & ips for extra catches.
"""
+import asyncio
import re
import sys
-
-import asyncio
-from aiodns import DNSResolver
from ipaddress import IPv4Network
from typing import Callable, List, Optional
-from theHarvester.lib import hostchecker
+from aiodns import DNSResolver
+
+from theHarvester.lib import hostchecker
#####################################################################
# DNS FORCE
@@ -24,7 +24,6 @@ from theHarvester.lib import hostchecker
class DnsForce:
-
def __init__(self, domain, dnsserver, verbose: bool = False) -> None:
self.domain = domain
self.subdo = False
@@ -33,20 +32,22 @@ class DnsForce:
# self.dnsserver = list(map(str, dnsserver.split(','))) if isinstance(dnsserver, str) else dnsserver
self.dnsserver = dnsserver
try:
- with open('/etc/theHarvester/wordlists/dns-names.txt', 'r') as file:
+ with open("/etc/theHarvester/wordlists/dns-names.txt", "r") as file:
self.list = file.readlines()
except FileNotFoundError:
try:
- with open('/usr/local/etc/theHarvester/wordlists/dns-names.txt', 'r') as file:
+ with open(
+ "/usr/local/etc/theHarvester/wordlists/dns-names.txt", "r"
+ ) as file:
self.list = file.readlines()
except FileNotFoundError:
- with open('wordlists/dns-names.txt', 'r') as file:
+ with open("wordlists/dns-names.txt", "r") as file:
self.list = file.readlines()
- self.domain = domain.replace('www.', '')
- self.list = [f'{word.strip()}.{self.domain}' for word in self.list]
+ self.domain = domain.replace("www.", "")
+ self.list = [f"{word.strip()}.{self.domain}" for word in self.list]
async def run(self):
- print(f'Starting DNS brute forcing with {len(self.list)} words')
+ print(f"Starting DNS brute forcing with {len(self.list)} words")
checker = hostchecker.Checker(self.list, nameserver=self.dnsserver)
resolved_pair, hosts, ips = await checker.check()
return resolved_pair, hosts, ips
@@ -57,16 +58,15 @@ class DnsForce:
#####################################################################
-IP_REGEX = r'\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}'
-PORT_REGEX = r'\d{1,5}'
-NETMASK_REGEX: str = r'\d{1,2}|' + IP_REGEX
-NETWORK_REGEX: str = r'\b({})(?:\:({}))?(?:\/({}))?\b'.format(
- IP_REGEX,
- PORT_REGEX,
- NETMASK_REGEX)
+IP_REGEX = r"\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}"
+PORT_REGEX = r"\d{1,5}"
+NETMASK_REGEX: str = r"\d{1,2}|" + IP_REGEX
+NETWORK_REGEX: str = r"\b({})(?:\:({}))?(?:\/({}))?\b".format(
+ IP_REGEX, PORT_REGEX, NETMASK_REGEX
+)
-def serialize_ip_range(ip: str, netmask: str = '24') -> str:
+def serialize_ip_range(ip: str, netmask: str = "24") -> str:
"""
Serialize a network range in a constant format, 'x.x.x.x/y'.
@@ -89,12 +89,12 @@ def serialize_ip_range(ip: str, netmask: str = '24') -> str:
__ip = __ip_matches.group(1)
__netmask = netmask if netmask else __ip_matches.group(3)
if __ip and __netmask:
- return str(IPv4Network('{}/{}'.format(__ip, __netmask), strict=False))
+ return str(IPv4Network("{}/{}".format(__ip, __netmask), strict=False))
elif __ip:
- return str(IPv4Network('{}/{}'.format(__ip, '24'), strict=False))
+ return str(IPv4Network("{}/{}".format(__ip, "24"), strict=False))
# invalid input ip
- return ''
+ return ""
def list_ips_in_network_range(iprange: str) -> List[str]:
@@ -133,12 +133,14 @@ async def reverse_single_ip(ip: str, resolver: DNSResolver) -> str:
"""
try:
__host = await resolver.gethostbyaddr(ip)
- return __host.name if __host else ''
+ return __host.name if __host else ""
except Exception:
- return ''
+ return ""
-async def reverse_all_ips_in_range(iprange: str, callback: Callable, nameservers: Optional[List[str]] = None) -> None:
+async def reverse_all_ips_in_range(
+ iprange: str, callback: Callable, nameservers: Optional[List[str]] = None
+) -> None:
"""
Reverse all the IPs stored in a network range.
All the queries are made concurrently.
@@ -185,8 +187,8 @@ def log_query(ip: str) -> None:
-------
out: None.
"""
- sys.stdout.write(chr(27) + '[2K' + chr(27) + '[G')
- sys.stdout.write('\r' + ip + ' - ')
+ sys.stdout.write(chr(27) + "[2K" + chr(27) + "[G")
+ sys.stdout.write("\r" + ip + " - ")
sys.stdout.flush()
diff --git a/theHarvester/discovery/duckduckgosearch.py b/theHarvester/discovery/duckduckgosearch.py
index 3539fca3..49ab9e1c 100644
--- a/theHarvester/discovery/duckduckgosearch.py
+++ b/theHarvester/discovery/duckduckgosearch.py
@@ -1,34 +1,36 @@
+import ujson
+
from theHarvester.discovery.constants import *
from theHarvester.lib.core import *
from theHarvester.parsers import myparser
-import ujson
class SearchDuckDuckGo:
-
def __init__(self, word, limit) -> None:
self.word = word
self.results = ""
self.totalresults = ""
self.dorks: List = []
self.links: List = []
- self.database = 'https://duckduckgo.com/?q='
- self.api = 'https://api.duckduckgo.com/?q=x&format=json&pretty=1' # Currently using API.
- self.quantity = '100'
+ self.database = "https://duckduckgo.com/?q="
+ self.api = "https://api.duckduckgo.com/?q=x&format=json&pretty=1" # Currently using API.
+ self.quantity = "100"
self.limit = limit
self.proxy = False
async def do_search(self) -> None:
# Do normal scraping.
- url = self.api.replace('x', self.word)
- headers = {'User-Agent': Core.get_user_agent()}
- first_resp = await AsyncFetcher.fetch_all([url], headers=headers, proxy=self.proxy)
+ url = self.api.replace("x", self.word)
+ headers = {"User-Agent": Core.get_user_agent()}
+ first_resp = await AsyncFetcher.fetch_all(
+ [url], headers=headers, proxy=self.proxy
+ )
self.results = first_resp[0]
self.totalresults += self.results
urls = await self.crawl(self.results)
urls = {url for url in urls if len(url) > 5}
all_resps = await AsyncFetcher.fetch_all(urls)
- self.totalresults += ''.join(all_resps)
+ self.totalresults += "".join(all_resps)
async def crawl(self, text):
"""
@@ -53,27 +55,39 @@ class SearchDuckDuckGo:
if isinstance(val, dict): # Validation check.
for key in val.keys():
value = val.get(key)
- if isinstance(value, str) and value != '' and 'https://' in value or 'http://' in value:
+ if (
+ isinstance(value, str)
+ and value != ""
+ and "https://" in value
+ or "http://" in value
+ ):
urls.add(value)
- if isinstance(val, str) and val != '' and 'https://' in val or 'http://' in val:
+ if (
+ isinstance(val, str)
+ and val != ""
+ and "https://" in val
+ or "http://" in val
+ ):
urls.add(val)
tmp = set()
for url in urls:
- if '<' in url and 'href=' in url: # Format is
- equal_index = url.index('=')
- true_url = ''
- for ch in url[equal_index + 1:]:
+ if (
+ "<" in url and "href=" in url
+ ): # Format is
+ equal_index = url.index("=")
+ true_url = ""
+ for ch in url[equal_index + 1 :]:
if ch == '"':
tmp.add(true_url)
break
true_url += ch
else:
- if url != '':
+ if url != "":
tmp.add(url)
return tmp
except Exception as e:
- print(f'Exception occurred: {e}')
+ print(f"Exception occurred: {e}")
return []
async def get_emails(self):
diff --git a/theHarvester/discovery/fullhuntsearch.py b/theHarvester/discovery/fullhuntsearch.py
index 911a5ac0..610f59c0 100644
--- a/theHarvester/discovery/fullhuntsearch.py
+++ b/theHarvester/discovery/fullhuntsearch.py
@@ -3,21 +3,23 @@ from theHarvester.lib.core import *
class SearchFullHunt:
-
def __init__(self, word) -> None:
self.word = word
self.key = Core.fullhunt_key()
if self.key is None:
- raise MissingKey('fullhunt')
+ raise MissingKey("fullhunt")
self.total_results = None
self.proxy = False
async def do_search(self) -> None:
- url = f'https://fullhunt.io/api/v1/domain/{self.word}/subdomains'
- response = await AsyncFetcher.fetch_all([url], json=True, headers={'User-Agent': Core.get_user_agent(),
- 'X-API-KEY': self.key},
- proxy=self.proxy)
- self.total_results = response[0]['hosts']
+ url = f"https://fullhunt.io/api/v1/domain/{self.word}/subdomains"
+ response = await AsyncFetcher.fetch_all(
+ [url],
+ json=True,
+ headers={"User-Agent": Core.get_user_agent(), "X-API-KEY": self.key},
+ proxy=self.proxy,
+ )
+ self.total_results = response[0]["hosts"]
async def get_hostnames(self):
return self.total_results
diff --git a/theHarvester/discovery/hackertarget.py b/theHarvester/discovery/hackertarget.py
index e9971858..96ae45b1 100644
--- a/theHarvester/discovery/hackertarget.py
+++ b/theHarvester/discovery/hackertarget.py
@@ -9,14 +9,19 @@ class SearchHackerTarget:
def __init__(self, word) -> None:
self.word = word
self.total_results = ""
- self.hostname = 'https://api.hackertarget.com'
+ self.hostname = "https://api.hackertarget.com"
self.proxy = False
self.results = None
async def do_search(self) -> None:
- headers = {'User-agent': Core.get_user_agent()}
- urls = [f'{self.hostname}/hostsearch/?q={self.word}', f'{self.hostname}/reversedns/?q={self.word}']
- responses = await AsyncFetcher.fetch_all(urls, headers=headers, proxy=self.proxy)
+ headers = {"User-agent": Core.get_user_agent()}
+ urls = [
+ f"{self.hostname}/hostsearch/?q={self.word}",
+ f"{self.hostname}/reversedns/?q={self.word}",
+ ]
+ responses = await AsyncFetcher.fetch_all(
+ urls, headers=headers, proxy=self.proxy
+ )
for response in responses:
self.total_results += response.replace(",", ":")
@@ -25,4 +30,8 @@ class SearchHackerTarget:
await self.do_search()
async def get_hostnames(self) -> list:
- return [result for result in self.total_results.splitlines() if 'No PTR records found' not in result]
+ return [
+ result
+ for result in self.total_results.splitlines()
+ if "No PTR records found" not in result
+ ]
diff --git a/theHarvester/discovery/huntersearch.py b/theHarvester/discovery/huntersearch.py
index bae95bde..46027bc9 100644
--- a/theHarvester/discovery/huntersearch.py
+++ b/theHarvester/discovery/huntersearch.py
@@ -1,10 +1,10 @@
+from typing import List
+
from theHarvester.discovery.constants import *
from theHarvester.lib.core import *
-from typing import List
class SearchHunter:
-
def __init__(self, word, limit, start) -> None:
self.word = word
self.limit = limit
@@ -12,10 +12,10 @@ class SearchHunter:
self.start = start
self.key = Core.hunter_key()
if self.key is None:
- raise MissingKey('Hunter')
+ raise MissingKey("Hunter")
self.total_results = ""
self.counter = start
- self.database = f'https://api.hunter.io/v2/domain-search?domain={self.word}&api_key={self.key}&limit=10'
+ self.database = f"https://api.hunter.io/v2/domain-search?domain={self.word}&api_key={self.key}&limit=10"
self.proxy = False
self.hostnames: List = []
self.emails: List = []
@@ -23,48 +23,79 @@ class SearchHunter:
async def do_search(self) -> None:
# First determine if a user account is not a free account, this call is free
is_free = True
- headers = {'User-Agent': Core.get_user_agent()}
- acc_info_url = f'https://api.hunter.io/v2/account?api_key={self.key}'
- response = await AsyncFetcher.fetch_all([acc_info_url], headers=headers, json=True)
- is_free = is_free if 'plan_name' in response[0]['data'].keys() and response[0]['data']['plan_name'].lower() \
- == 'free' else False
+ headers = {"User-Agent": Core.get_user_agent()}
+ acc_info_url = f"https://api.hunter.io/v2/account?api_key={self.key}"
+ response = await AsyncFetcher.fetch_all(
+ [acc_info_url], headers=headers, json=True
+ )
+ is_free = (
+ is_free
+ if "plan_name" in response[0]["data"].keys()
+ and response[0]["data"]["plan_name"].lower() == "free"
+ else False
+ )
# Extract the total number of requests that are available for an account
- total_requests_avail = response[0]['data']['requests']['searches']['available'] - response[0]['data']['requests']['searches']['used']
+ total_requests_avail = (
+ response[0]["data"]["requests"]["searches"]["available"]
+ - response[0]["data"]["requests"]["searches"]["used"]
+ )
if is_free:
- response = await AsyncFetcher.fetch_all([self.database], headers=headers, proxy=self.proxy, json=True)
+ response = await AsyncFetcher.fetch_all(
+ [self.database], headers=headers, proxy=self.proxy, json=True
+ )
self.emails, self.hostnames = await self.parse_resp(json_resp=response[0])
else:
# Determine the total number of emails that are available
# As the most emails you can get within one query are 100
# This is only done where paid accounts are in play
- hunter_dinfo_url = f'https://api.hunter.io/v2/email-count?domain={self.word}'
- response = await AsyncFetcher.fetch_all([hunter_dinfo_url], headers=headers, proxy=self.proxy, json=True)
- total_number_reqs = response[0]['data']['total'] // 100
+ hunter_dinfo_url = (
+ f"https://api.hunter.io/v2/email-count?domain={self.word}"
+ )
+ response = await AsyncFetcher.fetch_all(
+ [hunter_dinfo_url], headers=headers, proxy=self.proxy, json=True
+ )
+ total_number_reqs = response[0]["data"]["total"] // 100
# Parse out meta field within initial JSON response to determine the total number of results
if total_requests_avail < total_number_reqs:
- print('WARNING: account does not have enough requests to gather all emails')
- print(f'Total requests available: {total_requests_avail}, total requests '
- f'needed to be made: {total_number_reqs}')
- print('RETURNING current results, if you would still like to '
- 'run this module comment out the if request')
+ print(
+ "WARNING: account does not have enough requests to gather all emails"
+ )
+ print(
+ f"Total requests available: {total_requests_avail}, total requests "
+ f"needed to be made: {total_number_reqs}"
+ )
+ print(
+ "RETURNING current results, if you would still like to "
+ "run this module comment out the if request"
+ )
return
self.limit = 100
# max number of emails you can get per request is 100
# increments of 100 with offset determining where to start
# See docs for more details: https://hunter.io/api-documentation/v2#domain-search
for offset in range(0, 100 * total_number_reqs, 100):
- req_url = f'https://api.hunter.io/v2/domain-search?domain={self.word}&api_key={self.key}&limit{self.limit}&offset={offset}'
- response = await AsyncFetcher.fetch_all([req_url], headers=headers, proxy=self.proxy, json=True)
+ req_url = f"https://api.hunter.io/v2/domain-search?domain={self.word}&api_key={self.key}&limit{self.limit}&offset={offset}"
+ response = await AsyncFetcher.fetch_all(
+ [req_url], headers=headers, proxy=self.proxy, json=True
+ )
temp_emails, temp_hostnames = await self.parse_resp(response[0])
self.emails.extend(temp_emails)
self.hostnames.extend(temp_hostnames)
await asyncio.sleep(1)
async def parse_resp(self, json_resp):
- emails = list(sorted({email['value'] for email in json_resp['data']['emails']}))
- domains = list(sorted({source['domain'] for email in json_resp['data']['emails'] for source in email['sources']
- if self.word in source['domain']}))
+ emails = list(sorted({email["value"] for email in json_resp["data"]["emails"]}))
+ domains = list(
+ sorted(
+ {
+ source["domain"]
+ for email in json_resp["data"]["emails"]
+ for source in email["sources"]
+ if self.word in source["domain"]
+ }
+ )
+ )
return emails, domains
async def process(self, proxy: bool = False) -> None:
diff --git a/theHarvester/discovery/netlas.py b/theHarvester/discovery/netlas.py
index 697a0269..dd6c62fe 100644
--- a/theHarvester/discovery/netlas.py
+++ b/theHarvester/discovery/netlas.py
@@ -1,25 +1,27 @@
+from typing import Set
+
from theHarvester.discovery.constants import MissingKey
from theHarvester.lib.core import *
-from typing import Set
class SearchNetlas:
-
def __init__(self, word) -> None:
self.word = word
self.totalhosts: List = []
self.totalips: List = []
self.key = Core.netlas_key()
if self.key is None:
- raise MissingKey('netlas')
+ raise MissingKey("netlas")
self.proxy = False
async def do_search(self) -> None:
- api = f'https://app.netlas.io/api/domains/?q=*.{self.word}&source_type=include&start=0&fields=*'
- headers = {'X-API-Key': self.key}
- response = await AsyncFetcher.fetch_all([api], json=True, headers=headers, proxy=self.proxy)
- for domain in response[0]['items']:
- self.totalhosts.append(domain['data']['domain'])
+ api = f"https://app.netlas.io/api/domains/?q=*.{self.word}&source_type=include&start=0&fields=*"
+ headers = {"X-API-Key": self.key}
+ response = await AsyncFetcher.fetch_all(
+ [api], json=True, headers=headers, proxy=self.proxy
+ )
+ for domain in response[0]["items"]:
+ self.totalhosts.append(domain["data"]["domain"])
async def get_hostnames(self) -> List:
return self.totalhosts
diff --git a/theHarvester/discovery/onyphe.py b/theHarvester/discovery/onyphe.py
index 6f93a5e7..8960becd 100644
--- a/theHarvester/discovery/onyphe.py
+++ b/theHarvester/discovery/onyphe.py
@@ -1,33 +1,37 @@
-from theHarvester.lib.core import *
-from theHarvester.discovery.constants import MissingKey
from typing import Set
from urllib.parse import urlparse
+
+from theHarvester.discovery.constants import MissingKey
+from theHarvester.lib.core import *
+
# from theHarvester.parsers import myparser
class SearchOnyphe:
def __init__(self, word) -> None:
self.word = word
- self.response = ''
+ self.response = ""
self.totalhosts: Set = set()
self.totalips: Set = set()
self.asns: Set = set()
self.key = Core.onyphe_key()
if self.key is None:
- raise MissingKey('onyphe')
+ raise MissingKey("onyphe")
self.proxy = False
async def do_search(self) -> None:
# https://www.onyphe.io/docs/apis/search
# https://www.onyphe.io/search?q=domain%3Acharter.com&captcharesponse=j5cGT
# base_url = f'https://www.onyphe.io/api/v2/search/?q=domain:domain:{self.word}'
- base_url = f'https://www.onyphe.io/api/v2/search/?q=domain:{self.word}'
+ base_url = f"https://www.onyphe.io/api/v2/search/?q=domain:{self.word}"
headers = {
- 'User-Agent': Core.get_user_agent(),
+ "User-Agent": Core.get_user_agent(),
"Content-Type": "application/json",
- "Authorization": f'bearer {self.key}',
+ "Authorization": f"bearer {self.key}",
}
- response = await AsyncFetcher.fetch_all([base_url], json=True, headers=headers, proxy=self.proxy)
+ response = await AsyncFetcher.fetch_all(
+ [base_url], json=True, headers=headers, proxy=self.proxy
+ )
self.response = response[0]
await self.parse_onyphe_resp_json()
@@ -35,41 +39,74 @@ class SearchOnyphe:
if isinstance(self.response, list):
self.response = self.response[0]
if not isinstance(self.response, dict):
- raise Exception(f'An exception has occurred {self.response} is not a dict')
- if 'Success' == self.response['text']:
- if 'results' in self.response.keys():
- for result in self.response['results']:
+ raise Exception(f"An exception has occurred {self.response} is not a dict")
+ if "Success" == self.response["text"]:
+ if "results" in self.response.keys():
+ for result in self.response["results"]:
try:
- if 'alternativeip' in result.keys():
- self.totalips.update({altip for altip in result['alternativeip']})
- if 'url' in result.keys() and isinstance(result['url'], list):
- self.totalhosts.update(urlparse(url).netloc for url in result['url']
- if urlparse(url).netloc.endswith(self.word))
- self.asns.add(result['asn'])
- self.asns.add(result['geolocus']['asn'])
- self.totalips.add(result['geolocus']['subnet'])
- self.totalips.add(result['ip'])
- self.totalips.add(result['subnet'])
+ if "alternativeip" in result.keys():
+ self.totalips.update(
+ {altip for altip in result["alternativeip"]}
+ )
+ if "url" in result.keys() and isinstance(result["url"], list):
+ self.totalhosts.update(
+ urlparse(url).netloc
+ for url in result["url"]
+ if urlparse(url).netloc.endswith(self.word)
+ )
+ self.asns.add(result["asn"])
+ self.asns.add(result["geolocus"]["asn"])
+ self.totalips.add(result["geolocus"]["subnet"])
+ self.totalips.add(result["ip"])
+ self.totalips.add(result["subnet"])
# Shouldn't be needed as API autoparses urls from html raw data
# rawres = myparser.Parser(result['data'], self.word)
# if await rawres.hostnames():
# self.totalhosts.update(set(await rawres.hostnames()))
- for subdomain_key in ["domain", "hostname", "subdomains", "subject", "reverse", "geolocus"]:
+ for subdomain_key in [
+ "domain",
+ "hostname",
+ "subdomains",
+ "subject",
+ "reverse",
+ "geolocus",
+ ]:
if subdomain_key in result.keys():
- if subdomain_key == 'subject':
- self.totalhosts.update({domain for domain in result[subdomain_key]['altname']
- if domain.endswith(self.word)})
- elif subdomain_key == 'geolocus':
- self.totalhosts.update({domain for domain in result[subdomain_key]['domain']
- if domain.endswith(self.word)})
+ if subdomain_key == "subject":
+ self.totalhosts.update(
+ {
+ domain
+ for domain in result[subdomain_key][
+ "altname"
+ ]
+ if domain.endswith(self.word)
+ }
+ )
+ elif subdomain_key == "geolocus":
+ self.totalhosts.update(
+ {
+ domain
+ for domain in result[subdomain_key][
+ "domain"
+ ]
+ if domain.endswith(self.word)
+ }
+ )
else:
- self.totalhosts.update({domain for domain in result[subdomain_key]
- if domain.endswith(self.word)})
+ self.totalhosts.update(
+ {
+ domain
+ for domain in result[subdomain_key]
+ if domain.endswith(self.word)
+ }
+ )
except Exception as e:
- print(f'An exception has occurred on result: {result}: {e}')
+ print(f"An exception has occurred on result: {result}: {e}")
continue
else:
- print(f'Onhyphe API query did not succeed dumping current response: {self.response}')
+ print(
+ f"Onhyphe API query did not succeed dumping current response: {self.response}"
+ )
async def get_asns(self) -> set:
return self.asns
diff --git a/theHarvester/discovery/otxsearch.py b/theHarvester/discovery/otxsearch.py
index db44ae24..dbe0610d 100644
--- a/theHarvester/discovery/otxsearch.py
+++ b/theHarvester/discovery/otxsearch.py
@@ -1,10 +1,10 @@
-from typing import Set
-from theHarvester.lib.core import *
import re
+from typing import Set
+
+from theHarvester.lib.core import *
class SearchOtx:
-
def __init__(self, word) -> None:
self.word = word
self.totalhosts: Set = set()
@@ -12,14 +12,17 @@ class SearchOtx:
self.proxy = False
async def do_search(self) -> None:
- url = f'https://otx.alienvault.com/api/v1/indicators/domain/{self.word}/passive_dns'
+ url = f"https://otx.alienvault.com/api/v1/indicators/domain/{self.word}/passive_dns"
response = await AsyncFetcher.fetch_all([url], json=True, proxy=self.proxy)
responses = response[0]
dct = responses
- self.totalhosts = {host['hostname'] for host in dct['passive_dns']}
+ self.totalhosts = {host["hostname"] for host in dct["passive_dns"]}
# filter out ips that are just called NXDOMAIN
- self.totalips = {ip['address'] for ip in dct['passive_dns'] if re.match(r"^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}$",
- ip['address'])}
+ self.totalips = {
+ ip["address"]
+ for ip in dct["passive_dns"]
+ if re.match(r"^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}$", ip["address"])
+ }
async def get_hostnames(self) -> set:
return self.totalhosts
diff --git a/theHarvester/discovery/pentesttools.py b/theHarvester/discovery/pentesttools.py
index 700043a6..35f780c0 100644
--- a/theHarvester/discovery/pentesttools.py
+++ b/theHarvester/discovery/pentesttools.py
@@ -1,57 +1,66 @@
+import time
+from typing import List
+
+import ujson
+
from theHarvester.discovery.constants import *
from theHarvester.lib.core import *
-from typing import List
-import ujson
-import time
class SearchPentestTools:
-
def __init__(self, word) -> None:
# Script is largely based off https://pentest-tools.com/public/api_client.py.txt
self.word = word
self.key = Core.pentest_tools_key()
if self.key is None:
- raise MissingKey('PentestTools')
+ raise MissingKey("PentestTools")
self.total_results: List = []
- self.api = f'https://pentest-tools.com/api?key={self.key}'
+ self.api = f"https://pentest-tools.com/api?key={self.key}"
self.proxy = False
async def poll(self, scan_id):
while True:
time.sleep(3)
# Get the status of our scan
- scan_status_data = {
- 'op': 'get_scan_status',
- 'scan_id': scan_id
- }
- responses = await AsyncFetcher.post_fetch(url=self.api, data=ujson.dumps(scan_status_data), proxy=self.proxy)
+ scan_status_data = {"op": "get_scan_status", "scan_id": scan_id}
+ responses = await AsyncFetcher.post_fetch(
+ url=self.api, data=ujson.dumps(scan_status_data), proxy=self.proxy
+ )
res_json = ujson.loads(responses.strip())
- if res_json['op_status'] == 'success':
- if res_json['scan_status'] != 'waiting' and res_json['scan_status'] != 'running':
+ if res_json["op_status"] == "success":
+ if (
+ res_json["scan_status"] != "waiting"
+ and res_json["scan_status"] != "running"
+ ):
getoutput_data = {
- 'op': 'get_output',
- 'scan_id': scan_id,
- 'output_format': 'json'
+ "op": "get_output",
+ "scan_id": scan_id,
+ "output_format": "json",
}
- responses = await AsyncFetcher.post_fetch(url=self.api,
- data=ujson.dumps(getoutput_data),
- proxy=self.proxy)
+ responses = await AsyncFetcher.post_fetch(
+ url=self.api, data=ujson.dumps(getoutput_data), proxy=self.proxy
+ )
- res_json = ujson.loads(responses.strip('\n'))
+ res_json = ujson.loads(responses.strip("\n"))
self.total_results = await self.parse_json(res_json)
break
else:
- print(f"Operation get_scan_status failed because: {res_json['error']}. {res_json['details']}")
+ print(
+ f"Operation get_scan_status failed because: {res_json['error']}. {res_json['details']}"
+ )
break
@staticmethod
async def parse_json(json_results):
- status = json_results['op_status']
- if status == 'success':
- scan_tests = json_results['scan_output']['output_json']
- output_data = scan_tests[0]['output_data']
- host_to_ip = [f'{subdomain[0]}:{subdomain[1]}' for subdomain in output_data if len(subdomain) > 0]
+ status = json_results["op_status"]
+ if status == "success":
+ scan_tests = json_results["scan_output"]["output_json"]
+ output_data = scan_tests[0]["output_data"]
+ host_to_ip = [
+ f"{subdomain[0]}:{subdomain[1]}"
+ for subdomain in output_data
+ if len(subdomain) > 0
+ ]
return host_to_ip
return []
@@ -60,18 +69,20 @@ class SearchPentestTools:
async def do_search(self) -> None:
subdomain_payload = {
- 'op': 'start_scan',
- 'tool_id': 20,
- 'tool_params': {
- 'target': f'{self.word}',
- 'web_details': 'off',
- 'do_smart_search': 'off'
- }
+ "op": "start_scan",
+ "tool_id": 20,
+ "tool_params": {
+ "target": f"{self.word}",
+ "web_details": "off",
+ "do_smart_search": "off",
+ },
}
- responses = await AsyncFetcher.post_fetch(url=self.api, data=ujson.dumps(subdomain_payload), proxy=self.proxy)
+ responses = await AsyncFetcher.post_fetch(
+ url=self.api, data=ujson.dumps(subdomain_payload), proxy=self.proxy
+ )
res_json = ujson.loads(responses.strip())
- if res_json['op_status'] == 'success':
- scan_id = res_json['scan_id']
+ if res_json["op_status"] == "success":
+ scan_id = res_json["scan_id"]
await self.poll(scan_id)
async def process(self, proxy: bool = False) -> None:
diff --git a/theHarvester/discovery/projectdiscovery.py b/theHarvester/discovery/projectdiscovery.py
index a2a40542..dd28851c 100644
--- a/theHarvester/discovery/projectdiscovery.py
+++ b/theHarvester/discovery/projectdiscovery.py
@@ -3,21 +3,25 @@ from theHarvester.lib.core import *
class SearchDiscovery:
-
def __init__(self, word) -> None:
self.word = word
self.key = Core.projectdiscovery_key()
if self.key is None:
- raise MissingKey('ProjectDiscovery')
+ raise MissingKey("ProjectDiscovery")
self.total_results = None
self.proxy = False
async def do_search(self):
- url = f'https://dns.projectdiscovery.io/dns/{self.word}/subdomains'
- response = await AsyncFetcher.fetch_all([url], json=True, headers={'User-Agent': Core.get_user_agent(),
- 'Authorization': self.key},
- proxy=self.proxy)
- self.total_results = [f'{domains}.{self.word}' for domains in response[0]['subdomains']]
+ url = f"https://dns.projectdiscovery.io/dns/{self.word}/subdomains"
+ response = await AsyncFetcher.fetch_all(
+ [url],
+ json=True,
+ headers={"User-Agent": Core.get_user_agent(), "Authorization": self.key},
+ proxy=self.proxy,
+ )
+ self.total_results = [
+ f"{domains}.{self.word}" for domains in response[0]["subdomains"]
+ ]
async def get_hostnames(self):
return self.total_results
diff --git a/theHarvester/discovery/rapiddns.py b/theHarvester/discovery/rapiddns.py
index e2bd1a7a..1e2245af 100644
--- a/theHarvester/discovery/rapiddns.py
+++ b/theHarvester/discovery/rapiddns.py
@@ -1,9 +1,9 @@
from bs4 import BeautifulSoup
+
from theHarvester.lib.core import *
class SearchRapidDns:
-
def __init__(self, word) -> None:
self.word = word
self.total_results: List = []
@@ -11,14 +11,16 @@ class SearchRapidDns:
async def do_search(self):
try:
- headers = {'User-agent': Core.get_user_agent()}
+ headers = {"User-agent": Core.get_user_agent()}
# TODO see if it's worth adding sameip searches
# f'{self.hostname}/sameip/{self.word}?full=1#result'
- urls = [f'https://rapiddns.io/subdomain/{self.word}?full=1#result']
- responses = await AsyncFetcher.fetch_all(urls, headers=headers, proxy=self.proxy)
+ urls = [f"https://rapiddns.io/subdomain/{self.word}?full=1#result"]
+ responses = await AsyncFetcher.fetch_all(
+ urls, headers=headers, proxy=self.proxy
+ )
if len(responses[0]) <= 1:
return self.total_results
- soup = BeautifulSoup(responses[0], 'html.parser')
+ soup = BeautifulSoup(responses[0], "html.parser")
rows = soup.find("table").find("tbody").find_all("tr")
if rows:
# Validation check
@@ -27,13 +29,15 @@ class SearchRapidDns:
if len(cells) >= 0:
# sanity check
subdomain = str(cells[0].get_text())
- if cells[-1].get_text() == 'CNAME':
- self.total_results.append(f'{subdomain}')
+ if cells[-1].get_text() == "CNAME":
+ self.total_results.append(f"{subdomain}")
else:
- self.total_results.append(f'{subdomain}:{str(cells[1].get_text()).strip()}')
+ self.total_results.append(
+ f"{subdomain}:{str(cells[1].get_text()).strip()}"
+ )
self.total_results = list({domain for domain in self.total_results})
except Exception as e:
- print(f'An exception has occurred: {str(e)}')
+ print(f"An exception has occurred: {str(e)}")
async def process(self, proxy: bool = False) -> None:
self.proxy = proxy
diff --git a/theHarvester/discovery/rocketreach.py b/theHarvester/discovery/rocketreach.py
index 3928fc55..9f0b2ee0 100644
--- a/theHarvester/discovery/rocketreach.py
+++ b/theHarvester/discovery/rocketreach.py
@@ -1,58 +1,69 @@
+import asyncio
+from typing import Set
+
from theHarvester.discovery.constants import *
from theHarvester.lib.core import *
-from typing import Set
-import asyncio
class SearchRocketReach:
-
def __init__(self, word, limit) -> None:
self.ips: Set = set()
self.word = word
self.key = Core.rocketreach_key()
if self.key is None:
- raise MissingKey('RocketReach')
+ raise MissingKey("RocketReach")
self.hosts: Set = set()
self.proxy = False
- self.baseurl = 'https://rocketreach.co/api/v2/person/search'
+ self.baseurl = "https://rocketreach.co/api/v2/person/search"
self.links: Set = set()
self.limit = limit
async def do_search(self) -> None:
try:
headers = {
- 'Api-Key': self.key,
- 'Content-Type': 'application/json',
- 'User-Agent': Core.get_user_agent()
+ "Api-Key": self.key,
+ "Content-Type": "application/json",
+ "User-Agent": Core.get_user_agent(),
}
next_page = 1 # track pagination
for count in range(1, self.limit):
data = f'{{"query":{{"company_domain": ["{self.word}"]}}, "start": {next_page}, "page_size": 100}}'
- result = await AsyncFetcher.post_fetch(self.baseurl, headers=headers, data=data, json=True)
- if 'detail' in result.keys() and 'error' in result.keys() and 'Subscribe to a plan to access' in result['detail']:
+ result = await AsyncFetcher.post_fetch(
+ self.baseurl, headers=headers, data=data, json=True
+ )
+ if (
+ "detail" in result.keys()
+ and "error" in result.keys()
+ and "Subscribe to a plan to access" in result["detail"]
+ ):
# No more results can be fetched
break
- if 'detail' in result.keys() and 'Request was throttled.' in result['detail']:
+ if (
+ "detail" in result.keys()
+ and "Request was throttled." in result["detail"]
+ ):
# Rate limit has been triggered need to sleep extra
- print(f'RocketReach requests have been throttled; '
- f'{result["detail"].split(" ", 3)[-1].replace("available", "availability")}')
+ print(
+ f"RocketReach requests have been throttled; "
+ f'{result["detail"].split(" ", 3)[-1].replace("available", "availability")}'
+ )
break
- if 'profiles' in dict(result).keys():
- if len(result['profiles']) == 0:
+ if "profiles" in dict(result).keys():
+ if len(result["profiles"]) == 0:
break
- for profile in result['profiles']:
- if 'linkedin_url' in dict(profile).keys():
- self.links.add(profile['linkedin_url'])
- if 'pagination' in dict(result).keys():
- next_page = int(result['pagination']['next'])
- if next_page > int(result['pagination']['total']):
+ for profile in result["profiles"]:
+ if "linkedin_url" in dict(profile).keys():
+ self.links.add(profile["linkedin_url"])
+ if "pagination" in dict(result).keys():
+ next_page = int(result["pagination"]["next"])
+ if next_page > int(result["pagination"]["total"]):
break
await asyncio.sleep(get_delay() + 5)
except Exception as e:
- print(f'An exception has occurred: {e}')
+ print(f"An exception has occurred: {e}")
async def get_links(self):
return self.links
diff --git a/theHarvester/discovery/searchhunterhow.py b/theHarvester/discovery/searchhunterhow.py
index 0bfa7f7f..8618b71e 100644
--- a/theHarvester/discovery/searchhunterhow.py
+++ b/theHarvester/discovery/searchhunterhow.py
@@ -1,10 +1,12 @@
-from theHarvester.lib.core import *
-from theHarvester.discovery.constants import MissingKey
-from typing import Set
import base64
from datetime import datetime
+from typing import Set
+
from dateutil.relativedelta import relativedelta
+from theHarvester.discovery.constants import MissingKey
+from theHarvester.lib.core import *
+
class SearchHunterHow:
def __init__(self, word) -> None:
@@ -12,44 +14,55 @@ class SearchHunterHow:
self.total_hostnames: Set = set()
self.key = Core.hunterhow_key()
if self.key is None:
- raise MissingKey('hunterhow')
+ raise MissingKey("hunterhow")
self.proxy = False
async def do_search(self) -> None:
# https://hunter.how/search-api
query = f'domain.suffix="{self.word}"'
# second_query = f'domain="{self.word}"'
- encoded_query = base64.urlsafe_b64encode(query.encode("utf-8")).decode('ascii')
+ encoded_query = base64.urlsafe_b64encode(query.encode("utf-8")).decode("ascii")
page = 1
page_size = 100 # can be either: 10,20,50,100)
# The interval between the start time and the end time cannot exceed one year
# Can not exceed one year, but years=1 does not work due to their backend, 364 will suffice
today = datetime.today()
one_year_ago = today - relativedelta(days=364)
- start_time = one_year_ago.strftime('%Y-%m-%d')
- end_time = today.strftime('%Y-%m-%d')
+ start_time = one_year_ago.strftime("%Y-%m-%d")
+ end_time = today.strftime("%Y-%m-%d")
# two_years_ago = one_year_ago - relativedelta(days=364)
# start_time = two_years_ago.strftime('%Y-%m-%d')
# end_time = one_year_ago.strftime('%Y-%m-%d')
- url = "https://api.hunter.how/search?api-key=%s&query=%s&page=%d&page_size=%d&start_time=%s&end_time=%s" % (
- # self.key, encoded_query, page, page_size, start_time, end_time
- self.key, encoded_query, page, page_size, start_time, end_time
+ url = (
+ "https://api.hunter.how/search?api-key=%s&query=%s&page=%d&page_size=%d&start_time=%s&end_time=%s"
+ % (
+ # self.key, encoded_query, page, page_size, start_time, end_time
+ self.key,
+ encoded_query,
+ page,
+ page_size,
+ start_time,
+ end_time,
+ )
)
# print(f'Sending url: {url}')
- response = await AsyncFetcher.fetch_all([url], json=True, headers={'User-Agent': Core.get_user_agent(),
- 'x-api-key': f'{self.key}'},
- proxy=self.proxy)
+ response = await AsyncFetcher.fetch_all(
+ [url],
+ json=True,
+ headers={"User-Agent": Core.get_user_agent(), "x-api-key": f"{self.key}"},
+ proxy=self.proxy,
+ )
dct = response[0]
# print(f'json response: ')
# print(dct)
- if 'code' in dct.keys():
- if dct['code'] == 40001:
+ if "code" in dct.keys():
+ if dct["code"] == 40001:
print(f'Code 40001 indicates for searchhunterhow: {dct["message"]}')
return
# total = dct['data']['total']
# TODO determine if total is ever 100 how to get more subdomains?
- for sub in dct['data']['list']:
- self.total_hostnames.add(sub['domain'])
+ for sub in dct["data"]["list"]:
+ self.total_hostnames.add(sub["domain"])
async def get_hostnames(self) -> set:
return self.total_hostnames
diff --git a/theHarvester/discovery/shodansearch.py b/theHarvester/discovery/shodansearch.py
index b30adcc6..dd549e4e 100644
--- a/theHarvester/discovery/shodansearch.py
+++ b/theHarvester/discovery/shodansearch.py
@@ -1,17 +1,17 @@
-from theHarvester.discovery.constants import *
-from theHarvester.lib.core import *
-from shodan import exception
-from shodan import Shodan
from collections import OrderedDict
from typing import List
+from shodan import Shodan, exception
+
+from theHarvester.discovery.constants import *
+from theHarvester.lib.core import *
+
class SearchShodan:
-
def __init__(self) -> None:
self.key = Core.shodan_key()
if self.key is None:
- raise MissingKey('Shodan')
+ raise MissingKey("Shodan")
self.api = Shodan(self.key)
self.hostdatarow: List = []
self.tracker: OrderedDict = OrderedDict()
@@ -20,72 +20,81 @@ class SearchShodan:
try:
ipaddress = ip
results = self.api.host(ipaddress)
- asn = ''
+ asn = ""
domains: List = list()
hostnames: List = list()
- ip_str = ''
- isp = ''
- org = ''
+ ip_str = ""
+ isp = ""
+ org = ""
ports: List = list()
- title = ''
- server = ''
- product = ''
+ title = ""
+ server = ""
+ product = ""
technologies: List = list()
- data_first_dict = dict(results['data'][0])
+ data_first_dict = dict(results["data"][0])
- if 'ip_str' in data_first_dict.keys():
- ip_str += data_first_dict['ip_str']
+ if "ip_str" in data_first_dict.keys():
+ ip_str += data_first_dict["ip_str"]
- if 'http' in data_first_dict.keys():
- http_results_dict = dict(data_first_dict['http'])
- if 'title' in http_results_dict.keys():
- title_val = str(http_results_dict['title']).strip()
- if title_val != 'None':
+ if "http" in data_first_dict.keys():
+ http_results_dict = dict(data_first_dict["http"])
+ if "title" in http_results_dict.keys():
+ title_val = str(http_results_dict["title"]).strip()
+ if title_val != "None":
title += title_val
- if 'components' in http_results_dict.keys():
- for key in http_results_dict['components'].keys():
+ if "components" in http_results_dict.keys():
+ for key in http_results_dict["components"].keys():
technologies.append(key)
- if 'server' in http_results_dict.keys():
- server_val = str(http_results_dict['server']).strip()
- if server_val != 'None':
+ if "server" in http_results_dict.keys():
+ server_val = str(http_results_dict["server"]).strip()
+ if server_val != "None":
server += server_val
for key, value in results.items():
- if key == 'asn':
+ if key == "asn":
asn += value
- if key == 'domains':
+ if key == "domains":
value = list(value)
value.sort()
domains.extend(value)
- if key == 'hostnames':
+ if key == "hostnames":
value = [host.strip() for host in list(value)]
value.sort()
hostnames.extend(value)
- if key == 'isp':
+ if key == "isp":
isp += value
- if key == 'org':
+ if key == "org":
org += str(value)
- if key == 'ports':
+ if key == "ports":
value = list(value)
value.sort()
ports.extend(value)
- if key == 'product':
+ if key == "product":
product += value
technologies = list(set(technologies))
- self.tracker[ip] = {'asn': asn.strip(), 'domains': domains, 'hostnames': hostnames,
- 'ip_str': ip_str.strip(), 'isp': isp.strip(), 'org': org.strip(),
- 'ports': ports, 'product': product.strip(),
- 'server': server.strip(), 'technologies': technologies, 'title': title.strip()}
+ self.tracker[ip] = {
+ "asn": asn.strip(),
+ "domains": domains,
+ "hostnames": hostnames,
+ "ip_str": ip_str.strip(),
+ "isp": isp.strip(),
+ "org": org.strip(),
+ "ports": ports,
+ "product": product.strip(),
+ "server": server.strip(),
+ "technologies": technologies,
+ "title": title.strip(),
+ }
return self.tracker
except exception.APIError:
- print(f'{ip}: Not in Shodan')
- self.tracker[ip] = 'Not in Shodan'
+ print(f"{ip}: Not in Shodan")
+ self.tracker[ip] = "Not in Shodan"
except Exception as e:
# print(f'Error occurred in the Shodan IP search module: {e}')
- self.tracker[ip] = f'Error occurred in the Shodan IP search module: {e}'
+ self.tracker[ip] = f"Error occurred in the Shodan IP search module: {e}"
finally:
return self.tracker
diff --git a/theHarvester/discovery/sitedossier.py b/theHarvester/discovery/sitedossier.py
index c5f82f8f..245fb19a 100644
--- a/theHarvester/discovery/sitedossier.py
+++ b/theHarvester/discovery/sitedossier.py
@@ -1,9 +1,9 @@
-from theHarvester.discovery.constants import *
from bs4 import BeautifulSoup
+from theHarvester.discovery.constants import *
+
class SearchSitedossier:
-
def __init__(self, word):
self.word = word
self.totalhosts = set()
@@ -16,66 +16,91 @@ class SearchSitedossier:
# Hence the need for delays after each request to get the most results
# Feel free to tweak the delays as needed
url = f"http://{self.server}/parentdomain/{self.word}"
- headers = {'User-Agent': Core.get_user_agent()}
- response = await AsyncFetcher.fetch_all([url], headers=headers, proxy=self.proxy)
+ headers = {"User-Agent": Core.get_user_agent()}
+ response = await AsyncFetcher.fetch_all(
+ [url], headers=headers, proxy=self.proxy
+ )
base_response = response[0]
- soup = BeautifulSoup(base_response, 'html.parser')
+ soup = BeautifulSoup(base_response, "html.parser")
# iter_counter = 1
# iterations_needed = total_number // 100
# iterations_needed += 1
flagged_counter = 0
- stop_conditions = ['End of list.', 'No data currently available.']
- bot_string = 'Our web servers have detected unusual or excessive requests ' \
- 'from your computer or network. Please enter the unique "word"' \
- ' below to confirm that you are a human interactively using this site.'
- if (stop_conditions[0] not in base_response and stop_conditions[1] not in base_response) \
- and bot_string not in base_response:
- total_number = soup.find('i')
- total_number = int(total_number.text.strip().split(' ')[-1].replace(',', ''))
- hrefs = soup.find_all('a', href=True)
+ stop_conditions = ["End of list.", "No data currently available."]
+ bot_string = (
+ "Our web servers have detected unusual or excessive requests "
+ 'from your computer or network. Please enter the unique "word"'
+ " below to confirm that you are a human interactively using this site."
+ )
+ if (
+ stop_conditions[0] not in base_response
+ and stop_conditions[1] not in base_response
+ ) and bot_string not in base_response:
+ total_number = soup.find("i")
+ total_number = int(
+ total_number.text.strip().split(" ")[-1].replace(",", "")
+ )
+ hrefs = soup.find_all("a", href=True)
for a in hrefs:
- unparsed = a['href']
- if '/site/' in unparsed:
- subdomain = str(unparsed.split('/')[-1]).lower()
+ unparsed = a["href"]
+ if "/site/" in unparsed:
+ subdomain = str(unparsed.split("/")[-1]).lower()
self.totalhosts.add(subdomain)
await asyncio.sleep(get_delay() + 15 + get_delay())
for i in range(101, total_number, 100):
- headers = {'User-Agent': Core.get_user_agent()}
+ headers = {"User-Agent": Core.get_user_agent()}
iter_url = f"http://{self.server}/parentdomain/{self.word}/{i}"
- print(f'My current iter_url: {iter_url}')
- response = await AsyncFetcher.fetch_all([iter_url], headers=headers, proxy=self.proxy)
+ print(f"My current iter_url: {iter_url}")
+ response = await AsyncFetcher.fetch_all(
+ [iter_url], headers=headers, proxy=self.proxy
+ )
response = response[0]
- if stop_conditions[0] in response or stop_conditions[1] in response or flagged_counter >= 3:
+ if (
+ stop_conditions[0] in response
+ or stop_conditions[1] in response
+ or flagged_counter >= 3
+ ):
break
if bot_string in response:
new_sleep_time = get_delay() * 30
- print(f'Triggered a captcha for sitedossier sleeping for: {new_sleep_time} seconds')
+ print(
+ f"Triggered a captcha for sitedossier sleeping for: {new_sleep_time} seconds"
+ )
flagged_counter += 1
await asyncio.sleep(new_sleep_time)
- response = await AsyncFetcher.fetch_all([iter_url], headers={'User-Agent': Core.get_user_agent()},
- proxy=self.proxy)
+ response = await AsyncFetcher.fetch_all(
+ [iter_url],
+ headers={"User-Agent": Core.get_user_agent()},
+ proxy=self.proxy,
+ )
response = response[0]
if bot_string in response:
new_sleep_time = get_delay() * 30 * get_delay()
- print(f'Still triggering a captcha, sleeping longer for: {new_sleep_time}'
- f' and skipping this batch: {iter_url}')
+ print(
+ f"Still triggering a captcha, sleeping longer for: {new_sleep_time}"
+ f" and skipping this batch: {iter_url}"
+ )
await asyncio.sleep(new_sleep_time)
flagged_counter += 1
if flagged_counter >= 3:
break
- soup = BeautifulSoup(response, 'html.parser')
- hrefs = soup.find_all('a', href=True)
+ soup = BeautifulSoup(response, "html.parser")
+ hrefs = soup.find_all("a", href=True)
for a in hrefs:
- unparsed = a['href']
- if '/site/' in unparsed:
- subdomain = str(unparsed.split('/')[-1]).lower()
+ unparsed = a["href"]
+ if "/site/" in unparsed:
+ subdomain = str(unparsed.split("/")[-1]).lower()
self.totalhosts.add(subdomain)
await asyncio.sleep(get_delay() + 15 + get_delay())
- print(f'In total found: {len(self.totalhosts)}')
+ print(f"In total found: {len(self.totalhosts)}")
print(self.totalhosts)
else:
- print('Sitedossier module has triggered a captcha on first iteration, no results can be found.')
- print('Change IPs, manually solve the captcha, or wait before rerunning Sitedossier module')
+ print(
+ "Sitedossier module has triggered a captcha on first iteration, no results can be found."
+ )
+ print(
+ "Change IPs, manually solve the captcha, or wait before rerunning Sitedossier module"
+ )
async def get_hostnames(self):
return self.totalhosts
diff --git a/theHarvester/discovery/subdomaincenter.py b/theHarvester/discovery/subdomaincenter.py
index b70ede5d..7e64b7ec 100644
--- a/theHarvester/discovery/subdomaincenter.py
+++ b/theHarvester/discovery/subdomaincenter.py
@@ -2,22 +2,26 @@ from theHarvester.discovery.constants import *
class SubdomainCenter:
-
def __init__(self, word):
self.word = word
self.results = set()
- self.server = 'https://api.subdomain.center/?domain='
+ self.server = "https://api.subdomain.center/?domain="
self.proxy = False
async def do_search(self):
- headers = {'User-Agent': Core.get_user_agent()}
+ headers = {"User-Agent": Core.get_user_agent()}
try:
- current_url = f'{self.server}{self.word}'
- resp = await AsyncFetcher.fetch_all([current_url], headers=headers, proxy=self.proxy, json=True)
+ current_url = f"{self.server}{self.word}"
+ resp = await AsyncFetcher.fetch_all(
+ [current_url], headers=headers, proxy=self.proxy, json=True
+ )
self.results = resp[0]
- self.results = {sub[4:] if sub[:4] == 'www.' and sub[4:] else sub for sub in self.results}
+ self.results = {
+ sub[4:] if sub[:4] == "www." and sub[4:] else sub
+ for sub in self.results
+ }
except Exception as e:
- print(f'An exception has occurred in SubdomainCenter on : {e}')
+ print(f"An exception has occurred in SubdomainCenter on : {e}")
async def get_hostnames(self):
return self.results
diff --git a/theHarvester/discovery/subdomainfinderc99.py b/theHarvester/discovery/subdomainfinderc99.py
index d10f6e5d..3b1082f4 100644
--- a/theHarvester/discovery/subdomainfinderc99.py
+++ b/theHarvester/discovery/subdomainfinderc99.py
@@ -1,9 +1,11 @@
-from theHarvester.lib.core import *
-from theHarvester.discovery.constants import get_delay
-from theHarvester.parsers import myparser
from typing import Set
-from bs4 import BeautifulSoup
+
import ujson
+from bs4 import BeautifulSoup
+
+from theHarvester.discovery.constants import get_delay
+from theHarvester.lib.core import *
+from theHarvester.parsers import myparser
class SearchSubdomainfinderc99:
@@ -12,21 +14,24 @@ class SearchSubdomainfinderc99:
self.total_results: Set = set()
self.proxy = False
# TODO add api support
- self.server = 'https://subdomainfinder.c99.nl/'
- self.totalresults = ''
+ self.server = "https://subdomainfinder.c99.nl/"
+ self.totalresults = ""
async def do_search(self) -> None:
# Based on https://gist.github.com/th3gundy/bc83580cbe04031e9164362b33600962
- headers = {'User-Agent': Core.get_user_agent()}
- resp = await AsyncFetcher.fetch_all([self.server], headers=headers, proxy=self.proxy)
+ headers = {"User-Agent": Core.get_user_agent()}
+ resp = await AsyncFetcher.fetch_all(
+ [self.server], headers=headers, proxy=self.proxy
+ )
data = await self.get_csrf_params(resp[0])
- data['scan_subdomains'] = ''
- data['domain'] = self.word
- data['privatequery'] = 'on'
+ data["scan_subdomains"] = ""
+ data["domain"] = self.word
+ data["privatequery"] = "on"
await asyncio.sleep(get_delay())
- second_resp = await AsyncFetcher.post_fetch(self.server, headers=headers, proxy=self.proxy,
- data=ujson.dumps(data))
+ second_resp = await AsyncFetcher.post_fetch(
+ self.server, headers=headers, proxy=self.proxy, data=ujson.dumps(data)
+ )
# print(second_resp)
self.totalresults += second_resp
@@ -50,10 +55,10 @@ class SearchSubdomainfinderc99:
@staticmethod
async def get_csrf_params(data):
csrf_params = {}
- html = BeautifulSoup(data, 'html.parser').find('div', {'class': 'input-group'})
- for c in html.find_all('input'):
+ html = BeautifulSoup(data, "html.parser").find("div", {"class": "input-group"})
+ for c in html.find_all("input"):
try:
- csrf_params[c.get('name')] = c.get('value')
+ csrf_params[c.get("name")] = c.get("value")
except Exception:
continue
diff --git a/theHarvester/discovery/threatminer.py b/theHarvester/discovery/threatminer.py
index a196d4cb..a162a9fd 100644
--- a/theHarvester/discovery/threatminer.py
+++ b/theHarvester/discovery/threatminer.py
@@ -1,9 +1,9 @@
-from typing import Type, List, Set
+from typing import List, Set, Type
+
from theHarvester.lib.core import *
class SearchThreatminer:
-
def __init__(self, word) -> None:
self.word = word
self.totalhosts: Set = set()
@@ -11,13 +11,15 @@ class SearchThreatminer:
self.proxy = False
async def do_search(self) -> None:
- url = f'https://api.threatminer.org/v2/domain.php?q={self.word}&rt=5'
+ url = f"https://api.threatminer.org/v2/domain.php?q={self.word}&rt=5"
response = await AsyncFetcher.fetch_all([url], json=True, proxy=self.proxy)
- self.totalhosts = {host for host in response[0]['results']}
- second_url = f'https://api.threatminer.org/v2/domain.php?q={self.word}&rt=2'
- secondresp = await AsyncFetcher.fetch_all([second_url], json=True, proxy=self.proxy)
+ self.totalhosts = {host for host in response[0]["results"]}
+ second_url = f"https://api.threatminer.org/v2/domain.php?q={self.word}&rt=2"
+ secondresp = await AsyncFetcher.fetch_all(
+ [second_url], json=True, proxy=self.proxy
+ )
try:
- self.totalips = {resp['ip'] for resp in secondresp[0]['results']}
+ self.totalips = {resp["ip"] for resp in secondresp[0]["results"]}
except TypeError:
pass
diff --git a/theHarvester/discovery/tombasearch.py b/theHarvester/discovery/tombasearch.py
index 3f4993d5..9f464c20 100644
--- a/theHarvester/discovery/tombasearch.py
+++ b/theHarvester/discovery/tombasearch.py
@@ -1,10 +1,10 @@
+from typing import List
+
from theHarvester.discovery.constants import *
from theHarvester.lib.core import *
-from typing import List
class SearchTomba:
-
def __init__(self, word, limit, start) -> None:
self.word = word
self.limit = limit
@@ -12,10 +12,12 @@ class SearchTomba:
self.start = start
self.key = Core.tomba_key()
if self.key[0] is None or self.key[1] is None:
- raise MissingKey('Tomba Key and/or Secret')
+ raise MissingKey("Tomba Key and/or Secret")
self.total_results = ""
self.counter = start
- self.database = f'https://api.tomba.io/v1/domain-search?domain={self.word}&limit=10'
+ self.database = (
+ f"https://api.tomba.io/v1/domain-search?domain={self.word}&limit=10"
+ )
self.proxy = False
self.hostnames: List = []
self.emails: List = []
@@ -23,48 +25,81 @@ class SearchTomba:
async def do_search(self) -> None:
# First determine if a user account is not a free account, this call is free
is_free = True
- headers = {'User-Agent': Core.get_user_agent(), 'X-Tomba-Key': self.key[0], 'X-Tomba-Secret': self.key[1]}
- acc_info_url = 'https://api.tomba.io/v1/me'
- response = await AsyncFetcher.fetch_all([acc_info_url], headers=headers, json=True)
- is_free = is_free if 'name' in response[0]['data']['pricing'].keys() and response[0]['data']['pricing']['name'].lower() \
- == 'free' else False
+ headers = {
+ "User-Agent": Core.get_user_agent(),
+ "X-Tomba-Key": self.key[0],
+ "X-Tomba-Secret": self.key[1],
+ }
+ acc_info_url = "https://api.tomba.io/v1/me"
+ response = await AsyncFetcher.fetch_all(
+ [acc_info_url], headers=headers, json=True
+ )
+ is_free = (
+ is_free
+ if "name" in response[0]["data"]["pricing"].keys()
+ and response[0]["data"]["pricing"]["name"].lower() == "free"
+ else False
+ )
# Extract the total number of requests that are available for an account
- total_requests_avail = response[0]['data']['requests']['domains']['available'] - response[0]['data']['requests']['domains']['used']
+ total_requests_avail = (
+ response[0]["data"]["requests"]["domains"]["available"]
+ - response[0]["data"]["requests"]["domains"]["used"]
+ )
if is_free:
- response = await AsyncFetcher.fetch_all([self.database], headers=headers, proxy=self.proxy, json=True)
+ response = await AsyncFetcher.fetch_all(
+ [self.database], headers=headers, proxy=self.proxy, json=True
+ )
self.emails, self.hostnames = await self.parse_resp(json_resp=response[0])
else:
# Determine the total number of emails that are available
# As the most emails you can get within one query are 100
# This is only done where paid accounts are in play
- tomba_counter = f'https://api.tomba.io/v1/email-count?domain={self.word}'
- response = await AsyncFetcher.fetch_all([tomba_counter], headers=headers, proxy=self.proxy, json=True)
- total_number_reqs = response[0]['data']['total'] // 100
+ tomba_counter = f"https://api.tomba.io/v1/email-count?domain={self.word}"
+ response = await AsyncFetcher.fetch_all(
+ [tomba_counter], headers=headers, proxy=self.proxy, json=True
+ )
+ total_number_reqs = response[0]["data"]["total"] // 100
# Parse out meta field within initial JSON response to determine the total number of results
if total_requests_avail < total_number_reqs:
- print('WARNING: The account does not have enough requests to gather all the emails.')
- print(f'Total requests available: {total_requests_avail}, total requests '
- f'needed to be made: {total_number_reqs}')
- print('RETURNING current results, If you still wish to run this module despite the current results, please comment out the "if request" line.')
+ print(
+ "WARNING: The account does not have enough requests to gather all the emails."
+ )
+ print(
+ f"Total requests available: {total_requests_avail}, total requests "
+ f"needed to be made: {total_number_reqs}"
+ )
+ print(
+ 'RETURNING current results, If you still wish to run this module despite the current results, please comment out the "if request" line.'
+ )
return
self.limit = 100
# max number of emails you can get per request
# increments of max number with page determining where to start
# See docs for more details: https://developer.tomba.io/#domain-search
for page in range(0, total_number_reqs + 1):
- req_url = f'https://api.tomba.io/v1/domain-search?domain={self.word}&limit={self.limit}&page={page}'
- response = await AsyncFetcher.fetch_all([req_url], headers=headers, proxy=self.proxy, json=True)
+ req_url = f"https://api.tomba.io/v1/domain-search?domain={self.word}&limit={self.limit}&page={page}"
+ response = await AsyncFetcher.fetch_all(
+ [req_url], headers=headers, proxy=self.proxy, json=True
+ )
temp_emails, temp_hostnames = await self.parse_resp(response[0])
self.emails.extend(temp_emails)
self.hostnames.extend(temp_hostnames)
await asyncio.sleep(1)
async def parse_resp(self, json_resp):
- emails = list(sorted({email['email'] for email in json_resp['data']['emails']}))
- domains = list(sorted({source['website_url'] for email in json_resp['data']['emails'] for source in email['sources']
- if self.word in source['website_url']}))
+ emails = list(sorted({email["email"] for email in json_resp["data"]["emails"]}))
+ domains = list(
+ sorted(
+ {
+ source["website_url"]
+ for email in json_resp["data"]["emails"]
+ for source in email["sources"]
+ if self.word in source["website_url"]
+ }
+ )
+ )
return emails, domains
async def process(self, proxy: bool = False) -> None:
diff --git a/theHarvester/discovery/urlscan.py b/theHarvester/discovery/urlscan.py
index c5b1e7c4..c3e2371b 100644
--- a/theHarvester/discovery/urlscan.py
+++ b/theHarvester/discovery/urlscan.py
@@ -1,4 +1,5 @@
from typing import List, Set
+
from theHarvester.lib.core import *
@@ -12,13 +13,25 @@ class SearchUrlscan:
self.proxy = False
async def do_search(self) -> None:
- url = f'https://urlscan.io/api/v1/search/?q=domain:{self.word}'
+ url = f"https://urlscan.io/api/v1/search/?q=domain:{self.word}"
response = await AsyncFetcher.fetch_all([url], json=True, proxy=self.proxy)
resp = response[0]
- self.totalhosts = {f"{page['page']['domain']}" for page in resp['results']}
- self.totalips = {f"{page['page']['ip']}" for page in resp['results'] if 'ip' in page['page'].keys()}
- self.interestingurls = {f"{page['page']['url']}" for page in resp['results'] if self.word in page['page']['url'] and 'url' in page['page'].keys()}
- self.totalasns = {f"{page['page']['asn']}" for page in resp['results'] if 'asn' in page['page'].keys()}
+ self.totalhosts = {f"{page['page']['domain']}" for page in resp["results"]}
+ self.totalips = {
+ f"{page['page']['ip']}"
+ for page in resp["results"]
+ if "ip" in page["page"].keys()
+ }
+ self.interestingurls = {
+ f"{page['page']['url']}"
+ for page in resp["results"]
+ if self.word in page["page"]["url"] and "url" in page["page"].keys()
+ }
+ self.totalasns = {
+ f"{page['page']['asn']}"
+ for page in resp["results"]
+ if "asn" in page["page"].keys()
+ }
async def get_hostnames(self) -> Set:
return self.totalhosts
diff --git a/theHarvester/discovery/virustotal.py b/theHarvester/discovery/virustotal.py
index edc4d82d..5a849429 100644
--- a/theHarvester/discovery/virustotal.py
+++ b/theHarvester/discovery/virustotal.py
@@ -3,11 +3,10 @@ from theHarvester.lib.core import *
class SearchVirustotal:
-
def __init__(self, word) -> None:
self.key = Core.virustotal_key()
if self.key is None:
- raise MissingKey('virustotal')
+ raise MissingKey("virustotal")
self.word = word
self.proxy = False
self.hostnames: List = []
@@ -17,12 +16,14 @@ class SearchVirustotal:
# based on: https://developers.virustotal.com/reference/domains-relationships
# base_url = "https://www.virustotal.com/api/v3/domains/domain/subdomains?limit=40"
headers = {
- 'User-Agent': Core.get_user_agent(),
+ "User-Agent": Core.get_user_agent(),
"Accept": "application/json",
- "x-apikey": self.key
+ "x-apikey": self.key,
}
- base_url = f"https://www.virustotal.com/api/v3/domains/{self.word}/subdomains?limit=40"
- cursor = ''
+ base_url = (
+ f"https://www.virustotal.com/api/v3/domains/{self.word}/subdomains?limit=40"
+ )
+ cursor = ""
count = 0
fail_counter = 0
counter = 0
@@ -34,28 +35,43 @@ class SearchVirustotal:
# TODO add timer logic if proven to be needed
# in the meantime sleeping 16 seconds should eliminate hitting the rate limit
# in case rate limit is hit, fail counter exists and sleep for 65 seconds
- send_url = base_url + "&cursor=" + cursor if cursor != '' and len(cursor) > 2 else base_url
- responses = await AsyncFetcher.fetch_all([send_url], headers=headers, proxy=self.proxy, json=True)
+ send_url = (
+ base_url + "&cursor=" + cursor
+ if cursor != "" and len(cursor) > 2
+ else base_url
+ )
+ responses = await AsyncFetcher.fetch_all(
+ [send_url], headers=headers, proxy=self.proxy, json=True
+ )
jdata = responses[0]
- if 'data' not in jdata.keys():
+ if "data" not in jdata.keys():
await asyncio.sleep(60 + 5)
fail_counter += 1
- if 'meta' in jdata.keys():
- cursor = jdata['meta']['cursor'] if 'cursor' in jdata['meta'].keys() else ''
- if len(cursor) == 0 and 'data' in jdata.keys():
+ if "meta" in jdata.keys():
+ cursor = (
+ jdata["meta"]["cursor"] if "cursor" in jdata["meta"].keys() else ""
+ )
+ if len(cursor) == 0 and "data" in jdata.keys():
# if cursor no longer is within the meta field have hit last entry
breakcon = True
- count += jdata['meta']['count']
+ count += jdata["meta"]["count"]
if count == 0 or fail_counter >= 2:
break
- if 'data' in jdata.keys():
- data = jdata['data']
+ if "data" in jdata.keys():
+ data = jdata["data"]
self.hostnames.extend(await self.parse_hostnames(data, self.word))
counter += 1
await asyncio.sleep(16)
self.hostnames = list(sorted(set(self.hostnames)))
# verify domains such as x.x.com.multicdn.x.com are parsed properly
- self.hostnames = [host for host in self.hostnames if ((len(host.split('.')) >= 3) and host.split('.')[-2] == self.word.split('.')[-2])]
+ self.hostnames = [
+ host
+ for host in self.hostnames
+ if (
+ (len(host.split(".")) >= 3)
+ and host.split(".")[-2] == self.word.split(".")[-2]
+ )
+ ]
async def get_hostnames(self) -> list:
return self.hostnames
@@ -64,21 +80,36 @@ class SearchVirustotal:
async def parse_hostnames(data, word):
total_subdomains = set()
for attribute in data:
- total_subdomains.add(attribute['id'].replace('"', '').replace('www.', ''))
- attributes = attribute['attributes']
+ total_subdomains.add(attribute["id"].replace('"', "").replace("www.", ""))
+ attributes = attribute["attributes"]
total_subdomains.update(
- {value['value'].replace('"', '').replace('www.', '') for value in attributes['last_dns_records'] if
- word in value['value']})
- if 'last_https_certificate' in attributes.keys():
- total_subdomains.update({value.replace('"', '').replace('www.', '') for value in
- attributes['last_https_certificate']['extensions']['subject_alternative_name']
- if word in value})
+ {
+ value["value"].replace('"', "").replace("www.", "")
+ for value in attributes["last_dns_records"]
+ if word in value["value"]
+ }
+ )
+ if "last_https_certificate" in attributes.keys():
+ total_subdomains.update(
+ {
+ value.replace('"', "").replace("www.", "")
+ for value in attributes["last_https_certificate"]["extensions"][
+ "subject_alternative_name"
+ ]
+ if word in value
+ }
+ )
total_subdomains = list(sorted(total_subdomains))
# Other false positives may occur over time and yes there are other ways to parse this, feel free to implement
# them and submit a PR or raise an issue if you run into this filtering not being enough
# TODO determine if parsing 'v=spf1 include:_spf-x.acme.com include:_spf-x.acme.com' is worth parsing
- total_subdomains = [x for x in total_subdomains if not str(x).endswith('edgekey.net') and not str(x).endswith(
- 'akadns.net') and 'include:_spf' not in str(x)]
+ total_subdomains = [
+ x
+ for x in total_subdomains
+ if not str(x).endswith("edgekey.net")
+ and not str(x).endswith("akadns.net")
+ and "include:_spf" not in str(x)
+ ]
total_subdomains.sort()
return total_subdomains
diff --git a/theHarvester/discovery/yahoosearch.py b/theHarvester/discovery/yahoosearch.py
index a71ed604..d0c069b0 100644
--- a/theHarvester/discovery/yahoosearch.py
+++ b/theHarvester/discovery/yahoosearch.py
@@ -3,22 +3,24 @@ from theHarvester.parsers import myparser
class SearchYahoo:
-
def __init__(self, word, limit) -> None:
self.word = word
self.total_results = ""
- self.server = 'search.yahoo.com'
+ self.server = "search.yahoo.com"
self.limit = limit
self.proxy = False
async def do_search(self) -> None:
- base_url = f'https://{self.server}/search?p=%40{self.word}&b=xx&pz=10'
- headers = {
- 'Host': self.server,
- 'User-agent': Core.get_user_agent()
- }
- urls = [base_url.replace("xx", str(num)) for num in range(0, self.limit, 10) if num <= self.limit]
- responses = await AsyncFetcher.fetch_all(urls, headers=headers, proxy=self.proxy)
+ base_url = f"https://{self.server}/search?p=%40{self.word}&b=xx&pz=10"
+ headers = {"Host": self.server, "User-agent": Core.get_user_agent()}
+ urls = [
+ base_url.replace("xx", str(num))
+ for num in range(0, self.limit, 10)
+ if num <= self.limit
+ ]
+ responses = await AsyncFetcher.fetch_all(
+ urls, headers=headers, proxy=self.proxy
+ )
for response in responses:
self.total_results += response
@@ -33,8 +35,8 @@ class SearchYahoo:
# strip out numbers and dashes for emails that look like xxx-xxx-xxxemail@host.tld
for email in toparse_emails:
email = str(email)
- if '-' in email and email[0].isdigit() and email.index('-') <= 9:
- while email[0] == '-' or email[0].isdigit():
+ if "-" in email and email[0].isdigit() and email.index("-") <= 9:
+ while email[0] == "-" or email[0].isdigit():
email = email[1:]
emails.add(email)
return list(emails)
diff --git a/theHarvester/lib/__init__.py b/theHarvester/lib/__init__.py
index 8fc0aea3..7145285d 100644
--- a/theHarvester/lib/__init__.py
+++ b/theHarvester/lib/__init__.py
@@ -1 +1 @@
-__all__ = ['hostchecker']
+__all__ = ["hostchecker"]
diff --git a/theHarvester/lib/api/api.py b/theHarvester/lib/api/api.py
index 0849896c..508b5cd5 100644
--- a/theHarvester/lib/api/api.py
+++ b/theHarvester/lib/api/api.py
@@ -1,6 +1,7 @@
import argparse
-from typing import Any, Dict, Union, List
import os
+from typing import Any, Dict, List, Union
+
from fastapi import FastAPI, Header, Query, Request
from fastapi.responses import HTMLResponse, UJSONResponse
from slowapi import Limiter, _rate_limit_exceeded_handler
@@ -12,25 +13,37 @@ from starlette.staticfiles import StaticFiles
from theHarvester import __main__
limiter = Limiter(key_func=get_remote_address)
-app = FastAPI(title='Restful Harvest', description='Rest API for theHarvester powered by FastAPI', version='0.0.2')
+app = FastAPI(
+ title="Restful Harvest",
+ description="Rest API for theHarvester powered by FastAPI",
+ version="0.0.2",
+)
app.state.limiter = limiter
app.add_exception_handler(RateLimitExceeded, _rate_limit_exceeded_handler)
# This is where we will host files that arise if the user specifies a filename
try:
- app.mount('/static', StaticFiles(directory='theHarvester/lib/api/static/'), name='static')
+ app.mount(
+ "/static", StaticFiles(directory="theHarvester/lib/api/static/"), name="static"
+ )
except RuntimeError:
- static_path = os.path.expanduser('~/.local/share/theHarvester/static/')
+ static_path = os.path.expanduser("~/.local/share/theHarvester/static/")
if not os.path.isdir(static_path):
os.makedirs(static_path)
- app.mount('/static', StaticFiles(directory='~/.local/share/theHarvester/static/'), name='static')
+ app.mount(
+ "/static",
+ StaticFiles(directory="~/.local/share/theHarvester/static/"),
+ name="static",
+ )
-@app.get('/', response_class=HTMLResponse)
+@app.get("/", response_class=HTMLResponse)
async def root(*, user_agent: str = Header(None)) -> Union[RedirectResponse, str]:
# very basic user agent filtering
- if user_agent and ('gobuster' in user_agent or 'sqlmap' in user_agent or 'rustbuster' in user_agent):
- response = RedirectResponse(app.url_path_for('bot'))
+ if user_agent and (
+ "gobuster" in user_agent or "sqlmap" in user_agent or "rustbuster" in user_agent
+ ):
+ response = RedirectResponse(app.url_path_for("bot"))
return response
html = """
@@ -58,93 +71,130 @@ async def root(*, user_agent: str = Header(None)) -> Union[RedirectResponse, str
return html
-@app.get('/nicebot')
+@app.get("/nicebot")
async def bot() -> Dict[str, str]:
# nice bot
- string = {'bot': 'These are not the droids you are looking for'}
+ string = {"bot": "These are not the droids you are looking for"}
return string
-@app.get('/sources', response_class=UJSONResponse)
-@limiter.limit('5/minute')
+@app.get("/sources", response_class=UJSONResponse)
+@limiter.limit("5/minute")
async def getsources(request: Request):
# Endpoint for user to query for available sources theHarvester supports
# Rate limit of 5 requests per minute
sources = __main__.Core.get_supportedengines()
- return {'sources': sources}
+ return {"sources": sources}
-@app.get('/dnsbrute', response_class=UJSONResponse)
-@limiter.limit('5/minute')
-async def dnsbrute(request: Request, user_agent: str = Header(None),
- domain: str = Query(..., description='Domain to be brute forced')) -> Union[Dict[str, Any], RedirectResponse]:
+@app.get("/dnsbrute", response_class=UJSONResponse)
+@limiter.limit("5/minute")
+async def dnsbrute(
+ request: Request,
+ user_agent: str = Header(None),
+ domain: str = Query(..., description="Domain to be brute forced"),
+) -> Union[Dict[str, Any], RedirectResponse]:
# Endpoint for user to signal to do DNS brute forcing
# Rate limit of 5 requests per minute
# basic user agent filtering
- if user_agent and ('gobuster' in user_agent or 'sqlmap' in user_agent or 'rustbuster' in user_agent):
- response = RedirectResponse(app.url_path_for('bot'))
+ if user_agent and (
+ "gobuster" in user_agent or "sqlmap" in user_agent or "rustbuster" in user_agent
+ ):
+ response = RedirectResponse(app.url_path_for("bot"))
return response
- dns_bruteforce = await __main__.start(argparse.Namespace(dns_brute=True,
- dns_lookup=False,
- dns_server=False,
- dns_tld=False,
- domain=domain,
- filename='',
- google_dork=False,
- limit=500,
- proxies=False,
- shodan=False,
- source=','.join([]),
- start=0,
- take_over=False,
- virtual_host=False))
- return {'dns_bruteforce': dns_bruteforce}
+ dns_bruteforce = await __main__.start(
+ argparse.Namespace(
+ dns_brute=True,
+ dns_lookup=False,
+ dns_server=False,
+ dns_tld=False,
+ domain=domain,
+ filename="",
+ google_dork=False,
+ limit=500,
+ proxies=False,
+ shodan=False,
+ source=",".join([]),
+ start=0,
+ take_over=False,
+ virtual_host=False,
+ )
+ )
+ return {"dns_bruteforce": dns_bruteforce}
-@app.get('/query', response_class=UJSONResponse)
-@limiter.limit('2/minute')
-async def query(request: Request, dns_server: str = Query(""), user_agent: str = Header(None),
- dns_brute: bool = Query(False),
- dns_lookup: bool = Query(False),
- dns_tld: bool = Query(False),
- filename: str = Query(""),
- google_dork: bool = Query(False), proxies: bool = Query(False), shodan: bool = Query(False),
- take_over: bool = Query(False), virtual_host: bool = Query(False),
- source: List[str] = Query(..., description='Data sources to query comma separated with no space'),
- limit: int = Query(500), start: int = Query(0),
- domain: str = Query(..., description='Domain to be harvested')) -> Union[Dict[str, Any], RedirectResponse]:
-
+@app.get("/query", response_class=UJSONResponse)
+@limiter.limit("2/minute")
+async def query(
+ request: Request,
+ dns_server: str = Query(""),
+ user_agent: str = Header(None),
+ dns_brute: bool = Query(False),
+ dns_lookup: bool = Query(False),
+ dns_tld: bool = Query(False),
+ filename: str = Query(""),
+ google_dork: bool = Query(False),
+ proxies: bool = Query(False),
+ shodan: bool = Query(False),
+ take_over: bool = Query(False),
+ virtual_host: bool = Query(False),
+ source: List[str] = Query(
+ ..., description="Data sources to query comma separated with no space"
+ ),
+ limit: int = Query(500),
+ start: int = Query(0),
+ domain: str = Query(..., description="Domain to be harvested"),
+) -> Union[Dict[str, Any], RedirectResponse]:
# Query function that allows user to query theHarvester rest API
# Rate limit of 2 requests per minute
# basic user agent filtering
- if user_agent and ('gobuster' in user_agent or 'sqlmap' in user_agent or 'rustbuster' in user_agent):
- response = RedirectResponse(app.url_path_for('bot'))
+ if user_agent and (
+ "gobuster" in user_agent or "sqlmap" in user_agent or "rustbuster" in user_agent
+ ):
+ response = RedirectResponse(app.url_path_for("bot"))
return response
try:
- asns, iurls, twitter_people_list, \
- linkedin_people_list, linkedin_links, \
- aurls, aips, aemails, ahosts = await __main__.start(argparse.Namespace(dns_brute=dns_brute,
- dns_lookup=dns_lookup,
- dns_server=dns_server,
- dns_tld=dns_tld,
- domain=domain,
- filename=filename,
- google_dork=google_dork,
- limit=limit,
- proxies=proxies,
- shodan=shodan,
- source=','.join(source),
- start=start,
- take_over=take_over,
- virtual_host=virtual_host))
+ (
+ asns,
+ iurls,
+ twitter_people_list,
+ linkedin_people_list,
+ linkedin_links,
+ aurls,
+ aips,
+ aemails,
+ ahosts,
+ ) = await __main__.start(
+ argparse.Namespace(
+ dns_brute=dns_brute,
+ dns_lookup=dns_lookup,
+ dns_server=dns_server,
+ dns_tld=dns_tld,
+ domain=domain,
+ filename=filename,
+ google_dork=google_dork,
+ limit=limit,
+ proxies=proxies,
+ shodan=shodan,
+ source=",".join(source),
+ start=start,
+ take_over=take_over,
+ virtual_host=virtual_host,
+ )
+ )
- return {'asns': asns, 'interesting_urls': iurls,
- 'twitter_people': twitter_people_list,
- 'linkedin_people': linkedin_people_list,
- 'linkedin_links': linkedin_links,
- 'trello_urls': aurls,
- 'ips': aips,
- 'emails': aemails,
- 'hosts': ahosts}
+ return {
+ "asns": asns,
+ "interesting_urls": iurls,
+ "twitter_people": twitter_people_list,
+ "linkedin_people": linkedin_people_list,
+ "linkedin_links": linkedin_links,
+ "trello_urls": aurls,
+ "ips": aips,
+ "emails": aemails,
+ "hosts": ahosts,
+ }
except Exception:
- return {'exception': 'Please contact the server administrator to check the issue'}
+ return {
+ "exception": "Please contact the server administrator to check the issue"
+ }
diff --git a/theHarvester/lib/api/api_example.py b/theHarvester/lib/api/api_example.py
index e9799d77..b81ba7e9 100644
--- a/theHarvester/lib/api/api_example.py
+++ b/theHarvester/lib/api/api_example.py
@@ -3,6 +3,7 @@ Example script to query theHarvester rest API, obtain results, and write out to
"""
import asyncio
+
import aiohttp
import netaddr
@@ -24,92 +25,98 @@ async def main() -> None:
"""
url = "http://127.0.0.1:5000"
domain = "netflix.com"
- query_url = f'{url}/query?limit=300&source=bing,baidu,duckduckgo,dogpile&domain={domain}'
+ query_url = (
+ f"{url}/query?limit=300&source=bing,baidu,duckduckgo,dogpile&domain={domain}"
+ )
async with aiohttp.ClientSession() as session:
fetched_json = await fetch_json(session, query_url)
- total_asns = fetched_json['asns']
- interesting_urls = fetched_json['interesting_urls']
- twitter_people_list_tracker = fetched_json['twitter_people']
- linkedin_people_list_tracker = fetched_json['linkedin_people']
- linkedin_links_tracker = fetched_json['linkedin_links']
- trello_urls = fetched_json['trello_urls']
- ips = fetched_json['ips']
- emails = fetched_json['emails']
- hosts = fetched_json['hosts']
+ total_asns = fetched_json["asns"]
+ interesting_urls = fetched_json["interesting_urls"]
+ twitter_people_list_tracker = fetched_json["twitter_people"]
+ linkedin_people_list_tracker = fetched_json["linkedin_people"]
+ linkedin_links_tracker = fetched_json["linkedin_links"]
+ trello_urls = fetched_json["trello_urls"]
+ ips = fetched_json["ips"]
+ emails = fetched_json["emails"]
+ hosts = fetched_json["hosts"]
if len(total_asns) > 0:
- print(f'\n[*] ASNS found: {len(total_asns)}')
- print('--------------------')
+ print(f"\n[*] ASNS found: {len(total_asns)}")
+ print("--------------------")
total_asns = list(sorted(set(total_asns)))
for asn in total_asns:
print(asn)
if len(interesting_urls) > 0:
- print(f'\n[*] Interesting Urls found: {len(interesting_urls)}')
- print('--------------------')
+ print(f"\n[*] Interesting Urls found: {len(interesting_urls)}")
+ print("--------------------")
interesting_urls = list(sorted(set(interesting_urls)))
for iurl in interesting_urls:
print(iurl)
if len(twitter_people_list_tracker) == 0:
- print('\n[*] No Twitter users found.\n\n')
+ print("\n[*] No Twitter users found.\n\n")
else:
if len(twitter_people_list_tracker) >= 1:
- print('\n[*] Twitter Users found: ' + str(len(twitter_people_list_tracker)))
- print('---------------------')
+ print("\n[*] Twitter Users found: " + str(len(twitter_people_list_tracker)))
+ print("---------------------")
twitter_people_list_tracker = list(sorted(set(twitter_people_list_tracker)))
for usr in twitter_people_list_tracker:
print(usr)
if len(linkedin_people_list_tracker) == 0:
- print('\n[*] No LinkedIn users found.\n\n')
+ print("\n[*] No LinkedIn users found.\n\n")
else:
if len(linkedin_people_list_tracker) >= 1:
- print('\n[*] LinkedIn Users found: ' + str(len(linkedin_people_list_tracker)))
- print('---------------------')
- linkedin_people_list_tracker = list(sorted(set(linkedin_people_list_tracker)))
+ print(
+ "\n[*] LinkedIn Users found: " + str(len(linkedin_people_list_tracker))
+ )
+ print("---------------------")
+ linkedin_people_list_tracker = list(
+ sorted(set(linkedin_people_list_tracker))
+ )
for usr in linkedin_people_list_tracker:
print(usr)
if len(linkedin_links_tracker) == 0:
- print(f'\n[*] LinkedIn Links found: {len(linkedin_links_tracker)}')
+ print(f"\n[*] LinkedIn Links found: {len(linkedin_links_tracker)}")
linkedin_links_tracker = list(sorted(set(linkedin_links_tracker)))
- print('---------------------')
+ print("---------------------")
for link in linkedin_links_tracker:
print(link)
length_urls = len(trello_urls)
total = length_urls
- print('\n[*] Trello URLs found: ' + str(total))
- print('--------------------')
+ print("\n[*] Trello URLs found: " + str(total))
+ print("--------------------")
all_urls = list(sorted(set(trello_urls)))
for url in sorted(all_urls):
print(url)
if len(ips) == 0:
- print('\n[*] No IPs found.')
+ print("\n[*] No IPs found.")
else:
- print('\n[*] IPs found: ' + str(len(ips)))
- print('-------------------')
+ print("\n[*] IPs found: " + str(len(ips)))
+ print("-------------------")
# use netaddr as the list may contain ipv4 and ipv6 addresses
ip_list = sorted([netaddr.IPAddress(ip.strip()) for ip in set(ips)])
- print('\n'.join(map(str, ip_list)))
+ print("\n".join(map(str, ip_list)))
if len(emails) == 0:
- print('\n[*] No emails found.')
+ print("\n[*] No emails found.")
else:
- print('\n[*] Emails found: ' + str(len(emails)))
- print('----------------------')
+ print("\n[*] Emails found: " + str(len(emails)))
+ print("----------------------")
all_emails = sorted(list(set(emails)))
- print(('\n'.join(all_emails)))
+ print(("\n".join(all_emails)))
if len(hosts) == 0:
- print('\n[*] No hosts found.\n\n')
+ print("\n[*] No hosts found.\n\n")
else:
- print('\n[*] Hosts found: ' + str(len(hosts)))
- print('---------------------')
- print('\n'.join(hosts))
+ print("\n[*] Hosts found: " + str(len(hosts)))
+ print("---------------------")
+ print("\n".join(hosts))
-if __name__ == '__main__':
+if __name__ == "__main__":
asyncio.run(main())
diff --git a/theHarvester/lib/hostchecker.py b/theHarvester/lib/hostchecker.py
index 735bbaac..219841a0 100644
--- a/theHarvester/lib/hostchecker.py
+++ b/theHarvester/lib/hostchecker.py
@@ -7,14 +7,14 @@ Revised to use aiodns & asyncio on 2019-09-23
# Support for Python3.9
from __future__ import annotations
-import aiodns
import asyncio
import socket
-from typing import Tuple, Any, List, Set
+from typing import Any, List, Set, Tuple
+
+import aiodns
class Checker:
-
def __init__(self, hosts: list, nameserver: list) -> None:
self.hosts = hosts
self.realhosts: List = []
@@ -42,7 +42,7 @@ class Checker:
if addresses == [] or addresses is None or result is None:
return f"{host}:"
else:
- addresses = ','.join(map(str, list(sorted(set(addresses)))))
+ addresses = ",".join(map(str, list(sorted(set(addresses)))))
# addresses = list(sorted(addresses))
return f"{host}:{addresses}"
except Exception:
@@ -53,27 +53,31 @@ class Checker:
def chunks(lst, n):
"""Yield successive n-sized chunks from lst."""
for i in range(0, len(lst), n):
- yield lst[i:i + n]
+ yield lst[i : i + n]
async def query_all(self, resolver, hosts) -> list[Any]:
# TODO chunk list into 50 pieces regardless of IPs and subnets
- results = await asyncio.gather(*[asyncio.create_task(self.resolve_host(host, resolver))
- for host in hosts])
+ results = await asyncio.gather(
+ *[asyncio.create_task(self.resolve_host(host, resolver)) for host in hosts]
+ )
return results
async def check(self):
loop = asyncio.get_event_loop()
- resolver = aiodns.DNSResolver(loop=loop, timeout=8) if len(self.nameserver) == 0 \
+ resolver = (
+ aiodns.DNSResolver(loop=loop, timeout=8)
+ if len(self.nameserver) == 0
else aiodns.DNSResolver(loop=loop, timeout=8, nameservers=self.nameserver)
+ )
all_results = set()
for chunk in self.chunks(self.hosts, 50):
# TODO split this to get IPs added total ips
results = await self.query_all(resolver, chunk)
all_results.update(results)
for pair in results:
- host, addresses = pair.split(':')
+ host, addresses = pair.split(":")
self.realhosts.append(host)
- self.addresses.update({addr for addr in addresses.split(',')})
+ self.addresses.update({addr for addr in addresses.split(",")})
# address may be a list of ips
# and do a set comprehension to remove duplicates
self.realhosts.sort()
diff --git a/theHarvester/lib/version.py b/theHarvester/lib/version.py
index e5ea2a6a..99e39bb2 100644
--- a/theHarvester/lib/version.py
+++ b/theHarvester/lib/version.py
@@ -1,4 +1,5 @@
# coding=utf-8
+
def version() -> str:
- return '4.4.1'
+ return "4.4.1"
diff --git a/theHarvester/parsers/intelxparser.py b/theHarvester/parsers/intelxparser.py
index d61de739..030c451e 100644
--- a/theHarvester/parsers/intelxparser.py
+++ b/theHarvester/parsers/intelxparser.py
@@ -2,7 +2,6 @@ from typing import Set
class Parser:
-
def __init__(self) -> None:
self.emails: Set = set()
self.hosts: Set = set()
@@ -15,16 +14,16 @@ class Parser:
"""
if results is not None:
for dictionary in results["selectors"]:
- field = dictionary['selectorvalue']
- if '@' in field:
+ field = dictionary["selectorvalue"]
+ if "@" in field:
self.emails.add(field)
else:
field = str(field)
- if 'http' in field or 'https' in field:
- if field[:5] == 'https':
+ if "http" in field or "https" in field:
+ if field[:5] == "https":
field = field[8:]
else:
field = field[7:]
- self.hosts.add(field.replace(')', '').replace(',', ''))
+ self.hosts.add(field.replace(")", "").replace(",", ""))
return self.emails, self.hosts
return None, None
diff --git a/theHarvester/parsers/myparser.py b/theHarvester/parsers/myparser.py
index 5de16a24..62b88437 100644
--- a/theHarvester/parsers/myparser.py
+++ b/theHarvester/parsers/myparser.py
@@ -1,36 +1,70 @@
import re
-from typing import Set, List
+from typing import List, Set
class Parser:
-
def __init__(self, results, word) -> None:
self.results = results
self.word = word
self.temp: List = []
async def genericClean(self) -> None:
- self.results = self.results.replace('', '').replace('', '').replace('', '').replace('', '') \
- .replace('%3a', '').replace('', '').replace('', '') \
- .replace('', '').replace('', '')
+ self.results = (
+ self.results.replace("", "")
+ .replace("", "")
+ .replace("", "")
+ .replace("", "")
+ .replace("%3a", "")
+ .replace("", "")
+ .replace("", "")
+ .replace("", "")
+ .replace("", "")
+ )
- for search in ('<', '>', ':', '=', ';', '&', '%3A', '%3D', '%3C', '%2f', '/', '\\'):
- self.results = self.results.replace(search, ' ')
+ for search in (
+ "<",
+ ">",
+ ":",
+ "=",
+ ";",
+ "&",
+ "%3A",
+ "%3D",
+ "%3C",
+ "%2f",
+ "/",
+ "\\",
+ ):
+ self.results = self.results.replace(search, " ")
async def urlClean(self) -> None:
- self.results = self.results.replace('', '').replace('', '').replace('%2f', '').replace('%3a', '')
- for search in ('<', '>', ':', '=', ';', '&', '%3A', '%3D', '%3C'):
- self.results = self.results.replace(search, ' ')
+ self.results = (
+ self.results.replace("", "")
+ .replace("", "")
+ .replace("%2f", "")
+ .replace("%3a", "")
+ )
+ for search in ("<", ">", ":", "=", ";", "&", "%3A", "%3D", "%3C"):
+ self.results = self.results.replace(search, " ")
async def emails(self):
await self.genericClean()
# Local part is required, charset is flexible.
# https://tools.ietf.org/html/rfc6531 (removed * and () as they provide FP mostly)
- reg_emails = re.compile(r'[a-zA-Z0-9.\-_+#~!$&\',;=:]+' + '@' + '[a-zA-Z0-9.-]*' + self.word.replace('www.', ''))
+ reg_emails = re.compile(
+ r"[a-zA-Z0-9.\-_+#~!$&\',;=:]+"
+ + "@"
+ + "[a-zA-Z0-9.-]*"
+ + self.word.replace("www.", "")
+ )
self.temp = reg_emails.findall(self.results)
emails = await self.unique()
- true_emails = {str(email)[1:].lower().strip() if len(str(email)) > 1 and str(email)[0] == '.'
- else len(str(email)) > 1 and str(email).lower().strip() for email in emails}
+ true_emails = {
+ str(email)[1:].lower().strip()
+ if len(str(email)) > 1 and str(email)[0] == "."
+ else len(str(email)) > 1 and str(email).lower().strip()
+ for email in emails
+ }
# if email starts with dot shift email string and make sure all emails are lowercase
return true_emails
@@ -40,7 +74,11 @@ class Parser:
self.temp = reg_urls.findall(self.results)
allurls = await self.unique()
for iteration in allurls:
- if iteration.count('webcache') or iteration.count('google.com') or iteration.count('search?hl'):
+ if (
+ iteration.count("webcache")
+ or iteration.count("google.com")
+ or iteration.count("search?hl")
+ ):
pass
else:
urls.append(iteration)
@@ -50,11 +88,11 @@ class Parser:
# should check both www. and not www.
hostnames = []
await self.genericClean()
- reg_hosts = re.compile(r'[a-zA-Z0-9.-]*\.' + self.word)
+ reg_hosts = re.compile(r"[a-zA-Z0-9.-]*\." + self.word)
first_hostnames = reg_hosts.findall(self.results)
hostnames.extend(first_hostnames)
# TODO determine if necessary below or if only pass through is fine
- reg_hosts = re.compile(r'[a-zA-Z0-9.-]*\.' + self.word.replace('www.', ''))
+ reg_hosts = re.compile(r"[a-zA-Z0-9.-]*\." + self.word.replace("www.", ""))
# reg_hosts = re.compile(r'www\.[a-zA-Z0-9.-]*\.' + 'www.' + self.word)
# reg_hosts = re.compile(r'www\.[a-zA-Z0-9.-]*\.(?:' + 'www.' + self.word + ')?')
second_hostnames = reg_hosts.findall(self.results)
@@ -62,29 +100,31 @@ class Parser:
return list(set(hostnames))
async def hostnames_all(self):
- reg_hosts = re.compile('(.*?)')
+ reg_hosts = re.compile("(.*?)")
temp = reg_hosts.findall(self.results)
for iteration in temp:
- if iteration.count(':'):
- res = iteration.split(':')[1].split('/')[2]
+ if iteration.count(":"):
+ res = iteration.split(":")[1].split("/")[2]
else:
- res = iteration.split('/')[0]
+ res = iteration.split("/")[0]
self.temp.append(res)
hostnames = await self.unique()
return hostnames
async def set(self):
- reg_sets = re.compile(r'>[a-zA-Z\d]*')
+ reg_sets = re.compile(r">[a-zA-Z\d]*")
self.temp = reg_sets.findall(self.results)
sets = []
for iteration in self.temp:
- delete = iteration.replace('>', '')
- delete = delete.replace('", "")
+ delete = delete.replace(" Set[str]:
- found = re.finditer(r'(http|https)://(www\.)?trello.com/([a-zA-Z\d\-_\.]+/?)*', self.results)
+ found = re.finditer(
+ r"(http|https)://(www\.)?trello.com/([a-zA-Z\d\-_\.]+/?)*", self.results
+ )
urls = {match.group().strip() for match in found}
return urls