{"id":"CVE-2026-53500","aliases":["GHSA-6x26-6r6f-m537","PYSEC-2026-3617"],"url":"https://o3.security/vulnerability/CVE-2026-53500","summary":"Thumbor treats ALLOWED_SOURCES string patterns as unescaped regex, allowing hostname bypass via wildcard dot","details":"## Summary\n\nThe `ALLOWED_SOURCES` configuration is meant to restrict which hosts Thumbor's HTTP loader may fetch images from. Plain-string entries in that list (the overwhelming majority of real-world and documented configurations) are passed directly to `re.match()` without escaping. Because `.` is a regex wildcard, every dot in a domain name becomes a bypass vector: `s.glbimg.com` silently matches `sXglbimgYcom`, `sAglbimg.com`, and any other hostname that differs only at a dot position. This undermines the primary SSRF defence that `ALLOWED_SOURCES` is intended to provide.\n\n## Affected component\n\n`thumbor/loaders/http_loader.py` — `validate()`\n\n## Proof of concept\n\n```python\nimport re\nfrom thumbor.config import Config\nfrom thumbor.context import Context\nfrom thumbor.loaders import http_loader as loader\n\nconfig = Config()\nconfig.ALLOWED_SOURCES = [\"s.glbimg.com\"]   # typical user config\nctx = Context(None, config, None)\n\n# These should be blocked — both return True due to the unescaped dot\nprint(loader.validate(ctx, \"http://sXglbimgYcom/secret.jpg\"))  # True ← bypass\nprint(loader.validate(ctx, \"http://sAglbimg.com/secret.jpg\"))  # True ← bypass\n\n# Legitimate origin — correctly allowed\nprint(loader.validate(ctx, \"http://s.glbimg.com/logo.jpg\"))    # True ← correct\n```\n\n## Root cause\n\n`thumbor/loaders/http_loader.py` (before fix):\n\n```python\nfor pattern in context.config.ALLOWED_SOURCES:\n    if isinstance(pattern, Pattern):\n        match = url\n    else:\n        pattern = f\"^{pattern}$\"   # <-- dots not escaped, act as regex wildcard\n        match = res.hostname\n\n    if re.match(pattern, match):\n        return True\n```\n\n## Impact\n\nAn attacker who can influence the image source URL passed to Thumbor can fetch images from arbitrary hosts, bypassing the `ALLOWED_SOURCES` allowlist.\n\n**Preconditions:**\n\n- `ALLOWED_SOURCES` contains at least one plain-string entry (the common case; all official documentation examples use plain strings).\n- The attacker can supply or influence the image URL — true whenever  `ALLOW_UNSAFE_URL = True` (the default), or when the application forwards user input to a signed URL endpoint.\n\n## Fix\n\nApply `re.escape()` to plain-string patterns before compiling them, so every\ncharacter is matched literally:\n\n```python\nelse:\n    pattern = f\"^{re.escape(pattern)}$\"   # dots and other metacharacters are now literal\n    match = res.hostname\n```\n\nThis is a one-call addition with no breaking change for correctly written configurations. Users who need real regular-expression behaviour should supply a compiled pattern (`re.compile(r\"s\\.glbimg\\.com\")`), which is already handled by the existing `isinstance(pattern, Pattern)` branch and is unaffected by this change.\n\nThe `ALLOWED_SOURCES` docstring in `config.py` was also updated to document the two-mode behaviour explicitly.","published":"2026-07-31T18:35:54.234Z","modified":"2026-10-28T02:30:44.335059274Z","cvss":{"score":8.2,"severity":"HIGH","vector":"CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:N/A:L"},"epss":{"score":0.00287,"percentile":0.21286,"asOf":"2026-09-16"},"cisaKev":null,"exploitsKnown":0,"affectedPackages":[{"ecosystem":"PyPI","name":"thumbor","fixedVersion":"7.8.0"}],"fix":{"url":"https://github.com/thumbor/thumbor/commit/68876715350c6c8f49c324e5515e64908830aed7","label":"thumbor/thumbor@6887671"},"references":[{"type":"WEB","url":"https://github.com/thumbor/thumbor/releases/tag/7.8.0"},{"type":"ADVISORY","url":"https://github.com/CVEProject/cvelistV5/tree/main/cves/2026/53xxx/CVE-2026-53500.json"},{"type":"ADVISORY","url":"https://github.com/thumbor/thumbor/security/advisories/GHSA-6x26-6r6f-m537"},{"type":"ADVISORY","url":"https://nvd.nist.gov/vuln/detail/CVE-2026-53500"},{"type":"FIX","url":"https://github.com/thumbor/thumbor/commit/68876715350c6c8f49c324e5515e64908830aed7"},{"type":"PACKAGE","url":"https://github.com/thumbor/thumbor"}],"provenance":{"sources":["OSV.dev","FIRST.org (EPSS)"],"lastVerified":"2026-10-28T02:30:44.335059274Z"}}