Логотип exploitDog
Консоль
Логотип exploitDog

exploitDog

github логотип

GHSA-cj54-hpcc-gj6h

Опубликовано: 31 июл. 2026
Источник: github
Github: Прошло ревью
CVSS4: 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.

Пакеты

Наименование

thumbor

pip
Затронутые версииВерсия исправления

<= 7.7.7

7.8.0

EPSS

Процентиль: 28%
0.00357
Низкий

8.7 High

CVSS4

Дефекты

CWE-22

Связанные уязвимости

ubuntu
8 дней назад

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.

nvd
8 дней назад

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.

debian
8 дней назад

Thumbor is an open-source photo thumbnail service by globo.com. Prior ...

EPSS

Процентиль: 28%
0.00357
Низкий

8.7 High

CVSS4

Дефекты

CWE-22