bbot 2.4.2.6659rc0__py3-none-any.whl → 2.4.2.6706rc0__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/helpers/async_helpers.py +29 -8
- bbot/defaults.yml +6 -0
- bbot/modules/apkpure.py +5 -3
- bbot/modules/base.py +86 -32
- bbot/modules/docker_pull.py +3 -3
- bbot/modules/filedownload.py +8 -4
- bbot/modules/git_clone.py +5 -2
- bbot/modules/gitdumper.py +14 -5
- bbot/modules/github_workflows.py +12 -5
- bbot/modules/nuclei.py +4 -2
- bbot/modules/portscan.py +2 -0
- bbot/modules/postman_download.py +6 -3
- bbot/modules/trufflehog.py +2 -3
- bbot/scanner/preset/args.py +10 -1
- bbot/scanner/preset/preset.py +0 -2
- bbot/scanner/scanner.py +1 -4
- bbot/scripts/docs.py +8 -0
- bbot/test/test_step_1/test_scan.py +57 -0
- bbot/test/test_step_2/module_tests/test_module_apkpure.py +2 -0
- bbot/test/test_step_2/module_tests/test_module_bucket_file_enum.py +8 -3
- bbot/test/test_step_2/module_tests/test_module_docker_pull.py +2 -0
- bbot/test/test_step_2/module_tests/test_module_filedownload.py +5 -1
- bbot/test/test_step_2/module_tests/test_module_git_clone.py +4 -1
- bbot/test/test_step_2/module_tests/test_module_gitdumper.py +2 -0
- bbot/test/test_step_2/module_tests/test_module_portscan.py +3 -3
- bbot/test/test_step_2/module_tests/test_module_postman_download.py +6 -1
- bbot/test/test_step_2/module_tests/test_module_trufflehog.py +38 -12
- {bbot-2.4.2.6659rc0.dist-info → bbot-2.4.2.6706rc0.dist-info}/METADATA +2 -2
- {bbot-2.4.2.6659rc0.dist-info → bbot-2.4.2.6706rc0.dist-info}/RECORD +33 -33
- {bbot-2.4.2.6659rc0.dist-info → bbot-2.4.2.6706rc0.dist-info}/LICENSE +0 -0
- {bbot-2.4.2.6659rc0.dist-info → bbot-2.4.2.6706rc0.dist-info}/WHEEL +0 -0
- {bbot-2.4.2.6659rc0.dist-info → bbot-2.4.2.6706rc0.dist-info}/entry_points.txt +0 -0
bbot/__init__.py
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
|
+
import time
|
|
1
2
|
import uuid
|
|
2
3
|
import random
|
|
3
4
|
import asyncio
|
|
4
5
|
import logging
|
|
5
6
|
import functools
|
|
6
|
-
from
|
|
7
|
-
from .misc import human_timedelta
|
|
7
|
+
from contextlib import suppress
|
|
8
8
|
from cachetools import keys, LRUCache
|
|
9
9
|
from contextlib import asynccontextmanager
|
|
10
10
|
|
|
@@ -63,26 +63,27 @@ class TaskCounter:
|
|
|
63
63
|
self._lock = asyncio.Lock()
|
|
64
64
|
return self._lock
|
|
65
65
|
|
|
66
|
-
def count(self, task_name, n=1, _log=True):
|
|
66
|
+
def count(self, task_name, n=1, asyncio_task=None, _log=True):
|
|
67
67
|
if callable(task_name):
|
|
68
68
|
task_name = f"{task_name.__qualname__}()"
|
|
69
|
-
return self.Task(self, task_name, n=n, _log=_log)
|
|
69
|
+
return self.Task(self, task_name, n=n, _log=_log, asyncio_task=asyncio_task)
|
|
70
70
|
|
|
71
71
|
class Task:
|
|
72
|
-
def __init__(self, manager, task_name, n=1, _log=True):
|
|
72
|
+
def __init__(self, manager, task_name, n=1, _log=True, asyncio_task=None):
|
|
73
73
|
self.manager = manager
|
|
74
74
|
self.task_name = task_name
|
|
75
75
|
self.task_id = None
|
|
76
76
|
self.start_time = None
|
|
77
77
|
self.log = _log
|
|
78
78
|
self.n = n
|
|
79
|
+
self._asyncio_task = asyncio_task
|
|
79
80
|
|
|
80
81
|
async def __aenter__(self):
|
|
81
82
|
self.task_id = uuid.uuid4()
|
|
82
83
|
# if self.log:
|
|
83
84
|
# log.trace(f"Starting task {self.task_name} ({self.task_id})")
|
|
84
85
|
async with self.manager.lock:
|
|
85
|
-
self.start_time =
|
|
86
|
+
self.start_time = time.time()
|
|
86
87
|
self.manager.tasks[self.task_id] = self
|
|
87
88
|
return self
|
|
88
89
|
|
|
@@ -92,9 +93,29 @@ class TaskCounter:
|
|
|
92
93
|
# if self.log:
|
|
93
94
|
# log.trace(f"Finished task {self.task_name} ({self.task_id})")
|
|
94
95
|
|
|
96
|
+
@property
|
|
97
|
+
def asyncio_task(self):
|
|
98
|
+
if self._asyncio_task is None:
|
|
99
|
+
raise AttributeError("No asyncio task associated with this task")
|
|
100
|
+
return self._asyncio_task
|
|
101
|
+
|
|
102
|
+
@property
|
|
103
|
+
def function_name(self):
|
|
104
|
+
with suppress(AttributeError):
|
|
105
|
+
return self.asyncio_task.get_coro().__name__
|
|
106
|
+
return ""
|
|
107
|
+
|
|
108
|
+
async def cancel(self):
|
|
109
|
+
self.asyncio_task.cancel()
|
|
110
|
+
with suppress(asyncio.CancelledError):
|
|
111
|
+
await self.asyncio_task
|
|
112
|
+
|
|
113
|
+
@property
|
|
114
|
+
def running_for(self):
|
|
115
|
+
return time.time() - self.start_time
|
|
116
|
+
|
|
95
117
|
def __str__(self):
|
|
96
|
-
|
|
97
|
-
return f"{self.task_name} running for {running_for}"
|
|
118
|
+
return f"{self.task_name} running for {self.running_for:.1f}s"
|
|
98
119
|
|
|
99
120
|
|
|
100
121
|
def get_event_loop():
|
bbot/defaults.yml
CHANGED
|
@@ -133,6 +133,12 @@ deps:
|
|
|
133
133
|
# Load BBOT modules from these custom paths
|
|
134
134
|
module_dirs: []
|
|
135
135
|
|
|
136
|
+
# maximum runtime in seconds for each module's handle_event() is 60 minutes
|
|
137
|
+
# when the timeout is reached, the offending handle_event() will be cancelled and the module will move on to the next event
|
|
138
|
+
module_handle_event_timeout: 3600
|
|
139
|
+
# handle_batch() default timeout is 2 hours
|
|
140
|
+
module_handle_batch_timeout: 7200
|
|
141
|
+
|
|
136
142
|
# Infer certain events from others, e.g. IPs from IP ranges, DNS_NAMEs from URLs, etc.
|
|
137
143
|
speculate: True
|
|
138
144
|
# Passively search event data for URLs, hostnames, emails, etc.
|
bbot/modules/apkpure.py
CHANGED
|
@@ -13,14 +13,16 @@ class apkpure(BaseModule):
|
|
|
13
13
|
"author": "@domwhewell-sage",
|
|
14
14
|
}
|
|
15
15
|
options = {"output_folder": ""}
|
|
16
|
-
options_desc = {
|
|
16
|
+
options_desc = {
|
|
17
|
+
"output_folder": "Folder to download APKs to. If not specified, downloaded APKs will be deleted when the scan completes, to minimize disk usage."
|
|
18
|
+
}
|
|
17
19
|
|
|
18
20
|
async def setup(self):
|
|
19
|
-
output_folder = self.config.get("output_folder")
|
|
21
|
+
output_folder = self.config.get("output_folder", "")
|
|
20
22
|
if output_folder:
|
|
21
23
|
self.output_dir = Path(output_folder) / "apk_files"
|
|
22
24
|
else:
|
|
23
|
-
self.output_dir = self.
|
|
25
|
+
self.output_dir = self.helpers.temp_dir / "apk_files"
|
|
24
26
|
self.helpers.mkdir(self.output_dir)
|
|
25
27
|
return await super().setup()
|
|
26
28
|
|
bbot/modules/base.py
CHANGED
|
@@ -61,8 +61,6 @@ class BaseModule:
|
|
|
61
61
|
|
|
62
62
|
batch_size (int): Size of batches processed by handle_batch(). Default is 1.
|
|
63
63
|
|
|
64
|
-
batch_wait (int): Seconds to wait before force-submitting a batch. Default is 10.
|
|
65
|
-
|
|
66
64
|
api_failure_abort_threshold (int): Threshold for setting error state after failed HTTP requests (only takes effect when `api_request()` is used. Default is 5.
|
|
67
65
|
|
|
68
66
|
_preserve_graph (bool): When set to True, accept events that may be duplicates but are necessary for construction of complete graph. Typically only enabled for output modules that need to maintain full chains of events, e.g. `neo4j` and `json`. Default is False.
|
|
@@ -102,7 +100,6 @@ class BaseModule:
|
|
|
102
100
|
|
|
103
101
|
_module_threads = 1
|
|
104
102
|
_batch_size = 1
|
|
105
|
-
batch_wait = 10
|
|
106
103
|
|
|
107
104
|
# disable the module after this many failed attempts in a row
|
|
108
105
|
_api_failure_abort_threshold = 3
|
|
@@ -159,6 +156,13 @@ class BaseModule:
|
|
|
159
156
|
|
|
160
157
|
self._tasks = []
|
|
161
158
|
self._event_received = None
|
|
159
|
+
# maximum runtime for each module's handle_event()
|
|
160
|
+
self._default_handle_event_timeout = self.scan.config.get("module_handle_event_timeout", 60 * 60) # 1 hour
|
|
161
|
+
self._default_handle_batch_timeout = self.scan.config.get(
|
|
162
|
+
"module_handle_batch_timeout", 60 * 60 * 2
|
|
163
|
+
) # 2 hours
|
|
164
|
+
self._event_handler_watchdog_task = None
|
|
165
|
+
self._event_handler_watchdog_interval = self.event_handler_timeout / 10
|
|
162
166
|
|
|
163
167
|
# used for optional "per host" tracking
|
|
164
168
|
self._per_host_tracker = set()
|
|
@@ -399,6 +403,13 @@ class BaseModule:
|
|
|
399
403
|
module_threads = self._module_threads
|
|
400
404
|
return module_threads
|
|
401
405
|
|
|
406
|
+
@property
|
|
407
|
+
def event_handler_timeout(self):
|
|
408
|
+
module_timeout = self.config.get("module_timeout", None)
|
|
409
|
+
if module_timeout is not None:
|
|
410
|
+
return float(module_timeout)
|
|
411
|
+
return self._default_handle_event_timeout if self.batch_size <= 1 else self._default_handle_batch_timeout
|
|
412
|
+
|
|
402
413
|
@property
|
|
403
414
|
def auth_secret(self):
|
|
404
415
|
"""Indicates if the module is properly configured for authentication.
|
|
@@ -446,23 +457,28 @@ class BaseModule:
|
|
|
446
457
|
- If a "FINISHED" event is found, invokes 'finish()' method of the module.
|
|
447
458
|
"""
|
|
448
459
|
finish = False
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
460
|
+
submitted = False
|
|
461
|
+
if self.batch_size <= 1:
|
|
462
|
+
return
|
|
463
|
+
if self.num_incoming_events > 0:
|
|
464
|
+
events, finish = await self._events_waiting()
|
|
465
|
+
if events and not self.errored:
|
|
466
|
+
self.verbose(f"Handling batch of {len(events):,} events")
|
|
467
|
+
event_types = {}
|
|
468
|
+
for e in events:
|
|
469
|
+
event_types[e.type] = event_types.get(e.type, 0) + 1
|
|
470
|
+
event_types_sorted = sorted(event_types.items(), key=lambda x: x[1], reverse=True)
|
|
471
|
+
event_types_str = ", ".join(f"{k}: {v}" for k, v in event_types_sorted)
|
|
472
|
+
submitted = True
|
|
473
|
+
context = f"{self.name}.handle_batch({event_types_str})"
|
|
474
|
+
try:
|
|
475
|
+
await self.run_task(self.handle_batch(*events), context, n=len(events))
|
|
476
|
+
except asyncio.CancelledError:
|
|
477
|
+
self.debug(f"{context} was cancelled")
|
|
478
|
+
self.verbose(f"Finished handling batch of {len(events):,} events")
|
|
462
479
|
if finish:
|
|
463
480
|
context = f"{self.name}.finish()"
|
|
464
|
-
|
|
465
|
-
await self.finish()
|
|
481
|
+
await self.run_task(self.finish(), context)
|
|
466
482
|
return submitted
|
|
467
483
|
|
|
468
484
|
def make_event(self, *args, **kwargs):
|
|
@@ -594,6 +610,10 @@ class BaseModule:
|
|
|
594
610
|
asyncio.create_task(self._worker(), name=f"{self.scan.name}.{self.name}._worker()")
|
|
595
611
|
for _ in range(self.module_threads)
|
|
596
612
|
]
|
|
613
|
+
self._event_handler_watchdog_task = asyncio.create_task(
|
|
614
|
+
self._event_handler_watchdog(),
|
|
615
|
+
name=f"{self.scan.name}.{self.name}._event_handler_watchdog()",
|
|
616
|
+
)
|
|
597
617
|
|
|
598
618
|
async def _setup(self):
|
|
599
619
|
"""
|
|
@@ -662,11 +682,6 @@ class BaseModule:
|
|
|
662
682
|
async with self.scan._acatch(context=self._worker, unhandled_is_critical=True):
|
|
663
683
|
try:
|
|
664
684
|
while not self.scan.stopping and not self.errored:
|
|
665
|
-
# hold the reigns if our outgoing queue is full
|
|
666
|
-
if self._qsize > 0 and self.outgoing_event_queue.qsize() >= self._qsize:
|
|
667
|
-
await asyncio.sleep(0.1)
|
|
668
|
-
continue
|
|
669
|
-
|
|
670
685
|
# if batch wasn't big enough, we wait for the next event before continuing
|
|
671
686
|
if self.batch_size > 1:
|
|
672
687
|
submitted = await self._handle_batch()
|
|
@@ -689,14 +704,20 @@ class BaseModule:
|
|
|
689
704
|
if acceptable:
|
|
690
705
|
if event.type == "FINISHED":
|
|
691
706
|
context = f"{self.name}.finish()"
|
|
692
|
-
|
|
693
|
-
await self.finish()
|
|
707
|
+
try:
|
|
708
|
+
await self.run_task(self.finish(), context)
|
|
709
|
+
except asyncio.CancelledError:
|
|
710
|
+
self.debug(f"{context} was cancelled")
|
|
711
|
+
continue
|
|
694
712
|
else:
|
|
695
713
|
context = f"{self.name}.handle_event({event})"
|
|
696
714
|
self.scan.stats.event_consumed(event, self)
|
|
697
715
|
self.debug(f"Handling {event}")
|
|
698
|
-
|
|
699
|
-
await self.handle_event(event)
|
|
716
|
+
try:
|
|
717
|
+
await self.run_task(self.handle_event(event), context)
|
|
718
|
+
except asyncio.CancelledError:
|
|
719
|
+
self.debug(f"{context} was cancelled")
|
|
720
|
+
continue
|
|
700
721
|
self.debug(f"Finished handling {event}")
|
|
701
722
|
else:
|
|
702
723
|
self.debug(f"Not accepting {event} because {reason}")
|
|
@@ -852,6 +873,36 @@ class BaseModule:
|
|
|
852
873
|
async with self.scan._acatch(context), self._task_counter.count(context):
|
|
853
874
|
await self.helpers.execute_sync_or_async(callback)
|
|
854
875
|
|
|
876
|
+
async def run_task(self, coro, name, n=1):
|
|
877
|
+
"""
|
|
878
|
+
Start a task while tracking it in the module's task counter.
|
|
879
|
+
|
|
880
|
+
This lets us keep a detailed module status and selectively cancel tasks when needed, like when handle_event exceeds its max runtime.
|
|
881
|
+
"""
|
|
882
|
+
task = asyncio.create_task(coro)
|
|
883
|
+
async with self.scan._acatch(context=name), self._task_counter.count(task_name=name, asyncio_task=task, n=n):
|
|
884
|
+
return await task
|
|
885
|
+
|
|
886
|
+
async def _event_handler_watchdog(self):
|
|
887
|
+
"""
|
|
888
|
+
Watches handle_event and handle_batch tasks and cancels them if they exceed their max runtime.
|
|
889
|
+
"""
|
|
890
|
+
while not self.scan.stopping and not self.errored:
|
|
891
|
+
# if there are events in the outgoing queue, we leave the tasks alone
|
|
892
|
+
if self.outgoing_event_queue.qsize() > 0:
|
|
893
|
+
await self.helpers.sleep(self._event_handler_watchdog_interval)
|
|
894
|
+
continue
|
|
895
|
+
event_handler_tasks = [
|
|
896
|
+
t for t in self._task_counter.tasks.values() if t.function_name in ("handle_event", "handle_batch")
|
|
897
|
+
]
|
|
898
|
+
for task in event_handler_tasks:
|
|
899
|
+
if task.running_for > self.event_handler_timeout:
|
|
900
|
+
self.warning(
|
|
901
|
+
f"{self.name} Cancelling event handler task {task.task_name} because it's been running for {task.running_for:.1f}s (max timeout is {self.event_handler_timeout})"
|
|
902
|
+
)
|
|
903
|
+
await task.cancel()
|
|
904
|
+
await asyncio.sleep(self._event_handler_watchdog_interval)
|
|
905
|
+
|
|
855
906
|
async def queue_event(self, event):
|
|
856
907
|
"""
|
|
857
908
|
Asynchronously queues an incoming event to the module's event queue for further processing.
|
|
@@ -1266,7 +1317,7 @@ class BaseModule:
|
|
|
1266
1317
|
new_url, new_kwargs = iter_key(url, page, page_size, offset, **requests_kwargs)
|
|
1267
1318
|
result = await self.api_request(new_url, **new_kwargs)
|
|
1268
1319
|
if result is None:
|
|
1269
|
-
self.verbose(f"api_page_iter() got no response for {
|
|
1320
|
+
self.verbose(f"api_page_iter() got no response for {new_url}")
|
|
1270
1321
|
break
|
|
1271
1322
|
try:
|
|
1272
1323
|
if _json:
|
|
@@ -1708,10 +1759,13 @@ class BaseInterceptModule(BaseModule):
|
|
|
1708
1759
|
context = f"{self.name}.handle_event({event, kwargs})"
|
|
1709
1760
|
self.scan.stats.event_consumed(event, self)
|
|
1710
1761
|
self.debug(f"Intercepting {event}")
|
|
1711
|
-
|
|
1712
|
-
forward_event = await self.handle_event(event, **kwargs)
|
|
1713
|
-
|
|
1714
|
-
|
|
1762
|
+
try:
|
|
1763
|
+
forward_event = await self.run_task(self.handle_event(event, **kwargs), context)
|
|
1764
|
+
except asyncio.CancelledError:
|
|
1765
|
+
self.debug(f"{context} was cancelled")
|
|
1766
|
+
continue
|
|
1767
|
+
with suppress(ValueError, TypeError):
|
|
1768
|
+
forward_event, forward_event_reason = forward_event
|
|
1715
1769
|
|
|
1716
1770
|
if forward_event is False:
|
|
1717
1771
|
self.debug(f"Not forwarding {event} because {forward_event_reason}")
|
bbot/modules/docker_pull.py
CHANGED
|
@@ -17,7 +17,7 @@ class docker_pull(BaseModule):
|
|
|
17
17
|
options = {"all_tags": False, "output_folder": ""}
|
|
18
18
|
options_desc = {
|
|
19
19
|
"all_tags": "Download all tags from each registry (Default False)",
|
|
20
|
-
"output_folder": "Folder to download docker repositories to",
|
|
20
|
+
"output_folder": "Folder to download docker repositories to. If not specified, downloaded docker images will be deleted when the scan completes, to minimize disk usage.",
|
|
21
21
|
}
|
|
22
22
|
|
|
23
23
|
scope_distance_modifier = 2
|
|
@@ -34,11 +34,11 @@ class docker_pull(BaseModule):
|
|
|
34
34
|
)
|
|
35
35
|
}
|
|
36
36
|
self.all_tags = self.config.get("all_tags", True)
|
|
37
|
-
output_folder = self.config.get("output_folder")
|
|
37
|
+
output_folder = self.config.get("output_folder", "")
|
|
38
38
|
if output_folder:
|
|
39
39
|
self.output_dir = Path(output_folder) / "docker_images"
|
|
40
40
|
else:
|
|
41
|
-
self.output_dir = self.
|
|
41
|
+
self.output_dir = self.helpers.temp_dir / "docker_images"
|
|
42
42
|
self.helpers.mkdir(self.output_dir)
|
|
43
43
|
return await super().setup()
|
|
44
44
|
|
bbot/modules/filedownload.py
CHANGED
|
@@ -84,12 +84,12 @@ class filedownload(BaseModule):
|
|
|
84
84
|
"bz2", # Bzip2 Compressed File
|
|
85
85
|
],
|
|
86
86
|
"max_filesize": "10MB",
|
|
87
|
-
"
|
|
87
|
+
"output_folder": "",
|
|
88
88
|
}
|
|
89
89
|
options_desc = {
|
|
90
90
|
"extensions": "File extensions to download",
|
|
91
91
|
"max_filesize": "Cancel download if filesize is greater than this size",
|
|
92
|
-
"
|
|
92
|
+
"output_folder": "Folder to download files to. If not specified, downloaded files will be deleted when the scan completes, to minimize disk usage.",
|
|
93
93
|
}
|
|
94
94
|
|
|
95
95
|
scope_distance_modifier = 3
|
|
@@ -97,10 +97,14 @@ class filedownload(BaseModule):
|
|
|
97
97
|
async def setup(self):
|
|
98
98
|
self.extensions = list({e.lower().strip(".") for e in self.config.get("extensions", [])})
|
|
99
99
|
self.max_filesize = self.config.get("max_filesize", "10MB")
|
|
100
|
-
self.download_dir = self.scan.home / "filedownload"
|
|
101
|
-
self.helpers.mkdir(self.download_dir)
|
|
102
100
|
self.urls_downloaded = set()
|
|
103
101
|
self.files_downloaded = 0
|
|
102
|
+
output_dir = self.config.get("output_folder", "")
|
|
103
|
+
if output_dir:
|
|
104
|
+
self.download_dir = Path(output_dir) / "filedownload"
|
|
105
|
+
else:
|
|
106
|
+
self.download_dir = self.helpers.temp_dir / "filedownload"
|
|
107
|
+
self.helpers.mkdir(self.download_dir)
|
|
104
108
|
self.mime_db_file = await self.helpers.wordlist(
|
|
105
109
|
"https://raw.githubusercontent.com/jshttp/mime-db/master/db.json"
|
|
106
110
|
)
|
bbot/modules/git_clone.py
CHANGED
|
@@ -13,7 +13,10 @@ class git_clone(github):
|
|
|
13
13
|
"author": "@domwhewell-sage",
|
|
14
14
|
}
|
|
15
15
|
options = {"api_key": "", "output_folder": ""}
|
|
16
|
-
options_desc = {
|
|
16
|
+
options_desc = {
|
|
17
|
+
"api_key": "Github token",
|
|
18
|
+
"output_folder": "Folder to clone repositories to. If not specified, cloned repositories will be deleted when the scan completes, to minimize disk usage.",
|
|
19
|
+
}
|
|
17
20
|
|
|
18
21
|
deps_apt = ["git"]
|
|
19
22
|
|
|
@@ -24,7 +27,7 @@ class git_clone(github):
|
|
|
24
27
|
if output_folder:
|
|
25
28
|
self.output_dir = Path(output_folder) / "git_repos"
|
|
26
29
|
else:
|
|
27
|
-
self.output_dir = self.
|
|
30
|
+
self.output_dir = self.helpers.temp_dir / "git_repos"
|
|
28
31
|
self.helpers.mkdir(self.output_dir)
|
|
29
32
|
return await super().setup()
|
|
30
33
|
|
bbot/modules/gitdumper.py
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import asyncio
|
|
1
2
|
import regex as re
|
|
2
3
|
from pathlib import Path
|
|
3
4
|
from subprocess import CalledProcessError
|
|
@@ -19,7 +20,7 @@ class gitdumper(BaseModule):
|
|
|
19
20
|
"max_semanic_version": 10,
|
|
20
21
|
}
|
|
21
22
|
options_desc = {
|
|
22
|
-
"output_folder": "Folder to download repositories to",
|
|
23
|
+
"output_folder": "Folder to download repositories to. If not specified, downloaded repositories will be deleted when the scan completes, to minimize disk usage.",
|
|
23
24
|
"fuzz_tags": "Fuzz for common git tag names (v0.0.1, 0.0.2, etc.) up to the max_semanic_version",
|
|
24
25
|
"max_semanic_version": "Maximum version number to fuzz for (default < v10.10.10)",
|
|
25
26
|
}
|
|
@@ -28,11 +29,11 @@ class gitdumper(BaseModule):
|
|
|
28
29
|
|
|
29
30
|
async def setup(self):
|
|
30
31
|
self.urls_downloaded = set()
|
|
31
|
-
output_folder = self.config.get("output_folder")
|
|
32
|
+
output_folder = self.config.get("output_folder", "")
|
|
32
33
|
if output_folder:
|
|
33
34
|
self.output_dir = Path(output_folder) / "git_repos"
|
|
34
35
|
else:
|
|
35
|
-
self.output_dir = self.
|
|
36
|
+
self.output_dir = self.helpers.temp_dir / "git_repos"
|
|
36
37
|
self.helpers.mkdir(self.output_dir)
|
|
37
38
|
self.unsafe_regex = self.helpers.re.compile(r"^\s*fsmonitor|sshcommand|askpass|editor|pager", re.IGNORECASE)
|
|
38
39
|
self.ref_regex = self.helpers.re.compile(r"ref: refs/heads/([a-zA-Z\d_-]+)")
|
|
@@ -79,6 +80,7 @@ class gitdumper(BaseModule):
|
|
|
79
80
|
"staging",
|
|
80
81
|
"test",
|
|
81
82
|
"testing",
|
|
83
|
+
"trunk",
|
|
82
84
|
"wip",
|
|
83
85
|
]
|
|
84
86
|
url_patterns = [
|
|
@@ -121,7 +123,11 @@ class gitdumper(BaseModule):
|
|
|
121
123
|
dir_listing = await self.directory_listing_enabled(repo_url)
|
|
122
124
|
if dir_listing:
|
|
123
125
|
urls = await self.recursive_dir_list(dir_listing)
|
|
124
|
-
|
|
126
|
+
try:
|
|
127
|
+
result = await self.download_files(urls, repo_folder)
|
|
128
|
+
except asyncio.CancelledError:
|
|
129
|
+
self.verbose(f"Cancellation requested while downloading files from {repo_url}")
|
|
130
|
+
result = True
|
|
125
131
|
else:
|
|
126
132
|
result = await self.git_fuzz(repo_url, repo_folder)
|
|
127
133
|
if result:
|
|
@@ -171,7 +177,10 @@ class gitdumper(BaseModule):
|
|
|
171
177
|
result = await self.download_files(url_list, repo_folder)
|
|
172
178
|
if result:
|
|
173
179
|
await self.download_current_branch(repo_url, repo_folder)
|
|
174
|
-
|
|
180
|
+
try:
|
|
181
|
+
await self.download_git_objects(repo_url, repo_folder)
|
|
182
|
+
except asyncio.CancelledError:
|
|
183
|
+
self.verbose(f"Cancellation requested while downloading git objects from {repo_url}")
|
|
175
184
|
await self.download_git_packs(repo_url, repo_folder)
|
|
176
185
|
return True
|
|
177
186
|
else:
|
bbot/modules/github_workflows.py
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import zipfile
|
|
2
2
|
import fnmatch
|
|
3
|
+
from pathlib import Path
|
|
3
4
|
|
|
4
5
|
from bbot.modules.templates.github import github
|
|
5
6
|
|
|
@@ -13,10 +14,11 @@ class github_workflows(github):
|
|
|
13
14
|
"created_date": "2024-04-29",
|
|
14
15
|
"author": "@domwhewell-sage",
|
|
15
16
|
}
|
|
16
|
-
options = {"api_key": "", "num_logs": 1}
|
|
17
|
+
options = {"api_key": "", "num_logs": 1, "output_folder": ""}
|
|
17
18
|
options_desc = {
|
|
18
19
|
"api_key": "Github token",
|
|
19
20
|
"num_logs": "For each workflow fetch the last N successful runs logs (max 100)",
|
|
21
|
+
"output_folder": "Folder to download workflow logs and artifacts to",
|
|
20
22
|
}
|
|
21
23
|
|
|
22
24
|
scope_distance_modifier = 2
|
|
@@ -26,7 +28,11 @@ class github_workflows(github):
|
|
|
26
28
|
if self.num_logs > 100:
|
|
27
29
|
self.log.error("num_logs option is capped at 100")
|
|
28
30
|
return False
|
|
29
|
-
|
|
31
|
+
output_folder = self.config.get("output_folder", "")
|
|
32
|
+
if output_folder:
|
|
33
|
+
self.output_dir = Path(output_folder) / "workflow_logs"
|
|
34
|
+
else:
|
|
35
|
+
self.output_dir = self.scan.home / "workflow_logs"
|
|
30
36
|
self.helpers.mkdir(self.output_dir)
|
|
31
37
|
return await super().setup()
|
|
32
38
|
|
|
@@ -35,9 +41,10 @@ class github_workflows(github):
|
|
|
35
41
|
return r.is_success or getattr(r, "status_code", 0) == 404
|
|
36
42
|
|
|
37
43
|
async def filter_event(self, event):
|
|
38
|
-
if event.
|
|
39
|
-
|
|
40
|
-
|
|
44
|
+
if "git" not in event.tags:
|
|
45
|
+
return False, "event is not a git repository"
|
|
46
|
+
elif "github.com" not in event.data.get("url", ""):
|
|
47
|
+
return False, "event is not a github repository"
|
|
41
48
|
return True
|
|
42
49
|
|
|
43
50
|
async def handle_event(self, event):
|
bbot/modules/nuclei.py
CHANGED
|
@@ -28,6 +28,7 @@ class nuclei(BaseModule):
|
|
|
28
28
|
"directory_only": True,
|
|
29
29
|
"retries": 0,
|
|
30
30
|
"batch_size": 200,
|
|
31
|
+
"module_timeout": 21600, # 6 hours
|
|
31
32
|
}
|
|
32
33
|
options_desc = {
|
|
33
34
|
"version": "nuclei version",
|
|
@@ -38,11 +39,12 @@ class nuclei(BaseModule):
|
|
|
38
39
|
"concurrency": "maximum number of templates to be executed in parallel (default 25)",
|
|
39
40
|
"mode": "manual | technology | severe | budget. Technology: Only activate based on technology events that match nuclei tags (nuclei -as mode). Manual (DEFAULT): Fully manual settings. Severe: Only critical and high severity templates without intrusive. Budget: Limit Nuclei to a specified number of HTTP requests",
|
|
40
41
|
"etags": "tags to exclude from the scan",
|
|
41
|
-
"budget": "Used in budget mode to set the number of requests
|
|
42
|
+
"budget": "Used in budget mode to set the number of allowed requests per host",
|
|
42
43
|
"silent": "Don't display nuclei's banner or status messages",
|
|
43
44
|
"directory_only": "Filter out 'file' URL event (default True)",
|
|
44
45
|
"retries": "number of times to retry a failed request (default 0)",
|
|
45
46
|
"batch_size": "Number of targets to send to Nuclei per batch (default 200)",
|
|
47
|
+
"module_timeout": "Max time in seconds to spend handling each batch of events",
|
|
46
48
|
}
|
|
47
49
|
deps_ansible = [
|
|
48
50
|
{
|
|
@@ -57,7 +59,7 @@ class nuclei(BaseModule):
|
|
|
57
59
|
]
|
|
58
60
|
deps_pip = ["pyyaml~=6.0"]
|
|
59
61
|
in_scope_only = True
|
|
60
|
-
_batch_size =
|
|
62
|
+
_batch_size = 200
|
|
61
63
|
|
|
62
64
|
async def setup(self):
|
|
63
65
|
# attempt to update nuclei templates
|
bbot/modules/portscan.py
CHANGED
|
@@ -30,6 +30,7 @@ class portscan(BaseModule):
|
|
|
30
30
|
"adapter_ip": "",
|
|
31
31
|
"adapter_mac": "",
|
|
32
32
|
"router_mac": "",
|
|
33
|
+
"module_timeout": 259200, # 3 days
|
|
33
34
|
}
|
|
34
35
|
options_desc = {
|
|
35
36
|
"top_ports": "Top ports to scan (default 100) (to override, specify 'ports')",
|
|
@@ -42,6 +43,7 @@ class portscan(BaseModule):
|
|
|
42
43
|
"adapter_ip": "Send packets using this IP address. Not needed unless masscan's autodetection fails",
|
|
43
44
|
"adapter_mac": "Send packets using this as the source MAC address. Not needed unless masscan's autodetection fails",
|
|
44
45
|
"router_mac": "Send packets to this MAC address as the destination. Not needed unless masscan's autodetection fails",
|
|
46
|
+
"module_timeout": "Max time in seconds to spend handling each batch of events",
|
|
45
47
|
}
|
|
46
48
|
deps_common = ["masscan"]
|
|
47
49
|
batch_size = 1000000
|
bbot/modules/postman_download.py
CHANGED
|
@@ -14,15 +14,18 @@ class postman_download(postman):
|
|
|
14
14
|
"author": "@domwhewell-sage",
|
|
15
15
|
}
|
|
16
16
|
options = {"output_folder": "", "api_key": ""}
|
|
17
|
-
options_desc = {
|
|
17
|
+
options_desc = {
|
|
18
|
+
"output_folder": "Folder to download postman workspaces to. If not specified, downloaded workspaces will be deleted when the scan completes, to minimize disk usage.",
|
|
19
|
+
"api_key": "Postman API Key",
|
|
20
|
+
}
|
|
18
21
|
scope_distance_modifier = 2
|
|
19
22
|
|
|
20
23
|
async def setup(self):
|
|
21
|
-
output_folder = self.config.get("output_folder")
|
|
24
|
+
output_folder = self.config.get("output_folder", "")
|
|
22
25
|
if output_folder:
|
|
23
26
|
self.output_dir = Path(output_folder) / "postman_workspaces"
|
|
24
27
|
else:
|
|
25
|
-
self.output_dir = self.
|
|
28
|
+
self.output_dir = self.helpers.temp_dir / "postman_workspaces"
|
|
26
29
|
self.helpers.mkdir(self.output_dir)
|
|
27
30
|
return await super().setup()
|
|
28
31
|
|
bbot/modules/trufflehog.py
CHANGED
|
@@ -14,7 +14,7 @@ class trufflehog(BaseModule):
|
|
|
14
14
|
}
|
|
15
15
|
|
|
16
16
|
options = {
|
|
17
|
-
"version": "3.88.
|
|
17
|
+
"version": "3.88.34",
|
|
18
18
|
"config": "",
|
|
19
19
|
"only_verified": True,
|
|
20
20
|
"concurrency": 8,
|
|
@@ -88,8 +88,7 @@ class trufflehog(BaseModule):
|
|
|
88
88
|
|
|
89
89
|
if event.type == "CODE_REPOSITORY":
|
|
90
90
|
path = event.data["url"]
|
|
91
|
-
|
|
92
|
-
module = "github-experimental"
|
|
91
|
+
module = "github-experimental"
|
|
93
92
|
elif event.type == "FILESYSTEM":
|
|
94
93
|
path = event.data["path"]
|
|
95
94
|
if "git" in event.tags:
|
bbot/scanner/preset/args.py
CHANGED
|
@@ -9,9 +9,18 @@ from bbot.core.helpers.misc import chain_lists, get_closest_match, get_keys_in_d
|
|
|
9
9
|
log = logging.getLogger("bbot.presets.args")
|
|
10
10
|
|
|
11
11
|
|
|
12
|
+
universal_module_options = {
|
|
13
|
+
"batch_size": "The number of events to process in a single batch (only applies to batch modules)",
|
|
14
|
+
"module_threads": "How many event handlers to run in parallel",
|
|
15
|
+
"module_timeout": "Max time in seconds to spend handling each event or batch of events",
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
|
|
12
19
|
class BBOTArgs:
|
|
13
20
|
# module config options to exclude from validation
|
|
14
|
-
exclude_from_validation = re.compile(
|
|
21
|
+
exclude_from_validation = re.compile(
|
|
22
|
+
r".*modules\.[a-z0-9_]+\.(?:" + "|".join(universal_module_options.keys()) + ")$"
|
|
23
|
+
)
|
|
15
24
|
|
|
16
25
|
scan_examples = [
|
|
17
26
|
(
|
bbot/scanner/preset/preset.py
CHANGED
|
@@ -276,8 +276,6 @@ class Preset(metaclass=BasePreset):
|
|
|
276
276
|
self.exclude_flags.update(set(exclude_flags))
|
|
277
277
|
self.require_flags.update(set(require_flags))
|
|
278
278
|
|
|
279
|
-
# log.critical(f"{self.name}: verbose: {self.verbose}, debug: {self.debug}, silent: {self.silent}")
|
|
280
|
-
|
|
281
279
|
@property
|
|
282
280
|
def bbot_home(self):
|
|
283
281
|
return Path(self.config.get("home", "~/.bbot")).expanduser().resolve()
|
bbot/scanner/scanner.py
CHANGED
|
@@ -245,8 +245,6 @@ class Scanner:
|
|
|
245
245
|
self._cleanedup = False
|
|
246
246
|
self._omitted_event_types = None
|
|
247
247
|
|
|
248
|
-
self.__loop = None
|
|
249
|
-
self._manager_worker_loop_tasks = []
|
|
250
248
|
self.init_events_task = None
|
|
251
249
|
self.ticker_task = None
|
|
252
250
|
self.dispatcher_tasks = []
|
|
@@ -726,6 +724,7 @@ class Scanner:
|
|
|
726
724
|
scan_active_status.append(f" - {task}:")
|
|
727
725
|
# scan_active_status.append(f" incoming_queue_size: {m.num_incoming_events}")
|
|
728
726
|
# scan_active_status.append(f" outgoing_queue_size: {m.outgoing_event_queue.qsize()}")
|
|
727
|
+
|
|
729
728
|
for line in scan_active_status:
|
|
730
729
|
self.debug(line)
|
|
731
730
|
|
|
@@ -834,8 +833,6 @@ class Scanner:
|
|
|
834
833
|
tasks.append(self.ticker_task)
|
|
835
834
|
# dispatcher
|
|
836
835
|
tasks += self.dispatcher_tasks
|
|
837
|
-
# manager worker loops
|
|
838
|
-
tasks += self._manager_worker_loop_tasks
|
|
839
836
|
self.helpers.cancel_tasks_sync(tasks)
|
|
840
837
|
# process pool
|
|
841
838
|
self.helpers.process_pool.shutdown(cancel_futures=True)
|
bbot/scripts/docs.py
CHANGED
|
@@ -181,6 +181,14 @@ def update_docs():
|
|
|
181
181
|
assert len(bbot_output_module_table.splitlines()) > 10
|
|
182
182
|
update_md_files("BBOT OUTPUT MODULES", bbot_output_module_table)
|
|
183
183
|
|
|
184
|
+
# BBOT universal module options
|
|
185
|
+
from bbot.scanner.preset.args import universal_module_options
|
|
186
|
+
|
|
187
|
+
universal_module_options_table = ""
|
|
188
|
+
for option, description in universal_module_options.items():
|
|
189
|
+
universal_module_options_table += f"**{option}**: {description}\n"
|
|
190
|
+
update_md_files("BBOT UNIVERSAL MODULE OPTIONS", universal_module_options_table)
|
|
191
|
+
|
|
184
192
|
# BBOT module options
|
|
185
193
|
bbot_module_options_table = DEFAULT_PRESET.module_loader.modules_options_table()
|
|
186
194
|
assert len(bbot_module_options_table.splitlines()) > 100
|
|
@@ -88,6 +88,63 @@ async def test_scan(
|
|
|
88
88
|
assert len(scan6.dns_strings) == 1
|
|
89
89
|
|
|
90
90
|
|
|
91
|
+
@pytest.mark.asyncio
|
|
92
|
+
async def test_task_scan_handle_event_timeout(bbot_scanner):
|
|
93
|
+
from bbot.modules.base import BaseModule
|
|
94
|
+
|
|
95
|
+
# make a module that takes a long time to handle an event
|
|
96
|
+
class LongModule(BaseModule):
|
|
97
|
+
watched_events = ["IP_ADDRESS"]
|
|
98
|
+
handled_event = False
|
|
99
|
+
cancelled = False
|
|
100
|
+
_name = "long"
|
|
101
|
+
|
|
102
|
+
async def handle_event(self, event):
|
|
103
|
+
self.handled_event = True
|
|
104
|
+
try:
|
|
105
|
+
await self.helpers.sleep(99999999)
|
|
106
|
+
except asyncio.CancelledError:
|
|
107
|
+
self.cancelled = True
|
|
108
|
+
raise
|
|
109
|
+
|
|
110
|
+
# same thing but handle_batch
|
|
111
|
+
class LongBatchModule(BaseModule):
|
|
112
|
+
watched_events = ["IP_ADDRESS"]
|
|
113
|
+
handled_event = False
|
|
114
|
+
canceled = False
|
|
115
|
+
_name = "long_batch"
|
|
116
|
+
_batch_size = 2
|
|
117
|
+
|
|
118
|
+
async def handle_batch(self, *events):
|
|
119
|
+
self.handled_event = True
|
|
120
|
+
try:
|
|
121
|
+
await self.helpers.sleep(99999999)
|
|
122
|
+
except asyncio.CancelledError:
|
|
123
|
+
self.cancelled = True
|
|
124
|
+
raise
|
|
125
|
+
|
|
126
|
+
# scan with both modules
|
|
127
|
+
scan = bbot_scanner(
|
|
128
|
+
"127.0.0.1",
|
|
129
|
+
config={
|
|
130
|
+
"module_handle_event_timeout": 5,
|
|
131
|
+
"module_handle_batch_timeout": 5,
|
|
132
|
+
},
|
|
133
|
+
)
|
|
134
|
+
await scan._prep()
|
|
135
|
+
scan.modules["long"] = LongModule(scan=scan)
|
|
136
|
+
scan.modules["long_batch"] = LongBatchModule(scan=scan)
|
|
137
|
+
events = [e async for e in scan.async_start()]
|
|
138
|
+
assert events
|
|
139
|
+
assert any(e.data == "127.0.0.1" for e in events)
|
|
140
|
+
# make sure both modules were called
|
|
141
|
+
assert scan.modules["long"].handled_event
|
|
142
|
+
assert scan.modules["long_batch"].handled_event
|
|
143
|
+
# they should also be cancelled
|
|
144
|
+
assert scan.modules["long"].cancelled
|
|
145
|
+
assert scan.modules["long_batch"].cancelled
|
|
146
|
+
|
|
147
|
+
|
|
91
148
|
@pytest.mark.asyncio
|
|
92
149
|
async def test_url_extension_handling(bbot_scanner):
|
|
93
150
|
scan = bbot_scanner(config={"url_extension_blacklist": ["css"], "url_extension_httpx_only": ["js"]})
|
|
@@ -1,9 +1,11 @@
|
|
|
1
1
|
from pathlib import Path
|
|
2
2
|
from .base import ModuleTestBase, tempapkfile
|
|
3
|
+
from bbot.test.bbot_fixtures import bbot_test_dir
|
|
3
4
|
|
|
4
5
|
|
|
5
6
|
class TestAPKPure(ModuleTestBase):
|
|
6
7
|
modules_overrides = ["apkpure", "google_playstore", "speculate"]
|
|
8
|
+
config_overrides = {"modules": {"apkpure": {"output_folder": str(bbot_test_dir / "test_apkpure_files")}}}
|
|
7
9
|
apk_file = tempapkfile()
|
|
8
10
|
|
|
9
11
|
async def setup_after_prep(self, module_test):
|
|
@@ -1,10 +1,16 @@
|
|
|
1
1
|
from .base import ModuleTestBase
|
|
2
|
+
from bbot.test.bbot_fixtures import bbot_test_dir
|
|
2
3
|
|
|
3
4
|
|
|
4
5
|
class TestBucket_File_Enum(ModuleTestBase):
|
|
5
6
|
targets = ["http://127.0.0.1:8888"]
|
|
6
7
|
modules_overrides = ["bucket_file_enum", "filedownload", "httpx", "excavate", "cloudcheck"]
|
|
7
|
-
|
|
8
|
+
|
|
9
|
+
download_dir = bbot_test_dir / "test_bucket_file_enum"
|
|
10
|
+
config_overrides = {
|
|
11
|
+
"scope": {"report_distance": 5},
|
|
12
|
+
"modules": {"filedownload": {"output_folder": str(download_dir)}},
|
|
13
|
+
}
|
|
8
14
|
|
|
9
15
|
open_bucket_url = "https://testbucket.s3.amazonaws.com/"
|
|
10
16
|
open_bucket_body = """<ListBucketResult xmlns="http://s3.amazonaws.com/doc/2006-03-01/"><Name>testbucket</Name><Prefix></Prefix><Marker></Marker><MaxKeys>1000</MaxKeys><IsTruncated>false</IsTruncated><Contents><Key>index.html</Key><LastModified>2023-05-22T23:04:38.000Z</LastModified><ETag>"4a2d2d114f3abf90f8bd127c1f25095a"</ETag><Size>5</Size><StorageClass>STANDARD</StorageClass></Contents><Contents><Key>test.pdf</Key><LastModified>2022-04-30T21:13:40.000Z</LastModified><ETag>"723b0018c2f5a7ef06a34f84f6fa97e4"</ETag><Size>388901</Size><StorageClass>STANDARD</StorageClass></Contents></ListBucketResult>"""
|
|
@@ -32,8 +38,7 @@ trailer <</Root 1 0 R>>"""
|
|
|
32
38
|
)
|
|
33
39
|
|
|
34
40
|
def check(self, module_test, events):
|
|
35
|
-
|
|
36
|
-
files = list(download_dir.glob("*.pdf"))
|
|
41
|
+
files = list((self.download_dir / "filedownload").glob("*.pdf"))
|
|
37
42
|
assert any(e.type == "URL_UNVERIFIED" and e.data.endswith("test.pdf") for e in events)
|
|
38
43
|
assert not any(e.type == "URL_UNVERIFIED" and e.data.endswith("test.css") for e in events)
|
|
39
44
|
assert any(f.name.endswith("test.pdf") for f in files), "Failed to download PDF file from open bucket"
|
|
@@ -3,10 +3,12 @@ import tarfile
|
|
|
3
3
|
from pathlib import Path
|
|
4
4
|
|
|
5
5
|
from .base import ModuleTestBase
|
|
6
|
+
from bbot.test.bbot_fixtures import bbot_test_dir
|
|
6
7
|
|
|
7
8
|
|
|
8
9
|
class TestDockerPull(ModuleTestBase):
|
|
9
10
|
modules_overrides = ["speculate", "dockerhub", "docker_pull"]
|
|
11
|
+
config_overrides = {"modules": {"docker_pull": {"output_folder": str(bbot_test_dir / "test_docker_files")}}}
|
|
10
12
|
|
|
11
13
|
async def setup_before_prep(self, module_test):
|
|
12
14
|
module_test.httpx_mock.add_response(
|
|
@@ -1,11 +1,15 @@
|
|
|
1
1
|
from pathlib import Path
|
|
2
2
|
from .base import ModuleTestBase
|
|
3
|
+
from bbot.test.bbot_fixtures import bbot_test_dir
|
|
3
4
|
|
|
4
5
|
|
|
5
6
|
class TestFileDownload(ModuleTestBase):
|
|
6
7
|
targets = ["http://127.0.0.1:8888"]
|
|
7
8
|
modules_overrides = ["filedownload", "httpx", "excavate", "speculate"]
|
|
8
|
-
config_overrides = {
|
|
9
|
+
config_overrides = {
|
|
10
|
+
"web": {"spider_distance": 2, "spider_depth": 2},
|
|
11
|
+
"modules": {"filedownload": {"output_folder": str(bbot_test_dir / "test_filedownload_files")}},
|
|
12
|
+
}
|
|
9
13
|
|
|
10
14
|
pdf_data = """%PDF-1.
|
|
11
15
|
1 0 obj<</Pages 2 0 R>>endobj
|
|
@@ -6,10 +6,13 @@ import subprocess
|
|
|
6
6
|
from pathlib import Path
|
|
7
7
|
|
|
8
8
|
from .base import ModuleTestBase
|
|
9
|
+
from bbot.test.bbot_fixtures import bbot_test_dir
|
|
9
10
|
|
|
10
11
|
|
|
11
12
|
class TestGit_Clone(ModuleTestBase):
|
|
12
|
-
config_overrides = {
|
|
13
|
+
config_overrides = {
|
|
14
|
+
"modules": {"git_clone": {"api_key": "asdf", "output_folder": str(bbot_test_dir / "test_git_files")}}
|
|
15
|
+
}
|
|
13
16
|
modules_overrides = ["github_org", "speculate", "git_clone"]
|
|
14
17
|
|
|
15
18
|
file_content = "https://admin:admin@the-internet.herokuapp.com/basic_auth"
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
from pathlib import Path
|
|
2
2
|
from .base import ModuleTestBase
|
|
3
|
+
from bbot.test.bbot_fixtures import bbot_test_dir
|
|
3
4
|
|
|
4
5
|
|
|
5
6
|
class TestGitDumper_Dirlisting(ModuleTestBase):
|
|
@@ -8,6 +9,7 @@ class TestGitDumper_Dirlisting(ModuleTestBase):
|
|
|
8
9
|
]
|
|
9
10
|
|
|
10
11
|
modules_overrides = ["git", "gitdumper", "httpx"]
|
|
12
|
+
config_overrides = {"modules": {"gitdumper": {"output_folder": str(bbot_test_dir / "test_output")}}}
|
|
11
13
|
|
|
12
14
|
index_html = """<html>
|
|
13
15
|
<head>
|
|
@@ -76,7 +76,7 @@ class TestPortscan(ModuleTestBase):
|
|
|
76
76
|
def check(self, module_test, events):
|
|
77
77
|
assert set(self.syn_scanned) == {"8.8.8.0/24", "8.8.4.0/24"}
|
|
78
78
|
assert set(self.ping_scanned) == set()
|
|
79
|
-
assert self.syn_runs
|
|
79
|
+
assert self.syn_runs >= 1
|
|
80
80
|
assert self.ping_runs == 0
|
|
81
81
|
assert 1 == len(
|
|
82
82
|
[e for e in events if e.type == "DNS_NAME" and e.data == "evilcorp.com" and str(e.module) == "TARGET"]
|
|
@@ -133,7 +133,7 @@ class TestPortscanPingFirst(TestPortscan):
|
|
|
133
133
|
assert set(self.syn_scanned) == {"8.8.8.8/32"}
|
|
134
134
|
assert set(self.ping_scanned) == {"8.8.8.0/24", "8.8.4.0/24"}
|
|
135
135
|
assert self.syn_runs == 1
|
|
136
|
-
assert self.ping_runs
|
|
136
|
+
assert self.ping_runs >= 1
|
|
137
137
|
open_port_events = [e for e in events if e.type == "OPEN_TCP_PORT"]
|
|
138
138
|
assert len(open_port_events) == 3
|
|
139
139
|
assert {e.data for e in open_port_events} == {"8.8.8.8:443", "evilcorp.com:443", "www.evilcorp.com:443"}
|
|
@@ -149,7 +149,7 @@ class TestPortscanPingOnly(TestPortscan):
|
|
|
149
149
|
assert set(self.syn_scanned) == set()
|
|
150
150
|
assert set(self.ping_scanned) == {"8.8.8.0/24", "8.8.4.0/24"}
|
|
151
151
|
assert self.syn_runs == 0
|
|
152
|
-
assert self.ping_runs
|
|
152
|
+
assert self.ping_runs >= 1
|
|
153
153
|
open_port_events = [e for e in events if e.type == "OPEN_TCP_PORT"]
|
|
154
154
|
assert len(open_port_events) == 0
|
|
155
155
|
ip_events = [e for e in events if e.type == "IP_ADDRESS"]
|
|
@@ -1,8 +1,13 @@
|
|
|
1
1
|
from .base import ModuleTestBase
|
|
2
|
+
from bbot.test.bbot_fixtures import bbot_test_dir
|
|
2
3
|
|
|
3
4
|
|
|
4
5
|
class TestPostman_Download(ModuleTestBase):
|
|
5
|
-
config_overrides = {
|
|
6
|
+
config_overrides = {
|
|
7
|
+
"modules": {
|
|
8
|
+
"postman_download": {"api_key": "asdf", "output_folder": str(bbot_test_dir / "test_postman_files")}
|
|
9
|
+
}
|
|
10
|
+
}
|
|
6
11
|
modules_overrides = ["postman", "postman_download", "speculate"]
|
|
7
12
|
|
|
8
13
|
async def setup_before_prep(self, module_test):
|
|
@@ -1,15 +1,24 @@
|
|
|
1
|
-
import subprocess
|
|
2
|
-
import shutil
|
|
3
1
|
import io
|
|
2
|
+
import shutil
|
|
4
3
|
import zipfile
|
|
5
4
|
import tarfile
|
|
5
|
+
import subprocess
|
|
6
|
+
from copy import copy
|
|
6
7
|
from pathlib import Path
|
|
7
8
|
|
|
8
9
|
from .base import ModuleTestBase
|
|
10
|
+
from bbot.test.bbot_fixtures import bbot_test_dir
|
|
9
11
|
|
|
10
12
|
|
|
11
13
|
class TestTrufflehog(ModuleTestBase):
|
|
12
|
-
|
|
14
|
+
download_dir = bbot_test_dir / "test_trufflehog"
|
|
15
|
+
config_overrides = {
|
|
16
|
+
"modules": {
|
|
17
|
+
"postman_download": {"api_key": "asdf", "output_folder": str(download_dir)},
|
|
18
|
+
"docker_pull": {"output_folder": str(download_dir)},
|
|
19
|
+
"git_clone": {"output_folder": str(download_dir)},
|
|
20
|
+
}
|
|
21
|
+
}
|
|
13
22
|
modules_overrides = [
|
|
14
23
|
"github_org",
|
|
15
24
|
"speculate",
|
|
@@ -1129,15 +1138,20 @@ class TestTrufflehog(ModuleTestBase):
|
|
|
1129
1138
|
cwd=temp_repo_path,
|
|
1130
1139
|
)
|
|
1131
1140
|
|
|
1132
|
-
|
|
1141
|
+
# we need this test to work offline, so we patch git_clone to pull from a local file:// path
|
|
1142
|
+
old_handle_event = module_test.scan.modules["git_clone"].handle_event
|
|
1133
1143
|
|
|
1134
|
-
def
|
|
1135
|
-
event.
|
|
1136
|
-
|
|
1137
|
-
|
|
1138
|
-
|
|
1144
|
+
async def new_handle_event(event):
|
|
1145
|
+
if event.type == "CODE_REPOSITORY":
|
|
1146
|
+
event = copy(event)
|
|
1147
|
+
data = dict(event.data)
|
|
1148
|
+
data["url"] = event.data["url"].replace(
|
|
1149
|
+
"https://github.com/blacklanternsecurity", f"file://{temp_path}"
|
|
1150
|
+
)
|
|
1151
|
+
event.data = data
|
|
1152
|
+
return await old_handle_event(event)
|
|
1139
1153
|
|
|
1140
|
-
module_test.monkeypatch.setattr(module_test.scan.modules["git_clone"], "
|
|
1154
|
+
module_test.monkeypatch.setattr(module_test.scan.modules["git_clone"], "handle_event", new_handle_event)
|
|
1141
1155
|
|
|
1142
1156
|
def check(self, module_test, events):
|
|
1143
1157
|
vuln_events = [
|
|
@@ -1201,7 +1215,15 @@ class TestTrufflehog(ModuleTestBase):
|
|
|
1201
1215
|
|
|
1202
1216
|
|
|
1203
1217
|
class TestTrufflehog_NonVerified(TestTrufflehog):
|
|
1204
|
-
|
|
1218
|
+
download_dir = bbot_test_dir / "test_trufflehog_nonverified"
|
|
1219
|
+
config_overrides = {
|
|
1220
|
+
"modules": {
|
|
1221
|
+
"trufflehog": {"only_verified": False},
|
|
1222
|
+
"docker_pull": {"output_folder": str(download_dir)},
|
|
1223
|
+
"postman_download": {"api_key": "asdf", "output_folder": str(download_dir)},
|
|
1224
|
+
"git_clone": {"output_folder": str(download_dir)},
|
|
1225
|
+
}
|
|
1226
|
+
}
|
|
1205
1227
|
|
|
1206
1228
|
def check(self, module_test, events):
|
|
1207
1229
|
finding_events = [
|
|
@@ -1279,7 +1301,11 @@ class TestTrufflehog_HTTPResponse(ModuleTestBase):
|
|
|
1279
1301
|
class TestTrufflehog_RAWText(ModuleTestBase):
|
|
1280
1302
|
targets = ["http://127.0.0.1:8888/test.pdf"]
|
|
1281
1303
|
modules_overrides = ["httpx", "trufflehog", "filedownload", "extractous"]
|
|
1282
|
-
|
|
1304
|
+
|
|
1305
|
+
download_dir = bbot_test_dir / "test_trufflehog_rawtext"
|
|
1306
|
+
config_overrides = {
|
|
1307
|
+
"modules": {"trufflehog": {"only_verified": False}, "filedownload": {"output_folder": str(download_dir)}}
|
|
1308
|
+
}
|
|
1283
1309
|
|
|
1284
1310
|
async def setup_before_prep(self, module_test):
|
|
1285
1311
|
expect_args = {
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.3
|
|
2
2
|
Name: bbot
|
|
3
|
-
Version: 2.4.2.
|
|
3
|
+
Version: 2.4.2.6706rc0
|
|
4
4
|
Summary: OSINT automation for hackers.
|
|
5
5
|
License: GPL-3.0
|
|
6
6
|
Keywords: python,cli,automation,osint,threat-intel,intelligence,neo4j,scanner,python-library,hacking,recursion,pentesting,recon,command-line-tool,bugbounty,subdomains,security-tools,subdomain-scanner,osint-framework,attack-surface,subdomain-enumeration,osint-tool
|
|
@@ -18,7 +18,7 @@ Classifier: Topic :: Security
|
|
|
18
18
|
Requires-Dist: ansible-core (>=2.15.13,<3.0.0)
|
|
19
19
|
Requires-Dist: ansible-runner (>=2.3.2,<3.0.0)
|
|
20
20
|
Requires-Dist: beautifulsoup4 (>=4.12.2,<5.0.0)
|
|
21
|
-
Requires-Dist: cachetools (>=5.3.2,<
|
|
21
|
+
Requires-Dist: cachetools (>=5.3.2,<7.0.0)
|
|
22
22
|
Requires-Dist: cloudcheck (>=7.2.11,<8.0.0)
|
|
23
23
|
Requires-Dist: deepdiff (>=8.0.0,<9.0.0)
|
|
24
24
|
Requires-Dist: dnspython (>=2.4.2,<3.0.0)
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
bbot/__init__.py,sha256=
|
|
1
|
+
bbot/__init__.py,sha256=s1OTUHWAHT3aJvwRblp7G38RUxmbxe716-VaBNn5r2k,163
|
|
2
2
|
bbot/cli.py,sha256=1QJbANVw9Q3GFM92H2QRV2ds5756ulm08CDZwzwPpeI,11888
|
|
3
3
|
bbot/core/__init__.py,sha256=l255GJE_DvUnWvrRb0J5lG-iMztJ8zVvoweDOfegGtI,46
|
|
4
4
|
bbot/core/config/__init__.py,sha256=zYNw2Me6tsEr8hOOkLb4BQ97GB7Kis2k--G81S8vofU,342
|
|
@@ -11,7 +11,7 @@ bbot/core/event/base.py,sha256=1jUgd3I3TDITKoobh92ir_tIm38EN1ZbhoaX1W9fKts,67125
|
|
|
11
11
|
bbot/core/event/helpers.py,sha256=MohOCVBjkn_K1p4Ipgx-MKliZtV6l4NJPq3YgagkvSM,6507
|
|
12
12
|
bbot/core/flags.py,sha256=Ltvm8Bc4D65I55HuU5bzyjO1R3yMDNpVmreGU83ZBXE,1266
|
|
13
13
|
bbot/core/helpers/__init__.py,sha256=cpOGLKIgA3vdHYqsOtx63BFO_qbtwCmez2amFPu6YTs,111
|
|
14
|
-
bbot/core/helpers/async_helpers.py,sha256=
|
|
14
|
+
bbot/core/helpers/async_helpers.py,sha256=bVHEUIOZo8iCmuovLYb3oNLPdLFUoEyc6wZIIvtELVs,4399
|
|
15
15
|
bbot/core/helpers/bloom.py,sha256=gk02rO6x3F5MICa7ZUDsinRudwoGAifsbiyiMCwd0Gs,2739
|
|
16
16
|
bbot/core/helpers/cache.py,sha256=1aMr3HVD45cDtHEG5xlznDUCywRgO9oRFidscrs_5sA,1537
|
|
17
17
|
bbot/core/helpers/command.py,sha256=UBJa2RInEJtGjZ5e24PUQxPu1aTCIFkcCrrB0ERLdGI,12810
|
|
@@ -50,20 +50,20 @@ bbot/core/modules.py,sha256=G4rRVF1bQzp62kwpgxwMa_FTV4-huWwtcd6HpW9jQf0,31970
|
|
|
50
50
|
bbot/core/multiprocess.py,sha256=ocQHanskJ09gHwe7RZmwNdZyCOQyeyUoIHCtLbtvXUk,1771
|
|
51
51
|
bbot/core/shared_deps.py,sha256=mCMZeKSt46trzVqQDPGfXfEWg0Zw5YjiJx4BnsIRgHM,7640
|
|
52
52
|
bbot/db/sql/models.py,sha256=SrUdDOBCICzXJBY29p0VvILhMQ1JCuh725bqvIYogX0,4884
|
|
53
|
-
bbot/defaults.yml,sha256=
|
|
53
|
+
bbot/defaults.yml,sha256=TTxtlnyE9vPihXjkGMDbBpNRlGa48GhRXS23iFsKUAg,7830
|
|
54
54
|
bbot/errors.py,sha256=xwQcD26nU9oc7-o0kv5jmEDTInmi8_W8eKAgQZZxdVM,953
|
|
55
55
|
bbot/logger.py,sha256=wE-532v5FyKuSSoTdyW1xSfaOnLZB1axAJnB-uW2xrI,2745
|
|
56
56
|
bbot/modules/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
57
57
|
bbot/modules/ajaxpro.py,sha256=daE1yQoCsSI5c4dh3YKwRSggTISNjWgrK7qTPidk7cU,3764
|
|
58
58
|
bbot/modules/anubisdb.py,sha256=JCy2YCfa0e_VawpzNmcPXAosKUthmYGutireJ0gMDws,1916
|
|
59
|
-
bbot/modules/apkpure.py,sha256=
|
|
59
|
+
bbot/modules/apkpure.py,sha256=a_VRujUOIk7SVWyI9-N-nJVqfMApciIRdXVc-y_Ebuw,2558
|
|
60
60
|
bbot/modules/azure_realm.py,sha256=pP2PUlLy0K9KKaE8aNcznWjDW3PKHvnMejdOSc-o4ms,1612
|
|
61
61
|
bbot/modules/azure_tenant.py,sha256=qBn7CUA_hth2PqW55XZVjYxIw20xLYrMntXc6mYpmKU,5366
|
|
62
62
|
bbot/modules/baddns.py,sha256=ubO3KDfcIMJnMjyZX5FWZ4GWxLSekV_JQV7QvsPjtD0,6693
|
|
63
63
|
bbot/modules/baddns_direct.py,sha256=hWThpkXP87nnCRTlUh5qBJ1t4eo4l9kUmKNNxVNJI8A,3819
|
|
64
64
|
bbot/modules/baddns_zone.py,sha256=y1XaBUfFPnRbR2qaTqRyUsPgEL73722v2B8aS5YoGN4,1035
|
|
65
65
|
bbot/modules/badsecrets.py,sha256=LG37p48Rlxsfc3BmACMpkypsbuFTVvXqNhlP1IEsx0k,5109
|
|
66
|
-
bbot/modules/base.py,sha256=
|
|
66
|
+
bbot/modules/base.py,sha256=qH5nmYRP3Gc9z8ddr0iY1DR7b68tatv4M0IB-SjDobU,78162
|
|
67
67
|
bbot/modules/bevigil.py,sha256=0VLIxmeXRUI2-EoR6IzuHJMcX8KCHNNta-WYa3gVlDg,2862
|
|
68
68
|
bbot/modules/binaryedge.py,sha256=5F9LnZwRM_rZnzTv29hLJLI2GEQdzOwSpahPFC1kJC0,1397
|
|
69
69
|
bbot/modules/bucket_amazon.py,sha256=mwjYeEAcdfOpjbOa1sD8U9KBMMVY_c8FoHjSGR9GQbg,730
|
|
@@ -93,24 +93,24 @@ bbot/modules/dnscaa.py,sha256=pyaLqHrdsVhqtd1JBZVjKKcuYT_ywUbFYkrnfXcGD5s,5014
|
|
|
93
93
|
bbot/modules/dnscommonsrv.py,sha256=gEErfSur7Odkaif4CbXYx3OZ3FQrQESyiMGPbcDKSIg,1538
|
|
94
94
|
bbot/modules/dnsdumpster.py,sha256=bqUqyvRJVtoTXbDxTZ-kgPNq4dCE9xv_msBIn_Nj5IM,3251
|
|
95
95
|
bbot/modules/dnstlsrpt.py,sha256=ntNKVDXDgDVWr1A20ShNT5HFBhXsVEM5aUIEU_0c9HU,6427
|
|
96
|
-
bbot/modules/docker_pull.py,sha256=
|
|
96
|
+
bbot/modules/docker_pull.py,sha256=3Ui5z3pNfZDgX8q25h-LwKvUM7FDPST2dz1vk_I8gDc,9192
|
|
97
97
|
bbot/modules/dockerhub.py,sha256=JQkujjqvQRzQuvHjQ7JbFs_VlJj8dLRPRObAkBgUQhc,3493
|
|
98
98
|
bbot/modules/dotnetnuke.py,sha256=zipcHyNYr2FEecStb1Yrm938ps01RvHV8NnyqAvnGGc,10537
|
|
99
99
|
bbot/modules/emailformat.py,sha256=RLPJW-xitYB-VT4Lp08qVzFkXx_kMyV_035JT_Yf4fM,1082
|
|
100
100
|
bbot/modules/extractous.py,sha256=VSGKmHPAA_4r62jaN8Yqi3JcjehjxpI2lhe8i2j786s,4648
|
|
101
101
|
bbot/modules/ffuf.py,sha256=94TJ5xvqKwH0JaWmC_t1dLTpRsO8HEy4lnbsu8LF_HY,14965
|
|
102
102
|
bbot/modules/ffuf_shortnames.py,sha256=y5vnypLPN-KrjpmoG5zlqcX8VwfcLBpNg1yQI7bP9Hg,18737
|
|
103
|
-
bbot/modules/filedownload.py,sha256=
|
|
103
|
+
bbot/modules/filedownload.py,sha256=ZQZQCkXYb0lUP4DnEfL__8vc9KRi0m9hfBhClTKwy2U,8906
|
|
104
104
|
bbot/modules/fingerprintx.py,sha256=rdlR9d64AntAhbS_eJzh8bZCeLPTJPSKdkdKdhH_qAo,3269
|
|
105
105
|
bbot/modules/fullhunt.py,sha256=zeehQb9akBSbHW9dF4icH8Vfd8LqoTrpIvnQEEMWes8,1311
|
|
106
106
|
bbot/modules/generic_ssrf.py,sha256=KFdcHpUV9-Z7oN7emzbirimsNc2xZ_1IFqnsfIkEbcM,9196
|
|
107
107
|
bbot/modules/git.py,sha256=zmHeI0bn181T1P8C55HSebkdVGLTpzGxPc-LRqiHrbc,1723
|
|
108
|
-
bbot/modules/git_clone.py,sha256=
|
|
109
|
-
bbot/modules/gitdumper.py,sha256=
|
|
108
|
+
bbot/modules/git_clone.py,sha256=w-s3O6rZL_I8_BuPKotnAzXKnn7saw159jcQ_R1xtKw,2602
|
|
109
|
+
bbot/modules/gitdumper.py,sha256=d2FnSAraWftkQ9ENmkBk3_kUbuw4G3eKpsi0FXLzBss,12042
|
|
110
110
|
bbot/modules/github_codesearch.py,sha256=a-r2vE9N9WyBpFUiKCsg0TK4Qn7DaEGyVRTUKzkDLWA,3641
|
|
111
111
|
bbot/modules/github_org.py,sha256=WM18vJCHuOHJJ5rPzQzQ3Pmp7XPPuaMeVgNfW-FlO0k,8938
|
|
112
112
|
bbot/modules/github_usersearch.py,sha256=G8knkQBJsn7EKcMhcEaFPiB_Y5S96e2VaseBubsqOyk,3407
|
|
113
|
-
bbot/modules/github_workflows.py,sha256=
|
|
113
|
+
bbot/modules/github_workflows.py,sha256=xKntAFDeGuE4MqbEmhJyYXKbzoSh9tWYlHNlnF37PYA,10040
|
|
114
114
|
bbot/modules/gitlab.py,sha256=9oWWpBijeHCjuFBfWW4HvNqt7bvJvrBgBjaaz_UPPnE,5964
|
|
115
115
|
bbot/modules/google_playstore.py,sha256=N4QjzQag_bgDXfX17rytBiiWA-SQtYI2N0J_ZNEOdv0,3701
|
|
116
116
|
bbot/modules/gowitness.py,sha256=vcyZl87xJVHWTuRY032d2dORg3ykVKPIBwD2HzSxvYA,11784
|
|
@@ -147,7 +147,7 @@ bbot/modules/lightfuzz/submodules/xss.py,sha256=VP15TBeRjglIRjLvwmHJaOCNQOWS7R4W
|
|
|
147
147
|
bbot/modules/myssl.py,sha256=DoMF7o6MxIrcglCrC-W3nM-GPcyJRM4PlGdKfnOlIvs,942
|
|
148
148
|
bbot/modules/newsletters.py,sha256=1Q4JjShPsxHJ-by2CbGfCvEt80blUGPX0hxQIzB_a9M,2630
|
|
149
149
|
bbot/modules/ntlm.py,sha256=EGmb4k3YC_ZuHIU3mGUZ4yaMjE35wVQQSv8HwTsQJzY,4391
|
|
150
|
-
bbot/modules/nuclei.py,sha256=
|
|
150
|
+
bbot/modules/nuclei.py,sha256=65-tgre6a738Z2s3Bo0nxhNcvQ1OuYX8zQS5x7fFk7g,17983
|
|
151
151
|
bbot/modules/oauth.py,sha256=s-Q6PYJl1OLncGgHzCV0QAzbkewT5zzKCRaa8GidBqc,6720
|
|
152
152
|
bbot/modules/otx.py,sha256=GYi5GFLKlKuRHPYMqtq42bSulerkSpAWHM6ex5eK7ww,913
|
|
153
153
|
bbot/modules/output/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
@@ -179,9 +179,9 @@ bbot/modules/paramminer_headers.py,sha256=GMErLmTO0w7JRIpJE2VFvRTrjmoux_-jTx3Efa
|
|
|
179
179
|
bbot/modules/passivetotal.py,sha256=uGT6c_CUxBNInmClsTg8afIYA2ZykKYYCgjkyzujfHg,1653
|
|
180
180
|
bbot/modules/pgp.py,sha256=Xu2M9WEIlwTm5-Lv29g7BblI05tD9Dl0XsYSeY6UURs,2065
|
|
181
181
|
bbot/modules/portfilter.py,sha256=3iu4xqCsHafhVMbA32Mw6K_7Yn576Rz6GxXMevZQEpM,1752
|
|
182
|
-
bbot/modules/portscan.py,sha256=
|
|
182
|
+
bbot/modules/portscan.py,sha256=dmR2H3Wag6vLLIXrwyU_7bvYuLIv3zdJZ2japogwFUU,13076
|
|
183
183
|
bbot/modules/postman.py,sha256=vo761Nzu3kPBzfCY3KJcvsGEsjImaa7iA2z-LyASBDc,4589
|
|
184
|
-
bbot/modules/postman_download.py,sha256=
|
|
184
|
+
bbot/modules/postman_download.py,sha256=4pvF1ePRqxPxXL1jhI0gMf1CasfVFl-xjwU-U22SFXA,3607
|
|
185
185
|
bbot/modules/rapiddns.py,sha256=uONESr0B5pv9cSAr7lF4WWV31APUhXyHexvI04rUcyk,787
|
|
186
186
|
bbot/modules/reflected_parameters.py,sha256=RjS-4C-XC9U-jC9J7AYNqwn6I-O2y3LvTRhB68dpgKI,3281
|
|
187
187
|
bbot/modules/report/affiliates.py,sha256=vvus8LylqOfP-lfGid0z4FS6MwOpNuRTcSJ9aSnybp4,1713
|
|
@@ -208,7 +208,7 @@ bbot/modules/templates/sql.py,sha256=o-CdyyoJvHJdJBKkj3CIGXYxUta4w2AB_2Vr-k7cDDU
|
|
|
208
208
|
bbot/modules/templates/subdomain_enum.py,sha256=epyKSly08jqaINV_AMMWbNafIeQjJqvd3aj63KD0Mck,8402
|
|
209
209
|
bbot/modules/templates/webhook.py,sha256=uGFmcJ81GzGN1UI2k2O7nQF_fyh4ehLDEg2NSXaPnhk,3373
|
|
210
210
|
bbot/modules/trickest.py,sha256=MRgLW0YiDWzlWdAjyqfPPLFb-a51r-Ffn_dphiJI_gA,1550
|
|
211
|
-
bbot/modules/trufflehog.py,sha256=
|
|
211
|
+
bbot/modules/trufflehog.py,sha256=TA8TVj9he12I9TtVvHdH_WJwDqLK97mATwtkuSi3l2g,8703
|
|
212
212
|
bbot/modules/url_manipulation.py,sha256=4J3oFkqTSJPPmbKEKAHJg2Q2w4QNKtQhiN03ZJq5VtI,4326
|
|
213
213
|
bbot/modules/urlscan.py,sha256=-w_3Bm6smyG2GLQyIbnMUkKmeQVauo-V6F4_kJDYG7s,3740
|
|
214
214
|
bbot/modules/vhost.py,sha256=cirOe0HR4M0TEBN8JdXo2l0s2flc8ZSdxggGm79blT8,5459
|
|
@@ -250,15 +250,15 @@ bbot/scanner/__init__.py,sha256=sJ7FoLQ1vwLscH8hju2PEUyGTZ_OwMVvW9b11CrCWdI,89
|
|
|
250
250
|
bbot/scanner/dispatcher.py,sha256=_hsIegfUDrt8CUdXqgRvp1J0UwwzqVSDxjQmiviO41c,793
|
|
251
251
|
bbot/scanner/manager.py,sha256=eyd_0IjnPH3e-tJSOwY-rxauVI6L9Ltr3pWmpPSO5Jc,11019
|
|
252
252
|
bbot/scanner/preset/__init__.py,sha256=If_YqKILIxjlaJvf8lFc5zQTHDkounLdC8x_72N-V10,49
|
|
253
|
-
bbot/scanner/preset/args.py,sha256=
|
|
253
|
+
bbot/scanner/preset/args.py,sha256=Oto4sO8E9hKeQn6Fp8ua_WB3xvYI97GgnBFg5f4jh0Y,19547
|
|
254
254
|
bbot/scanner/preset/conditions.py,sha256=hFL9cSIWGEsv2TfM5UGurf0c91cyaM8egb5IngBmIjA,1569
|
|
255
255
|
bbot/scanner/preset/environ.py,sha256=9KbEOLWkUdoAf5Ez_2A1NNm6QduQElbnNnrPi6VDhZs,4731
|
|
256
256
|
bbot/scanner/preset/path.py,sha256=X32-ZUmL7taIv37VKF1KfmeiK9fjuQOE7pWUTEbPK8c,2483
|
|
257
|
-
bbot/scanner/preset/preset.py,sha256=
|
|
258
|
-
bbot/scanner/scanner.py,sha256=
|
|
257
|
+
bbot/scanner/preset/preset.py,sha256=G_aMMI33d2OlzNUwjfi5ddJdxa8nK0oF5HrYAsuregU,40708
|
|
258
|
+
bbot/scanner/scanner.py,sha256=6TgnGMa_Zl4Wm6fKxujGDHGrNC6ad1pexUDCZ7xRy5I,55320
|
|
259
259
|
bbot/scanner/stats.py,sha256=re93sArKXZSiD0Owgqk2J3Kdvfm3RL4Y9Qy_VOcaVk8,3623
|
|
260
260
|
bbot/scanner/target.py,sha256=lI0Tn5prQiPiJE3WW-ZLx_l6EFqzAVabtyL-nfXJ8cE,10636
|
|
261
|
-
bbot/scripts/docs.py,sha256=
|
|
261
|
+
bbot/scripts/docs.py,sha256=aYAHlcHtMAhM-XGTDiSpzccnX1dh0Xi_WxmC2bgylQ4,11373
|
|
262
262
|
bbot/test/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
263
263
|
bbot/test/bbot_fixtures.py,sha256=XrCQDLVe80BG3QTUDnXb0y-cWnBpJJoRh2Z3J3xJn_w,9961
|
|
264
264
|
bbot/test/conftest.py,sha256=OacpJ98g00HqCoHpEnuzzMK47LkbZdJWr25Pm0SbTM0,11783
|
|
@@ -289,7 +289,7 @@ bbot/test/test_step_1/test_modules_basic.py,sha256=ELpGlsthSq8HaxB5My8-ESVHqMxqd
|
|
|
289
289
|
bbot/test/test_step_1/test_presets.py,sha256=HnJhKwDnVh9Y6adgxqe85677rWpnFil_WS5GjX21ZvM,40959
|
|
290
290
|
bbot/test/test_step_1/test_python_api.py,sha256=Fk5bxEsPSjsMZ_CcRMTJft8I48EizwHJivG9Fy4jIu0,5502
|
|
291
291
|
bbot/test/test_step_1/test_regexes.py,sha256=wSx_e6hgHuBh95igL_fauWKK4a1xXujs9TtyLBaMwRM,14636
|
|
292
|
-
bbot/test/test_step_1/test_scan.py,sha256=
|
|
292
|
+
bbot/test/test_step_1/test_scan.py,sha256=jGySAMGb0gXirJpDniX6SSm4TbpHYrkGHcDPpJRGHQg,10676
|
|
293
293
|
bbot/test/test_step_1/test_scope.py,sha256=S2nssENKJKCvgXUMyU8MFQmXHeUIz0C_sbWGkdYti2A,3063
|
|
294
294
|
bbot/test/test_step_1/test_target.py,sha256=4Xz6Fns_6wa2O3AXDBvd7W04LCfZSCiit2lezQJicTI,19472
|
|
295
295
|
bbot/test/test_step_1/test_web.py,sha256=qzMb5v_1l6fK6SvJZoHpBI3Zb7iaHU_VnenQ8UQIK-4,19637
|
|
@@ -301,7 +301,7 @@ bbot/test/test_step_2/module_tests/test_module_affiliates.py,sha256=d6uAzb_MF4oN
|
|
|
301
301
|
bbot/test/test_step_2/module_tests/test_module_aggregate.py,sha256=hjxbMxAEFhS7W8RamBrM1t6T-tsLHq95MmQVfrYsock,487
|
|
302
302
|
bbot/test/test_step_2/module_tests/test_module_ajaxpro.py,sha256=S2pFV0TgOJ01SMHnIxcoBkGZ8SAaQVY9o32DOFoZ1u4,3857
|
|
303
303
|
bbot/test/test_step_2/module_tests/test_module_anubisdb.py,sha256=y_GMm20Fy4z9L0fN2dYOExaSsi8Z9PwMKnAjSsYhBk8,545
|
|
304
|
-
bbot/test/test_step_2/module_tests/test_module_apkpure.py,sha256
|
|
304
|
+
bbot/test/test_step_2/module_tests/test_module_apkpure.py,sha256=-G4hoRPFhNH-rj6MvuhqlwiXHqBqHgfFGYxbzoyKz2s,2983
|
|
305
305
|
bbot/test/test_step_2/module_tests/test_module_asn.py,sha256=qIbitSAEAmYyxhpxvdFDsQrHaaxfgKsFox9Q3jTmvgI,10616
|
|
306
306
|
bbot/test/test_step_2/module_tests/test_module_asset_inventory.py,sha256=NEMSPBlczpA5NbQpIcRBpRXpVnfS9lmSS1U7eJmIXAU,3878
|
|
307
307
|
bbot/test/test_step_2/module_tests/test_module_azure_realm.py,sha256=gPRvGA9RHsAcYlHxQG0lHZOYolyzbe6L-ALGIvW-Mg0,1288
|
|
@@ -315,7 +315,7 @@ bbot/test/test_step_2/module_tests/test_module_binaryedge.py,sha256=tK1fv4o2JOxa
|
|
|
315
315
|
bbot/test/test_step_2/module_tests/test_module_bucket_amazon.py,sha256=bKTnrR3FfCaAXZTWJh2m4bm-Pa1xiJV9lY2N_waaq2s,4002
|
|
316
316
|
bbot/test/test_step_2/module_tests/test_module_bucket_azure.py,sha256=CUK1OHGhA-piIzPG15UNiMdaK1oSSCwCv_WPcCvC4VQ,2112
|
|
317
317
|
bbot/test/test_step_2/module_tests/test_module_bucket_digitalocean.py,sha256=EFzScFloY6DQVD7MbvQOfsSp_8tApV4epKFm9OP1_ZM,907
|
|
318
|
-
bbot/test/test_step_2/module_tests/test_module_bucket_file_enum.py,sha256=
|
|
318
|
+
bbot/test/test_step_2/module_tests/test_module_bucket_file_enum.py,sha256=7n7eFWQJanZjyammwBDM9Jmt5kJ8AXn9eS4fmKEBqm4,2463
|
|
319
319
|
bbot/test/test_step_2/module_tests/test_module_bucket_firebase.py,sha256=gM3h1staY3tEHF2l9cYgRhaVwEg7ykfo4E0mvhqTA0g,506
|
|
320
320
|
bbot/test/test_step_2/module_tests/test_module_bucket_google.py,sha256=wXROpF9TSQVOa8cGTOo8k9uDEj7H5pNAcppj4WR3qnY,1312
|
|
321
321
|
bbot/test/test_step_2/module_tests/test_module_bufferoverrun.py,sha256=6pyJ0dbx8u0BPVXyiqdDPgECLGfIGkWPHZ35tbBzoPg,1582
|
|
@@ -342,7 +342,7 @@ bbot/test/test_step_2/module_tests/test_module_dnscommonsrv.py,sha256=_dqcgVQAc1
|
|
|
342
342
|
bbot/test/test_step_2/module_tests/test_module_dnsdumpster.py,sha256=p--1JaDb7PjiAYRQiAQ_qYBQAQgkBzB1iLnDp6B0UCk,59745
|
|
343
343
|
bbot/test/test_step_2/module_tests/test_module_dnsresolve.py,sha256=15LEcggP_eVYFQdMO1zHTvoGc6n8IaUjsQDmX0sZS4o,2077
|
|
344
344
|
bbot/test/test_step_2/module_tests/test_module_dnstlsrpt.py,sha256=8xXSFo0vwKfehIqgF41tbEkL1vbp6RIB8kiO8TSH4NU,2648
|
|
345
|
-
bbot/test/test_step_2/module_tests/test_module_docker_pull.py,sha256
|
|
345
|
+
bbot/test/test_step_2/module_tests/test_module_docker_pull.py,sha256=SKc43IKzwoi74qaY8felJXnrgNVXrXRQlg6BtrSjyAc,28151
|
|
346
346
|
bbot/test/test_step_2/module_tests/test_module_dockerhub.py,sha256=9T8CFcFP32MOppUmSVNBUSifnk2kMONqzW_7vvvKdpk,3907
|
|
347
347
|
bbot/test/test_step_2/module_tests/test_module_dotnetnuke.py,sha256=Q7M3hrbEwOuORZXPS-pIGFTRzB2-g4cEvGtsEcTp7t8,8049
|
|
348
348
|
bbot/test/test_step_2/module_tests/test_module_emailformat.py,sha256=cKxBPnEQ4AiRKV_-hSYEE6756ypst3hi6MN0L5RTukY,461
|
|
@@ -351,13 +351,13 @@ bbot/test/test_step_2/module_tests/test_module_excavate.py,sha256=hoVQnZYb_tI1Fl
|
|
|
351
351
|
bbot/test/test_step_2/module_tests/test_module_extractous.py,sha256=PuTE5rkEIFPwU9lhCYpTgNSkrVjcXm8PClbfOkfRS84,17973
|
|
352
352
|
bbot/test/test_step_2/module_tests/test_module_ffuf.py,sha256=z8ihAM1WYss7QGXIjbi67cekg8iOemDjaM8YR9_qSEs,4100
|
|
353
353
|
bbot/test/test_step_2/module_tests/test_module_ffuf_shortnames.py,sha256=0-a9J-gq8bUtmxl_-QPVidwZ9KkCvgvoG30Ot3a8lqM,8406
|
|
354
|
-
bbot/test/test_step_2/module_tests/test_module_filedownload.py,sha256=
|
|
354
|
+
bbot/test/test_step_2/module_tests/test_module_filedownload.py,sha256=Fd_5DKA0VaHyCR6qS5WPS9CoyPKFeBTswhAwM5RsU-c,4473
|
|
355
355
|
bbot/test/test_step_2/module_tests/test_module_fingerprintx.py,sha256=nU3jxbkGcmPYiSzc6thJhNvjAFb4qVxcR7rkOAvjB18,445
|
|
356
356
|
bbot/test/test_step_2/module_tests/test_module_fullhunt.py,sha256=NblfNHQrE82j-cESvm66hpN-ooKZwR1kEwJDTk_BXac,1946
|
|
357
357
|
bbot/test/test_step_2/module_tests/test_module_generic_ssrf.py,sha256=ZhfZpH0QTl6_YftGoZzZk6_2x0ZDnWjZ7vNZMTibBHw,3228
|
|
358
358
|
bbot/test/test_step_2/module_tests/test_module_git.py,sha256=gyBS3vZUWAyatGlcY26mGOYeqXSqJA5pbhJWgTmLqNo,1656
|
|
359
|
-
bbot/test/test_step_2/module_tests/test_module_git_clone.py,sha256=
|
|
360
|
-
bbot/test/test_step_2/module_tests/test_module_gitdumper.py,sha256=
|
|
359
|
+
bbot/test/test_step_2/module_tests/test_module_git_clone.py,sha256=z97IZLTVaa3aJ9O3NDP3MaOnmDocL-kT8kT-1rMAqn0,13318
|
|
360
|
+
bbot/test/test_step_2/module_tests/test_module_gitdumper.py,sha256=bsSly-OOyDuFffVmwJpm7aEafrSHgLAk_VsJ69S3LUo,17616
|
|
361
361
|
bbot/test/test_step_2/module_tests/test_module_github_codesearch.py,sha256=M50xBiGG2EuPGXDJU6uFsSUE-fhqZl3CzYtNdszW7LM,4735
|
|
362
362
|
bbot/test/test_step_2/module_tests/test_module_github_org.py,sha256=5tKO6NH4TPBeIdeTf7Bz9PUZ1pcvKsjrG0nFhc3YgT0,25458
|
|
363
363
|
bbot/test/test_step_2/module_tests/test_module_github_usersearch.py,sha256=IIQ0tYZjQN8_L8u_N4m8Nz3kbB4IyBp95tYCPcQeScg,5264
|
|
@@ -395,10 +395,10 @@ bbot/test/test_step_2/module_tests/test_module_paramminer_headers.py,sha256=8hRU
|
|
|
395
395
|
bbot/test/test_step_2/module_tests/test_module_passivetotal.py,sha256=fTGQECQ0OzcwiH64-0igFRKO-rs3kXScivZord_oWWU,1120
|
|
396
396
|
bbot/test/test_step_2/module_tests/test_module_pgp.py,sha256=_T-kmpr5F0cJHl4_mpfhxK3aj0hH3UxDLeV1iByanJk,1607
|
|
397
397
|
bbot/test/test_step_2/module_tests/test_module_portfilter.py,sha256=gOEy1XYtTJNGvTH6o3NNSOXXPdste462BdQvayOwzVs,2012
|
|
398
|
-
bbot/test/test_step_2/module_tests/test_module_portscan.py,sha256=
|
|
398
|
+
bbot/test/test_step_2/module_tests/test_module_portscan.py,sha256=I42EhBN736JShaek5TvE-NHD8X3hcVxXLHhlJcqquYQ,7539
|
|
399
399
|
bbot/test/test_step_2/module_tests/test_module_postgres.py,sha256=bNHzDvPs5AkoA_ho7s50bFaF5qzV7KL3DplhOA1ZYa4,2688
|
|
400
400
|
bbot/test/test_step_2/module_tests/test_module_postman.py,sha256=7SxZi39dJhnwyg4IVf5M8VNdQU_zaPAufTEw60rgCkg,22311
|
|
401
|
-
bbot/test/test_step_2/module_tests/test_module_postman_download.py,sha256=
|
|
401
|
+
bbot/test/test_step_2/module_tests/test_module_postman_download.py,sha256=u3lF7Uve8riHNCj9XEFrRBONQHUzccsfq-qEkwQ51wE,14225
|
|
402
402
|
bbot/test/test_step_2/module_tests/test_module_python.py,sha256=6UQVXGJ1ugfNbt9l_nN0q5FVxNWlpq6j0sZcB0Nh_Pg,184
|
|
403
403
|
bbot/test/test_step_2/module_tests/test_module_rapiddns.py,sha256=zXHNLnUjLO22yRwrDFCZ40sRTmFVZEj9q_dyK8w1TYM,4441
|
|
404
404
|
bbot/test/test_step_2/module_tests/test_module_reflected_parameters.py,sha256=4cY8yK9iImB1O68pi1lACcPEtNQ9-sud9Xl16fYB8cU,9003
|
|
@@ -423,7 +423,7 @@ bbot/test/test_step_2/module_tests/test_module_subdomains.py,sha256=r1zCmw5ZZ_0w
|
|
|
423
423
|
bbot/test/test_step_2/module_tests/test_module_teams.py,sha256=r91ZZxhj3pEhKnjr1jGwhcqOPXTqNJNupC1CDKccfH8,1638
|
|
424
424
|
bbot/test/test_step_2/module_tests/test_module_telerik.py,sha256=vGORDSRU1S1hkLlHO3KOdoAYLqWcrPpvVGxadTGl5y8,11099
|
|
425
425
|
bbot/test/test_step_2/module_tests/test_module_trickest.py,sha256=6mTYH6fIah-WbKnFI-_WZBwRdKFi-oeWyVtl1n0nVAU,1630
|
|
426
|
-
bbot/test/test_step_2/module_tests/test_module_trufflehog.py,sha256=
|
|
426
|
+
bbot/test/test_step_2/module_tests/test_module_trufflehog.py,sha256=lXX2KYh5s1FVpivth43Nj3MLfMkiksrooEne_DAL_qg,96796
|
|
427
427
|
bbot/test/test_step_2/module_tests/test_module_txt.py,sha256=R-EBfEZM0jwY2yuVyfYhoccDOl0Y2uQZSkXQ1HyinUA,247
|
|
428
428
|
bbot/test/test_step_2/module_tests/test_module_unarchive.py,sha256=--p2kpnyfbABuJI5qmDbD-K_D5e4Icvq5VzEyp6AWVE,10676
|
|
429
429
|
bbot/test/test_step_2/module_tests/test_module_url_manipulation.py,sha256=aP3nK2TQQOjk0ZeuHhHYfZm_e37qrrXbnufd7m-QeJU,1144
|
|
@@ -452,8 +452,8 @@ bbot/wordlists/raft-small-extensions-lowercase_CLEANED.txt,sha256=ZSIVebs7ptMvHx
|
|
|
452
452
|
bbot/wordlists/top_open_ports_nmap.txt,sha256=LmdFYkfapSxn1pVuQC2LkOIY2hMLgG-Xts7DVtYzweM,42727
|
|
453
453
|
bbot/wordlists/valid_url_schemes.txt,sha256=0B_VAr9Dv7aYhwi6JSBDU-3M76vNtzN0qEC_RNLo7HE,3310
|
|
454
454
|
bbot/wordlists/wordninja_dns.txt.gz,sha256=DYHvvfW0TvzrVwyprqODAk4tGOxv5ezNmCPSdPuDUnQ,570241
|
|
455
|
-
bbot-2.4.2.
|
|
456
|
-
bbot-2.4.2.
|
|
457
|
-
bbot-2.4.2.
|
|
458
|
-
bbot-2.4.2.
|
|
459
|
-
bbot-2.4.2.
|
|
455
|
+
bbot-2.4.2.6706rc0.dist-info/LICENSE,sha256=GzeCzK17hhQQDNow0_r0L8OfLpeTKQjFQwBQU7ZUymg,32473
|
|
456
|
+
bbot-2.4.2.6706rc0.dist-info/METADATA,sha256=7rjBEj5pcTefGTU6PY90E3IBhUd5zt-fnQIWvZGoJto,18308
|
|
457
|
+
bbot-2.4.2.6706rc0.dist-info/WHEEL,sha256=b4K_helf-jlQoXBBETfwnf4B04YC67LOev0jo4fX5m8,88
|
|
458
|
+
bbot-2.4.2.6706rc0.dist-info/entry_points.txt,sha256=cWjvcU_lLrzzJgjcjF7yeGuRA_eDS8pQ-kmPUAyOBfo,38
|
|
459
|
+
bbot-2.4.2.6706rc0.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|