CVE-2026-57947: Verified Repro With Script Download
CVE-2026-57947: Pinpoint through 3.1.0 allows authenticated users to register internal webhook URLs, leading to SSRF when alarm webhooks are delivered.
CVE-2026-57947 is verified against pinpoint-apm/pinpoint · maven. Affected versions: <= 3.1.0 (affected from 0 through 3.1.0). Fixed in Not specified in advisory; GitHub issue milestone 3.1.1; no fixed commit identified. Vulnerability class: SSRF. This medium reproduction includes runnable sandbox proof, artifacts, and a plain-text agent view under REPRO-2026-00253.
What Is CVE-2026-57947?
CVE-2026-57947 is a medium-severity (CVSS 6.3) server-side request forgery (CWE-918) in Pinpoint. An authenticated user can register webhook URLs that resolve to internal hosts or cloud metadata endpoints, and when an alarm fires Pinpoint issues the request. Pruva reproduced it (reproduction REPRO-2026-00253).
CVE-2026-57947 Severity & CVSS Score
CVE-2026-57947 is rated medium severity, with a CVSS base score of 6.3 out of 10.
Medium — meaningful risk under specific conditions. Schedule a fix in the normal cycle.
Affected pinpoint-apm/pinpoint Versions
pinpoint-apm/pinpoint · maven versions <= 3.1.0 (affected from 0 through 3.1.0) are affected.
How to Reproduce CVE-2026-57947
pruva-verify REPRO-2026-00253 curl -O https://pruva.dev/api/v1/reproductions/REPRO-2026-00253/artifacts/bundle/repro/reproduction_steps.sh && chmod +x reproduction_steps.sh && ./reproduction_steps.sh Proof of Reproduction for CVE-2026-57947
- reached the target end-to-end
- on the real production code path
- high confidence
- the upstream fix blocks the same trigger
authenticated user registers an arbitrary webhook URL through /api/webhook or /api/application/webhook
- POST /api/webhook (or /api/application/webhook) stores internal URL; alarm batch job sends HTTP POST to that URL
No distinct SSRF bypass or alternate trigger found for the Pinpoint webhook SSRF vulnerability. The upstream fix (f683a24b4f) blocks all tested private-target variants via registration-time URL validation and send-time DNS-resolution validation.
How the agent worked
Root Cause and Exploit Chain for CVE-2026-57947
Pinpoint 3.1.0 allows any authenticated user (minimum ROLE_USER) to register arbitrary webhook URLs through the /api/webhook and /api/application/webhook endpoints. The URL validation in WebhookController.validateURL() only checks that the supplied string is syntactically a valid URL (new URL(...).toURI()), with no filtering of private IP ranges, loopback addresses, or link-local metadata endpoints. The same lack of SSRF protection exists in the batch alarm sender (WebhookSenderImpl.validateURL()). When an alarm rule fires, the Pinpoint batch server performs a real HTTP POST to every registered webhook URL, so an attacker can coerce the server into sending requests to internal services such as 127.0.0.1 or 169.254.169.254.
- Package/component affected:
maven:com.navercorp.pinpoint:pinpoint-web(alsopinpoint-batch, which contains the alarm sender) - Affected versions: Pinpoint through 3.1.0 (per CVE record and VulnCheck advisory)
- Risk level and consequences: Medium-to-high. A authenticated user can force the Pinpoint server to issue server-side requests to internal network resources, including cloud metadata endpoints (e.g., AWS IMDSv1
http://169.254.169.254/). The webhook payload contains user-group member details, so a controlled internal endpoint can also exfiltrate names, e-mails, and phone numbers. This enables internal port scanning, metadata credential theft, and lateral movement from the Pinpoint server IP.
Impact Parity
- Disclosed/claimed maximum impact: SSRF — the server makes unauthorized outbound HTTP requests to internal/attacker-controlled URLs when alarm thresholds are breached.
- Reproduced impact from this run: The reproduction started the real
pinpointdocker/pinpoint-web:3.1.0container, authenticated as a non-admin user, registered webhooks pointing tohttp://169.254.169.254/...,http://127.0.0.1/..., and a controlled internal listener (http://ssrf-listener:9999/callback). It then started the realpinpointdocker/pinpoint-batch:3.1.0alarm job and observed the batch server make a real HTTP POST to the controlled listener. - Parity:
fullfor the SSRF claim — the server-side request was demonstrated end-to-end across the real product. - Not demonstrated: Actual extraction of cloud metadata credentials, because no live metadata service was present in the sandbox. The listener stands in for the attacker-controlled internal endpoint and proves the server-side request path.
Root Cause
The vulnerable code is in the webhook module of Pinpoint 3.1.0:
webhook/src/main/java/com/navercorp/pinpoint/web/webhook/controller/WebhookController.java(lines 117–120):private void validateURL(Webhook webhook) throws MalformedURLException, URISyntaxException { URL u = new URL(webhook.getUrl()); webhook.setUrl(u.toURI().toString()); }This only validates URL syntax. It does not reject
127.0.0.1,169.254.169.254,10.0.0.0/8, etc.batch-alarmsender/src/main/java/com/navercorp/pinpoint/batch/alarm/sender/WebhookSenderImpl.java(lines 132–135):private String validateURL(String url) throws MalformedURLException, URISyntaxException { URL u = new URL(url); return u.toURI().toString(); }The same missing checks are applied before the alarm sender calls
restTemplate.exchange(validatedUrl, HttpMethod.POST, httpEntity, String.class).
There is no post-DNS guard, no IP allowlist/denylist, and no redirect protection. Any scheme/host/address accepted by java.net.URL is accepted by the application.
The issue was disclosed as GitHub issue #13857 ("Webhook URL missing SSRF protection allows authenticated users to reach internal network resources"). A patched version is not explicitly stated in the advisory, but the issue is closed against the 3.1.1 milestone.
Reproduction Steps
The full reproduction is captured in bundle/repro/reproduction_steps.sh. At a high level it:
- Brings up the real Pinpoint backend (
pinpoint-hbase:3.1.0, MySQL, ZooKeeper, Redis) and the vulnerablepinpoint-web:3.1.0container with basic authentication enabled. - Inserts a fake application row into the HBase
ApplicationIndextable so the alarm batch job recognises the target application. - Starts a controlled internal HTTP listener (
ssrf-listener) inside the same Docker network. - Authenticates as
testuser(a user with onlyROLE_USER) and registers three webhooks:http://169.254.169.254/latest/meta-data/iam/security-credentials/http://127.0.0.1:9999/ssrf-pochttp://ssrf-listener:9999/callback(via the/api/application/webhookvariant)
- Verifies that all three internal URLs are stored in the database via
GET /api/webhook?applicationId=<app>. - Authenticates as
adminuser, creates a user group, and creates an alarm rule (TOTAL COUNT, threshold0) linked to the listener webhook withwebhookSend=true. - Starts the real
pinpoint-batch:3.1.0container withalarmJobenabled and waits for the batch server to deliver a POST to the controlled listener.
Expected evidence:
- Registration responses contain
{"result":"SUCCESS"}and the stored webhook list contains the internal URLs. bundle/logs/ssrf-listener.logshows a POST request fromApache-HttpClient/5.4.4 (Java/17.0.19)to/callback.bundle/logs/ppssrf-batch.logshowsSuccessfully sent webhook : Webhook{...url='http://ssrf-listener:9999/callback'...}.
Evidence
bundle/logs/reproduction_steps.log— full console output of the reproduction, including 401 for unauthenticated access, successful login, webhook registrations, and the listener receiving the POST.bundle/logs/ppssrf-web.log— Pinpoint Web startup logs showing the active Spring profiles (release,basicLogin) and the vulnerablewebhookEnable=trueconfiguration.bundle/logs/ppssrf-batch.log— batch alarm job logs showing theTOTAL COUNTchecker detecting the alarm and the successful webhook send:ResponseCountChecker result is true for application (test-app-...). value is 0. (threshold : 0). Successfully sent webhook : Webhook{webhookId='12'...url='http://ssrf-listener:9999/callback'...}bundle/logs/ssrf-listener.log— the controlled listener records the server-side POST:LISTENER_POST path=/callback headers={...'User-Agent': 'Apache-HttpClient/5.4.4 (Java/17.0.19)'...} LISTENER "POST /callback HTTP/1.1" 200 -bundle/repro/artifacts/stored_webhooks.json— JSON list of stored webhooks, includinghttp://169.254.169.254/...andhttp://127.0.0.1:9999/ssrf-poc.bundle/repro/artifacts/register_metadata.json,register_loopback.json,register_listener.json— each contains{"result":"SUCCESS"}.bundle/repro/artifacts/create_alarm.json— alarm rule created withwebhookSend=trueand linked to the listener webhook.
Recommendations / Next Steps
- Fix approach: Add a strict URL validator that rejects private/reserved/loopback/link-local IP addresses and metadata endpoints both at registration time and in the batch sender. A robust fix should also:
- Resolve the hostname to IP addresses and check the resolved set against an allowlist/denylist (post-DNS guard).
- Disable HTTP redirects or validate redirect targets.
- Optionally require an explicit allowlist of webhook domains/IPs configurable by the operator.
- Upgrade guidance: Upgrade to a patched release > 3.1.0 once available. Until then, restrict access to the
/api/webhookand/api/alarmRuleendpoints and apply network-level egress controls on the Pinpoint server. - Testing recommendations: Regression tests should assert that
127.0.0.1,169.254.169.254,10.0.0.0/8,192.168.0.0/16,172.16.0.0/12, and::1are rejected by the webhook registration endpoint, and that the alarm sender does not send to those addresses even when already stored in the database.
Additional Notes
- Idempotency: The script uses a unique timestamp suffix for the application name, user group, and HBase row qualifier, so repeated runs do not collide with leftover MySQL/HBase state. Two consecutive executions with
REUSE_STACK=1both completed successfully. - Limitations: The reproduction seeds a fake
ApplicationIndexrow in HBase so the alarm batch job recognises the target application. In a real deployment this row is created automatically when a Pinpoint agent reports for the application. Seeding the row is a sandbox-only convenience and does not change the vulnerability path. - Clean-run note: A full clean run requires several minutes for the HBase container to bootstrap. The script is designed to perform that bootstrap automatically when
REUSE_STACKis not set.
Variant Analysis & Alternative Triggers for CVE-2026-57947
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:
Registration-time validation (
WebhookController.validateURL()→WebhookUrlValidator.validate()inwebhook/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
ipaddresslibrary correctly parses IPv4/IPv6 literals including compressed forms, NAT64 translations, and IPv4-mapped IPv6.
- Assumes that any obvious private/loopback/link-local IP literal or blocked hostname (
Send-time DNS validation (
WebhookSenderImpl→WebhookDnsResolverinbatch-alarmsender/src/main/java/com/navercorp/pinpoint/batch/alarm/sender/WebhookDnsResolver.java).- Assumes that the Apache HttpClient 5
webhookRestTemplateis always used for webhook delivery and that its configuredDnsResolveris invoked for every request. - Assumes that blocking at DNS resolution (by throwing
UnknownHostExceptionfor 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.
- Assumes that the Apache HttpClient 5
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:
- 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). - 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]). - 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. - Alternate entry point:
/api/alarmRule/includeWebhooksonly links existing webhook IDs to rules; it does not accept a URL, so it cannot bypass the validator. - 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.
- Package/component affected:
maven:com.navercorp.pinpoint:pinpoint-webandmaven:com.navercorp.pinpoint:pinpoint-batch(same as the parent claim). - Affected versions: Pinpoint through
v3.1.0is vulnerable; the fix is present onmaster(tested atf683a24b4fand latestf59a5468ae) but nov3.1.1release 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.0acceptshttp://127.0.0.1.nip.io/and that the patched code blocks it. TheWebhookDnsResolvermock test shows that the same candidate would be blocked at send time even if it were stored. - Parity:
nonefor 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
- Run
bundle/vuln_variant/reproduction_steps.sh. - The script:
- Locates the Pinpoint source repository (from the project cache or clones it).
- Extracts the patched
WebhookUrlValidatorandWebhookDnsResolverfrom the fixed commitf683a24b4f. - 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.
- Expected evidence:
- The harness prints every private-target candidate as
vulnerable=trueand eitherreg=true(blocked at registration) orsend=BLOCKED(...)(blocked at DNS resolution). - Public controls print as
allowed=true. - The script exits
1with the messageRESULT: no bypass found; patch blocks all private-target variants.
- The harness prints every private-target candidate as
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/masteratf59a5468ae).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
- 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. - 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.
- 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. - Add regression tests for the variant candidates. The existing
WebhookUrlValidatorTestandWebhookDnsResolverTestcover 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.1image or later tag exists. The standalone Java harness exercises the exact patched classes (WebhookUrlValidatorandWebhookDnsResolver) that the product uses. The vulnerable runtime check is optional and depends on theppssrf-webcontainer from the parent reproduction still being present. - Source identity: vulnerable
v3.1.0(e3940e516c), fixedf683a24b4f, latest masterf59a5468ae.
CVE-2026-57947 Reproduction Transcript
The agent's step-by-step process — every tool call, every handoff, the moment the exploit fired.
Full session Replay every step — scrub the timeline or play it back.
docker ps && echo docker available{"cwd":"/data/pruva/runs/37f6347b-a435-4fc6-8a66-89724db7070e","duration_ms":124,"exit_code":0,"stderr":"","stdout":"CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES\ncf8c5fcf1692 wordpress:php8.2-apache \"docker-entrypoint.s…\" 3 minutes ago Up 3 minutes 0.0.0.0:18083->80/tcp, [::]:18083->80/tcp blocksy_test-wp_vuln-1\n3ffa4688f443 wordpress:php8.2-apache \"docker-entrypoint.s…\" 4 minutes ago Up 3 minutes 0.0.0.0:18082->80/tcp, [::]:18082->80/tcp … [truncated]Artifacts and Evidence for CVE-2026-57947
Scripts, logs, diffs, and output captured during the reproduction.
How to Fix CVE-2026-57947
Upgrade pinpoint-apm/pinpoint · maven to Not specified in advisory; GitHub issue milestone 3.1.1; no fixed commit identified or later.
FAQ: CVE-2026-57947
How is CVE-2026-57947 triggered?
Which versions of Pinpoint are affected by CVE-2026-57947?
How can I reproduce CVE-2026-57947?
References for CVE-2026-57947
Authoritative sources for CVE-2026-57947 — official vulnerability databases and the upstream advisory. Pruva's reproduction verifies the issue firsthand; these are the primary records to corroborate it.