Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
fd48531
module restructure
TheTechromancer Jun 6, 2025
a841201
working on tests
TheTechromancer Jun 10, 2025
8f960bf
include poetry lock
TheTechromancer Jun 10, 2025
de35489
fix cloud tests
TheTechromancer Jun 10, 2025
0496431
fix scan tests
TheTechromancer Jun 11, 2025
38bb180
re-add authentication
TheTechromancer Jun 11, 2025
73dd2e2
fix docker tests
TheTechromancer Jun 11, 2025
c6e5d76
fix docker tests
TheTechromancer Jun 11, 2025
6a78475
Merge pull request #36 from blacklanternsecurity/module-restructure
TheTechromancer Jun 11, 2025
f93075e
fix finding filter WIP
TheTechromancer Jun 11, 2025
a4ff916
working api and cli tests
TheTechromancer Jun 12, 2025
b90610c
Merge pull request #42 from blacklanternsecurity/fix-finding-filter
TheTechromancer Jun 12, 2025
8a6879d
Fix command README.md
colin-stubbs Jun 13, 2025
838ec69
Fix dir name
colin-stubbs Jun 13, 2025
9e93037
Add guidance on usage of API key via HTTP header
colin-stubbs Jun 13, 2025
5b4fbc6
Update README.md
colin-stubbs Jun 13, 2025
90859bb
Merge pull request #43 from colin-stubbs/stable
TheTechromancer Jun 13, 2025
7a869de
Update README.md
RamadhanAmizudin Jun 17, 2025
500ea9d
Merge pull request #48 from RamadhanAmizudin/patch-1
TheTechromancer Jun 17, 2025
fe8eed6
debug 3.12 funniness
TheTechromancer Jun 25, 2025
4325069
exclude mongodb from reload
TheTechromancer Jul 1, 2025
17ef13b
Merge branch 'dev' into reload-excludes
TheTechromancer Jul 1, 2025
ba47ad3
Merge pull request #53 from blacklanternsecurity/reload-excludes
TheTechromancer Jul 1, 2025
dab7597
remove unused import
TheTechromancer Jul 1, 2025
f22e286
fix hot reload
TheTechromancer Jul 2, 2025
eefb1ff
fix
TheTechromancer Jul 2, 2025
02cb237
Merge pull request #54 from blacklanternsecurity/fix-hot-reload
TheTechromancer Jul 2, 2025
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
2 changes: 1 addition & 1 deletion .github/workflows/docker-tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ concurrency:
cancel-in-progress: true

jobs:
test:
docker-test:
runs-on: self-hosted
steps:
- uses: actions/checkout@v4
Expand Down
34 changes: 27 additions & 7 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,13 +29,13 @@ BBOT Server is a database and multiplayer hub for all your [BBOT](https://github

```bash
# clone the repo and cd into it
git clone [email protected]:blacklanternsecurity/bbot-server.git && cd bbot_server
git clone [email protected]:blacklanternsecurity/bbot-server.git && cd bbot-server

# Install in editable mode
pipx install -e .
```

Note: to update to the latest version, run `git pull` in the `bbot_server` directory.
Note: to update to the latest version, run `git pull` in the `bbot-server` directory.

## Start the server

Expand Down Expand Up @@ -63,7 +63,7 @@ The first time you start BBOT Server, an API key will be auto generated and put

# list of API keys to be considered valid
api_keys:
- 4aa8b3c2-9b4d-4208-890c-4ce9ad3b4710
- deadbeef-9b4d-4208-890c-4ce9ad3b4710
```

The `api_keys` value in `config.yml` is used by both the server (as a database of valid API keys), and by the client (it will pick one from the list and use it). Normally it just works and you don't have to mess with it. But to access BBOT Server remotely, you'll need to copy the API key from the server onto your local system, along with its URL:
Expand All @@ -77,6 +77,8 @@ api_keys:

This tells `bbctl` (the client) where the server is, and gives it the means to authenticate.

To utilise the API key and interact with the BBOT Server via the HTTP API, set the `X-API-Key` HTTP header to the value of a valid API key.

### Adding and Revoking API Keys

API keys can be added and removed if you are on the server machine:
Expand All @@ -94,13 +96,29 @@ bbctl server apikey delete deadbeef-9b4d-4208-890c-4ce9ad3b4710

## Send a BBOT Scan to the Server

You can output a BBOT scan directly to BBOT server:
You can output a BBOT scan directly to BBOT server with the following preset:

```yaml
# bbot-server.yml

output_modules:
- http

config:
modules:
http:
# URL of BBOT Server
url: http://localhost:8807/v1/events/
# API Key header
headers:
x-api-key: deadbeef-9b4d-4208-890c-4ce9ad3b4710
```

Note that this requires BBOT 3.0 or later (install with `pipx install git+https://github.com/blacklanternsecurity/[email protected]`)

```bash
# Start a BBOT scan, sending output to BBOT server
bbot -t evilcorp.com -p subdomain-enum -om http -c modules.http.url=http://localhost:8807/v1/events/
bbot -t evilcorp.com -p subdomain-enum ./bbot-server.yml
```

## Ingest events from past BBOT scans
Expand Down Expand Up @@ -200,10 +218,10 @@ You can list targets like so:

```bash
# List targets
bbctl target list
bbctl scan target list

# Create a new target
bbctl target create --seeds seeds.txt --blacklist blacklist.txt --name custom_target
bbctl scan target create --seeds seeds.txt --blacklist blacklist.txt --name custom_target

# List only the assets that match your new target
bbctl asset list --target custom_target
Expand Down Expand Up @@ -331,4 +349,6 @@ After connecting your AI client to BBOT Server, you can ask it sensible question

*REST API*

Connect to the default URL at [http://localhost:8807](http://localhost:8807/) to view and use the interactive API documentation.

![rest-api](https://github.com/user-attachments/assets/567bd266-b047-4005-bc0b-22d5bfd2a12b)
3 changes: 0 additions & 3 deletions bbot_server/agent/__init__.py

This file was deleted.

3 changes: 2 additions & 1 deletion bbot_server/api/_fastapi.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
import logging
from contextlib import asynccontextmanager
from fastapi.openapi.utils import get_openapi
from fastapi import FastAPI, HTTPException, Depends, Request, WebSocket
from fastapi.responses import RedirectResponse, ORJSONResponse
from fastapi import FastAPI, HTTPException, Depends, Request, WebSocket

from bbot_server import modules # noqa: F401
import bbot_server.config as bbcfg
from bbot_server.errors import BBOTServerError, handle_bbot_server_error

Expand Down
13 changes: 3 additions & 10 deletions bbot_server/applets/_root.py
Original file line number Diff line number Diff line change
@@ -1,20 +1,13 @@
import bbot_server.config as bbcfg

from bbot_server.applets._base import BaseApplet

# applet imports
from bbot_server.applets.stats import StatsApplet
from bbot_server.applets.assets import AssetsApplet
from bbot_server.applets.events import EventsApplet
from bbot_server.applets.scans import ScansApplet
from bbot_server.applets.activity import ActivityApplet
from bbot_server.applets.base import BaseApplet


class RootApplet(BaseApplet):
include_apps = [AssetsApplet, EventsApplet, ScansApplet, ActivityApplet, StatsApplet]

name = "Root Applet"

attach_to = ""

_nested = False

_route_prefix = ""
Expand Down
3 changes: 0 additions & 3 deletions bbot_server/applets/agents/__init__.py

This file was deleted.

50 changes: 35 additions & 15 deletions bbot_server/applets/_base.py → bbot_server/applets/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
import asyncio
import inspect
import logging
import traceback
from fastapi import APIRouter
from omegaconf import OmegaConf
from typing import Annotated, Any # noqa
Expand All @@ -10,12 +11,14 @@
from pymongo import WriteConcern, ASCENDING
from pymongo.errors import OperationFailure

from bbot_server.assets import Asset
from bbot.models.pydantic import Event
from bbot_server.modules import API_MODULES
from bbot_server.errors import BBOTServerError
from bbot.core.helpers import misc as bbot_misc
from bbot_server.applets._routing import ROUTE_TYPES
from bbot_server.utils import misc as bbot_server_misc
from bbot_server.models.activity_models import Activity
from bbot_server.modules.activity.activity_models import Activity

word_regex = re.compile(r"\W+")

Expand Down Expand Up @@ -75,8 +78,8 @@ class BaseApplet:
# the pydantic model this applet uses
model = None

# optionally you can include other applets
include_apps = []
# which other applet should include this one
attach_to = ""

# whether to nest this applet under its parent
# this is typically true for every applet except the root
Expand All @@ -85,6 +88,16 @@ class BaseApplet:
# optionally override route prefix
_route_prefix = None

# priority of this applet's handle_activity method, between 1 and 5, inclusive
# higher numbers are higher priority
# this is used to determine the order in which applets' .handle_activity methods are called
_activity_priority = 3

# priority of this applet's handle_event method, between 1 and 5, inclusive
# higher numbers are higher priority
# this is used to determine the order in which applets' .handle_event methods are called
_event_priority = 3

# BBOT helpers
helpers = bbot_server_misc
bbot_helpers = bbot_misc
Expand Down Expand Up @@ -113,14 +126,13 @@ def __init__(self, parent=None):

self._add_custom_routes()

for app in self.include_apps:
applets_to_include = API_MODULES.get(self.name_lowercase, {})
for included_app_name in sorted(applets_to_include):
try:
self.include_app(app)
self.include_app(applets_to_include[included_app_name])
except Exception as e:
self.log.error(f"Error including app {app}: {e}")
import traceback

traceback.print_exc()
self.log.error(f"Error including app {included_app_name}: {e}")
self.log.error(traceback.format_exc())

self._setup_finished = False

Expand Down Expand Up @@ -346,7 +358,7 @@ async def _cleanup(self):
async def cleanup(self):
pass

async def handle_activity(self, activity: Activity):
async def handle_activity(self, activity: Activity, asset: Asset = None):
pass

async def handle_event(self, event: Event, asset=None):
Expand All @@ -367,7 +379,8 @@ async def _emit_activity(self, activity: Activity):
await self.root.message_queue.publish_asset(activity)

def include_app(self, app_class):
self.log.debug(f"{self.__class__.__name__} including {app_class.__name__}")
self.log.debug(f"{self.name_lowercase} including applet {app_class.name_lowercase}")

# instantiate it
applet = app_class(parent=self)
# set it as an attribute on self
Expand Down Expand Up @@ -401,10 +414,17 @@ async def _put_obj(self, obj):
{"host": obj.host, "type": self.model.__name__}, {"$set": obj.model_dump()}, upsert=True
)

@cached_property
def name_lowercase(self):
# Replace non-alphanumeric characters with an underscore
return word_regex.sub("_", self.name.lower())
class NameLowercaseDescriptor:
def __init__(self):
self._cache = {}

def __get__(self, obj, owner):
cache_key = owner if obj is None else obj
if cache_key not in self._cache:
self._cache[cache_key] = word_regex.sub("_", cache_key.name.lower())
return self._cache[cache_key]

name_lowercase = NameLowercaseDescriptor()

def all_child_applets(self, include_self=False):
applets = []
Expand Down
6 changes: 0 additions & 6 deletions bbot_server/applets/buckets.py

This file was deleted.

28 changes: 0 additions & 28 deletions bbot_server/applets/export.py

This file was deleted.

11 changes: 0 additions & 11 deletions bbot_server/applets/redirects.py

This file was deleted.

10 changes: 0 additions & 10 deletions bbot_server/applets/urls.py

This file was deleted.

21 changes: 0 additions & 21 deletions bbot_server/applets/web_screenshots.py

This file was deleted.

10 changes: 0 additions & 10 deletions bbot_server/applets/yara_rules.py

This file was deleted.

File renamed without changes.
Loading
Loading