🛡️ CVE-2026-67422 — pymdown-extensions

🟠 CVSS 8.0 — High ✅ No Known Exploit CWE-1333 NVD
8.0
CVSS Score
0 Low4 Medium7 High9 Critical10

Description

pymdown-extensions: exponential-backtracking ReDoS in caret, tilde, betterem, and magiclink inline processors

Summary

Four inline processors in pymdown-extensions contain regular expressions with

exponential backtracking. A single untrusted Markdown line under

50 bytes drives markdown.markdown() into unbounded CPU on the rendering thread

(seconds at ~45 bytes, growing exponentially with each added character). All four

fire in the extension's default configuration

and are reachable through the documented public API. The caret/tilde/

betterem blow-up was introduced by the emphasis-pattern rewrite in PR #2547

(first released in 10.13, Dec 2024) — earlier releases used a linear

(.+?) / ([^\s]+?) content group — and is present through 11.0 (latest);

magiclink's host pattern is long-standing and affects effectively all releases.

Likely CWE-1333 (Inefficient Regular Expression Complexity).

This is a distinct issue from CVE-2025-68142 (ReDoS in pymdownx.blocks.caption,

RE_FIG_NUM, fixed in 10.16.1): different extensions, different regexes, and a

different root cause (delimiter-run partition ambiguity rather than a ./\.

typo).

Details

Four regexes share, or closely mirror, a vulnerable shape — an inner group that

can partition a run of the delimiter character into {2,}-sized pieces in

exponentially many ways, wrapped in a lazy +? that must fail before the engine

can give up:

| Extension | Regex | Location (11.0) |

|---|---|---|

| pymdownx.caret (superscript ^…^) | SUP2 | pymdownx/caret.py:56 |

| pymdownx.tilde (subscript ~…~) | SUB2 | pymdownx/tilde.py:55 |

| pymdownx.betterem (underscore _…_) | SMART_UNDER_EM2 (default) | pymdownx/betterem.py:93 |

| pymdownx.magiclink (bare-URL autolink) | RE_LINK | pymdownx/magiclink.py:56 (host at :59) |

pymdownx/caret.py:56 (pymdown-extensions 11.0):

```python

SUP2 = r'(?<!\^)(\^)(?![\^\s])((?:[^\^\s]|\^{2,})+?)(?<![\^\s])(\^)(?!\^)'

```

The content group (?:[^\^\s]|\^{2,})+? matches a run of carets only via the

\^{2,} branch. A run of *k* carets can be split into ≥2-length pieces in

exponentially many combinations; when no caret can serve as a valid closing

delimiter (the trailing (?<![\^\s])(\^) cannot be satisfied), the engine

explores every partition before failing. SUB2 (tilde) and SMART_UNDER_EM2

(betterem) are the same construct for ~ and _. In betterem the default

smart_enable='underscore' routes underscores to SmartUnderscoreProcessor

SMART_UNDER_EM2 (betterem.py:93), which is the default-reachable,

API-exploitable pattern; the non-smart UNDER_EM2 (:69, used only when

smart_enable is asterisk/disable) shares the shape but did not reproduce

through the public markdown.markdown() pipeline on the tested payload, so a fix

and regression test should target SMART_UNDER_EM2.

pymdownx/magiclink.py:59 has the analogous ambiguity in the host portion, where

overlapping character classes let a run of dots be grouped exponentially:

```python

(?:ht|f)tps?://[^_\W][-\w]*(?:\.[-\w.]+)* # host: '\.' and '[-\w.]' inside (?:...)* both match '.'

```

SUP2/SUB2/SMART_UNDER_EM2 are applied at each delimiter occurrence via the

default PatternSequenceProcessor subclasses (pymdownx/util.py); RE_LINK is

applied by MagiclinkPattern (registered unconditionally at priority 85). In all

four cases, rendering markdown.markdown(src, extensions=[ext]) on untrusted

src in default configuration is sufficient to reach the regex.

PoC

Single self-contained script; runs against the pinned release in an ephemeral

env. Non-destructive — the input is ordinary Markdown text; the impact is CPU/time

(a per-render alarm caps each attempt so the script terminates).

```python

import signal

import time

from importlib.metadata import version

import markdown

print(f"# pymdown-extensions {version('pymdown-extensions')} / markdown {version('markdown')}")

CAP = 5.0 # a single render exceeding this is treated as a hang

class Timeout(Exception):

pass

def render(ext, text):

signal.signal(signal.SIGALRM, lambda *_: (_ for _ in ()).throw(Timeout()))

signal.setitimer(signal.ITIMER_REAL, CAP)

t = time.perf_counter()

try:

markdown.markdown(text, extensions=[ext])

return time.perf_counter() - t

except Timeout:

return None

finally:

signal.setitimer(signal.ITIMER_REAL, 0)

# ext -> (malicious builder, benign builder [valid & closed], ramp, hang count)

CASES = {

"pymdownx.caret": (lambda n: "^a" + "^" * n + "b", lambda n: "^" + "a" * n + "^", [24, 30, 36], 44),

"pymdownx.tilde": (lambda n: "~a" + "~" * n + "b", lambda n: "~" + "a" * n + "~", [24, 30, 36], 44),

"pymdownx.betterem": (lambda n: "_a" + "_" * n + "b", lambda n: "_" + "a" * n + "_", [24, 30, 36], 44),

"pymdownx.magiclink": (lambda n: "http://a" + "." * n + " ", lambda n: "

How this vulnerability can be exploited

This issue can be reached over the network, attack complexity is low, an attacker needs no privileges on the target. No user interaction is required. The scope is unchanged, so the impact stays within the vulnerable component. Rated impact: confidentiality none, integrity none, availability high.

Weakness class

CVE-2026-67422 is classified as CWE-1333: Inefficient Regular Expression Complexity. A regular expression backtracks catastrophically on crafted input, consuming CPU out of proportion to input size.

Affected software

CVE-2026-67422 is recorded against 2 packages.

  • pymdown-extensions (fixed in 11.0.1)
  • unknown

Timeline and source

Published on 7 August 2026 and last revised on 11 August 2026. No public exploit is currently recorded for this entry. Record sourced from NVD.

References

github.com (Web)
nvd.nist.gov (Advisory)
github.com (Web)
github.com (Package)

Details

Severity HIGH
CVSS Score 8.0
CVSS Vector CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H
CWE CWE-1333
Public Exploit ✅ No
Source NVD
Published 2026-08-07
Updated 2026-08-12
Modified 2026-08-11
Fix URL N/A

Affected Packages

Software From version Fixed in
pymdown-extensions 11.0.1
unknown

Site Security Check

Is pymdown-extensions part of your stack?

CVE-2026-67422 is rated CVSS 8.0 High. BotEraser scans your installation against known CVE records and tells you whether this vulnerability applies to the versions you actually run.

Scan My Site Free →

No credit card required  ·  Results in minutes

ⓘ Data Notice: The information presented above has been compiled from publicly available internet sources. Boteraser aggregates this data solely for informational purposes and does not independently classify, evaluate, or endorse any findings about the vulnerabilities listed. The accuracy and completeness of this information is the sole responsibility of the original publishers. Boteraser and its operators accept no liability for any decisions made based on this data.