SOCLIFE
SOCLIFE

Evidence-led security analysis

Published knowledge

Search SOC//LIFE

CasesCASE-008

SOC investigation

Photo ZIP Lure Leads to Persistent Node JS Access

A hospitality phishing lure delivered a fake image shortcut that launched PowerShell, staged a user-space Node JS implant, altered Defender exclusions, and established dual registry persistence.

Based on publicly reported attack activity. User identities and workstation names have been anonymized.

Initial alert
Suspicious Node JS execution with registry persistence
Severity
High

Case story

What happened

The endpoint shows unusual user-space Node JS and startup persistence after a browser-delivered photo archive.

  1. Trusted-service redirects deliver the lure

    A hospitality-themed notification routes the user toward a photo-themed archive.

  2. Fake image shortcut starts PowerShell

    The user opens a PNG-masquerading shortcut and the script chain begins.

  3. User-space Node JS runs JavaScript

    A legitimate runtime under the user profile executes the implant.

  4. Defender exclusion precedes payload launch

    A temporary executable is excluded from inspection and then runs.

  5. Run and RunOnce preserve two paths

    Separate startup entries persist the Node JS component and ProgramData payload.

  6. C2 and late-stage actions appear

    Persisted components communicate externally and selected hosts show automation or shutdown behavior.

Investigation

What was checked

Follow how the analyst tested and revised explanations. This is discovery order, not event chronology.
  1. Recover the lure

    The delivery window and message are recovered.

    Next pivot

    Search the device for the downloaded shortcut.

    View query
    Q-01

    Recover the phishing redirect chain

    What this checks

    Find source-reported Calendly/Google/photo redirect activity for the affected mailbox.

    KQL
    let target_user="denis@example.com";
    EmailUrlInfo
    | where Timestamp >= ago(14d)
    | where Url has_any ("calendly.com/url?q=","share.google/","photo-")
    | join kind=inner (
        EmailEvents
        | where Timestamp >= ago(14d)
        | where RecipientEmailAddress =~ target_user
        | project NetworkMessageId, MailTime=Timestamp, SenderFromAddress, SenderMailFromDomain,
                  SenderDisplayName, RecipientEmailAddress, Subject, DeliveryAction,
                  DeliveryLocation, ThreatTypes, AuthenticationDetails
    ) on NetworkMessageId
    | where Url contains "calendly.com/url?q="
        or Url contains "share.google/"
        or (Url contains "photo-" and Url contains ".cfd")
    | project MailTime,NetworkMessageId,SenderFromAddress,SenderMailFromDomain,SenderDisplayName,
              RecipientEmailAddress,Subject,Url,DeliveryAction,AuthenticationDetails
    | order by MailTime asc
    SPL
    (
      index=<email_index> sourcetype=<email_events_sourcetype> earliest=-14d
    )
    OR
    (
      index=<email_url_index> sourcetype=<email_url_info_sourcetype> earliest=-14d
    )
    | eval recipient=lower(coalesce(recipient,RecipientEmailAddress)),
           message_id=coalesce(message_id,NetworkMessageId),
           url=coalesce(url,Url),
           sender=lower(coalesce(sender,SenderFromAddress))
    | where recipient="denis@example.com"
    | eventstats values(sender) as senders values(Subject) as subjects values(AuthenticationDetails) as auth by message_id recipient
    | where isnotnull(url) AND (
        like(lower(url),"%calendly.com/url?q=%")
        OR like(lower(url),"%share.google/%")
        OR (like(lower(url),"%photo-%") AND like(lower(url),"%.cfd%"))
    )
    | table _time recipient message_id senders subjects url auth
    | sort 0 _time
    What to look for

    A result that materially strengthens or weakens the current compromise hypothesis.

    Technical details
    Tested signal

    Trusted-service delivery followed by a photo-themed landing page.

    Assumptions
    • Required Microsoft Defender telemetry is available for the investigation window.
    Data requirements and relevant fields
    email

    Defender for Office 365 email and URL telemetry.

    • Timestamp
    • NetworkMessageId
    • SenderFromAddress
    • SenderMailFromDomain
    • SenderDisplayName
    • RecipientEmailAddress
    • Subject
    • DeliveryAction
    • DeliveryLocation
    • ThreatTypes
    • AuthenticationDetails
    • Url
    • UrlDomain
    KQL schema

    Validate Defender table availability and local retention.

    SPL schema

    Replace index/sourcetype placeholders and normalize local fields.

    Limitations
    • Source-specific filenames, domains, and ports can rotate and must remain supporting evidence.

    KQL uses Microsoft Defender XDR telemetry. SPL is a normalized raw-event scaffold and requires local field mapping.

    More reasoning

    Observation

    A hospitality notification preceded the endpoint activity.

    Working explanation

    Trusted-service delivery may have hidden the final payload destination.

    What was checked

    Correlate the mailbox with Calendly, Google redirect, and photo-themed URL pivots.

    Interpretation

    Trusted infrastructure is not itself malicious.

    Supporting evidence
  2. Trace shortcut execution

    User execution is tied to script staging.

    Next pivot

    Find Node JS or Wave 2 compilation.

    View query
    Q-02

    Trace fake image shortcut into PowerShell

    What this checks

    Identify IMG/PHOTO PNG-masquerading LNK execution and immediate script staging.

    KQL
    let target_device="<DEVICE_NAME>";
    DeviceProcessEvents
    | where Timestamp >= ago(14d)
    | where DeviceName =~ target_device
    | where
        (ProcessCommandLine has ".png.lnk" and ProcessCommandLine has_any ("IMG-","PHOTO-"))
        or
        (FileName in~ ("powershell.exe","pwsh.exe") and
         ProcessCommandLine has_any ("Invoke-WebRequest"," iwr ","iwr ") and
         ProcessCommandLine has ".ps1")
    | project Timestamp,DeviceName,AccountUpn,FileName,FolderPath,ProcessCommandLine,SHA1,SHA256,
              InitiatingProcessFileName,InitiatingProcessCommandLine
    | order by Timestamp asc
    SPL
    index=<endpoint_process_index> sourcetype=<process_events_sourcetype> earliest=-14d
    | eval device=coalesce(device,DeviceName,host),
           process_name=lower(coalesce(process_name,FileName)),
           cmd=coalesce(process_command_line,ProcessCommandLine)
    | where device="<DEVICE_NAME>" AND (
        (like(lower(cmd),"%.png.lnk%") AND (like(cmd,"%IMG-%") OR like(cmd,"%PHOTO-%")))
        OR
        (process_name IN ("powershell.exe","pwsh.exe") AND
         (like(lower(cmd),"%invoke-webrequest%") OR like(lower(cmd),"%iwr %")) AND
         like(lower(cmd),"%.ps1%"))
    )
    | table _time device process_name cmd InitiatingProcessFileName InitiatingProcessCommandLine SHA1 SHA256
    | sort 0 _time
    What to look for

    A result that materially strengthens or weakens the current compromise hypothesis.

    Technical details
    Tested signal

    Fake image shortcut followed by PowerShell retrieval.

    Assumptions
    • Required Microsoft Defender telemetry is available for the investigation window.
    Data requirements and relevant fields
    process

    Endpoint process creation telemetry.

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

    Validate Defender table availability and local retention.

    SPL schema

    Replace index/sourcetype placeholders and normalize local fields.

    Limitations
    • Source-specific filenames, domains, and ports can rotate and must remain supporting evidence.

    KQL uses Microsoft Defender XDR telemetry. SPL is a normalized raw-event scaffold and requires local field mapping.

    More reasoning

    Observation

    The archive contains a fake image shortcut.

    Working explanation

    Opening it launched the downloader.

    What was checked

    Search IMG/PHOTO PNG-LNK and PowerShell retrieval behavior.

    Interpretation

    Behavior is more durable than the exact filename.

    Supporting evidence
  3. Confirm Node JS implant

    User-space JavaScript execution materially raises confidence.

    Next pivot

    Check Defender exclusions and temporary payload execution.

    View query
    Q-03

    Confirm user-space Node JS implant execution

    What this checks

    Find Node JS under the user profile executing a JavaScript payload.

    KQL
    let target_device="<DEVICE_NAME>";
    DeviceProcessEvents
    | where Timestamp >= ago(14d)
    | where DeviceName =~ target_device
    | where FileName =~ "node.exe"
    | where FolderPath has @"\AppData\Local\Nodejs\"
    | where ProcessCommandLine has ".js"
    | project Timestamp,DeviceName,AccountUpn,FileName,FolderPath,ProcessCommandLine,ProcessUniqueId,
              SHA1,SHA256,InitiatingProcessFileName,InitiatingProcessCommandLine
    | order by Timestamp asc
    SPL
    index=<endpoint_process_index> sourcetype=<process_events_sourcetype> earliest=-14d
    | 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)
    | where device="<DEVICE_NAME>" AND process_name="node.exe"
        AND like(process_path,"%\\appdata\\local\\nodejs\\%")
        AND like(lower(cmd),"%.js%")
    | table _time device process_name process_path cmd SHA1 SHA256 InitiatingProcessFileName InitiatingProcessCommandLine
    | sort 0 _time
    What to look for

    A result that materially strengthens or weakens the current compromise hypothesis.

    Technical details
    Tested signal

    User-space Node JS with JavaScript argument on a non-development endpoint.

    Assumptions
    • Required Microsoft Defender telemetry is available for the investigation window.
    Data requirements and relevant fields
    process

    Endpoint process creation telemetry.

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

    Validate Defender table availability and local retention.

    SPL schema

    Replace index/sourcetype placeholders and normalize local fields.

    Limitations
    • Source-specific filenames, domains, and ports can rotate and must remain supporting evidence.

    KQL uses Microsoft Defender XDR telemetry. SPL is a normalized raw-event scaffold and requires local field mapping.

    More reasoning

    Observation

    Node JS appears under a user-writable path.

    Working explanation

    The runtime is executing the campaign's JavaScript implant.

    What was checked

    Review path, JavaScript argument, parent, hashes, and user context.

    Interpretation

    Node JS itself is legitimate software.

    Supporting evidence
  4. Measure defense evasion

    A short exclusion-to-execution sequence supports attacker-controlled staging.

    Next pivot

    Review startup persistence.

    View query
    Q-04

    Correlate Defender exclusion with Temp execution

    What this checks

    Find security exclusions followed by temporary executable launch.

    KQL
    let target_device="<DEVICE_NAME>";
    let exclusions =
        DeviceProcessEvents
        | where Timestamp >= ago(14d)
        | where DeviceName =~ target_device
        | where FileName in~ ("powershell.exe","pwsh.exe")
        | where ProcessCommandLine has "Add-MpPreference" and ProcessCommandLine has "-ExclusionProcess"
        | project DeviceId,DeviceName,ExclusionTime=Timestamp,ExclusionCmd=ProcessCommandLine;
    let tempExecs =
        DeviceProcessEvents
        | where Timestamp >= ago(14d)
        | where DeviceName =~ target_device
        | where FolderPath has @"\AppData\Local\Temp\" and FileName endswith ".exe"
        | project DeviceId,TempExecTime=Timestamp,TempFile=FileName,TempPath=FolderPath,TempCmd=ProcessCommandLine;
    exclusions
    | join kind=inner tempExecs on DeviceId
    | where TempExecTime between (ExclusionTime .. ExclusionTime + 30m)
    | project DeviceName,ExclusionTime,ExclusionCmd,TempExecTime,TempFile,TempPath,TempCmd
    | order by ExclusionTime asc
    SPL
    index=<endpoint_process_index> sourcetype=<process_events_sourcetype> earliest=-14d
    | 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 device="<DEVICE_NAME>" AND 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 0 exclusion_time
    What to look for

    A result that materially strengthens or weakens the current compromise hypothesis.

    Technical details
    Tested signal

    Defender process exclusion followed by user-temp executable execution.

    Assumptions
    • Required Microsoft Defender telemetry is available for the investigation window.
    Data requirements and relevant fields
    process

    Endpoint process creation telemetry.

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

    Validate Defender table availability and local retention.

    SPL schema

    Replace index/sourcetype placeholders and normalize local fields.

    Limitations
    • Source-specific filenames, domains, and ports can rotate and must remain supporting evidence.

    KQL uses Microsoft Defender XDR telemetry. SPL is a normalized raw-event scaffold and requires local field mapping.

    More reasoning

    Observation

    The implant changes Defender preferences before payload launch.

    Working explanation

    The exclusion was created to reduce inspection of a staged executable.

    What was checked

    Correlate ExclusionProcess changes with Temp executable launches.

    Interpretation

    Managed software changes require explicit owner/change context.

    Supporting evidence
  5. Validate dual persistence

    Persistence scope and cleanup requirements are established.

    Next pivot

    Scope active C2 from the persisted components.

    View query
    Q-05

    Validate dual Run and RunOnce persistence

    What this checks

    Recover startup values pointing to Node JS or ProgramData.

    KQL
    let target_device="<DEVICE_NAME>";
    DeviceRegistryEvents
    | where Timestamp >= ago(14d)
    | where DeviceName =~ target_device
    | 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")
    | project Timestamp,DeviceName,ActionType,PersistenceKind,RegistryKey,RegistryValueName,
              RegistryValueData,PreviousRegistryValueData,InitiatingProcessFileName,
              InitiatingProcessCommandLine,InitiatingProcessAccountUpn
    | order by Timestamp asc
    SPL
    index=<endpoint_registry_index> sourcetype=<registry_events_sourcetype> earliest=-14d
    | eval device=coalesce(device,DeviceName,host),
           registry_key=coalesce(registry_key,RegistryKey),
           value_data=coalesce(value_data,RegistryValueData),
           persistence_kind=case(
             like(lower(registry_key),"%\\runonce"),"RunOnce",
             like(lower(registry_key),"%\\run"),"Run",
             true(),"Other")
    | where device="<DEVICE_NAME>"
      AND (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\\%"))
    | table _time device persistence_kind registry_key RegistryValueName value_data InitiatingProcessFileName InitiatingProcessCommandLine
    | sort 0 _time
    What to look for

    A result that materially strengthens or weakens the current compromise hypothesis.

    Technical details
    Tested signal

    Run/RunOnce persistence targeting the source-reported locations.

    Assumptions
    • Required Microsoft Defender telemetry is available for the investigation window.
    Data requirements and relevant fields
    registry

    Endpoint registry creation/modification telemetry.

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

    Validate Defender table availability and local retention.

    SPL schema

    Replace index/sourcetype placeholders and normalize local fields.

    Limitations
    • Source-specific filenames, domains, and ports can rotate and must remain supporting evidence.

    KQL uses Microsoft Defender XDR telemetry. SPL is a normalized raw-event scaffold and requires local field mapping.

    More reasoning

    Observation

    The campaign uses separate Run and RunOnce paths.

    Working explanation

    One path may survive partial remediation.

    What was checked

    Review startup values pointing to Node JS or ProgramData and check for RunOnce recreation.

    Interpretation

    Run/RunOnce are common; path and preceding behavior matter.

    Supporting evidence
  6. Scope active control

    Destination infrastructure and containment urgency are defined.

    Next pivot

    Contain the endpoint and hunt the behavior across the estate.

    View query
    Q-06

    Scope non-standard C2 and late-stage behavior

    What this checks

    Find source-reported high-port connections from Node JS/user-space payloads and late-stage browser/shutdown activity.

    KQL
    let target_device="<DEVICE_NAME>";
    DeviceNetworkEvents
    | where Timestamp >= ago(14d)
    | where DeviceName =~ target_device
    | 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\"
    | project Timestamp,DeviceName,RemoteUrl,RemoteIP,RemotePort,Protocol,
              InitiatingProcessFileName,InitiatingProcessFolderPath,InitiatingProcessCommandLine
    | order by Timestamp asc
    SPL
    index=<endpoint_network_index> sourcetype=<endpoint_network_events_sourcetype> earliest=-14d
    | eval device=coalesce(device,DeviceName,host),
           remote_port=coalesce(remote_port,RemotePort,dest_port),
           process_name=lower(coalesce(process_name,InitiatingProcessFileName)),
           process_path=lower(coalesce(process_path,InitiatingProcessFolderPath))
    | where device="<DEVICE_NAME>" AND 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\\%"))
    | table _time device process_name process_path RemoteIP RemoteUrl remote_port
    | sort 0 _time
    What to look for

    A result that materially strengthens or weakens the current compromise hypothesis.

    Technical details
    Tested signal

    Active C2-like network activity from persisted components.

    Assumptions
    • Required Microsoft Defender telemetry is available for the investigation window.
    Data requirements and relevant fields
    network

    Endpoint network telemetry.

    • Timestamp
    • DeviceId
    • DeviceName
    • RemoteUrl
    • RemoteIP
    • RemotePort
    • Protocol
    • InitiatingProcessFileName
    • InitiatingProcessFolderPath
    • InitiatingProcessCommandLine
    • InitiatingProcessAccountUpn
    • InitiatingProcessUniqueId
    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 local retention.

    SPL schema

    Replace index/sourcetype placeholders and normalize local fields.

    Limitations
    • Source-specific filenames, domains, and ports can rotate and must remain supporting evidence.

    KQL uses Microsoft Defender XDR telemetry. SPL is a normalized raw-event scaffold and requires local field mapping.

    More reasoning

    Observation

    Affected systems can beacon on non-standard ports.

    Working explanation

    The persistent implant may be actively controlled.

    What was checked

    Search the source-reported ports from Node JS/user-space payloads.

    Interpretation

    Ports and domains can rotate, so process/path context remains primary.

    Supporting evidence

Response

Actions to take

Contain affected systems, preserve evidence, and scope the same behavior elsewhere.
  • Isolate the endpoint and preserve process, registry, file, network, and browser-download evidence.
  • Remove both persistence mechanisms and their payload targets after evidence collection.
  • Remove attacker-deployed user-space Node JS and associated JavaScript payloads that are not business-required.
  • Revert unauthorized Defender exclusions and verify tamper protection.
  • Block confirmed malicious campaign infrastructure without blanket-blocking legitimate Calendly, Google, Cloudflare, or Node JS services.
  • Search all endpoints for the same shortcut, PowerShell, Node JS, registry, and network behaviors.
  • Monitor remediated hosts for RunOnce recreation, headless browser activity, or renewed persistence.

Conclusion

What was concluded

Microsoft observed the campaign across hospitality organizations in Europe and Asia. The actor changed lure details and PowerShell obfuscation while keeping the endpoint sequence comparatively stable.

The Case therefore treats filenames, domains, and ports as supporting evidence. The investigation is anchored on the chain a SOC can correlate across email, process, registry, and network telemetry.

Technical detail

Technical evidence

Stable evidence anchors preserve the fields behind the investigation story.
E-01

E-01Email artifact

Microsoft observed trusted-service phishing and redirect abuse targeting hospitality users in Europe and Asia.

Sector
Hospitality / hotels
Delivery
Trusted-service redirect chain
Lure
Photo/guest complaint workflow
Referenced by
E-02

E-02Process execution

A browser-downloaded archive contains a fake image shortcut that launches an obfuscated PowerShell stage.

Archive pattern
photo-<digits>.zip
Shortcut pattern
IMG-/PHOTO-<digits>.png.lnk
Execution
PowerShell downloader
Referenced by
E-03

E-03Process execution

The campaign executes a legitimate Node JS runtime from a user-writable profile path to run randomized JavaScript payloads.

Runtime
Node JS
Location
User profile Local Nodejs directory
Payload
Random JavaScript file
Referenced by
E-04

E-04Process execution

PowerShell adds Defender process exclusions for temporary executables shortly before those payloads run.

Security change
Defender process exclusion
Target
Temporary executable
Follow on
Payload execution
Referenced by
E-05

E-05Registry event

Microsoft observed dual persistence using Run for the Node JS component and RunOnce for a ProgramData payload, including RunOnce refresh behavior.

Primary persistence
Run → Node JS
Secondary persistence
RunOnce → ProgramData
Notable behavior
RunOnce refresh loop
Referenced by
E-06

E-06Network event

Later-stage systems beaconed on non-standard ports and selected devices showed headless browser activity or forced shutdown.

Ports
8443, 8445, 8453, 5555, 56001-56003
Late stage
Headless browser / shutdown
Attribution
No known actor attribution by Microsoft
Referenced by

Detection engineering

Would your SOC catch this behavior?

See the detection built for this investigation.
View detection

Behavior context

ATT&CK and sources

Behavior mapping

MITRE ATT&CK

These mappings describe the behavior examined here. They do not establish attribution.

Review boundary

Sources and limits

Last reviewed
External sources
5

This case documents the available evidence and analytical limits; control effectiveness is environment-specific.