Evilginx Pro 4.2 - Anti-phishing evasions and more!
It's been about five months, already, since the release of Evilginx Pro, and I'm proud to release the second major update. This release was hugely influenced by the feedback I received from multiple Evilginx Pro users I met at the x33fcon conference, which took place in Gdynia in June 2025. Being able to communicate with you directly to exchange ideas and learn how you use the tool gives me confidence that I'm moving in the right direction with the implementation of new features.
At x33fcon this year, I also had the opportunity to give a talk about the various anti-phishing techniques I've come across in recent years and how an attacker would approach to evade them. The video of the talk can be found below:
I've recently released the 4.2 update for Evilginx Pro and thought it would be a good idea to share what's new and how to use the latest features.
Without further ado, let's jump into the changes, starting with the most significant ones.
Proxy engine rewrite
The first and most significant change is the complete rewrite of the proxy engine. Back when I released Evilginx 2.0, I had only just started learning programming in the Go language. The code quality I produced then was... mediocre at best 😅. The proxy code residing in http_proxy.go
eventually took the form of spaghetti and quickly spiralled out of control. The code worked, but at times caused Evilginx to behave erratically.
The eight-year-old legacy code made it impossible to add any new features, because even the most minor additions risked causing the whole proxy logic to crumble like a Jenga tower. Since this part of the code became the core of Evilginx, I was pretty reluctant to touch it, in order not to break the tool's main functionality. With the release of Evilginx Pro, it became clear that the time had come for a complete rewrite.
The proxy engine rewrite was introduced with Evilginx Pro version 4.1. All of the proxy components now work together much more reliably, and most importantly, the proxy code is now ready to give more power to the users who'd like to have complete control over the live modification of HTTP packets in transit. Full use of these changes will be made when Phishlets 4.0 format is released in future updates.
Phishlets 4.0 (not yet available) will allow you to:
- Capture data from the request & response HTTP headers.
- Capture data from the request & response HTTP content body.
- Inject custom headers into HTTP requests & responses.
- Modify the values of the request & response HTTP headers.
- Modify the content body in HTTP requests & responses.
Additionally, here are the most notable tweaks introduced with the proxy engine rewrite:
Improved HTML injection
Evilginx, from the beginning, has utilised string pattern recognition to identify suitable locations for injecting its meta tags or JavaScript code blocks into proxied HTML content. This approach was very error-prone, as regular expressions would sometimes miss the pattern detection, resulting in crucial injections being omitted.
After the changes, Evilginx will now correctly parse the whole structure of the HTML document, looking for the specific object types. When performing the injection, rather than inserting a string into the HTML code, it will generate a new HTML object, which will later get properly formatted when the HTML content is rendered to a string.
This change now allows you to select the location where you'd like to inject your JavaScript js_inject
injects.
js_inject:
- trigger_domains: ["login.microsoftonline.com"]
trigger_paths: [".*"]
location: "<location_string>"
Where the <location_string>
can be one of the following:
Location | Description |
---|---|
head |
Inject at the end of the <head> tag. |
body_top |
Inject at the beginning of the <body> tag. |
body_bottom |
Inject at the end of the <body> tag. |
Phishlet collision fixes
One of the fixes, which needs mentioning, is the fix when handling multiple active phishlets which target the same destination hostnames.
Let's say you have two phishlets phishlet1
and phishlet2
. Both of them have the same hostname defined in proxy_hosts
. Evilginx now allows you to enable both phishlets as it will now recognise the target hostname based on the defined phishlet's hostname, rather than the target hostname.
Redirector fixes
If you had a redirector set up for your lure and you generated the lure URL with embedded custom parameters, Evilginx would lose the custom parameters and fail to forward them when the redirector redirected to the phishing page. The forwarding of custom parameters is now fixed, allowing you to use redirectors to their full potential.
Anti-phishing evasion
One of the most prominent features, released in update 4.1, was the option in phishlets to rewrite URL paths. Rewriting URL paths, while reverse proxying website content, allows you to protect your phishing pages from URL path pattern detection implemented by Google Chrome Safe Browsing protection.
Keep in mind that the following information is entirely speculative and the conclusion is based on my trial & error testing, rather than on reverse engineering the code of the detection engine.
If you reverse proxy the Google sign-in page and Safe Browsing kicks in, it will try to match the URL path and URL query to one of its known patterns. As an example, Safe Browsing will see the following phishing URL:
https://accounts.phishing.com/v3/signin/identifier?followup=https%3A%2F%2Faccounts.google.com%2F&ifkv=ABCD01234&passive=0123456789&flowName=GlifWebSignIn&flowEntry=ServiceLogin&dsh=XYZXYZ1234
It will first detect the URL path, which it marks as the known URL path for the Google sign-in page: /v3/signin/identifier
Then it will try to match the keys and values of the URL query to look for known patterns. The keys it may match are as follows: followup
, ifkv
, passive
, flowName
, flowEntry
or dsh
.
Once it determines that the page, based on the URL path & query, must be the Google sign-in page, it will check the website's domain. That's when it will see phishing.com
, instead of google.com
and detection will be triggered.
What I managed to figure out is that when the reverse proxy rewrites the URL path and modifies the URL query of the requests, Safe Browsing will have trouble detecting the phishing page, even with Enhanced protection turned on (it allegedly uses AI, so you know it must be good 😉).
That's where the new rewrite_urls
feature comes in, exclusive to Evilginx Pro. We will now analyse the rewrite URL rules in the phishlet created to proxy the Microsoft 365 sign-in page:
rewrite_urls:
- trigger:
domains: ['login.microsoftonline.com']
paths: ['/common/oauth2/v2.0/authorize']
rewrite:
path: '/signin'
query:
- key: 'boop'
value: '{id}'
exclude_keys: ['client_id']
The MS365 sign-in page for our following example has the following URL:
https://login.microsoftonline.com/common/oauth2/v2.0/authorize?client_id=4765445b-32c6-49b0-83e6-1d93765276ca&response_type=code%20id_token&scope=openid%20profile%20https%3A%2F%2Fwww.office.com%2Fv2%2FOfficeHome.All&response_mode=form_post&nonce=XXXYYYZZZ&client-request-id=f4978b5c-2855-46a9-9d58-18c850118fc5&state=ABCDEF0123
Once the phishing link is opened in the browser, Evilginx Pro will attempt to proxy the URL above to display its contents to the user, and the phishlet's rewrite URL rules will trigger.
The proxy engine will detect a forwarded request to the URL with the hostname login.microsoftonline.com
and URL path /common/oauth2/v2.0/authorize
. Evilginx will then generate a new, rewritten URL and return it in the Location
header of an HTTP 302 redirection response to redirect the user's browser:
https://login.phishing.com/signin?client_id=4765445b-32c6-49b0-83e6-1d93765276ca&boop=123456789
The proxy engine will also map the generated URL to the original URL that served as a trigger for the URL rewrite. Once the user's web browser switches to the rewritten URL, the new HTTP request will be sent to the rewritten URL.
When Evilginx receives the request, it sees that the provided URL is mapped to the previously cached URL, which triggered the rewrite URL rule and the URL the proxy forwards the request to is replaced with it. That way, Evilginx can seamlessly forward the spoofed URL, converting it to the legitimate one in real-time.
The rewrite:path
value dictates the URL path that the proxy will redirect to. The rewrite:query
includes the query keys with values, which will be added to the URL. At least one of the injected query parameters must contain the placeholder {id}
within its rewrite:query:value
field. It does not have to match exactly a string {id}
, but it can also be combined with another string, e.g. something-{id}-token
.
The rewrite:exclude_keys
array contains the names of keys that Evilginx will preserve, preventing them from being rewritten. You can see in the example above that the generated rewritten URL preserved the key client_id
with its original value. Some websites use JavaScript to access GET query parameters from the URL address bar. Removing these parameters may break the website's functionality. You need to verify yourself if the website starts acting up when GET parameters are removed. Of course, preventing too many GET parameters from being rewritten may result in pattern matching triggering detections, so use this with caution.
DNS providers
Update 4.2 expands on the number of DNS providers supported by Evilginx Pro. The tool now allows for automatic management of DNS records for the following providers:
- Route53 (AWS)
- Cloudflare
- Gandi.net
For demonstration purposes, let's go through the process of setting up the Route53 DNS provider from AWS.
How to configure the Route53 DNS provider with Evilginx Pro?
At this point, I assume you already have an AWS account with access to the admin dashboard. The first step is to create a new user for managing DNS records on Route53 via Identity and Access Management (IAM).
Once you pick the user name, you will be asked to specify the permissions for the created user. Since we want the user to have full access to Route53 domain management, select Attach policies directly and look for AmazonRoute53FullAccess
policy name.
To keep things clean, you can create a group with the mentioned policy and add the user to this group instead.
AmazonRoute53FullAccess
permission policy for the created user.Once the user is created, we will need to create an access key that will be used to configure Evilginx Pro for automatic DNS record management on Route53.
When asked to provide the use case, pick Other
.
With the access key created, you will need to copy the Access key and Secret access key and store them in a safe place, preferably in a password manager. These keys will need to be entered to configure the Route53 DNS provider in Evilginx Pro.
Now that you have created the access keys for Route 53 domain management, go to your Route 53 dashboard, open the Hosted Zones management panel and create a new hosted zone.
Enter the name of your registered domain and click the Hosted zone details dropdown to see the nameservers you need to point your domain registrar to.
You will now need to go to your domain registrar, where you purchased the domain, and set up custom nameservers for your domain by entering all of the nameserver hostnames from the list you obtained at Route 53. Keep in mind that DNS propagation worldwide usually takes up to an hour, but may also take up to 72 hours.
With everything set up, we can use the obtained access keys to configure the DNS providers in Evilginx Pro.
In the Evilginx Pro client, connect to the server you want to use the domain we set up with Route 53 and add the domain:
domains add phishing.com
Configure the domain to be managed by the Route 53 DNS provider with the generated access keys:
domains config phishing.com route53 access_key_id=<access_key> secret_access_key=<secret_access_key>
Once this is done, you can test if everything worked by typing:
domains list phishing.com
You should see the list of all of your DNS records obtained through the DNS provider. If there is an error, please investigate the error message and use it for troubleshooting. The most likely issue would be that you're using the access key for an account with insufficient permissions.
If you prefer to use Cloudflare as the DNS provider for your domain, follow the same steps:
- Onboard your domain on Cloudflare
- Obtain the Cloudflare name servers and set them for your domain in your domain registrar admin panel.
You will need to create the Cloudflare API token to use with Evilginx Pro. Set it up later in the client with the command:
domains config <domain> cloudflare api_token=<api_token>
Lures
Custom hostnames for Lure URLs are back
In version 4.1, I have removed the ability to configure custom hostnames for lure URLs. I made this decision due to issues with Evilginx proxy session management, through cookies stored for specific phishlet hostnames. The problems became apparent when the lure URL hostname had an entirely different hostname suffix than the hostname configured for the particular phishlet.
Suddenly, Evilginx had to manage TLS certificates for an entirely different hostname (since wildcard certificates work only for a subdomain one level up), and cookies to manage Evilginx proxy sessions had to be set for multiple domains, to recognise the same visitor across redirects. Since web browsers do not allow web servers to set cookies for a different domain than the domain of the server responding to the request, additional effort would have to be made to redirect between hostnames with one-shot tokens sent through GET query parameters. I did not want to overcomplicate this process.
As for version 4.2, the customisable hostnames for lure URLs are back, although in a more simplified form. What Evilginx allows you to do now is only customise the first subdomain of the lure hostname.
For example, if you had your Google phishlet hostname set to notgoogle.phishing.com
, the lure URL hostname would become accounts.notgoogle.phishing.com
, since the primary sign-in subdomain for that phishlet is hardcoded to be accounts
. Now you can change the subdomain prefix to be anything you want:
lures set <id> hostname www.notgoogle.phishing.com
This will change the lure hostname to www.notgoogle.phishing.com
. You can pick any hostname you want as long as you only change the top subdomain of the phishlet's hostname. The hostname needs to end with notgoogle.phishing.com
in this example.
I also added a feature in version 4.2, which was not available in previous versions of Evilginx. You can now specify an empty subdomain for your lure URL hostname. This is extremely helpful if you want your phishing URL hostname to consist entirely of your domain name.
First, you need to set your phishlet's hostname to your top-level domain name:
phishlets set <phishlet_name> hostname phishing.com
Your Google phishlet's lure URL hostname would become accounts.phishing.com
, so we need to remove the subdomain with:
lures set <id> hostname phishing.com
Now your lure URL hostname will become your top-level domain name and will look similar to this:
https://phishing.com/whatever/path/you/want.pdf
Custom URL queries are now allowed
Another addition in 4.2 is the ability to set up custom URL query parameters for your lure URLs. This was not possible before, as Evilginx would strip the URL queries from the customised lure URL path.
You can now add your own URL query parameters (like archType=x64
) to make your lures better fit your social engineering pretext, thus making them more believable:
lures set <id> path /client/6.5.9.11873/ZoomInstallerFull.exe?archType=x64
Custom parameters can now be encrypted
Evilginx allows passing custom parameters through lure URLs. These parameters are encoded with base64 and embedded as a GET query parameter within the generated lure URLs. The following command allows for embedding custom parameters email
and from_name
in your lure URL:
lures get-url <id> email=target@email.com from_name=Kuba
This will generate the following URL:
https://phishing.com/documents/Annual_Bonus_Report_2025.pdf?yui=dK-1zTbo6N5Bg9RYqI1QxahXeyIsHVPvKk87oDmFLKIm8hTWbMABxPynIVBaY1imev4mAkf4aRV-Yh4E4MwOKg
The custom parameters can be used to customise the HTML content of your Redirectors, or they can be used in your js_inject
JavaScript injects. Custom parameters are also used internally by the custom Gophish integration for Evilginx.
Until now, the value dK-1zTbo6N5Bg9RYqI1QxahXeyIsHVPvKk87oDmFLKIm8hTWbMABxPynIVBaY1imev4mAkf4aRV-Yh4E4MwOKg
could have been decoded by anyone who knew how, as Evilginx has never used encryption to protect the contents of the passed custom parameters. The source code for encoding custom parameters is publicly available, and I'm pretty sure that defenders have already discovered how to utilise custom parameter decoding for phishing URL detection. If you enumerate all GET parameter values of the URL and find one that can be successfully decoded, you can be 100% sure the link was generated with Evilginx.
Changes in Evilginx Pro version 4.2 now include an option to use AES-256 encryption, protecting your custom parameters embedded within generated lure URLs.
The only thing you need to do is configure the encryption key on your Evilginx Pro server with:
config enc_key your-custom-encryption-passphrase
Leaving the enc_key
config option empty will use the previous method of encoding, which anyone can decode.
This change significantly impacts the Gophish integration for Evilginx, which also had to be updated, as covered in the next section.
Gophish integration
[x] Gophish: Added an option to submit captured credentials to the Gophish server. Configure with command: `config gophish submit_credentials <true/false>`
[x] Gophish: Added an option to send the SHA256 hash of the captured password to the Gophish server instead of the clear text version. Configure with command: `config gophish hash_passwords <true/false>`
The Gophish integration for Evilginx was updated to version 0.12.2 from version 0.12.1. This update introduces the encryption key support for custom parameters. If you configured enc_key
on your Evilginx Pro server, you MUST enter the passphrase in the Lure Encryption Key field when creating a new Gophish campaign.
You can also now use Lure URLs in Gophish with predefined GET query parameters or with your own custom parameters you want to pass to your Redirectors or JavaScript injects. Gophish will properly repackage the Lure URL with added Gophish-specific parameters for campaign tracking.
Send captured credentials to Gophish
The long-requested feature has finally arrived in 4.2. Evilginx Pro can now send the captured credentials to the Gophish instance. This feature is turned off by default due to security reasons. You can enable it on your server with the command:
config gophish submit_credentials true
If you prefer not to send the captured passwords in clear-text, you can opt for a more secure option to send an SHA-256 hash of the passwords instead. You can enable this feature with the command (submit_credentials
needs to be set to true
):
config gophish hash_passwords true
Phishlets
[x] Phishlet's enabled status can now be determined by looking at the list of `lures` with disabled phishlets being grayed out and enabled ones in color.
[x] Phishlet's hostname is now visible when listing the configured `lures`.
You can now see if the phishlet the lure refers to is disabled or enabled when listing your lures. The lures with a coloured phishlet name are enabled, while the greyed-out ones are not.
You can now also see the phishlet's configured hostname directly in the lures list table.
A minor tweak was added to warn you if the phishlet the lure refers to is disabled when you generate the lure URL:
Too many times have I opened the lure URL of a disabled phishlet, trying to figure out what happened when the phishing page would not load.
Javascript obfuscation
[x] Improved the speed of multi-threaded Javascript obfuscation by increasing the number of concurrent socket connections to the Evilpuppet instance.
[x] Javascript obfuscation level can now be changed with command: `config obfuscation javascript <off/low/medium/high/ultra>`
The performance of dynamic JavaScript obfuscation was improved by increasing the number of concurrent socket connections to the Evilpuppet instance.
You can now also control the intensity of JavaScript obfuscation of proxied web content with the command:
config obfuscation javascript <off/low/medium/high/ultra>
The following table explains what each setting does:
Level | Description |
---|---|
off |
Obfuscation completely disabled (not recommended). |
low |
Evilginx built-in injected scripts and scripts injected via js_inject through phishlets are obfuscated. |
medium |
Everything above and all scripts inlined within HTML pages are obfuscated. (recommended) |
high |
Everything above and all scripts requested through <script src='...'></script> tags are obfuscated. |
ultra |
Everything above and all scripts dynamically generated for HTML obfuscation are obfuscated (may significantly slow down page loads). |
It is recommended to use the medium
option for JavaScript obfuscation as it provides the best balance between speed and the risk of detection.
Fixes
I've finally managed to resolve the infinite redirects issue when triggering lure URLs on conflicting URL paths.
Let's say you had your phishing lure URL path defined as /login
, and the target's website sign-in page was also hosted at /login
URL path. Once the lure URL was clicked on /login
URL path and all BotGuard checks have completed, Evilginx would redirect the visitor to path /login
on the same hostname, which is the URL path of the target website's sign-in page. When the redirect hit, Evilginx would detect /login
URL path as the trigger for the phishing lure URL, and it would repeat the cycle. The redirects would continue until the web browser got tired of them, but more importantly, they would prevent the redirection to the sign-in page.
The temporary fix I released with version 4.1.1 would make every phishing lure URL trigger ONLY ONCE to prevent any lure retriggers in the same proxy session. The drawback was that if the same user clicked the same phishing lure URL again, it would not redirect to the sign-in page at all, since technically the lure URL would work only once. This was not ideal, and I had to come up with a better solution.
I took advantage of the fact that most of the redirects happen internally through HTTP 301/302 responses. This gave me an opportunity to detect them and add unique GET query markers to the Location
header URLs to make them ignored by lure URL triggers.
Other changes
[x] Added an option to enable debug output in the server logs for troubleshooting purposes. Configure with command: `config debug <true/false>`
[x] Added support to deploy servers running on Ubuntu 24.10.
The other minor changes that were added made it possible to enable debug output on the Evilginx Pro server easily. Just type the command:
config debug true
Then, after you SSH to your Evilginx Pro server, type:
sudo journalctl -a -u evilginx
This will allow you to browse the full server log for troubleshooting, which is sometimes very useful when debugging issues with phishlets during the engagements.
Oh, and you can now use automated deployment with servers deploy
command to deploy Evilginx Pro servers to servers running on Ubuntu 24.10 due to popular demand.
Closing thoughts
Evilginx Pro is currently in the state I envisioned it to be for release. Given the significant time spent on development (and hacking cons) over the past months, sacrifices had to be made. One of the essential sacrifices was the documentation. I wanted to let you know that I am aware the documentation is currently lacking in content (as of August 2025), and it is the next thing on the list I will be addressing. There is obviously no point in releasing new features without letting everyone know about them and how to use them. This blog post is just the first step.
I have several ideas and modules lined up that I want to work on. Some of them are:
- Evilginx 4.0 CE - Release the new open-source version with the proxy engine rewrite and the SQLite support to the public.
- Phishlets 4.0 - Redo the phishlets format with a cleaned-up structure and syntax. Make it easier to create new phishlets and add support for embedding static web page content into the phishlets.
- Evilpuppet for MFA flow automation - New way to perform phishing attacks, focusing just on reverse proxying the MFA flow. This will probably be a talk I give next year!
I want to thank all of you for your continued support. ❤️
I'm incredibly lucky to be able to do this full time, and it wouldn't be possible without you giving me the benefit of the doubt.
If you haven't already, make sure to apply and join our BREAKDEV RED community for red teamers. Our secure Discord server is a great place to discuss everything related to cybersecurity, and I'd love to see you there!
If you're already using Evilginx Pro and you'd like something improved or added, feel free to let me know via email, on Twitter @mrgretzky or on LinkedIn.
Happy phishing! 🪝🐟
P.S. Nothing in this blog post was generated with LLMs. Let's keep the internet created by humans for humans.