Skip to content

IceCubeSandwich/CaddySmith

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

3 Commits
 
 
 
 
 
 

Repository files navigation

CaddySmith

CaddySmith is a small Python script that reads a Cobalt Strike or Sliver C2 profile and forges a Caddy web server config out of it. The generated Caddyfile turns a regular Linux box into a redirector: legitimate beacon traffic gets reverse-proxied to your team server, and anything else (scanners, search bots, blue team probes, random curl) gets bounced to a decoy URL.

I wrote this because Caddy is a much friendlier web server to stand up quickly than Apache, single static binary, automatic Let's Encrypt certs, no a2enmod dance.

Authorized engagements only. This is offensive security tooling. Only run it against environments you have written permission to test.

Supported profile formats

CaddySmith auto-detects the format from the file contents:

  • Cobalt Strike — the classic text format with set uri "/foo" directives. Each HTTP-GET / HTTP-POST URI becomes its own exact-path route with the profile's client headers enforced.
  • Sliver — the JSON implant config exported from Sliver. Sliver doesn't have fixed URIs; it generates them at beacon-build time from path / file / extension lists. CaddySmith handles this by matching the top-level path prefixes as globs (e.g. path /api* /static* /resources*) and doing a substring match on the Chrome build number from the UA (which survives Sliver's per-platform UA rewrites).

You can also force a specific parser with --profile-type cobaltstrike or --profile-type sliver.

What it does

Given a profile file, the script pulls out:

  • The User-Agent string
  • URIs (CS) or path prefixes (Sliver)
  • Client-side headers (CS only — Sliver doesn't dictate specific headers)
  • The Host header (used as the redirector's domain name if you don't pass --server-name)
  • Whether staging is enabled (CS only — set host_stage)

Then it builds a Caddyfile that:

  1. (Optional) Returns 403 to plain HTTP traffic
  2. Blocks ~15 known-bad user agents (curl, nmap, sqlmap, Googlebot, etc.)
  3. Blocks direct-IP access and any HTTP method that isn't GET/POST
  4. Reverse-proxies only the profile's URIs (or prefixes for Sliver), gated on the matching User-Agent
  5. Blocks common scanner probe paths (.env, /wp-admin, .php, etc.)
  6. Redirects everything else to your decoy URL

Requirements

  • Python 3.7+ (uses only the standard library, no pip install needed)
  • Caddy 2.x on the redirector itself (install guide)

Quick start

python3 caddysmith.py my.profile \
    --backend https://teamserver.internal:443 \
    --decoy   https://www.example.com/ \
    --server-name redirector.example.com \
    --email   you@example.com \
    --forbid-http \
    -o redirector.caddy

This writes the generated config to redirector.caddy and prints a summary of what it did to stderr.

To deploy it on the redirector, drop the file in /etc/caddy/ and import it from your main Caddyfile:

# /etc/caddy/Caddyfile
import /etc/caddy/redirector.caddy

If you passed --email (recommended), the generated snippet already contains the global options block, so the main Caddyfile only needs the import line. If you didn't, add the email manually to a { } block above the import.

Then validate and reload:

sudo caddy validate --config /etc/caddy/Caddyfile
sudo systemctl reload caddy

Caddy will auto-provision a Let's Encrypt cert for the domain in --server-name as long as the A record points at the redirector.

All the flags

Flag Default What it does
profile (required) Path to the .profile file
--backend https://teamserver.local:443 Where to proxy matched traffic
--decoy https://www.example.com/ Where unmatched traffic redirects
--server-name c2.example.com Your redirector's domain name
--policy strict strict, lax, or none
--profile-type auto Force cobaltstrike or sliver (default: auto-detect)
--extra-uri PATH Extra URI to proxy (with UA check). Can repeat.
--lax-uri PATH Extra URI to proxy (no checks). Can repeat.
--allow-ua STRING Extra UA allowed on --extra-uri routes. Can repeat.
--forbid-http off Return 403 on plain HTTP
--email EMAIL Email for Let's Encrypt registration and renewal notices
-o, --output FILE stdout Write the config to a file

Policy modes

  • strict (default) — checks User-Agent, all client headers, direct-IP, HTTP method, and probe paths
  • lax — only blocks bad user agents, no per-route header matching
  • none — proxies any request to a profile URI, no filtering

Strict is what you usually want. Lax is useful when you're debugging why a real beacon isn't connecting.

A worked example

Say you have a profile that mimics an Amazon endpoint and you want to deploy it on redirector.0xtb.sh:

python3 caddysmith.py amazon.profile \
    --backend https://10.1.1.10:443 \
    --decoy   https://www.amazon.com/ \
    --server-name redirector.0xtb.sh \
    --forbid-http \
    --policy strict \
    -o /etc/caddy/redirector.caddy

The summary shows you exactly which routes got built, e.g.:

Routes built:   2
  - [profile-get] /broadcast
  - [profile-post] /1/events/com.amazon.csm.csa.prod

If you regenerate and see no routes, the script probably failed to parse your profile — check the warnings on stderr.

Sliver example

For a Sliver implant config (JSON):

python3 caddysmith.py sliver-implant.json \
    --backend https://10.1.1.10:443 \
    --decoy   https://docs.godotengine.org/ \
    --server-name gdscript.rocks \
    --email   you@example.com \
    --forbid-http \
    --policy strict \
    -o /etc/caddy/redirector.caddy

The summary will tell you that Sliver was detected and show the prefix route:

Profile type:   sliver
Routes built:   1
  - [sliver] /api /public /resources /services /static (prefix)

Because Sliver generates URIs randomly from path × file × extension combinations, the generated path matcher uses prefix globs (path /api* /public* /resources* /services* /static*) rather than exact paths. The User-Agent matcher uses a substring of the Chrome build number (e.g. 3921.146), which Sliver preserves across its per-platform UA rewrites.

Smoke tests after you deploy

# Plain HTTP should be 403'd (if you used --forbid-http)
curl -I http://redirector.0xtb.sh/

# Bare hostname should redirect to the decoy
curl -kI https://redirector.0xtb.sh/

# Bad UA should also redirect
curl -kI -A "curl/8.4.0" https://redirector.0xtb.sh/broadcast

# A request with the right UA + path should proxy through (200)
# You need to also send all the profile's client headers in strict mode.

Known limitations

  • Staging rules aren't generated (Cobalt Strike). If your CS profile has set host_stage "true" (or doesn't set it at all), the script will warn you and skip stager URIs. Add set host_stage "false"; to your profile, or pass the stager URIs explicitly via --extra-uri.
  • Sliver prefix matching is wider than CS exact matching. With Sliver, the proxy route claims any URL starting with /api, /static, etc. Scanner probes that happen to use those prefixes (e.g. /api/.env) will be sent to the team server rather than blocked locally — but Sliver's own HTTP transport authenticates via implant ID, so unauthorized requests get rejected at the C2 layer. The UA gating still keeps most scanners out.
  • One backend per run. Every route proxies to the same --backend. If you need multiple team servers, run the script multiple times and merge by hand.
  • Profile URI parsing is line-based (Cobalt Strike). set uri "/path1 /path2"; works (multiple paths on one line), but unusual formatting might trip up the parser. Check the route list in the summary to confirm.
  • HTTPS backend cert verification is off by default. Team servers usually have self-signed certs, so the generated config includes tls_insecure_skip_verify. If your backend has a real cert, delete that line from the generated file.

Credits

The Apache-based Malleable-Redirector was the starting point for what this script generates, same three-track URI model (profile URIs / extra URIs / lax URIs), same policy modes, same general layout. CaddySmith just translates the output to Caddy syntax instead of Apache .htaccess.

License

MIT

About

Generate Caddy redirector configs from Cobalt Strike or Sliver C2 profiles. Supports strict header matching, scanner blocking, and Let's Encrypt out of the box.

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors

Languages