Bug Description
The application allows outbound webhooks. To prevent Server-Side Request Forgery (SSRF), it checks isSafeUrl(), which performs a DNS lookup to ensure the IP isn't internal (e.g., 127.0.0.1, AWS Metadata 169.254.169.254). However, after passing the check, fetch(webhook.url) is called. This forces a second DNS resolution.
This introduces a classic Time-of-Check to Time-of-Use (TOCTOU) vulnerability. An attacker can set up a DNS server with an extremely low TTL. The first request (the check) returns a safe, public IP. The second request (the fetch) returns an internal IP (like 169.254.169.254). The attacker can then exfiltrate the serverless container's IAM roles, internal network topology, or hit internal databases.
Steps to Reproduce
- Register an outbound webhook with a URL pointing to an attacker-controlled DNS server (e.g.,
http://rebind.attacker.com).
- Configure the DNS server to return
8.8.8.8 on the first resolution, and 169.254.169.254 on the second resolution.
- Trigger the webhook dispatch.
- Observe that the SSRF check passes, but the subsequent fetch hits the internal AWS Metadata IP or local services.
Affected Area
API Routes
Screenshots
No response
Browser & OS
No response
Environment
Both
Additional Context
Suggested Fix:
- Modify
isSafeUrl to return the resolved, safe IP address instead of a boolean.
- Pass this exact IP address directly to the
fetch call.
- Override the
Host header in the fetch options to match the original URL's hostname so the target server processes it correctly, ensuring the IP used is explicitly pinned.
Bug Description
The application allows outbound webhooks. To prevent Server-Side Request Forgery (SSRF), it checks
isSafeUrl(), which performs a DNS lookup to ensure the IP isn't internal (e.g.,127.0.0.1, AWS Metadata169.254.169.254). However, after passing the check,fetch(webhook.url)is called. This forces a second DNS resolution.This introduces a classic Time-of-Check to Time-of-Use (TOCTOU) vulnerability. An attacker can set up a DNS server with an extremely low TTL. The first request (the check) returns a safe, public IP. The second request (the
fetch) returns an internal IP (like169.254.169.254). The attacker can then exfiltrate the serverless container's IAM roles, internal network topology, or hit internal databases.Steps to Reproduce
http://rebind.attacker.com).8.8.8.8on the first resolution, and169.254.169.254on the second resolution.Affected Area
API Routes
Screenshots
No response
Browser & OS
No response
Environment
Both
Additional Context
Suggested Fix:
isSafeUrlto return the resolved, safe IP address instead of a boolean.fetchcall.Hostheader in thefetchoptions to match the original URL's hostname so the target server processes it correctly, ensuring the IP used is explicitly pinned.