Account-Verification and Browser OTP Services on Corporate Assets
WindowsmacOSNetwork
IOC Hunt low DPRK IT Worker Fraud
The services an operation uses to phone-verify accounts in bulk, hold VoIP numbers for personas, and generate multi-factor codes in a browser so a whole team can share one second factor.
What this hunt looks for
The operation manual in Hayden McKenzie's second report names the services behind its accounts: sms-man.com and sms-activate.org for bulk phone verification, mailforspam.com as a throwaway Gmail secondary address, slynumber.com and hushed.com for persona numbers, and ssn-check.org for validating Social Security numbers. His fourth report shows SlyNumber used by a working cell.
The manual also names 2fa.live for browser TOTP generation, so multi-factor secrets are shared across a team rather than bound to a device. It is the service most likely to reach an employer's network after hire, because the employer's own prompt is what the code answers. The others belong mostly to the pre-hire stage.
- Technique
- Establish Accounts (MITRE ATT&CK T1585) and Multi-Factor Authentication interception or sharing (T1111) in support of fraudulently obtained employment (T1078).
- Data sources
- DNS or proxy logs, EDR network telemetry
- Last updated
- 2026-10-04
Indicators
The atomic values this hunt matches on, listed separately from the queries so they can be lifted into tooling that speaks none of the query languages below. Every value here is reported by one of the sources cited at the foot of this page.
| Type | Value | Note |
|---|---|---|
| Domain | 2fa.live |
Browser-based TOTP generation, so a multi-factor secret can be shared across a team (HM2). |
| Domain | sms-man.com |
SMS verification service for phone-verifying accounts at scale (HM2). |
| Domain | sms-activate.org |
SMS verification service for phone-verifying accounts at scale (HM2). |
| Domain | mailforspam.com |
Temporary email, used as the secondary verification address on new Gmail accounts (HM2). |
| Domain | slynumber.com |
VoIP numbers used to satisfy phone verification on persona mailboxes and profiles (HM2, HM4). |
| Domain | hushed.com |
VoIP numbers named beside SlyNumber as the replacement for Skype and Google Voice (HM2). |
| Domain | ssn-check.org |
Public Social Security number validation service used on stolen or borrowed identities (HM2). |
Logic
Every query below implements these steps, so the same hunt returns the same result in each product.
- Collect: DNS lookups and web connections from Windows and macOS endpoints, plus DNS server or proxy logs
- Match: name is 2fa.live, or a subdomain of it, the browser one-time-code generator
- Match: or name is, or is a subdomain of, one of six pre-hire services (SMS verification, disposable mail and numbers, SSN check)
- Report: sources with lookup counts and the distinct services reached, most services first
Queries
Defender / Sentinel KQL
Written for this site, from indicators reported by Hayden McKenzieA source that reaches 2fa.live, or several of the listed services, is worth checking against the issued-machine inventory; macOS hosts rely on the DnsEvents branch where Defender omits the URL.
// Parent = the last two labels of the host, so any subdomain of a listed domain matches.
union
(DeviceNetworkEvents
| where Timestamp > ago(90d) and isnotempty(RemoteUrl)
| project Timestamp, Source = DeviceName, Name = RemoteUrl),
(DnsEvents
| where TimeGenerated > ago(90d)
| project Timestamp = TimeGenerated, Source = ClientIP, Name)
| extend Parent = extract(@"^(?:[a-z][a-z0-9+.-]*://)?(?:[^/:?#]*\.)?([^./:?#]+\.[^./:?#]+)\.?(?:[:/?#]|$)", 1, tolower(Name))
| where Parent in~ (
// optional: the six pre-hire services are lower fidelity; delete this line and the next
"sms-man.com", "sms-activate.org", "mailforspam.com", "slynumber.com", "hushed.com", "ssn-check.org",
"2fa.live")
| summarize Lookups = count(), FirstSeen = min(Timestamp), LastSeen = max(Timestamp), Domains = make_set(Parent) by Source
| order by array_length(Domains) desc, Lookups desc
Lower-fidelity parts (1)
- the six pre-hire services (sms-man.com, sms-activate.org, mailforspam.com, slynumber.com, hushed.com, ssn-check.org) — disposable numbers and addresses have ordinary privacy uses and most of this activity precedes hire on the operation's own machines. Remove: delete the // optional: comment line and the line of six domains beneath it, leaving only "2fa.live"
Splunk SPL
Written for this site, from indicators reported by Hayden McKenzieThe same match over DNS logs, grouped by client so a source reaching several services sorts first.
```Placeholder: index=dns carries the CIM DNS field query; for proxy data use the host field your proxy extracts.```
index=dns earliest=-90d
| eval query=lower(query)
| rex field=query "(?<parent>[^.]+\.[^.]+)\.?$"
| where parent IN (
```optional: the six pre-hire services are lower fidelity; delete this line and the next```
"sms-man.com", "sms-activate.org", "mailforspam.com", "slynumber.com", "hushed.com", "ssn-check.org",
"2fa.live")
| stats count AS lookups min(_time) AS first_seen max(_time) AS last_seen dc(parent) AS services values(parent) AS domains BY src
| convert ctime(first_seen) ctime(last_seen)
| sort - services, - lookups
Lower-fidelity parts (1)
- the six pre-hire services (sms-man.com, sms-activate.org, mailforspam.com, slynumber.com, hushed.com, ssn-check.org) — disposable numbers and addresses have ordinary privacy uses and most of this activity precedes hire on the operation's own machines. Remove: delete the // optional: comment line and the line of six domains beneath it, leaving only "2fa.live"
Sigma
Written for this site, from indicators reported by Hayden McKenzieThe same match as an endpoint DNS rule that applies to Windows and macOS DNS telemetry.
title: DNS Lookup of an Account-Verification or Browser One-Time-Code Service
id: 0e1399c7-1224-4248-b93c-03deeedd0f48
status: experimental
description: >
Detects lookups of services named in DPRK IT-worker operation material for
bulk phone verification, disposable numbers and email, SSN validation, and
browser-based TOTP generation that lets a team share one second factor.
references:
- https://haydenmckenzie.com/research/dprk-it-worker-operation-part-two
- https://haydenmckenzie.com/research/dprk-it-worker-cell-part-one
logsource:
category: dns_query
detection:
selection_otp_exact:
QueryName: '2fa.live'
selection_otp_sub:
QueryName|endswith: '.2fa.live'
# optional: the six pre-hire services are lower fidelity; delete this line and the two selections below it
selection_prehire_exact:
QueryName:
- 'sms-man.com'
- 'sms-activate.org'
- 'mailforspam.com'
- 'slynumber.com'
- 'hushed.com'
- 'ssn-check.org'
selection_prehire_sub:
QueryName|endswith:
- '.sms-man.com'
- '.sms-activate.org'
- '.mailforspam.com'
- '.slynumber.com'
- '.hushed.com'
- '.ssn-check.org'
condition: 1 of selection_*
falsepositives:
- Privacy-minded staff using a second number or a disposable address for
personal sign-ups
level: medium
Lower-fidelity parts (1)
- the six pre-hire services (sms-man.com, sms-activate.org, mailforspam.com, slynumber.com, hushed.com, ssn-check.org) — disposable numbers and addresses have ordinary privacy uses and most of this activity precedes hire on the operation's own machines. Remove: delete the # optional: comment line and the selection_prehire_exact and selection_prehire_sub blocks beneath it
CrowdStrike LogScale
Written for this site, from indicators reported by Hayden McKenzieThe same match over Falcon DnsRequest events on Windows and macOS.
#event_simpleName=DnsRequest
| lower(DomainName, as=DomainName)
| regex("(?<parent>[^.]+\\.[^.]+)\\.?$", field=DomainName)
| in(parent, values=[
// optional: the six pre-hire services are lower fidelity; delete this line and the next
"sms-man.com", "sms-activate.org", "mailforspam.com", "slynumber.com", "hushed.com", "ssn-check.org",
"2fa.live"])
| groupBy([ComputerName], function=[count(as=Lookups), min(@timestamp, as=FirstSeen), max(@timestamp, as=LastSeen), count(parent, distinct=true, as=Services), collect([parent])])
| sort(Services, order=desc)
Lower-fidelity parts (1)
- the six pre-hire services (sms-man.com, sms-activate.org, mailforspam.com, slynumber.com, hushed.com, ssn-check.org) — disposable numbers and addresses have ordinary privacy uses and most of this activity precedes hire on the operation's own machines. Remove: delete the // optional: comment line and the line of six domains beneath it, leaving only "2fa.live"
Elastic
Written for this site, from indicators reported by Hayden McKenzieThe same match over ECS DNS and network events, grouped by host.
FROM logs-*
| EVAL name = TO_LOWER(COALESCE(dns.question.name, destination.domain))
| WHERE name RLIKE "(.*\\.)?(2fa\\.live|sms-man\\.com|sms-activate\\.org|mailforspam\\.com|slynumber\\.com|hushed\\.com|ssn-check\\.org)\\.?"
| EVAL parent = REPLACE(name, "^(.*\\.)?([^.]+\\.[^.]+)\\.?$", "$2")
| STATS lookups = COUNT(*), services = COUNT_DISTINCT(parent), domains = VALUES(parent), first_seen = MIN(@timestamp), last_seen = MAX(@timestamp) BY host.name
| SORT services DESC, lookups DESC
Lower-fidelity parts (1)
- the six pre-hire services (sms-man.com, sms-activate.org, mailforspam.com, slynumber.com, hushed.com, ssn-check.org) — disposable numbers and addresses have ordinary privacy uses and most of this activity precedes hire on the operation's own machines. Remove: in the RLIKE pattern, delete the six pre-hire names and their pipes, leaving (.*\\.)?(2fa\\.live)\\.?
Cortex XQL
Written for this site, from indicators reported by Hayden McKenzieThe same match over Cortex network events carrying a DNS name.
dataset = xdr_data
| filter event_type = ENUM.NETWORK and (dns_query_name != null or action_external_hostname != null)
| alter host = lowercase(coalesce(dns_query_name, action_external_hostname))
| alter parent = arrayindex(regextract(host, "([^.]+[.][^.]+)[.]?$"), 0)
| filter parent in (
// optional: the six pre-hire services are lower fidelity; delete this line and the next
"sms-man.com", "sms-activate.org", "mailforspam.com", "slynumber.com", "hushed.com", "ssn-check.org",
"2fa.live")
| comp count() as lookups, count_distinct(parent) as services, min(_time) as first_seen by agent_hostname
| sort desc services, desc lookups
Lower-fidelity parts (1)
- the six pre-hire services (sms-man.com, sms-activate.org, mailforspam.com, slynumber.com, hushed.com, ssn-check.org) — disposable numbers and addresses have ordinary privacy uses and most of this activity precedes hire on the operation's own machines. Remove: delete the // optional: comment line and the line of six domains beneath it, leaving only "2fa.live"
False positives and tuning
Disposable numbers and throwaway addresses have privacy uses, and browser TOTP sites serve people who never set up an authenticator app. A single lookup means little.
The stronger result is 2fa.live reached as the same account completes a multi-factor prompt, or several of these services reached from one issued machine. Most activity precedes hire, on the operation's own machines.
Sources
- Dissecting the Identity Lifecycle — Hayden McKenzie, 2026-08-13
- The Women Behind North Korea's IT Worker Operations — Hayden McKenzie, 2026-09-23