An SSRF exploit aimed at the AWS metadata endpoint at 169.254.169.254 succeeded in an early-access deployment last quarter. A WAF sat in front of the application and had rules for metadata endpoint patterns in query parameters. The SSRF instead used a URL-encoded redirect chain built server-side from a user-controlled input field, rather than a direct parameter. The WAF did not see the final destination. The kernel's connect() syscall did.
SSRF (Server-Side Request Forgery, CWE-918) describes a vulnerability in which an attacker induces a server to make outbound HTTP or TCP connections to unintended destinations. These commonly include cloud instance metadata services, internal network segments inaccessible from the public internet, or localhost services on the application host. Application and network perimeter detection can be systematically evaded. Kernel network socket-layer detection cannot.
Why SSRF Evades Perimeter Detection
WAFs and application-layer controls examine HTTP requests as they reach the application. To block SSRF at this point, the malicious destination must appear in the inbound request during inspection. Several widespread SSRF methods prevent that:
Server-side URL assembly
The most frequent pattern is for an application to accept a user-provided identifier or path component and use it to build an internal URL on the server. The inbound request contains only the identifier. The application assembles the final destination after the WAF inspection point. Unless the WAF knows the application's URL construction logic, which it does not, it cannot assess that destination.
DNS rebinding
An attacker registers a controlled domain and assigns its A record an extremely low TTL. The application resolves the domain, receives a legitimate-looking IP address, and the WAF permits the connection because the address appears benign. After that resolution check, the attacker changes the DNS record to 169.254.169.254 or an internal IP. Depending on DNS caching and the application's retry behavior, the actual TCP connect() can reach the rebound destination.
HTTP redirect chains
When the URL fetched by the application returns an HTTP 301 or 302 redirect, the application follows it. The WAF examines the initial URL, which may pass inspection. It never sees the redirected URL pointing to the internal destination unless configured to follow redirects for every outbound HTTP request from the application, an expensive and uncommon setup.
IPv6 and encoded URL variants
Rules for "169.254.169.254" miss decimal variants such as 2852039166, the same IP's decimal encoding, IPv6 link-local forms such as fe80::, and percent-encoded forms. Application-layer string matching is a whack-a-mole contest across a broad encoding space.
Socket Layer View
Every evasion method above shares one requirement: regardless of how the destination was assembled or which intermediaries participated, the TCP connection must eventually invoke connect() on a socket in the process making the outbound request. The Linux kernel handles the connect() syscall after DNS resolution, redirect following, and URL decoding. The final resolved destination IP is the sockaddr argument supplied to connect().
An eBPF kprobe attached to the tcp_connect kernel function, or to the socket connect syscall entry, receives the sockaddr structure with the destination address and port in binary form. No string encoding can bypass it, and no redirect needs to be followed. The kernel observes the actual destination of the TCP connection.
eBPF SSRF Detection Hook Points
Relevant eBPF kernel functions for SSRF detection:
/* Attach here to see all TCP connect attempts with destination */
kprobe/tcp_connect
/* Or at the syscall level if you prefer the syscall boundary */
tracepoint/syscalls/sys_enter_connect
At tcp_connect, the probe gets a struct sock pointer, exposing sk_daddr, the destination IPv4 address as a 32-bit integer, and sk_dport. For IPv6, fields are sk_v6_daddr and sk_dport. PID, PPID, and comm come from the current task struct via bpf_get_current_task().
Detection at this layer is direct: compare the destination address with ranges designated internal or disallowed for the workload. In cloud environments, these include the link-local metadata service (169.254.0.0/16), RFC 1918 private ranges (10.0.0.0/8, 172.16.0.0/12, 192.168.0.0/16) when they should not be reachable from the monitored service, and localhost (127.0.0.0/8).
A complete eBPF SSRF detector also associates each connect() event with the process that initiated it. An application server process, such as a Java web application, connecting to 169.254.169.254 is almost certainly SSRF. The same connection from a monitoring agent is expected. Process-aware allowlisting limits false positives while preserving detection coverage.
SSRF Detection in a Real Scenario
Consider a web application that accepts a "webhook URL" from users and sends an outbound HTTP request to that URL to deliver event payloads. The application checks that the URL begins with "https://" and resolves to a public IP address during validation. An attacker creates a domain that initially resolves to a public IP and passes validation, then changes its DNS record to resolve to 169.254.169.254 in a DNS rebinding attack.
When asynchronous webhook delivery occurs, the DNS record has been rebound. The application resolves the domain again for the delivery request, receives 169.254.169.254, and calls connect(). WAF inspection took place during webhook registration. The application-layer check also occurred then. Neither detects the delivery-time SSRF.
At the kernel socket layer, the eBPF program at tcp_connect runs as soon as connect() is called. It sees 169.254.169.254, matches the address against the disallowed range, and raises an alert, or can optionally block the connection by returning a non-zero value from the eBPF program to signal rejection. Process context identifies the Java application server PID and comm. The timing aligns with an outbound webhook delivery event, giving the alert enough context to start a complete incident trace.
Coverage and Limits of This Approach
Kernel socket-layer SSRF detection covers every TCP connection, regardless of how the application assembled its destination. URL encoding, redirect chains, and DNS rebinding cannot evade it because the connection is observed after all those steps finish.
The limits are important. This method detects outbound connections to disallowed IP ranges, but does not by itself establish whether attacker-controlled input or a configuration change triggered the connection. Process correlation and behavioral baselining, where normal application processes do not connect to 169.254.169.254, significantly lower false positives. Each alert still requires investigation to distinguish exploitation from misconfiguration.
When SSRF targets internal hostnames instead of known IP ranges, detection combines socket-level observation with knowledge of the internal services accessible from the monitored workload. A known IP can be added to the disallowed range list. For an unknown IP discovered through internal DNS, detection relies on deviation from the behavioral baseline rather than IP range matching.
Less common but possible UDP-based SSRF requires attaching to the UDP send path instead of tcp_connect. The principle is the same, but the attachment point differs.