A threat actor exploited a critical pre-authentication remote code execution vulnerability in marimo to harvest AWS credentials, retrieve an SSH private key from AWS Secrets Manager, and authenticate to a bastion host in just eight seconds, according to the Sysdig Threat Research Team.
This vulnerability, tracked as CVE-2026-39987, affects marimo versions up to 0.20.4 and was fixed in version 0.23.0.
The issue originates in the /terminal/ws WebSocket endpoint, which exposed an interactive pseudo-terminal without enforcing the authentication validation applied to other WebSocket routes.
An attacker able to access this endpoint could obtain a shell as the marimo process user without needing any credentials.
Sysdig observed that the attacker initiated a fresh WebSocket session at 18:57:22 UTC. Just four seconds later, the attacker retrieved AWS credentials stored in the application’s Redis backend.
By 18:57:30, the actor had authenticated to an internet-accessible bastion host using an SSH private key extracted from AWS Secrets Manager.
Researchers did not attribute this rapid progression to an AI agent. Instead, researchers noted that the actor spent about four hours building and debugging a custom Python toolkit during earlier sessions.
Once the toolkit was staged in the /tmp/ directory, the scripts automated tasks such as credential recovery, accessing Secrets Manager, extracting the SSH key, and authenticating to the bastion, all through a single background Python invocation.
Over a nine-hour intrusion, the operator executed more than 850 interactive commands. These commands included probing the local network, validating AWS credentials, calling Secrets Manager APIs, discovering EC2 instances, and preparing reverse-shell infrastructure.
The attacker utilized two separate AWS credential paths. The first credential was collected from environment variables or credential files on the compromised host.
At the same time, the second was retrieved through a Redis lookup against an application-stored credential value. CloudTrail logs indicated that the credentials belonged to different IAM users, suggesting the actor iteratively switched identities after the initial set failed to expose the targeted secret.
The custom scripts used the Boto3 library to call `secretsmanager:GetSecretValue`, including fallback attempts across multiple AWS Regions. The retrieved private key material was saved to `/tmp/bastion_key` with 0600 permissions before being replayed against the bastion.
Additionally, the operator attempted an alternative persistence method through EC2 Instance Connect. AWS logs recorded denied requests for `DescribeInstances`, `DescribeKeyPairs`, and `DescribeInstanceInformation`, followed by a blocked `SendSSHPublicKey` request targeting the placeholder instance ID i-0000000000000000. This sequence may serve as a high-confidence behavioral detection signal.
| Indicator type | Value | Role / detection context |
|---|---|---|
| Source IP | 172.236.12[.]17 | Interactive /terminal/ws WebSocket sessions; AS63949 Akamai Connected Cloud/Linode |
| C2 / relay IP | 45.79.187[.]72 | Attacker-controlled VPS used for nc -lvp 4444 callback listener |
| Vulnerable endpoint | /terminal/ws | Unauthenticated marimo WebSocket terminal endpoint |
| Dropped files | /tmp/chain.py, /tmp/full_chain.py | Base64-decoded Python automation scripts |
| SSH key path | /tmp/bastion_key | Private key retrieved from Secrets Manager and saved with mode 0600 |
| Log artifacts | /tmp/chain.log, /tmp/chain_output.txt, /tmp/callback.log | Output files associated with chain execution and listener activity |
| AWS API | secretsmanager:GetSecretValue | Secret retrieval preceding SSH pivot |
| AWS API pattern | DescribeInstances → denied; DescribeKeyPairs → denied; DescribeInstanceInformation → denied; SendSSHPublicKey | Failed EC2 Instance Connect automation sequence |
| Command pattern | base64 -d > /tmp/<name>.py followed by nohup python3 | Script staging and background execution behavior |
Note: IP addresses and domains are intentionally defanged (e.g., [.]) to prevent accidental resolution or hyperlinking. Re-fang only within controlled threat intelligence platforms such as MISP, VirusTotal, or your SIEM.
Organizations should upgrade marimo to version 0.23.0 or later immediately and restrict network access to notebook services. Defenders should hunt for unauthenticated connections to /terminal/ws, unexpected Python processes accessing AWS Secrets Manager, SSH private keys written under /tmp, and outbound connections to unapproved hosts on port 4444.
CloudTrail detections should prioritize the attack chain rather than the apparent operator style: a compromised workload identity calling Secrets Manager, followed closely by SSH authentication, unusual EC2 Instance Connect activity, or outbound callback traffic.
Keep your SOC up to date on active malware & phishing within 24h of their emergence. Try ANYRUN to prevent incidents with early detection.
Attackers abused compromised ConnectWise Cloud Backup credentials to access Microsoft 365 customer environments, prompting ConnectWise…
Anthropic says an internal Claude research model reconstructed how the National Security Agency generated seeds…
Two WordPress Core vulnerabilities can be chained to execute remote code without authentication, according to…
A newly disclosed OpenBSD kernel exploitation chain combines a shared memory accounting flaw with a…
GhostAction attackers have expanded their GitHub credential theft campaign with a second stage that disguises…
Indian authorities have warned that SideCopy hackers are targeting Indian organizations, including academic institutions, with…