Skip to content

Commit

Permalink
support filesystem, mobile app targets
Browse files Browse the repository at this point in the history
  • Loading branch information
github-actions committed Dec 18, 2024
1 parent 48c0859 commit 9fedf1d
Show file tree
Hide file tree
Showing 6 changed files with 90 additions and 9 deletions.
5 changes: 5 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -317,6 +317,11 @@ Targets can be any of the following:
- `IP_RANGE` (`1.2.3.0/24`)
- `OPEN_TCP_PORT` (`192.168.0.1:80`)
- `URL` (`https://www.evilcorp.com`)
- `EMAIL_ADDRESS` (`[email protected]`)
- `ORG_STUB` (`ORG:evilcorp`)
- `USER_STUB` (`USER:bobsmith`)
- `FILESYSTEM` (`FILESYSTEM:/tmp/asdf`)
- `MOBILE_APP` (`MOBILE_APP:https://play.google.com/store/apps/details?id=com.evilcorp.app`)

For more information, see [Targets](https://www.blacklanternsecurity.com/bbot/Stable/scanning/#targets-t). To learn how BBOT handles scope, see [Scope](https://www.blacklanternsecurity.com/bbot/Stable/scanning/#scope).

Expand Down
23 changes: 22 additions & 1 deletion bbot/core/event/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,8 @@
from typing import Optional
from contextlib import suppress
from radixtarget import RadixTarget
from urllib.parse import urljoin, parse_qs
from pydantic import BaseModel, field_validator
from urllib.parse import urlparse, urljoin, parse_qs


from .helpers import *
Expand Down Expand Up @@ -1584,6 +1584,27 @@ class RAW_DNS_RECORD(DictHostEvent, DnsEvent):
class MOBILE_APP(DictEvent):
_always_emit = True

def _sanitize_data(self, data):
if isinstance(data, str):
data = {"url": data}
if "url" not in data:
raise ValidationError("url is required for MOBILE_APP events")
url = data["url"]
# parse URL
try:
self.parsed_url = urlparse(url)
except Exception as e:
raise ValidationError(f"Error parsing URL {url}: {e}")
if not "id" in data:
# extract "id" getparam
params = parse_qs(self.parsed_url.query)
try:
_id = params["id"][0]
except Exception:
raise ValidationError("id is required for MOBILE_APP events")
data["id"] = _id
return data

def _pretty_string(self):
return self.data["url"]

Expand Down
16 changes: 15 additions & 1 deletion bbot/scanner/target.py
Original file line number Diff line number Diff line change
Expand Up @@ -95,9 +95,9 @@ def add(self, targets):
else:
event = self.make_event(target)
if event:
self.inputs.add(target)
_events = [event]
for event in _events:
self.inputs.add(event.data)
events.add(event)

# sort by host size to ensure consistency
Expand Down Expand Up @@ -140,6 +140,20 @@ def handle_username(self, match):
return [username_event]
return []

@special_target_type(r"^(?:FILESYSTEM|FILE|FOLDER|DIR|PATH):(.*)")
def handle_filesystem(self, match):
filesystem_event = self.make_event({"path": match.group(1)}, event_type="FILESYSTEM")
if filesystem_event:
return [filesystem_event]
return []

@special_target_type(r"^(?:MOBILE_APP|APK|IPA|APP):(.*)")
def handle_mobile_app(self, match):
mobile_app_event = self.make_event({"url": match.group(1)}, event_type="MOBILE_APP")
if mobile_app_event:
return [mobile_app_event]
return []

def get(self, event, single=True, **kwargs):
results = super().get(event, **kwargs)
if results and single:
Expand Down
14 changes: 7 additions & 7 deletions bbot/test/bbot_fixtures.py
Original file line number Diff line number Diff line change
Expand Up @@ -224,12 +224,12 @@ class bbot_events:
return bbot_events


@pytest.fixture(scope="session", autouse=True)
def install_all_python_deps():
deps_pip = set()
for module in DEFAULT_PRESET.module_loader.preloaded().values():
deps_pip.update(set(module.get("deps", {}).get("pip", [])))
# @pytest.fixture(scope="session", autouse=True)
# def install_all_python_deps():
# deps_pip = set()
# for module in DEFAULT_PRESET.module_loader.preloaded().values():
# deps_pip.update(set(module.get("deps", {}).get("pip", [])))

constraint_file = tempwordlist(get_python_constraints())
# constraint_file = tempwordlist(get_python_constraints())

subprocess.run([sys.executable, "-m", "pip", "install", "--constraint", constraint_file] + list(deps_pip))
# subprocess.run([sys.executable, "-m", "pip", "install", "--constraint", constraint_file] + list(deps_pip))
36 changes: 36 additions & 0 deletions bbot/test/test_step_1/test_events.py
Original file line number Diff line number Diff line change
Expand Up @@ -979,6 +979,42 @@ def test_event_magic():
zip_file.unlink()


@pytest.mark.asyncio
async def test_mobile_app():
scan = Scanner()
with pytest.raises(ValidationError):
scan.make_event("com.evilcorp.app", "MOBILE_APP", parent=scan.root_event)
with pytest.raises(ValidationError):
scan.make_event({"id": "com.evilcorp.app"}, "MOBILE_APP", parent=scan.root_event)
with pytest.raises(ValidationError):
scan.make_event({"url": "https://play.google.com/store/apps/details"}, "MOBILE_APP", parent=scan.root_event)
mobile_app = scan.make_event(
{"url": "https://play.google.com/store/apps/details?id=com.evilcorp.app"}, "MOBILE_APP", parent=scan.root_event
)
assert sorted(mobile_app.data.items()) == [
("id", "com.evilcorp.app"),
("url", "https://play.google.com/store/apps/details?id=com.evilcorp.app"),
]

scan = Scanner("MOBILE_APP:https://play.google.com/store/apps/details?id=com.evilcorp.app")
events = [e async for e in scan.async_start()]
assert len(events) == 3
assert events[1].type == "MOBILE_APP"
assert sorted(events[1].data.items()) == [
("id", "com.evilcorp.app"),
("url", "https://play.google.com/store/apps/details?id=com.evilcorp.app"),
]


@pytest.mark.asyncio
async def test_filesystem():
scan = Scanner("FILESYSTEM:/tmp/asdf")
events = [e async for e in scan.async_start()]
assert len(events) == 3
assert events[1].type == "FILESYSTEM"
assert events[1].data == {"path": "/tmp/asdf"}


def test_event_hashing():
scan = Scanner("example.com")
url_event = scan.make_event("https://api.example.com/", "URL_UNVERIFIED", parent=scan.root_event)
Expand Down
5 changes: 5 additions & 0 deletions docs/scanning/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,11 @@ Targets declare what's in-scope, and seed a scan with initial data. BBOT accepts
- `IP_RANGE` (`1.2.3.0/24`)
- `OPEN_TCP_PORT` (`192.168.0.1:80`)
- `URL` (`https://www.evilcorp.com`)
- `EMAIL_ADDRESS` (`[email protected]`)
- `ORG_STUB` (`ORG:evilcorp`)
- `USER_STUB` (`USER:bobsmith`)
- `FILESYSTEM` (`FILESYSTEM:/tmp/asdf`)
- `MOBILE_APP` (`MOBILE_APP:https://play.google.com/store/apps/details?id=com.evilcorp.app`)

Note that BBOT only discriminates down to the host level. This means, for example, if you specify a URL `https://www.evilcorp.com` as the target, the scan will be *seeded* with that URL, but the scope of the scan will be the entire host, `www.evilcorp.com`. Other ports/URLs on that same host may also be scanned.

Expand Down

0 comments on commit 9fedf1d

Please sign in to comment.