diff --git a/bbot/cli.py b/bbot/cli.py index 4c45254d6..b2df0c9b9 100755 --- a/bbot/cli.py +++ b/bbot/cli.py @@ -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 @@ -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[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") diff --git a/docs/dev/helpers/command.md b/docs/dev/helpers/command.md new file mode 100644 index 000000000..a3da89c08 --- /dev/null +++ b/docs/dev/helpers/command.md @@ -0,0 +1,13 @@ +# Command Helpers + +These are helpers related to executing shell commands. They are used throughout BBOT and its modules for executing various binaries such as `nmap`, `nuclei`, etc. + +Note that these helpers can be invoked directly from `self.helpers`, e.g.: + +```python +self.helpers.run("ls", "-l") +``` + +::: bbot.core.helpers.command + options: + show_root_heading: false diff --git a/docs/dev/helpers/dns.md b/docs/dev/helpers/dns.md new file mode 100644 index 000000000..5a51d6116 --- /dev/null +++ b/docs/dev/helpers/dns.md @@ -0,0 +1,19 @@ +# DNS + +These are helpers related to DNS resolution. They are used throughout BBOT and its modules for performing DNS lookups and detecting DNS wildcards, etc. + +Note that these helpers can be invoked directly from `self.helpers`, e.g.: + +```python +self.helpers.resolve("evilcorp.com") +``` + +::: bbot.core.helpers.dns.DNSHelper + handler: python + options: + members: + - resolve + - resolve_batch + - resolve_raw + - is_wildcard + - is_wildcard_domain diff --git a/docs/dev/helpers/interactsh.md b/docs/dev/helpers/interactsh.md new file mode 100644 index 000000000..5431e6337 --- /dev/null +++ b/docs/dev/helpers/interactsh.md @@ -0,0 +1,5 @@ +# Interact.sh + +::: bbot.core.helpers.interactsh.Interactsh + options: + show_root_heading: false diff --git a/docs/dev/helpers/web.md b/docs/dev/helpers/web.md new file mode 100644 index 000000000..b25f0e7ca --- /dev/null +++ b/docs/dev/helpers/web.md @@ -0,0 +1,15 @@ +# Web + +These are helpers for making various web requests. + +Note that these helpers can be invoked directly from `self.helpers`, e.g.: + +```python +self.helpers.request("https://www.evilcorp.com") +``` + +::: bbot.core.helpers.web + options: + show_root_heading: false + members: + - WebHelper diff --git a/docs/dev/helpers/wordcloud.md b/docs/dev/helpers/wordcloud.md new file mode 100644 index 000000000..cc2d6671c --- /dev/null +++ b/docs/dev/helpers/wordcloud.md @@ -0,0 +1,13 @@ +# Word Cloud + +These are helpers related to BBOT's Word Cloud, a mechanism for storing target-specific keywords that are useful for custom wordlists, etc. + +Note that these helpers can be invoked directly from `self.helpers`, e.g.: + +```python +self.helpers.word_cloud +``` + +::: bbot.core.helpers.wordcloud + options: + show_root_heading: false diff --git a/poetry.lock b/poetry.lock index 55c25b105..8d407cff4 100644 --- a/poetry.lock +++ b/poetry.lock @@ -1,5 +1,16 @@ # This file is automatically @generated by Poetry 1.5.1 and should not be changed by hand. +[[package]] +name = "aioconsole" +version = "0.6.2" +description = "Asynchronous console and interfaces for asyncio" +optional = false +python-versions = ">=3.7" +files = [ + {file = "aioconsole-0.6.2-py3-none-any.whl", hash = "sha256:1968021eb03b88fcdf5f5398154b21585e941a7b98c9fcef51c4bb0158156619"}, + {file = "aioconsole-0.6.2.tar.gz", hash = "sha256:bac11286f1062613d2523ceee1ba81c676cd269812b865b66b907448a7b5f63e"}, +] + [[package]] name = "ansible" version = "7.7.0" @@ -2433,4 +2444,4 @@ testing = ["big-O", "jaraco.functools", "jaraco.itertools", "more-itertools", "p [metadata] lock-version = "2.0" python-versions = "^3.9" -content-hash = "2215f588e30cd553c593079522ce8b98beb344554688e62d2409237fd245f21d" +content-hash = "f1d4ab56691dfb6c47d2f31ad29c14c74d4f768b2f100d60fccc8167707426f0" diff --git a/pyproject.toml b/pyproject.toml index ee2a01c94..2c4bd2680 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -47,6 +47,7 @@ httpx = {extras = ["http2"], version = "^0.24.1"} dnspython = "^2.4.2" anyio = "4.0.0rc1" httpcore = "^0.17.3" +aioconsole = "^0.6.2" [tool.poetry.group.dev.dependencies] flake8 = "^6.0.0"