Skip to content
Merged
Show file tree
Hide file tree
Changes from 29 commits
Commits
Show all changes
30 commits
Select commit Hold shift + click to select a range
8fc15da
Prepare release 6.1.4 private
web-flow Aug 16, 2025
3764e37
Merge pull request #1065 from BC-SECURITY/release/6.1.4-private
vinnybod Aug 16, 2025
7baa7b2
Updated reset and clean functions (#1069)
Cx01N Aug 17, 2025
4fc7fa2
fixed install script where goenv breaks in wsl
Cx01N Aug 22, 2025
8ea1ac6
fixed launcher_bat when go agent is used
Cx01N Aug 22, 2025
1cac86a
cleaned up code
Cx01N Aug 22, 2025
606824c
added go to spawnas
Cx01N Aug 22, 2025
d2e20f1
added go to spawn
Cx01N Aug 22, 2025
c282455
added other languages to fodhelper
Cx01N Aug 22, 2025
abef4e6
updated changelog
Cx01N Aug 22, 2025
e0b0e93
fixed issue where gopire doesn't detect high integrity
Cx01N Aug 22, 2025
5f5da01
format fixes
Cx01N Aug 22, 2025
009bf7c
split fodhelper yaml text to 2 lines
Cx01N Aug 22, 2025
9b816ea
fixed yamls with needs revision tag still included
Cx01N Aug 22, 2025
2a55909
yaml fixes?
Cx01N Aug 22, 2025
66cc865
yaml fixes?
Cx01N Aug 22, 2025
8d5734e
manual yaml changes...
Cx01N Aug 23, 2025
282c9be
more manual yaml changes...
Cx01N Aug 23, 2025
a6a157e
remove git clone crlf
Cx01N Aug 23, 2025
2b20263
removed install changes
Cx01N Aug 23, 2025
5eb8c13
Merge pull request #1071 from BC-SECURITY/6.0_fixes
Cx01N Aug 23, 2025
c9a22aa
fixed yaml merge mistake (#1073)
Cx01N Aug 24, 2025
7826d02
Fixes to C# execution on IPY Agent (#1074)
Cx01N Aug 31, 2025
878919b
Fix/port normalization overrides host port (#793)
ruvolof Aug 14, 2025
aa8e95f
Add X-Empire-Auth header and remove passlib (#1075)
vinnybod Sep 2, 2025
8972fc5
Prepare release 6.2.0 private
web-flow Sep 2, 2025
bbdd3b5
Merge pull request #1076 from BC-SECURITY/release/6.2.0-private
vinnybod Sep 2, 2025
72f0a31
Update starkiller version to v3.1.0
web-flow Sep 2, 2025
880a131
Merge branch 'main' into release/6.2.0
vinnybod Sep 2, 2025
c8c4004
Update CHANGELOG.md
vinnybod Sep 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
21 changes: 19 additions & 2 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,20 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

## [Unreleased]

- Fixed port normalization to allow host port and bind port to be different.
## [6.2.0] - 2025-09-02
- Updated Starkiller to v3.1.0

- Added clean and reset options to the server
Comment thread
vinnybod marked this conversation as resolved.
- Added other agent language support to fodhelper
- Added go support to spawn and spawnas
- Fixed launcher_bat when go agent is used
- Fixed issue where gopire doesn't detect high integrity agents
- Fixed C# execution in iron python agent to use ordered arguements
- Removed redundant C# function from iron python agent
- Cleaned up Rubeus and RunCoff yamls
- Fixed port normalization to allow host port and bind port to be different
- Allow 'X-Empire-Token' as an alternative header to 'Authorization'
- Remove abandoned passlib library and use bcrypt directly

## [6.1.3] - 2025-07-11

Expand Down Expand Up @@ -1139,7 +1152,11 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- Updated shellcoderdi to newest version (@Cx01N)
- Added a Nim launcher (@Hubbl3)

[Unreleased]: https://github.com/BC-SECURITY/Empire-Sponsors/compare/v6.1.3...HEAD
[Unreleased]: https://github.com/BC-SECURITY/Empire-Sponsors/compare/v6.2.0...HEAD

[6.2.0]: https://github.com/BC-SECURITY/Empire-Sponsors/compare/v6.1.4...v6.2.0

[6.1.4]: https://github.com/BC-SECURITY/Empire-Sponsors/compare/v6.1.3...v6.1.4

[6.1.3]: https://github.com/BC-SECURITY/Empire-Sponsors/compare/v6.1.2...v6.1.3

Expand Down
1 change: 1 addition & 0 deletions docs/SUMMARY.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
* [Quickstart](quickstart/README.md)
* [Installation](quickstart/installation.md)
* [Server](quickstart/server.md)
* [Resetting](quickstart/resetting.md)
* [Starkiller](starkiller/README.md)
* [Introduction](starkiller/introduction.md)
* [Agent Tasks](starkiller/agent-tasks.md)
Expand Down
36 changes: 36 additions & 0 deletions docs/quickstart/resetting.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
# Resetting Empire state (--reset and --clean)

Empire provides two maintenance flags to help you return the system to a clean state between runs.

## Reset

Start fresh without historical data while keeping generated files and dependencies on disk intact.

- What it does:
- Drops existing data and reinitializes the database schema. On the next start, Empire repopulates default values defined in your configuration.
- What it does not do:
- Does not delete or modify your configuration files (for example, config.yaml).
- Does not delete Starkiller or Empire-Compiler.

### Example
```bash
./ps-empire server --reset
```

## Clean

Completely resets Empire to a pristine state, removing config and all Starkiller and Empire-Compiler files.

- What it does:
- Performs everything --reset does (drops data and reinitializes the database).
- Deletes configuration files (for example, config.yaml)
- Removes Starkiller and Empire-Compiler files.

- What it does not do:
- Does not uninstall system-level prerequisites (for example, Python, Docker, or database servers).
- Does not remove your source checkout itself if you’re running from a cloned repository.

### Example
```bash
./ps-empire server --clean
```
4 changes: 2 additions & 2 deletions docs/quickstart/server.md
Original file line number Diff line number Diff line change
Expand Up @@ -99,7 +99,7 @@ plugins:
options:
report: all
```
* **plugin_marketplace** - This points the server to where Empire should look for additionl available plugins to install. This defaults to the BC Security plugin marketplace but can point to a private marketplace as well.
* **plugin_marketplace** - This points the server to where Empire should look for additional available plugins to install. This defaults to the BC Security plugin marketplace but can point to a private marketplace as well.
name - the display name for the marketplace in Empire
git_url - git project to pull plugins from

Expand All @@ -121,7 +121,7 @@ directories:

* **logging** - See [Logging](../logging/logging.md) for more information on logging configuration.

* **submodules** - Control if submodules wil be auto updated on startup.
* **submodules** - Control if submodules will be auto updated on startup.

```
submodules:
Expand Down
7 changes: 6 additions & 1 deletion empire/arguments.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,12 @@
general_group.add_argument(
"--reset",
action="store_true",
help="Resets Empire's database and deletes any app data accumulated over previous runs.",
help="Drop and reinitialize the database. Keep config and Starkiller/Empire-Compiler files intact.",
)
general_group.add_argument(
"--clean",
action="store_true",
help="Drop and reinitialize the database. Removes Starkiller/Empire-Compiler files.",
)
general_group.add_argument(
"-v", "--version", action="store_true", help="Display current Empire version."
Expand Down
47 changes: 37 additions & 10 deletions empire/server/api/jwt_auth.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
from datetime import datetime, timedelta
from typing import Annotated

from fastapi import Depends, HTTPException
from fastapi.security import OAuth2PasswordBearer
import bcrypt
from fastapi import Depends, HTTPException, Request
from fastapi.security import APIKeyHeader, OAuth2PasswordBearer
from jose import JWTError, jwt
from passlib.context import CryptContext
from pydantic import BaseModel
from sqlalchemy.orm import Session
from starlette import status
Expand All @@ -30,17 +30,20 @@ class TokenData(BaseModel):
username: str | None = None


pwd_context = CryptContext(schemes=["bcrypt"], deprecated="auto")

oauth2_scheme = OAuth2PasswordBearer(tokenUrl="token")
# Support both Authorization header and custom header
oauth2_scheme = OAuth2PasswordBearer(tokenUrl="token", auto_error=False)
api_key_header = APIKeyHeader(name="X-Empire-Token", auto_error=False)


def verify_password(plain_password, hashed_password):
return pwd_context.verify(plain_password, hashed_password)
password_byte_enc = plain_password.encode("utf-8")
return bcrypt.checkpw(password_byte_enc, hashed_password.encode("utf-8"))


def get_password_hash(password):
return pwd_context.hash(password)
def get_password_hash(plain_password: str) -> str:
pwd_bytes = plain_password.encode("utf-8")
salt = bcrypt.gensalt()
return bcrypt.hashpw(pwd_bytes, salt).decode("utf-8")


def get_user(db, username: str) -> models.User:
Expand Down Expand Up @@ -68,23 +71,47 @@ def create_access_token(data: dict, expires_delta: timedelta | None = None):
return jwt.encode(to_encode, SECRET_KEY, algorithm=ALGORITHM)


def get_token_from_headers(request: Request) -> str:
"""Check both Authorization and X-Empire-Token headers for JWT token"""
token = request.headers.get("X-Empire-Token")
if token:
# Remove 'Bearer ' prefix if present
return token.removeprefix("Bearer ")

auth_header = request.headers.get("Authorization")
if auth_header:
return auth_header.removeprefix("Bearer ")

# No valid token found in either header
raise HTTPException(
status_code=status.HTTP_401_UNAUTHORIZED,
detail="Could not validate credentials - token required in either 'Authorization' or 'X-Empire-Token' header",
headers={"WWW-Authenticate": "Bearer"},
)


async def get_current_user(
db: CurrentSession,
token: str = Depends(oauth2_scheme),
request: Request,
):
credentials_exception = HTTPException(
status_code=status.HTTP_401_UNAUTHORIZED,
detail="Could not validate credentials",
headers={"WWW-Authenticate": "Bearer"},
)

try:
token = get_token_from_headers(request)
payload = jwt.decode(token, SECRET_KEY, algorithms=[ALGORITHM])
username: str = payload.get("sub")
if username is None:
raise credentials_exception
token_data = TokenData(username=username)
except JWTError as e:
raise credentials_exception from e
except HTTPException:
# Re-raise HTTPExceptions from get_token_from_headers
raise
user = get_user(db, username=token_data.username)
if user is None:
raise credentials_exception
Expand Down
2 changes: 1 addition & 1 deletion empire/server/common/empire.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@
if TYPE_CHECKING:
from socket import SocketIO

VERSION = "6.1.3 BC Security Fork"
VERSION = "6.2.0 BC Security Fork"

log = logging.getLogger(__name__)

Expand Down
2 changes: 1 addition & 1 deletion empire/server/config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ starkiller:
enabled: true
repo: https://github.com/BC-SECURITY/Starkiller.git
# Can be a branch, tag, or commit hash
ref: v3.0.1
ref: v3.1.0
submodules:
auto_update: true
plugin_marketplace:
Expand Down
13 changes: 6 additions & 7 deletions empire/server/core/db/defaults.py
Original file line number Diff line number Diff line change
@@ -1,24 +1,23 @@
import logging
import os
import random
import secrets
import string

from passlib import pwd
from passlib.context import CryptContext
import bcrypt

from empire.server.core.config.config_manager import empire_config
from empire.server.core.db import models

database_config = empire_config.database.defaults

pwd_context = CryptContext(schemes=["bcrypt"], deprecated="auto")

log = logging.getLogger(__name__)


def get_default_hashed_password():
password = database_config.password
return pwd_context.hash(password)
pwd_bytes = database_config.password.encode("utf-8")
salt = bcrypt.gensalt()
return bcrypt.hashpw(pwd_bytes, salt).decode("utf-8")


def get_default_user():
Expand All @@ -33,7 +32,7 @@ def get_default_user():
def get_default_config():
return models.Config(
staging_key=get_staging_key(),
jwt_secret_key=pwd.genword(length=32, charset="hex"),
jwt_secret_key=secrets.token_hex(16),
ip_filtering=True,
)

Expand Down
45 changes: 44 additions & 1 deletion empire/server/data/agent/gopire/main.template
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@ import (
"path/filepath"
"runtime"
"strings"
"unsafe"
"golang.org/x/sys/windows"
)

func getSysinfo(server string, nonce string) string {
Expand All @@ -26,11 +28,16 @@ func getSysinfo(server string, nonce string) string {
operatingsystem := runtime.GOOS
v := w32.RtlGetVersion()
osDetails := fmt.Sprintf("%s %d Build:%d", operatingsystem, v.MajorVersion, v.BuildNumber)
highIntegrity := fmt.Sprintf("%t", false)
internalIP := getIP()
architecture := runtime.GOARCH
version := "1.23"

hi, err := IsHighIntegrity()
if err != nil {
hi = false
}
highIntegrity := fmt.Sprintf("%t", hi)

return fmt.Sprintf("%s|%s|%s|%s|%s|%s|%s|%s|%s|%d|%s|%s|%s",
nonce, server, "", username, hostname, internalIP, osDetails, highIntegrity, processName, processID,
language, version, architecture)
Expand All @@ -51,6 +58,42 @@ func getIP() string {
return ""
}

func IsHighIntegrity() (bool, error) {
const (
SECURITY_MANDATORY_HIGH_RID = 0x00003000
)

hProc := windows.CurrentProcess()

var tok windows.Token
if err := windows.OpenProcessToken(hProc, windows.TOKEN_QUERY, &tok); err != nil {
return false, err
}
defer tok.Close()

var outLen uint32
err := windows.GetTokenInformation(tok, windows.TokenIntegrityLevel, nil, 0, &outLen)
if err != windows.ERROR_INSUFFICIENT_BUFFER {
return false, err
}

buf := make([]byte, outLen)
if err := windows.GetTokenInformation(tok, windows.TokenIntegrityLevel, &buf[0], outLen, &outLen); err != nil {
return false, err
}

type tokenMandatoryLabel struct {
Label windows.SIDAndAttributes
}
tml := (*tokenMandatoryLabel)(unsafe.Pointer(&buf[0]))
sid := tml.Label.Sid

subAuthCount := *(*uint8)(unsafe.Pointer(uintptr(unsafe.Pointer(sid)) + 1))
lastRID := *(*uint32)(unsafe.Pointer(uintptr(unsafe.Pointer(sid)) + uintptr(8+int(subAuthCount-1)*4)))

return lastRID >= SECURITY_MANDATORY_HIGH_RID, nil
}

func main() {
profile := "{{ PROFILE }}"
server := "{{ HOST }}"
Expand Down
Loading
Loading