# Variant RCA Report: CVE-2026-57947 — Pinpoint Webhook SSRF

## Summary

No distinct bypass or alternate trigger was found for the Pinpoint webhook SSRF vulnerability (CVE-2026-57947). The upstream fix (`f683a24b4f` / `83d1228185` on `master`) replaces the trivial `URL → URI` syntactic check with a hardened `WebhookUrlValidator` at registration time and a custom `WebhookDnsResolver` at send time. The variant analysis tested 27 private-target URL forms (alternative IP encodings, IPv6/NAT64 variants, DNS-rebinding hostnames, zone IDs, and hostname tricks) plus 3 public-control URLs. All private-target candidates were blocked by the patched code; all public controls remained allowed. The existing vulnerable runtime (Pinpoint Web 3.1.0) was also confirmed to accept the bypass-candidate URL `http://127.0.0.1.nip.io/`, showing the candidate would have worked against the unpatched product. The patched source code rejects or blocks every tested variant, so the outcome is a negative variant with a systematic rule-out matrix.

## Fix Coverage / Assumptions

The fix relies on two layers of defense:

1. **Registration-time validation** (`WebhookController.validateURL()` → `WebhookUrlValidator.validate()` in `webhook/src/main/java/com/navercorp/pinpoint/web/webhook/support/WebhookUrlValidator.java`).
   - Assumes that any obvious private/loopback/link-local IP literal or blocked hostname (`localhost`, `*.localhost`, `local`, `*.local`) can be rejected without DNS resolution.
   - Assumes the `ipaddress` library correctly parses IPv4/IPv6 literals including compressed forms, NAT64 translations, and IPv4-mapped IPv6.

2. **Send-time DNS validation** (`WebhookSenderImpl` → `WebhookDnsResolver` in `batch-alarmsender/src/main/java/com/navercorp/pinpoint/batch/alarm/sender/WebhookDnsResolver.java`).
   - Assumes that the Apache HttpClient 5 `webhookRestTemplate` is always used for webhook delivery and that its configured `DnsResolver` is invoked for every request.
   - Assumes that blocking at DNS resolution (by throwing `UnknownHostException` for any non-public resolved address) is sufficient to prevent the TCP connection.
   - Assumes `disableRedirectHandling()` prevents a public endpoint from redirecting the batch server to a private target.

The fix also removes the `UserGroup`/`UserMember` payload from the webhook body, eliminating the secondary data-exfiltration vector.

## Variant / Alternate Trigger

The following candidate variants were tested against the patched code:

1. **Alternative IP literal encodings** for `127.0.0.1`: decimal (`2130706433`), octal (`0177.0.0.1`), hex (`0x7f000001`), short forms (`127.1`, `127.0.1`).
2. **IPv6/NAT64 forms**: `[::1]`, `[0:0:0:0:0:0:0:1]`, `[::127.0.0.1]`, `[::ffff:127.0.0.1]`, `[::ffff:7f00:1]`, `[64:ff9b::7f00:1]`, `[64:ff9b::a9fe:a9fe]`, zone IDs (`[fe80::1%25lo0]`, `[::1%25lo0]`).
3. **DNS-rebinding / hostname tricks**: `127.0.0.1.nip.io`, `127-0-0-1.nip.io`, `127.0.0.1.xip.io`, `example.localhost.com`, `localhost.example.com`.
4. **Alternate entry point**: `/api/alarmRule/includeWebhooks` only links existing webhook IDs to rules; it does not accept a URL, so it cannot bypass the validator.
5. **Public controls**: `http://8.8.8.8/`, `http://example.com/`, `https://[2606:4700:4700::1111]/` (expected to remain allowed).

All private-target variants were blocked. The only candidates that passed registration were hostnames that resolve to private IPs; those were blocked by the `WebhookDnsResolver` before any HTTP request was sent.

## Impact

- **Package/component affected:** `maven:com.navercorp.pinpoint:pinpoint-web` and `maven:com.navercorp.pinpoint:pinpoint-batch` (same as the parent claim).
- **Affected versions:** Pinpoint through `v3.1.0` is vulnerable; the fix is present on `master` (tested at `f683a24b4f` and latest `f59a5468ae`) but no `v3.1.1` release tag was available.
- **Risk level and consequences:** The original vulnerability allowed authenticated users to coerce the server into POSTing to internal services. The tested variants would have the same impact if they bypassed the fix, but none did. Therefore, the residual risk from the tested variants is **none**.

## Impact Parity

- **Disclosed/claimed maximum impact:** SSRF — server-side POST to internal/attacker-controlled URLs when alarms fire, with potential exfiltration of user-group member details.
- **Reproduced impact from this variant run:** No bypass was reproduced. The variant script demonstrates that the unpatched `v3.1.0` accepts `http://127.0.0.1.nip.io/` and that the patched code blocks it. The `WebhookDnsResolver` mock test shows that the same candidate would be blocked at send time even if it were stored.
- **Parity:** `none` for the variant — the variant did not reproduce the SSRF on the fixed code.
- **Not demonstrated:** Actual server-side POST to a private target on the patched code. The patch prevented this for every candidate.

## Root Cause

The same underlying root cause as the parent report: the webhook URL was trusted without validation of its network-level destination. The fix addresses this by:
- Adding a strict, RFC-aware URL/address validator at the registration boundary.
- Adding a post-DNS validator at the network boundary inside the HTTP client.
- Removing sensitive data from the webhook payload.

Because both layers are present, the variant surface is reduced to a hypothetical path that bypasses the custom `DnsResolver` or uses a different HTTP client. No such path was found in the codebase.

## Reproduction Steps

1. Run `bundle/vuln_variant/reproduction_steps.sh`.
2. The script:
   - Locates the Pinpoint source repository (from the project cache or clones it).
   - Extracts the patched `WebhookUrlValidator` and `WebhookDnsResolver` from the fixed commit `f683a24b4f`.
   - Compiles a standalone Java harness that compares the vulnerable `new URL().toURI()` validation against the patched validator and the patched DNS resolver.
   - Tests 27 private-target bypass candidates and 3 public controls.
   - If the vulnerable Pinpoint 3.1.0 web container is still running, it also attempts to register `http://127.0.0.1.nip.io/` to show the unpatched endpoint accepts it.
3. Expected evidence:
   - The harness prints every private-target candidate as `vulnerable=true` and either `reg=true` (blocked at registration) or `send=BLOCKED(...)` (blocked at DNS resolution).
   - Public controls print as `allowed=true`.
   - The script exits `1` with the message `RESULT: no bypass found; patch blocks all private-target variants`.

## Evidence

- `bundle/logs/vuln_variant/reproduction_steps.log` — full console output of the variant script.
- `bundle/logs/vuln_variant/compile.log` — Java compilation output.
- `bundle/logs/vuln_variant/harness.log` — output of the bypass-candidate comparison harness.
- `bundle/vuln_variant/artifacts/source_identity.txt` — tested source refs (`v3.1.0`, `f683a24b4f`, `origin/master` at `f59a5468ae`).
- `bundle/vuln_variant/artifacts/src/webhook/support/WebhookUrlValidator.java` — patched validator source extracted from the fixed commit.
- `bundle/vuln_variant/artifacts/src/batch/alarm/sender/WebhookDnsResolver.java` — patched DNS resolver source extracted from the fixed commit.
- Key harness excerpt (all 27 private-target candidates blocked):
  ```
  Private target candidates: 27
    accepted by vulnerable validator: 27
    blocked by patched validator/DNS: 27
    not blocked by patched code: 0
  Public control URLs: 3
    still allowed by patched code: 3
  RESULT: no bypass found; patch blocks all private-target variants
  ```

## Recommendations / Next Steps

1. **Close the registration-time DNS gap.** Consider resolving hostnames during `WebhookUrlValidator.validate()` and rejecting any host that resolves to a non-public address *before* storing the webhook. This eliminates the "store-but-never-send" edge case and reduces reliance on the custom DNS resolver being invoked.
2. **Add audit/logging for blocked webhook attempts.** Log or alert when an authenticated user attempts to register a private/internal URL, because it may indicate SSRF reconnaissance.
3. **Test alternate HTTP clients.** If the project ever replaces Apache HttpClient 5 with another client (e.g., Java 11 `HttpClient`), ensure the same DNS-resolution-time validation is retained.
4. **Add regression tests for the variant candidates.** The existing `WebhookUrlValidatorTest` and `WebhookDnsResolverTest` cover the basic cases; adding the decimal/octal/hex IP encodings and DNS-rebinding hostnames would harden the test suite against future regressions.

## Additional Notes

- **Idempotency:** The variant script was run twice consecutively and produced identical results both times. It uses unique suffixes for any runtime webhook registration attempt.
- **Limitations:** The fixed version was tested at the source level because no `pinpointdocker/pinpoint-web:3.1.1` image or later tag exists. The standalone Java harness exercises the exact patched classes (`WebhookUrlValidator` and `WebhookDnsResolver`) that the product uses. The vulnerable runtime check is optional and depends on the `ppssrf-web` container from the parent reproduction still being present.
- **Source identity:** vulnerable `v3.1.0` (`e3940e516c`), fixed `f683a24b4f`, latest master `f59a5468ae`.
