Jul 30, 2026·6 min read·4 visits
The dssrf-js library before 1.0.4 strips the '@' character from URLs during safety validation. This causes the library to validate a safe, mutated host string (e.g., evil.com127.0.0.1) while the downstream client retrieves the original URL, connecting directly to restricted local resources.
An SSRF validation bypass exists in dssrf-js (v1.0.3 and prior) due to an improper string normalization sequence inside is_url_safe. Before validating the host using Node's WHATWG parser, the helper strips the '@' symbol. This corrupts the parser's authority resolution, while the application's client requests the original, un-sanitized string containing internal IP targets.
The dssrf-js library is a Node.js utility designed to protect applications from Server-Side Request Forgery (SSRF) attacks. It intercepts user-supplied destination URLs, sanitizing and validating them to prevent connections to loopback networks, private IP spaces, and local metadata endpoints. This is typically achieved by parsing the host, resolving the domain, and cross-referencing IPs against blocklists.
In versions prior to 1.0.4, the library performs unsafe pre-processing on the raw URL string before handing it over to the parsing engine. This design flaw introduces a parser differential between the sanitizer's internal validation context and the downstream client's execution context. As a result, input designed to exploit this differential bypasses safety validation altogether.
Because the validator asserts that the input is safe, the application proceeds to execute the request using the original, unmodified URL. This allows remote attackers to target internal systems, accessing local administrative dashboards, AWS metadata services, and databases without authorization.
The root cause of this vulnerability lies in the helper function remove_at_symbol_in_string executed within is_url_safe. In version 1.0.3, this function strips all occurrences of the '@' character from the raw URL string before invoking the WHATWG parser.
According to the WHATWG URL standard, the '@' character separates optional userinfo credentials from the host segment in a URL authority blocks: scheme://username:password@hostname/path. When a client parses a URL with this format, it routes the network connection exclusively to the designated host, treating the preceding authority data as credentials.
When an attacker provides an authority-based URL like http://evil.com@127.0.0.1/, the true network destination is the local IP address 127.0.0.1. However, the validator's stripping routine modifies this input string to http://evil.com127.0.0.1/ before validation. The WHATWG parser processes the modified string and extracts the hostname as evil.com127.0.0.1. Because this hostname points to a public namespace or fails DNS lookup completely, the library mistakenly marks the URL as safe. The application then executes the HTTP request against the original, un-mutated URL, making a direct connection to the internal loopback.
The workflow diagram below demonstrates how the validation bypass operates step-by-step, contrasting the path taken by the validator with the path taken by the client application.
This execution discrepancy illustrates why string manipulation must never be executed on raw URLs prior to parsing and structure verification.
Comparing the pre-patched codebase with the updated implementation highlights the structural changes introduced to address this parser differential.
// VULNERABLE: dist/helpers.js (v1.0.3 and prior)
async function is_url_safe(url) {
try {
let u = normalize_unicode(url);
u = replace_backslash_with_slash_in_string(u);
u = replace_two_slashes_url_to_normal_url(u);
u = remove_at_symbol_in_string(u); // Destructive modification of raw URL
const schema = normalize_schema(u);
if (!is_proto_safe(schema)) return false;
const parsed = new URL(u);
const hostname = parsed.hostname.replace(/^\[|\]$/g, "");
if (await is_hostname_resolve_to_internal_ip(hostname)) return false;
return true;
} catch {
return false;
}
}In the patched release (v1.0.4), the destructive raw string modification of the '@' symbol has been completely removed. Instead, the validation library delegates structure parsing directly to a new is_parsed_url_safe function.
// PATCHED: dist/helpers.js (v1.0.4)
async function is_parsed_url_safe(parsed) {
if (!is_proto_safe(parsed.protocol)) return false;
// Explicitly reject URLs containing userinfo structures
if (parsed.username !== "" || parsed.password !== "") return false;
const hostname = parsed.hostname.replace(/^\[|\]$/g, "");
if (await is_hostname_resolve_to_internal_ip(hostname)) return false;
return true;
}
async function is_url_safe(url) {
try {
let u = normalize_unicode(url);
u = replace_backslash_with_slash_in_string(u);
u = replace_two_slashes_url_to_normal_url(u);
const parsed = new URL(u); // Parsed directly on minimally normalized input
if (!await is_parsed_url_safe(parsed)) return false;
return true;
} catch {
return false;
}
}This fix successfully addresses the core design flaw by evaluating the actual structure parsed by Node's URL library and ensuring that credentials blocks are completely rejected, rendering the attack vector non-viable.
To exploit this vulnerability, the target application must rely on dssrf-js version < 1.0.4 and execute requests with the original, unmodified URL string. An attacker must also identify an endpoint that passes input to the security helper before dispatching the payload to the internal infrastructure.
Consider a target application exposing a preview routing service at https://target.app/preview?url=.... An attacker executes the following command to target an internal Redis server running on the loopback adapter:
curl -v "https://target.app/preview?url=http://example.com@127.0.0.1:6379/"The backend server calls is_url_safe('http://example.com@127.0.0.1:6379/'). The library strips the '@' character, yielding http://example.com127.0.0.1:6379/. Since the hostname example.com127.0.0.1 resolves to a public DNS destination or fails, validation returns success. The library then passes the original URL to the HTTP client (e.g., got, axios), which connects directly to 127.0.0.1 on port 6379, completely bypassing the SSRF defense.
The impact of this SSRF bypass is classified as High. An attacker with network access to the vulnerable endpoint can manipulate downstream requests to interact with restricted infrastructure. This includes reaching cloud metadata services (such as AWS IMSv1 at 169.254.169.254), administrative systems, databases, and local daemon sockets.
While the patch successfully blocks userinfo-based bypass attempts, security engineers must recognize that third-party validation wrappers are always prone to residual architectural risks. First, the Time-of-Check to Time-of-Use (TOCTOU) bug class remains. If dssrf-js validates a domain pointing to a safe external IP, a subsequent DNS change can redirect the client's request to an internal network target during connection establishment.
Second, discrepancies in underlying parsing engines remain a concern. If the backend HTTP client interprets complex URL components differently than the Node.js WHATWG parser, attackers may find similar routing anomalies. Applications should rely on network-level segregation and request pinning instead of software-only URL validation wrappers.
The definitive solution is to upgrade dssrf-js to version 1.0.4 or later. This removes the vulnerable string processing helper and enforces strict userinfo validation checks inside the core module.
For systems that cannot be immediately updated, security teams can implement an application-level input validation filter. Any input string containing the '@' character in its authority block should be dropped immediately before invoking dssrf-js logic.
Additionally, host firewalls, cloud security groups, and local Kubernetes NetworkPolicies should be configured to drop egress traffic originating from application processes directed at private subnets or internal metadata targets. This ensures that even if validation is bypassed, network-level routing policies block unauthorized connections.
CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:N/VC:N/VI:H/VA:N/SC:N/SI:N/SA:N| Product | Affected Versions | Fixed Version |
|---|---|---|
dssrf-js HackingRepo | < 1.0.4 | 1.0.4 |
| Attribute | Detail |
|---|---|
| CWE ID | CWE-76: Improper Neutralization of Equivalent Special Elements |
| Attack Vector | Network (AV:N) |
| CVSS v4.0 Score | 8.7 |
| EPSS Score | N/A (New 2026 Vulnerability) |
| Impact | High Integrity Compromise (VI:H) |
| Exploit Status | None (No active public exploits cataloged) |
| KEV Status | False |
The software receives an input that can be represented in multiple equivalent ways, but fails to neutralize all equivalent representations before processing the input.
CVE-2026-67426 is a critical vulnerability in Flyto2 Core prior to version 2.26.7. The standalone flyto-verification service binds to all interfaces (0.0.0.0) on port 8344 and exposes an unauthenticated POST /run endpoint. This endpoint accepts an arbitrary client-controlled callback URL and makes an outbound POST request containing the sensitive internal runner secret in the headers. Attackers can exploit this to retrieve the FLYTO_RUNNER_SECRET and perform Server-Side Request Forgery (SSRF) against internal network targets.
CVE-2026-66066 (popularly known as 'KindaRails2Shell') is a critical security vulnerability in the Active Storage component of Ruby on Rails. The vulnerability arises from an insecure default integration with the libvips image processing library via the ruby-vips gem. Under default configurations, Active Storage fails to restrict untrusted format loaders within libvips, allowing remote, unauthenticated attackers to upload malformed files that leverage external dataset features to read local server files. By extracting cryptographic secrets such as SECRET_KEY_BASE from the leaked file contents, attackers can forge signed Marshal serialization payloads to achieve remote code execution.
A Use-After-Free (UAF) vulnerability exists in msgpack-ruby prior to version 1.8.2. The MessagePack::Buffer#clear method returns the associated 4 KiB rmem page to the shared pool but fails to reset the buffer's tracking pointers (rmem_last, rmem_end, and rmem_owner). Subsequent write operations on the cleared buffer can alias the freed page, allowing concurrent buffers to access, disclose, or corrupt cross-buffer data. This issue is resolved in version 1.8.2.
Flyto2 Core (flyto-core) prior to version 2.26.7 did not utilize its centralized SSRF validation mechanism ('validate_url_with_env_config') across multiple HTTP-emitting modules. This oversight allowed low-privileged users executing automated workflows to perform Server-Side Request Forgery (SSRF) attacks against internal endpoints, loopback interfaces, and cloud provider metadata services.
An SSRF vulnerability exists in Flyto2 Core due to improper validation of intermediate HTTP redirect hops. While the initial request target is validated against an SSRF protection policy, the HTTP client library (aiohttp) transparently follows 30x redirects to local, internal, or cloud metadata endpoints without application-level revalidation.
A logic vulnerability exists in @dynatrace-oss/dynatrace-mcp-server prior to version 1.8.7. The create_dynatrace_notebook tool lacks a human-approval gate, allowing an attacker to exploit indirect prompt injection to force the underlying LLM client to create persistent Dynatrace notebooks without the operator's consent.