Sysdig Traces Marimo RCE Attacker's Eight-Second AWS-to-SSH Pivot

Summary
Sysdig TRT traced a marimo CVE-2026-39987 intrusion: an operator used a hand-built toolkit and harvested AWS credentials to retrieve an SSH key and reach a bastion in eight seconds. The report details the attack chain, detection signals and mitigations.
Key points
- CVE-2026-39987 is a pre-authentication RCE in marimo’s /terminal/ws endpoint. Versions through 0.20.4 are affected; version 0.23.0 fixes the flaw.
- Sysdig observed an operator use harvested AWS credentials to retrieve an SSH key from Secrets Manager and authenticate to a bastion eight seconds after opening a WebSocket session.
- The operator built and debugged Python scripts by hand over several hours. Sysdig found no evidence of AI use during the observed activity.
- Credentials were collected from the compromised host’s environment or files and the application’s Redis backend; the two credentials mapped to different IAM users.
- EC2 enumeration calls were denied, but the operator’s script still attempted an SSH key push using a placeholder instance ID; AWS blocked the attempt.
- Sysdig recommends upgrading to marimo 0.23.0 or later, restricting or disabling the terminal endpoint, auditing and limiting cloud credentials, and restricting notebook-container egress.
- Sysdig attributed the observed WebSocket sessions to 172.236.12.17 and the callback listener to 45.79.187.72.
Article Details
- Attack Vectors
- Unauthenticated access to marimo's /terminal/ws WebSocket endpoint provided an interactive shell and enabled pre-authentication remote code execution.
- The operator harvested AWS credentials from environment variables and credential files on the compromised host, and separately from the application's Redis backend.
- The harvested credentials were used to query AWS Secrets Manager for an SSH private key, which was then used to authenticate to an internet-reachable bastion host.
- The operator attempted an alternate access path using EC2 enumeration followed by an EC2 Instance Connect public-key push; the enumeration was denied and the push to a placeholder instance ID was blocked.
- A Python reverse shell connected to an attacker-owned VPS listener over TCP port 4444.
- Defensive Notes
- Update marimo to version 0.23.0 or later; restrict or disable /terminal/ws, and require authentication at the reverse proxy if the endpoint remains enabled.
- Audit and rotate credentials exposed on internet-reachable marimo hosts, including environment variables, credential files, application .env files, and credentials stored in the application's data layer.
- Restrict Secrets Manager permissions so notebook-host credentials cannot retrieve unrelated secrets such as a bastion SSH key.
- Restrict outbound network access from notebook containers, especially public-internet TCP connections on unnecessary ports.
- Hunt for chain.py, bastion_key, and similarly named files in /tmp; monitor for the reported script-deployment patterns and sequences of Secrets Manager calls, outbound TCP connections, and SSH authentication.
- Monitor for denied EC2 enumeration calls followed by SendSSHPublicKey attempts against the placeholder instance ID.
Indicators of compromise
| Type | Indicator | Context |
|---|---|---|
| IPV4 | 172[.]236[.]12[.]17 | Source IP for the operator's interactive WebSocket sessions; the article assesses it as disposable VPS infrastructure. |
| IPV4 | 45[.]79[.]187[.]72 | Attacker-owned VPS used as the reverse-shell callback listener and for SSH-based listener setup. |
MITRE ATT&CK
T1021.004 · SSHThe operator used the retrieved private key to authenticate over SSH to an internet-reachable bastion host.T1046 · Network Service DiscoveryThe first interactive command probed the RFC1918 /24 network containing the compromised host using /dev/tcp.T1059.004 · Unix ShellThe operator used shell commands to deploy scripts, start them with nohup, and run a bash interactive shell through the reverse-shell connection.T1059.006 · PythonThe operator ran hand-written Python scripts for AWS Secrets Manager access, SSH key handling, and reverse-shell setup.T1078.004 · Cloud AccountsThe operator used harvested credentials associated with IAM users to access AWS services.T1095 · Non-Application Layer ProtocolThe operator's Python reverse shell used a raw TCP connection to the VPS listener on port 4444.T1098.004 · SSH Authorized KeysThe operator attempted to push a public key using EC2 Instance Connect's SendSSHPublicKey call; the attempt was blocked.T1190 · Exploit Public-Facing ApplicationThe operator exploited marimo's unauthenticated /terminal/ws endpoint for pre-authentication remote code execution.T1552.001 · Credentials In FilesThe operator harvested AWS credentials from credential files and application configuration on the compromised host.T1552.004 · Private KeysThe operator retrieved an SSH private key from Secrets Manager, saved it locally, and used it for bastion authentication.T1555.006 · Cloud Secrets Management StoresUsing harvested AWS credentials, the operator queried AWS Secrets Manager for a bastion SSH private key.T1571 · Non-Standard PortThe reverse-shell callback used TCP port 4444 for the listener on the attacker-owned VPS.T1580 · Cloud Infrastructure DiscoveryThe operator attempted AWS EC2 instance and key-pair enumeration with DescribeInstances and DescribeKeyPairs.
CVE
Malware
Vendors
Products
Amazon GuardDutyand outbound network egress runtime policy classes cover the on-host portion of this attack chain. AWS GuardDuty's UnauthorizedAccess:IAMUser/InstanceCredentialExfiltration.OutsideAWS finding covers the cloud sideAWS EC2 Instance ConnectAWS EC2 Instance Connect: Automation fired against a null targetAWS Secrets Managerchain end to end: initial access through the unauthenticated WebSocket terminal, an AWS Secrets Manager call using credentials harvested from the compromised instance, and SSH access to a bastion hostCloudTrailFirst harvested AWS credential validated via GetCallerIdentity in CloudTrail MarimoThe attacker exploited CVE-2026-39987, a pre-authentication remote code execution (RCE) vulnerability in marimo. They ran a complete credential-pivot chain end to end: initial access through the unauthenticated