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

Developer Documentation #692

Merged
merged 29 commits into from
Sep 29, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
29 commits
Select commit Hold shift + click to select a range
a11e7b8
fix poetry.lock conflicts
TheTechromancer Sep 20, 2023
c36be66
documenting Scanner() class
TheTechromancer Sep 12, 2023
590b4a2
steadily working through helpers
TheTechromancer Sep 12, 2023
ae0f173
refactor module inheritance
TheTechromancer Sep 13, 2023
3753724
code cleanup in geoip modules
TheTechromancer Sep 13, 2023
0fd2ad2
fix tests
TheTechromancer Sep 13, 2023
4e3b107
resolve conflicts in modules/base.py
TheTechromancer Sep 22, 2023
8c8ff8d
tweak scanner docs
TheTechromancer Sep 13, 2023
9b2a7ce
documentation for Target
TheTechromancer Sep 14, 2023
d4d1cb3
documentation for ScanManager, Event
TheTechromancer Sep 14, 2023
baa1d46
fix agent tests
TheTechromancer Sep 14, 2023
8d03b1a
more scanner documentation
TheTechromancer Sep 14, 2023
b38ca86
even more scanner docs
TheTechromancer Sep 14, 2023
64380fe
blacked
TheTechromancer Sep 14, 2023
65041f5
resolve conflicts in helpers/misc.py
TheTechromancer Sep 22, 2023
6c51348
finished documenting misc helpers
TheTechromancer Sep 15, 2023
2f10635
steady work on developer documentation
TheTechromancer Sep 15, 2023
e6afb1c
fix tests, started interact.sh developer docs
TheTechromancer Sep 15, 2023
bf5fd41
interactsh developer documentation
TheTechromancer Sep 16, 2023
b6160e6
resolve conflicts in helpers/regexes.py
TheTechromancer Sep 22, 2023
c2dd699
docs for dns mutator, massdns module
TheTechromancer Sep 18, 2023
803386f
blacked
TheTechromancer Sep 18, 2023
14e78c3
add per_domain_only module attribute (cleaning up module inheritance)
TheTechromancer Sep 19, 2023
fefa3b5
remove unused root_domains template
TheTechromancer Sep 20, 2023
441caff
small update to docs index.md
TheTechromancer Sep 21, 2023
99e36eb
Add Discord Bot Example
TheTechromancer Sep 22, 2023
bce1335
update discord bot docs
TheTechromancer Sep 22, 2023
42a3ad2
cleaned up README
TheTechromancer Sep 22, 2023
8081379
cleaned up README
TheTechromancer Sep 22, 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
8 changes: 3 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,13 +18,9 @@ BBOT typically outperforms other subdomain enumeration tools by 20-25%. To learn

## Installation ([pip](https://pypi.org/project/bbot/))

For more installation methods including [Docker](https://hub.docker.com/r/blacklanternsecurity/bbot), see [Installation](https://www.blacklanternsecurity.com/bbot/#installation).
Note: Requires Linux and Python 3.9+. For more installation methods including [Docker](https://hub.docker.com/r/blacklanternsecurity/bbot), see [Installation](https://www.blacklanternsecurity.com/bbot/#installation).

```bash
# Prerequisites:
# - Linux (Windows and macOS are *not* supported)
# - Python 3.9 or newer

# stable version
pipx install bbot

Expand Down Expand Up @@ -120,6 +116,8 @@ For details, see [Configuration](https://www.blacklanternsecurity.com/bbot/scann

## BBOT as a Python library

BBOT exposes a Python API that allows it to be used for all kinds of fun and nefarious purposes, like a [Discord Bot that responds to `/scan evilcorp.com`](https://www.blacklanternsecurity.com/bbot/dev/#bbot-python-library-advanced-usage#discord-bot-example).

**Synchronous**

```python
Expand Down
2 changes: 1 addition & 1 deletion bbot/cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -299,7 +299,7 @@ async def _main():

scanner.helpers.word_cloud.load()

await scanner.prep()
await scanner._prep()

if not options.dry_run:
if not options.agent_mode and not options.yes and sys.stdin.isatty():
Expand Down
268 changes: 262 additions & 6 deletions bbot/core/event/base.py

Large diffs are not rendered by default.

16 changes: 15 additions & 1 deletion bbot/core/event/helpers.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,21 @@

def get_event_type(data):
"""
Attempt to divine event type from data
Determines the type of event based on the given data.

Args:
data (str): The data to be used for determining the event type.

Returns:
str: The type of event such as "IP_ADDRESS", "IP_RANGE", or "URL_UNVERIFIED".

Raises:
ValidationError: If the event type could not be determined.

Notes:
- Utilizes `smart_decode_punycode` and `smart_decode` to preprocess the data.
- Makes use of `ipaddress` standard library to check for IP and network types.
- Checks against a set of predefined regular expressions stored in `event_type_regexes`.
"""

# IP address
Expand Down
106 changes: 98 additions & 8 deletions bbot/core/helpers/command.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,28 @@


async def run(self, *command, check=False, text=True, **kwargs):
"""
Simple helper for running a command, and getting its output as a string
process = await run(["ls", "/tmp"])
process.stdout --> "file1.txt\nfile2.txt"
"""Runs a command asynchronously and gets its output as a string.

This method is a simple helper for executing a command and capturing its output.
If an error occurs during execution, it can optionally raise an error or just log the stderr.

Args:
*command (str): The command to run as separate arguments.
check (bool, optional): If set to True, raises an error if the subprocess exits with a non-zero status.
Defaults to False.
text (bool, optional): If set to True, decodes the subprocess output to string. Defaults to True.
**kwargs (dict): Additional keyword arguments for the subprocess.

Returns:
CompletedProcess: A completed process object with attributes for the command, return code, stdout, and stderr.

Raises:
CalledProcessError: If the subprocess exits with a non-zero status and `check=True`.

Examples:
>>> process = await run(["ls", "/tmp"])
>>> process.stdout
"file1.txt\nfile2.txt"
"""
proc, _input, command = await self._spawn_proc(*command, **kwargs)
if proc is not None:
Expand Down Expand Up @@ -41,10 +59,28 @@ async def run(self, *command, check=False, text=True, **kwargs):


async def run_live(self, *command, check=False, text=True, **kwargs):
"""
Simple helper for running a command and iterating through its output line by line in realtime
async for line in run_live(["ls", "/tmp"]):
log.info(line)
"""Runs a command asynchronously and iterates through its output line by line in realtime.

This method is useful for executing a command and capturing its output on-the-fly, as it is generated.
If an error occurs during execution, it can optionally raise an error or just log the stderr.

Args:
*command (str): The command to run as separate arguments.
check (bool, optional): If set to True, raises an error if the subprocess exits with a non-zero status.
Defaults to False.
text (bool, optional): If set to True, decodes the subprocess output to string. Defaults to True.
**kwargs (dict): Additional keyword arguments for the subprocess.

Yields:
str or bytes: The output lines of the command, either as a decoded string (if `text=True`)
or as bytes (if `text=False`).

Raises:
CalledProcessError: If the subprocess exits with a non-zero status and `check=True`.

Examples:
>>> async for line in run_live(["tail", "-f", "/var/log/auth.log"]):
... log.info(line)
"""
proc, _input, command = await self._spawn_proc(*command, **kwargs)
if proc is not None:
Expand Down Expand Up @@ -92,6 +128,27 @@ async def run_live(self, *command, check=False, text=True, **kwargs):


async def _spawn_proc(self, *command, **kwargs):
"""Spawns an asynchronous subprocess.

Prepares the command and associated keyword arguments. If the `input` argument is provided,
it checks to ensure that the `stdin` argument is not also provided. Once prepared, it creates
and returns the subprocess. If the command executable is not found, it logs a warning and traceback.

Args:
*command (str): The command to run as separate arguments.
**kwargs (dict): Additional keyword arguments for the subprocess.

Raises:
ValueError: If both stdin and input arguments are provided.

Returns:
tuple: A tuple containing the created process (or None if creation failed), the input (or None if not provided),
and the prepared command (or None if subprocess creation failed).

Examples:
>>> _spawn_proc("ls", "-l", input="data")
(<Process ...>, "data", ["ls", "-l"])
"""
command, kwargs = self._prepare_command_kwargs(command, kwargs)
_input = kwargs.pop("input", None)
if _input is not None:
Expand All @@ -110,6 +167,17 @@ async def _spawn_proc(self, *command, **kwargs):


async def _write_stdin(proc, _input):
"""
Asynchronously writes input to an active subprocess's stdin.

This function takes an `_input` parameter, which can be of type str, bytes,
list, tuple, or an asynchronous generator. The input is then written line by
line to the stdin of the given `proc`.

Args:
proc (subprocess.Popen): An active subprocess object.
_input (str, bytes, list, tuple, async generator): The data to write to stdin.
"""
if _input is not None:
if isinstance(_input, (str, bytes)):
_input = [_input]
Expand All @@ -124,6 +192,28 @@ async def _write_stdin(proc, _input):


def _prepare_command_kwargs(self, command, kwargs):
"""
Prepare arguments for passing into `asyncio.create_subprocess_exec()`.

This method modifies the `kwargs` dictionary in place to prepare it for
use in the `asyncio.create_subprocess_exec()` method. It sets the default
values for keys like 'limit', 'stdout', and 'stderr' if they are not
already present. It also handles the case when 'sudo' needs to be run.

Args:
command (list): The command to be run in the subprocess.
kwargs (dict): The keyword arguments to be passed to `asyncio.create_subprocess_exec()`.

Returns:
tuple: A tuple containing the modified `command` and `kwargs`.

Examples:
>>> _prepare_command_kwargs(['ls', '-l'], {})
(['ls', '-l'], {'limit': 104857600, 'stdout': -1, 'stderr': -1})

>>> _prepare_command_kwargs(['ls', '-l'], {'sudo': True})
(['sudo', '-E', '-A', 'LD_LIBRARY_PATH=...', 'PATH=...', 'ls', '-l'], {'limit': 104857600, 'stdout': -1, 'stderr': -1, 'env': environ(...)})
"""
# limit = 100MB (this is needed for cases like httpx that are sending large JSON blobs over stdout)
if not "limit" in kwargs:
kwargs["limit"] = 1024 * 1024 * 100
Expand Down
Loading