SSRF: translate-from-URL fetches arbitrary internal addresses (gui download_with_limit + high_level.translate)
Summary
PDFMathTranslate fetches caller-supplied URLs server-side (translate a document from a link) without validating the URL's scheme or resolved address. A remote user of a deployed instance can make the server issue GET requests to arbitrary internal targets (loopback, private ranges, link-local cloud metadata 169.254.169.254, other internal HTTP services). When the target returns a valid PDF its translated content is returned to the caller, so impact ranges from blind SSRF to read of internal documents.
Affected version: confirmed on commit 6243ca6 (v1.9.12, current main).
Details
Two sinks take a user-controlled URL and fetch it with no host validation:
pdf2zh/gui.py (Gradio "Link" input):
def download_with_limit(url: str, save_path: str, size_limit: int) -> str:
...
with requests.get(url, stream=True, timeout=10) as response:
response.raise_for_status()pdf2zh/high_level.py translate() (core path used by CLI, GUI, and any API/MCP wrapper): any file argument starting with http:///https:// is downloaded:
r = requests.get(file, allow_redirects=True)Neither validates that the URL is http(s) to a public address; there is no private/loopback/link-local/metadata block, and high_level follows redirects (so a public URL can 302 to an internal one).
POC
(available upon request)
Impact
A remote user of a deployed instance can cause the server to request arbitrary internal addresses (internal HTTP services, cloud metadata). Non-PDF responses give a blind SSRF with a status/timing oracle; PDF responses (e.g. an internal document server) are translated and returned, i.e. internal document read. Redirect-following lets a public URL reach internal targets.
A fix PR (scheme + resolved-address validation, redirects re-validated per hop) is attached.
Source: PDFMathTranslate/PDFMathTranslate