Embedded malware detection for your legitimate domain.
Standard firewalls and origin scanners miss third-party script exploits. Our crawler renders your site as a real visitor every five minutes, intercepts every outbound domain, and cross-checks against 500+ malicious blocklists — paging your team with full forensic reproduction the moment something's wrong.

Download the WordPress Plugin
Connect your WordPress site to ExploitShield in under a minute. The plugin adds third-party domain monitoring, threat alerts and access to the full forensic reports straight from your admin dashboard.
Find out if your domain is hosting malware.
Enter the domain you'd like us to investigate. We'll run a forensic malware sweep — third-party scripts, injected payloads, blocklist hits, compromised dependencies — and walk you through every finding on a live demonstration call.
Preview scan — rate-limited, no data stored without consent.
Five phases. Every five minutes. Every domain you own.
The detector isn't a checklist scanner — it's a continuously running, instrumented browser that experiences your website the way an actual customer does. Here's exactly what happens on every cycle:
Initiation
A headless Chromium worker leases your domain from our distributed scheduler every 300 seconds. Each scan uses a fresh browser context with cleared cookies, cache, and storage so we always observe what a brand new visitor would see.
Full Page Render
We execute every script on the page exactly the way a real customer's browser would — synchronous tags, async modules, deferred bundles, tag-manager-injected partners, lazy-loaded iframes, and service workers. Server-side cloaking cannot hide from a real renderer.
Network Interception
Every outbound request — XHR, fetch, WebSocket, image beacon, script src, font, stylesheet, iframe, and prefetch — is captured into a HAR-style ledger with method, headers, response size, MIME type, initiator stack, and timing.
Blocklist Cross-Check
Each unique third-party domain is normalized and matched in parallel against 500+ blocklist feeds: Spamhaus, URLhaus, OpenPhish, PhishTank, abuse.ch, MalwareBazaar, Feodo Tracker, ThreatFox, Maltrail, Quad9, and dozens of community and vendor sources, refreshed hourly.
Forensic Alert
If a hit is found, we dispatch an email within seconds containing the offending domain, the initiator script and line number, the full request/response, a screenshot of the rendered page, the HAR file, and a one-click reproduction URL.
Twelve technical capabilities you don't get from a regular WAF.
Real Visitor Simulation
Headless Chromium with full JavaScript execution, fonts, and layout — not just a curl request that misses everything modern websites actually do.
5-Minute Cadence
288 independent scans per day, per domain. Detection windows for skimmers and malicious tag updates close from days to minutes.
500+ Malicious Domain Blocklists
Spamhaus DBL, URLhaus, OpenPhish, PhishTank, abuse.ch, ThreatFox, Maltrail, Quad9 — aggregated, deduplicated and refreshed hourly.
Third-Party Connection Map
We catalog every external domain your site reaches — analytics, CDN, fonts, ad tech, A/B testers, chat widgets — and surface unexpected newcomers.
Magecart & Skimmer Defense
Tuned heuristics catch payment-form skimmers that hide behind tag-managers, fake jQuery libraries, or compromised WordPress plugins.
Crypto-Jacking Detection
Identify unauthorized in-browser miners (Coinhive successors, WASM miners) trying to consume your customers' CPU.
Supply-Chain Visibility
When an upstream vendor's script silently starts calling a new domain, you'll see it within five minutes — long before any disclosure cycle.
Full Forensic Reproduction
Each alert ships with HAR, screenshot, console log, initiator stack trace, and a stable replay URL for your dev team to validate the fix.
Email + Webhook Alerts
Native delivery to inbox, plus webhooks into Slack, Microsoft Teams, PagerDuty, Opsgenie, or any HTTP endpoint.
Zero Site Modification
No script, agent, plugin, DNS change, or server access required. We monitor exactly what a public visitor sees from outside your perimeter.
Multi-Path Coverage
Define multiple URL templates per domain — homepage, checkout, login, /admin, blog post — so we cover the surfaces that actually carry risk.
Geo-Distributed Probes
Scans originate from US, EU, and APAC vantage points to catch geo-targeted payloads that only fire for specific visitor regions.

500+ global blocklists. Synced hourly. Deduplicated automatically.
Malicious domains rotate faster than any single vendor can track. We aggregate signal from commercial threat feeds, open-source community trackers, DNS-layer reputation, and our own crawler-trap honeypots to cover the entire attack surface — so you don't have to subscribe to twenty different services.
Real Chromium. Real DevTools Protocol. Not a headless stub.
The crawler is a full chromium.exe instance driven over the Chrome DevTools Protocol — not an HTTP fetcher, not a JS interpreter, not a regex pass over your HTML. Before any page script runs, we inject an instrumentation shim that wraps the exact browser APIs malware payloads abuse, and we record every call site, argument, and stack trace into an in-memory ledger.
Full JavaScript Execution
Every script runs the way it does for a real visitor — synchronous tags, async modules, dynamic import(), eval, the Function constructor, setTimeout(string), WebAssembly. No interpreter shortcuts, no regex passes over your HTML.
- ›eval / new Function()
- ›setTimeout(string)
- ›dynamic import()
- ›WebAssembly
- ›ServiceWorker
DOM Instrumentation
Before any page script runs, we install a monitoring shim that wraps every browser API a malware payload typically abuses — and records the call site, arguments, and stack trace into an in-memory ledger.
- ›window.open / location.assign / replace
- ›document.write / writeln
- ›innerHTML setter / iframe.srcdoc setter
- ›appendChild / insertBefore / setAttribute
- ›script.src / iframe.src / anchor.href setter
- ›fetch / XMLHttpRequest.open+send / sendBeacon
- ›WebSocket / EventSource / Worker / SharedWorker
- ›navigator.serviceWorker.register
- ›navigator.clipboard / getUserMedia / RTCPeerConnection
- ›canvas.toDataURL / getImageData (fingerprinting)
- ›Notification.requestPermission
MutationObserver On Document Root
A live observer flags every dynamically-added <script>, <iframe>, <object>, <embed>, <form>, <a>, and <link> after page load — so post-render DOM injection from tag managers and skimmers is captured the moment it lands.
- ›<script>
- ›<iframe>
- ›<object>
- ›<embed>
- ›<form>
- ›<a>
- ›<link>
Every byte of every request, captured at the wire.
An optional MITM layer runs as a sibling process with its own short-lived CA, intercepts every HTTP and HTTPS flow Chromium issues, and writes structured records to a per-session proxy_flows.jsonl. This is the evidence backbone behind every alert — when we say a third-party domain shipped a payload, we have the full request, response, headers, cookies, and body bytes to prove it.
- Headers
- Full request and response header lists — not summaries. Every Cookie, Set-Cookie, Authorization, custom header.
- Bodies
- SHA-256, byte size, decoded sample, and optional saved binary for HTML, JS, JSON, XML, WASM, and any executable extension.
- Cookies
- Parsed from both Cookie and Set-Cookie, including SameSite, HttpOnly, Secure, Domain, Path, Expires, Max-Age.
- Provenance
- Initiator, referrer, redirect chain, request/response timing, frame top/sub status.
- TLS
- On-the-fly mitmproxy CA trusted by the crawler only. HTTPS bodies are decrypted in-process — never on disk in plaintext.
- QUIC Disabled
- Chromium is launched with --disable-quic and proxy-bypass <-loopback>, forcing every byte through the capture layer.
{
"flow_id": "fl_00471_8a3c",
"ts": "2026-05-14T14:05:01.882Z",
"method": "POST",
"url": "https://api.malicious-cdn.ru/c.php",
"initiator": "/static/tag-mgr.js:442",
"request": {
"headers": [...18 entries],
"body": { "size": 1248, "sha256": "9f1e…", "sample": "{\"d\":\"BASE64…\"}" }
},
"response": {
"status": 200,
"headers": [...11 entries],
"set_cookie": [{ "name":"sid", "samesite":"None", "secure":true }],
"body": { "size": 412, "sha256": "21ab…", "path": "bodies/fl_00471_resp.bin" }
},
"tls": { "intercepted": true, "ca": "ephemeral_session_ca" },
"verdict": { "blocklist": "URLhaus", "severity": "Critical" }
}Every flow is verified by two independent observers.
The crawler captures every request twice — once from inside Chromium via the DevTools Protocol, and once from outside via the MITM proxy. Records are joined into a canonical_flow with a correlation_score from 0 to 100. Anything seen by only one channel — a request the browser issued but the proxy never saw, or wire bytes the browser refuses to acknowledge — is automatically suspect and surfaced for review.
- canonical_id
- Stable per-request hash that joins the CDP record and the MITM proxy record into one canonical flow.
- seen_by_cdp / seen_by_proxy
- Dual-source confirmation. A flow seen by only one channel is suspicious by construction.
- correlation_score
- 0–100. 90+ = both channels agree on URL, method, status, body sha256, redirect chain.
- redirect_chain
- Every 30x hop with from_url, to_url, status, headers, and registrable-domain transition.
- body_sha256
- Response body fingerprint shared with the deobfuscation, signature, and diff pipelines.
- initiator
- Type (parser / script / preload / other) and full JS stack when available.
{
"canonical_id": "700dca22c573c544",
"url": "https://home.abv.bg/",
"initial_url": "https://abv.bg/",
"method": "GET",
"response_status": 200,
"mime_type": "text/html",
"seen_by_cdp": true,
"seen_by_proxy": true,
"correlation_score": 90,
"redirect_chain": [
{ "from_url": "https://abv.bg/",
"to_url": "https://home.abv.bg/",
"status": 301 }
],
"body_sha256": "6708fc5e26f88bcee00fa8b994e65e6cd98fbc228…",
"initiator": { "type": "other" }
}Every request traced to the frame and JS call stack that issued it.
The crawler reconstructs the full Chromium frame hierarchy and stamps every captured request with its originating frame_id, parent_id, and a flag for whether the frame is third-party to the top document. Each network request also carries its full V8 initiator stack — function names, script IDs, line and column numbers — so when a malicious request fires, the alert points to the exact script and the exact line that caused it, not just the URL.
- frame_id
- Stable Chromium frame identifier — every request is attributed back to the exact frame that issued it.
- parent_id
- Parent frame reference — reconstructs the full iframe nesting chain from main document down to ad slots.
- external_to_page
- True when the frame's registrable domain differs from the top document — flags third-party iframes by construction.
- requests[]
- Per-frame request ledger: request_id, url, resource_type, external flag — joins to canonical_flow records.
{
"request_id": "489310B50F204290B55C9F21C03045F5",
"url": "https://img.abv.bg/e/s/counters.html?v=2.2",
"initiator_type": "script",
"call_frames": [
{ "functionName": "wrapped",
"scriptId": "6",
"lineNumber": 36, "columnNumber": 24 },
{ "functionName": "",
"url": "https://home.abv.bg/js/main.min.js?v=2.0",
"lineNumber": 4724, "columnNumber": 15 }
],
"initiator_domains": ["home.abv.bg"]
}- initiator_typeparser / script / preload / other / signal — the V8-reported reason the request was issued.
- initiator_urlURL of the script that triggered the request when initiator_type = script.
- line / columnExact source position inside the initiator script for parser-issued requests.
- call_frames[]Full JS call stack: functionName, scriptId, url, lineNumber, columnNumber per frame.
- initiator_domainsDeduplicated list of registrable domains across the entire call stack — exposes long supply-chain hops.
Certificate chain and security headers, captured per response.
Every TLS handshake is recorded down to the cipher suite, ALPN, full issuer chain, validity window, and a stable certificate fingerprint. Every response is parsed for security headers — clickjacking, CORS, CORP/COOP/COEP, HSTS, CSP, Permissions-Policy — so a silent posture change on a third-party host is flagged the cycle it happens.
- SNI
- Server Name Indication sent by the crawler — confirms the actual hostname the TLS handshake addressed.
- ALPN
- Negotiated protocol (h2 / http/1.1) — surfaces silent HTTP/2 vs HTTP/1.1 downgrades.
- Cipher
- Full cipher suite name (e.g. TLS_AES_256_GCM_SHA384). Weak suites are flagged.
- Issuer chain
- C / O / OU / CN of the issuing CA, plus serial and validity window (notbefore → notafter).
- Subject + SANs
- All Subject Alternative Names recorded — catches over-broad certs and unexpected hostname bindings.
- Cert sha256
- Stable fingerprint per leaf certificate. Pinned across runs to detect silent reissue or substitution.
- X-Frame-OptionsDENY / SAMEORIGIN per response — clickjacking exposure.
- Content-Security-PolicyFull directive parse — script-src / frame-src / connect-src drift from baseline.
- Strict-Transport-Securitymax-age, includeSubDomains, preload presence.
- Cross-Origin-Resource-Policysame-site / same-origin / cross-origin per asset.
- Cross-Origin-Opener-PolicyWindow isolation enforcement.
- Cross-Origin-Embedder-PolicyCross-origin isolation contract.
- Referrer-PolicyOutbound referrer leakage classification.
- Permissions-PolicyFeature gating (camera, microphone, geolocation, payment).
DNS, reverse DNS, certs, entropy, homograph detection — per domain, per IP.
Every third-party domain seen during a scan is enriched with independent DNS resolution, PTR lookups, full TLS certificate metadata, Shannon entropy of the registrable label, mixed-script and Latin-Cyrillic homograph flags, and a provider classification (large_provider / cdn / hosting / unknown). Every observed IP is then queried against ThreatFox and ismalicious in parallel, with results cached and fused.
- observed_ips
- Remote IPs the proxy actually transacted with during the scan — wire-confirmed contact.
- resolved_ips
- Independent DNS resolution from the crawler host — catches DNS rebinding and split-horizon answers.
- reverse_dns
- PTR lookup per IP — reveals 1e100.net, cloudflare, fastly, anonymous VPS naming patterns.
- certificates[]
- Per-handshake leaf cert: sni, alpn, cipher, subject, issuer, serial, validity window, sha256, full SAN list.
- punycode_homograph_flags
- IDN / mixed-script / look-alike Latin–Cyrillic homograph detection on every domain seen.
- domain_entropy
- Shannon entropy of the registrable label — flags algorithmically-generated DGA hostnames.
- provider_classification
- large_provider / cdn / hosting / unknown — separates Google/Cloudflare/Fastly noise from long-tail infrastructure.
{
"observed_ips": ["142.251.127.84"],
"reverse_dns": { "142.251.127.84":
"lcfrai-in-f84.1e100.net" },
"cert.cn": "accounts.google.com",
"cert.issuer": "Google Trust Services / WE2",
"cert.sha256": "b2b9e2b69263b1a1c4e3a390…",
"entropy": 3.366,
"homograph": [],
"provider": "large_provider"
}- ThreatFox (abuse.ch)Live IOC database — per-IP query returns hit_count, max_confidence, and the matching IOC entries.
- ismalicious.comMulti-source reputation API — blocklist hits, OTX pulses, geo, ASN, classification.
- Verdict fusionmalicious = any(provider.malicious) — a single high-confidence hit is enough to mark the IP.
- TTL cachePer-IP results cached with expiry — predictable cost across 5-minute scan cadences.
Every cookie classified by site relation — never by raw value.
The crawler enumerates every cookie set during a scan from three independent sources — the proxy Set-Cookie header, the CDP request layer, and document.cookie — then classifies each one by the relationship between its domain and the site's registrable base. Cookie values are stored as SHA-256 only — we capture posture, never PII.
- ›name
- ›domain + base_domain
- ›relation: same_host / same_registrable_domain / cross_site
- ›secure / httponly / samesite
- ›path / expires / max-age
- ›value_sha256 (never raw value)
- ›source: proxy_set_cookie / cdp_request / dom_document_cookie
- ›evidence: flow_id, status
| name | relation | flags |
|---|---|---|
| _abv_home_auth | same_registrable_domain | Secure |
| _abv_home_auth_req | same_registrable_domain | Secure · HttpOnly · Lax |
| _abv_login_csrf_token | same_host | Secure · HttpOnly |
| _ga | same_registrable_domain | — |
| _gid_dc | cross_site | Secure · None |
| IDE | cross_site | Secure · HttpOnly · None |
Decode first. Match second. Catch payloads hiding behind escape sequences.
Captured script bodies are passed through a static deobfuscation pipeline before any signature engine runs. Each decoder reads the source bytes, emits the recovered plaintext with a SHA-256 link back to the source URL, and re-scans the result for embedded URLs that get added to the third-party domain ledger automatically.
String.fromCharCode chains
Reconstructs the literal string a payload was trying to hide.
atob / btoa
Decodes Base64 string literals — extracts URLs, payload bodies, second-stage code.
unescape / decodeURIComponent
Resolves percent-encoded payloads commonly used in eval-pack droppers.
Hex / \xHH and \uHHHH
Walks dense escape sequences and emits the decoded plaintext.
URL extraction from decoded strings
Every decoded string is re-scanned for http(s) URLs and added to the third-party domain ledger.
SHA-256 per source body
Decoded artifacts are tied back to source_url + source_sha256 so each finding has provable provenance.
Every script is dumped to disk, hashed, and scanned.
Every JavaScript body the MITM proxy captures is written to samples/<NNNN>_<host>_<sha256-prefix>.js, indexed in manifests/javascript_samples.{json,csv}, and submitted to the configured AV engine. The scanner output lands in scanner_results/ next to a top-level av_scan_summary.json carrying enabled, status (clean / suspicious / infected / error), sample_count, and a per-sample row.
- One sample = one captured response body, not one URL. Duplicates are kept and hashed independently.
- sha256 is the AV-engine input AND the de-dup / corpus-comparison key across scans.
- Body provenance is preserved: source_body_path points back into proxy_capture/.../bodies/<flow_id>_response.bin so the AV verdict can be replayed against the exact wire bytes.
- Manifest ships as both JSON (for the pipeline) and CSV (for the SOC analyst).
- Scan summary status (clean / suspicious / infected / error) becomes a top-level evidence class on the alert.
RDAP for every third-party domain. Age is a verdict input, not a footnote.
Every detected third-party domain is normalised to its registrable form and queried over RDAP — IANA bootstrap first, the TLD's RDAP endpoint second, an ismalicious WHOIS fallback third. Raw payloads are cached and hashed so any age- or registrar-based verdict can be replayed off the original bytes.
01 · scope + chain
- lookup_scope = detected_third_party_domains_only — the first-party host is never queried.
- Observed host (accounts.google.com) normalised to its registrable domain (google.com) before lookup; the normalisation flag is preserved on the record.
- Resolution chain: IANA RDAP bootstrap → TLD RDAP endpoint → ismalicious WHOIS fallback → raw response cached with sha256.
- young_domain_days_threshold = 365 — domains younger than this surface as young_domain: true and feed the age-band scoring buckets.
02 · fields extracted
- ›creation_date + creation_datetime + confidence (high / medium / low) + source label (rdap.events.registration, …)
- ›age_days, years_registered
- ›registrar (e.g. MarkMonitor Inc.)
- ›name_servers[] (e.g. ns1.google.com … ns4.google.com)
- ›whois_server, rdap_url, rdap_base_url, rdap_bootstrap_publication
- ›raw_truncated + raw_sha256 — any verdict can be re-derived from the raw payload
- ›creation_date_candidates[] — every date the parser found, not just the winner
03 · live examples
Every WHOIS record is cached, hashed, and replayable. New registrar, new nameserver, or a creation date flipping forward = a diff event on the next scan, not a silent change.
70+ behavioural signatures, not just URL blacklists.
Blocklists catch known bad domains. Signatures catch known bad behaviours — even on first-seen infrastructure that no feed has flagged yet. Every captured script, response body, and command line is run through a regex engine grouped by adversary intent. Each signature carries an impact tier that feeds the per-domain risk score.
- WScript.Shell .Run / .ExecCritical
- Shell.Application ShellExecuteCritical
- MSHTA remote payloadCritical
- regsvr32 scriptlet (/i:http)Critical
- rundll32 javascript: handlerCritical
- WMI Win32_Process.CreateCritical
- XMLHTTP → ADODB.Stream → SaveToFileCritical
- certutil -urlcache -split -fCritical
- bitsadmin /transferCritical
- PowerShell DownloadString / DownloadFileCritical
- responseBody → Write to diskHigh
- FileSystemObject CreateTextFileHigh
- HKCU/HKLM Run / RunOnce keyCritical
- Startup folder dropCritical
- schtasks /createCritical
- sc create / sc configCritical
- WMI __EventFilter + ActiveScriptEventConsumerCritical
- eval(unescape(...))High
- eval(atob(...))High
- String.fromCharCode chainsMedium
- Hex / Unicode escape densityHigh
- Long Base64 string literalsMedium
- MSScriptControl AddCode / EvalHigh
- Hidden iframe (display:none / 0×0)High
- data:text/html script injectionHigh
- Prototype pollution writesHigh
- createElement('script') + remote .srcHigh
- Push-notification social engineeringMedium
- In-browser miner (coinhive / cryptonight / WASM)High
- navigator.plugins / mimeTypes / userAgentMedium
- WMI Win32_OperatingSystem / BIOS / NetworkAdapterMedium
- Plugin probes (Flash / Silverlight / Java)Medium
- Canvas toDataURL / getImageData fingerprintingLow
- WebRTC / getUserMedia probesMedium
Every detection ships with a score, the reasons for it, and its weaknesses.
A signature firing is not a verdict. Each finding is graded 0.00 – 1.00 by counting independent corroborating sources (proxy, CDP, DOM, decoded payload, reputation hit) and subtracting weakening factors (single-channel only, provider rate-limited, body truncated). The alert email shows the tier, the why[] array, and the weaknesses[] array — so you can argue with the robot, not just trust it.
| Confidence range | Tier | Label | What it means |
|---|---|---|---|
| 0.85 – 1.00 | Critical | Multi-source | Proxy + CDP + DOM all corroborate. Evidence chain is reproducible from raw artifacts. |
| 0.65 – 0.84 | High | Wire-confirmed | Proxy or CDP captured the byte-level request. Body sha256 and headers preserved. |
| 0.45 – 0.64 | Medium | DOM-only | Observed at the DOM layer (e.g. dynamic <script> insertion) without an independent network record. |
| 0.20 – 0.44 | Low | Embedded | Pattern matched inside a captured body but no fetch occurred. Reported as a candidate. |
| 0.00 – 0.19 | Info | Weakened | Single weak signal, contradicted by other sources, or matching a known-benign allowlist. |
- +CDP network evidence is available
- +MITM proxy captured the request and response body
- +DOM evidence contributes to this finding
- +Initiator stack identifies the originating script
- +Frame attribution links the request to a third-party iframe
- +Decoded payload re-resolves to a known-bad domain
- +Cert sha256 matches a previously-flagged certificate
- +Reputation provider returned a positive verdict
- −Single-channel observation (CDP without proxy or vice versa)
- −Reputation provider rate-limited (HTTP 429) on this run
- −WHOIS unavailable for this TLD — domain age is unscored
- −Body partially captured — exceeded max_body_bytes
- −Initiator stack truncated by V8 — call frames incomplete
- −Domain is on the operator allowlist — finding suppressed
New domains are prosecuted on sight.
The age of a third-party domain is itself a signal. We resolve creation date through native WHOIS for .com .net .org .io .co .bg .uk .de .nl .ru and dozens more, with an ismalicious reputation fallback for obscure TLDs. Each domain gets a base score from its age bucket, then context and correlation multipliers stack on top.
- Redirect target+10
- Runtime redirect+10
- Executable delivery+10
- <script src>+8
- <iframe src>+8
- ServiceWorker register+8
- fetch / XHR+5
- Threat-intel domain hit+18
- Threat-intel URL hit+14
- Lookalike domain+8
- New since baseline+8
| Age bucket | Severity | Base score | Note |
|---|---|---|---|
| 0–1 day | Critical | 35 | Registered within the last day |
| 2–7 days | Critical | 30 | Registered within the last week |
| 8–30 days | High | 24 | Registered within the last month |
| 31–90 days | Medium | 16 | Registered within the last quarter |
| 91–180 days | Medium | 10 | Registered within the last six months |
| 181–365 days | Low | 6 | Registered within the last year |
| 1–2 years | Info | 1 | Older than one year, still relatively young |
| 2+ years | Info | 0 | Established domain |
Three layers of threat intelligence. One verdict per domain.
Every third-party domain is queried in parallel against live IOC databases, multi-signal reputation APIs, and aggregated open community feeds. Verdicts are fused with malicious = any(provider.malicious), cached with TTL, and deduplicated per scan to keep cost predictable.
Live IOC databases queried per scan, cached with TTL, deduplicated across providers.
- URLhaus (host endpoint)
- URLhaus (URL endpoint)
- ThreatFox (search_ioc)
- MalwareBazaar
- Feodo Tracker
Multi-signal verdicts: blocklist hits, OTX pulse counts, geo, classification, risk score.
- ismalicious /check (full + reputation)
- blocklist hit count
- OTX pulse correlation
- data-trust + evidence fields
- geo + ASN enrichment
Aggregated and refreshed hourly, normalised and deduplicated against the live providers.
- Spamhaus DBL / DROP
- OpenPhish
- PhishTank
- Maltrail
- Quad9
- OpenDNS public
- StevenBlack
- hpHosts mirror
- SURBL
Five profiles. Pick your evidence depth.
Each monitored domain runs under one of five preset profiles, controlling proxy capture, body retention, interaction simulation, browser variants, and threat-intel breadth. Switch profiles per domain without restarting the scheduler.
1 variant, no MITM proxy, no third-party IP analysis. Fastest cycle, lowest cost — ideal for low-risk marketing sites.
MITM proxy on suspicious bodies, third-party IP analysis on, full artifacts copied per run.
Full body capture (≤2 MB), 5-click interaction simulation, observation schedule at 0/5/15/30/60 min, fresh browser per result, WHOIS + RDAP + ThreatFox.
4 randomised browser variants per scan to defeat geo-, UA-, and timing-based cloaking.
All providers enabled: URLhaus + ThreatFox + ismalicious + WHOIS + domain-age scoring + infrastructure DNS.
5-minute default interval · jitter configurable · file-backed state · no SQLite, no shared DB · every domain isolated under customers/<slug>/
Five scheduled observations per scan. Catch the late, lazy, and gated payloads.
A single screenshot lies. The crawler holds the page open and re-observes it at 0s · 5s · 15s · 30s · 60s, recording a fresh DOM sha256, request count, runtime artifact count, and live script and iframe tallies at every offset. Any drift between offsets — a new script appended, an iframe injected, a fetch fired — is captured with full context. Defer-and-execute payloads have nowhere to hide.
First DOM, request count, script and iframe inventory captured immediately after page settle.
Catches scripts that defer execution to defeat fast scanners.
Aligns with simulated clicks and scroll events for interaction-gated payloads.
Lazy-loaded ads, tracking pixels, and on-idle iframes have all fired.
Polling beacons, websocket pings, retry-storm payloads are all surfaced.
- dom_sha256
- Hash of the live DOM at each offset — any byte-level mutation between offsets is flagged as runtime drift.
- request_count
- Cumulative network request count — a step-up between offsets means new third-party calls fired late.
- runtime_artifact_count
- Total instrumentation events captured by the in-page shim (window.open, eval, appendChild, etc.).
- script_count / iframe_count
- Live script and iframe tallies — exposes dynamically injected DOM grown after initial load.
[
{ "offset_seconds": 0, "request_count": 84,
"script_count": 8, "iframe_count": 12,
"dom_sha256": "efb7af5196c4b046…" },
{ "offset_seconds": 5, "request_count": 84, … },
{ "offset_seconds": 15, "request_count": 84, … },
{ "offset_seconds": 30, "request_count": 84, … },
{ "offset_seconds": 60, "request_count": 84,
"runtime_artifact_count": 471 }
]Every hop, every domain, every header — start to end.
Traffic distribution systems hide behind multi-hop 30x chains that cross registrable domains, swap schemes, and rewrite query strings. The crawler reconstructs the entire chain into a single redirect_path with a stable path_id, ordered domains[], external_hop_count, and dual-source confirmation from both the proxy and the DevTools Protocol — so a chain seen by only one observer is automatically suspect.
- path_id
- Stable hash for the start_url → end_url path. The same path across runs proves stable redirect behaviour.
- hop_count
- Total HTTP redirect hops in the path. Long chains (3+ hops) are flagged for review.
- external_hop_count
- Count of hops that cross the registrable domain boundary. >0 means the redirect leaves your origin.
- domains[]
- Ordered list of every domain visited during the redirect chain — full provenance of the user's path.
- sources[]
- proxy / cdp / both — dual-source confirmation that the redirect actually fired at the wire.
- hops[]
- Per-hop record: from_url, to_url, status, headers, redirect_type, relation, evidence (request_id or flow_id).
{
"path_id": "f61329aaf75cf349",
"start_url": "https://abv.bg/",
"end_url": "https://home.abv.bg/",
"hop_count": 1,
"external_hop_count": 0,
"domains": ["abv.bg", "home.abv.bg"],
"sources": ["proxy", "cdp"],
"hops": [{
"from_url": "https://abv.bg/",
"to_url": "https://home.abv.bg/",
"status": 301,
"relation": "same_registrable_domain",
"evidence": {
"flow_id": "ec6121e5-b730-43e3-…",
"request_id": "C26BF8CD24EAE44C…",
"headers": { "location": "https://home.abv.bg/",
"strict-transport-security":
"max-age=31536000; includeSubDomains" }
}
}]
}A queryable graph of every URL, domain, and IP touched during a scan.
The crawler emits resource_graph.json — a typed node and edge graph that connects the requested URL to its final URL, every subresource, every registrable domain, and every remote IP. Each node carries its kind and base_domain so investigators can pivot from one suspicious script to every domain it loaded, every IP that served it, and every redirect that delivered it — in a single traversal.
| node.kind | What it represents |
|---|---|
| requested_url | The original URL the operator monitored — the entry point of the graph. |
| final_url | The settled URL after every redirect — the page the user actually saw. |
| url | Any subresource URL (script, stylesheet, image, iframe, fetch target) discovered during the scan. |
| domain | Aggregate node grouping every URL that shares a hostname — collapses noise. |
| base_domain | Registrable domain node — the unit at which third-party relationships are evaluated. |
| ip | Remote IP node, joined to the domains it served and the ASN that owns the netblock. |
{
"nodes": [
{ "id": "https://abv.bg",
"kind": "requested_url",
"base_domain": "abv.bg" },
{ "id": "https://passport.abv.bg/…",
"kind": "final_url",
"base_domain": "abv.bg" },
{ "id": "https://img.abv.bg/e/s/counters.html",
"kind": "url",
"base_domain": "abv.bg" }
],
"edges": [
{ "from": "abv.bg",
"to": "home.abv.bg",
"kind": "redirect" },
{ "from": "home.abv.bg",
"to": "img.abv.bg",
"kind": "subresource" }
]
}Every IP linked to its RIR allocation, ASN, and netblock owner.
DNS tells you the IP. RDAP tells you who owns it. Every third-party IP observed during a scan is enriched with its registry handle, network name, allocation type, and full address range — so a domain hosted on DIGITALOCEAN-104-131-0-0 is flagged differently from the same domain on CLOUDFLARENET. The same enrichment groups every IP under one allocation, which exposes infrastructure clusters even when individual hostnames look unrelated.
- handle
- RDAP allocation handle (e.g. NET-104-131-0-0-1). The canonical identifier for the IP block.
- name
- Network name as registered with the RIR (e.g. DIGITALOCEAN-104-131-0-0, CLOUDFLARENET).
- type
- Allocation type (DIRECT ALLOCATION / REASSIGNED / etc.) — separates first-party hosters from resellers.
- start_address / end_address
- Full netblock range — clusters every IP in the allocation under a single owner.
- country
- Registered country code when published by the RIR — surfaces unexpected jurisdictional exposure.
| ip | domain | network owner |
|---|---|---|
| 104.131.8.164 | bugs.jquery.com | DIGITALOCEAN-104-131-0-0 |
| 104.18.22.19 | www.w3.org | CLOUDFLARENET |
| 142.251.127.84 | accounts.google.com | |
| 194.153.145.60 | abv.bg | EVOLINK-NET |
Native RDAP queries with TTL cache. No paid ASN feed dependency — every netblock resolved against the canonical RIR (ARIN, RIPE, APNIC, LACNIC, AFRINIC).
Every scan grades itself. Every scan diffs the last.
Each run emits a coverage_dashboard_summary.json scoring how complete the evidence is, plus a crawl_diff.json that compares this cycle's external domains, IPs, scripts, cookies, redirect chains, and TLS certificates against the per-domain baseline. New third-party domains, vanished assets, and changed certificate fingerprints are surfaced as drift events with their own severity tier.
- confirmed_external_domains
- Third-party domains observed by both the browser and the proxy.
- confirmed_third_party_ips
- Remote IPs that completed at least one TCP+TLS session during the scan.
- candidate_third_party_ips
- Resolved or embedded IPs that did not produce a confirmed network contact.
- proxy_flows
- Total HTTP/HTTPS flows captured by the MITM layer for this run.
- score
- Coverage health 0.00–1.00. <1.0 = at least one warning the operator should review.
- warnings / hard_failures
- Structured operator notes (e.g. unresolved candidate IPs, missing artifact, schema drift).
{
"status": "partial_with_warnings",
"score": 0.95,
"confirmed_external_domains": 19,
"confirmed_third_party_ips": 20,
"candidate_third_party_ips": 76,
"proxy_flows": 131,
"warnings": [
"Some third-party IPs are embedded or DNS-resolution
candidates only; not confirmed network contacts."
],
"hard_failures": []
}- [+] new third-party domain · api.malicious-cdn.ru
- [~] cert sha256 changed · cdn.partner.io
- [!] CSP script-src widened · checkout
- [-] removed asset · tag-mgr/v2.js
Confirmed contacts vs candidates. We never inflate one into the other.
The commercial monitoring contract draws a hard line between domains and IPs that the browser actually contacted and those that only appeared in source bytes, redirects, or DNS responses. Confirmed contacts have wire-level proof — proxy + CDP records, body sha256, certificate fingerprint. Candidates are tracked with full provenance but are never reported as if they had loaded. This is the distinction that lets us guarantee a coverage score at all.
The MITM proxy completed a TCP+TLS session to this exact IP. Highest evidentiary weight — wire-level proof.
Domain seen by both the proxy and the CDP request layer. Counted toward the network-contact guarantee.
URL extracted from HTML / CSS / JS source bytes but never actually fetched. Surfaced as a candidate, never as a contact.
Domain appeared as a redirect target in a Location header but did not load. Tracked, not claimed.
IP returned by DNS for an observed domain but with no completed network session. Reported as a candidate IP, not a contact.
URL recovered from a decoded base64 / hex / fromCharCode payload. Tied to source_sha256 for full provenance.
external_domains: 61 confirmed_loaded: 19 ← reported as contacts embedded_only: 41 ← reported as candidates third_party_ips: 96 confirmed_loaded (proxy + CDP): 20 ← reported as contacts embedded_or_resolved_only: 76 ← reported as candidates network_endpoints: 150
Every alert ships with the evidence — not just a warning.
When a third-party domain on your site matches a malicious blocklist, your engineering team gets a self-contained forensic dossier in seconds. No log-diving. No back-and-forth. Just enough to patch and prove remediation.
- [!]Initiator script + line number
- [!]Full HTTP request and response headers
- [!]Pixel-accurate page screenshot at time of detection
- [!]Complete HAR archive of the scan session
- [!]Browser console log + JS stack trace
- [!]Deterministic replay URL for your devs
- [!]Severity score and blocklist source attribution
- [!]WHOIS + ASN data on the malicious domain
- THREAT_ID
- DXD-X9902-Z
- SEVERITY
- CRITICAL · 9.4 / 10
- DOMAIN
- api.malicious-cdn.ru
- MATCHED_LISTS
- URLhaus, abuse.ch, ThreatFox
- DETECTED_AT
- 2026-05-14T14:05:02Z
- PARENT_URL
- /checkout/payment
- INITIATOR
- /static/tag-mgr.js:442
- METHOD
- POST · 1.2 KB · 200 OK
Engineered for SMBs that can't afford a SOC team.
Protect checkout from skimmers
Magecart-style attacks live in third-party scripts. We catch the moment a foreign domain appears on your /checkout page.
Monitor every client domain
One dashboard, white-label reports, and an API to pipe findings into your own client portal.
Audit your supply chain
When a vendor's CDN script silently changes its outbound calls, you find out in five minutes — not after disclosure.
PHI exfiltration safety net
Catch tracking pixels accidentally loaded on patient portals before they violate HIPAA.
PCI-DSS 4.0 readiness
Requirement 6.4.3 and 11.6.1 expect you to know every script on payment pages. We give you a continuous, evidence-grade ledger.
Stop ad-malvertising drift
Ad networks rotate creatives constantly. We surface malicious payloads embedded in ad chains within minutes.
Why a WAF or a weekly file scanner isn't enough.
| Capability | D.E.D. | Standard WAF | Weekly File Scanner |
|---|---|---|---|
| Detects client-side third-party malware | ● YES | — NO | ◐ Partial |
| 5-minute scan interval | ● YES | ◐ N/A | — NO |
| Full JavaScript execution | ● YES | — NO | ◐ Some |
| 500+ blocklist cross-check | ● YES | — NO | ◐ Some |
| No site modification required | ● YES | — NO | ● YES |
| Forensic reproduction URL per alert | ● YES | — NO | — NO |
| Geo-distributed probes | ● YES | — NO | — NO |
| Magecart / payment skimmer detection | ● YES | ◐ Partial | ◐ Partial |
Transparent plans. No per-seat surprises.
Every plan includes the full 5-minute crawl pipeline, all 500+ blocklists, and unlimited alerts. You're paying for domain coverage and operational integrations, not feature locks.
For a single business website that needs continuous third-party visibility.
- [+]1 monitored domain
- [+]Up to 3 monitored URL paths
- [+]5-minute scan interval
- [+]Email alerts with full forensic report
- [+]500+ blocklist coverage
- [+]30-day retention
For multi-property businesses, online stores and agencies in growth mode.
- [+]Up to 5 monitored domains
- [+]Unlimited URL paths per domain
- [+]5-minute scan interval (1-min on request)
- [+]Email + Slack + Webhook alerts
- [+]Geo-distributed probes (US/EU/APAC)
- [+]90-day retention + HAR exports
- [+]Per-tenant allow-list management
For SOCs, MSPs, healthcare, fintech, and PCI-scope merchants.
- [+]Unlimited domains and URL paths
- [+]1-minute scan interval
- [+]White-label multi-tenant dashboards
- [+]Role-based access + SSO/SAML
- [+]REST API + SIEM integrations
- [+]EU-only data residency option
- [+]SOC2 Type II + ISO 27001 reports
- [+]Dedicated security engineer
Twelve answers your security lead is going to ask anyway.
Most malware on legitimate websites is delivered through embedded third-party JavaScript — a compromised tag manager, a hijacked CDN script, an outdated plugin, a backdoored npm dependency loaded over the wire. Traditional WAFs only see traffic hitting your origin server, so they are blind to anything injected client-side. We render your site as a real visitor every five minutes and watch what it actually loads.
Stop trusting that nothing's wrong. Start watching.
Add your first domain, confirm ownership, and the inaugural scan runs immediately. The 5-minute cycle begins right after — no agent, no plugin, no DNS change required.