SOCLIFE
SOCLIFE

Evidence-led security analysis

Published knowledge

Search SOC//LIFE

HuntsHUNT-009

Hypothesis-led threat hunting

Hunt for Photo ZIP Node JS Persistence Across Endpoints

Hunts user-space Node JS, fake photo shortcuts, PowerShell staging, Defender exclusions, dual Run/RunOnce persistence, high-port beaconing, and late-stage automation.

Question

Hunt goal

One or more endpoints may have executed photo-masquerading shortcuts that staged a Node JS implant, weakened endpoint protection, established dual registry persistence, and maintained command-and-control through user-space payloads.

Why this hunt

Microsoft observed the campaign across hospitality organizations in Europe and Asia from April 2026, with seven PowerShell obfuscation phases but a comparatively stable execution and persistence sequence.

Data sources

Where to look

  • ProcessEndpoint process telemetry.
  • FileEndpoint file telemetry including download origin/referrer.
  • RegistryEndpoint registry telemetry.
  • NetworkEndpoint network telemetry.

Search path

Hunt steps

Each search broadens the view from patient zero to related users, devices, infrastructure, and follow-on activity.
  1. Step 1First search

    Inventory user-space Node JS across endpoints

    Finding

    The baseline separates expected Node JS populations from rare user-space runtime activity.

    Developer and packaged-application devices should be identified before deeper hunting.

    View query
    Q-01First search

    Inventory user-space Node JS across endpoints

    What this checks

    Establish where Node JS runs from user-writable paths and identify rare populations.

    KQL
    DeviceProcessEvents
    | where Timestamp >= ago(30d)
    | where FileName =~ "node.exe"
    | where FolderPath has @"\AppData\Local\"
    | summarize FirstSeen=min(Timestamp),LastSeen=max(Timestamp),Executions=count(),
                Paths=make_set(FolderPath,20),Commands=make_set(ProcessCommandLine,50),
                Parents=make_set(InitiatingProcessFileName,20),Users=make_set(AccountUpn,20)
      by DeviceId,DeviceName
    | order by Executions asc,LastSeen desc
    SPL
    index=<endpoint_process_index> sourcetype=<process_events_sourcetype> earliest=-30d
    | eval device=coalesce(device,DeviceName,host),
           user=lower(coalesce(user,AccountUpn)),
           process_name=lower(coalesce(process_name,FileName)),
           process_path=lower(coalesce(process_path,FolderPath)),
           cmd=coalesce(process_command_line,ProcessCommandLine),
           parent=lower(coalesce(parent,InitiatingProcessFileName))
    | where process_name="node.exe" AND like(process_path,"%\\appdata\\local\\%")
    | stats min(_time) as first_seen max(_time) as last_seen count as executions
            values(process_path) as paths values(cmd) as commands values(parent) as parents values(user) as users
      by device
    | convert ctime(first_seen) ctime(last_seen)
    | sort executions - last_seen
    What to look for

    A result that helps separate normal endpoint activity from campaign-consistent behavior.

    Technical details
    Tested signal

    User-profile Node JS execution by device and user.

    Assumptions
    • Required endpoint telemetry is available and expected software populations can be identified.
    Data requirements and relevant fields
    process

    Endpoint process telemetry.

    • Timestamp
    • DeviceId
    • DeviceName
    • FileName
    • FolderPath
    • ProcessId
    • ProcessUniqueId
    • ProcessCommandLine
    • AccountUpn
    • SHA1
    • SHA256
    • InitiatingProcessFileName
    • InitiatingProcessFolderPath
    • InitiatingProcessCommandLine
    • InitiatingProcessUniqueId
    KQL schema

    Validate Defender table availability and retention.

    SPL schema

    Replace placeholders and map fields locally.

    Limitations
    • Source-specific filenames, domains, and ports are supporting pivots rather than universal requirements.

    KQL uses Microsoft Defender XDR endpoint telemetry. SPL is a normalized scaffold requiring local field mapping.

  2. Step 2Pivot

    Hunt fake photo shortcut execution

    Finding

    Fake PNG shortcut patterns connect browser/download activity to user execution.

    The names are useful retrospectively but can rotate.

    View query
    Q-02Pivot

    Hunt fake photo shortcut execution

    What this checks

    Search process/file telemetry for the two documented fake PNG shortcut naming families.

    KQL
    let p=
    DeviceProcessEvents
    | where Timestamp >= ago(30d)
    | where ProcessCommandLine has ".png.lnk"
    | where ProcessCommandLine has_any ("IMG-","PHOTO-")
    | project Timestamp,DeviceId,DeviceName,Signal="process",FileName,FolderPath,
              ProcessCommandLine,AccountUpn,InitiatingProcessFileName,SHA1,SHA256;
    let f=
    DeviceFileEvents
    | where Timestamp >= ago(30d)
    | where FileName endswith ".png.lnk"
    | where FileName startswith "IMG-" or FileName startswith "PHOTO-"
    | project Timestamp,DeviceId,DeviceName,Signal="file",FileName,FolderPath,
              ProcessCommandLine=InitiatingProcessCommandLine,
              AccountUpn=InitiatingProcessAccountUpn,InitiatingProcessFileName,
              FileOriginUrl,FileOriginReferrerUrl,SHA1,SHA256;
    union p,f
    | order by Timestamp desc
    SPL
    (
     index=<endpoint_process_index> sourcetype=<process_events_sourcetype> earliest=-30d
    )
    OR
    (
     index=<endpoint_file_index> sourcetype=<file_events_sourcetype> earliest=-30d
    )
    | eval device=coalesce(device,DeviceName,host),
           file_name=coalesce(file_name,FileName),
           cmd=coalesce(process_command_line,ProcessCommandLine,InitiatingProcessCommandLine),
           user=lower(coalesce(user,AccountUpn,InitiatingProcessAccountUpn)),
           origin_url=coalesce(origin_url,FileOriginUrl),
           referrer_url=coalesce(referrer_url,FileOriginReferrerUrl)
    | where like(lower(file_name),"img-%.png.lnk")
        OR like(lower(file_name),"photo-%.png.lnk")
        OR (like(lower(cmd),"%.png.lnk%") AND (like(cmd,"%IMG-%") OR like(cmd,"%PHOTO-%")))
    | table _time device user file_name cmd origin_url referrer_url InitiatingProcessFileName SHA1 SHA256
    | sort - _time
    What to look for

    A result that helps separate normal endpoint activity from campaign-consistent behavior.

    Technical details
    Tested signal

    IMG/PHOTO PNG-masquerading LNK activity.

    Assumptions
    • Required endpoint telemetry is available and expected software populations can be identified.
    Data requirements and relevant fields
    process

    Endpoint process telemetry.

    • Timestamp
    • DeviceId
    • DeviceName
    • FileName
    • FolderPath
    • ProcessId
    • ProcessUniqueId
    • ProcessCommandLine
    • AccountUpn
    • SHA1
    • SHA256
    • InitiatingProcessFileName
    • InitiatingProcessFolderPath
    • InitiatingProcessCommandLine
    • InitiatingProcessUniqueId
    file

    Endpoint file telemetry including download origin/referrer.

    • Timestamp
    • DeviceId
    • DeviceName
    • ActionType
    • FileName
    • FolderPath
    • SHA1
    • SHA256
    • FileOriginUrl
    • FileOriginReferrerUrl
    • FileOriginIP
    • FileSize
    • InitiatingProcessFileName
    • InitiatingProcessFolderPath
    • InitiatingProcessCommandLine
    • InitiatingProcessAccountUpn
    • InitiatingProcessUniqueId
    KQL schema

    Validate Defender table availability and retention.

    SPL schema

    Replace placeholders and map fields locally.

    Limitations
    • Source-specific filenames, domains, and ports are supporting pivots rather than universal requirements.

    KQL uses Microsoft Defender XDR endpoint telemetry. SPL is a normalized scaffold requiring local field mapping.

  3. Step 3Pivot

    Hunt behavioral PowerShell decode-and-download

    Finding

    Behavioral PowerShell hunting survives syntax changes by focusing on decode plus retrieval plus script staging.

    Literal XOR/variable signatures are less durable.

    View query
    Q-03Pivot

    Hunt behavioral PowerShell decode-and-download

    What this checks

    Find PowerShell combining arithmetic/BigInt-style decoding with web retrieval and script staging.

    KQL
    DeviceProcessEvents
    | where Timestamp >= ago(30d)
    | where FileName in~ ("powershell.exe","pwsh.exe")
    | where ProcessCommandLine has_any ("[bigint]","-as [bigint]","-band","-shr","% 256","/ 256")
    | where ProcessCommandLine has_any ("Invoke-WebRequest"," iwr ","iwr ")
    | where ProcessCommandLine has_any ("OutFile",".ps1")
    | project Timestamp,DeviceName,AccountUpn,FileName,ProcessCommandLine,FolderPath,
              SHA1,SHA256,InitiatingProcessFileName,InitiatingProcessCommandLine
    | order by Timestamp desc
    SPL
    index=<endpoint_process_index> sourcetype=<process_events_sourcetype> earliest=-30d
    | eval device=coalesce(device,DeviceName,host),
           user=lower(coalesce(user,AccountUpn)),
           process_name=lower(coalesce(process_name,FileName)),
           cmd=lower(coalesce(process_command_line,ProcessCommandLine))
    | where process_name IN ("powershell.exe","pwsh.exe")
      AND (like(cmd,"%[bigint]%") OR like(cmd,"%-as [bigint]%") OR like(cmd,"%-band%") OR like(cmd,"%-shr%"))
      AND (like(cmd,"%invoke-webrequest%") OR like(cmd,"%iwr %"))
      AND (like(cmd,"%outfile%") OR like(cmd,"%.ps1%"))
    | table _time device user process_name ProcessCommandLine FolderPath SHA1 SHA256 InitiatingProcessFileName InitiatingProcessCommandLine
    | sort - _time
    What to look for

    A result that helps separate normal endpoint activity from campaign-consistent behavior.

    Technical details
    Tested signal

    Obfuscated decode plus web download plus script output.

    Assumptions
    • Required endpoint telemetry is available and expected software populations can be identified.
    Data requirements and relevant fields
    process

    Endpoint process telemetry.

    • Timestamp
    • DeviceId
    • DeviceName
    • FileName
    • FolderPath
    • ProcessId
    • ProcessUniqueId
    • ProcessCommandLine
    • AccountUpn
    • SHA1
    • SHA256
    • InitiatingProcessFileName
    • InitiatingProcessFolderPath
    • InitiatingProcessCommandLine
    • InitiatingProcessUniqueId
    KQL schema

    Validate Defender table availability and retention.

    SPL schema

    Replace placeholders and map fields locally.

    Limitations
    • Source-specific filenames, domains, and ports are supporting pivots rather than universal requirements.

    KQL uses Microsoft Defender XDR endpoint telemetry. SPL is a normalized scaffold requiring local field mapping.

  4. Step 4Pivot

    Hunt Defender exclusion followed by Temp execution

    Finding

    A tight exclusion-to-temp-execution sequence identifies active payload staging.

    Validate managed installation/security workflows before containment.

    View query
    Q-04Pivot

    Hunt Defender exclusion followed by Temp execution

    What this checks

    Search for Defender ExclusionProcess changes followed by user-temp executable launches.

    KQL
    let e=DeviceProcessEvents
    | where Timestamp >= ago(30d)
    | where FileName in~ ("powershell.exe","pwsh.exe")
    | where ProcessCommandLine has "Add-MpPreference" and ProcessCommandLine has "-ExclusionProcess"
    | project DeviceId,DeviceName,ExclusionTime=Timestamp,ExclusionCmd=ProcessCommandLine;
    let x=DeviceProcessEvents
    | where Timestamp >= ago(30d)
    | where FolderPath has @"\AppData\Local\Temp\" and FileName endswith ".exe"
    | project DeviceId,ExecTime=Timestamp,FileName,FolderPath,ProcessCommandLine;
    e
    | join kind=inner x on DeviceId
    | where ExecTime between (ExclusionTime .. ExclusionTime+30m)
    | project DeviceName,ExclusionTime,ExclusionCmd,ExecTime,FileName,FolderPath,ProcessCommandLine
    | order by ExclusionTime desc
    SPL
    index=<endpoint_process_index> sourcetype=<process_events_sourcetype> earliest=-30d
    | eval device=coalesce(device,DeviceName,host),
           process_name=lower(coalesce(process_name,FileName)),
           process_path=lower(coalesce(process_path,FolderPath)),
           cmd=coalesce(process_command_line,ProcessCommandLine),
           event_type=case(
             process_name IN ("powershell.exe","pwsh.exe") AND like(lower(cmd),"%add-mppreference%") AND like(lower(cmd),"%-exclusionprocess%"),"exclusion",
             like(process_path,"%\\appdata\\local\\temp\\%") AND like(process_name,"%.exe"),"temp_exec",
             true(),"other")
    | where event_type!="other"
    | sort 0 device _time
    | streamstats current=f last(eval(if(event_type="exclusion",_time,null()))) as exclusion_time by device
    | where event_type="temp_exec" AND isnotnull(exclusion_time) AND _time<=exclusion_time+1800
    | table _time exclusion_time device process_name process_path cmd
    | sort - exclusion_time
    What to look for

    A result that helps separate normal endpoint activity from campaign-consistent behavior.

    Technical details
    Tested signal

    Protection change immediately preceding staged executable execution.

    Assumptions
    • Required endpoint telemetry is available and expected software populations can be identified.
    Data requirements and relevant fields
    process

    Endpoint process telemetry.

    • Timestamp
    • DeviceId
    • DeviceName
    • FileName
    • FolderPath
    • ProcessId
    • ProcessUniqueId
    • ProcessCommandLine
    • AccountUpn
    • SHA1
    • SHA256
    • InitiatingProcessFileName
    • InitiatingProcessFolderPath
    • InitiatingProcessCommandLine
    • InitiatingProcessUniqueId
    KQL schema

    Validate Defender table availability and retention.

    SPL schema

    Replace placeholders and map fields locally.

    Limitations
    • Source-specific filenames, domains, and ports are supporting pivots rather than universal requirements.

    KQL uses Microsoft Defender XDR endpoint telemetry. SPL is a normalized scaffold requiring local field mapping.

  5. Step 5Pivot

    Hunt dual Run and RunOnce persistence

    Finding

    Devices with Node JS/ProgramData startup persistence expose the campaign's durable recovery design.

    RunOnce recreation is particularly useful during cleanup validation.

    View query
    Q-05Pivot

    Hunt dual Run and RunOnce persistence

    What this checks

    Find startup values pointing into the user-space Nodejs directory or ProgramData and rank devices showing both mechanisms.

    KQL
    DeviceRegistryEvents
    | where Timestamp >= ago(30d)
    | where RegistryKey has @"\Software\Microsoft\Windows\CurrentVersion\Run"
        or RegistryKey has @"\Software\Microsoft\Windows\CurrentVersion\RunOnce"
    | where RegistryValueData has_any (@"\AppData\Local\Nodejs\",@"\ProgramData\")
    | extend PersistenceKind=case(
        RegistryKey endswith @"\RunOnce","RunOnce",
        RegistryKey endswith @"\Run","Run","Other")
    | summarize FirstSeen=min(Timestamp),LastSeen=max(Timestamp),
                Kinds=make_set(PersistenceKind,5),Values=make_set(RegistryValueData,50),
                ValueNames=make_set(RegistryValueName,50),
                Initiators=make_set(InitiatingProcessFileName,20)
      by DeviceId,DeviceName
    | extend KindCount=array_length(Kinds)
    | order by KindCount desc,LastSeen desc
    SPL
    index=<endpoint_registry_index> sourcetype=<registry_events_sourcetype> earliest=-30d
    | eval device=coalesce(device,DeviceName,host),
           registry_key=coalesce(registry_key,RegistryKey),
           value_data=coalesce(value_data,RegistryValueData),
           value_name=coalesce(value_name,RegistryValueName),
           persistence_kind=case(
             like(lower(registry_key),"%\\runonce"),"RunOnce",
             like(lower(registry_key),"%\\run"),"Run",
             true(),"Other")
    | where (like(lower(registry_key),"%\\currentversion\\run") OR like(lower(registry_key),"%\\currentversion\\runonce"))
        AND (like(lower(value_data),"%\\appdata\\local\\nodejs\\%") OR like(lower(value_data),"%\\programdata\\%"))
    | stats min(_time) as first_seen max(_time) as last_seen dc(persistence_kind) as kind_count
            values(persistence_kind) as kinds values(value_data) as values values(value_name) as value_names
      by device
    | convert ctime(first_seen) ctime(last_seen)
    | sort - kind_count - last_seen
    What to look for

    A result that helps separate normal endpoint activity from campaign-consistent behavior.

    Technical details
    Tested signal

    Run/RunOnce persistence toward source-reported locations.

    Assumptions
    • Required endpoint telemetry is available and expected software populations can be identified.
    Data requirements and relevant fields
    registry

    Endpoint registry telemetry.

    • Timestamp
    • DeviceId
    • DeviceName
    • ActionType
    • RegistryKey
    • RegistryValueName
    • RegistryValueData
    • PreviousRegistryValueData
    • InitiatingProcessFileName
    • InitiatingProcessFolderPath
    • InitiatingProcessCommandLine
    • InitiatingProcessAccountUpn
    • InitiatingProcessUniqueId
    KQL schema

    Validate Defender table availability and retention.

    SPL schema

    Replace placeholders and map fields locally.

    Limitations
    • Source-specific filenames, domains, and ports are supporting pivots rather than universal requirements.

    KQL uses Microsoft Defender XDR endpoint telemetry. SPL is a normalized scaffold requiring local field mapping.

  6. Step 6Pivot

    Hunt non-standard beaconing from user-space payloads

    Finding

    Unexpected high-port communication from user-space processes identifies active implant control candidates.

    Ports can rotate, so process/path context remains important.

    View query
    Q-06Pivot

    Hunt non-standard beaconing from user-space payloads

    What this checks

    Search the source-reported C2 ports from Node JS, Temp, Nodejs, or ProgramData process locations.

    KQL
    DeviceNetworkEvents
    | where Timestamp >= ago(30d)
    | where RemotePort in (8443,8445,8453,5555,56001,56002,56003)
    | where InitiatingProcessFileName =~ "node.exe"
        or InitiatingProcessFolderPath has @"\AppData\Local\Temp\"
        or InitiatingProcessFolderPath has @"\AppData\Local\Nodejs\"
        or InitiatingProcessFolderPath has @"\ProgramData\"
    | summarize FirstSeen=min(Timestamp),LastSeen=max(Timestamp),Connections=count(),
                Destinations=make_set(strcat(RemoteUrl,"|",RemoteIP,"|",tostring(RemotePort)),100),
                Processes=make_set(InitiatingProcessFileName,20),
                Paths=make_set(InitiatingProcessFolderPath,30)
      by DeviceId,DeviceName,InitiatingProcessAccountUpn
    | order by Connections desc
    SPL
    index=<endpoint_network_index> sourcetype=<endpoint_network_events_sourcetype> earliest=-30d
    | eval device=coalesce(device,DeviceName,host),
           user=lower(coalesce(user,InitiatingProcessAccountUpn)),
           remote_port=coalesce(remote_port,RemotePort,dest_port),
           process_name=lower(coalesce(process_name,InitiatingProcessFileName)),
           process_path=lower(coalesce(process_path,InitiatingProcessFolderPath))
    | where remote_port IN (8443,8445,8453,5555,56001,56002,56003)
        AND (process_name="node.exe" OR like(process_path,"%\\appdata\\local\\%") OR like(process_path,"%\\programdata\\%"))
    | stats min(_time) as first_seen max(_time) as last_seen count as connections
            values(RemoteUrl) as remote_urls values(RemoteIP) as remote_ips values(remote_port) as ports
      by device user
    | convert ctime(first_seen) ctime(last_seen)
    | sort - connections
    What to look for

    A result that helps separate normal endpoint activity from campaign-consistent behavior.

    Technical details
    Tested signal

    Unexpected high-port communication from suspicious user-space processes.

    Assumptions
    • Required endpoint telemetry is available and expected software populations can be identified.
    Data requirements and relevant fields
    network

    Endpoint network telemetry.

    • Timestamp
    • DeviceId
    • DeviceName
    • RemoteUrl
    • RemoteIP
    • RemotePort
    • Protocol
    • InitiatingProcessFileName
    • InitiatingProcessFolderPath
    • InitiatingProcessCommandLine
    • InitiatingProcessAccountUpn
    • InitiatingProcessUniqueId
    KQL schema

    Validate Defender table availability and retention.

    SPL schema

    Replace placeholders and map fields locally.

    Limitations
    • Source-specific filenames, domains, and ports are supporting pivots rather than universal requirements.

    KQL uses Microsoft Defender XDR endpoint telemetry. SPL is a normalized scaffold requiring local field mapping.

  7. Step 7Pivot

    Hunt late-stage automation on Node JS hosts

    Finding

    Headless browser flags or shutdown commands identify selected late-stage activity on already suspicious hosts.

    These behaviors are supporting evidence and are not expected on every victim.

    View query
    Q-07Pivot

    Hunt late-stage automation on Node JS hosts

    What this checks

    Find headless browser flags or immediate shutdown commands on hosts that already showed user-space Node JS.

    KQL
    let suspiciousHosts =
        DeviceProcessEvents
        | where Timestamp >= ago(30d)
        | where FileName =~ "node.exe" and FolderPath has @"\AppData\Local\Nodejs\"
        | distinct DeviceId;
    DeviceProcessEvents
    | where Timestamp >= ago(30d)
    | where DeviceId in (suspiciousHosts)
    | where ProcessCommandLine has_any ("--headless","--no-sandbox","shutdown -s -t 0")
    | project Timestamp,DeviceName,AccountUpn,FileName,FolderPath,ProcessCommandLine,
              InitiatingProcessFileName,InitiatingProcessCommandLine
    | order by Timestamp desc
    SPL
    index=<endpoint_process_index> sourcetype=<process_events_sourcetype> earliest=-30d
    | eval device=coalesce(device,DeviceName,host),
           process_name=lower(coalesce(process_name,FileName)),
           process_path=lower(coalesce(process_path,FolderPath)),
           cmd=coalesce(process_command_line,ProcessCommandLine),
           node_seen=if(process_name="node.exe" AND like(process_path,"%\\appdata\\local\\nodejs\\%"),1,0)
    | eventstats max(node_seen) as has_user_node by device
    | where has_user_node=1 AND (
        like(lower(cmd),"%--headless%") OR like(lower(cmd),"%--no-sandbox%") OR like(lower(cmd),"%shutdown -s -t 0%"))
    | table _time device process_name process_path cmd InitiatingProcessFileName InitiatingProcessCommandLine
    | sort - _time
    What to look for

    A result that helps separate normal endpoint activity from campaign-consistent behavior.

    Technical details
    Tested signal

    Headless/no-sandbox browser automation or forced shutdown on affected hosts.

    Assumptions
    • Required endpoint telemetry is available and expected software populations can be identified.
    Data requirements and relevant fields
    process

    Endpoint process telemetry.

    • Timestamp
    • DeviceId
    • DeviceName
    • FileName
    • FolderPath
    • ProcessId
    • ProcessUniqueId
    • ProcessCommandLine
    • AccountUpn
    • SHA1
    • SHA256
    • InitiatingProcessFileName
    • InitiatingProcessFolderPath
    • InitiatingProcessCommandLine
    • InitiatingProcessUniqueId
    KQL schema

    Validate Defender table availability and retention.

    SPL schema

    Replace placeholders and map fields locally.

    Limitations
    • Source-specific filenames, domains, and ports are supporting pivots rather than universal requirements.

    KQL uses Microsoft Defender XDR endpoint telemetry. SPL is a normalized scaffold requiring local field mapping.

Blast radius

Wider-compromise pivots

  • Search guest-facing endpoints first in hospitality environments.
  • Inventory user-space language runtimes on non-development endpoints.
  • Review PowerShell-launched compiler activity on ordinary workstations.
  • Search temporary installer helpers after Defender exclusions.
  • Track RunOnce recreation across sign-in/reboot cycles.
  • Correlate affected endpoints with exposed email recipients.

Evidence threshold

What would increase confidence

  • Node JS appears under a user profile on a non-development device.
  • Node JS executes unexplained JavaScript.
  • The device has no historical user-space Node JS baseline.
  • A fake image shortcut or suspicious PowerShell precedes the runtime.
  • Defender exclusions are added for Temp executables.
  • Run/RunOnce points to Node JS or ProgramData.
  • Persisted processes connect to unusual destinations or high ports.
  • No approved workflow explains the sequence.

Conclusion

Result and next action

The hunt converts a current hospitality campaign into durable endpoint surfaces centered on user-space runtime execution, behavioral PowerShell staging, protection changes, dual registry persistence, and process-aware network activity.

  • Isolate confirmed endpoints and remove both persistence mechanisms.
  • Revert unauthorized Defender exclusions.
  • Remove malicious JavaScript and payloads after evidence collection.
  • Search all endpoints for the same behavior and source-scoped IOCs.
  • Tune developer/build endpoints separately.
  • Monitor remediated hosts for RunOnce recreation and renewed C2.

The hunt starts by learning where user-space Node JS belongs. Only then does it expand into shortcut execution, PowerShell staging, protection changes, dual persistence, high-port communication, and selected late-stage actions.

This follows Microsoft's main defensive lesson: the actor changed visible artifacts while keeping a recognizable endpoint sequence.

Context

ATT&CK and limits

Behavior mapping

MITRE ATT&CK

This mapping describes the valid-account behavior examined by the Hunt. It does not prove token theft, attribution, or technique-wide coverage.

Review boundary

Sources and limits

The conclusion stays bounded to the stated scope and available logs.