Sea Turtle-Style Authoritative DNS Hijack Revealed by NS/MX Drift and Mismatched Certificate Issuance
Sea Turtle (tracked by Cisco Talos since 2019) and the related DNSpionage campaign pioneered a distinct variant of infrastructure compromise: rather than registering a new attacker-owned domain, the operator compromises the victim's domain registrar account, the registry, or an upstream/vendor DNS provider and silently rewrites the authoritative NS, A, or MX records for the victim's own legitimate domain. Because the domain name itself never changes, certificate pinning, brand-reputation filtering, and lookalike-domain detections are all blind to this technique — traffic to mail.victim.com or vpn.victim.com is simply routed to adversary-controlled infrastructure while the hostname the end user sees remains completely legitimate. Sea Turtle went further: once DNS control was established, the operator requested a fresh, validly-issued Let's Encrypt TLS certificate for the hijacked hostname through automated domain validation, which succeeds because the attacker now controls the very DNS records used to prove domain ownership — letting them terminate TLS and intercept credentials without triggering any certificate warning in the victim's browser. Detection therefore cannot rely on domain reputation; it must instead monitor the victim's own authoritative DNS records for unexpected drift against a known-good baseline, and independently monitor Certificate Transparency logs for newly-issued certificates on the organization's own domains from a certificate authority that does not match its normal issuance pattern. Either signal alone has a plausible innocent explanation (a legitimate DNS provider migration, or a routine certificate renewal through a new vendor); the two signals occurring for the same domain within a matter of days is the high-confidence hijack-and-intercept chain this detection is built to surface.
What is THREAT-Infra-SeaTurtleDNSRegistrarHijack Sea Turtle-Style Authoritative DNS Hijack Revealed by NS/MX Drift and Mismatched Certificate Issuance?
Sea Turtle-Style Authoritative DNS Hijack Revealed by NS/MX Drift and Mismatched Certificate Issuance (THREAT-Infra-SeaTurtleDNSRegistrarHijack) maps to the Resource Development tactic — the adversary is trying to establish resources they can use to support operations in MITRE ATT&CK.
This page provides production-ready detection logic for Sea Turtle-Style Authoritative DNS Hijack Revealed by NS/MX Drift and Mismatched Certificate Issuance, covering the data sources and telemetry it touches: DNS: DNS query completed (external authoritative-record monitoring), Certificate: Certificate Registration (Certificate Transparency log ingestion), Custom log tables — AuthoritativeDNSMonitor_CL, CertTransparency_CL. The queries below are rated high severity at medium confidence, and ship for 7 SIEM platforms — KQL, SPL, Elastic, QRadar, Sumo, YARA-L, LogScale.
MITRE ATT&CK
- Tactic
- Resource Development
let LookbackBaseline = 14d;
let DetectWindow = 24h;
// Customize with your organization's actual authoritative DNS provider hostnames and mail platform
let ApprovedNSProviders = dynamic(["ns1.contoso-dns.com", "ns2.contoso-dns.com", "ns-cloud-a1.googledomains.com"]);
let ApprovedMXHosts = dynamic(["mail.protection.outlook.com", "aspmx.l.google.com"]);
// Customize with the certificate authority your organization actually issues through; a change away from this is the tripwire
let ApprovedCAs = dynamic(["DigiCert Inc", "Sectigo Limited", "GlobalSign nv-sa"]);
// Part 1: authoritative NS/MX record drift against a 14-day baseline, ingested from a scheduled external
// registrar/DNS-over-HTTPS lookup job into a custom table (AuthoritativeDNSMonitor_CL)
let DNSBaseline = AuthoritativeDNSMonitor_CL
| where TimeGenerated between (ago(LookbackBaseline + DetectWindow) .. ago(DetectWindow))
| where RecordType_s in ("NS", "MX")
| summarize BaselineValues = make_set(RecordValue_s) by Domain_s, RecordType_s;
let DNSDrift = AuthoritativeDNSMonitor_CL
| where TimeGenerated > ago(DetectWindow)
| where RecordType_s in ("NS", "MX")
| join kind=inner DNSBaseline on Domain_s, RecordType_s
| where RecordValue_s !in (BaselineValues)
| where not(RecordType_s == "NS" and RecordValue_s has_any (ApprovedNSProviders))
| where not(RecordType_s == "MX" and RecordValue_s has_any (ApprovedMXHosts))
| project TimeGenerated, Domain_s, SignalType = "UnauthorizedDNSRecordDrift", Detail = strcat(RecordType_s, " -> ", RecordValue_s);
// Part 2: newly-issued certificate for a monitored domain from a CA outside the approved list, ingested
// from a Certificate Transparency log stream (crt.sh / CertSpotter-style feed) into CertTransparency_CL
let CertMismatch = CertTransparency_CL
| where TimeGenerated > ago(DetectWindow)
| where not(IssuerCA_s has_any (ApprovedCAs))
| project TimeGenerated, Domain_s = SubjectCN_s, SignalType = "UnapprovedCAIssuance", Detail = strcat("Issued by ", IssuerCA_s);
// Part 3: correlate both signals per domain - two independent signals on the same domain is the
// hijack-then-intercept chain; a single signal is still surfaced but at lower confidence
DNSDrift
| union CertMismatch
| summarize Signals = make_set(SignalType), SignalCount = dcount(SignalType), Events = count(),
FirstSeen = min(TimeGenerated), LastSeen = max(TimeGenerated), Evidence = make_set(Detail)
by Domain_s
| extend ChainConfirmed = SignalCount >= 2
| project Domain_s, ChainConfirmed, Signals, Events, FirstSeen, LastSeen, Evidence
| order by ChainConfirmed desc, LastSeen desc Two-signal detection for registrar/authoritative-DNS hijack. Part 1 compares each monitored domain's current NS/MX records (ingested from a scheduled external DNS lookup into AuthoritativeDNSMonitor_CL) against a 14-day baseline and flags drift to a value outside the organization's approved provider lists. Part 2 flags any newly-observed certificate for a monitored domain (ingested from a Certificate Transparency log stream into CertTransparency_CL) issued by a CA outside the organization's approved-issuer list. Part 3 groups both signal types by domain: a domain showing both an unauthorized DNS record change and an unapproved-CA certificate is the Sea Turtle-style hijack-then-intercept chain (ChainConfirmed=true); a domain showing only one signal is still surfaced for review at lower confidence.
Data Sources
Required Tables
False Positives
- A legitimate, planned migration of DNS hosting or registrar (e.g., moving from one provider to Cloudflare or Route 53) deliberately changes NS records — update ApprovedNSProviders as part of the change-management ticket before the cutover
- A legitimate mail platform migration (e.g., moving on-premises Exchange to Microsoft 365 or Google Workspace) changes MX records as an expected, scheduled event
- A legitimate certificate authority migration (e.g., switching to Let's Encrypt or ZeroSSL for cost or automation reasons) will trigger the CA-mismatch signal even though it is an authorized change — update ApprovedCAs in advance of the switch
- Certificate Transparency log ingestion duplicates: multiple independent CT logs recording the same legitimate renewal within minutes of each other can inflate the Events count without indicating compromise
Sigma rule & cross-platform mapping
The detection logic for Sea Turtle-Style Authoritative DNS Hijack Revealed by NS/MX Drift and Mismatched Certificate Issuance (THREAT-Infra-SeaTurtleDNSRegistrarHijack) above is provided in a vendor-neutral
form so you can deploy it on any SIEM. The same logic is shipped here as native
KQL (Microsoft Sentinel / Defender), SPL (Splunk), Elastic (Elastic Security (EQL)), QRadar (IBM QRadar (AQL)), Sumo (Sumo Logic CSE), YARA-L (Google Chronicle / SecOps), LogScale (CrowdStrike LogScale (CQL)) queries. In Sigma terms, this detection targets the
following logsource:
logsource:
category: network_connection
product: windows Browse the community-maintained Sigma rules for this technique:
Platform-specific guides for THREAT-Infra-SeaTurtleDNSRegistrarHijack
Testing Methodology
Validate this detection against 3 adversary techniques from Atomic Red Team. Each test below lists the behaviour to exercise and the telemetry you should expect to see. Executable commands and cleanup steps are available with Pro.
- Test 1Simulate Unauthorized NS Record Drift for a Monitored Domain
Expected signal: A single AuthoritativeDNSMonitor_CL row for Domain_s=atomic-test.internal, RecordType_s=NS, RecordValue_s=ns1.rogue-hijack-test.invalid, differing from any prior baseline value for that domain.
- Test 2Simulate Unapproved-CA Certificate Issuance for the Same Domain
Expected signal: A single CertTransparency_CL row for SubjectCN_s=atomic-test.internal, IssuerCA_s=Rogue Test CA, which does not match any entry in ApprovedCAs.
- Test 3Combined Chain: DNS Drift Plus Unapproved-CA Issuance Within the Correlation Window
Expected signal: Two custom-table rows for the same test domain: one AuthoritativeDNSMonitor_CL NS drift row and one CertTransparency_CL unapproved-CA row, both timestamped within the same 24-hour detection window.
Response Playbook
Triage
- Confirm the DNS change is genuinely unauthorized: check change-management tickets and the registrar/DNS provider's own audit log for an authenticated, expected change matching the timestamp.
- Pull the registrar account's authentication history around the change window — source IP, user agent, and whether MFA was satisfied or bypassed — to determine whether the account itself was compromised.
- For a flagged certificate, retrieve the full Certificate Transparency entry (issuer, serial number, validation method, SAN list) and compare the SAN list against the domain's legitimate hostname inventory; an unexpected SAN entry (e.g., a wildcard or an internal-looking hostname) indicates broader intent.
- If both signals fired for the same domain (ChainConfirmed=true), treat this as an active interception incident, not a hygiene finding — traffic to that hostname may currently be terminated by adversary infrastructure.
- Check whether the affected domain fronts mail (MX) or a VPN/SSO login surface — these are the two highest-value interception targets and change the urgency and blast radius calculus.
- Attempt an out-of-band resolution of the domain from multiple external vantage points (different geographic resolvers) to confirm whether the hijacked record has propagated globally or is only visible to a subset of resolvers (partial/targeted hijack).
- Review whether any users authenticated to the affected hostname during the exposure window and whether their credentials or session tokens should be treated as compromised.
Containment
- Revert the NS/MX record to the last known-good value directly through the registrar or registry, not just through the compromised account's own console if that account's credentials are suspect.
- Lock the registrar account, rotate its credentials and API keys, and enable/verify registry-lock or transfer-lock if the registrar supports it to prevent a repeat change.
- If a mismatched certificate was issued, request revocation from the issuing CA and, once DNS is restored, reissue and deploy a certificate from the organization's approved CA.
- Force a password reset and session/token revocation for any user who authenticated to the affected hostname (mail, VPN, SSO) during the suspected exposure window.
- Notify the registrar/registrant's abuse or fraud team and, where the registry itself may be compromised (as in the Sea Turtle campaign), escalate to the relevant ccTLD/gTLD operator.
- Add the rogue nameservers/mail hosts and the mismatched certificate's issuer/serial to a watchlist so recurrence — including against other domains in the portfolio — is caught immediately.
Evidence Collection
- Registrar/registry account audit logs: authentication events, API calls, and the specific record-change transaction with before/after values.
- Historical DNS record snapshots or passive DNS history establishing the exact prior NS/MX values and the timestamp of the change.
- The full Certificate Transparency log entry for the mismatched certificate: issuer, serial number, validation method, SAN list, and the CT log(s) it was submitted to.
- External resolution results from multiple independent DNS vantage points, establishing propagation scope and confirming the hijack is (or is not) still live.
- Identity provider or mail platform sign-in logs for the affected hostname during the exposure window, to identify potentially intercepted credentials or sessions.
- WHOIS/RDAP history showing registrar or nameserver delegation changes correlated with the detection timestamp.
Escalation Criteria
- ! Both signals fired for the same domain within the correlation window (ChainConfirmed=true) — this is a confirmed hijack-then-intercept chain, not a hygiene finding.
- ! The affected domain fronts mail (MX) or an identity/VPN login surface, indicating credential or communications interception rather than a low-value parked hostname.
- ! The registrar account shows authentication from an unfamiliar source with MFA bypassed or absent.
- ! Evidence of successful sign-ins or mail flow through the hijacked infrastructure during the exposure window.
- ! The same rogue nameserver or mismatched-CA pattern appears against more than one domain in the portfolio, indicating a registrar- or registry-level compromise rather than a single-domain account takeover.
Investigation Guide
Forensic Artifacts
- >
Registrar/registry audit logs showing the authenticated account, source IP, and the specific NS/MX/glue record change transaction. - >
Passive DNS history or scheduled zone snapshots showing the prior and post-change record values and the propagation timeline. - >
Certificate Transparency log entries (crt.sh, Google/Cloudflare CT monitors) for the domain, showing issuer, serial number, validation method, and SAN list. - >
Registrar/DNS provider account access logs, including API key usage and any MFA challenge/bypass events around the change. - >
WHOIS/RDAP historical records showing registrar or nameserver delegation changes.
Tuning Guidance
This detection is only as good as its three allowlists, all of which must be populated per organization before deployment: ApprovedNSProviders and ApprovedMXHosts should list every nameserver/mail host your domains legitimately use today, and ApprovedCAs should list the specific certificate authority (or authorities) your organization actually issues through — shipped generically, a Let's Encrypt or ZeroSSL shop will alert on every routine renewal. Seed the 14-day DNS baseline and the 90/180-day CT baselines before enabling alerting, since both AuthoritativeDNSMonitor_CL and CertTransparency_CL depend on an external scheduled job (a DNS-over-HTTPS/registrar-API poller and a CT-log stream subscriber respectively) that this detection assumes is already populating those tables — without that ingestion pipeline neither signal exists. Route planned DNS or CA migrations through change management and update the relevant allowlist before the cutover so the standing detection does not fire on authorized work; where a migration cannot be pre-announced, expect and accept a single confirmatory alert rather than suppressing the detection outright. Prioritize ChainConfirmed=true results — a domain showing both an unapproved DNS record and an unapproved-CA certificate within the same window is materially higher confidence than either signal alone, and should be worked ahead of single-signal findings regardless of arrival order.
Hunting Queries
Groups newly-observed nameserver values (never seen anywhere in the 90-day portfolio history) by the nameserver itself rather than by domain, surfacing a single rogue nameserver that has taken over delegation for one or more domains — the pattern seen when a registry- or reseller-level compromise (as in Sea Turtle) affects multiple victims through shared upstream infrastructure.
// Hunt: nameserver value never before observed across the entire monitored domain portfolio (cluster indicator)
let BaselineWindow = 90d;
let SeenNS = AuthoritativeDNSMonitor_CL
| where TimeGenerated between (ago(BaselineWindow) .. ago(24h))
| where RecordType_s == "NS"
| distinct RecordValue_s;
AuthoritativeDNSMonitor_CL
| where TimeGenerated > ago(24h)
| where RecordType_s == "NS"
| where RecordValue_s !in (SeenNS)
| summarize DomainsAffected = dcount(Domain_s), Domains = make_set(Domain_s, 25) by RecordValue_s
| where DomainsAffected >= 1
| order by DomainsAffected desc index=dns_monitor sourcetype="authoritative_dns_monitor" record_type="NS" earliest=-24h
| search NOT [
search index=dns_monitor sourcetype="authoritative_dns_monitor" record_type="NS" earliest=-90d latest=-24h
| fields record_value
| rename record_value as record_value
| dedup record_value
]
| stats dc(domain) as DomainsAffected, values(domain) as Domains by record_value
| sort - DomainsAffected Standalone, lower-confidence view of the certificate-authority signal that does not require a coincident DNS drift event — useful for catching a hijack where the DNS change happened outside the monitoring window or via a channel not yet instrumented (e.g., a stub zone or secondary DNS provider), leaving the CA change as the only visible artifact.
// Hunt: any certificate issued for a monitored domain by a CA never before seen for that specific domain, independent of DNS correlation
let BaselineWindow = 180d;
let KnownCAsPerDomain = CertTransparency_CL
| where TimeGenerated between (ago(BaselineWindow) .. ago(24h))
| summarize KnownCAs = make_set(IssuerCA_s) by Domain_s = SubjectCN_s;
CertTransparency_CL
| where TimeGenerated > ago(24h)
| extend Domain_s = SubjectCN_s
| join kind=inner KnownCAsPerDomain on Domain_s
| where IssuerCA_s !in (KnownCAs)
| project TimeGenerated, Domain_s, IssuerCA_s, KnownCAs index=certstream sourcetype="cert_transparency" earliest=-24h
| rename subject_cn as domain
| join type=inner domain
[ search index=certstream sourcetype="cert_transparency" earliest=-180d latest=-24h
| rename subject_cn as domain
| stats values(issuer_ca) as KnownCAs by domain ]
| where NOT mvfind(KnownCAs, issuer_ca) >= 0
| table _time, domain, issuer_ca, KnownCAs Atomic Red Team Tests
Inserts a synthetic row into the AuthoritativeDNSMonitor_CL custom table representing an NS record for a monitored test domain that resolves to a nameserver outside the ApprovedNSProviders list, simulating the DNS half of a registrar hijack without touching any real domain's records.
Command
# POST a synthetic event to the Log Analytics / SIEM ingestion API for table AuthoritativeDNSMonitor_CL:
# {"Domain_s":"atomic-test.internal","RecordType_s":"NS","RecordValue_s":"ns1.rogue-hijack-test.invalid","TimeGenerated":"<now>"}
curl -s -o /dev/null -X POST "$INGESTION_ENDPOINT/AuthoritativeDNSMonitor_CL" -H 'Content-Type: application/json' -d '{"Domain_s":"atomic-test.internal","RecordType_s":"NS","RecordValue_s":"ns1.rogue-hijack-test.invalid","TimeGenerated":"'"$(date -u +%Y-%m-%dT%H:%M:%SZ)"'"}' Cleanup
Delete or expire the injected test row from AuthoritativeDNSMonitor_CL per your table's retention/purge process. Expected Telemetry
A single AuthoritativeDNSMonitor_CL row for Domain_s=atomic-test.internal, RecordType_s=NS, RecordValue_s=ns1.rogue-hijack-test.invalid, differing from any prior baseline value for that domain.
Expected Detection
KQL/SPL Part 1 fires SignalType=UnauthorizedDNSRecordDrift for atomic-test.internal, since the value is absent from the 14-day baseline and not in ApprovedNSProviders.
Inserts a synthetic Certificate Transparency row into CertTransparency_CL for the same test domain, issued by a CA outside the ApprovedCAs list, simulating the automated domain-validated certificate an adversary obtains after establishing DNS control.
Command
# POST a synthetic event to the Log Analytics / SIEM ingestion API for table CertTransparency_CL:
# {"SubjectCN_s":"atomic-test.internal","IssuerCA_s":"Rogue Test CA","TimeGenerated":"<now>"}
curl -s -o /dev/null -X POST "$INGESTION_ENDPOINT/CertTransparency_CL" -H 'Content-Type: application/json' -d '{"SubjectCN_s":"atomic-test.internal","IssuerCA_s":"Rogue Test CA","TimeGenerated":"'"$(date -u +%Y-%m-%dT%H:%M:%SZ)"'"}' Cleanup
Delete or expire the injected test row from CertTransparency_CL per your table's retention/purge process. Expected Telemetry
A single CertTransparency_CL row for SubjectCN_s=atomic-test.internal, IssuerCA_s=Rogue Test CA, which does not match any entry in ApprovedCAs.
Expected Detection
KQL/SPL Part 2 fires SignalType=UnapprovedCAIssuance for atomic-test.internal.
Runs both prior atomic tests back to back for the same test domain within a short window, reproducing the full Sea Turtle-style hijack-then-intercept chain that the detection's Part 3 correlation is designed to escalate above single-signal findings.
Command
# Run the two prior atomic tests in sequence (NS drift, then unapproved-CA cert) for the same Domain_s/SubjectCN_s value within a few minutes of each other. Cleanup
Delete or expire both injected test rows from AuthoritativeDNSMonitor_CL and CertTransparency_CL. Expected Telemetry
Two custom-table rows for the same test domain: one AuthoritativeDNSMonitor_CL NS drift row and one CertTransparency_CL unapproved-CA row, both timestamped within the same 24-hour detection window.
Expected Detection
KQL/SPL Part 3 groups both signals by Domain_s=atomic-test.internal, SignalCount=2, and sets ChainConfirmed=true — the highest-confidence output tier.