Feb 18, 2026·7 min read·52 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.
CVE-2026-118777 is a prototype pollution vulnerability (CWE-1321) in the Forge JavaScript cryptography and networking library (distributed on npm as node-forge) prior to version 1.4.1. The flaw resides in the forge.http client subsystem, where incoming HTTP cookie names and paths parsed from Set-Cookie response headers are stored directly into standard JavaScript objects without key validation or property isolation. By returning crafted Set-Cookie headers containing keys such as __proto__ or constructor, a malicious HTTP server can inject arbitrary properties into Object.prototype, attach attributes to the global Object constructor, or delete built-in prototype methods to cause an application-wide denial of service.
CVE-2026-118775 (GHSA-4642-5mcj-2rqw) is an integer overflow and validation flaw in the ASN.1 serialization module of Forge (node-forge) prior to version 1.4.1. The asn1.oidToDer() function fails to validate the range of the first two arcs of an Object Identifier (OID) and truncates the packed initial subidentifier to a single 8-bit integer. Applications that perform policy checks on untrusted OID strings before encoding them into Distinguished Encoding Rules (DER) format can be tricked into emitting DER bytes that correspond to a completely different, restricted OID.
CVE-2026-119194 is an algorithmic complexity vulnerability (CWE-407) in the pyasn1 library for Python, affecting versions 0.3.3 through 0.6.4. The flaw resides in the schemaless decoding routine ConstructedPayloadDecoderBase._decodeComponentsSchemaless used across BER, CER, and DER codecs. By sending an ASN.1 container populated with child components whose tag identifiers are separated by multiples of CPython's integer hash modulus (sys.hash_info.modulus), an unauthenticated attacker can force worst-case hash collisions in an unbounded Python set, degrading decoding performance from O(N) to O(N^2) and causing a Denial of Service (DoS).
CVE-2026-119195 is an algorithmic complexity vulnerability (CWE-407) in the pyasn1 library for Python, affecting all versions prior to 0.6.5. When decoding constructed strings or indefinite-length ANY types across BER, CER, and certain DER code paths, the payload decoders repeatedly concatenate incoming fragments onto an immutable bytes object using the += operator. Because Python bytes objects cannot be resized in-place, each fragment iteration allocates a new buffer and copies all previously accumulated bytes, resulting in quadratic O(N^2) time and memory copy complexity. An unauthenticated remote attacker can supply a highly fragmented ASN.1 stream to exhaust CPU cycles and stall application worker threads.
An algorithmic complexity vulnerability (CWE-407) in pyasn1 versions prior to 0.6.5 allows unauthenticated attackers to cause a Denial of Service (DoS) via crafted ASN.1 payloads. The flaw resides in the binary-encoded ASN.1 REAL mantissa deserialization routine within RealPayloadDecoder.valueDecoder. By performing iterative byte-string slicing and repeated bitwise shifts on a growing arbitrary-precision integer, the decoder exhibits quadratic time and memory allocation complexity relative to the mantissa length. Supplying a BER-, CER-, or DER-encoded payload with a large REAL mantissa causes excessive CPU consumption and blocks application worker processes.
A vulnerability classified under CWE-407 (Inefficient Algorithmic Complexity) exists in the pyasn1 library prior to version 0.6.5. When parsing constructed ASN.1 BIT STRING payloads under BER, CER, or schema-driven DER encoding rules, the BitStringPayloadDecoder repeatedly shifts and copies the entire accumulated bit value for each incoming fragment. Processing inputs with a large number of small constructed fragments induces quadratic CPU consumption, allowing unauthenticated remote attackers to cause a denial of service via resource exhaustion during the initial decoding phase.