Rust macOS Implant - Gaslight

31187949466?profile=RESIZE_400xOur friends at SentinelLABS have analyzed a Rust macOS implant that embeds a 3.5 KB prompt-injection payload containing 38 fabricated “system” messages, designed to steer an LLM-assisted triage pipeline into aborting or refusing analysis.  Command-and-control runs over a Telegram Bot API polling loop, with AES-GCM payloads over certificate-pinned TLS.

The implant self-redacts its Telegram bot token in its own runtime output, denying it to anyone who captures logs or crash artifacts.  Analysts have assessed with high confidence that the implant, which they track as macOS.Gaslight, belongs to a cluster of DPRK-aligned macOS activity.

In early June, an Apple XProtect update surfaced a Mach-O sample that had been uploaded to VirusTotal on 22 May.  The XProtect rule targets the file solely by its hash, rather than by any internal strings or bytecode, yet the sample remains undetected by static engines on VirusTotal at the time of writing.  The binary is ad hoc signed and carries the identifier endpoint-macos-aarch64-5555494492fc075f441637fb9d894913dde3a2ea.

31187955293?profile=RESIZE_710xmacOS.Gaslight sample on VirusTotal 23 June 2026

The sample is a macOS implant and infostealer written in Rust.  Its most notable feature is an embedded cascade of fabricated system-failure messages, designed to make an LLM-assisted triage agent doubt its own session.  It attacks the agent’s perception, rather than the sandbox it runs in.  Accordingly, we dub this family macOS.Gaslight.


31187950057?profile=RESIZE_584xSome of the many fake LLM data messages embedded in the binary

Researchers are highly confident that this implant is part of a cluster of DPRK-aligned macOS activity.  Apple’s XProtect detects the sample under the rule MACOS_BONZAI_COBUCH, and SentinelLABS associates the BONZAI signature family with North Korean threat activity.  A sibling BONZAI sample is also caught by Apple’s AIRPIPE rule, a family that SentinelLABS also ties to North Korean activity.

Command & Control | Telegram Bot API - The implant’s command-and-control channel is a Telegram Bot API getUpdates polling loop.  The polling branch executes only when no webhook is registered, and the dispatch handler keys on three Telegram error codes: BotBlocked, InvalidToken, and Conflict.

Telegram issues a Conflict response when two instances of the same bot token poll simultaneously, so the implant treats that response as an implicit single-instance lock. A second copy detects the conflict and terminates.

31187951660?profile=RESIZE_710xHandling the Telegram Bot API error codes

Once the bot token is validated and the polling loop is active, the operator can task the implant, including via the interactive shell described below, and the collected data is returned over the same channel using Telegram’s multipart/attach:// file-upload mechanism.

The bot token, the chat ID (tg_room_id), and the rest of the operator configuration are supplied at runtime and are absent from this sample.  Accordingly, the analysis below is based on static examination of the binary and its embedded payloads.

Transport Hardening | AES-GCM Over Pinned TLS - All C2 payloads are encrypted with AES-GCM, implemented using the pure-Rust aes-gcm 0.10.3 crate, with a fresh nonce generated per message via CCRandomGenerateBytes.  The AES key is supplied at runtime through the aes_key field in the operator config rather than being embedded in the sample.

In addition to payload encryption, the implant configures a custom certificate trust anchor and calls SecTrustSetAnchorCertificatesOnly, restricting TLS trust evaluation to that anchor alone.  This certificate pinning rejects connections that are intercepted by a standard proxy CA, thereby frustrating network-level inspection of the operator’s traffic.


31187951677?profile=RESIZE_584xCustom certificate pinning via SecTrustSetAnchorCertificatesOnly

The implant also honors the host’s proxy settings, reading the active system proxy configuration via SCDynamicStoreCopyProxies and routing the traffic from its Rust reqwest/hyper networking stack accordingly.  As a result, the C2 can still reach the operator on networks that force outbound connections through a proxy.

Taken together, those choices make the channel harder to inspect in transit while still allowing it to operate in tightly managed enterprise networks.

Operator Access | An Interactive Shell - After validation and activation, the operator gains an interactive shell. Two co-located command menus define six verbs.

Verb

Function

help

Show command help

id

Identify the implant to the operator

shell

Execute a shell command via execvp, with posix_spawnp available as an alternative spawn path

kill

Terminate a target process by PID

upload

Exfiltrate a file via the Telegram file-attach mechanism

stop

Halt the implant

There is some evidence of a seventh command, focus, but we were unable to recover further details from our analysis.

31187951882?profile=RESIZE_710xOperator command menu strings embedded in the implant

The implant creates an IOPMAssertionCreateWithName power-management assertion to prevent the system from entering sleep.  Blocking sleep sustains long-running C2 polling and collection across periods of user inactivity, making the implant resilient to a host that would otherwise idle.  All told, the functionality provides the operator with a persistent, interactive foothold on the host.

The 15-Field Cross-Platform Operator Config - The implant reads its operator configuration using serde, a widely used Rust serialization and deserialization framework.  The operator provides the implant with a config blob at runtime, and serde fills in a predefined set of fields.  By default, serde matches incoming config keys to fields by their literal names, so the entire configuration schema of 15 field names is baked into the binary as plaintext.

tg_room_id                

github_token              

github_repo                

github_polling_interval

main_upload_url         

main_base_url            

aes_key                     

payload_path_linux     

payload_path_macos  

persist_name_linux     

persist_name_macos  

persist_type_linux       

persist_type_macos    

init_python_enable      

persist_enable          

The Linux- and GitHub-related fields are not exercised in the sample, suggesting the schema is an operator-facing interface to a broader toolset.

Collection | A Gated Python Stealer With Its Own Runtime Supply Chain - The implant carries a 6.6 KB base64-encoded Python script which serves as a data collection module. Once decoded, it harvests:

Chrome, Brave, Firefox, and Safari browser data

Terminal command histories

Installed application listings

A running-process snapshot via ps aux

System hardware and software profile via system_profiler

A raw copy of login.keychain-db

Collected artifacts are archived to temp/collected_data.zip and uploaded to the operator via Telegram.


31187952064?profile=RESIZE_584xDecoded Python stealer targets the victim’s keychain and other data

Analysts have previously documented Atomic macOS Stealer (AMOS) harvesting the same login keychain copy and browser data, as well as an early Rust macOS stealer targeting login.keychain-db in 2023.

A separate 2 KB base64-encoded bash installer fetches and stages a self-contained cpython-3.10.18 interpreter from the astral-sh/python-build-standalone project.  The installer, a prerequisite for deploying the Python stealer, carries the literal constants PY_VERSION=3.10.18 and BUILD_DATE=20250708 and targets both arm64 and x86_64 macOS.  The widespread use of emojis and strict adherence to comment headers are consistent with LLM-generated output.


31187952456?profile=RESIZE_584xThe decoded Bash script contains “written by AI”.

Microsoft has previously described macOS stealers bundling Python via PyInstaller and Nuitka.  However, fetching a standalone CPython build from astral-sh/python-build-standalone at runtime has not been previously documented as far as we are aware.  The separation keeps the main implant in Rust while letting the operator stage a fuller Python-based collection environment only when needed.

Sentinel identified init_python_enable in the serde schema as the configuration field associated with both the stealer and installer.  Consistent with our earlier observations, it found no exact runtime branch logic and therefore describes both only as configurable capabilities present in the binary.

Persistence | An Apple System-Service Masquerade - Persistence is achieved through a LaunchAgent.  This implant’s plist carries the Label value com.apple.system.services.activity.  Masquerading within Apple’s com. apple.* namespace is a tactic widely used in many macOS malware families, including those previously tied to DPRK-linked activities.

31187952471?profile=RESIZE_710xEmbedded LaunchAgent uses the label com.apple.system.services.activity

In order to write a valid absolute path to itself into the plist’s ProgramArguments array, the implant resolves its own executable location at runtime via __NSGetExecutablePath.

The implant’s persistence behavior is controlled by the persist_enable serde config field, and again, we did not recover a separate static branch that would confirm exactly how the installation is triggered in this sample.

OPSEC | Bot-Token Self-Redaction - Telegram bot tokens are a known weak point in bot-based C2.  If the token can be recovered, defenders can use it as a detection artifact and even query the Telegram Bot API directly, exposing the bot’s chat history, operator commands, and registered webhooks.  macOS.Gaslight addresses this by implementing a self-redaction routine in its Telegram URL constructor.

When the URL path segment is the 4-byte literal “file” (0x656c6966, little-endian), the constructor replaces the following token with the hardcoded placeholder file/token:redacted, preventing the live bot credential from appearing in any diagnostic output or error string the implant produces at runtime.

31187952481?profile=RESIZE_710xThe Telegram URL constructor token-redaction branch

The logic prevents anyone who captures the process’s logs, errors, or crash artifacts from determining the bot token, which otherwise is only available in the config itself and cannot be recovered from the sample.

NVISO Labs has previously noted that most documented Telegram bot abuse embeds recoverable tokens; macOS.Gaslight’s runtime self-redaction appears novel relative to that reporting.

A Prompt Injection That Targets the Analyst - The implant does little conventional anti-analysis.  It resolves its API calls at runtime via dlsym to avoid embedding them in the static symbol table, and it locates its own executable dynamically rather than from a hardcoded path.  What makes the sample notable is its attempt to mislead the analyst reading the output.  It carries a 3.5 KB Markdown-fenced blob of hostile data containing 38 fabricated “system” messages delimited with  tokens.

The  tokens and the surrounding Markdown fence mimic an LLM triage harness’s own prompt scaffold, blurring the boundary between untrusted sample data and trusted instructions.  The scaffold contains fake system messages about token expiry, out-of-memory kills, disk exhaustion, and repeated operation failures.  It also plants bogus warnings about injection vulnerabilities and static-analysis flags.  The aim is to push an LLM agent to abort, truncate, or refuse analysis.

Check Point first documented this kind of analyst-targeting prompt injection publicly in 2025, describing a Windows proof-of-concept that used a single direct-instruction prompt injection to evade AI-based detection.

Socket has since documented a Hades supply-chain payload whose stealer opens with a fake prompt-injection header to pollute AI-assisted analysis, while the leaked Shai-Hulud code carried an “Anthropic Magic String” intended to stop Claude Code from analyzing it.  Each relied on a single injected block or header rather than the 38-message harness-spoofing cascade seen here.

Previous SentinelLABS research, by contrast, examined malware that uses LLMs to generate or support capability at runtime rather than interfere with analyst tooling.

Conclusion - macOS.Gaslight packs considerable capability into a single, persistent Rust binary, bundling a credential and session-data stealer, an interactive shell, and a self-staged Python collection chain behind a hardened Telegram C2.  Aside from the runtime-fetched standalone CPython interpreter, these are all established macOS tradecraft.  However, macOS.Gaslight is noteworthy for its analyst-targeting prompt injection, an attempt to weaponize the LLM-assisted triage pipelines that increasingly sit in the reverse-engineering loop.

Anyone building such tooling should treat the contents of the samples they triage as adversarial input, never as instructions, and be prepared to keep hostile content out of the model entirely.  As LLM-assisted analysis becomes routine, defenders should expect more samples built to exploit it.

Indicators of Compromise

macOS.Gaslight Mach-O sample

6328567511d88fdc2ae0939c5ef17b7a63d2a833881900de018a4f12f4982525

Sibling BONZAI sample

77b4fd46994992f0e57302cfe76ed23c0d90101381d2b89fc2ddf5c4536e77ca

Ad hoc signing identifier

endpoint-macos-aarch64-5555494492fc075f441637fb9d894913dde3a2ea

LaunchAgent Label

com.apple.system.services.activity

Python payload script

baabf249c77bc54c54ab0e66e15af798bd28aa5b4683554456a8b73ab8741239 

Bash Installer script

b3c56d689414343589f38394d19ba2fe9a518133281200faa0556ba4e4136394

 

This article is shared at no charge for educational and informational purposes only.

Red Sky Alliance is a Cyber Threat Analysis and Intelligence Service organization.  We provide indicators of compromise information (CTI) via a notification service (RedXray) or an analysis service (CTAC).  For questions, comments, or assistance, please contact the office directly at 1-844-492-7225 or feedback@redskyalliance.com    

Weekly Cyber Intelligence Briefings:
REDSHORTS - Weekly Cyber Intelligence Briefings
https://register.gotowebinar.com/register/5207428251321676122

E-mail me when people leave their comments –

You need to be a member of Red Sky Alliance to add comments!