Feb 18, 2026·7 min read·36 visits
The Tlon extension for OpenClaw didn't check if the URL you gave it was safe. Attackers could point it at `localhost` or AWS metadata (`169.254.169.254`) to steal credentials or map internal networks. Fixed in version 2026.2.14 by adding a strict SSRF guard and URL validator.
A classic Server-Side Request Forgery (SSRF) vulnerability in OpenClaw's Tlon (Urbit) extension allowed authenticated users to coerce the server into making arbitrary HTTP requests to internal networks, loopback interfaces, or cloud metadata services. By failing to validate the user-supplied 'ship' URL, the application acted as an open proxy for internal reconnaissance.
In the world of open-source software, the core application is usually a fortress. It's beaten, battered, and hardened by thousands of eyes. But the extensions? The plugins? The "optional integrations"? That's the soft underbelly. That's where the bodies are buried.
OpenClaw is a robust gateway, but it offers an extension for Tlon (Urbit)—a decentralized personal server platform that is already esoteric enough to make most security researchers glaze over. And that’s exactly why this vulnerability is interesting. It hides in the complexity of a feature most people probably don't use, but is available to anyone who installs the default suite.
The vulnerability here isn't some complex memory corruption or a race condition in the kernel. It's a logic error born of trust. The application assumed that if a user provides a URL for their "ship" (Urbit server), they probably mean a public URL on the internet. But as we know in this industry, if you give a user a text box that accepts a URL, and you don't sanitize it, you haven't built a feature—you've built a proxy.
The root cause here is Server-Side Request Forgery (SSRF), specifically CWE-918. Conceptually, SSRF is like telling a security guard, "Hey, go check what's inside that room for me." A good guard checks if you have clearance for that room. A vulnerable guard—like OpenClaw's Tlon extension—just walks into the bank vault because you asked nicely.
In the Tlon extension, the code needed to authenticate with a user's Urbit ship. To do this, it took a url and a code from the configuration. It then constructed a request to ${url}/~/login.
The flaw was the total absence of boundary checks. There was no logic to say, "Hey, maybe we shouldn't allow the URL to resolve to 127.0.0.1 or 169.254.169.254." The fetch API in Node.js (and the browser) is protocol-agnostic and network-agnostic. It will happily fetch whatever you tell it to. This means the OpenClaw server, sitting comfortably behind your corporate firewall, could be tricked into scanning your internal network or retrieving sensitive cloud instance metadata.
Let's look at the smoking gun. This is the code that existed prior to version 2026.2.14. It's painfully simple, which is usually a good thing, except when it involves network calls based on user input.
auth.ts)// The 'url' parameter comes directly from user config
export async function authenticate(url: string, code: string): Promise<string> {
// 🚩 DANGER: No validation of 'url' before usage
const resp = await fetch(`${url}/~/login`, {
method: "POST",
headers: { "Content-Type": "application/x-www-form-urlencoded" },
body: `password=${code}`,
});
// ... processing response
}If I set my URL to http://localhost:22, this code tries to POST to the SSH port. If I set it to an internal Jenkins server, it tries to login there.
bfa7d21e)The patch introduced by Peter Steinberger is a masterclass in defense-in-depth. They didn't just add a regex check; they ripped out the underlying fetch mechanism and replaced it with a guarded version.
// The new world order
import { fetchWithSsrFGuard } from '@openclaw/plugin-sdk';
// 1. Validate the URL structure first
const validatedUrl = validateUrbitBaseUrl(rawUrl);
// 2. Use the guarded fetcher
const resp = await fetchWithSsrFGuard(`${validatedUrl}/~/login`, {
method: 'POST',
// ...
}, {
// 3. Explicit policy: Block private networks by default
allowPrivateNetwork: config.allowPrivateNetwork ?? false,
auditContext: 'tlon-urbit-login'
});The fix does three critical things:
allowPrivateNetwork: false).So, how do we weaponize this? Let's assume OpenClaw is running on an AWS EC2 instance. This is the holy grail of SSRF targets because of the Instance Metadata Service (IMDS).
http://169.254.169.254/latest/meta-data/iam/security-credentials/admin-role/~/login the code appends. A simple # fragment identifier usually kills the rest of the path: http://169.254.169.254/.../admin-role#)fetch('http://169.254.169.254/.../admin-role#/~/login').# creates a client-side fragment, effectively stripping the login path from the server request.The application expects a login response but instead gets a JSON blob containing AccessKeyId, SecretAccessKey, and Token. Depending on how OpenClaw handles the error, this data might be:
Even without full response reflection, a Blind SSRF allows us to port scan the internal network. http://localhost:6379 responding quickly vs. http://localhost:6380 timing out tells us where the Redis instance lives.
You might look at the CVSS score of 6.5 and think, "Medium severity? I'll patch it next month." That would be a mistake. CVSS scores often fail to capture context.
If your OpenClaw instance is running inside a Kubernetes cluster, this SSRF allows an attacker to hit the Kubelet API. If it's in AWS, it's IAM credentials. If it's on a corporate LAN, it's access to that dusty old Windows 2008 server that hasn't been patched since the Obama administration.
SSRF turns the perimeter firewall into Swiss cheese. The attacker is no longer "outside"; they are originating requests from trusted infrastructure. Trust relationships (like "allow traffic from 10.0.0.5") are immediately bypassed. This vulnerability transforms OpenClaw from a productivity tool into a pivot point for lateral movement.
The remediation is straightforward: Update to version 2026.2.14.
The patch implemented in commit bfa7d21e is robust. It moves the responsibility of safety from the developer (who might forget a check) to the framework (the plugin-sdk).
If you cannot patch immediately:
169.254.169.254 (Cloud Metadata)127.0.0.0/8 (Localhost)10.0.0.0/8, 172.16.0.0/12, 192.168.0.0/16 (Private LANs)Never use a raw fetch or axios call with user-supplied URLs. Always use a wrapper library that resolves DNS before connection and verifies the IP against a denylist. Remember: DNS Rebinding attacks can bypass simple string checks on hostnames.
CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:N/A:N| Product | Affected Versions | Fixed Version |
|---|---|---|
openclaw openclaw | <= 2026.2.13 | 2026.2.14 |
| Attribute | Detail |
|---|---|
| Vulnerability Type | Server-Side Request Forgery (SSRF) |
| CWE ID | CWE-918 |
| CVSS Score | 6.5 (Medium) |
| Attack Vector | Network |
| Privileges Required | Low (Authenticated User) |
| Exploit Status | PoC Available |
The application does not properly validate that the destination IP address of a request is not within a private network range.
An uncontrolled resource consumption vulnerability exists in the Scala-based http4s-blaze-server package of the http4s/blaze library. The vulnerability allows remote, unauthenticated attackers to cause an Out of Memory Error (OOM) and JVM crash by streaming a continuous sequence of small or empty WebSocket continuation frames with the FIN bit set to 0. This bypasses typical payload size checks because of the JVM's per-object allocation overhead, leading to rapid heap exhaustion with minimal network bandwidth.
A critical path traversal vulnerability has been identified in the OpenList Go-based backend package. The vulnerability exists within the batch rename handler because the application does not validate the source filename parameter before constructing filesystems paths. This omission allows authenticated users to escape their designated directory and rename files in sibling paths.
OpenList version 4.2.3 and prior is vulnerable to an authorization bypass and metadata leakage. When configured with the Bleve search engine backend, OpenList fails to perform separator-aware path matching when validating tenant containment. This allows authenticated users to access sibling directories sharing similar name prefixes. Furthermore, the search backend returns unfiltered global result counts, leaking existence verification data of unauthorized files via side-channel analysis.
An authorization bypass vulnerability in OpenList version 4.2.3 and below allows authenticated users to read arbitrary files outside of their designated base directories due to an insecure path prefix check using Go's standard strings.HasPrefix function.
A security policy bypass vulnerability exists in the AWS API MCP Server (awslabs-aws-api-mcp-server) from version 0.2.13 through 1.3.46. When the server fails to load the read-only operations index during startup (due to transient network failures, file permission issues, or other exceptions), it logs a warning but continues running in an insecure, degraded state. Under this condition, the security policy engine fails open, silently skipping all subsequent security checks and consent prompts for the lifetime of the process. This permits unauthorized mutating AWS CLI commands to execute via indirect prompt injection attacks.
An incomplete escaping vulnerability in the npm package 'shescape' allows unauthenticated users to trigger dynamic shell expansions, absolute path disclosure, and command block break-outs on Unix and Windows systems.