github BC-SECURITY/Empire v6.7.1

3 hours ago

[6.7.1] - 2026-07-25

  • Updated Starkiller to v3.6.0

Security

  • Fixed a path traversal vulnerability where a crafted multipart upload filename could write files outside the downloads directory via the avatar (POST /api/v2/users/{uid}/avatar) and download (POST /api/v2/downloads/) upload endpoints (#824)

Fixed

  • Empire Compiler downloads now authenticate against the GitHub API with GITHUB_TOKEN/GH_TOKEN when set, avoiding intermittent 403 rate-limit failures on shared egress IPs (e.g. CI). When the compiler still can't be fetched, the server degrades gracefully and raises a clear error only if C# compilation is attempted, instead of crashing at startup.

[6.7.0] - 2026-07-06

Fixed

  • http_malleable listener no longer crashes with AttributeError when a request arrives on a URI not defined in the malleable profile. It now returns a 404 default response instead of propagating an unhandled exception.

Added

  • Performance indexes migration (0002) — apply with poetry run alembic -c alembic.ini upgrade head (from the Empire root) on existing databases.

Changed

  • Migrated the data-access layer from the SQLAlchemy 1.x Query API to the 2.0 select() idiom across the core services, data_util, jwt_auth, the http/http_malleable listeners, and basic_reporting. Behavior-preserving and still synchronous; db.query() no longer appears in empire/server.
  • Migrated the ORM models in core/db/models.py from Column()/declarative_base() to 2.0 typed declarative (Mapped[]/mapped_column()). Schema-preserving: generated DDL is identical and alembic autogenerate detects no diff.
  • Modernization quick wins (no behavior change): timezone-aware datetime.now(UTC) in jwt_auth, O(1) startup existence checks in core/db/base.py, removal of the Python-2 old_div shim and unused math_util.py, and a ruff pre-commit bump to match pyproject.toml.
  • Bounded the dynamic-PowerShell helper caches with lru_cache and made script generation deterministic, cutting test_load_modules from ~54s to ~1s on CI.
  • Trimmed agent-comms SQL hot paths: dropped lazy="joined" on Agent.host, and used db.get(Agent, session_id) / a scalar select(Agent.hostname) in the hot request paths.
  • Collapsed the per-result-packet SessionLocal.begin() loop in _handle_agent_response so one agent callback opens a single transaction regardless of how many result packets it carries. Per-callback wall time roughly halves for multi-packet batches on MySQL (e.g. 2.0x at N=10, 2.2x at N=25); N=1 is unchanged. Trade-off: a mid-batch failure now rolls back the whole batch instead of partially committing.
  • Indexed columns the agent-comms / auth hot paths filter on: composite (listener, archived) on Agent, single-column AgentFile.session_id, AgentFile.parent_id, and User.username. Model-only via index=True; fresh databases pick the indexes up automatically.
  • profile_service.load_malleable_profiles preloads existing Profile names once and dedupes in-memory instead of one SELECT per file (mirrors module_service.load_modules). Duplicate names across categories now log a warning naming the conflicting file instead of being silently caught at commit time.
  • Lazy-loaded custom_generate module classes. The importlib.spec_from_file_location + Module() instantiation now happens on first execute and is cached, so boot skips the file-by-file import work for modules that may never run in a given session.
  • Replaced three large credential test fixtures with a tiny synthetic module to cut CI obfuscation time; production module sources are unchanged.
  • Promoted the main_menu_mock and module_service fixtures in test_modules.py to module scope so the five tests share one ModuleService.

Removed

  • Dropped 4 unused/redundant Python dependencies to reduce supply-chain surface: pycryptodome (superseded by cryptography in the 2021 pycrypto migration; no Crypto.* imports remain), pyOpenSSL (last imports removed in 2020 cleanup, forgotten in pyproject.toml), pytest-timeout (the @pytest.mark.timeout markers that justified it have since been removed), and the redundant direct coverage pin (already pulled transitively by pytest-cov).
  • Replaced python-jose with pyjwt. python-jose is effectively abandoned (last release 3.3.0 in 2022) and pulled unmaintained ecdsa / rsa transitive dependencies with a poor security history. pyjwt is the actively-maintained library FastAPI now recommends in its OAuth2/JWT tutorial; the call surface in jwt_auth.py is identical (jwt.encode/jwt.decode), with the exception type changing from JWTError to jwt.InvalidTokenError.
  • Replaced the SQLAlchemy-Utc dependency with a 90-line vendored module (empire/server/core/db/utc_datetime.py) carrying the upstream MIT notice. The package shipped only two helpers (UtcDateTime column type + dialect-aware utcnow() SQL expression); behavior — including per-dialect SQL — is byte-identical so the existing schema and column defaults are unchanged.
  • Replaced requests-file with inline file:// dispatch in plugin_service._download_tar using stdlib urllib.parse + Path.open. The dependency existed solely to mount a FileAdapter on a requests.Session for one method; HTTP downloads now use requests.get(...) directly and file:// URLs are handled with a short scheme check.
  • Modernized the vendored malleable/ profile parser from Py2/Py3 compatibility (six.moves.urllib.* + six.moves.range) to Py3 stdlib (urllib.parse + builtin range). Mechanical change — six.moves.urllib.parseurllib.parse is a 1:1 rename, and six.moves.range is just the Py3 builtin. Drops the six direct dep that was added back when python-jose was removed.

Fixed

  • Preserved subprocess diagnostics across stager compile failures: the Go/Dotnet compilers now include the return code and fall back to stdout when stderr is empty, and stager_service.generate_stager surfaces a 400 instead of a 500 stack trace.
  • Removed the instance-level threading.Lock in AgentCommunicationService that serialized all concurrent agent file writes; also fixed a zero-byte file left on decompression failure and guarded the download-progress calc against a malformed total_filesize.
  • Fixed clone_git_repo leaking its /tmp staging directory, which could fill a tmpfs-backed /tmp and cause cascades of unrelated-looking test failures.
  • Fixed the update-starkiller release action updating the wrong ref in config.yaml; it now scopes updates to starkiller.repo/starkiller.ref with yq.
  • Fixed undefer(AgentTask.output_original) referencing a nonexistent attribute (the column is original_output).

Don't miss a new Empire release

NewReleases is sending notifications on new releases.