MacSync Scripted Execution and Credential Theft
An attacker has deployed MacSync Stealer on a macOS host by tricking a user into executing a curl-to-zsh one-liner, which then runs in-memory scripts to harvest credentials and keychains.
Based on research by Huntress 2026-09-20 12 steps · 4 queries T1027 T1059.002 T1059.004 T1140 T1204.002 T1548.004 T1555.001 T1566.002
Brief
Why Now
The recent Huntress report, MacSync Stealer: How a Google Search for Claude Led to a macOS Infostealer (https://www.huntress.com/blog/fake-claude-macsync), details a campaign using malvertising to deliver infostealers to macOS users. The adversary uses a ClickFix lure, prompting users to paste a command into their terminal to fix a software error. This command is a one-liner that downloads and executes malicious scripts directly in memory. Our hunt provides a method to find these infections before the stealer exfiltrates keychains and browser data.
How the Hunt Flows
The hunt begins with a scoping phase using hb_software_inventory. We identify macOS hosts running AI-related packages like Claude, Anthropic, or ChatGPT. Attackers target these users specifically with fake software updates or fixes for these tools. This scoping step allows the team to prioritize workstations where the lure is most likely to succeed.
The first lead query targets hb_process_activity to find the initial infection vector. We search for instances where curl pipes content directly to zsh or sh. Because developers often use one-liners for legitimate software management, this query is not a high-fidelity detection on its own. The hunt provides these results to an analyst who judges the parent process and specific command arguments to separate malvertising lures from admin tasks.
After an analyst confirms a suspicious process lead, the hunt gates into parallel evidence gathering across hb_script_activity and hb_file_activity. We search for rare script content containing specific daemon_function logic and AppleScript osascript commands. These in-memory blocks are the core of the stealer, used to bypass TCC prompts and steal the user keychain. Simultaneously, the hunt looks for the creation of /tmp/osalogging.zip, the hardcoded staging path the stealer uses to store loot.
This multi-surface pivot is why this is a hunt, not a single detection. A rule looking only for curl-to-shell commands creates excessive noise, while a rule looking only for /tmp/osalogging.zip misses infections where the file was already moved or renamed. By linking the initial lure to the rare in-memory logic and the staging file, we confirm the full infection chain.
What the Hunt Cannot See
This hunt has two main blind spots. First, it depends on process command-line history in hb_process_activity. If the initial infection occurred outside the telemetry retention window, the lead query will return zero results. Second, the hunt requires comprehensive script block logging. If the macOS environment does not capture the content of AppleScript or shell blocks, the background logic used by the stealer remains invisible.
In this series
Steps
-
Identify hosts with AI software
Query · scopingFind macOS hosts running AI-related tools that match the malvertising campaign lures.
reads hb_software_inventorysqlSELECT device_hostname, package_name, vendor_name FROM hb_software_inventory WHERE (instr(',' || '{{ai_keywords}}' || ',', ',' || LOWER(package_name) || ',') > 0 OR instr(',' || '{{ai_keywords}}' || ',', ',' || LOWER(vendor_name) || ',') > 0)What a hit looks like. A list of hostnames to focus the hunt on. Silence means no known AI software matches were found.
-
Suspicious curl-to-shell lead
Query · detection candidateIdentify the primary ClickFix delivery mechanism where curl pipes content directly to a shell interpreter.
reads hb_process_activitysqlSELECT device_hostname, process_name, process_cmd_line, time FROM hb_process_activity WHERE (LOWER(process_cmd_line) LIKE '%curl %' AND (LOWER(process_cmd_line) LIKE '%|%zsh%' OR LOWER(process_cmd_line) LIKE '%|%sh%')) AND ('{{scope_hosts}}' = '' OR instr(',' || '{{scope_hosts}}' || ',', ',' || device_hostname || ',') > 0) AND time >= datetime('now', '-{{lookback_days}} days')What a hit looks like. Process events showing one-liner scripted execution. Silence proof that no such command ran within the retention window.
-
Assess lead suspicion
Agent triageDetermine if the curl execution looks like the MacSync loader or a legitimate admin task.
-
Gate on suspicious execution
DecisionOpen the expensive queries only if a suspicious lead is confirmed.
-
MacSync in-memory script logic
Query · baselineIdentify the rare background daemon functions and AppleScript keychain theft logic.
reads hb_script_activitysqlSELECT device_hostname, script_content, time FROM hb_script_activity WHERE (LOWER(script_content) LIKE '%daemon_function%' OR LOWER(script_content) LIKE '%osascript%') AND ('{{scope_hosts}}' = '' OR instr(',' || '{{scope_hosts}}' || ',', ',' || device_hostname || ',') > 0) AND time >= datetime('now', '-{{lookback_days}} days') GROUP BY script_content HAVING COUNT(DISTINCT device_hostname) <= 3What a hit looks like. Rare scripts containing MacSync core logic. Silence suggests no scripted theft was captured.
-
Exfiltration staging file
Query · enrichmentFind the creation of the specific /tmp/osalogging.zip archive used to stage stolen loot.
reads hb_file_activitysqlSELECT device_hostname, file_path, activity_name, time FROM hb_file_activity WHERE LOWER(file_path) = '/tmp/osalogging.zip' AND ('{{scope_hosts}}' = '' OR instr(',' || '{{scope_hosts}}' || ',', ',' || device_hostname || ',') > 0) AND time >= datetime('now', '-{{lookback_days}} days')What a hit looks like. Records of the staging zip file being created or modified. Silence means the file was not created or has already been removed.
-
Triage infection evidence
Agent triageSynthesize the lead and follow-on script/file activity into a final verdict.
-
Route based on infection status
DecisionDirect confirmed infections to response and others to documentation.
-
Isolate and remediate host
Response actionHalt data exfiltration and remove the malware's active components.
-
Analyst final review
Analyst taskManually verify the findings and confirm remediation success.
-
Close out hunt
Analyst taskFinalize the hunt and document the negative result.
Coverage
Scenario coverage
| Stage | Covered | How, or why not |
|---|---|---|
| ClickFix Malvertising Lure T1566.002 · T1204.002 · T1059.004 |
Yes | curl-to-shell-lead |
| Background ZSH Loader T1027 · T1140 · T1059.004 |
Yes | macsync-script-logic |
| Dynamic AppleScript Stealer T1059.002 · T1555.001 · T1548.004 |
Yes | macsync-script-logic, staging-file-activity |
| Mach-O RAT and Persistence T1543.001 · T1071.001 · T1573.002 |
Out of scope | Belongs to another part of the 'MacSync Stealer: How a Google Search for Claude Led to a macOS Infostealer' series. |
| Screen Recording Permission Capture T1113 · T1548.004 |
Out of scope | Belongs to another part of the 'MacSync Stealer: How a Google Search for Claude Led to a macOS Infostealer' series. |
| Crypto Wallet Trojanization T1539 · T1552 · T1491 |
Out of scope | Belongs to another part of the 'MacSync Stealer: How a Google Search for Claude Led to a macOS Infostealer' series. |
Blind spots
- Needs hb_process_activity with at least 30 days of command-line history. If the malvertising event happened more than 14 days ago, the primary lead query will return zero results despite an active infection. It would answer Whether the initial curl pipe command was executed outside the telemetry retention window.. Remediation: Increase retention for hb_process_activity on macOS endpoints to 30 days.
- Needs hb_script_activity with full AppleScript and Shell block logging enabled. By default, many macOS systems do not log script block content. Without this, the background logic of the stealer is invisible. It would answer Whether the in-memory daemon_function and osascript logic can be observed.. Remediation: Deploy MDM profiles to enable comprehensive shell and script block logging.
Parameters & data
Parameters
| Parameter | Type | Default | What it is |
|---|---|---|---|
ai_keywords | list[string] | claude, anthropic, chatgpt, claude code | Keywords to identify potential target hosts running AI software. |
lookback_days | number | 14 | Days of history to examine. |
scope_hosts | list[host] | — | Optional list of hosts to narrow the hunt based on the scoping step. |
Telemetry
| Source | Category | Telemetry |
|---|---|---|
| Endpoint telemetry (hb_ surfaces) | endpoint | endpoint |
Source
---
analysis: "A static detection rule for 'curl piped to shell' creates excessive noise\
\ in developer environments. This hunt uses a gated flow to first isolate suspicious\
\ one-liners and then corroborate them with rare in-memory script logic and exfiltration\
\ staging files\u2014a multi-surface pivot that a single process rule cannot perform."
blind_spots:
- id: process-history-retention
owner: SOC Infrastructure
question: Whether the initial curl pipe command was executed outside the telemetry
retention window.
remediation: Increase retention for hb_process_activity on macOS endpoints to 30
days.
requires: hb_process_activity with at least 30 days of command-line history
risk: If the malvertising event happened more than 14 days ago, the primary lead
query will return zero results despite an active infection.
stage: initial-access-clickfix-lure
- id: script-logging-disabled
owner: Mac Platform Team
question: Whether the in-memory daemon_function and osascript logic can be observed.
remediation: Deploy MDM profiles to enable comprehensive shell and script block
logging.
requires: hb_script_activity with full AppleScript and Shell block logging enabled
risk: By default, many macOS systems do not log script block content. Without this,
the background logic of the stealer is invisible.
stage: background-zsh-loader
coverage:
- stage: initial-access-clickfix-lure
status: covered
steps:
- curl-to-shell-lead
- stage: background-zsh-loader
status: covered
steps:
- macsync-script-logic
- stage: dynamic-applescript-theft
status: covered
steps:
- macsync-script-logic
- staging-file-activity
- reason: 'Belongs to another part of the ''MacSync Stealer: How a Google Search for
Claude Led to a macOS Infostealer'' series.'
stage: persistent-macho-rat
status: out_of_scope
- reason: 'Belongs to another part of the ''MacSync Stealer: How a Google Search for
Claude Led to a macOS Infostealer'' series.'
stage: screen-capture-helper
status: out_of_scope
- reason: 'Belongs to another part of the ''MacSync Stealer: How a Google Search for
Claude Led to a macOS Infostealer'' series.'
stage: wallet-app-trojanization
status: out_of_scope
guardrails:
claims: no_unsupported
evidence: citation_required
missing_data: not_benign
telemetry: untrusted
hunt:
applicability: campaign-specific
handoff: keep-as-periodic-hunt
justification: MacSync Stealer targets highly valuable developer credentials and
crypto assets via legitimate AI domains; identifying the initial scripted execution
is the only way to stop the theft before session cookies are exfiltrated.
methodology: model-assisted
trigger: intel-report
hypothesis: An attacker has deployed MacSync Stealer on a macOS host by tricking a
user into executing a curl-to-zsh one-liner, which then runs in-memory scripts to
harvest credentials and keychains.
labels:
- hunt
- attack.t1566.002
- attack.t1204.002
- attack.t1059.004
- attack.t1027
- attack.t1140
- attack.t1059.002
- attack.t1555.001
- attack.t1548.004
name: MacSync Scripted Execution and Credential Theft
parameters:
ai_keywords:
default:
- claude
- anthropic
- chatgpt
- claude code
description: Keywords to identify potential target hosts running AI software.
from:
kind: article
observed: '2026-08-17'
ref: huntress-macsync
type: list[string]
lookback_days:
default: '14'
description: Days of history to examine.
from:
kind: manual
observed: '2026-08-17'
ref: hunt-standard
type: number
scope_hosts:
default: []
description: Optional list of hosts to narrow the hunt based on the scoping step.
from:
kind: manual
observed: '2026-08-17'
ref: analyst-input
type: list[host]
provenance:
authors:
- name: Huntbase hunt generation
org: huntbase.io
generated:
by: huntbase-hunt-generation
from: https://www.huntress.com/blog/fake-claude-macsync
gates:
- dry-run
- lint
model: hb_google/gemini-3-flash-preview
rationale: Focus on macOS workstations used by developers or researchers who are likely
to experiment with AI tools. Use the software inventory to identify targets that
have recently installed or searched for AI assistance tools.
references:
- name: 'Huntress - MacSync Stealer: How a Google Search for Claude Led to a macOS
Infostealer'
url: https://www.huntress.com/blog/fake-claude-macsync
related:
- hunt: macsync-persistence-macho-rat
reason: This hunt focuses on the initial delivery and in-memory theft; long-term
persistence via Mach-O RATs and LaunchAgents is a separate stage of the kill chain.
relation: out-of-scope-alternative
scenario:
stages:
- name: ClickFix Malvertising Lure
observables:
- curl -sL [URL] | zsh
- claude.ai/share/
- Google Ads sponsored search for 'Claude Code'
- Display name 'Apple Support'
slug: initial-access-clickfix-lure
tactic: initial-access
techniques:
- T1566.002
- T1204.002
- T1059.004
- name: Background ZSH Loader
observables:
- daemon_function
- Base64 encoded gzip heredoc
- /tmp/osalogging.zip
slug: background-zsh-loader
tactic: execution
techniques:
- T1027
- T1140
- T1059.004
- name: Dynamic AppleScript Stealer
observables:
- osascript in-memory execution
- Chromium Safe Storage AES key extraction
- TCC prompt for Full Disk Access
- User password phishing prompt
- Extraction of login keychain secrets
slug: dynamic-applescript-theft
tactic: credential-access
techniques:
- T1059.002
- T1555.001
- T1548.004
- name: Mach-O RAT and Persistence
observables:
- 85.206.161.241:8443
- WebSocket over TLS
- LaunchAgent plist creation in Home folder
- .mpwd credential file
- .zshrc modification
slug: persistent-macho-rat
tactic: persistence
techniques:
- T1543.001
- T1071.001
- T1573.002
- name: Screen Recording Permission Capture
observables:
- Capture agent binary with blank icon
- --tcc-only command line flag
- -o [path] screenshot output
- TCC Screen Recording prompt
slug: screen-capture-helper
tactic: collection
techniques:
- T1113
- T1548.004
- name: Crypto Wallet Trojanization
observables:
- Modification of 60+ wallet extensions
- Trojanized Ledger Wallet app
- Fake recovery phrase phishing HTML
- Targeting of 21 desktop wallet apps
slug: wallet-app-trojanization
tactic: impact
techniques:
- T1539
- T1552
- T1491
summary: A malvertising campaign for 'Claude Code' lures users to a legitimate shared
conversation on claude.ai that instructs them to run a curl one-liner. This executes
a multi-stage infection chain involving a background zsh loader, a dynamic AppleScript
stealer that harvests credentials and keychain data, and a persistent Mach-O RAT.
The attack concludes by gaining screen recording permissions and trojanizing crypto
wallet applications to phish for recovery phrases.
series:
index: 1
slug: macsync-stealer-how-a-google-search-for-claude-led-to-a-macos-infostealer
title: 'MacSync Stealer: How a Google Search for Claude Led to a macOS Infostealer'
total: 2
severity: high
targets:
analyst:
name: Tier-2 analyst
role: analyst
endpoint:
category: endpoint
name: Endpoint telemetry (hb_ surfaces)
telemetry:
- endpoint
hunter:
agent: true
name: Hunt agent
tlp: clear
type: investigation
---
# MacSync Scripted Execution and Credential Theft
This hunt identifies the early stages of a MacSync Stealer infection by looking for the initial scripted delivery and the subsequent in-memory AppleScript execution used for credential theft. It uses a gated approach, first identifying suspicious curl-to-shell patterns before performing a fan-out investigation for in-memory script logic and exfiltration staging files. The hunt focuses on developer environments where AI tools like Claude are common lures.
## scoping-ai-software
<!-- Identify hosts with AI software -->
Find macOS hosts running AI-related tools that match the malvertising campaign lures.
```sqlite target=endpoint role=scoping params=(ai_keywords=ai_keywords)
~~~yaml
expected: A list of hostnames to focus the hunt on. Silence means no known AI software
matches were found.
reads:
- device_hostname
- package_name
- vendor_name
silence: not_evidence_of_absence
source: hb_software_inventory
verified: dry-run
verified_at: '2026-09-20'
~~~
SELECT device_hostname, package_name, vendor_name FROM hb_software_inventory WHERE (instr(',' || '{{ai_keywords}}' || ',', ',' || LOWER(package_name) || ',') > 0 OR instr(',' || '{{ai_keywords}}' || ',', ',' || LOWER(vendor_name) || ',') > 0)
```
## curl-to-shell-lead
<!-- Suspicious curl-to-shell lead -->
Identify the primary ClickFix delivery mechanism where curl pipes content directly to a shell interpreter.
```sqlite target=endpoint role=detection-candidate params=(lookback_days=lookback_days, scope_hosts=scope_hosts)
~~~yaml
expected: Process events showing one-liner scripted execution. Silence proof that
no such command ran within the retention window.
reads:
- device_hostname
- process_name
- process_cmd_line
- time
silence: not_evidence_of_absence
source: hb_process_activity
verified: dry-run
verified_at: '2026-09-20'
~~~
SELECT device_hostname, process_name, process_cmd_line, time FROM hb_process_activity WHERE (LOWER(process_cmd_line) LIKE '%curl %' AND (LOWER(process_cmd_line) LIKE '%|%zsh%' OR LOWER(process_cmd_line) LIKE '%|%sh%')) AND ('{{scope_hosts}}' = '' OR instr(',' || '{{scope_hosts}}' || ',', ',' || device_hostname || ',') > 0) AND time >= datetime('now', '-{{lookback_days}} days')
```
## assess-curl-lead
<!-- Assess lead suspicion -->
```agent target=hunter
cite: required
context:
- curl-to-shell-lead
max_iterations: 3
objective: Judge whether the curl-to-shell commands are consistent with malvertising
ClickFix lures, noting any suspicious parent processes or arguments.
success_criteria: A verdict of malicious | suspicious | benign citing specific process
command lines.
tools:
- endpoint
```
## gate-on-curl
<!-- Gate on suspicious execution -->
if~: "the assess-curl-lead verdict is suspicious or malicious for at least one host" (confidence: high, judge=hunter)
then: → parallel-investigation
indeterminate: → analyst-final-review
unavailable: → analyst-final-review (blind_spot: process-history-retention)
else: → close-out-benign
## parallel-investigation
<!-- Parallel evidence gathering -->
parallel:
- → macsync-script-logic
- → staging-file-activity
join: → triage-macsync
## macsync-script-logic
<!-- MacSync in-memory script logic -->
Identify the rare background daemon functions and AppleScript keychain theft logic.
```sqlite target=endpoint role=baseline params=(lookback_days=lookback_days, scope_hosts=scope_hosts)
~~~yaml
baseline:
compare: first_seen
window: '{{lookback_days}}d'
expected: Rare scripts containing MacSync core logic. Silence suggests no scripted
theft was captured.
prevalence:
by: device_hostname
key:
- script_content
rare_below: 3
reads:
- device_hostname
- script_content
- time
silence: not_evidence_of_absence
source: hb_script_activity
verified: dry-run
verified_at: '2026-09-20'
~~~
SELECT device_hostname, script_content, time FROM hb_script_activity WHERE (LOWER(script_content) LIKE '%daemon_function%' OR LOWER(script_content) LIKE '%osascript%') AND ('{{scope_hosts}}' = '' OR instr(',' || '{{scope_hosts}}' || ',', ',' || device_hostname || ',') > 0) AND time >= datetime('now', '-{{lookback_days}} days') GROUP BY script_content HAVING COUNT(DISTINCT device_hostname) <= 3
```
## staging-file-activity
<!-- Exfiltration staging file -->
Find the creation of the specific /tmp/osalogging.zip archive used to stage stolen loot.
```sqlite target=endpoint role=enrichment params=(lookback_days=lookback_days, scope_hosts=scope_hosts)
~~~yaml
expected: Records of the staging zip file being created or modified. Silence means
the file was not created or has already been removed.
reads:
- device_hostname
- file_path
- activity_name
- time
silence: not_evidence_of_absence
source: hb_file_activity
verified: dry-run
verified_at: '2026-09-20'
~~~
SELECT device_hostname, file_path, activity_name, time FROM hb_file_activity WHERE LOWER(file_path) = '/tmp/osalogging.zip' AND ('{{scope_hosts}}' = '' OR instr(',' || '{{scope_hosts}}' || ',', ',' || device_hostname || ',') > 0) AND time >= datetime('now', '-{{lookback_days}} days')
```
## triage-macsync
<!-- Triage infection evidence -->
```agent target=hunter
cite: required
context:
- assess-curl-lead
- macsync-script-logic
- staging-file-activity
max_iterations: 6
objective: Analyze the combined results of the curl lead, the rare daemon script logic,
and the staging file creation to confirm a successful MacSync infection.
success_criteria: A verdict of malicious | suspicious | benign citing relevant telemetry
across all steps.
tools:
- endpoint
```
## route-infection
<!-- Route based on infection status -->
if~: "the triage-macsync verdict is malicious for at least one host" (confidence: high, judge=hunter)
then: → isolate-and-remediate
indeterminate: → analyst-final-review
unavailable: → analyst-final-review (blind_spot: script-logging-disabled)
else: → close-out-benign
## isolate-and-remediate
<!-- Isolate and remediate host -->
```action target=endpoint
~~~yaml
approval: required
~~~
Isolate the endpoint from the network. Kill any active zsh processes with daemon_function logic. Remove the staging file at /tmp/osalogging.zip. Prompt the user for an immediate password reset and session revocation.
```
→ analyst-final-review
## analyst-final-review
<!-- Analyst final review -->
```manual target=analyst
Review the telemetry for the affected hosts. Confirm if the user actually clicked 'Allow' on the TCC prompts mentioned in the report. Check for any follow-on RAT persistence that may have survived the cleanup.
```
→ close-out-benign
## close-out-benign
<!-- Close out hunt -->
```manual target=analyst
Log the examined hosts and the time window. Note any visibility gaps in script or process logging for future remediation.
```
→ end
Run it
Take this hunt into your environment.
Open it in Huntbase to run every step against your own connections, with Scout weighing the evidence and your analysts in command. Or take the open hunt.md file anywhere that reads the format.
Machine-drafted by huntbase-hunt-generation using hb_google/gemini-3-flash-preview, gated by dry-run, lint, then reviewed by a person.