Laptop Shipping Address and Sign-In Geography Against the Record

Identity / cloud

Behavioral Hunt medium DPRK IT Worker FraudLaptop Farm

Joins HR, asset-shipping and identity-provider data to find new hires whose laptop shipped away from their recorded address, or whose first month of sign-ins geolocates away from it, the control Mandiant and the FBI single out.

What this hunt looks for

Every laptop-farm case begins with a shipment. The employer sends a device to the address the new hire gave, which in a DPRK operation belongs to a facilitator; the Department of Justice's June 2025 action describes facilitators hosting victim-company laptops at their residences and reaching them with KVM switches. The paper trail exists before the device boots.

State, Treasury and FBI guidance (2022) warns of developers who cannot receive items at the address on their identification; the FBI recommends signature-on-delivery (2023) and flags address or payment-platform changes during onboarding (2025). Mandiant's recommendation is implemented here: verify the laptop ships to, and geolocates at, the stated home.

Three joins do the work: the shipping address on the asset record against the HR home address; the state and country of the first thirty days of sign-ins against the record; and the location of the first MDM enrolment against the same. None needs endpoint telemetry, so it catches the hardware KVM case endpoint hunts cannot.

Technique
Valid-account misuse by a fraudulently hired employee (MITRE ATT&CK T1078) supported by hardware or software remote access from a location other than the one the identity claims (T1200, T1219); detection through provisioning and identity data rather than endpoint telemetry.
Data sources
HR information system, Asset and shipping records, Identity provider sign-in logs, MDM enrolment records
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.

TypeValueNote
Red flag Laptop shipped to an address that is not the one on the employee's identification or HR record State, Treasury and FBI guidance (2022) and FBI PSA231018 (2023).
Red flag First-month sign-ins geolocating to a different state or country from the address on file Mandiant's recommendation to verify the laptop geolocates where the individual reports living (Google Cloud, 2024).

Logic

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

  1. Collect: Entra ID sign-ins (including Intune enrolment), the HR home address, and the asset shipping address
  2. Exclude: your own office and VPN egress ranges (replace the placeholder CIDR list)
  3. Match: no sign-in in the 30 days after start date comes from the home country, or none from the home state
  4. Match: or an Intune enrolment sign-in in that window comes from outside the home state or country
  5. Match: or the laptop's shipping postal code differs from the home postal code
  6. Report: new hires with the countries, states and addresses seen, newest start date first

Queries

Defender / Sentinel KQL

Written for this site, from indicators reported by Mandiant, FBI, Treasury

Rows are new hires whose first-month sign-ins or Intune enrolment sit away from the recorded home; a mismatch is a question for the hiring manager, not an alert.

// Hunt: new hires whose first month of sign-ins, or first Intune enrolment, sits away from the home address on record.
// Needs a Sentinel watchlist NewHires from HR: HomeState as the region name, HomeCountry as the two-letter code the identity provider reports.
let CorporateEgress = dynamic([
    "198.51.100.0/24",   // replace with your own office and VPN egress ranges
    "203.0.113.0/24"
]);
let NewHires = _GetWatchlist('NewHires')
    | project UserPrincipalName = tostring(UserPrincipalName),
              HomeState = toupper(trim(@"\s+", tostring(HomeState))),
              HomeCountry = toupper(trim(@"\s+", tostring(HomeCountry))),
              StartDate = todatetime(StartDate);
SigninLogs
| where TimeGenerated > ago(120d) and ResultType == "0"
| where not(coalesce(ipv4_is_in_any_range(IPAddress, CorporateEgress), false))
| join kind=inner NewHires on UserPrincipalName
| where TimeGenerated between (StartDate .. (StartDate + 30d))
| extend SignInState = toupper(trim(@"\s+", tostring(LocationDetails.state))),
         SignInCountry = toupper(trim(@"\s+", tostring(LocationDetails.countryOrRegion)))
| where isnotempty(SignInCountry)
| summarize SignIns = count(), States = make_set(SignInState), Countries = make_set(SignInCountry),
            EnrolmentAwayFromHome = countif(AppDisplayName =~ "Microsoft Intune Enrollment" and (SignInCountry != HomeCountry or SignInState != HomeState)),
            Addresses = make_set(IPAddress, 20)
        by UserPrincipalName, HomeState, HomeCountry, StartDate
| where set_has_element(Countries, HomeCountry) == false or set_has_element(States, HomeState) == false or EnrolmentAwayFromHome > 0
| project UserPrincipalName, StartDate, HomeCountry, HomeState, Countries, States, EnrolmentAwayFromHome, SignIns, Addresses
| sort by StartDate desc

Splunk SPL

Written for this site, from indicators reported by Mandiant, FBI, Treasury

Combines the shipping-address mismatch and the sign-in mismatch in one result keyed by employee; replace the placeholder egress ranges and lookups first.

```Placeholders: index=identity holds Okta, Entra ID or ms:aad:signin events with action, src_ip, user and app extracted; new_hires.csv carries user, start_time (epoch), home_state and home_country in the full-name form iplocation returns; asset_shipments.csv and employee_directory.csv come from the asset system and HR.```
| inputlookup asset_shipments.csv
| lookup employee_directory.csv employee_id OUTPUT home_postal_code
| eval ship_postal_code=upper(trim(ship_postal_code)), home_postal_code=upper(trim(home_postal_code))
| where isnotnull(home_postal_code) AND ship_postal_code!=home_postal_code
| eval signal="shipping-address-mismatch", subject=employee_id, detail=ship_postal_code." shipped, ".home_postal_code." on record", when=ship_date
| append
    [ search index=identity (sourcetype=okta OR sourcetype=azuread OR sourcetype="ms:aad:signin") action=success earliest=-120d
      | where NOT (cidrmatch("198.51.100.0/24", src_ip) OR cidrmatch("203.0.113.0/24", src_ip))
      | lookup new_hires.csv user OUTPUT start_time home_state home_country
      | eval home_state=trim(home_state), home_country=trim(home_country)
      | where isnotnull(start_time) AND _time>=start_time AND _time<=(start_time+2592000)
      | iplocation src_ip
      | eval enrol_away=if(app="Microsoft Intune Enrollment" AND (lower(Country)!=lower(home_country) OR lower(Region)!=lower(home_state)), 1, 0)
      | stats count AS sign_ins values(Region) AS regions values(Country) AS countries values(src_ip) AS addresses max(enrol_away) AS enrol_away BY user, home_state, home_country, start_time
      | where isnull(mvfind(countries, "(?i)^".home_country."$")) OR isnull(mvfind(regions, "(?i)^".home_state."$")) OR enrol_away=1
      | eval signal="signin-away-from-home", subject=user, detail=mvjoin(countries, "/")." vs ".home_country, when=start_time ]
| table subject, signal, detail, when
| sort - when

False positives and tuning

People move, ship to a workplace or a relative, and travel in their first month, so a mismatch is a question for the hiring manager rather than an alert.

Corporate VPN egress geolocates every sign-in to the concentrator and must be excluded, or the join run against the pre-VPN device address the identity provider records.

Geolocation of consumer addresses is accurate to the state more often than the city, hence the state and country comparison. The hunt is only as good as the HR export.

If the address on file was never verified against an identity document, comparing against it proves little; run the shipping check against the document address.

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.