Skip to main content
Every request that reaches the Apache redirector passes through three sequential security layers. A request must clear all three to reach a C2 backend. Requests that fail at any layer receive the CloudEdge CDN decoy page or a 403 Forbidden response — they are never proxied to Mythic, Sliver, or Havoc.

Layer 1 — redirect.rules (scanner blocking)

/etc/apache2/redirect.rules is included in both the HTTP and HTTPS VirtualHosts:
# Inside redirector-http.conf and redirector-https.conf
Include /etc/apache2/redirect.rules
The file is downloaded at boot from BaddKharma/redRules, an adapted version of curi0usJack’s redirect rules. It contains RewriteCond directives that match requests from known AV vendors, security scanner IP ranges, and TOR exit nodes.

Modifications from the original

ChangeReason
All 302 redirects replaced with 403 ForbiddenAvoids redirect chains that could leak your actual C2 domain
Setup directives stripped (Define REDIR_TARGET, RewriteEngine On, RewriteOptions Inherit)These conflict with the VirtualHost-level configuration already in place
AWS and Azure IP blocks commented outThose ranges include your own C2 server IPs. Leaving them active would block legitimate callbacks from AWS-hosted implant targets

Check the installed rule count

grep -c 'RewriteCond' /etc/apache2/redirect.rules
A typical output is several hundred rules covering AV vendor ranges, commercial scanner CIDRs, and TOR exit nodes.

Update redirect.rules manually

The file is downloaded once at instance boot. To pull the latest version at any time:
curl -sL "https://raw.githubusercontent.com/BaddKharma/redRules/main/redirect.rules" \
  -o /etc/apache2/redirect.rules
sudo systemctl reload apache2
Scanner blocking is controlled by the enable_redirector_htaccess_filtering Terraform variable. It defaults to true (enabled) and is automatically set to false in closed-environment mode (HTB/VL/PG). In closed mode, redirect.rules is replaced with a comment-only placeholder and no IP blocking is applied.

Layer 2 — header validation

Requests that pass scanner blocking must include a specific HTTP header with the correct token. Without it, Apache falls through to the DocumentRoot (/var/www/html/decoy/) and serves the CloudEdge CDN maintenance page.

Header configuration

SettingDefaultTerraform variable
Header nameX-Request-IDc2_header_name
Token valueAuto-generated at deploy timec2_header_value (leave empty to auto-generate)
Retrieve the active header and token after deployment:
terraform output deployment_info
Look for the C2 Header line in the output:
C2 Header: X-Request-ID: <your-token>

How it works in Apache

Each proxy rule in both VirtualHosts is conditional on the header:
# Request must carry the correct X-Request-ID value
RewriteCond %{HTTP:X-Request-ID} ^<your-token>$
RewriteRule ^/cdn/media/stream/(.*) http://<mythic-private-ip>/$1 [P,L]
If the condition is not met, the RewriteRule is skipped. With no matching rule, the request falls through to the decoy page. The check is repeated independently for each C2 backend.

Testing header validation

# Should return the CloudEdge CDN decoy page (no header)
curl -s http://localhost/ | head -5

# Should be proxied to Mythic (correct header)
curl -v -H "X-Request-ID: <your-token>" http://localhost/cdn/media/stream/

Layer 3 — URI prefix routing

A request with a valid header is routed to the C2 backend whose URI prefix matches the request path. Each framework uses a distinct CDN-style prefix to blend in with normal web traffic.

Default URI prefix table

URI prefixBackendTerraform variableHow the path is forwarded
/cdn/media/stream/Mythicmythic_uri_prefixPrefix stripped — Mythic receives the remainder
/cloud/storage/objects/Sliversliver_uri_prefixPrefix stripped — Sliver receives the remainder
/edge/cache/assets/Havochavoc_uri_prefixFull path preserved — Havoc receives /edge/cache/assets/...
URI prefixes are baked into agent payloads at deploy time. Changing them after generating payloads requires regenerating all agents.

Mythic and Sliver — prefix stripped

For Mythic and Sliver, Apache strips the URI prefix before forwarding:
# Mythic: /cdn/media/stream/update -> http://<mythic-ip>/update
RewriteCond %{HTTP:X-Request-ID} ^<token>$
RewriteRule ^/cdn/media/stream/(.*) http://<mythic-private-ip>/$1 [P,L]
ProxyPassReverse /cdn/media/stream/ http://<mythic-private-ip>/
An Apollo agent posting to /cdn/media/stream/update reaches Mythic as a POST to /update.

Havoc — full path preserved

For Havoc, Apache preserves the entire path including the prefix:
# Havoc: /edge/cache/assets/update -> http://<havoc-ip>/edge/cache/assets/update
RewriteCond %{HTTP:X-Request-ID} ^<token>$
RewriteRule ^(/edge/cache/assets/.*) http://<havoc-private-ip>$1 [P,L]
ProxyPassReverse /edge/cache/assets/ http://<havoc-private-ip>/
Havoc’s listener validates that incoming URI paths match the demon’s configured paths exactly. Do not strip the prefix for Havoc. If you see Havoc demons connecting but receiving no tasks, check that the URI prefix in the listener matches the prefix in redirect.rules and the demon configuration.

SSL termination

SSL terminates at the redirector. All traffic forwarded through VPC peering to C2 backends is plain HTTP, regardless of whether the implant connected over HTTPS. C2 servers listen on port 80; the redirector handles TLS on their behalf.
Implant ──HTTPS:443──► Redirector (TLS termination)

                              │  plain HTTP:80

                       Mythic / Sliver / Havoc

Inspect the live Apache configuration

SSH to the redirector and view the active VirtualHost files:
sudo cat /etc/apache2/sites-available/redirector-http.conf
View all active VirtualHosts and their ports:
sudo apache2ctl -S
Expected output:
VirtualHost configuration:
*:443                  yourdomain.tld (/etc/apache2/sites-enabled/redirector-https.conf:1)
*:80                   yourdomain.tld (/etc/apache2/sites-enabled/redirector-http.conf:1)

Build docs developers (and LLMs) love