SOCLIFE
SOCLIFE

Evidence-led security analysis

Published knowledge

Search SOC//LIFE

DetectionsDET-010

Behavior-based detection engineering

Rare Chunked Curl Upload on macOS

Detects first-seen curl PUT uploads on macOS using binary transfer and recurring chunk parameters, then confirms matching same-process network activity.

Behavior

What it detects

A macOS device with no recent matching curl-upload history performs binary HTTP PUT activity with upload session and chunk parameters, confirmed by network telemetry from the same process.

Engineering decision

Why this detection

curl is a standard macOS utility, and HTTP PUT is not inherently suspicious. The detection therefore requires a much narrower sequence: a current Mac with no recent matching history performs binary PUT with all recurring chunk parameters, and endpoint network telemetry confirms the same stable curl process.

The domain is deliberately not part of the primary requirement. That choice mirrors the research finding that MacSync infrastructure rotates faster than its request and execution behavior.

Signal chain

Detection logic

  1. Restrict the candidate population to current macOS endpoints.
  2. Collect curl processes using HTTP PUT, binary upload, and all three recurring chunk parameters.
  3. Build a thirty-day per-device baseline for the same high-specificity curl upload behavior.
  4. Keep devices without prior matching behavior.
  5. Require matching network telemetry from the same stable curl process identity.
  6. Require network activity within two minutes of the upload process event.
  7. Preserve user, device, command line, destination, parent, and hash context for triage.

Primary analytic

Query

KQL and SPL express the same analytical intent using source-specific schemas.
Q-01Detection logic

Rare chunked curl upload on macOS

What this checks

Detect a macOS curl process performing chunked HTTP PUT upload behavior with the recurring MacSync request parameters, then confirm matching network activity from the same stable process identity.

KQL
let current_window = 1d;
let baseline_window = 30d;
let mac_devices =
    DeviceInfo
    | where Timestamp >= ago(7d)
    | summarize arg_max(Timestamp, DeviceName, OSPlatform) by DeviceId
    | where OSPlatform startswith "macOS"
    | project DeviceId, MacDeviceName=DeviceName, OSPlatform;
let historical_uploads =
    DeviceProcessEvents
    | where Timestamp between (ago(baseline_window) .. ago(current_window))
    | where FileName =~ "curl"
    | where ProcessCommandLine has_all ("-X PUT", "--data-binary", "upload_id=", "chunk_index=", "total_chunks=")
    | summarize by DeviceId;
let upload_processes =
    DeviceProcessEvents
    | where Timestamp >= ago(current_window)
    | where FileName =~ "curl"
    | where ProcessCommandLine has_all ("-X PUT", "--data-binary", "upload_id=", "chunk_index=", "total_chunks=")
    | project
        ProcessTime=Timestamp,
        DeviceId,
        DeviceName,
        AccountUpn,
        ProcessUniqueId,
        ProcessCommandLine,
        FolderPath,
        SHA1,
        SHA256,
        InitiatingProcessFileName,
        InitiatingProcessFolderPath,
        InitiatingProcessCommandLine;
let upload_network =
    DeviceNetworkEvents
    | where Timestamp >= ago(current_window)
    | where InitiatingProcessFileName =~ "curl"
    | where RemoteUrl has_all ("upload_id=", "chunk_index=", "total_chunks=")
    | project
        NetworkTime=Timestamp,
        DeviceId,
        InitiatingProcessUniqueId,
        RemoteUrl,
        RemoteIP,
        RemotePort,
        Protocol,
        NetCommandLine=InitiatingProcessCommandLine;
upload_processes
| join kind=inner mac_devices on DeviceId
| join kind=leftanti historical_uploads on DeviceId
| join kind=inner upload_network on $left.DeviceId == $right.DeviceId and $left.ProcessUniqueId == $right.InitiatingProcessUniqueId
| where NetworkTime between (ProcessTime - 2m .. ProcessTime + 2m)
| project
    ProcessTime,
    NetworkTime,
    DeviceName,
    OSPlatform,
    AccountUpn,
    ProcessCommandLine,
    RemoteUrl,
    RemoteIP,
    RemotePort,
    Protocol,
    InitiatingProcessFileName,
    InitiatingProcessFolderPath,
    InitiatingProcessCommandLine,
    SHA1,
    SHA256
| order by ProcessTime desc
SPL
(
    index=<endpoint_process_index> sourcetype=<process_events_sourcetype> earliest=-30d
)
OR
(
    index=<endpoint_network_index> sourcetype=<endpoint_network_events_sourcetype> earliest=-30d
)
OR
(
    index=<device_inventory_index> sourcetype=<device_info_sourcetype> earliest=-7d
)
| eval
    device=coalesce(device,DeviceName,host),
    device_id=coalesce(device_id,DeviceId),
    os=coalesce(os,OSPlatform),
    process_name=lower(coalesce(process_name,FileName,InitiatingProcessFileName)),
    process_uid=coalesce(process_uid,ProcessUniqueId,InitiatingProcessUniqueId),
    cmd=coalesce(process_command_line,ProcessCommandLine,InitiatingProcessCommandLine),
    remote_url=coalesce(remote_url,RemoteUrl),
    event_type=case(
        isnotnull(OSPlatform),"device_info",
        process_name="curl"
            AND like(cmd,"%-X PUT%")
            AND like(cmd,"%--data-binary%")
            AND like(cmd,"%upload_id=%")
            AND like(cmd,"%chunk_index=%")
            AND like(cmd,"%total_chunks=%"),"upload_process",
        isnotnull(remote_url)
            AND process_name="curl"
            AND like(remote_url,"%upload_id=%")
            AND like(remote_url,"%chunk_index=%")
            AND like(remote_url,"%total_chunks=%"),"upload_network",
        true(),"other"
    ),
    is_current=if(_time>=relative_time(now(),"-1d"),1,0)
| where event_type!="other"
| eventstats
    latest(eval(if(event_type="device_info",os,null()))) as os_platform
    by device_id
| where like(lower(os_platform),"macos%")
| eventstats
    count(eval(event_type="upload_process" AND is_current=0)) as historical_matching_uploads
    by device_id
| stats
    min(_time) as first_time
    max(_time) as last_time
    max(eval(if(event_type="upload_process" AND is_current=1,1,0))) as has_upload_process
    max(eval(if(event_type="upload_network" AND is_current=1,1,0))) as has_upload_network
    values(eval(if(event_type="upload_process",cmd,null()))) as process_commands
    values(eval(if(event_type="upload_network",remote_url,null()))) as remote_urls
    values(RemoteIP) as remote_ips
    values(RemotePort) as remote_ports
    values(AccountUpn) as users
    values(InitiatingProcessFileName) as parents
    by device_id device process_uid os_platform historical_matching_uploads
| where historical_matching_uploads=0 AND has_upload_process=1 AND has_upload_network=1
| where last_time-first_time<=120
| convert ctime(first_time) ctime(last_time)
| sort - last_time

What to look for

A macOS device with no recent matching curl-upload baseline performs binary PUT activity carrying upload session and chunk parameters, with matching outbound network telemetry.

Technical details

Tested signal

First-seen curl PUT with binary upload and chunk identifiers, joined to same-process network activity.

Assumptions

  • DeviceInfo contains current OSPlatform values.
  • ProcessUniqueId and InitiatingProcessUniqueId are populated for the endpoint.
  • Thirty days is an initial baseline for similar curl upload behavior.

Data requirements and relevant fields

endpoint

Device inventory used to restrict analytics to current macOS endpoints.

  • Timestamp
  • DeviceId
  • DeviceName
  • OSPlatform
process

Endpoint process creation telemetry with stable process identity, path, command line, parent, and user context.

  • Timestamp
  • DeviceId
  • DeviceName
  • FileName
  • FolderPath
  • ProcessId
  • ProcessUniqueId
  • ProcessCommandLine
  • AccountName
  • AccountUpn
  • SHA1
  • SHA256
  • InitiatingProcessFileName
  • InitiatingProcessFolderPath
  • InitiatingProcessCommandLine
  • InitiatingProcessId
  • InitiatingProcessUniqueId
  • InitiatingProcessAccountName
  • InitiatingProcessAccountUpn
network

Endpoint network telemetry with destination URL, port, initiating process, user, and stable process identity.

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

Validate Defender for Endpoint coverage on macOS, table availability, field population, and retention before operational use.

SPL schema

Replace index/sourcetype placeholders and map device, process, file, and network fields to local telemetry.

Limitations

  • Developers, backup tools, or internal automation can legitimately use curl PUT and binary uploads.
  • The exact URI path can evolve; the primary logic requires the more durable upload parameter combination.

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

Analyst workflow

What the analyst should look for

  • Is the device a managed macOS endpoint?
  • Does the user or application legitimately perform scripted curl PUT uploads?
  • Is the curl process launched from an interactive shell, AppleScript, automation agent, or approved backup client?
  • Do the request parameters contain upload session and chunk semantics?
  • Was there earlier curl payload retrieval or decode/unpack activity?
  • Did osascript participate in shell, copy, cleanup, or retrieval behavior?
  • Was sensitive data staged under temporary paths and archived before the upload?
  • Were temporary artifacts removed after the transfer?

Expected result

A macOS device with no recent matching curl-upload baseline performs binary PUT activity carrying upload session and chunk parameters, with matching outbound network telemetry.

Investigation pivots

Drilldowns

Use the candidate context to reconstruct what executed, what changed, and what communicated next.
View query — Review shell-to-curl payload retrieval
Q-02Drilldown

Review shell-to-curl payload retrieval

What this checks

Determine whether the candidate macOS device used an interactive Unix shell to retrieve payload content through the recurring retrieval path.

KQL
let target_device="<DEVICE_NAME>";
DeviceNetworkEvents
| where Timestamp >= ago(7d)
| where DeviceName =~ target_device
| where InitiatingProcessFileName =~ "curl"
| where RemoteUrl has "/curl/"
| project
    Timestamp,
    DeviceName,
    RemoteUrl,
    RemoteIP,
    RemotePort,
    InitiatingProcessFileName,
    InitiatingProcessFolderPath,
    InitiatingProcessCommandLine,
    InitiatingProcessAccountUpn,
    InitiatingProcessUniqueId
| order by Timestamp asc
SPL
index=<endpoint_network_index> sourcetype=<endpoint_network_events_sourcetype> earliest=-7d
| eval
    device=coalesce(device,DeviceName,host),
    process_name=lower(coalesce(process_name,InitiatingProcessFileName)),
    remote_url=coalesce(remote_url,RemoteUrl),
    cmd=coalesce(cmd,InitiatingProcessCommandLine)
| where device="<DEVICE_NAME>" AND process_name="curl" AND like(remote_url,"%/curl/%")
| table _time device process_name cmd RemoteIP RemotePort remote_url InitiatingProcessAccountUpn InitiatingProcessUniqueId
| sort 0 _time

What to look for

curl retrieval activity from a user shell, especially when the request path matches the recurring payload-retrieval shape.

Technical details

Tested signal

zsh or shell context followed by curl reaching a retrieval URI path.

Assumptions

  • The candidate device and investigation window are known from Q-01.

Data requirements and relevant fields

process

Endpoint process creation telemetry with stable process identity, path, command line, parent, and user context.

  • Timestamp
  • DeviceId
  • DeviceName
  • FileName
  • FolderPath
  • ProcessId
  • ProcessUniqueId
  • ProcessCommandLine
  • AccountName
  • AccountUpn
  • SHA1
  • SHA256
  • InitiatingProcessFileName
  • InitiatingProcessFolderPath
  • InitiatingProcessCommandLine
  • InitiatingProcessId
  • InitiatingProcessUniqueId
  • InitiatingProcessAccountName
  • InitiatingProcessAccountUpn
network

Endpoint network telemetry with destination URL, port, initiating process, user, and stable process identity.

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

Validate Defender for Endpoint coverage on macOS, table availability, field population, and retention before operational use.

SPL schema

Replace index/sourcetype placeholders and map device, process, file, and network fields to local telemetry.

Limitations

  • Administrative scripts can legitimately use shell-launched curl.

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

View query — Inspect AppleScript-assisted shell activity
Q-03Drilldown

Inspect AppleScript-assisted shell activity

What this checks

Find osascript activity using shell or native utilities observed in the post-execution chain.

KQL
let target_device="<DEVICE_NAME>";
DeviceProcessEvents
| where Timestamp >= ago(7d)
| where DeviceName =~ target_device
| where FileName =~ "osascript"
| where ProcessCommandLine has_any ("sh -c", "cp ", "rm ", "curl ", "mkdir ", "killall", "dscl")
| project
    Timestamp,
    DeviceName,
    AccountName,
    AccountUpn,
    FileName,
    FolderPath,
    ProcessCommandLine,
    ProcessUniqueId,
    SHA1,
    SHA256,
    InitiatingProcessFileName,
    InitiatingProcessCommandLine
| order by Timestamp asc
SPL
index=<endpoint_process_index> sourcetype=<process_events_sourcetype> earliest=-7d
| eval
    device=coalesce(device,DeviceName,host),
    process_name=lower(coalesce(process_name,FileName)),
    cmd=coalesce(cmd,ProcessCommandLine)
| where device="<DEVICE_NAME>" AND process_name="osascript"
    AND (
        like(lower(cmd),"%sh -c%")
        OR like(lower(cmd),"%cp %")
        OR like(lower(cmd),"%rm %")
        OR like(lower(cmd),"%curl %")
        OR like(lower(cmd),"%mkdir %")
        OR like(lower(cmd),"%killall%")
        OR like(lower(cmd),"%dscl%")
    )
| table _time device AccountName AccountUpn process_name cmd InitiatingProcessFileName InitiatingProcessCommandLine
| sort 0 _time

What to look for

osascript activity that quickly chains native utilities into network, staging, or cleanup behavior.

Technical details

Tested signal

AppleScript-assisted shell, copy, removal, retrieval, directory, process, or user-discovery commands.

Assumptions

  • The candidate device is known.

Data requirements and relevant fields

process

Endpoint process creation telemetry with stable process identity, path, command line, parent, and user context.

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

Validate Defender for Endpoint coverage on macOS, table availability, field population, and retention before operational use.

SPL schema

Replace index/sourcetype placeholders and map device, process, file, and network fields to local telemetry.

Limitations

  • AppleScript automation is common on managed Macs and must be interpreted with user/application context.

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

View query — Check temporary staging and archive creation
Q-04Drilldown

Check temporary staging and archive creation

What this checks

Search the candidate device for temporary sync staging and compression activity preceding the upload.

KQL
let target_device="<DEVICE_NAME>";
let archive_processes =
    DeviceProcessEvents
    | where Timestamp >= ago(7d)
    | where DeviceName =~ target_device
    | where FileName in~ ("zip","ditto","tar","gzip")
    | where ProcessCommandLine has "/tmp/"
    | project
        Timestamp,
        DeviceName,
        Signal="archive-process",
        FileName,
        FolderPath,
        ProcessCommandLine,
        AccountName,
        InitiatingProcessFileName,
        InitiatingProcessCommandLine;
let staged_files =
    DeviceFileEvents
    | where Timestamp >= ago(7d)
    | where DeviceName =~ target_device
    | where FolderPath startswith "/tmp/sync"
        or FolderPath startswith "/tmp/"
    | where FileName endswith ".zip"
    | project
        Timestamp,
        DeviceName,
        Signal="staged-file",
        FileName,
        FolderPath,
        ProcessCommandLine=InitiatingProcessCommandLine,
        AccountName=InitiatingProcessAccountName,
        InitiatingProcessFileName,
        InitiatingProcessCommandLine;
union archive_processes, staged_files
| order by Timestamp asc
SPL
(
    index=<endpoint_process_index> sourcetype=<process_events_sourcetype> earliest=-7d
)
OR
(
    index=<endpoint_file_index> sourcetype=<file_events_sourcetype> earliest=-7d
)
| eval
    device=coalesce(device,DeviceName,host),
    process_name=lower(coalesce(process_name,FileName,InitiatingProcessFileName)),
    folder=coalesce(folder,FolderPath),
    cmd=coalesce(cmd,ProcessCommandLine,InitiatingProcessCommandLine),
    signal=case(
        process_name IN ("zip","ditto","tar","gzip") AND like(cmd,"%/tmp/%"),"archive-process",
        like(folder,"/tmp/sync%"),"staged-file",
        like(folder,"/tmp/%") AND like(lower(FileName),"%.zip"),"staged-file",
        true(),"other"
    )
| where device="<DEVICE_NAME>" AND signal!="other"
| table _time device signal process_name folder FileName cmd AccountName InitiatingProcessFileName
| sort 0 _time

What to look for

Staging under temporary sync paths or temporary ZIP archive creation close to the upload window.

Technical details

Tested signal

Temporary collection directories followed by archive utility execution in the temporary filesystem.

Assumptions

  • Process and file telemetry cover the pre-exfiltration window.

Data requirements and relevant fields

process

Endpoint process creation telemetry with stable process identity, path, command line, parent, and user context.

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

Endpoint file telemetry used to scope temporary staging, archive creation, and cleanup.

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

Validate Defender for Endpoint coverage on macOS, table availability, field population, and retention before operational use.

SPL schema

Replace index/sourcetype placeholders and map device, process, file, and network fields to local telemetry.

Limitations

  • Backup, software packaging, and developer workflows can legitimately archive data under temporary paths.

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

Legitimate resemblance

What the analyst should confirm

Similar activity can be legitimate. Confirm the approved purpose and expected context before escalating.
  • A developer or operations engineer uses curl to upload chunked artifacts to an internal or approved external API.

    The destination, command template, automation owner, device role, authentication method, and historical behavior match an approved engineering workflow.
  • A backup or data-transfer utility invokes curl with binary PUT semantics and temporary archives.

    The parent process, destination, schedule, archive source, software owner, and change documentation match the approved backup or migration workflow.
  • A security-research Mac reproduces MacSync behavior in an isolated lab.

    The endpoint is a named research asset, the exercise window is documented, and no production credentials or data are involved.

Confirmed match

Action after a confirmed match

  • Isolate the affected Mac when unauthorized collection or exfiltration is confirmed.
  • Terminate malicious shell, curl, AppleScript, and related child processes.
  • Preserve temporary staging, archive, process, network, and command-line evidence before cleanup.
  • Reset or rotate credentials exposed from Keychain, browsers, SSH material, cloud credentials, or local files.
  • Block confirmed malicious MacSync infrastructure while continuing behavior-based hunting for rotated domains.
  • Search all managed Macs for the same retrieval, staging, chunked upload, and cleanup behavior.
  • Review Terminal and ClickFix-resistant platform controls and user-awareness coverage.

Threat hunt

Could this be happening elsewhere?

Hunt for this behavior across the environment.
View threat hunt

Technical boundary

Telemetry and limitations

Endpoint

Device inventory used to restrict analytics to current macOS endpoints.

Required fields
  • Timestamp
  • DeviceId
  • DeviceName
  • OSPlatform
Process

Endpoint process creation telemetry with stable process identity, path, command line, parent, and user context.

Required fields
  • Timestamp
  • DeviceId
  • DeviceName
  • FileName
  • FolderPath
  • ProcessId
  • ProcessUniqueId
  • ProcessCommandLine
  • AccountName
  • AccountUpn
  • SHA1
  • SHA256
  • InitiatingProcessFileName
  • InitiatingProcessFolderPath
  • InitiatingProcessCommandLine
  • InitiatingProcessId
  • InitiatingProcessUniqueId
  • InitiatingProcessAccountName
  • InitiatingProcessAccountUpn
Network

Endpoint network telemetry with destination URL, port, initiating process, user, and stable process identity.

Required fields
  • Timestamp
  • DeviceId
  • DeviceName
  • RemoteUrl
  • RemoteIP
  • RemotePort
  • Protocol
  • LocalIP
  • LocalPort
  • InitiatingProcessFileName
  • InitiatingProcessFolderPath
  • InitiatingProcessCommandLine
  • InitiatingProcessAccountName
  • InitiatingProcessAccountUpn
  • InitiatingProcessUniqueId
File

Endpoint file telemetry used to scope temporary staging, archive creation, and cleanup.

Required fields
  • Timestamp
  • DeviceId
  • DeviceName
  • ActionType
  • FileName
  • FolderPath
  • SHA1
  • SHA256
  • FileSize
  • InitiatingProcessFileName
  • InitiatingProcessFolderPath
  • InitiatingProcessCommandLine
  • InitiatingProcessAccountName
  • InitiatingProcessAccountUpn
  • InitiatingProcessUniqueId

Blind spots

  • The actor can replace curl with another HTTP client while preserving the broader collection and staging behavior.
  • A device with legitimate historical matching curl uploads can fail the first-seen baseline despite compromise.
  • Stable process identity might not be populated consistently on every endpoint sensor version.
  • Request parameters or URI paths can rotate, so the hunt should retain collection, staging, and cleanup pivots.

Behavior mapping

MITRE ATT&CK

Mappings describe the behavior examined by this analytic. They do not prove attribution, deployment, or technique-wide coverage.

Review boundary

Sources and limits

External sources
4

Exact fields, retention, and operational thresholds remain environment-specific.