Evilginx is a man-in-the-middle attack framework used for phishing login credentials along with session cookies, which in turn allows to bypass 2-factor authentication protection.
This tool is a successor to Evilginx, released in 2017, which used a custom version of nginx HTTP server to provide man-in-the-middle functionality to act as a proxy between a browser and phished website. Present version is fully written in GO as a standalone application, which implements its own HTTP and DNS server, making it extremely easy to set up and use.
in working with evilginx
While working with Evilginx, I encountered a problem: some targets were sending their requests via WebSocket, which the tool was unable to intercept. To solve this, a Python script was implemented to do it.
A lightweight TLS-terminating WebSocket proxy for observing, logging, and optionally remapping domains inside message payloads. Useful for debugging WebSocket apps in dev/test environments, load testing, and protocol inspection---with explicit authorization only.
- 🧭 Transparent proxy: Forwards frames between client ↔ target server.
- 📝 Structured logging: Headers + prettified JSON payloads to a rotating session log.
- 🔁 Optional domain remap: Replace
CLIENT_DOMAIN
⇄SERVER_DOMAIN
strings inside message bodies (both directions) to test cross-env scenarios. - 🔒 TLS server: Listens on
wss://HOST:PORT
with your cert/key. - ⚙️ Minimal deps:
websockets
, Python 3.10+.
Before opening the lure link, cd into websocket_proxy and start the proxy:
cd websocket_proxy
python -m venv .venv
source .venv/bin/activate
pip install -r requirements.txt
python websocket_proxy.py
The proxy prints where it's listening and what (if any) domain remap rules are active.
Edit the constants at the top of proxy.py
(or read them from env
vars---see below):
Name Purpose Example
TARGET_URL
Upstream WS endpoint to connect to wss://upstream.example.com/ws
LISTEN_HOST
Local bind address 127.0.0.1
LISTEN_PORT
Local TLS port 8080
SSL_CERT_FILE
Path to certificate (PEM) ./certs/dev.crt
SSL_KEY_FILE
Path to private key (PEM) ./certs/dev.key
LOG_FILE
Where requests/responses are logged proxy.log
CLIENT_DOMAIN
(Optional) String to replace in dev.example.local
client→server messages
If you prefer env-based config, export these before running:
export TARGET_URL="wss://api.example.com/ws"
export LISTEN_HOST="0.0.0.0"
export LISTEN_PORT="8443"
export SSL_CERT_FILE="./certs/dev.crt"
export SSL_KEY_FILE="./certs/dev.key"
export LOG_FILE="./proxy.log"
export CLIENT_DOMAIN="dev.example.local"
export SERVER_DOMAIN="api.example.com"
(Adjust the script to os.getenv
if you want full env support.)
- On client connect, the proxy establishes a corresponding connection
to
TARGET_URL
. - For each frame:
- Attempts JSON parse; if valid, logs a pretty-printed payload.
- Applies string replacement for domain remap (best-effort, safe---no JSON mutation beyond text replace).
- Ships the (possibly modified) message to the other side.
- Request metadata (client IP, path, headers) is logged per session.
- Local UI ↔ Cloud API: Point the UI at
wss://localhost:8080
while the proxy forwards to your cloud WS and logs everything. - Environment parity: Test a build that still references
dev.example.local
by remapping toapi.example.com
on the fly. - Protocol debugging: Inspect message flows and headers without touching the upstream.
Generate a quick self-signed cert (dev only):
openssl req -x509 -newkey rsa:2048 -nodes -keyout certs/dev.key -out certs/dev.crt -days 365 -subj "/CN=localhost"
Then set SSL_CERT_FILE
and SSL_KEY_FILE
to those paths.
Point your client at the proxy:
wss://LISTEN_HOST:LISTEN_PORT/your/path
The proxy connects to TARGET_URL
. Check proxy.log
for headers and
payloads.
- New sessions are marked with a timestamped banner.
- Each direction is labeled:
INCOMING CONNECTION
client -> server (modified)
server -> client (modified)
- JSON payloads are pretty-printed when possible; otherwise raw text is logged.
- For authorized debugging and testing only.
- Do not use for credential collection, phishing, or intercepting third-party traffic without written permission.
- Logs can include sensitive data depending on your app---store and handle logs responsibly.
- Payload mutation is simple string replace; it won't rewrite embedded encodings or binary frames.
- No per-message filters, rate limiting, or backpressure tuning beyond
what
websockets
provides. - Not a drop-in for HTTP proxying; this targets WS/WSS.
- Handshake fails: Verify cert/key paths and that your client trusts the cert.
- No traffic: Confirm your app points to the proxy URL, not the upstream.
- Broken JSON after remap: Rare---set
CLIENT_DOMAIN
/SERVER_DOMAIN
empty to disable remap and retest.
MIT. See LICENSE
.
Built with ❤️ for engineers who like to see what's actually on the wire.
I am very much aware that Evilginx can be used for nefarious purposes. This work is merely a demonstration of what adept attackers can do. It is the defender's responsibility to take such attacks into consideration and find ways to protect their users against this type of phishing attacks. Evilginx should be used only in legitimate penetration testing assignments with written permission from to-be-phished parties.