Skip to content

Replicant v0.3.0

Choose a tag to compare

@404SecNotFound 404SecNotFound released this 29 Jul 09:50
· 142 commits to main since this release
3e5c163

The web UI becomes something you can actually reach and navigate.

The technique catalog is unchanged: still 24 techniques across FortiGate, Palo Alto PAN-OS and Check Point, and the catalog's own version stays at 0.2.0 because no technique changed.

Reaching it

replicant web bound a random loopback port and minted a per-session token into the URL, so the address changed on every restart and reaching it from another machine meant an SSH tunnel.

  • Fixed port 9787. A busy port is now an error naming the port and the flag, not a reason to silently pick another.
  • --host accepts any local address or 0.0.0.0. The Host allowlist follows the bind address, plus loopback, plus repeatable --allowed-host. On a wildcard bind any IP literal is accepted, since DNS rebinding needs a hostname.
  • The token persists to ~/.config/replicant/web-token, 0600 in a 0700 directory, and is accepted as a Bearer header, X-Replicant-Token, a query parameter, or an httpOnly SameSite=Strict cookie. --rotate-token replaces it.
  • scripts/replicant-web.service, a systemd unit template, verified under a real systemd rather than by inspection.

Security

This release deliberately relaxes the enforced loopback bind from 0.1.0, so the compensating controls are the point rather than polish.

  • The web token no longer reaches the systemd journal. Under systemd, stdout is the journal, so the startup banner was writing the token in cleartext to a file readable by root and the systemd-journal group, defeating the 0600 token file. The banner now reveals the token only on a terminal.
  • A cookie is an ambient credential and the previous ones were not. A cookie-authenticated write must carry a matching Origin, and a missing Origin is refused. Header and query auth stay exempt, since nothing spends those on a user's behalf.
  • /ws/terminal performs its own Host, Origin, token and enabled checks. Websocket scopes never traverse HTTP middleware, so the guard protecting every /api route did not protect the PTY endpoint.
  • --no-auth is refused on a non-loopback bind without an explicit acknowledgement flag, and the embedded terminal tab is off by default there.

Still plain HTTP: put it on a management segment or behind a TLS-terminating proxy named with --allowed-host.

Navigating it

At 24 techniques the left rail was a flat list.

  • Grouped by ATT&CK tactic, collapsible, with counts; a technique mapped to several tactics appears under each. Ordered by the kill chain, which is neither alphabetical nor numeric.
  • One filter box matching technique ID, name, use case ID and ATT&CK ID at once, so whichever identifier your backlog uses will find the entry.
  • A Docs tab rendering the vendor CEF references in the browser.
  • An anchor control in the run form, defaulting to now for a live send and fixed for file output, with a warning before a live send goes out with a fixed anchor. The web path previously had no way to set this at all, so every live send from the UI carried the deterministic default and could land outside every recent-window rule.

Fixed

  • The run readout printed cap 2000 on runs that were not being throttled at all. The figure was right and the label was false.
  • No webbrowser.open attempt without a display, which printed a gio error over the startup banner on every headless start.

Documentation

Where Replicant stands on the firewall vendors' trademarks and documentation is now written down in NOTICE and docs/prior-art-and-licensing.md section 3: an independent project with no vendor affiliation, the [Constructed] claim on the golden lines verified against Fortinet's published examples, all nine field-mapping tables reviewed, and the CEF specification's own terms read.

Verification

519 Python tests (443 at 0.2.0), 68 frontend (17 at 0.2.0). black, ruff, mypy and shellcheck clean. CI gained a job that boots systemd as PID 1 and asserts the unit starts, runs as a non-root user, serves, refuses unauthenticated requests, keeps the token out of the journal, and recovers from SIGKILL.

Full changelog: https://github.com/404SecNotFound/Replicant/blob/v0.3.0/CHANGELOG.md