Sep 5, 2026·6 min read·1 visit
A Time-of-Check to Time-of-Use (TOCTOU) flaw in CodeWhale's DNS validation logic allows unauthenticated attackers to bypass SSRF protections by intentionally failing the initial DNS resolution and resolving to a restricted IP during the subsequent HTTP request.
A critical Server-Side Request Forgery (SSRF) bypass vulnerability exists in CodeWhale before version 0.8.64 (and version 0.8.41 in the 0.8.x branch) due to a Time-of-Check to Time-of-Use (TOCTOU) bug in its DNS pre-flight validation mechanism. By returning a temporary resolution failure during validation and subsequently resolving to restricted IPs during HTTP execution, attackers can bypass security rules.
CodeWhale, an application developed by Hmbown, includes features to fetch remote content using URLs provided by users. To secure these features, CodeWhale implements a DNS pinning and pre-flight validation mechanism designed to prevent Server-Side Request Forgery (SSRF) attacks. The intended design checks hostnames prior to initiating external HTTP connections, validating their resolved IP addresses against local network restriction policies.
However, before version 0.8.64 (and version 0.8.41 in the legacy branch), the validation engine contained a severe logical flaw in its handling of pre-flight DNS resolution failures. Specifically, if the initial DNS resolution failed, the application silently ignored the failure and permitted the subsequent HTTP connection to proceed. This omission transforms the validation logic into a classic Time-of-Check to Time-of-Use (TOCTOU) vulnerability.
An attacker can exploit this behavior to bypass local network policies. By orchestrating a DNS configuration that fails during the validation check but succeeds during the actual fetch operation, the attacker can force the CodeWhale server to establish connections to internal loopback addresses, local network services, or cloud infrastructure metadata endpoints.
The vulnerability resides in crates/tui/src/tools/fetch_url.rs within the validate_fetch_target function. When validating a destination host, the function attempts to perform a DNS lookup using tokio::net::lookup_host. The underlying implementation is designed to verify that the target IP does not resolve to local loopback ranges, link-local addresses, or private subnets defined in the network policy.
If the DNS resolution succeeds, the application validates the returned IP addresses. If the DNS lookup fails and returns an error (such as std::io::ErrorKind::TimedOut or a temporary server failure), the original code caught the error but failed to abort the transaction. Instead, the function assumed that any hostname failing resolution during the check phase would inevitably fail during the actual request execution phase.
This assumption is fundamentally flawed. An attacker can control the DNS server authoritative for the target domain and respond differently during the pre-flight check and the actual HTTP request. By intentionally triggering a DNS resolution failure (such as returning SERVFAIL or dropping the query) during the validation check, the attacker coaxes the validation function into returning Ok(None), effectively bypassing all security checks.
Let us review the vulnerable implementation compared to the patch introduced in commit 26de44a8bd5051f8f944ea60b2c37ae1d2b7d25e. In the vulnerable code path, the DNS lookup was wrapped inside an if let Ok(addrs) pattern. If lookup_host returned an Err, the entire block was skipped, and Ok(first_valid.map(...)) returned Ok(None), bypassing validation.
// Pre-patch vulnerable implementation logic
let mut first_valid: Option<std::net::IpAddr> = None;
if let Ok(addrs) = tokio::net::lookup_host((host.as_str(), 0u16)).await {
for addr in addrs {
validate_dns_resolved_ip(&host, &addr.ip(), context.network_policy.as_ref())?;
if first_valid.is_none() {
first_valid = Some(addr.ip());
}
}
}
// If DNS resolution fails, the HTTP request was allowed to proceed
Ok(first_valid.map(|validated_ip| (host, validated_ip)))The patch explicitly changes this behavior. The lookup_host call is now chained with .map_err to immediately elevate any resolution failure to a ToolError::permission_denied error, aborting the execution. Furthermore, the patched code enforces that if no addresses are returned, the execution is terminated.
// Patched implementation in crates/tui/src/tools/fetch_url.rs
let addrs = tokio::net::lookup_host((host.as_str(), 0u16))
.await
.map_err(|e| {
ToolError::permission_denied(format!(
"could not resolve host before fetch_url request: {e}"
))
})?;
let mut first_valid: Option<std::net::IpAddr> = None;
for addr in addrs {
validate_dns_resolved_ip(&host, &addr.ip(), context.network_policy.as_ref())?;
if first_valid.is_none() {
first_valid = Some(addr.ip());
}
}
let Some(validated_ip) = first_valid else {
return Err(ToolError::permission_denied(
"host resolved to no addresses before fetch_url request",
));
};
Ok(Some((host, validated_ip)))While this patch successfully addresses the immediate TOCTOU bypass of a failing check, a sophisticated attacker could still leverage DNS Rebinding with short TTLs (Time-To-Live) if the HTTP client library conducts its own independent DNS resolution without using the pinned IP address retrieved during the pre-flight phase. Comprehensive mitigation requires pinning the resolved IP at the socket layer.
Exploiting this vulnerability requires the attacker to control the authoritative DNS server for a domain (e.g., ssrf.attacker.com) and a configuration that returns stateful responses. The attacker schedules the DNS server to behave differently across consecutive queries or implements a temporary denial-of-service/error state.
In step one, the attacker submits an API request to CodeWhale to fetch https://ssrf.attacker.com/metadata. CodeWhale executes the pre-flight validator, which queries ssrf.attacker.com. The attacker's DNS server drops the request or returns SERVFAIL. The validator catches the error, assumes the host is unresolvable, and permits the connection.
In step two, the HTTP client (reqwest) executes the actual GET request. It resolves ssrf.attacker.com once more. This time, the attacker's DNS server returns a valid local address, such as 127.0.0.1 or the AWS metadata endpoint 169.254.169.254. The HTTP client establishes a connection to the internal endpoint, completely bypassing the SSRF filters.
The security implications of an SSRF bypass of this nature are severe. An attacker can leverage the CodeWhale server as an entry point to scan the internal network, access internal APIs, and query local microservices that rely on network perimeter security for authentication.
In cloud environments, this vulnerability permits the retrieval of highly sensitive metadata, such as temporary IAM credentials from the Instance Metadata Service (IMDSv1) at 169.254.169.254. The vulnerability received a CVSS v4.0 base score of 9.2 (Critical), indicating high confidential impact on both the immediate system and subsequent systems (VC:H, SC:H) without requiring user interaction or elevated privileges.
Because the DNS resolution check failed silently, standard application logs would not easily capture the bypass attempt, making detection via basic request logging difficult without specialized network monitoring or DNS queries inspection.
Beyond resolving the TOCTOU SSRF flaw, commit 26de44a8bd5051f8f944ea60b2c37ae1d2b7d25e introduced critical security upgrades to protect local system boundaries. First, the image_analyze tool was hardened against directory traversal via symbolic links. The application previously verified paths syntactically but did not validate the resolution of symbolic links. The patch canonicalizes paths to ensure they stay within the workspace.
Second, the JavaScript execution tool was modified to prevent child scripts from inheriting the parent process's environment variables. This prevents execution containers from reading and leaking sensitive infrastructure secrets, such as API tokens. Users must upgrade to CodeWhale version 0.8.64 (or 0.8.41 on the older branch) to apply these fixes.
If upgrading is delayed, network-level egress filters should be implemented. Firewalls or container network policies must block the CodeWhale process from establishing connections to loopback addresses, private IP ranges (RFC 1918), and cloud metadata endpoints.
CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:N/VC:H/VI:N/VA:N/SC:H/SI:N/SA:N| Product | Affected Versions | Fixed Version |
|---|---|---|
CodeWhale Hmbown | >= 0.8.5, < 0.8.41 | 0.8.41 |
CodeWhale Hmbown | >= 0.8.41, < 0.8.64 | 0.8.64 |
| Attribute | Detail |
|---|---|
| CWE ID | CWE-918: Server-Side Request Forgery (SSRF) |
| Attack Vector | Network |
| CVSS v4.0 Score | 9.2 (Critical) |
| EPSS Score | 0.0037 (Percentile: 30.08%) |
| Impact | High Confidentiality Breach |
| Exploit Status | Proof-of-Concept in tests; no weaponized exploits |
| KEV Status | Not listed |
The web application fetches a resource without sufficiently validating the destination URI, allowing an attacker to coerce the server into sending requests to restricted internal network endpoints.
An argument injection vulnerability in CodeWhale (CVE-2026-75912 / GHSA-c6mw-8xh8-gpq6) allows unauthenticated remote attackers to execute arbitrary option commands on the git binary. By passing malicious command-line flags inside git_blame and git_show tool helper functions, an attacker can bypass typical access controls to read arbitrary local system files via the underlying git process.
SurrealDB prior to version 3.2.0 is vulnerable to an authorization bypass where authenticated users can invoke custom API endpoints belonging to other tenants. This cross-tenant data access occurs because the system fails to validate authorization scope boundaries against request-supplied namespace and database identifiers before executing scripts with elevated definer's rights.
An incorrect authorization vulnerability (CWE-863) in SurrealDB allows authenticated, low-privileged users to execute unauthorized state-modifying queries. This occurs because the database disabled permissions during evaluation of custom PERMISSIONS WHERE predicates to prevent infinite recursion.
SiYuan before v3.7.4 fails to enforce publish-access filters on five filetree path-resolution endpoints, allowing unauthenticated attackers to reconstruct private directory layouts and map document structures.
Prior to version v3.7.4, the SiYuan personal knowledge management system contained a critical logical authorization vulnerability within its database view rendering component. The flaws allowed unauthenticated remote attackers to bypass publish-access filters on databases, exposing sensitive Relation and Rollup cell contents belonging to private or password-protected repositories.
An information disclosure vulnerability exists in SiYuan prior to v3.7.4 due to missing authorization checks on the getEncryptedNotebookStatus API endpoint, allowing unprivileged or anonymous users to enumerate protected notebooks.