Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Dev --> Stable 1.1.2 #777

Merged
merged 67 commits into from
Nov 3, 2023
Merged
Show file tree
Hide file tree
Changes from 59 commits
Commits
Show all changes
67 commits
Select commit Hold shift + click to select a range
7d0ff24
filedownload module
TheTechromancer Oct 11, 2023
af70529
filedownload web helpers
TheTechromancer Oct 11, 2023
efda63f
filedownload module tests
TheTechromancer Oct 11, 2023
70c00c5
finishing up filedownload helpers, ssl verification consolidation
TheTechromancer Oct 11, 2023
8f338a3
Created and added dehashed module. Built out the primary function of …
SpamFaux May 4, 2023
1ee1caf
Created a credshed and dehashed module for bbot.
SpamFaux Sep 28, 2023
0d89357
resolved conflicts
TheTechromancer Oct 12, 2023
90002e9
removed debugging statement
TheTechromancer Oct 11, 2023
5eaf82e
removed debugging statement
TheTechromancer Oct 11, 2023
82748d9
resolved conflicts
TheTechromancer Oct 12, 2023
3d316ca
dehashed module tests
TheTechromancer Oct 12, 2023
d4568bb
credshed module tests, optimizations
TheTechromancer Oct 12, 2023
ee94867
flake8
TheTechromancer Oct 12, 2023
8800515
update README
TheTechromancer Oct 12, 2023
d9c28a3
move module list to bottom of readme
TheTechromancer Oct 12, 2023
a08f854
blacked
TheTechromancer Oct 12, 2023
e4f2617
Merge pull request #774 from blacklanternsecurity/filedownload-module-2
TheTechromancer Oct 13, 2023
0c006dd
Refresh module docs
blsaccess Oct 13, 2023
6b4a321
small bugfix for asset_inventory
TheTechromancer Oct 13, 2023
49f9f3e
Merge pull request #765 from blacklanternsecurity/pw_modules
TheTechromancer Oct 13, 2023
6baa563
Merge pull request #776 from blacklanternsecurity/update-readme
TheTechromancer Oct 13, 2023
0bad648
Refresh module docs
blsaccess Oct 13, 2023
eb08f7e
bumped version 1.1.1 --> 1.1.2
TheTechromancer Oct 13, 2023
5170e87
excavate CSP submodule functional
liquidsec Oct 15, 2023
864c933
removed uncessary set
liquidsec Oct 15, 2023
4ee94ed
added missing dev docs
TheTechromancer Oct 17, 2023
84c2dc4
fix console hang
TheTechromancer Oct 17, 2023
c2ed0a6
Merge pull request #788 from blacklanternsecurity/fix-console-hang
TheTechromancer Oct 17, 2023
1655bc7
add microsoft on-prem subdomains
TheTechromancer Oct 17, 2023
ca89535
undo debugging changes
TheTechromancer Oct 17, 2023
5e3a57a
fix ssl verification issues
TheTechromancer Oct 17, 2023
c7aca03
updated release history
TheTechromancer Oct 17, 2023
6b1e320
fix test_modules_basic tests
TheTechromancer Oct 17, 2023
f32448a
working on tests
TheTechromancer Oct 18, 2023
2f08f7f
Merge pull request #791 from blacklanternsecurity/fix-ssl-issues
TheTechromancer Oct 18, 2023
6ded074
Merge pull request #790 from blacklanternsecurity/ms-on-prem-subdomains
TheTechromancer Oct 18, 2023
93bd1be
Merge pull request #781 from blacklanternsecurity/bump-version
TheTechromancer Oct 18, 2023
bb80806
Merge pull request #780 from blacklanternsecurity/asset-inventory-sta…
TheTechromancer Oct 18, 2023
663cf44
emitting DNS_NAME instead
liquidsec Oct 19, 2023
3a46dca
Merge pull request #783 from blacklanternsecurity/csp-excavate
liquidsec Oct 20, 2023
896db82
added a bunch of azure domains
TheTechromancer Oct 22, 2023
5f217b7
Merge pull request #797 from blacklanternsecurity/more-cloud-domains
TheTechromancer Oct 22, 2023
9f52a1b
allow override of batch_size / max_event_handlers
TheTechromancer Oct 23, 2023
8c5f3e5
allow override of batch_size / max_event_handlers
TheTechromancer Oct 23, 2023
11bdbe8
fix bug
TheTechromancer Oct 23, 2023
5c16e5f
Adding nuclei retries / batchsize option, test rework
liquidsec Oct 23, 2023
e5887b1
Merge pull request #798 from blacklanternsecurity/batch-size-override
TheTechromancer Oct 23, 2023
b42d845
Merge branch 'dev' into nuclei_additional_options
liquidsec Oct 23, 2023
8de8343
removing unnecessary setup
liquidsec Oct 23, 2023
0259450
Merge pull request #799 from blacklanternsecurity/nuclei_additional_o…
TheTechromancer Oct 23, 2023
43aa8b4
Update release_history.md
TheTechromancer Oct 23, 2023
67db834
Refresh module docs
blsaccess Oct 23, 2023
0c699e1
update docs
TheTechromancer Oct 24, 2023
560c899
fix readme gif
TheTechromancer Oct 24, 2023
ffc4a18
update README
TheTechromancer Oct 24, 2023
2e86588
Update release_history.md
TheTechromancer Oct 24, 2023
b25496f
Merge pull request #801 from blacklanternsecurity/update-readme
liquidsec Oct 24, 2023
3438beb
Update release_history.md
TheTechromancer Oct 24, 2023
606c570
Merge pull request #792 from blacklanternsecurity/update-release-history
TheTechromancer Oct 24, 2023
cb76846
Refresh module docs
blsaccess Oct 24, 2023
2e05822
fix wappalyzer tests to work on python 3.12
TheTechromancer Oct 31, 2023
892beee
Merge pull request #810 from blacklanternsecurity/fix-wappalyzer
TheTechromancer Oct 31, 2023
bfe4d01
Fix inconsistency with 404 URLs
TheTechromancer Oct 31, 2023
c77c809
Offload wayback URL parsing into separate process
TheTechromancer Oct 31, 2023
1e8c6bd
increase max tasks per child
TheTechromancer Oct 31, 2023
6c8e8ae
Merge pull request #813 from blacklanternsecurity/wayback-json-parsing
TheTechromancer Nov 1, 2023
a5df7b3
Merge pull request #808 from blacklanternsecurity/fix-404-inconsistency
TheTechromancer Nov 1, 2023
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
121 changes: 117 additions & 4 deletions README.md

Large diffs are not rendered by default.

48 changes: 27 additions & 21 deletions bbot/cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,8 @@
import sys
import asyncio
import logging
import threading
import traceback
from aioconsole import ainput
from omegaconf import OmegaConf
from contextlib import suppress

Expand Down Expand Up @@ -306,36 +306,42 @@ async def _main():
log.hugesuccess(f"Scan ready. Press enter to execute {scanner.name}")
input()

def keyboard_listen():
allowed_errors = 10
def handle_keyboard_input(keyboard_input):
kill_regex = re.compile(r"kill (?P<module>[a-z0-9_]+)")
if keyboard_input:
log.verbose(f'Got keyboard input: "{keyboard_input}"')
kill_match = kill_regex.match(keyboard_input)
if kill_match:
module = kill_match.group("module")
if module in scanner.modules:
log.hugewarning(f'Killing module: "{module}"')
scanner.manager.kill_module(module, message="killed by user")
else:
log.warning(f'Invalid module: "{module}"')
else:
toggle_log_level(logger=log)
scanner.manager.modules_status(_log=True)

async def akeyboard_listen():
allowed_errors = 10
while 1:
keyboard_input = "a"
try:
keyboard_input = input()
allowed_errors = 10
keyboard_input = await ainput()
except Exception:
allowed_errors -= 1
if keyboard_input:
log.verbose(f'Got keyboard input: "{keyboard_input}"')
kill_match = kill_regex.match(keyboard_input)
if kill_match:
module = kill_match.group("module")
if module in scanner.modules:
log.hugewarning(f'Killing module: "{module}"')
scanner.manager.kill_module(module, message="killed by user")
else:
log.warning(f'Invalid module: "{module}"')
else:
toggle_log_level(logger=log)
scanner.manager.modules_status(_log=True)
handle_keyboard_input(keyboard_input)
if allowed_errors <= 0:
break

keyboard_listen_thread = threading.Thread(target=keyboard_listen, daemon=True)
keyboard_listen_thread.start()
try:
keyboard_listen_task = asyncio.create_task(akeyboard_listen())

await scanner.async_start_without_generator()
await scanner.async_start_without_generator()
finally:
keyboard_listen_task.cancel()
with suppress(asyncio.CancelledError):
await keyboard_listen_task

except bbot.core.errors.ScanError as e:
log_to_stderr(str(e), level="ERROR")
Expand Down
21 changes: 15 additions & 6 deletions bbot/core/configurator/__init__.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import os
import re
import sys
from pathlib import Path
from omegaconf import OmegaConf
Expand Down Expand Up @@ -41,20 +42,28 @@
sentinel = object()


exclude_from_validation = re.compile(r".*modules\.[a-z0-9_]+\.(?:batch_size|max_event_handlers)$")


def check_cli_args():
for c in args.cli_config:
if not is_file(c):
c = c.split("=")[0].strip()
v = OmegaConf.select(default_config, c, default=sentinel)
if v is sentinel:
conf = [a for a in args.cli_config if not is_file(a)]
all_options = None
for c in conf:
c = c.split("=")[0].strip()
v = OmegaConf.select(default_config, c, default=sentinel)
# if option isn't in the default config
if v is sentinel:
if exclude_from_validation.match(c):
continue
if all_options is None:
from ...modules import module_loader

modules_options = set()
for module_options in module_loader.modules_options().values():
modules_options.update(set(o[0] for o in module_options))
global_options = set(default_config.keys()) - {"modules", "output_modules"}
all_options = global_options.union(modules_options)
match_and_exit(c, all_options, msg="module option")
match_and_exit(c, all_options, msg="module option")


def ensure_config_files():
Expand Down
12 changes: 12 additions & 0 deletions bbot/core/event/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -1129,6 +1129,18 @@ class GEOLOCATION(BaseEvent):
_always_emit = True


class PASSWORD(BaseEvent):
_always_emit = True


class HASHED_PASSWORD(BaseEvent):
_always_emit = True


class USERNAME(BaseEvent):
_always_emit = True


class SOCIAL(DictEvent):
_always_emit = True

Expand Down
24 changes: 23 additions & 1 deletion bbot/core/helpers/cloud/azure.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,25 +2,47 @@


class Azure(BaseCloudProvider):
# mostly pulled from https://learn.microsoft.com/en-us/azure/azure-government/compare-azure-government-global-azure
domains = [
"azconfig.io",
"azmk8s.io",
"azure-api.net",
"azure-api.us",
"azure-automation.net",
"azure-automation.us",
"azure-devices.net",
"azure-devices.us",
"azure-mobile.net",
"azure.com",
"azure.net",
"azure.us",
"azurecontainer.io",
"azurecr.io",
"azurecr.us",
"azuredatalakestore.net",
"azureedge.net",
"azurefd.net",
"azurehdinsight.net",
"azurehdinsight.us",
"azurewebsites.net",
"botframework.com",
"cloudapp.net",
"windows.net",
"loganalytics.io",
"loganalytics.us",
"microsoft.us",
"microsoftonline.com",
"microsoftonline.us",
"onmicrosoft.com",
"powerbi.com",
"powerbigov.us",
"trafficmanager.net",
"usgovcloudapi.net",
"usgovtrafficmanager.net",
"visualstudio.com",
"vo.msecnd.net",
"windows.net",
"windowsazure.com",
"windowsazure.us",
]

bucket_name_regex = r"[a-z0-9][a-z0-9-_\.]{1,61}[a-z0-9]"
Expand Down
2 changes: 1 addition & 1 deletion bbot/core/helpers/regexes.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@
_hostname_regex = r"(?!\w*\.\w+)\w(?:[\w-]{0,100}\w)?"
hostname_regex = re.compile(r"^" + _hostname_regex + r"$", re.I)

_email_regex = r"(?:[^\W_][\w\-\.\+]{,100})@" + _dns_name_regex
_email_regex = r"(?:[^\W_][\w\-\.\+']{,100})@" + _dns_name_regex
email_regex = re.compile(_email_regex, re.I)

_ptr_regex = r"(?:[0-9]{1,3}[-_\.]){3}[0-9]{1,3}"
Expand Down
126 changes: 86 additions & 40 deletions bbot/core/helpers/web.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
import traceback
from pathlib import Path
from bs4 import BeautifulSoup
from contextlib import asynccontextmanager

from httpx._models import Cookies

Expand Down Expand Up @@ -131,7 +132,10 @@ class WebHelper:
def __init__(self, parent_helper):
self.parent_helper = parent_helper
self.http_debug = self.parent_helper.config.get("http_debug", False)
self._ssl_context_noverify = None
self.ssl_verify = self.parent_helper.config.get("ssl_verify", False)
if self.ssl_verify is False:
self.ssl_verify = self.ssl_context_noverify()
self.web_client = self.AsyncClient(persist_cookies=False)

def AsyncClient(self, *args, **kwargs):
Expand Down Expand Up @@ -216,7 +220,7 @@ async def request(self, *args, **kwargs):
if client_kwargs:
client = self.AsyncClient(**client_kwargs)

try:
async with self._acatch(url, raise_error):
if self.http_debug:
logstr = f"Web request: {str(args)}, {str(kwargs)}"
log.debug(logstr)
Expand All @@ -226,41 +230,6 @@ async def request(self, *args, **kwargs):
f"Web response from {url}: {response} (Length: {len(response.content)}) headers: {response.headers}"
)
return response
except httpx.PoolTimeout:
# this block exists because of this:
# https://github.com/encode/httpcore/discussions/783
log.verbose(f"PoolTimeout to URL: {url}")
self.web_client = self.AsyncClient(persist_cookies=False)
return await self.request(*args, **kwargs)
except httpx.TimeoutException:
log.verbose(f"HTTP timeout to URL: {url}")
if raise_error:
raise
except httpx.ConnectError:
log.verbose(f"HTTP connect failed to URL: {url}")
if raise_error:
raise
except httpx.RequestError as e:
log.trace(f"Error with request to URL: {url}: {e}")
log.trace(traceback.format_exc())
if raise_error:
raise
except ssl.SSLError as e:
msg = f"SSL error with request to URL: {url}: {e}"
log.trace(msg)
log.trace(traceback.format_exc())
if raise_error:
raise httpx.RequestError(msg)
except anyio.EndOfStream as e:
msg = f"AnyIO error with request to URL: {url}: {e}"
log.trace(msg)
log.trace(traceback.format_exc())
if raise_error:
raise httpx.RequestError(msg)
except BaseException as e:
log.trace(f"Unhandled exception with request to URL: {url}: {e}")
log.trace(traceback.format_exc())
raise

async def download(self, url, **kwargs):
"""
Expand All @@ -272,9 +241,11 @@ async def download(self, url, **kwargs):
url (str): The URL of the file to download.
filename (str, optional): The filename to save the downloaded file as.
If not provided, will generate based on URL.
max_size (str or int): Maximum filesize as a string ("5MB") or integer in bytes.
cache_hrs (float, optional): The number of hours to cache the downloaded file.
A negative value disables caching. Defaults to -1.
method (str, optional): The HTTP method to use for the request, defaults to 'GET'.
raise_error (bool, optional): Whether to raise exceptions for HTTP connect, timeout errors. Defaults to False.
**kwargs: Additional keyword arguments to pass to the httpx request.

Returns:
Expand All @@ -285,28 +256,48 @@ async def download(self, url, **kwargs):
"""
success = False
filename = kwargs.pop("filename", self.parent_helper.cache_filename(url))
follow_redirects = kwargs.pop("follow_redirects", True)
max_size = kwargs.pop("max_size", None)
warn = kwargs.pop("warn", True)
raise_error = kwargs.pop("raise_error", False)
if max_size is not None:
max_size = self.parent_helper.human_to_bytes(max_size)
cache_hrs = float(kwargs.pop("cache_hrs", -1))
total_size = 0
chunk_size = 8192
log.debug(f"Downloading file from {url} with cache_hrs={cache_hrs}")
if cache_hrs > 0 and self.parent_helper.is_cached(url):
log.debug(f"{url} is cached at {self.parent_helper.cache_filename(url)}")
success = True
else:
# kwargs["raise_error"] = True
# kwargs["stream"] = True
kwargs["follow_redirects"] = follow_redirects
if not "method" in kwargs:
kwargs["method"] = "GET"
try:
async with self.AsyncClient().stream(url=url, **kwargs) as response:
async with self._acatch(url, raise_error), self.AsyncClient().stream(url=url, **kwargs) as response:
status_code = getattr(response, "status_code", 0)
log.debug(f"Download result: HTTP {status_code}")
if status_code != 0:
response.raise_for_status()
with open(filename, "wb") as f:
async for chunk in response.aiter_bytes(chunk_size=8192):
agen = response.aiter_bytes(chunk_size=chunk_size)
async for chunk in agen:
if max_size is not None and total_size + chunk_size > max_size:
log.verbose(
f"Filesize of {url} exceeds {self.parent_helper.bytes_to_human(max_size)}, file will be truncated"
)
agen.aclose()
break
total_size += chunk_size
f.write(chunk)
success = True
except httpx.HTTPError as e:
log.warning(f"Failed to download {url}: {e}")
log_fn = log.verbose
if warn:
log_fn = log.warning
log_fn(f"Failed to download {url}: {e}")
return

if success:
Expand Down Expand Up @@ -465,7 +456,7 @@ async def curl(self, *args, **kwargs):
curl_command.append("--path-as-is")

# respect global ssl verify settings
if self.ssl_verify == False:
if self.ssl_verify is not True:
curl_command.append("-k")

headers = kwargs.get("headers", {})
Expand Down Expand Up @@ -574,6 +565,61 @@ def is_spider_danger(self, source_event, url):
return True
return False

def ssl_context_noverify(self):
if self._ssl_context_noverify is None:
ssl_context = ssl.create_default_context()
ssl_context.check_hostname = False
ssl_context.verify_mode = ssl.CERT_NONE
ssl_context.options &= ~ssl.OP_NO_SSLv2 & ~ssl.OP_NO_SSLv3
ssl_context.set_ciphers("ALL:@SECLEVEL=0")
ssl_context.options |= 0x4 # Add the OP_LEGACY_SERVER_CONNECT option
self._ssl_context_noverify = ssl_context
return self._ssl_context_noverify

@asynccontextmanager
async def _acatch(self, url, raise_error):
"""
Asynchronous context manager to handle various httpx errors during a request.

Yields:
None

Note:
This function is internal and should generally not be used directly.
`url`, `args`, `kwargs`, and `raise_error` should be in the same context as this function.
"""
try:
yield
except httpx.TimeoutException:
log.verbose(f"HTTP timeout to URL: {url}")
if raise_error:
raise
except httpx.ConnectError:
log.verbose(f"HTTP connect failed to URL: {url}")
if raise_error:
raise
except httpx.RequestError as e:
log.trace(f"Error with request to URL: {url}: {e}")
log.trace(traceback.format_exc())
if raise_error:
raise
except ssl.SSLError as e:
msg = f"SSL error with request to URL: {url}: {e}"
log.trace(msg)
log.trace(traceback.format_exc())
if raise_error:
raise httpx.RequestError(msg)
except anyio.EndOfStream as e:
msg = f"AnyIO error with request to URL: {url}: {e}"
log.trace(msg)
log.trace(traceback.format_exc())
if raise_error:
raise httpx.RequestError(msg)
except BaseException as e:
log.trace(f"Unhandled exception with request to URL: {url}: {e}")
log.trace(traceback.format_exc())
raise


user_keywords = [re.compile(r, re.I) for r in ["user", "login", "email"]]
pass_keywords = [re.compile(r, re.I) for r in ["pass"]]
Expand Down
Loading
Loading