PYSEC-2026-3619

See a problem?
Import Source
https://github.com/pypa/advisory-database/blob/main/vulns/thumbor/PYSEC-2026-3619.yaml
JSON Data
https://api.osv.dev/v1/vulns/PYSEC-2026-3619
Aliases
Published
2026-08-04T11:34:46.338544Z
Modified
2026-08-04T14:30:17.900502764Z
Severity
  • 7.5 (High) CVSS_V3 - CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H CVSS Calculator
Summary
Thumbor convolution filter allows divide-by-zero in C extension leading to remote DoS
Details

Summary

Thumbor's filters:convolution(<matrix>, <columns>, <should_normalize>) filter passes the user-controlled <columns> value to a C extension (thumbor/ext/filters/_convolution.c) where it is used as a divisor (for % and /) without validating columns > 0. When columns=0, the C code triggers undefined behavior; on x8664 this reliably results in a fatal divide-by-zero trap (SIGFPE) and crashes the Thumbor process (confirmed on Linux x8664 and macOS Intel x86_64), causing a remote denial of service.

Details

Root cause

The Python filter accepts columns=0, and the native C extension uses columns_count as a divisor without validating it.

1) Python filter entry point allows columns=0 (thumbor/filters/convolution.py):

@filter_method(
    r"(?:[-]?[\d]+\.?[\d]*[;])*(?:[-]?[\d]+\.?[\d]*)",
    BaseFilter.PositiveNumber,  # accepts 0
    BaseFilter.Boolean,
)
async def convolution(self, matrix, columns, should_normalize=True):
    ...
    imgdata = _convolution.apply(..., matrix, columns, should_normalize)

BaseFilter.PositiveNumber matches "0" (thumbor/filters/__init__.py):

class BaseFilter:
    PositiveNumber = {"regex": r"[\d]+", "parse": int}  # matches "0"
    PositiveNonZeroNumber = {"regex": r"[\d]*[1-9][\d]*", "parse": int}

2) C extension divides/modulos by columns_count without a zero check (thumbor/ext/filters/_convolution.c):

kernel_size = PyTuple_Size(kernel_tuple);
if ((kernel_size % columns_count != 0) ||
    (kernel_size % 2 == 0) ||
    ((kernel_size / columns_count) % 2) == 0) {
    // TODO: error, not a valid kernel
    return NULL;
}

PoC

Test environment

  • Linux x86_64

Preconditions

  • The convolution filter is enabled (it is enabled by default via BUILTIN_FILTERS).
  • Either:
    • /unsafe/ URLs are allowed (ALLOW_UNSAFE_URL=True), OR
    • /unsafe/ is disabled, and the attacker has a valid signed URL (i.e., the attacker is an authorized user/partner, or can obtain signed URLs from a trusted signing service).

Example request (signed URL)

http://<host>:<port>/<url-sign>/400x400/filters:convolution(1;2;1;2;4;2;1;2;1,0,true)/example.jpg

Example request (/unsafe/)

http://<host>:<port>/unsafe/400x400/filters:convolution(1;2;1;2;4;2;1;2;1,0,true)/example.jpg

Impact

  • Remote Denial of Service via process crash (SIGFPE) on x8664 (confirmed on Linux x8664 and macOS Intel x86_64).
  • Exploitability depends on deployment:
    • If /unsafe/ is enabled: unauthenticated remote DoS.
    • If /unsafe/ is disabled: the attacker needs a valid signed URL.(i.e., the attacker is an authorized user/partner, or can obtain signed URLs from a trusted signing service)

Suggested remediation

  • In the C extension (thumbor/ext/filters/_convolution.c):
    • Reject columns_count <= 0 before any % or /.
  • In the Python filter (thumbor/filters/convolution.py):
    • Require columns to be non-zero (e.g., use BaseFilter.PositiveNonZeroNumber).
References

Affected packages

PyPI / thumbor

Package

Affected ranges

Type
ECOSYSTEM
Events
Introduced
0Unknown introduced version / All previous versions are affected
Fixed
7.8.0

Affected versions

4.*
4.1.3
4.4.1
4.5.3
4.5.4
4.6.0
4.7.0
4.7.1
4.8.0
4.8.1
4.8.2
4.8.3
4.8.4
4.8.5
4.8.6
4.9.0
4.9.1
4.10.0
4.10.1
4.10.2
4.10.3
4.11.0
4.11.1
4.12.0
4.12.1
4.12.2
5.*
5.0.0rc1
5.0.0rc2
5.0.0
5.0.1
5.0.2
5.0.3
5.0.4
5.0.5
5.0.6
5.1.0
5.2.0
5.2.1
6.*
6.0.0b1
6.0.0b2
6.0.0b3
6.0.0b4
6.0.0b5
6.0.0
6.0.1
6.0.2
6.1.0
6.1.1
6.1.2
6.1.3
6.1.4
6.1.5
6.2.0
6.2.1
6.3.0
6.3.1
6.3.2
6.4.0
6.4.1
6.4.2
6.4.3
6.5.0
6.5.1
6.5.2
6.6.0
6.6.1
6.7.0
6.7.1
6.7.2
6.7.3
6.7.4
6.7.5
6.7.6
7.*
7.0.0a1
7.0.0a2
7.0.0a3
7.0.0a4
7.0.0a5
7.0.0b1
7.0.0
7.0.1
7.0.2
7.0.3
7.0.5
7.0.6
7.0.7
7.0.8
7.0.9
7.0.10
7.0.11
7.0.12
7.1.0
7.1.1
7.1.2
7.2.0
7.2.1
7.3.0
7.3.1
7.3.2
7.4.0
7.4.1
7.4.2
7.4.3
7.4.4
7.4.5
7.4.6
7.4.7
7.5.0
7.5.1
7.5.2
7.6.0
7.7.0
7.7.1
7.7.2
7.7.3
7.7.4
7.7.5
7.7.6
7.7.7

Database specific

source
"https://github.com/pypa/advisory-database/blob/main/vulns/thumbor/PYSEC-2026-3619.yaml"