See Write-up: https://www.linkedin.com/pulse/punishing-aitms-using-css-flask-jay-kerai-ffrhe
The aim of this fork is add the ability to alert the user to not put their credentials in against AiTM/Evilgnix, this is based of the work of CIPP. Additionally I added supporting code for a teams webhook for alerting SecOps Teams. Note that this can be defeated trivially by rewriting the CSS (see https://nicolasuter.medium.com/aitm-phishing-with-azure-functions-a1530b52df05) You can however flip the logic and only get users to authenticate ONLY if they see the "safe" image as the sign-in box background. This would severly hinder distributed attacks, however targeted attacks would still be possible. (Ideally this should be unique across all tenants to mitigate any CSS manipulation)
I needed a "serverless" solution for hosting for VPS and a local one so I've included both, deploy whichever you need! (gunicorn app:app)
Server-side:
Output below is from Render free tier
See Defending against AiTMs/Phishing: https://www.linkedin.com/posts/jay-kerai-cyber_devfender-entra-token-activity-7122902992873287681-P03M & https://github.com/jkerai1/So-You-ve-Got-MFA-Defending-and-Responding-Against-MFA-Bypass-Techniques-in-Entra
Example of being served back "safe.png":
CSS:
.ext-sign-in-box
{
background-image: url('https://{Your Domain or IP}/companyBranding.png');
}
We can use a web App to do this with the gunicorn startup command (gunicorn app:app)
I have used github auth with github action to auto deploy. I suggest forking the repo first and authorize to your own fork:
Then we add the web app URL (Optionally we can bind our web app to a custom domain) to our CSS to be uploaded to company branding in Entra (see CSS Above). Azure can handle the TLS/SSL for us too.
The clarion call tells you if someone is logging into an AitM proxy that is proxying your company's M365 login page
This is extremely experimental and can be defeated:
Here is a good example of swapping referrer to appear legitimate https://github.com/rvdwegen/vdwegen-api/blob/main/tenantDetails/run.ps1#L106
Also See: https://insights.spotit.be/2024/06/03/clipping-the-canarys-wings-bypassing-aitm-phishing-detections/
This article from Zolder describes the concept quite well. This is not my original idea and the credit goes to them for it.
M365 allows you to inject custom CSS into the M365 login screen through the Company Branding settings. Ostensibly, this allows you to put a cool background image on your login page that fits your company branding.
We can take advantage of this to detect when a user is logging into an Adversary in the Middle (AitM) proxy like Evilginx that is mimicking your legitimate login page.
Clarion creates and hosts a small tracking pixel that we can embed into our custom company branding CSS file. When a normal login occurs, this CSS is retrieved dynamically and rendered on the M365 login page. When that happens during a routine login, the referer header for the CSS retrieval is login.microsoftonline.com
However, if a threat actor has created an AitM domain and login page and someone puts their username in to log in, the CSS will get pulled by the transparent proxy and the referer header will be the AitM proxy domain. If our tracking pixel is ever requested with a referer header that is NOT login.microsoftonline.com
, it is highly likely that someone is logging into a transparent proxy (DISCLAIMER: MAJOR ASSUMPTIONS WITH THIS CONCEPT. DO YOUR OWN TESTING TO MAKE SURE THIS IS THE CASE!).
Zolder uses their own site, didsomeoneclone.me as their proof of concept. It works like a charm! But I wanted to create the whole system to learn more about it and demonstrate the entire process, start to finish. Thank you to Zolder for their work on this! Really cool concept and great work.
Additionally, I'm sure there are people out there that would love to use this detection capability but want to host it on their own infrastructure for privacy reasons. Clarion demonstrates a very simple, naive approach to how one would implement the tech required to alert on AitM attacks in progress.
For testing, you can use a self-signed certificate.
openssl req -x509 -newkey rsa:4096 -nodes -out cert.pem -keyout key.pem -days 365
Important: Make sure you accept the warning about the invalid certificate in the browser that you want to test if using a self-signed cert. If you don't, the client browser will throw an error when trying to load the remote CSS and will not trigger the clarion call.
For production use, you would want a legitimate signed certificate.
Clone the repo to your publically accessible host and install the requirements:
$ pip3 install -r requirements.txt
Then run the app (you need root level permissions to bind to port 443):
$ [sudo] python3 app.py
* Serving Flask app 'app'
* Debug mode: on
WARNING: This is a development server. Do not use it in a production deployment. Use a production WSGI server instead.
* Running on all addresses (0.0.0.0)
* Running on https://127.0.0.1:443
* Running on https://[Public IP]:443
Press CTRL+C to quit
* Restarting with stat
[*] Your public IP address is: [Public IP]
[*] Embed this pixel in your CSS file with the following code:
[some CSS element] {
background-image: url('https://[Public IP]/[pixel name].png');
background-size: 0 0;
}
* Debugger is active!
* Debugger PIN: 422-205-484
Take this custom CSS pixel element and add it to your M365 Company Branding as a custom CSS file:
The M365 Company Branding CSS Schema has the list of CSS elements that the M365 login page can use. .ext-footer
seems to be a good option to trigger the clarion call.
eg.
.ext-footer {
background-image: url(https://[Public IP]/[pixel name].png);
background-size: 0 0;
}
I'm willing to bet that this works with login pages for any service that allows you to specify custom CSS. I don't know which ones do, so if you have any ideas, open a PR or issue and let me know!
Thank you again to Zolder for the original article on this technique!