bbot 2.6.0.6879rc0__py3-none-any.whl → 2.7.2.7254rc0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of bbot might be problematic. Click here for more details.
- bbot/__init__.py +1 -1
- bbot/core/engine.py +1 -1
- bbot/core/flags.py +1 -0
- bbot/core/helpers/bloom.py +6 -7
- bbot/core/helpers/dns/dns.py +0 -1
- bbot/core/helpers/dns/engine.py +0 -2
- bbot/core/helpers/files.py +2 -2
- bbot/core/helpers/git.py +17 -0
- bbot/core/helpers/misc.py +1 -0
- bbot/core/helpers/ntlm.py +0 -2
- bbot/core/helpers/regex.py +1 -1
- bbot/core/modules.py +0 -54
- bbot/defaults.yml +4 -2
- bbot/modules/apkpure.py +1 -1
- bbot/modules/base.py +11 -5
- bbot/modules/dnsbimi.py +1 -4
- bbot/modules/dnsdumpster.py +35 -52
- bbot/modules/dnstlsrpt.py +0 -6
- bbot/modules/docker_pull.py +1 -1
- bbot/modules/emailformat.py +17 -1
- bbot/modules/filedownload.py +1 -1
- bbot/modules/git_clone.py +47 -22
- bbot/modules/gitdumper.py +4 -14
- bbot/modules/github_workflows.py +1 -1
- bbot/modules/gitlab_com.py +31 -0
- bbot/modules/gitlab_onprem.py +84 -0
- bbot/modules/gowitness.py +0 -6
- bbot/modules/graphql_introspection.py +5 -2
- bbot/modules/httpx.py +2 -0
- bbot/modules/iis_shortnames.py +0 -7
- bbot/modules/internal/unarchive.py +9 -3
- bbot/modules/lightfuzz/lightfuzz.py +5 -1
- bbot/modules/nuclei.py +1 -1
- bbot/modules/output/base.py +0 -5
- bbot/modules/postman_download.py +1 -1
- bbot/modules/retirejs.py +232 -0
- bbot/modules/securitytxt.py +0 -3
- bbot/modules/subdomaincenter.py +1 -16
- bbot/modules/telerik.py +6 -1
- bbot/modules/templates/gitlab.py +98 -0
- bbot/modules/trufflehog.py +1 -1
- bbot/scanner/manager.py +7 -4
- bbot/scanner/scanner.py +1 -1
- bbot/scripts/benchmark_report.py +433 -0
- bbot/test/benchmarks/__init__.py +2 -0
- bbot/test/benchmarks/test_bloom_filter_benchmarks.py +105 -0
- bbot/test/benchmarks/test_closest_match_benchmarks.py +76 -0
- bbot/test/benchmarks/test_event_validation_benchmarks.py +438 -0
- bbot/test/benchmarks/test_excavate_benchmarks.py +291 -0
- bbot/test/benchmarks/test_ipaddress_benchmarks.py +143 -0
- bbot/test/benchmarks/test_weighted_shuffle_benchmarks.py +70 -0
- bbot/test/test_step_1/test_bbot_fastapi.py +2 -2
- bbot/test/test_step_1/test_events.py +0 -1
- bbot/test/test_step_1/test_scan.py +1 -8
- bbot/test/test_step_2/module_tests/base.py +6 -1
- bbot/test/test_step_2/module_tests/test_module_dnsbimi.py +2 -1
- bbot/test/test_step_2/module_tests/test_module_dnsdumpster.py +3 -5
- bbot/test/test_step_2/module_tests/test_module_emailformat.py +1 -1
- bbot/test/test_step_2/module_tests/test_module_emails.py +2 -2
- bbot/test/test_step_2/module_tests/test_module_excavate.py +35 -6
- bbot/test/test_step_2/module_tests/test_module_gitlab_com.py +66 -0
- bbot/test/test_step_2/module_tests/{test_module_gitlab.py → test_module_gitlab_onprem.py} +4 -69
- bbot/test/test_step_2/module_tests/test_module_lightfuzz.py +2 -2
- bbot/test/test_step_2/module_tests/test_module_retirejs.py +159 -0
- bbot/test/test_step_2/module_tests/test_module_telerik.py +1 -1
- {bbot-2.6.0.6879rc0.dist-info → bbot-2.7.2.7254rc0.dist-info}/METADATA +7 -4
- {bbot-2.6.0.6879rc0.dist-info → bbot-2.7.2.7254rc0.dist-info}/RECORD +70 -60
- {bbot-2.6.0.6879rc0.dist-info → bbot-2.7.2.7254rc0.dist-info}/WHEEL +1 -1
- bbot/modules/censys.py +0 -98
- bbot/modules/gitlab.py +0 -141
- bbot/modules/zoomeye.py +0 -77
- bbot/test/test_step_2/module_tests/test_module_censys.py +0 -83
- bbot/test/test_step_2/module_tests/test_module_zoomeye.py +0 -35
- {bbot-2.6.0.6879rc0.dist-info → bbot-2.7.2.7254rc0.dist-info}/entry_points.txt +0 -0
- {bbot-2.6.0.6879rc0.dist-info → bbot-2.7.2.7254rc0.dist-info/licenses}/LICENSE +0 -0
bbot/modules/censys.py
DELETED
|
@@ -1,98 +0,0 @@
|
|
|
1
|
-
from bbot.modules.templates.subdomain_enum import subdomain_enum_apikey
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
class censys(subdomain_enum_apikey):
|
|
5
|
-
"""
|
|
6
|
-
thanks to https://github.com/owasp-amass/amass/blob/master/resources/scripts/cert/censys.ads
|
|
7
|
-
"""
|
|
8
|
-
|
|
9
|
-
watched_events = ["DNS_NAME"]
|
|
10
|
-
produced_events = ["DNS_NAME"]
|
|
11
|
-
flags = ["subdomain-enum", "passive", "safe"]
|
|
12
|
-
meta = {
|
|
13
|
-
"description": "Query the Censys API",
|
|
14
|
-
"created_date": "2022-08-04",
|
|
15
|
-
"author": "@TheTechromancer",
|
|
16
|
-
"auth_required": True,
|
|
17
|
-
}
|
|
18
|
-
options = {"api_key": "", "max_pages": 5}
|
|
19
|
-
options_desc = {
|
|
20
|
-
"api_key": "Censys.io API Key in the format of 'key:secret'",
|
|
21
|
-
"max_pages": "Maximum number of pages to fetch (100 results per page)",
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
base_url = "https://search.censys.io/api"
|
|
25
|
-
|
|
26
|
-
async def setup(self):
|
|
27
|
-
self.max_pages = self.config.get("max_pages", 5)
|
|
28
|
-
return await super().setup()
|
|
29
|
-
|
|
30
|
-
async def ping(self):
|
|
31
|
-
url = f"{self.base_url}/v1/account"
|
|
32
|
-
resp = await self.api_request(url, retry_on_http_429=False)
|
|
33
|
-
d = resp.json()
|
|
34
|
-
assert isinstance(d, dict), f"Invalid response from {url}: {resp}"
|
|
35
|
-
quota = d.get("quota", {})
|
|
36
|
-
used = int(quota.get("used", 0))
|
|
37
|
-
allowance = int(quota.get("allowance", 0))
|
|
38
|
-
assert used < allowance, "No quota remaining"
|
|
39
|
-
|
|
40
|
-
def prepare_api_request(self, url, kwargs):
|
|
41
|
-
api_id, api_secret = self.api_key.split(":", 1)
|
|
42
|
-
kwargs["auth"] = (api_id, api_secret)
|
|
43
|
-
return url, kwargs
|
|
44
|
-
|
|
45
|
-
async def query(self, query):
|
|
46
|
-
results = set()
|
|
47
|
-
cursor = ""
|
|
48
|
-
for i in range(self.max_pages):
|
|
49
|
-
url = f"{self.base_url}/v2/certificates/search"
|
|
50
|
-
json_data = {
|
|
51
|
-
"q": f"names: {query}",
|
|
52
|
-
"per_page": 100,
|
|
53
|
-
}
|
|
54
|
-
if cursor:
|
|
55
|
-
json_data.update({"cursor": cursor})
|
|
56
|
-
resp = await self.api_request(
|
|
57
|
-
url,
|
|
58
|
-
method="POST",
|
|
59
|
-
json=json_data,
|
|
60
|
-
)
|
|
61
|
-
|
|
62
|
-
if resp is None:
|
|
63
|
-
break
|
|
64
|
-
|
|
65
|
-
try:
|
|
66
|
-
d = resp.json()
|
|
67
|
-
except Exception as e:
|
|
68
|
-
self.warning(f"Failed to parse JSON from {url} (response: {resp}): {e}")
|
|
69
|
-
|
|
70
|
-
if resp.status_code < 200 or resp.status_code >= 400:
|
|
71
|
-
if isinstance(d, dict):
|
|
72
|
-
error = d.get("error", "")
|
|
73
|
-
if error:
|
|
74
|
-
self.warning(error)
|
|
75
|
-
self.verbose(f'Non-200 Status code: {resp.status_code} for query "{query}", page #{i + 1}')
|
|
76
|
-
self.debug(f"Response: {resp.text}")
|
|
77
|
-
break
|
|
78
|
-
else:
|
|
79
|
-
if d is None:
|
|
80
|
-
break
|
|
81
|
-
elif not isinstance(d, dict):
|
|
82
|
-
break
|
|
83
|
-
status = d.get("status", "").lower()
|
|
84
|
-
result = d.get("result", {})
|
|
85
|
-
hits = result.get("hits", [])
|
|
86
|
-
if status != "ok" or not hits:
|
|
87
|
-
break
|
|
88
|
-
|
|
89
|
-
for h in hits:
|
|
90
|
-
names = h.get("names", [])
|
|
91
|
-
for n in names:
|
|
92
|
-
results.add(n.strip(".*").lower())
|
|
93
|
-
|
|
94
|
-
cursor = result.get("links", {}).get("next", "")
|
|
95
|
-
if not cursor:
|
|
96
|
-
break
|
|
97
|
-
|
|
98
|
-
return results
|
bbot/modules/gitlab.py
DELETED
|
@@ -1,141 +0,0 @@
|
|
|
1
|
-
from bbot.modules.base import BaseModule
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
class gitlab(BaseModule):
|
|
5
|
-
watched_events = ["HTTP_RESPONSE", "TECHNOLOGY", "SOCIAL"]
|
|
6
|
-
produced_events = ["TECHNOLOGY", "SOCIAL", "CODE_REPOSITORY", "FINDING"]
|
|
7
|
-
flags = ["active", "safe", "code-enum"]
|
|
8
|
-
meta = {
|
|
9
|
-
"description": "Detect GitLab instances and query them for repositories",
|
|
10
|
-
"created_date": "2024-03-11",
|
|
11
|
-
"author": "@TheTechromancer",
|
|
12
|
-
}
|
|
13
|
-
options = {"api_key": ""}
|
|
14
|
-
options_desc = {"api_key": "Gitlab access token"}
|
|
15
|
-
|
|
16
|
-
scope_distance_modifier = 2
|
|
17
|
-
|
|
18
|
-
async def setup(self):
|
|
19
|
-
await self.require_api_key()
|
|
20
|
-
return True
|
|
21
|
-
|
|
22
|
-
async def filter_event(self, event):
|
|
23
|
-
# only accept out-of-scope SOCIAL events
|
|
24
|
-
if event.type == "HTTP_RESPONSE":
|
|
25
|
-
if event.scope_distance > self.scan.scope_search_distance:
|
|
26
|
-
return False, "event is out of scope distance"
|
|
27
|
-
elif event.type == "TECHNOLOGY":
|
|
28
|
-
if not event.data["technology"].lower().startswith("gitlab"):
|
|
29
|
-
return False, "technology is not gitlab"
|
|
30
|
-
if not self.helpers.is_ip(event.host) and self.helpers.tldextract(event.host).domain == "gitlab":
|
|
31
|
-
return False, "gitlab instance is not self-hosted"
|
|
32
|
-
elif event.type == "SOCIAL":
|
|
33
|
-
if event.data["platform"] != "gitlab":
|
|
34
|
-
return False, "platform is not gitlab"
|
|
35
|
-
return True
|
|
36
|
-
|
|
37
|
-
async def handle_event(self, event):
|
|
38
|
-
if event.type == "HTTP_RESPONSE":
|
|
39
|
-
await self.handle_http_response(event)
|
|
40
|
-
elif event.type == "TECHNOLOGY":
|
|
41
|
-
await self.handle_technology(event)
|
|
42
|
-
elif event.type == "SOCIAL":
|
|
43
|
-
await self.handle_social(event)
|
|
44
|
-
|
|
45
|
-
async def handle_http_response(self, event):
|
|
46
|
-
# identify gitlab instances from HTTP responses
|
|
47
|
-
# HTTP_RESPONSE --> TECHNOLOGY
|
|
48
|
-
# HTTP_RESPONSE --> FINDING
|
|
49
|
-
headers = event.data.get("header", {})
|
|
50
|
-
if "x_gitlab_meta" in headers:
|
|
51
|
-
url = event.parsed_url._replace(path="/").geturl()
|
|
52
|
-
await self.emit_event(
|
|
53
|
-
{"host": str(event.host), "technology": "GitLab", "url": url},
|
|
54
|
-
"TECHNOLOGY",
|
|
55
|
-
parent=event,
|
|
56
|
-
context=f"{{module}} detected {{event.type}}: GitLab at {url}",
|
|
57
|
-
)
|
|
58
|
-
description = f"GitLab server at {event.host}"
|
|
59
|
-
await self.emit_event(
|
|
60
|
-
{"host": str(event.host), "description": description},
|
|
61
|
-
"FINDING",
|
|
62
|
-
parent=event,
|
|
63
|
-
context=f"{{module}} detected {{event.type}}: {description}",
|
|
64
|
-
)
|
|
65
|
-
|
|
66
|
-
async def handle_technology(self, event):
|
|
67
|
-
# retrieve gitlab groups from gitlab instances
|
|
68
|
-
# TECHNOLOGY --> SOCIAL
|
|
69
|
-
# TECHNOLOGY --> URL
|
|
70
|
-
# TECHNOLOGY --> CODE_REPOSITORY
|
|
71
|
-
base_url = self.get_base_url(event)
|
|
72
|
-
projects_url = self.helpers.urljoin(base_url, "api/v4/projects?simple=true")
|
|
73
|
-
await self.handle_projects_url(projects_url, event)
|
|
74
|
-
groups_url = self.helpers.urljoin(base_url, "api/v4/groups?simple=true")
|
|
75
|
-
await self.handle_groups_url(groups_url, event)
|
|
76
|
-
|
|
77
|
-
async def handle_social(self, event):
|
|
78
|
-
# retrieve repositories from gitlab user
|
|
79
|
-
# SOCIAL --> CODE_REPOSITORY
|
|
80
|
-
# SOCIAL --> SOCIAL
|
|
81
|
-
username = event.data.get("profile_name", "")
|
|
82
|
-
if not username:
|
|
83
|
-
return
|
|
84
|
-
base_url = self.get_base_url(event)
|
|
85
|
-
urls = [
|
|
86
|
-
# group
|
|
87
|
-
self.helpers.urljoin(base_url, f"api/v4/users/{username}/projects?simple=true"),
|
|
88
|
-
# user
|
|
89
|
-
self.helpers.urljoin(base_url, f"api/v4/groups/{username}/projects?simple=true"),
|
|
90
|
-
]
|
|
91
|
-
for url in urls:
|
|
92
|
-
await self.handle_projects_url(url, event)
|
|
93
|
-
|
|
94
|
-
async def handle_projects_url(self, projects_url, event):
|
|
95
|
-
for project in await self.gitlab_json_request(projects_url):
|
|
96
|
-
project_url = project.get("web_url", "")
|
|
97
|
-
if project_url:
|
|
98
|
-
code_event = self.make_event({"url": project_url}, "CODE_REPOSITORY", tags="git", parent=event)
|
|
99
|
-
await self.emit_event(
|
|
100
|
-
code_event, context=f"{{module}} enumerated projects and found {{event.type}} at {project_url}"
|
|
101
|
-
)
|
|
102
|
-
namespace = project.get("namespace", {})
|
|
103
|
-
if namespace:
|
|
104
|
-
await self.handle_namespace(namespace, event)
|
|
105
|
-
|
|
106
|
-
async def handle_groups_url(self, groups_url, event):
|
|
107
|
-
for group in await self.gitlab_json_request(groups_url):
|
|
108
|
-
await self.handle_namespace(group, event)
|
|
109
|
-
|
|
110
|
-
async def gitlab_json_request(self, url):
|
|
111
|
-
response = await self.api_request(url)
|
|
112
|
-
if response is not None:
|
|
113
|
-
try:
|
|
114
|
-
json = response.json()
|
|
115
|
-
except Exception:
|
|
116
|
-
return []
|
|
117
|
-
if json and isinstance(json, list):
|
|
118
|
-
return json
|
|
119
|
-
return []
|
|
120
|
-
|
|
121
|
-
async def handle_namespace(self, namespace, event):
|
|
122
|
-
namespace_name = namespace.get("path", "")
|
|
123
|
-
namespace_url = namespace.get("web_url", "")
|
|
124
|
-
namespace_path = namespace.get("full_path", "")
|
|
125
|
-
if namespace_name and namespace_url and namespace_path:
|
|
126
|
-
namespace_url = self.helpers.parse_url(namespace_url)._replace(path=f"/{namespace_path}").geturl()
|
|
127
|
-
social_event = self.make_event(
|
|
128
|
-
{"platform": "gitlab", "profile_name": namespace_path, "url": namespace_url},
|
|
129
|
-
"SOCIAL",
|
|
130
|
-
parent=event,
|
|
131
|
-
)
|
|
132
|
-
await self.emit_event(
|
|
133
|
-
social_event,
|
|
134
|
-
context=f'{{module}} found GitLab namespace ({{event.type}}) "{namespace_name}" at {namespace_url}',
|
|
135
|
-
)
|
|
136
|
-
|
|
137
|
-
def get_base_url(self, event):
|
|
138
|
-
base_url = event.data.get("url", "")
|
|
139
|
-
if not base_url:
|
|
140
|
-
base_url = f"https://{event.host}"
|
|
141
|
-
return self.helpers.urlparse(base_url)._replace(path="/").geturl()
|
bbot/modules/zoomeye.py
DELETED
|
@@ -1,77 +0,0 @@
|
|
|
1
|
-
from bbot.modules.templates.subdomain_enum import subdomain_enum_apikey
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
class zoomeye(subdomain_enum_apikey):
|
|
5
|
-
watched_events = ["DNS_NAME"]
|
|
6
|
-
produced_events = ["DNS_NAME"]
|
|
7
|
-
flags = ["affiliates", "subdomain-enum", "passive", "safe"]
|
|
8
|
-
meta = {
|
|
9
|
-
"description": "Query ZoomEye's API for subdomains",
|
|
10
|
-
"created_date": "2022-08-03",
|
|
11
|
-
"author": "@TheTechromancer",
|
|
12
|
-
"auth_required": True,
|
|
13
|
-
}
|
|
14
|
-
options = {"api_key": "", "max_pages": 20, "include_related": False}
|
|
15
|
-
options_desc = {
|
|
16
|
-
"api_key": "ZoomEye API key",
|
|
17
|
-
"max_pages": "How many pages of results to fetch",
|
|
18
|
-
"include_related": "Include domains which may be related to the target",
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
base_url = "https://api.zoomeye.hk"
|
|
22
|
-
|
|
23
|
-
async def setup(self):
|
|
24
|
-
self.max_pages = self.config.get("max_pages", 20)
|
|
25
|
-
self.include_related = self.config.get("include_related", False)
|
|
26
|
-
return await super().setup()
|
|
27
|
-
|
|
28
|
-
def prepare_api_request(self, url, kwargs):
|
|
29
|
-
kwargs["headers"]["API-KEY"] = self.api_key
|
|
30
|
-
return url, kwargs
|
|
31
|
-
|
|
32
|
-
async def ping(self):
|
|
33
|
-
url = f"{self.base_url}/resources-info"
|
|
34
|
-
r = await self.api_request(url, retry_on_http_429=False)
|
|
35
|
-
assert int(r.json()["quota_info"]["remain_total_quota"]) > 0, "No quota remaining"
|
|
36
|
-
|
|
37
|
-
async def handle_event(self, event):
|
|
38
|
-
query = self.make_query(event)
|
|
39
|
-
results = await self.query(query)
|
|
40
|
-
if results:
|
|
41
|
-
for hostname in results:
|
|
42
|
-
if hostname == event:
|
|
43
|
-
continue
|
|
44
|
-
tags = []
|
|
45
|
-
if not hostname.endswith(f".{query}"):
|
|
46
|
-
tags = ["affiliate"]
|
|
47
|
-
await self.emit_event(
|
|
48
|
-
hostname,
|
|
49
|
-
"DNS_NAME",
|
|
50
|
-
event,
|
|
51
|
-
tags=tags,
|
|
52
|
-
context=f'{{module}} searched ZoomEye API for "{query}" and found {{event.type}}: {{event.data}}',
|
|
53
|
-
)
|
|
54
|
-
|
|
55
|
-
async def query(self, query):
|
|
56
|
-
results = set()
|
|
57
|
-
query_type = 0 if self.include_related else 1
|
|
58
|
-
url = f"{self.base_url}/domain/search?q={self.helpers.quote(query)}&type={query_type}&page=" + "{page}"
|
|
59
|
-
i = 0
|
|
60
|
-
agen = self.api_page_iter(url)
|
|
61
|
-
try:
|
|
62
|
-
async for j in agen:
|
|
63
|
-
r = list(await self.parse_results(j))
|
|
64
|
-
if r:
|
|
65
|
-
results.update(set(r))
|
|
66
|
-
if not r or i >= (self.max_pages - 1):
|
|
67
|
-
break
|
|
68
|
-
i += 1
|
|
69
|
-
finally:
|
|
70
|
-
await agen.aclose()
|
|
71
|
-
return results
|
|
72
|
-
|
|
73
|
-
async def parse_results(self, r):
|
|
74
|
-
results = set()
|
|
75
|
-
for entry in r.get("list", []):
|
|
76
|
-
results.add(entry["name"])
|
|
77
|
-
return results
|
|
@@ -1,83 +0,0 @@
|
|
|
1
|
-
from .base import ModuleTestBase
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
class TestCensys(ModuleTestBase):
|
|
5
|
-
config_overrides = {"modules": {"censys": {"api_key": "api_id:api_secret"}}}
|
|
6
|
-
|
|
7
|
-
async def setup_before_prep(self, module_test):
|
|
8
|
-
module_test.httpx_mock.add_response(
|
|
9
|
-
url="https://search.censys.io/api/v1/account",
|
|
10
|
-
match_headers={"Authorization": "Basic YXBpX2lkOmFwaV9zZWNyZXQ="},
|
|
11
|
-
json={
|
|
12
|
-
"email": "info@blacklanternsecurity.com",
|
|
13
|
-
"login": "nope",
|
|
14
|
-
"first_login": "1917-08-03 20:03:55",
|
|
15
|
-
"last_login": "1918-05-19 01:15:22",
|
|
16
|
-
"quota": {"used": 26, "allowance": 250, "resets_at": "1919-06-03 16:30:32"},
|
|
17
|
-
},
|
|
18
|
-
)
|
|
19
|
-
module_test.httpx_mock.add_response(
|
|
20
|
-
url="https://search.censys.io/api/v2/certificates/search",
|
|
21
|
-
match_headers={"Authorization": "Basic YXBpX2lkOmFwaV9zZWNyZXQ="},
|
|
22
|
-
method="POST",
|
|
23
|
-
match_json={"q": "names: blacklanternsecurity.com", "per_page": 100},
|
|
24
|
-
json={
|
|
25
|
-
"code": 200,
|
|
26
|
-
"status": "OK",
|
|
27
|
-
"result": {
|
|
28
|
-
"query": "names: blacklanternsecurity.com",
|
|
29
|
-
"total": 196,
|
|
30
|
-
"duration_ms": 1046,
|
|
31
|
-
"hits": [
|
|
32
|
-
{
|
|
33
|
-
"parsed": {
|
|
34
|
-
"validity_period": {
|
|
35
|
-
"not_before": "2021-11-18T00:09:46Z",
|
|
36
|
-
"not_after": "2022-11-18T00:09:46Z",
|
|
37
|
-
},
|
|
38
|
-
"issuer_dn": "C=US, ST=Arizona, L=Scottsdale, O=GoDaddy.com\\, Inc., OU=http://certs.godaddy.com/repository/, CN=Go Daddy Secure Certificate Authority - G2",
|
|
39
|
-
"subject_dn": "CN=asdf.blacklanternsecurity.com",
|
|
40
|
-
},
|
|
41
|
-
"fingerprint_sha256": "590ad51b8db62925f0fd3f300264c6a36692e20ceec2b5a22e7e4b41c1575cdc",
|
|
42
|
-
"names": ["asdf.blacklanternsecurity.com", "asdf2.blacklanternsecurity.com"],
|
|
43
|
-
},
|
|
44
|
-
],
|
|
45
|
-
"links": {"next": "NextToken", "prev": ""},
|
|
46
|
-
},
|
|
47
|
-
},
|
|
48
|
-
)
|
|
49
|
-
module_test.httpx_mock.add_response(
|
|
50
|
-
url="https://search.censys.io/api/v2/certificates/search",
|
|
51
|
-
match_headers={"Authorization": "Basic YXBpX2lkOmFwaV9zZWNyZXQ="},
|
|
52
|
-
method="POST",
|
|
53
|
-
match_json={"q": "names: blacklanternsecurity.com", "per_page": 100, "cursor": "NextToken"},
|
|
54
|
-
json={
|
|
55
|
-
"code": 200,
|
|
56
|
-
"status": "OK",
|
|
57
|
-
"result": {
|
|
58
|
-
"query": "names: blacklanternsecurity.com",
|
|
59
|
-
"total": 196,
|
|
60
|
-
"duration_ms": 1046,
|
|
61
|
-
"hits": [
|
|
62
|
-
{
|
|
63
|
-
"parsed": {
|
|
64
|
-
"validity_period": {
|
|
65
|
-
"not_before": "2021-11-18T00:09:46Z",
|
|
66
|
-
"not_after": "2022-11-18T00:09:46Z",
|
|
67
|
-
},
|
|
68
|
-
"issuer_dn": "C=US, ST=Arizona, L=Scottsdale, O=GoDaddy.com\\, Inc., OU=http://certs.godaddy.com/repository/, CN=Go Daddy Secure Certificate Authority - G2",
|
|
69
|
-
"subject_dn": "CN=zzzz.blacklanternsecurity.com",
|
|
70
|
-
},
|
|
71
|
-
"fingerprint_sha256": "590ad51b8db62925f0fd3f300264c6a36692e20ceec2b5a22e7e4b41c1575cdc",
|
|
72
|
-
"names": ["zzzz.blacklanternsecurity.com"],
|
|
73
|
-
},
|
|
74
|
-
],
|
|
75
|
-
"links": {"next": "", "prev": ""},
|
|
76
|
-
},
|
|
77
|
-
},
|
|
78
|
-
)
|
|
79
|
-
|
|
80
|
-
def check(self, module_test, events):
|
|
81
|
-
assert any(e.data == "asdf.blacklanternsecurity.com" for e in events), "Failed to detect asdf subdomain"
|
|
82
|
-
assert any(e.data == "asdf2.blacklanternsecurity.com" for e in events), "Failed to detect asdf2 subdomain"
|
|
83
|
-
assert any(e.data == "zzzz.blacklanternsecurity.com" for e in events), "Failed to detect zzzz subdomain"
|
|
@@ -1,35 +0,0 @@
|
|
|
1
|
-
from .base import ModuleTestBase
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
class TestZoomEye(ModuleTestBase):
|
|
5
|
-
config_overrides = {"modules": {"zoomeye": {"api_key": "asdf", "include_related": True, "max_pages": 3}}}
|
|
6
|
-
|
|
7
|
-
async def setup_before_prep(self, module_test):
|
|
8
|
-
module_test.httpx_mock.add_response(
|
|
9
|
-
url="https://api.zoomeye.hk/resources-info",
|
|
10
|
-
match_headers={"API-KEY": "asdf"},
|
|
11
|
-
json={"quota_info": {"remain_total_quota": 5}},
|
|
12
|
-
)
|
|
13
|
-
module_test.httpx_mock.add_response(
|
|
14
|
-
url="https://api.zoomeye.hk/domain/search?q=blacklanternsecurity.com&type=0&page=1",
|
|
15
|
-
json={"list": [{"name": "asdf.blacklanternsecurity.com"}]},
|
|
16
|
-
)
|
|
17
|
-
module_test.httpx_mock.add_response(
|
|
18
|
-
url="https://api.zoomeye.hk/domain/search?q=blacklanternsecurity.com&type=0&page=2",
|
|
19
|
-
json={"list": [{"name": "zzzz.blacklanternsecurity.com"}]},
|
|
20
|
-
)
|
|
21
|
-
module_test.httpx_mock.add_response(
|
|
22
|
-
url="https://api.zoomeye.hk/domain/search?q=blacklanternsecurity.com&type=0&page=3",
|
|
23
|
-
json={"list": [{"name": "ffff.blacklanternsecurity.com"}, {"name": "affiliate.bls"}]},
|
|
24
|
-
)
|
|
25
|
-
module_test.httpx_mock.add_response(
|
|
26
|
-
url="https://api.zoomeye.hk/domain/search?q=blacklanternsecurity.com&type=0&page=4",
|
|
27
|
-
json={"list": [{"name": "nope.blacklanternsecurity.com"}]},
|
|
28
|
-
)
|
|
29
|
-
|
|
30
|
-
def check(self, module_test, events):
|
|
31
|
-
assert any(e.data == "asdf.blacklanternsecurity.com" for e in events), "Failed to detect subdomain #1"
|
|
32
|
-
assert any(e.data == "zzzz.blacklanternsecurity.com" for e in events), "Failed to detect subdomain #2"
|
|
33
|
-
assert any(e.data == "ffff.blacklanternsecurity.com" for e in events), "Failed to detect subdomain #3"
|
|
34
|
-
assert any(e.data == "affiliate.bls" and "affiliate" in e.tags for e in events), "Failed to detect affiliate"
|
|
35
|
-
assert not any(e.data == "nope.blacklanternsecurity.com" for e in events), "Failed to obey max_pages"
|
|
File without changes
|
|
File without changes
|