Skip to content

stina-sh/RedC2

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

45 Commits
 
 
 
 
 
 
 
 
 
 

Repository files navigation


██████╗ ███████╗██████╗  ██████╗██████╗ 
██╔══██╗██╔════╝██╔══██╗██╔════╝╚════██╗
██████╔╝█████╗  ██║  ██║██║      █████╔╝
██╔══██╗██╔══╝  ██║  ██║██║     ██╔═══╝ 
██║  ██║███████╗██████╔╝╚██████╗███████╗
╚═╝  ╚═╝╚══════╝╚═════╝  ╚═════╝╚══════╝

* RedC2*

A fileless, containerized red team Command & Control platform
Flutter · Go · SurrealDB · bwrap · memfd


Platform Frontend Backend DB License


redc2 is an end-to-end red teaming platform inspired by Havoc C2 and Core Impact, built for operators who demand full control over their tooling stack. From agent deployment to post-exploitation, every component is designed around a single principle — leave no trace. Tools never touch disk, environments are containerized and disposable, and all operational state lives in an embedded database that starts and dies with the operator. The result is a self-contained offensive platform that runs entirely from a single binary, pairs a modern Flutter UI with a Go backend over a zero-overhead FFI bridge, and executes arbitrary tooling through fileless memory techniques that bypass conventional endpoint defenses.

◈ Architecture Overview

┌─────────────────────────────────────────────────────────────────────────────┐
│                           FLUTTER UI  (redc2)                               │
│                                                                             │
│  ┌──────────────┐  ┌──────────────┐  ┌──────────────┐  ┌────────────────┐  │
│  │  Sessions    │  │  Listeners   │  │   Exploits   │  │   Footprint    │  │
│  │  (Terminal)  │  │  (Bubbles)   │  │  (Tools UI)  │  │  (Tools UI)    │  │
│  └──────┬───────┘  └──────┬───────┘  └──────┬───────┘  └───────┬────────┘  │
│         └─────────────────┴──────────────────┴──────────────────┘           │
│                                    │                                         │
│                           FFI  (dart:ffi)                                    │
└────────────────────────────────────┼────────────────────────────────────────┘
                                     │
                       ┌─────────────▼─────────────┐
                       │        libh.so             │
                       │   (Go → C shared library)  │
                       │                            │
                       │  GoInit()  GoGetTools()    │
                       │  GoRunExploit()  etc.      │
                       └─────────────┬─────────────┘
                                     │
              ┌──────────────────────┼──────────────────────┐
              │                      │                       │
  ┌───────────▼──────────┐  ┌───────▼──────────┐  ┌────────▼────────────┐
  │    SurrealDB          │  │  SessionBubble   │  │   Tool Executor     │
  │  (embedded, memory)  │  │  HTTP C2 Server  │  │  (memfd + bwrap)    │
  │                      │  │                  │  │                     │
  │  tools     sessions  │  │  /register       │  │  ELF → memfd_create │
  │  listeners commands  │  │  /command-var    │  │  /proc/self/fd/N    │
  │  outputs   exploits  │  │  /out-var        │  │  bwrap rootfs env   │
  └──────────────────────┘  └──────────────────┘  └─────────────────────┘

◈ Core Design Pillars

1 · Fileless Execution via memfd_create

No binary ever touches disk. Tools stored as base64 in SurrealDB are decoded entirely in RAM and executed through a Linux anonymous file descriptor.

┌──────────────────────────────────────────────────────────────────┐
│  TOOL LIFECYCLE  —  zero disk contact                            │
│                                                                  │
│  SurrealDB           Go runtime           Linux kernel           │
│  ┌──────────┐        ┌─────────────┐      ┌────────────────┐    │
│  │binary_b64│──b64──▶│ base64.Decode│     │ memfd_create() │    │
│  └──────────┘        └──────┬──────┘      └───────┬────────┘    │
│                             │  []byte              │  fd=N       │
│                             └──────── Write ──────▶│            │
│                                                    │            │
│                                        exec via    │            │
│                                 /proc/self/fd/N ◀──┘            │
│                                                                  │
│  ✓ Never written to /tmp, /var, or any mountpoint               │
│  ✓ fd auto-closed after exec (MFD_CLOEXEC)                      │
│  ✓ Kernel >= 3.17 required                                       │
└──────────────────────────────────────────────────────────────────┘

2 · Containerized Environments via bwrap

Each tool category runs inside its own Debian bookworm-slim rootfs, extracted to /dev/shm (tmpfs) and isolated with bubblewrap. No Alpine, no musl — full glibc compatibility guaranteed.

┌─────────────────────────────────────────────────────────────────────┐
│  EXECUTION ENVIRONMENTS                                             │
│                                                                     │
│  ┌──────────────┐  ┌──────────────┐  ┌──────────────────────────┐  │
│  │  env-web     │  │  env-nxc     │  │  env-impacket            │  │
│  │  ──────────  │  │  ──────────  │  │  ──────────────────────  │  │
│  │  nikto       │  │ netexec      │  │  secretsdump             │  │
│  │  sqlmap      │  │  crackmapexec│  │  psexec / wmiexec        │  │
│  │  gobuster    │  │  smbclient   │  │  GetUserSPNs             │  │
│  └──────────────┘  └──────────────┘  └──────────────────────────┘  │
│                                                                     │
│  ┌──────────────┐  ┌──────────────┐  ┌──────────────────────────┐  │
│  │  env-certipy │  │  env-ruby    │  │  env-perl                │  │
│  │  ──────────  │  │  ──────────  │  │  ──────────────────────  │  │
│  │  certipy-ad  │  │  WinRM gems  │  │  ldap-utils              │  │
│  │  PKI attacks │  │  Metasploit  │  │  Net::LDAP               │  │
│  └──────────────┘  └──────────────┘  └──────────────────────────┘  │
│                                                                     │
│  All environments:                                                  │
│  • Base:  debian:bookworm-slim  (docker export → flat tar)         │
│  • Mount: /dev/shm/.tool-cache/<env>  (tmpfs, never persisted)     │
│  • Exec:  bwrap --ro-bind rootfs / -- /tool <args>                 │
└─────────────────────────────────────────────────────────────────────┘

3 · Dynamic Tool Registry

Tools are not compiled into the binary. They live in SurrealDB and are loaded at runtime from operator-controlled URLs.

                  ADDING A TOOL  (from the Exploit tab)
                  
  Operator                  Flutter UI              Go backend
     │                          │                       │
     │  paste binary URL        │                       │
     │  paste schema URL        │                       │
     │─────────────────────────▶│                       │
     │                          │   GoAddTool(b64, sch) │
     │                          │──────────────────────▶│
     │                          │                       │── HTTP GET binary.json
     │                          │                       │── HTTP GET schema.json
     │                          │                       │── merge manifests
     │                          │                       │── UpsertTool → SurrealDB
     │                          │◀──────────────────────│
     │  tool appears in UI      │                       │
     │◀─────────────────────────│                       │

  binary.json                        schema.json
  ┌───────────────────────┐          ┌────────────────────────────┐
  │ {                     │          │ {                          │
  │   "id": "nxc",        │          │   "id": "nxc",             │
  │   "version": "1.x",   │          │   "name": "NetExec",       │
  │   "binary_b64": "...", │          │   "category": "lateral",   │
  │   "data_tar_b64": "..."│          │   "argv_template": [       │
  │ }                     │          │     "{binary}", "{target}", │
  └───────────────────────┘          │     "-u", "{user}", ...    │
                                     │   ],                       │
  ↑ ELF + optional rootfs tar.gz     │   "fields": [ ... ]        │
                                     │ }                          │
                                     └────────────────────────────┘

4 · SurrealDB as the Operational Brain

All runtime state — sessions, listeners, commands, terminal output, tool results — persists in an embedded SurrealDB instance that starts automatically (launched into a memfd, no disk install needed).

┌──────────────────────────────────────────────────────────────┐
│  SURREALDB SCHEMA                                            │
│                                                              │
│  sessions       listeners      commands       outputs        │
│  ───────────    ──────────     ────────────   ───────────    │
│  id             name           port           port           │
│  remote_addr    port           tool           data           │
│  port           protocol       args           timestamp      │
│  last_seen      status         status                        │
│  status         created_at     created_at     exploits       │
│  created_at                    executed_at    ──────────     │
│                                               tool_name      │
│  adapters       tools                         params         │
│  ─────────      ───────────────────────────   success        │
│  name           id · name · description       output         │
│  ip             category · placement                         │
│  updated_at     binary_b64 · data_tar_b64                    │
│                 argv_template · fields                       │
│                 version · added_at                           │
│                                                              │
│  ⚑  Record IDs with hyphens (tools:env-nxc) must be         │
│     backtick-escaped in SQL; use type::string(id) to         │
│     coerce RecordID → plain string server-side.              │
└──────────────────────────────────────────────────────────────┘

5 · FFI Bridge (Go ↔ Dart)

The entire backend is compiled into a single C shared library. Dart binds to it with zero network overhead.

  Dart/Flutter side                     Go/CGo side
  ────────────────────────────────────────────────────────────────
  
  FfiBridge.instance.runExploit(        GoRunExploit(toolName, paramsJson)
    toolName: "nxc",                    │
    params: {"target": "10.0.0.1"}      ├─ json.Unmarshal(params)
  )                                     ├─ RunToolInMemory(id, params)
  │                                     │    ├─ GetTool(id) ← SurrealDB
  │  Pointer<Utf8>                       │    ├─ memfdCreate + write ELF
  │◀──────────────── *C.char ────────────│    ├─ extractTarGzB64 → /dev/shm
  │                                     │    ├─ BuildArgv(template, params)
  result.toDartString()                 │    └─ exec.Command(argv...).Output()
  jsonDecode(...)                       ├─ CreateExploit(result) ← SurrealDB
                                        └─ jsonStr({success, message, output})

  Memory rule: Go allocates → Go frees.
  GoFreeString() called from Dart after every string consume.

◈ UI Layout

┌──────────────────────────────────────────────────────────────────────┐
│ ● Havoc C2 Platform                              ⚙  ?               │
├──────────────────────────────────────────────────────────────────────┤
│                                                                      │
│   ╔════════════════════════════════════════════════════════════╗     │
│   ║      Network Graph  (force-directed, drag/zoom/pan)        ║     │
│   ║                                                            ║     │
│   ║   [C&C] ──── [session1] ──── [session2]                    ║     │
│   ║      ╲──────────────────────────────── [E&E]               ║     │
│   ╚════════════════════════════════════════════════════════════╝     │
│  ← drag divider to resize ────────────────────────────────────────→  │
│                                                                      │
│  [ ◈ INTERNAL ]    [ ◈ EXTERNAL ]                                    │
├──────────────────────────────────────────────────────────────────────┤
│                                                                      │
│  INTERNAL                       EXTERNAL                             │
│  ┌────────────────────────┐     ┌────────────────────────┐          │
│  │ Sessions │ Listeners   │     │ Exploits │ Footprint   │          │
│  │                        │     │                        │          │
│  │ ┌──────────────────┐   │     │ TOOLS          CONFIG  │          │
│  │ │ 10.0.0.55        │   │     │ ──────────────┬─────── │          │
│  │ │ User: CORP\admin │   │     │ ▼ scanning    │ nmap   │          │
│  │ │ [ONLINE]    >_   │   │     │   nmap        │ ─────  │          │
│  │ └──────────────────┘   │     │ ▶ lateral     │ target │          │
│  │                        │     │ ▶ ad-attacks  │        │          │
│  │ + Create Listener      │     │ ▶ web         │ [ RUN ]│          │
│  └────────────────────────┘     └────────────────────────┘          │
└──────────────────────────────────────────────────────────────────────┘

◈ C2 Agent Protocol

  Agent                         SessionBubble (Go HTTP)
  ─────────────────────────────────────────────────────────
  
  POST /register?id=agent-001
                               CreateSession(id, remoteAddr)
  
  GET  /command-var            ← poll every N seconds
       ← Header: tool: shell
       ← Header: args: whoami
  
  POST /out-var?out=NT AUTHORITY\SYSTEM
                               AppendOutput(port, data)
  
  GET  /get-var                ← operator polls from Flutter
       ← "NT AUTHORITY\SYSTEM\n..."
  
  GET  /Rubeus.exe             ← tool delivery
       ← 302 + binary bytes

◈ Stack

Layer Technology Role
UI Flutter / Dart Cross-platform desktop GUI
FFI dart:ffi + package:ffi Zero-overhead Go↔Dart bridge
Backend Go (CGo, c-shared) C2 logic, HTTP bubbles, tool exec
Database SurrealDB v2 (embedded) Persistent operational state
Execution memfd_create(2) Fileless ELF loading
Isolation bwrap (bubblewrap) Per-tool Debian rootfs containers
Rootfs docker export (flat tar) Portable glibc environments
Comms Raw HTTP over TCP Agent ↔ SessionBubble protocol

◈ Build

Prerequisites

# Go + CGo toolchain
go version        # >= 1.21
gcc --version     # CGo requires a C compiler

# Flutter SDK
flutter --version # >= 3.x

# Runtime
bwrap --version   # bubblewrap for env isolation

Compile the shared library

cd server/
./lib_build.sh
# → server/libh.so

Run the Flutter app

# Copy the library where Flutter can find it
cp server/libh.so server/redc2/

cd server/redc2/
flutter run -d linux

Build a rootfs environment

# Example: impacket environment
docker run --name tmp-env debian:bookworm-slim bash -c \
  "apt-get update && apt-get install -y python3-impacket && exit"
docker export tmp-env | gzip > env-impacket.tar.gz
docker rm tmp-env

# Base64-encode for the binary manifest
base64 -w 0 env-impacket.tar.gz > env-impacket.b64

◈ Tool Schema Reference

// binary.json  — hosted by operator
{
  "id":           "mytool",
  "version":      "1.0",
  "binary_b64":   "<base64-encoded ELF binary>",
  "data_tar_b64": "<base64-encoded .tar.gz rootfs>"  // optional
}

// schema.json  — hosted by operator
{
  "id":          "mytool",
  "name":        "My Tool",
  "description": "Does recon things",
  "placement":   "exploit",          // "exploit" | "footprint"
  "category":    "scanning",
  "color":       "0xFFB71C1C",
  "icon":        "radar",
  "argv_template": [
    "{binary}",                      // replaced with /proc/self/fd/N
    "{target}",                      // replaced with params["target"]
    "--timeout", "30"                // literal args pass through unchanged
  ],
  "fields": [
    {
      "key":      "target",
      "label":    "Target",
      "hint":     "192.168.1.0/24",
      "required": true,
      "keyboard": "text"
    }
  ]
}

◈ Key Engineering Notes

■ SurrealDB record IDs with hyphens  (e.g. tools:env-nxc)
  must be backtick-escaped in raw SQL strings:
    SELECT * FROM `tools:env-nxc`
  Go values passed to DB.Query() stay clean (no backticks).

■ type::string(id) is the correct SurrealDB syntax to coerce
  a RecordID Thing into a plain string server-side.
  Do NOT use: string(meta::id(id))

■ surrealdb.go v0.3.2 does not reliably unmarshal RecordID
  into Go string fields. Always coerce server-side.

■ When SurrealDB returns status=ERR, the Go handler must
  short-circuit before attempting to decode the result field.

■ Debian bookworm-slim (glibc) resolved all crypt_r / statx
  symbol errors that were caused by Alpine (musl) rootfs images.

■ memfd_create syscall number on linux/amd64 = 319
  Requires kernel >= 3.17.

◈ Disclaimer

This project is intended for authorized penetration testing and red team operations only.
Use against systems without explicit written permission is illegal.
The authors assume no liability for misuse.


Built for operators, by operators.

[redc2]  ██████████████░░░░░░  72% operational

About

Fifty_Shades_Of_Red a modern command and control framework for quick CTF action

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors