bbot 2.2.0.5242rc0__py3-none-any.whl → 2.2.0.5263rc0__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/config/logger.py +6 -3
- bbot/core/core.py +20 -3
- bbot/core/engine.py +3 -3
- bbot/core/event/base.py +16 -12
- bbot/core/helpers/command.py +4 -3
- bbot/core/helpers/depsinstaller/installer.py +5 -0
- bbot/core/helpers/process.py +0 -18
- bbot/core/multiprocess.py +58 -0
- bbot/modules/censys.py +9 -13
- bbot/modules/internal/dnsresolve.py +12 -1
- bbot/modules/passivetotal.py +9 -11
- bbot/scanner/scanner.py +4 -1
- bbot/test/conftest.py +0 -1
- bbot/test/fastapi_test.py +17 -0
- bbot/test/test_step_1/test_bbot_fastapi.py +82 -0
- bbot/test/test_step_1/test_dns.py +36 -0
- bbot/test/test_step_1/test_events.py +32 -0
- bbot/test/test_step_2/module_tests/test_module_censys.py +4 -1
- bbot/test/test_step_2/module_tests/test_module_passivetotal.py +3 -1
- {bbot-2.2.0.5242rc0.dist-info → bbot-2.2.0.5263rc0.dist-info}/METADATA +1 -1
- {bbot-2.2.0.5242rc0.dist-info → bbot-2.2.0.5263rc0.dist-info}/RECORD +25 -22
- {bbot-2.2.0.5242rc0.dist-info → bbot-2.2.0.5263rc0.dist-info}/LICENSE +0 -0
- {bbot-2.2.0.5242rc0.dist-info → bbot-2.2.0.5263rc0.dist-info}/WHEEL +0 -0
- {bbot-2.2.0.5242rc0.dist-info → bbot-2.2.0.5263rc0.dist-info}/entry_points.txt +0 -0
bbot/__init__.py
CHANGED
bbot/core/config/logger.py
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import os
|
|
1
2
|
import sys
|
|
2
3
|
import atexit
|
|
3
4
|
import logging
|
|
@@ -9,6 +10,7 @@ from contextlib import suppress
|
|
|
9
10
|
|
|
10
11
|
from ..helpers.misc import mkdir, error_and_exit
|
|
11
12
|
from ...logger import colorize, loglevel_mapping
|
|
13
|
+
from ..multiprocess import SHARED_INTERPRETER_STATE
|
|
12
14
|
|
|
13
15
|
|
|
14
16
|
debug_format = logging.Formatter("%(asctime)s [%(levelname)s] %(name)s %(filename)s:%(lineno)s %(message)s")
|
|
@@ -65,8 +67,9 @@ class BBOTLogger:
|
|
|
65
67
|
|
|
66
68
|
self.listener = None
|
|
67
69
|
|
|
68
|
-
|
|
69
|
-
if
|
|
70
|
+
# if we haven't set up logging yet, do it now
|
|
71
|
+
if not "_BBOT_LOGGING_SETUP" in os.environ:
|
|
72
|
+
os.environ["_BBOT_LOGGING_SETUP"] = "1"
|
|
70
73
|
self.queue = multiprocessing.Queue()
|
|
71
74
|
self.setup_queue_handler()
|
|
72
75
|
# Start the QueueListener
|
|
@@ -113,7 +116,7 @@ class BBOTLogger:
|
|
|
113
116
|
|
|
114
117
|
self.core_logger.setLevel(log_level)
|
|
115
118
|
# disable asyncio logging for child processes
|
|
116
|
-
if
|
|
119
|
+
if not SHARED_INTERPRETER_STATE.is_main_process:
|
|
117
120
|
logging.getLogger("asyncio").setLevel(logging.ERROR)
|
|
118
121
|
|
|
119
122
|
def addLoggingLevel(self, levelName, levelNum, methodName=None):
|
bbot/core/core.py
CHANGED
|
@@ -6,6 +6,7 @@ from contextlib import suppress
|
|
|
6
6
|
from omegaconf import OmegaConf
|
|
7
7
|
|
|
8
8
|
from bbot.errors import BBOTError
|
|
9
|
+
from .multiprocess import SHARED_INTERPRETER_STATE
|
|
9
10
|
|
|
10
11
|
|
|
11
12
|
DEFAULT_CONFIG = None
|
|
@@ -41,9 +42,23 @@ class BBOTCore:
|
|
|
41
42
|
self.logger
|
|
42
43
|
self.log = logging.getLogger("bbot.core")
|
|
43
44
|
|
|
45
|
+
self._prep_multiprocessing()
|
|
46
|
+
|
|
47
|
+
def _prep_multiprocessing(self):
|
|
44
48
|
import multiprocessing
|
|
49
|
+
from .helpers.process import BBOTProcess
|
|
50
|
+
|
|
51
|
+
if SHARED_INTERPRETER_STATE.is_main_process:
|
|
52
|
+
# if this is the main bbot process, set the logger and queue for the first time
|
|
53
|
+
from functools import partialmethod
|
|
45
54
|
|
|
46
|
-
|
|
55
|
+
BBOTProcess.__init__ = partialmethod(
|
|
56
|
+
BBOTProcess.__init__, log_level=self.logger.log_level, log_queue=self.logger.queue
|
|
57
|
+
)
|
|
58
|
+
|
|
59
|
+
# this makes our process class the default for process pools, etc.
|
|
60
|
+
mp_context = multiprocessing.get_context("spawn")
|
|
61
|
+
mp_context.Process = BBOTProcess
|
|
47
62
|
|
|
48
63
|
@property
|
|
49
64
|
def home(self):
|
|
@@ -187,12 +202,14 @@ class BBOTCore:
|
|
|
187
202
|
if os.environ.get("BBOT_TESTING", "") == "True":
|
|
188
203
|
process = self.create_thread(*args, **kwargs)
|
|
189
204
|
else:
|
|
190
|
-
if
|
|
205
|
+
if SHARED_INTERPRETER_STATE.is_scan_process:
|
|
191
206
|
from .helpers.process import BBOTProcess
|
|
192
207
|
|
|
193
208
|
process = BBOTProcess(*args, **kwargs)
|
|
194
209
|
else:
|
|
195
|
-
|
|
210
|
+
import multiprocessing
|
|
211
|
+
|
|
212
|
+
raise BBOTError(f"Tried to start server from process {multiprocessing.current_process().name}")
|
|
196
213
|
process.daemon = True
|
|
197
214
|
return process
|
|
198
215
|
|
bbot/core/engine.py
CHANGED
|
@@ -10,6 +10,7 @@ import traceback
|
|
|
10
10
|
import contextlib
|
|
11
11
|
import contextvars
|
|
12
12
|
import zmq.asyncio
|
|
13
|
+
import multiprocessing
|
|
13
14
|
from pathlib import Path
|
|
14
15
|
from concurrent.futures import CancelledError
|
|
15
16
|
from contextlib import asynccontextmanager, suppress
|
|
@@ -17,6 +18,7 @@ from contextlib import asynccontextmanager, suppress
|
|
|
17
18
|
from bbot.core import CORE
|
|
18
19
|
from bbot.errors import BBOTEngineError
|
|
19
20
|
from bbot.core.helpers.async_helpers import get_event_loop
|
|
21
|
+
from bbot.core.multiprocess import SHARED_INTERPRETER_STATE
|
|
20
22
|
from bbot.core.helpers.misc import rand_string, in_exception_chain
|
|
21
23
|
|
|
22
24
|
|
|
@@ -264,10 +266,8 @@ class EngineClient(EngineBase):
|
|
|
264
266
|
return [s for s in self.CMDS if isinstance(s, str)]
|
|
265
267
|
|
|
266
268
|
def start_server(self):
|
|
267
|
-
import multiprocessing
|
|
268
|
-
|
|
269
269
|
process_name = multiprocessing.current_process().name
|
|
270
|
-
if
|
|
270
|
+
if SHARED_INTERPRETER_STATE.is_scan_process:
|
|
271
271
|
kwargs = dict(self.server_kwargs)
|
|
272
272
|
# if we're in tests, we use a single event loop to avoid weird race conditions
|
|
273
273
|
# this allows us to more easily mock http, etc.
|
bbot/core/event/base.py
CHANGED
|
@@ -1003,13 +1003,15 @@ class ClosestHostEvent(DictHostEvent):
|
|
|
1003
1003
|
if parent_url is not None:
|
|
1004
1004
|
self.data["url"] = parent_url.geturl()
|
|
1005
1005
|
# inherit closest path
|
|
1006
|
-
if not "path" in self.data and isinstance(parent.data, dict):
|
|
1006
|
+
if not "path" in self.data and isinstance(parent.data, dict) and not parent.type == "HTTP_RESPONSE":
|
|
1007
1007
|
parent_path = parent.data.get("path", None)
|
|
1008
1008
|
if parent_path is not None:
|
|
1009
1009
|
self.data["path"] = parent_path
|
|
1010
1010
|
# inherit closest host
|
|
1011
1011
|
if parent.host:
|
|
1012
1012
|
self.data["host"] = str(parent.host)
|
|
1013
|
+
# we do this to refresh the hash
|
|
1014
|
+
self.data = self.data
|
|
1013
1015
|
break
|
|
1014
1016
|
# die if we still haven't found a host
|
|
1015
1017
|
if not self.host:
|
|
@@ -1559,6 +1561,8 @@ class FILESYSTEM(DictPathEvent):
|
|
|
1559
1561
|
self.add_tag("compressed")
|
|
1560
1562
|
self.add_tag(f"{compression}-archive")
|
|
1561
1563
|
self.data["compression"] = compression
|
|
1564
|
+
# refresh hash
|
|
1565
|
+
self.data = self.data
|
|
1562
1566
|
|
|
1563
1567
|
|
|
1564
1568
|
class RAW_DNS_RECORD(DictHostEvent, DnsEvent):
|
|
@@ -1639,23 +1643,23 @@ def make_event(
|
|
|
1639
1643
|
tags = set(tags)
|
|
1640
1644
|
|
|
1641
1645
|
if is_event(data):
|
|
1642
|
-
|
|
1643
|
-
if scan is not None and not
|
|
1644
|
-
|
|
1645
|
-
if scans is not None and not
|
|
1646
|
-
|
|
1646
|
+
event = copy(data)
|
|
1647
|
+
if scan is not None and not event.scan:
|
|
1648
|
+
event.scan = scan
|
|
1649
|
+
if scans is not None and not event.scans:
|
|
1650
|
+
event.scans = scans
|
|
1647
1651
|
if module is not None:
|
|
1648
|
-
|
|
1652
|
+
event.module = module
|
|
1649
1653
|
if parent is not None:
|
|
1650
|
-
|
|
1654
|
+
event.parent = parent
|
|
1651
1655
|
if context is not None:
|
|
1652
|
-
|
|
1656
|
+
event.discovery_context = context
|
|
1653
1657
|
if internal == True:
|
|
1654
|
-
|
|
1658
|
+
event.internal = True
|
|
1655
1659
|
if tags:
|
|
1656
|
-
|
|
1660
|
+
event.tags = tags.union(event.tags)
|
|
1657
1661
|
event_type = data.type
|
|
1658
|
-
return
|
|
1662
|
+
return event
|
|
1659
1663
|
else:
|
|
1660
1664
|
if event_type is None:
|
|
1661
1665
|
event_type, data = get_event_type(data)
|
bbot/core/helpers/command.py
CHANGED
|
@@ -210,9 +210,10 @@ async def _write_proc_line(proc, chunk):
|
|
|
210
210
|
return True
|
|
211
211
|
except Exception as e:
|
|
212
212
|
proc_args = [str(s) for s in getattr(proc, "args", [])]
|
|
213
|
-
command = " ".join(proc_args)
|
|
214
|
-
|
|
215
|
-
|
|
213
|
+
command = " ".join(proc_args).strip()
|
|
214
|
+
if command:
|
|
215
|
+
log.warning(f"Error writing line to stdin for command: {command}: {e}")
|
|
216
|
+
log.trace(traceback.format_exc())
|
|
216
217
|
return False
|
|
217
218
|
|
|
218
219
|
|
|
@@ -44,6 +44,11 @@ class DepsInstaller:
|
|
|
44
44
|
self.parent_helper.mkdir(self.command_status)
|
|
45
45
|
self.setup_status = self.read_setup_status()
|
|
46
46
|
|
|
47
|
+
# make sure we're using a minimal git config
|
|
48
|
+
self.minimal_git_config = self.data_dir / "minimal_git.config"
|
|
49
|
+
self.minimal_git_config.touch()
|
|
50
|
+
os.environ["GIT_CONFIG_GLOBAL"] = str(self.minimal_git_config)
|
|
51
|
+
|
|
47
52
|
self.deps_behavior = self.parent_helper.config.get("deps_behavior", "abort_on_failure").lower()
|
|
48
53
|
self.ansible_debug = self.core.logger.log_level <= logging.DEBUG
|
|
49
54
|
self.venv = ""
|
bbot/core/helpers/process.py
CHANGED
|
@@ -1,15 +1,11 @@
|
|
|
1
1
|
import logging
|
|
2
2
|
import traceback
|
|
3
3
|
import threading
|
|
4
|
-
import multiprocessing
|
|
5
4
|
from multiprocessing.context import SpawnProcess
|
|
6
5
|
|
|
7
6
|
from .misc import in_exception_chain
|
|
8
7
|
|
|
9
8
|
|
|
10
|
-
current_process = multiprocessing.current_process()
|
|
11
|
-
|
|
12
|
-
|
|
13
9
|
class BBOTThread(threading.Thread):
|
|
14
10
|
|
|
15
11
|
default_name = "default bbot thread"
|
|
@@ -57,17 +53,3 @@ class BBOTProcess(SpawnProcess):
|
|
|
57
53
|
if not in_exception_chain(e, (KeyboardInterrupt,)):
|
|
58
54
|
log.warning(f"Error in {self.name}: {e}")
|
|
59
55
|
log.trace(traceback.format_exc())
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
if current_process.name == "MainProcess":
|
|
63
|
-
# if this is the main bbot process, set the logger and queue for the first time
|
|
64
|
-
from bbot.core import CORE
|
|
65
|
-
from functools import partialmethod
|
|
66
|
-
|
|
67
|
-
BBOTProcess.__init__ = partialmethod(
|
|
68
|
-
BBOTProcess.__init__, log_level=CORE.logger.log_level, log_queue=CORE.logger.queue
|
|
69
|
-
)
|
|
70
|
-
|
|
71
|
-
# this makes our process class the default for process pools, etc.
|
|
72
|
-
mp_context = multiprocessing.get_context("spawn")
|
|
73
|
-
mp_context.Process = BBOTProcess
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import os
|
|
2
|
+
import atexit
|
|
3
|
+
from contextlib import suppress
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class SharedInterpreterState:
|
|
7
|
+
"""
|
|
8
|
+
A class to track the primary BBOT process.
|
|
9
|
+
|
|
10
|
+
Used to prevent spawning multiple unwanted processes with multiprocessing.
|
|
11
|
+
"""
|
|
12
|
+
|
|
13
|
+
def __init__(self):
|
|
14
|
+
self.main_process_var_name = "_BBOT_MAIN_PID"
|
|
15
|
+
self.scan_process_var_name = "_BBOT_SCAN_PID"
|
|
16
|
+
atexit.register(self.cleanup)
|
|
17
|
+
|
|
18
|
+
@property
|
|
19
|
+
def is_main_process(self):
|
|
20
|
+
is_main_process = self.main_pid == os.getpid()
|
|
21
|
+
return is_main_process
|
|
22
|
+
|
|
23
|
+
@property
|
|
24
|
+
def is_scan_process(self):
|
|
25
|
+
is_scan_process = os.getpid() == self.scan_pid
|
|
26
|
+
return is_scan_process
|
|
27
|
+
|
|
28
|
+
@property
|
|
29
|
+
def main_pid(self):
|
|
30
|
+
main_pid = int(os.environ.get(self.main_process_var_name, 0))
|
|
31
|
+
if main_pid == 0:
|
|
32
|
+
main_pid = os.getpid()
|
|
33
|
+
# if main PID is not set, set it to the current PID
|
|
34
|
+
os.environ[self.main_process_var_name] = str(main_pid)
|
|
35
|
+
return main_pid
|
|
36
|
+
|
|
37
|
+
@property
|
|
38
|
+
def scan_pid(self):
|
|
39
|
+
scan_pid = int(os.environ.get(self.scan_process_var_name, 0))
|
|
40
|
+
if scan_pid == 0:
|
|
41
|
+
scan_pid = os.getpid()
|
|
42
|
+
# if scan PID is not set, set it to the current PID
|
|
43
|
+
os.environ[self.scan_process_var_name] = str(scan_pid)
|
|
44
|
+
return scan_pid
|
|
45
|
+
|
|
46
|
+
def update_scan_pid(self):
|
|
47
|
+
os.environ[self.scan_process_var_name] = str(os.getpid())
|
|
48
|
+
|
|
49
|
+
def cleanup(self):
|
|
50
|
+
with suppress(Exception):
|
|
51
|
+
if self.is_main_process:
|
|
52
|
+
with suppress(KeyError):
|
|
53
|
+
del os.environ[self.main_process_var_name]
|
|
54
|
+
with suppress(KeyError):
|
|
55
|
+
del os.environ[self.scan_process_var_name]
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
SHARED_INTERPRETER_STATE = SharedInterpreterState()
|
bbot/modules/censys.py
CHANGED
|
@@ -15,25 +15,21 @@ class censys(subdomain_enum_apikey):
|
|
|
15
15
|
"author": "@TheTechromancer",
|
|
16
16
|
"auth_required": True,
|
|
17
17
|
}
|
|
18
|
-
options = {"
|
|
18
|
+
options = {"api_key": "", "max_pages": 5}
|
|
19
19
|
options_desc = {
|
|
20
|
-
"
|
|
21
|
-
"api_secret": "Censys.io API Secret",
|
|
20
|
+
"api_key": "Censys.io API Key in the format of 'key:secret'",
|
|
22
21
|
"max_pages": "Maximum number of pages to fetch (100 results per page)",
|
|
23
22
|
}
|
|
24
23
|
|
|
25
24
|
base_url = "https://search.censys.io/api"
|
|
26
25
|
|
|
27
26
|
async def setup(self):
|
|
28
|
-
self.api_id = self.config.get("api_id", "")
|
|
29
|
-
self.api_secret = self.config.get("api_secret", "")
|
|
30
|
-
self.auth = (self.api_id, self.api_secret)
|
|
31
27
|
self.max_pages = self.config.get("max_pages", 5)
|
|
32
28
|
return await super().setup()
|
|
33
29
|
|
|
34
30
|
async def ping(self):
|
|
35
31
|
url = f"{self.base_url}/v1/account"
|
|
36
|
-
resp = await self.
|
|
32
|
+
resp = await self.api_request(url)
|
|
37
33
|
d = resp.json()
|
|
38
34
|
assert isinstance(d, dict), f"Invalid response from {url}: {resp}"
|
|
39
35
|
quota = d.get("quota", {})
|
|
@@ -41,6 +37,11 @@ class censys(subdomain_enum_apikey):
|
|
|
41
37
|
allowance = int(quota.get("allowance", 0))
|
|
42
38
|
assert used < allowance, "No quota remaining"
|
|
43
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
|
+
|
|
44
45
|
async def query(self, query):
|
|
45
46
|
results = set()
|
|
46
47
|
cursor = ""
|
|
@@ -52,11 +53,10 @@ class censys(subdomain_enum_apikey):
|
|
|
52
53
|
}
|
|
53
54
|
if cursor:
|
|
54
55
|
json_data.update({"cursor": cursor})
|
|
55
|
-
resp = await self.
|
|
56
|
+
resp = await self.api_request(
|
|
56
57
|
url,
|
|
57
58
|
method="POST",
|
|
58
59
|
json=json_data,
|
|
59
|
-
auth=self.auth,
|
|
60
60
|
)
|
|
61
61
|
|
|
62
62
|
if resp is None:
|
|
@@ -96,7 +96,3 @@ class censys(subdomain_enum_apikey):
|
|
|
96
96
|
break
|
|
97
97
|
|
|
98
98
|
return results
|
|
99
|
-
|
|
100
|
-
@property
|
|
101
|
-
def auth_secret(self):
|
|
102
|
-
return self.api_id and self.api_secret
|
|
@@ -79,7 +79,16 @@ class DNSResolve(BaseInterceptModule):
|
|
|
79
79
|
await self.resolve_event(main_host_event, types=non_minimal_rdtypes)
|
|
80
80
|
# check for wildcards if the event is within the scan's search distance
|
|
81
81
|
if new_event and main_host_event.scope_distance <= self.scan.scope_search_distance:
|
|
82
|
-
await self.handle_wildcard_event(main_host_event)
|
|
82
|
+
event_data_changed = await self.handle_wildcard_event(main_host_event)
|
|
83
|
+
if event_data_changed:
|
|
84
|
+
# since data has changed, we check again whether it's a duplicate
|
|
85
|
+
if self.scan.ingress_module.is_incoming_duplicate(event, add=True):
|
|
86
|
+
if not event._graph_important:
|
|
87
|
+
return False, "event was already emitted by its module"
|
|
88
|
+
else:
|
|
89
|
+
self.debug(
|
|
90
|
+
f"Event {event} was already emitted by its module, but it's graph-important so it gets a pass"
|
|
91
|
+
)
|
|
83
92
|
|
|
84
93
|
# if there weren't any DNS children and it's not an IP address, tag as unresolved
|
|
85
94
|
if not main_host_event.raw_dns_records and not event_is_ip:
|
|
@@ -152,6 +161,8 @@ class DNSResolve(BaseInterceptModule):
|
|
|
152
161
|
if wildcard_data != event.data:
|
|
153
162
|
self.debug(f'Wildcard detected, changing event.data "{event.data}" --> "{wildcard_data}"')
|
|
154
163
|
event.data = wildcard_data
|
|
164
|
+
return True
|
|
165
|
+
return False
|
|
155
166
|
|
|
156
167
|
async def emit_dns_children(self, event):
|
|
157
168
|
for rdtype, children in event.dns_children.items():
|
bbot/modules/passivetotal.py
CHANGED
|
@@ -11,36 +11,34 @@ class passivetotal(subdomain_enum_apikey):
|
|
|
11
11
|
"author": "@TheTechromancer",
|
|
12
12
|
"auth_required": True,
|
|
13
13
|
}
|
|
14
|
-
options = {"
|
|
15
|
-
options_desc = {"
|
|
14
|
+
options = {"api_key": ""}
|
|
15
|
+
options_desc = {"api_key": "PassiveTotal API Key in the format of 'username:api_key'"}
|
|
16
16
|
|
|
17
17
|
base_url = "https://api.passivetotal.org/v2"
|
|
18
18
|
|
|
19
19
|
async def setup(self):
|
|
20
|
-
self.username = self.config.get("username", "")
|
|
21
|
-
self.api_key = self.config.get("api_key", "")
|
|
22
|
-
self.auth = (self.username, self.api_key)
|
|
23
20
|
return await super().setup()
|
|
24
21
|
|
|
25
22
|
async def ping(self):
|
|
26
23
|
url = f"{self.base_url}/account/quota"
|
|
27
|
-
j = (await self.api_request(url
|
|
24
|
+
j = (await self.api_request(url)).json()
|
|
28
25
|
limit = j["user"]["limits"]["search_api"]
|
|
29
26
|
used = j["user"]["counts"]["search_api"]
|
|
30
27
|
assert used < limit, "No quota remaining"
|
|
31
28
|
|
|
29
|
+
def prepare_api_request(self, url, kwargs):
|
|
30
|
+
api_username, api_key = self.api_key.split(":", 1)
|
|
31
|
+
kwargs["auth"] = (api_username, api_key)
|
|
32
|
+
return url, kwargs
|
|
33
|
+
|
|
32
34
|
async def abort_if(self, event):
|
|
33
35
|
# RiskIQ is famous for their junk data
|
|
34
36
|
return await super().abort_if(event) or "unresolved" in event.tags
|
|
35
37
|
|
|
36
38
|
async def request_url(self, query):
|
|
37
39
|
url = f"{self.base_url}/enrichment/subdomains?query={self.helpers.quote(query)}"
|
|
38
|
-
return await self.api_request(url
|
|
40
|
+
return await self.api_request(url)
|
|
39
41
|
|
|
40
42
|
def parse_results(self, r, query):
|
|
41
43
|
for subdomain in r.json().get("subdomains", []):
|
|
42
44
|
yield f"{subdomain}.{query}"
|
|
43
|
-
|
|
44
|
-
@property
|
|
45
|
-
def auth_secret(self):
|
|
46
|
-
return self.username and self.api_key
|
bbot/scanner/scanner.py
CHANGED
|
@@ -10,11 +10,11 @@ from datetime import datetime
|
|
|
10
10
|
from collections import OrderedDict
|
|
11
11
|
|
|
12
12
|
from bbot import __version__
|
|
13
|
-
|
|
14
13
|
from bbot.core.event import make_event
|
|
15
14
|
from .manager import ScanIngress, ScanEgress
|
|
16
15
|
from bbot.core.helpers.misc import sha1, rand_string
|
|
17
16
|
from bbot.core.helpers.names_generator import random_name
|
|
17
|
+
from bbot.core.multiprocess import SHARED_INTERPRETER_STATE
|
|
18
18
|
from bbot.core.helpers.async_helpers import async_to_sync_gen
|
|
19
19
|
from bbot.errors import BBOTError, ScanError, ValidationError
|
|
20
20
|
|
|
@@ -259,6 +259,9 @@ class Scanner:
|
|
|
259
259
|
Creates the scan's output folder, loads its modules, and calls their .setup() methods.
|
|
260
260
|
"""
|
|
261
261
|
|
|
262
|
+
# update the master PID
|
|
263
|
+
SHARED_INTERPRETER_STATE.update_scan_pid()
|
|
264
|
+
|
|
262
265
|
self.helpers.mkdir(self.home)
|
|
263
266
|
if not self._prepped:
|
|
264
267
|
# save scan preset
|
bbot/test/conftest.py
CHANGED
|
@@ -16,7 +16,6 @@ from bbot.core.helpers.interactsh import server_list as interactsh_servers
|
|
|
16
16
|
# silence stdout + trace
|
|
17
17
|
root_logger = logging.getLogger()
|
|
18
18
|
pytest_debug_file = Path(__file__).parent.parent.parent / "pytest_debug.log"
|
|
19
|
-
print(f"pytest_debug_file: {pytest_debug_file}")
|
|
20
19
|
debug_handler = logging.FileHandler(pytest_debug_file)
|
|
21
20
|
debug_handler.setLevel(logging.DEBUG)
|
|
22
21
|
debug_format = logging.Formatter("%(asctime)s [%(levelname)s] %(name)s %(filename)s:%(lineno)s %(message)s")
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
from typing import List
|
|
2
|
+
from bbot import Scanner
|
|
3
|
+
from fastapi import FastAPI, Query
|
|
4
|
+
|
|
5
|
+
app = FastAPI()
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
@app.get("/start")
|
|
9
|
+
async def start(targets: List[str] = Query(...)):
|
|
10
|
+
scanner = Scanner(*targets, modules=["httpx"])
|
|
11
|
+
events = [e async for e in scanner.async_start()]
|
|
12
|
+
return [e.json() for e in events]
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
@app.get("/ping")
|
|
16
|
+
async def ping():
|
|
17
|
+
return {"status": "ok"}
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
import time
|
|
2
|
+
import httpx
|
|
3
|
+
import multiprocessing
|
|
4
|
+
from pathlib import Path
|
|
5
|
+
from subprocess import Popen
|
|
6
|
+
from contextlib import suppress
|
|
7
|
+
|
|
8
|
+
cwd = Path(__file__).parent.parent.parent
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
def run_bbot_multiprocess(queue):
|
|
12
|
+
from bbot import Scanner
|
|
13
|
+
|
|
14
|
+
scan = Scanner("http://127.0.0.1:8888", "blacklanternsecurity.com", modules=["httpx"])
|
|
15
|
+
events = [e.json() for e in scan.start()]
|
|
16
|
+
queue.put(events)
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
def test_bbot_multiprocess(bbot_httpserver):
|
|
20
|
+
|
|
21
|
+
bbot_httpserver.expect_request("/").respond_with_data("test@blacklanternsecurity.com")
|
|
22
|
+
|
|
23
|
+
queue = multiprocessing.Queue()
|
|
24
|
+
events_process = multiprocessing.Process(target=run_bbot_multiprocess, args=(queue,))
|
|
25
|
+
events_process.start()
|
|
26
|
+
events_process.join()
|
|
27
|
+
events = queue.get()
|
|
28
|
+
assert len(events) >= 3
|
|
29
|
+
scan_events = [e for e in events if e["type"] == "SCAN"]
|
|
30
|
+
assert len(scan_events) == 2
|
|
31
|
+
assert any([e["data"] == "test@blacklanternsecurity.com" for e in events])
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
def test_bbot_fastapi(bbot_httpserver):
|
|
35
|
+
|
|
36
|
+
bbot_httpserver.expect_request("/").respond_with_data("test@blacklanternsecurity.com")
|
|
37
|
+
fastapi_process = start_fastapi_server()
|
|
38
|
+
|
|
39
|
+
try:
|
|
40
|
+
|
|
41
|
+
# wait for the server to start with a timeout of 60 seconds
|
|
42
|
+
start_time = time.time()
|
|
43
|
+
while True:
|
|
44
|
+
try:
|
|
45
|
+
response = httpx.get("http://127.0.0.1:8978/ping")
|
|
46
|
+
response.raise_for_status()
|
|
47
|
+
break
|
|
48
|
+
except httpx.HTTPError:
|
|
49
|
+
if time.time() - start_time > 60:
|
|
50
|
+
raise TimeoutError("Server did not start within 60 seconds.")
|
|
51
|
+
time.sleep(0.1)
|
|
52
|
+
continue
|
|
53
|
+
|
|
54
|
+
# run a scan
|
|
55
|
+
response = httpx.get(
|
|
56
|
+
"http://127.0.0.1:8978/start",
|
|
57
|
+
params={"targets": ["http://127.0.0.1:8888", "blacklanternsecurity.com"]},
|
|
58
|
+
timeout=100,
|
|
59
|
+
)
|
|
60
|
+
events = response.json()
|
|
61
|
+
assert len(events) >= 3
|
|
62
|
+
scan_events = [e for e in events if e["type"] == "SCAN"]
|
|
63
|
+
assert len(scan_events) == 2
|
|
64
|
+
assert any([e["data"] == "test@blacklanternsecurity.com" for e in events])
|
|
65
|
+
|
|
66
|
+
finally:
|
|
67
|
+
with suppress(Exception):
|
|
68
|
+
fastapi_process.terminate()
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
def start_fastapi_server():
|
|
72
|
+
import os
|
|
73
|
+
import sys
|
|
74
|
+
|
|
75
|
+
env = os.environ.copy()
|
|
76
|
+
with suppress(KeyError):
|
|
77
|
+
del env["BBOT_TESTING"]
|
|
78
|
+
python_executable = str(sys.executable)
|
|
79
|
+
process = Popen(
|
|
80
|
+
[python_executable, "-m", "uvicorn", "bbot.test.fastapi_test:app", "--port", "8978"], cwd=cwd, env=env
|
|
81
|
+
)
|
|
82
|
+
return process
|
|
@@ -631,6 +631,42 @@ def custom_lookup(query, rdtype):
|
|
|
631
631
|
assert len(modified_wildcard_events) == 0
|
|
632
632
|
|
|
633
633
|
|
|
634
|
+
@pytest.mark.asyncio
|
|
635
|
+
async def test_wildcard_deduplication(bbot_scanner):
|
|
636
|
+
|
|
637
|
+
custom_lookup = """
|
|
638
|
+
def custom_lookup(query, rdtype):
|
|
639
|
+
if rdtype == "TXT" and query.strip(".").endswith("evilcorp.com"):
|
|
640
|
+
return {""}
|
|
641
|
+
"""
|
|
642
|
+
|
|
643
|
+
mock_data = {
|
|
644
|
+
"evilcorp.com": {"A": ["127.0.0.1"]},
|
|
645
|
+
}
|
|
646
|
+
|
|
647
|
+
from bbot.modules.base import BaseModule
|
|
648
|
+
|
|
649
|
+
class DummyModule(BaseModule):
|
|
650
|
+
watched_events = ["DNS_NAME"]
|
|
651
|
+
per_domain_only = True
|
|
652
|
+
|
|
653
|
+
async def handle_event(self, event):
|
|
654
|
+
for i in range(30):
|
|
655
|
+
await self.emit_event(f"www{i}.evilcorp.com", "DNS_NAME", parent=event)
|
|
656
|
+
|
|
657
|
+
# scan without omitted event type
|
|
658
|
+
scan = bbot_scanner(
|
|
659
|
+
"evilcorp.com", config={"dns": {"minimal": False, "wildcard_ignore": []}, "omit_event_types": []}
|
|
660
|
+
)
|
|
661
|
+
await scan.helpers.dns._mock_dns(mock_data, custom_lookup_fn=custom_lookup)
|
|
662
|
+
dummy_module = DummyModule(scan)
|
|
663
|
+
scan.modules["dummy_module"] = dummy_module
|
|
664
|
+
events = [e async for e in scan.async_start()]
|
|
665
|
+
dns_name_events = [e for e in events if e.type == "DNS_NAME"]
|
|
666
|
+
assert len(dns_name_events) == 2
|
|
667
|
+
assert 1 == len([e for e in dns_name_events if e.data == "_wildcard.evilcorp.com"])
|
|
668
|
+
|
|
669
|
+
|
|
634
670
|
@pytest.mark.asyncio
|
|
635
671
|
async def test_dns_raw_records(bbot_scanner):
|
|
636
672
|
|
|
@@ -966,3 +966,35 @@ def test_event_magic():
|
|
|
966
966
|
assert event.tags == {"folder"}
|
|
967
967
|
|
|
968
968
|
zip_file.unlink()
|
|
969
|
+
|
|
970
|
+
|
|
971
|
+
def test_event_hashing():
|
|
972
|
+
scan = Scanner("example.com")
|
|
973
|
+
url_event = scan.make_event("https://api.example.com/", "URL_UNVERIFIED", parent=scan.root_event)
|
|
974
|
+
host_event_1 = scan.make_event("www.example.com", "DNS_NAME", parent=url_event)
|
|
975
|
+
host_event_2 = scan.make_event("test.example.com", "DNS_NAME", parent=url_event)
|
|
976
|
+
finding_data = {"description": "Custom Yara Rule [find_string] Matched via identifier [str1]"}
|
|
977
|
+
finding1 = scan.make_event(finding_data, "FINDING", parent=host_event_1)
|
|
978
|
+
finding2 = scan.make_event(finding_data, "FINDING", parent=host_event_2)
|
|
979
|
+
finding3 = scan.make_event(finding_data, "FINDING", parent=host_event_2)
|
|
980
|
+
|
|
981
|
+
assert finding1.data == {
|
|
982
|
+
"description": "Custom Yara Rule [find_string] Matched via identifier [str1]",
|
|
983
|
+
"host": "www.example.com",
|
|
984
|
+
}
|
|
985
|
+
assert finding2.data == {
|
|
986
|
+
"description": "Custom Yara Rule [find_string] Matched via identifier [str1]",
|
|
987
|
+
"host": "test.example.com",
|
|
988
|
+
}
|
|
989
|
+
assert finding3.data == {
|
|
990
|
+
"description": "Custom Yara Rule [find_string] Matched via identifier [str1]",
|
|
991
|
+
"host": "test.example.com",
|
|
992
|
+
}
|
|
993
|
+
assert finding1.id != finding2.id
|
|
994
|
+
assert finding2.id == finding3.id
|
|
995
|
+
assert finding1.data_id != finding2.data_id
|
|
996
|
+
assert finding2.data_id == finding3.data_id
|
|
997
|
+
assert finding1.data_hash != finding2.data_hash
|
|
998
|
+
assert finding2.data_hash == finding3.data_hash
|
|
999
|
+
assert hash(finding1) != hash(finding2)
|
|
1000
|
+
assert hash(finding2) == hash(finding3)
|
|
@@ -2,11 +2,12 @@ from .base import ModuleTestBase
|
|
|
2
2
|
|
|
3
3
|
|
|
4
4
|
class TestCensys(ModuleTestBase):
|
|
5
|
-
config_overrides = {"modules": {"censys": {"
|
|
5
|
+
config_overrides = {"modules": {"censys": {"api_key": "api_id:api_secret"}}}
|
|
6
6
|
|
|
7
7
|
async def setup_before_prep(self, module_test):
|
|
8
8
|
module_test.httpx_mock.add_response(
|
|
9
9
|
url="https://search.censys.io/api/v1/account",
|
|
10
|
+
match_headers={"Authorization": "Basic YXBpX2lkOmFwaV9zZWNyZXQ="},
|
|
10
11
|
json={
|
|
11
12
|
"email": "info@blacklanternsecurity.com",
|
|
12
13
|
"login": "nope",
|
|
@@ -17,6 +18,7 @@ class TestCensys(ModuleTestBase):
|
|
|
17
18
|
)
|
|
18
19
|
module_test.httpx_mock.add_response(
|
|
19
20
|
url="https://search.censys.io/api/v2/certificates/search",
|
|
21
|
+
match_headers={"Authorization": "Basic YXBpX2lkOmFwaV9zZWNyZXQ="},
|
|
20
22
|
match_content=b'{"q": "names: blacklanternsecurity.com", "per_page": 100}',
|
|
21
23
|
json={
|
|
22
24
|
"code": 200,
|
|
@@ -45,6 +47,7 @@ class TestCensys(ModuleTestBase):
|
|
|
45
47
|
)
|
|
46
48
|
module_test.httpx_mock.add_response(
|
|
47
49
|
url="https://search.censys.io/api/v2/certificates/search",
|
|
50
|
+
match_headers={"Authorization": "Basic YXBpX2lkOmFwaV9zZWNyZXQ="},
|
|
48
51
|
match_content=b'{"q": "names: blacklanternsecurity.com", "per_page": 100, "cursor": "NextToken"}',
|
|
49
52
|
json={
|
|
50
53
|
"code": 200,
|
|
@@ -2,15 +2,17 @@ from .base import ModuleTestBase
|
|
|
2
2
|
|
|
3
3
|
|
|
4
4
|
class TestPassiveTotal(ModuleTestBase):
|
|
5
|
-
config_overrides = {"modules": {"passivetotal": {"
|
|
5
|
+
config_overrides = {"modules": {"passivetotal": {"api_key": "jon@bls.fakedomain:asdf"}}}
|
|
6
6
|
|
|
7
7
|
async def setup_before_prep(self, module_test):
|
|
8
8
|
module_test.httpx_mock.add_response(
|
|
9
9
|
url="https://api.passivetotal.org/v2/account/quota",
|
|
10
|
+
match_headers={"Authorization": "Basic am9uQGJscy5mYWtlZG9tYWluOmFzZGY="},
|
|
10
11
|
json={"user": {"counts": {"search_api": 10}, "limits": {"search_api": 20}}},
|
|
11
12
|
)
|
|
12
13
|
module_test.httpx_mock.add_response(
|
|
13
14
|
url="https://api.passivetotal.org/v2/enrichment/subdomains?query=blacklanternsecurity.com",
|
|
15
|
+
match_headers={"Authorization": "Basic am9uQGJscy5mYWtlZG9tYWluOmFzZGY="},
|
|
14
16
|
json={"subdomains": ["asdf"]},
|
|
15
17
|
)
|
|
16
18
|
|
|
@@ -1,22 +1,22 @@
|
|
|
1
|
-
bbot/__init__.py,sha256=
|
|
1
|
+
bbot/__init__.py,sha256=zHXaGwoPshL7cKbzVuXyPNKEb-cMwH90drmKbblMwKE,130
|
|
2
2
|
bbot/cli.py,sha256=7S3a4eB-Dl8yodc5WC-927Z30CNlLl9EXimGvIVypJo,10434
|
|
3
3
|
bbot/core/__init__.py,sha256=l255GJE_DvUnWvrRb0J5lG-iMztJ8zVvoweDOfegGtI,46
|
|
4
4
|
bbot/core/config/__init__.py,sha256=zYNw2Me6tsEr8hOOkLb4BQ97GB7Kis2k--G81S8vofU,342
|
|
5
5
|
bbot/core/config/files.py,sha256=pNrcw61UKKZeMt0rp9Ac5mUK7LdIRmcpojMxI-LwjeA,1413
|
|
6
|
-
bbot/core/config/logger.py,sha256=
|
|
7
|
-
bbot/core/core.py,sha256=
|
|
8
|
-
bbot/core/engine.py,sha256=
|
|
6
|
+
bbot/core/config/logger.py,sha256=YBWLg3x0bEaHKQR8Fn9e1Y0OeyFTED0fgAMtUZT2gEw,10583
|
|
7
|
+
bbot/core/core.py,sha256=zFSbPd0YcjpeI1uO9U6lYY5gKmzNSTGhduvXvB6qZ0M,7065
|
|
8
|
+
bbot/core/engine.py,sha256=XulYpIPtFej1fYB0pjJB1z1hYcI9aHIUuE0fpMcLSc8,29352
|
|
9
9
|
bbot/core/event/__init__.py,sha256=8ut88ZUg0kbtWkOx2j3XzNr_3kTfgoM-3UdiWHFA_ag,56
|
|
10
|
-
bbot/core/event/base.py,sha256=
|
|
10
|
+
bbot/core/event/base.py,sha256=y6uvXJ2Z832d0v7lvkbuP8BJraqaiGM35BcvPkPT1Ig,60666
|
|
11
11
|
bbot/core/event/helpers.py,sha256=PUN4Trq5_wpKVuhmwUQWAr40apgMXhJ9Gz-VfZ0j3lA,1554
|
|
12
12
|
bbot/core/flags.py,sha256=Ltvm8Bc4D65I55HuU5bzyjO1R3yMDNpVmreGU83ZBXE,1266
|
|
13
13
|
bbot/core/helpers/__init__.py,sha256=0UNwcZjNsX41hbHdo3yZPuARkYWch-okI68DScexve4,86
|
|
14
14
|
bbot/core/helpers/async_helpers.py,sha256=KF-Dl4zI_VjXhQdOmwGub3sIGavlXlitZkzJAn_l6Tw,3621
|
|
15
15
|
bbot/core/helpers/bloom.py,sha256=z7gttz-ugvwj7s2L14feJhEx2rzECdqcB255A0hjvNI,2579
|
|
16
16
|
bbot/core/helpers/cache.py,sha256=1aMr3HVD45cDtHEG5xlznDUCywRgO9oRFidscrs_5sA,1537
|
|
17
|
-
bbot/core/helpers/command.py,sha256=
|
|
17
|
+
bbot/core/helpers/command.py,sha256=2i0hz-uo6zUV9oGtXf-9erh_g3SazDtYeh6pETlfPPA,12810
|
|
18
18
|
bbot/core/helpers/depsinstaller/__init__.py,sha256=2mx1nYylSyvwl0GCM9YDHqrFEt2_5dSWAjP1RmhmbQg,37
|
|
19
|
-
bbot/core/helpers/depsinstaller/installer.py,sha256=
|
|
19
|
+
bbot/core/helpers/depsinstaller/installer.py,sha256=5lanuF26n1DD0IV1uNn1zBfZFE3f4Qeh_fXnmTv-gic,16937
|
|
20
20
|
bbot/core/helpers/depsinstaller/sudo_askpass.py,sha256=yGa2OQv30RO75QkMuG1iruKqb7amQxRVRRcHmvIeGhk,1276
|
|
21
21
|
bbot/core/helpers/diff.py,sha256=7waBeHFGnAKn-R-sBd-wc3yjwxT_umwy4YxfE7JFd6w,10599
|
|
22
22
|
bbot/core/helpers/dns/__init__.py,sha256=2JK8P0BUfPlh4CTuuOWQCOacwL7NEtGFYPJsxbA0Zwo,27
|
|
@@ -32,7 +32,7 @@ bbot/core/helpers/libmagic.py,sha256=a9tmL558cM5lzN69YahBc7JNHmPnBJgu9Wa8Q5bH1S0
|
|
|
32
32
|
bbot/core/helpers/misc.py,sha256=rvfZmm8UHCChmbMorjPMybaCZTkERrKZhxvY9S4dVPc,86873
|
|
33
33
|
bbot/core/helpers/names_generator.py,sha256=Sj_Q-7KQyElEpalzlUadSwaniESqrIVVEle9ycPIiho,10322
|
|
34
34
|
bbot/core/helpers/ntlm.py,sha256=P2Xj4-GPos2iAzw4dfk0FJp6oGyycGhu2x6sLDVjYjs,2573
|
|
35
|
-
bbot/core/helpers/process.py,sha256=
|
|
35
|
+
bbot/core/helpers/process.py,sha256=2bbveUuLIrr6wjixZRdFX-gqNE-b7rxVpzBPHYNcmzM,1704
|
|
36
36
|
bbot/core/helpers/ratelimiter.py,sha256=K8qFIyJPJtfdb9kSW6_lL6ahWqxR2uWyCBkDlg6uJgo,1990
|
|
37
37
|
bbot/core/helpers/regex.py,sha256=XURaY6ijpOYYU9lzWMAKg12G1VFtGJjlJl07_eN1xxk,4170
|
|
38
38
|
bbot/core/helpers/regexes.py,sha256=kz3y3hyEJvsnF7-4NrkrHTwwhkbRqEYi_wqA_rmZlgQ,5859
|
|
@@ -45,6 +45,7 @@ bbot/core/helpers/web/ssl_context.py,sha256=aWVgl-d0HoE8B4EBKNxaa5UAzQmx79DjDByf
|
|
|
45
45
|
bbot/core/helpers/web/web.py,sha256=K7BOts1c1bRjU5rpluD94jClwchmBMZQk8FZI1ljS94,22661
|
|
46
46
|
bbot/core/helpers/wordcloud.py,sha256=WdQwboCNcCxcUdLuB6MMMDQBL4ZshFM_f6GW7nUZEBQ,19819
|
|
47
47
|
bbot/core/modules.py,sha256=OOUSncr-EM6bJBrI3iH5wvfnpTXKQ-A8OL8UMvkL0CU,31432
|
|
48
|
+
bbot/core/multiprocess.py,sha256=ocQHanskJ09gHwe7RZmwNdZyCOQyeyUoIHCtLbtvXUk,1771
|
|
48
49
|
bbot/core/shared_deps.py,sha256=A3vrI62uPTTayNIHhWAj6xz43cj--oXWC4prmDlgJnw,6958
|
|
49
50
|
bbot/db/sql/models.py,sha256=AXefz4nEtpV2p19d6rLaEcCQNnqXODpldA-P5uSUntg,4729
|
|
50
51
|
bbot/defaults.yml,sha256=_3sNH-2TWPaQHZ6ozBA1UKWLB7HuHK8vjZ534mb8cO4,6042
|
|
@@ -73,7 +74,7 @@ bbot/modules/bufferoverrun.py,sha256=FcHzj04iSyBqyfUY6lBPLlkD-hT-hBgICKJqlgIGkHY
|
|
|
73
74
|
bbot/modules/builtwith.py,sha256=A2Q70GtHtMHQgLYgX-UhUNzBwIZ7n0p5IOEI5lPSBk4,5369
|
|
74
75
|
bbot/modules/bypass403.py,sha256=Qrszg-vVfjKA0IosM88CekpW6BcazJThpl5moXIdeiQ,6843
|
|
75
76
|
bbot/modules/c99.py,sha256=cvyLZ7oHk6kxq6UWu0oISbXLKwnZW9yNBmsvT1RTGwI,1388
|
|
76
|
-
bbot/modules/censys.py,sha256=
|
|
77
|
+
bbot/modules/censys.py,sha256=J9ntCZGpS-HPf4EfoS6NpkOv7RqGk7AZmYIpLI8uYIE,3309
|
|
77
78
|
bbot/modules/certspotter.py,sha256=dENaH4gUGV-ainYOupBO7GkOW9KQutTzC1NdWfpMhnw,877
|
|
78
79
|
bbot/modules/chaos.py,sha256=axnGkINmr-3Hu4x_x3t7eQ8n7wpCsmUp2HykWhcRDGU,1513
|
|
79
80
|
bbot/modules/code_repository.py,sha256=x70Z45VnNNMF8BPkHfGWZXsZXw_fStGB3y0-8jbP1Ns,2078
|
|
@@ -120,7 +121,7 @@ bbot/modules/internal/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3h
|
|
|
120
121
|
bbot/modules/internal/aggregate.py,sha256=csWYIt2fUp9K_CRxP3bndUMIjpNIh8rmBubp5Fr1-nc,395
|
|
121
122
|
bbot/modules/internal/base.py,sha256=BXO4Hc7XKaAOaLzolF3krJX1KibPxtek2GTQUgnCHk0,387
|
|
122
123
|
bbot/modules/internal/cloudcheck.py,sha256=kkD5DC5Y7EPCiVTdEspZktkflArU3unx--9ciBikxHo,4099
|
|
123
|
-
bbot/modules/internal/dnsresolve.py,sha256=
|
|
124
|
+
bbot/modules/internal/dnsresolve.py,sha256=UW88BlpJ7gOjPARrjVgtwpDIDPNQZRpuIRpL2yVP6T4,15251
|
|
124
125
|
bbot/modules/internal/excavate.py,sha256=1kPiaUlpJVT_xgPgmzqhpf04uanIe9ZayQsWANbzhd4,51238
|
|
125
126
|
bbot/modules/internal/speculate.py,sha256=qT9tQWiSZ1bhAt-CeehRztVo_DP5eOqdbpPVVxGktTg,8765
|
|
126
127
|
bbot/modules/internetdb.py,sha256=Edg0Z84dH8dPTZMd7RlzvYBYNq8JHs_ns_ldnFxwRKo,5415
|
|
@@ -156,7 +157,7 @@ bbot/modules/output/websocket.py,sha256=sDTtHU-Ey_tvS0gMi6PVPV9L4qAmGyWeccxAKfEW
|
|
|
156
157
|
bbot/modules/paramminer_cookies.py,sha256=q1PzftHQpCHLz81_VgLZsO6moia7ZtnU32igfcySi2w,1816
|
|
157
158
|
bbot/modules/paramminer_getparams.py,sha256=_j6rgaqV5wGJoa8p5-KKbe2YsVGUtmWIanCVtFiF97Y,1893
|
|
158
159
|
bbot/modules/paramminer_headers.py,sha256=QEnWxveQVuS91MEYKYtfquqdNzimN7sQHuxpcqiTHpo,10305
|
|
159
|
-
bbot/modules/passivetotal.py,sha256=
|
|
160
|
+
bbot/modules/passivetotal.py,sha256=sq6xN8k3Dfbpa4hEleR8d9u45_lA5kOCTTaHtkjgVAg,1593
|
|
160
161
|
bbot/modules/pgp.py,sha256=Xu2M9WEIlwTm5-Lv29g7BblI05tD9Dl0XsYSeY6UURs,2065
|
|
161
162
|
bbot/modules/portscan.py,sha256=eb1sNb8ffe5gcjOsOOvX48oNtecJ1Tqq6exoz_G_jZ4,13327
|
|
162
163
|
bbot/modules/postman.py,sha256=tbLrxi5pOycLABvphORxyK9duTSBXZLgpf1vAZvOIa0,3512
|
|
@@ -220,29 +221,31 @@ bbot/scanner/preset/conditions.py,sha256=hFL9cSIWGEsv2TfM5UGurf0c91cyaM8egb5IngB
|
|
|
220
221
|
bbot/scanner/preset/environ.py,sha256=-wbFk1YHpU8IJLKVw23Q3btQTICeX0iulURo7D673L0,4732
|
|
221
222
|
bbot/scanner/preset/path.py,sha256=p9tZC7XcgZv2jXpbEJAg1lU2b4ZLX5COFnCxEUOXz2g,2234
|
|
222
223
|
bbot/scanner/preset/preset.py,sha256=-HH_nlr4VaXmKCooXMG5av39gOUdCVOO_y9Bhgbt_u4,40180
|
|
223
|
-
bbot/scanner/scanner.py,sha256=
|
|
224
|
+
bbot/scanner/scanner.py,sha256=pcnJJyDJiHLSuWfeM22_5Zv3S0HhFUq1oEDZDngX704,53835
|
|
224
225
|
bbot/scanner/stats.py,sha256=re93sArKXZSiD0Owgqk2J3Kdvfm3RL4Y9Qy_VOcaVk8,3623
|
|
225
226
|
bbot/scanner/target.py,sha256=X25gpgRv5HmqQjGADiSe6b8744yOkRhAGAvKKYbXnSI,19886
|
|
226
227
|
bbot/scripts/docs.py,sha256=kg2CzovmUVGJx9hBZjAjUdE1hXeIwC7Ry3CyrnE8GL8,10782
|
|
227
228
|
bbot/test/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
228
229
|
bbot/test/bbot_fixtures.py,sha256=J1_MfpCMXftfGHZc-dgn42ODpTmSJidoBibOltfthac,9862
|
|
229
|
-
bbot/test/conftest.py,sha256=
|
|
230
|
+
bbot/test/conftest.py,sha256=El74QSpuE4jOxvQOUGqoUcXP7j3UJiIDlThErxZj1lE,11193
|
|
230
231
|
bbot/test/coverage.cfg,sha256=ko9RacAYsJxWJCL8aEuNtkAOtP9lexYiDbeFWe8Tp8Y,31
|
|
232
|
+
bbot/test/fastapi_test.py,sha256=9OhOFRyagXTshMsnuzuKIcR4uzS6VW65m7h9KgB4jSA,381
|
|
231
233
|
bbot/test/owasp_mastg.apk,sha256=Hai_V9JmEJ-aB8Ab9xEaGXXOAfGQudkUvNOuPb75byE,66651
|
|
232
234
|
bbot/test/run_tests.sh,sha256=0oprBl970NAqXS4YQa8nRUtKljPeS_WNSvd-QmO5FNY,945
|
|
233
235
|
bbot/test/test.conf,sha256=HrE7z3O91wg2MeauqIP-rwq10FAB5K6WrHJrX1eN7oc,963
|
|
234
236
|
bbot/test/test_output.ndjson,sha256=Jfor8nUJ3QTEwXxD6UULrFXM4zhP5wflWo_UNekM3V8,323
|
|
235
237
|
bbot/test/test_step_1/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
236
238
|
bbot/test/test_step_1/test__module__tests.py,sha256=RpD4yuVuQRgbbUkfuasxUlyoVxhTm6TeDyi87y_AaK0,1461
|
|
239
|
+
bbot/test/test_step_1/test_bbot_fastapi.py,sha256=Ph1oTgwOz-oz5H_5Scjv0ItGwenS93lyF4eOkOOlfZ0,2538
|
|
237
240
|
bbot/test/test_step_1/test_bloom_filter.py,sha256=OpiZXsBX-I8QdTK0LqSYkGMDLA6vL_6t0wco9ypxxtQ,2114
|
|
238
241
|
bbot/test/test_step_1/test_cli.py,sha256=m4LyIiiedR41f5SpnlkxgE-f42fF7qoGcuthpYxQ_m8,24688
|
|
239
242
|
bbot/test/test_step_1/test_command.py,sha256=5IeGV6TKB0xtFEsfsU_0mNrOmEdIQiQ3FHkUmsBNoOI,6485
|
|
240
243
|
bbot/test/test_step_1/test_config.py,sha256=Q38hygpke2GDcv8OguVZuiSOnfDJxEMrRy20dN5Qsn0,887
|
|
241
244
|
bbot/test/test_step_1/test_depsinstaller.py,sha256=zr9f-wJDotD1ZvKXGEuDRWzFYMAYBI6209mI_PWPtTQ,703
|
|
242
|
-
bbot/test/test_step_1/test_dns.py,sha256=
|
|
245
|
+
bbot/test/test_step_1/test_dns.py,sha256=uFNQCaHLmmVz8KLIHTtZXSLXYLKZkRd7NiIQC5TSGXY,32132
|
|
243
246
|
bbot/test/test_step_1/test_docs.py,sha256=YWVGNRfzcrvDmFekX0Cq9gutQplsqvhKTpZ0XK4tWvo,82
|
|
244
247
|
bbot/test/test_step_1/test_engine.py,sha256=Bfid3-D9ziN93w4vym97tFEn_l2Iof08wjITTv_lAZw,4269
|
|
245
|
-
bbot/test/test_step_1/test_events.py,sha256=
|
|
248
|
+
bbot/test/test_step_1/test_events.py,sha256=8vuskVWNF73AoiQf-UHpFouRUTtRjRzTw1Xt6H0UjkI,48173
|
|
246
249
|
bbot/test/test_step_1/test_files.py,sha256=5Q_3jPpMXULxDHsanSDUaj8zF8bXzKdiJZHOmoYpLhQ,699
|
|
247
250
|
bbot/test/test_step_1/test_helpers.py,sha256=oY2hWhgL-TCB67ve1bAyIwZO3wNRWpx4SjCHNUxHep8,38676
|
|
248
251
|
bbot/test/test_step_1/test_manager_deduplication.py,sha256=hZQpDXzg6zvzxFolVOcJuY-ME8NXjZUsqS70BRNXp8A,15594
|
|
@@ -283,7 +286,7 @@ bbot/test/test_step_2/module_tests/test_module_bufferoverrun.py,sha256=os7A6vdwl
|
|
|
283
286
|
bbot/test/test_step_2/module_tests/test_module_builtwith.py,sha256=c_Ta6OXWYdUdcwuE-AbaT-tzj8SUuglMecQX6mDavuE,5051
|
|
284
287
|
bbot/test/test_step_2/module_tests/test_module_bypass403.py,sha256=-MV06l6Q7d_sM0L2OIw1ReXJc2dj30xC3suOl2HhZTY,3551
|
|
285
288
|
bbot/test/test_step_2/module_tests/test_module_c99.py,sha256=-xyL1y3eX_rGuBR-U0N1HDZuAw_A_UysN5PupWe0iDI,7427
|
|
286
|
-
bbot/test/test_step_2/module_tests/test_module_censys.py,sha256=
|
|
289
|
+
bbot/test/test_step_2/module_tests/test_module_censys.py,sha256=RoFfLS0hgASdSoctJEzaKrHVqqRkuPRKPTYVCX2rZLo,4177
|
|
287
290
|
bbot/test/test_step_2/module_tests/test_module_certspotter.py,sha256=60jCOeK1yaUEgtTxYW-T47kZgKt9XxP2qBH9w-0MDBk,636
|
|
288
291
|
bbot/test/test_step_2/module_tests/test_module_chaos.py,sha256=9JRgtDEnnJgmEMCTB2bqRJRkBavLys-6ypHPxrM_hXk,956
|
|
289
292
|
bbot/test/test_step_2/module_tests/test_module_cloudcheck.py,sha256=sRWgotul2I4qY5SjNBDuo2iOceHs-3x0cqJ-K29GitY,3927
|
|
@@ -348,7 +351,7 @@ bbot/test/test_step_2/module_tests/test_module_otx.py,sha256=tWkJlrdSCP2IUPGkB2R
|
|
|
348
351
|
bbot/test/test_step_2/module_tests/test_module_paramminer_cookies.py,sha256=BHbyFTh37DX7RdDQChqtbs8emCHXUTZGGq3e4UlklEA,2377
|
|
349
352
|
bbot/test/test_step_2/module_tests/test_module_paramminer_getparams.py,sha256=mWHeWQ_di3q6cMt164xaQ2hjqYkuIil8grs7-Um87PY,10083
|
|
350
353
|
bbot/test/test_step_2/module_tests/test_module_paramminer_headers.py,sha256=qTT-5fyqw69LaZBxak3FvPfH7lh9W0KVBtJgslD-3eQ,5545
|
|
351
|
-
bbot/test/test_step_2/module_tests/test_module_passivetotal.py,sha256=
|
|
354
|
+
bbot/test/test_step_2/module_tests/test_module_passivetotal.py,sha256=fTGQECQ0OzcwiH64-0igFRKO-rs3kXScivZord_oWWU,1120
|
|
352
355
|
bbot/test/test_step_2/module_tests/test_module_pgp.py,sha256=-m-nPq6WR5UzPDuxeZbuzBQfFi1QfrZQ8RZH4g11ocE,1609
|
|
353
356
|
bbot/test/test_step_2/module_tests/test_module_portscan.py,sha256=StCm93P4q3o-NhPtUDOA6g_LTH2cwzEw0l2V5ZN5eeI,7306
|
|
354
357
|
bbot/test/test_step_2/module_tests/test_module_postman.py,sha256=XvgfMgUhJuVgGkgT-JzxJyevNSVv7YvX1yLKJHmD3dw,5026
|
|
@@ -404,8 +407,8 @@ bbot/wordlists/raft-small-extensions-lowercase_CLEANED.txt,sha256=ruUQwVfia1_m2u
|
|
|
404
407
|
bbot/wordlists/top_open_ports_nmap.txt,sha256=LmdFYkfapSxn1pVuQC2LkOIY2hMLgG-Xts7DVtYzweM,42727
|
|
405
408
|
bbot/wordlists/valid_url_schemes.txt,sha256=VciB-ww0y-O8Ii1wpTR6rJzGDiC2r-dhVsIJApS1ZYU,3309
|
|
406
409
|
bbot/wordlists/wordninja_dns.txt.gz,sha256=DYHvvfW0TvzrVwyprqODAk4tGOxv5ezNmCPSdPuDUnQ,570241
|
|
407
|
-
bbot-2.2.0.
|
|
408
|
-
bbot-2.2.0.
|
|
409
|
-
bbot-2.2.0.
|
|
410
|
-
bbot-2.2.0.
|
|
411
|
-
bbot-2.2.0.
|
|
410
|
+
bbot-2.2.0.5263rc0.dist-info/LICENSE,sha256=GzeCzK17hhQQDNow0_r0L8OfLpeTKQjFQwBQU7ZUymg,32473
|
|
411
|
+
bbot-2.2.0.5263rc0.dist-info/METADATA,sha256=w_Verhck5yE-bvYz7e08XFMYQSLCh0bWxLxq5VSqcfw,17109
|
|
412
|
+
bbot-2.2.0.5263rc0.dist-info/WHEEL,sha256=Nq82e9rUAnEjt98J6MlVmMCZb-t9cYE2Ir1kpBmnWfs,88
|
|
413
|
+
bbot-2.2.0.5263rc0.dist-info/entry_points.txt,sha256=cWjvcU_lLrzzJgjcjF7yeGuRA_eDS8pQ-kmPUAyOBfo,38
|
|
414
|
+
bbot-2.2.0.5263rc0.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|