Unrelated Employees Authenticating from One Fixed-Line Address

Identity / cloud

Behavioral Hunt medium DPRK IT Worker FraudLaptop Farm

The signature of a laptop farm seen from the identity side: two or more employees who have nothing to do with each other, signing in from the same residential address.

What this hunt looks for

ReliaQuest states the inference plainly: when two supposedly unrelated users in different environments connect from the exact same address, it strongly suggests a laptop farm. A facilitator racks employer-issued laptops in one residence and each egresses through its single connection; per the Department of Justice, Christina Chapman's operation ran more than ninety devices at one address.

Within a single employer the query is trivial, and its power grows when observations are shared between organisations, as across ReliaQuest's customers. ReliaQuest also notes that where one operator is uncovered several more are often present, up to ten inside one company within six months.

Technique
Valid-account misuse (MITRE ATT&CK T1078) surfaced by shared physical egress across accounts that should have no relationship.
Data sources
Identity provider sign-in logs, VPN concentrator logs
Last updated
2026-10-04

Logic

Every query below implements these steps, so the same hunt returns the same result in each product.

  1. Collect: successful Entra ID and Okta sign-ins with source address and user
  2. Exclude: your own office and VPN egress ranges (replace the placeholder CIDR list)
  3. Match: a source address used by 2 or more distinct users
  4. Match: (optional) only accounts first seen in the last 120 days, which removes most household and shared-office noise
  5. Report: addresses with their users, most users first

Queries

Defender / Sentinel KQL

Written for this site, from indicators reported by ReliaQuest

Addresses shared by two or more employees outside your own egress; fill the CorporateEgress ranges first or the output is meaningless.

// Hunt: unrelated employees signing in from one fixed-line address (a laptop-farm signature).
let CorporateEgress = dynamic([
    "198.51.100.0/24",   // replace with your own office and VPN egress ranges
    "203.0.113.0/24"
]);
// optional: restrict to accounts first seen in the last 120 days; the first sign-in is only a proxy for hire date and fails in a recently migrated tenant
let RecentHires = SigninLogs
    | where TimeGenerated > ago(180d)
    | summarize FirstEverSignIn = min(TimeGenerated) by UserPrincipalName
    | where FirstEverSignIn > ago(120d)
    | project UserPrincipalName;
SigninLogs
| where TimeGenerated > ago(90d)
| where ResultType == "0"
| where not(coalesce(ipv4_is_in_any_range(IPAddress, CorporateEgress), false))
// optional: recent hires only
| where UserPrincipalName in (RecentHires)
| summarize Users = dcount(UserPrincipalName), UserList = make_set(UserPrincipalName, 25),
            SignIns = count(), FirstSeen = min(TimeGenerated), LastSeen = max(TimeGenerated)
        by IPAddress
| where Users >= 2
| sort by Users desc
Lower-fidelity parts (1)
  • the recent-hires restriction — the first sign-in is only a proxy for hire date and fails in a recently migrated tenant. Remove: delete the // optional: restrict line with the let RecentHires statement under it, and the // optional: recent hires only line with the where under it

Splunk SPL

Written for this site, from indicators reported by ReliaQuest

Addresses shared by two or more employees outside your own egress; replace the placeholder CIDR ranges first.

```Placeholder: index=identity holds Okta, Entra ID or ms:aad:signin events with action, src_ip and user extracted.```
index=identity (sourcetype=okta OR sourcetype=azuread OR sourcetype="ms:aad:signin") action=success earliest=-180d
| eventstats min(_time) AS first_ever BY user
| where _time >= relative_time(now(), "-90d")
```replace the two placeholder ranges with your own office and VPN egress ranges```
| where NOT (cidrmatch("198.51.100.0/24", src_ip) OR cidrmatch("203.0.113.0/24", src_ip))
```optional: recent hires only; the first sign-in is a proxy for hire date and fails in a recently migrated tenant```
| where first_ever >= relative_time(now(), "-120d")
| stats dc(user) AS distinct_users values(user) AS users count AS signins min(_time) AS first_seen max(_time) AS last_seen BY src_ip
| where distinct_users >= 2
| convert ctime(first_seen) ctime(last_seen)
| sort - distinct_users
Lower-fidelity parts (1)
  • the recent-hires restriction — the first sign-in is only a proxy for hire date and fails in a recently migrated tenant. Remove: delete the ```optional comment and the where first_ever line under it

CrowdStrike LogScale

Written for this site, from indicators reported by ReliaQuest

Same logic over ingested Entra ID or Okta events, reading the address and user from the raw JSON; the recent-hires restriction is not expressible without a first-seen lookup.

// Hunt: unrelated employees signing in from one fixed-line address. Fields are read from the raw Entra ID or Okta JSON (ipAddress, userPrincipalName, alternateId), so no parser field name is assumed.
/"resultType"\s*:\s*"0"|"result"\s*:\s*"SUCCESS"/i
| regex("ipAddress[\" :]+(?<ip>[0-9a-fA-F:.]+)", strict=false)
| regex("(?:userPrincipalName|alternateId)[^a-zA-Z0-9_]+(?<user>[^\"]+)", strict=false)
| ip=* user=*
// replace the placeholder ranges with your own office and VPN egress ranges
| !cidr(ip, subnet=["198.51.100.0/24", "203.0.113.0/24"])
| groupBy([ip], function=[count(user, distinct=true, as=users), collect([user], limit=25), count(as=signIns), min(@timestamp, as=firstSeen), max(@timestamp, as=lastSeen)])
| users >= 2
| sort(users, order=desc)

Elastic

Written for this site, from indicators reported by ReliaQuest

Same logic over the Entra ID and Okta integrations; the recent-hires restriction needs a first-seen join that ES|QL does not express here.

FROM logs-azure.signinlogs-*, logs-okta*
| WHERE @timestamp > NOW() - 90 days
| WHERE event.outcome == "success"
| WHERE NOT CIDR_MATCH(source.ip, "198.51.100.0/24", "203.0.113.0/24")
| EVAL account = COALESCE(azure.signinlogs.properties.user_principal_name, user.name)
| STATS users = COUNT_DISTINCT(account), user_list = VALUES(account), sign_ins = COUNT(*), first_seen = MIN(@timestamp), last_seen = MAX(@timestamp) BY source.ip
| WHERE users >= 2
| SORT users DESC

False positives and tuning

Two employees at one address is usually a couple, a shared flat, a coworking space or a small satellite office. The corporate-egress exclusion has to be complete or the results are meaningless.

A genuine finding combines recent hire, unrelated accounts, a residential address and remote-access or KVM tooling. The recent-hire variant uses first sign-in as hire date, a proxy that fails in a recently migrated tenant.

Sources

Tuning required before use This hunt is a starting point. Adapt the queries to your schema, validate them against your baseline and tune them for the false positives noted above. Indicators go stale as operators rotate hosts.