CVE-2026-61632
PyMdown Extensions: Path traversal in the b64 extension lets <img src> read files outside base_path
PyMdown Extensions is a set of extensions for the Python-Markdown markdown project. In versions up to and including 10.21.3, the b64 extension is vulnerable to a path traversal that discloses arbitrary files: it inlines images referenced by <img src="..."> by joining the src onto the configured base_path with os.path.normpath and opening the result directly, without verifying that the resolved path stays inside base_path. As a result, an src containing ../ sequences or an absolute path reads a file outside base_path as long as it has an allowed image extension (.png, .jpg, .jpeg, .gif, .svg), and the file's contents are then base64-encoded into the rendered output, disclosing them. An application that renders untrusted Markdown with pymdownx.b64 enabled can therefore leak the contents of image-extension files readable by the process to whoever controls the Markdown or views the output, a targeted file-read bounded by the extension check. This issue has been fixed in version 11.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
- Low
- Integrity
- None
- Availability
- None
Exploit likelihood
What each source reports
CVE List Score used
5.3
PyMdown Extensions: Path traversal in the b64 extension lets <img src> read files outside base_path
PyMdown Extensions is a set of extensions for the Python-Markdown markdown project. In versions up to and including 10.21.3, the b64 extension is vulnerable to a path traversal that discloses arbitrary files: it inlines images referenced by <img src="..."> by joining the src onto the configured base_path with os.path.normpath and opening the result directly, without verifying that the resolved path stays inside base_path. As a result, an src containing ../ sequences or an absolute path reads a file outside base_path as long as it has an allowed image extension (.png, .jpg, .jpeg, .gif, .svg), and the file's contents are then base64-encoded into the rendered output, disclosing them. An application that renders untrusted Markdown with pymdownx.b64 enabled can therefore leak the contents of image-extension files readable by the process to whoever controls the Markdown or views the output, a targeted file-read bounded by the extension check. This issue has been fixed in version 11.0.
- Record
CVE-2026-61632- CVSS 3.1
- 5.3 Medium
CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:L/I:N/A:N - Weakness
- CWE-22
- Updated
- 2026-11-02
EUVDEUVD-2026-54149
5.3
PyMdown Extensions is a set of extensions for the Python-Markdown markdown project. In versions up to and including 10.21.3, the b64 extension is vulnerable to a path traversal that discloses arbitrary files: it inlines images referenced by <img src="..."> by joining the src onto the configured base_path with os.path.normpath and opening the result directly, without verifying that the resolved path stays inside base_path. As a result, an src containing ../ sequences or an absolute path reads a file outside base_path as long as it has an allowed image extension (.png, .jpg, .jpeg, .gif, .svg), and the file's contents are then base64-encoded into the rendered output, disclosing them. An application that renders untrusted Markdown with pymdownx.b64 enabled can therefore leak the contents of image-extension files readable by the process to whoever controls the Markdown or views the output, a targeted file-read bounded by the extension check. This issue has been fixed in version 11.0.
- Record
EUVD-2026-54149- CVSS 3.1
- 5.3 Medium
CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:L/I:N/A:N - Updated
- 2026-11-02
GitHub AdvisoriesGHSA-9xwg-3r6f-jcx2
5.3
PyMdown Extensions: Path traversal in the b64 extension lets <img src> read files outside base_path
Summary
The b64 extension inlines images referenced by <img src="..."> as base64 data URIs. When resolving the src path it joins it onto the configured base_path with os.path.normpath and opens the result directly, with no check that the resolved path stays inside base_path. A src containing ../ sequences, or an absolute path, therefore reads a file outside base_path as long as that file has an allowed image extension (.png, .jpg, .jpeg, .gif, .svg). The base64 of that file is then embedded in the rendered output, disclosing its contents.
This is a separate code path from the snippets traversal issues (GHSA-jh85-wwv9-24hv, GHSA-62q4-447f-wv8h). It lives in pymdownx/b64.py and has no path restriction of any kind. Confirmed on 10.21.3 installed from PyPI.
Details
In pymdownx/b64.py, function repl_path (around lines 68 to 90 on main):
if is_absolute:
file_name = os.path.normpath(path) # absolute src: base_path ignored entirely
else:
file_name = os.path.normpath(os.path.join(base_path, path)) # relative src: '../' escapes base_path
if os.path.exists(file_name):
ext = os.path.splitext(file_name)[1].lower()
for b64_ext in file_types:
if ext in b64_ext:
with open(file_name, "rb") as f: # opened with no containment check
...
There is no startswith(base_path), no os.path.realpath comparison, and no rejection of ... Both branches are reachable from an attacker-controlled src.
PoC
Reproduced against an unmodified pymdown-extensions==10.21.3 from PyPI. The script creates a base_path directory and a PNG one level above it, then renders Markdown whose image src points outside base_path, and confirms the outside file's bytes appear base64-encoded in the output.
import base64, os, shutil, tempfile, markdown
root = tempfile.mkdtemp()
base_path = os.path.join(root, "docs"); os.makedirs(base_path)
outside = os.path.join(root, "secret"); os.makedirs(outside)
png = base64.b64decode(
"iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVR42mNk"
"+M9QDwADhgGAWjR9awAAAABJRU5ErkJggg=="
)
with open(os.path.join(outside, "secret.png"), "wb") as f:
f.write(png)
md = markdown.Markdown(
extensions=["pymdownx.b64"],
extension_configs={"pymdownx.b64": {"base_path": base_path}},
)
html = md.convert('<img src="../secret/secret.png">')
assert base64.b64encode(png).decode() in html, "not leaked"
print("LEAKED:", html)
Output:
LEAKED: <p><img src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQ..."></p>
The base64 of a file outside base_path is present in the output. The absolute-path branch behaves the same way: an absolute src bypasses base_path entirely via os.path.normpath(path). Both were confirmed leaking.
Impact
An application that renders untrusted Markdown with pymdownx.b64 enabled exposes the contents of image-extension files on the server, or any path the process can read, to whoever controls the Markdown and whoever views the output. The reach is bounded by the image-extension check, so it is a targeted file read rather than full arbitrary read, but it still discloses file contents that were never meant to be exposed.
Suggested fix
Resolve the real path and require it to stay within base_path before opening:
file_name = os.path.realpath(os.path.join(base_path, path))
base_real = os.path.realpath(base_path)
if file_name != base_real and not file_name.startswith(base_real + os.sep):
return m.group(0) # leave the tag untouched; do not read outside base_path
The same containment check should apply to the absolute-path branch rather than trusting an absolute src. Using realpath instead of abspath also closes the related symlink-following gap in the snippets handler.
- Record
GHSA-9xwg-3r6f-jcx2- CVSS 3.1
- 5.3 Medium
CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:L/I:N/A:N - Weakness
- CWE-22
- Updated
- 2026-11-02
NVD
5.3
PyMdown Extensions is a set of extensions for the Python-Markdown markdown project. In versions up to and including 10.21.3, the b64 extension is vulnerable to a path traversal that discloses arbitrary files: it inlines images referenced by <img src="..."> by joining the src onto the configured base_path with os.path.normpath and opening the result directly, without verifying that the resolved path stays inside base_path. As a result, an src containing ../ sequences or an absolute path reads a file outside base_path as long as it has an allowed image extension (.png, .jpg, .jpeg, .gif, .svg), and the file's contents are then base64-encoded into the rendered output, disclosing them. An application that renders untrusted Markdown with pymdownx.b64 enabled can therefore leak the contents of image-extension files readable by the process to whoever controls the Markdown or views the output, a targeted file-read bounded by the extension check. This issue has been fixed in version 11.0.
- Record
CVE-2026-61632- CVSS 3.1
- 5.3 Medium
CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:L/I:N/A:N - Weakness
- CWE-22
- Updated
- 2026-11-02
OSV
5.3
PyMdown Extensions: Path traversal in the b64 extension lets <img src> read files outside base_path
PyMdown Extensions is a set of extensions for the Python-Markdown markdown project. In versions up to and including 10.21.3, the b64 extension is vulnerable to a path traversal that discloses arbitrary files: it inlines images referenced by <img src="..."> by joining the src onto the configured base_path with os.path.normpath and opening the result directly, without verifying that the resolved path stays inside base_path. As a result, an src containing ../ sequences or an absolute path reads a file outside base_path as long as it has an allowed image extension (.png, .jpg, .jpeg, .gif, .svg), and the file's contents are then base64-encoded into the rendered output, disclosing them. An application that renders untrusted Markdown with pymdownx.b64 enabled can therefore leak the contents of image-extension files readable by the process to whoever controls the Markdown or views the output, a targeted file-read bounded by the extension check. This issue has been fixed in version 11.0.
- Record
CVE-2026-61632- CVSS 3.1
- 5.3 Medium
CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:L/I:N/A:N - Weakness
- CWE-22
- Updated
- 2026-11-02
OSVDEBIAN-CVE-2026-61632
5.3
PyMdown Extensions is a set of extensions for the Python-Markdown markdown project. In versions up to and including 10.21.3, the b64 extension is vulnerable to a path traversal that discloses arbitrary files: it inlines images referenced by <img src="..."> by joining the src onto the configured base_path with os.path.normpath and opening the result directly, without verifying that the resolved path stays inside base_path. As a result, an src containing ../ sequences or an absolute path reads a file outside base_path as long as it has an allowed image extension (.png, .jpg, .jpeg, .gif, .svg), and the file's contents are then base64-encoded into the rendered output, disclosing them. An application that renders untrusted Markdown with pymdownx.b64 enabled can therefore leak the contents of image-extension files readable by the process to whoever controls the Markdown or views the output, a targeted file-read bounded by the extension check. This issue has been fixed in version 11.0.
- Record
DEBIAN-CVE-2026-61632- CVSS 3.1
- 5.3 Medium
CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:L/I:N/A:N - Updated
- 2026-11-02
Show 6 moreShow less
OSVGHSA-9xwg-3r6f-jcx2
5.3
PyMdown Extensions: Path traversal in the b64 extension lets <img src> read files outside base_path
Summary
The b64 extension inlines images referenced by <img src="..."> as base64 data URIs. When resolving the src path it joins it onto the configured base_path with os.path.normpath and opens the result directly, with no check that the resolved path stays inside base_path. A src containing ../ sequences, or an absolute path, therefore reads a file outside base_path as long as that file has an allowed image extension (.png, .jpg, .jpeg, .gif, .svg). The base64 of that file is then embedded in the rendered output, disclosing its contents.
This is a separate code path from the snippets traversal issues (GHSA-jh85-wwv9-24hv, GHSA-62q4-447f-wv8h). It lives in pymdownx/b64.py and has no path restriction of any kind. Confirmed on 10.21.3 installed from PyPI.
Details
In pymdownx/b64.py, function repl_path (around lines 68 to 90 on main):
if is_absolute:
file_name = os.path.normpath(path) # absolute src: base_path ignored entirely
else:
file_name = os.path.normpath(os.path.join(base_path, path)) # relative src: '../' escapes base_path
if os.path.exists(file_name):
ext = os.path.splitext(file_name)[1].lower()
for b64_ext in file_types:
if ext in b64_ext:
with open(file_name, "rb") as f: # opened with no containment check
...
There is no startswith(base_path), no os.path.realpath comparison, and no rejection of ... Both branches are reachable from an attacker-controlled src.
PoC
Reproduced against an unmodified pymdown-extensions==10.21.3 from PyPI. The script creates a base_path directory and a PNG one level above it, then renders Markdown whose image src points outside base_path, and confirms the outside file's bytes appear base64-encoded in the output.
import base64, os, shutil, tempfile, markdown
root = tempfile.mkdtemp()
base_path = os.path.join(root, "docs"); os.makedirs(base_path)
outside = os.path.join(root, "secret"); os.makedirs(outside)
png = base64.b64decode(
"iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVR42mNk"
"+M9QDwADhgGAWjR9awAAAABJRU5ErkJggg=="
)
with open(os.path.join(outside, "secret.png"), "wb") as f:
f.write(png)
md = markdown.Markdown(
extensions=["pymdownx.b64"],
extension_configs={"pymdownx.b64": {"base_path": base_path}},
)
html = md.convert('<img src="../secret/secret.png">')
assert base64.b64encode(png).decode() in html, "not leaked"
print("LEAKED:", html)
Output:
LEAKED: <p><img src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQ..."></p>
The base64 of a file outside base_path is present in the output. The absolute-path branch behaves the same way: an absolute src bypasses base_path entirely via os.path.normpath(path). Both were confirmed leaking.
Impact
An application that renders untrusted Markdown with pymdownx.b64 enabled exposes the contents of image-extension files on the server, or any path the process can read, to whoever controls the Markdown and whoever views the output. The reach is bounded by the image-extension check, so it is a targeted file read rather than full arbitrary read, but it still discloses file contents that were never meant to be exposed.
Suggested fix
Resolve the real path and require it to stay within base_path before opening:
file_name = os.path.realpath(os.path.join(base_path, path))
base_real = os.path.realpath(base_path)
if file_name != base_real and not file_name.startswith(base_real + os.sep):
return m.group(0) # leave the tag untouched; do not read outside base_path
The same containment check should apply to the absolute-path branch rather than trusting an absolute src. Using realpath instead of abspath also closes the related symlink-following gap in the snippets handler.
- Record
GHSA-9xwg-3r6f-jcx2- CVSS 3.1
- 5.3 Medium
CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:L/I:N/A:N - Weakness
- CWE-22
- Updated
- 2026-11-02
OSVOESA-2026-4182
7.5
python-pymdown-extensions security update
Extension pack for Python Markdown.
Security Fix(es):
PyMdown Extensions is a set of extensions for the Python-Markdown markdown project. From 10.0.1 until 10.21.3, pymdownx.snippets uses a string-prefix containment check in SnippetPreprocessor.get_snippet_path() in pymdownx/snippets.py when restrict_base_path: True, allowing markdown snippet directives to read files from sibling paths that share the same base_path prefix, such as docs and docs_internal. This is a regression of CVE-2023-32309. This issue is fixed in version 10.21.3.(CVE-2026-46338)
PyMdown Extensions is a set of extensions for the Python-Markdown markdown project. In versions up to and including 10.21.3, the b64 extension is vulnerable to a path traversal that discloses arbitrary files: it inlines images referenced by <img src="..."> by joining the src onto the configured base_path with os.path.normpath and opening the result directly, without verifying that the resolved path stays inside base_path. As a result, an src containing ../ sequences or an absolute path reads a file outside base_path as long as it has an allowed image extension (.png, .jpg, .jpeg, .gif, .svg), and the file's contents are then base64-encoded into the rendered output, disclosing them. An application that renders untrusted Markdown with pymdownx.b64 enabled can therefore leak the contents of image-extension files readable by the process to whoever controls the Markdown or views the output, a targeted file-read bounded by the extension check. This issue has been fixed in version 11.0.(CVE-2026-61632)
pymdown-extensions is a collection of extensions for the Python Markdown library. In versions up to and including 11.0, four inline processors (caret, tilde, betterem, and magiclink) use regular expressions whose content groups can partition a run of delimiter characters in exponentially many ways, causing catastrophic backtracking. As a result, a single untrusted Markdown line under 50 bytes rendered with markdown.markdown() in each extension's default configuration drives the rendering thread into unbounded CPU usage that grows exponentially with input length, enabling an unauthenticated remote attacker who can submit Markdown to cause denial of service. The exposure is concrete for web applications that render user-supplied Markdown (comments, wikis, issue bodies, live preview), including any app using pymdownx.extra which bundles the vulnerable betterem default, as well as hosted docs/CI systems that build untrusted Markdown. The issue has been fixed in version 11.0.1.(CVE-2026-67422)
- Record
OESA-2026-4182- CVSS 3.1
- 7.5 High
CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H - Updated
- 2026-11-02
OSVOESA-2026-4183
7.5
python-pymdown-extensions security update
Extension pack for Python Markdown.
Security Fix(es):
PyMdown Extensions is a set of extensions for the Python-Markdown markdown project. From 10.0.1 until 10.21.3, pymdownx.snippets uses a string-prefix containment check in SnippetPreprocessor.get_snippet_path() in pymdownx/snippets.py when restrict_base_path: True, allowing markdown snippet directives to read files from sibling paths that share the same base_path prefix, such as docs and docs_internal. This is a regression of CVE-2023-32309. This issue is fixed in version 10.21.3.(CVE-2026-46338)
PyMdown Extensions is a set of extensions for the Python-Markdown markdown project. In versions up to and including 10.21.3, the b64 extension is vulnerable to a path traversal that discloses arbitrary files: it inlines images referenced by <img src="..."> by joining the src onto the configured base_path with os.path.normpath and opening the result directly, without verifying that the resolved path stays inside base_path. As a result, an src containing ../ sequences or an absolute path reads a file outside base_path as long as it has an allowed image extension (.png, .jpg, .jpeg, .gif, .svg), and the file's contents are then base64-encoded into the rendered output, disclosing them. An application that renders untrusted Markdown with pymdownx.b64 enabled can therefore leak the contents of image-extension files readable by the process to whoever controls the Markdown or views the output, a targeted file-read bounded by the extension check. This issue has been fixed in version 11.0.(CVE-2026-61632)
pymdown-extensions is a collection of extensions for the Python Markdown library. In versions up to and including 11.0, four inline processors (caret, tilde, betterem, and magiclink) use regular expressions whose content groups can partition a run of delimiter characters in exponentially many ways, causing catastrophic backtracking. As a result, a single untrusted Markdown line under 50 bytes rendered with markdown.markdown() in each extension's default configuration drives the rendering thread into unbounded CPU usage that grows exponentially with input length, enabling an unauthenticated remote attacker who can submit Markdown to cause denial of service. The exposure is concrete for web applications that render user-supplied Markdown (comments, wikis, issue bodies, live preview), including any app using pymdownx.extra which bundles the vulnerable betterem default, as well as hosted docs/CI systems that build untrusted Markdown. The issue has been fixed in version 11.0.1.(CVE-2026-67422)
- Record
OESA-2026-4183- CVSS 3.1
- 7.5 High
CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H - Updated
- 2026-11-02
OSVOESA-2026-4184
7.5
python-pymdown-extensions security update
Extension pack for Python Markdown.
Security Fix(es):
PyMdown Extensions is a set of extensions for the Python-Markdown markdown project. From 10.0.1 until 10.21.3, pymdownx.snippets uses a string-prefix containment check in SnippetPreprocessor.get_snippet_path() in pymdownx/snippets.py when restrict_base_path: True, allowing markdown snippet directives to read files from sibling paths that share the same base_path prefix, such as docs and docs_internal. This is a regression of CVE-2023-32309. This issue is fixed in version 10.21.3.(CVE-2026-46338)
PyMdown Extensions is a set of extensions for the Python-Markdown markdown project. In versions up to and including 10.21.3, the b64 extension is vulnerable to a path traversal that discloses arbitrary files: it inlines images referenced by <img src="..."> by joining the src onto the configured base_path with os.path.normpath and opening the result directly, without verifying that the resolved path stays inside base_path. As a result, an src containing ../ sequences or an absolute path reads a file outside base_path as long as it has an allowed image extension (.png, .jpg, .jpeg, .gif, .svg), and the file's contents are then base64-encoded into the rendered output, disclosing them. An application that renders untrusted Markdown with pymdownx.b64 enabled can therefore leak the contents of image-extension files readable by the process to whoever controls the Markdown or views the output, a targeted file-read bounded by the extension check. This issue has been fixed in version 11.0.(CVE-2026-61632)
pymdown-extensions is a collection of extensions for the Python Markdown library. In versions up to and including 11.0, four inline processors (caret, tilde, betterem, and magiclink) use regular expressions whose content groups can partition a run of delimiter characters in exponentially many ways, causing catastrophic backtracking. As a result, a single untrusted Markdown line under 50 bytes rendered with markdown.markdown() in each extension's default configuration drives the rendering thread into unbounded CPU usage that grows exponentially with input length, enabling an unauthenticated remote attacker who can submit Markdown to cause denial of service. The exposure is concrete for web applications that render user-supplied Markdown (comments, wikis, issue bodies, live preview), including any app using pymdownx.extra which bundles the vulnerable betterem default, as well as hosted docs/CI systems that build untrusted Markdown. The issue has been fixed in version 11.0.1.(CVE-2026-67422)
- Record
OESA-2026-4184- CVSS 3.1
- 7.5 High
CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H - Updated
- 2026-11-02
OSVPYSEC-2026-3609
5.3
PyMdown Extensions: Path traversal in the b64 extension lets <img src> read files outside base_path
Summary
The b64 extension inlines images referenced by <img src="..."> as base64 data URIs. When resolving the src path it joins it onto the configured base_path with os.path.normpath and opens the result directly, with no check that the resolved path stays inside base_path. A src containing ../ sequences, or an absolute path, therefore reads a file outside base_path as long as that file has an allowed image extension (.png, .jpg, .jpeg, .gif, .svg). The base64 of that file is then embedded in the rendered output, disclosing its contents.
This is a separate code path from the snippets traversal issues (GHSA-jh85-wwv9-24hv, GHSA-62q4-447f-wv8h). It lives in pymdownx/b64.py and has no path restriction of any kind. Confirmed on 10.21.3 installed from PyPI.
Details
In pymdownx/b64.py, function repl_path (around lines 68 to 90 on main):
if is_absolute:
file_name = os.path.normpath(path) # absolute src: base_path ignored entirely
else:
file_name = os.path.normpath(os.path.join(base_path, path)) # relative src: '../' escapes base_path
if os.path.exists(file_name):
ext = os.path.splitext(file_name)[1].lower()
for b64_ext in file_types:
if ext in b64_ext:
with open(file_name, "rb") as f: # opened with no containment check
...
There is no startswith(base_path), no os.path.realpath comparison, and no rejection of ... Both branches are reachable from an attacker-controlled src.
PoC
Reproduced against an unmodified pymdown-extensions==10.21.3 from PyPI. The script creates a base_path directory and a PNG one level above it, then renders Markdown whose image src points outside base_path, and confirms the outside file's bytes appear base64-encoded in the output.
import base64, os, shutil, tempfile, markdown
root = tempfile.mkdtemp()
base_path = os.path.join(root, "docs"); os.makedirs(base_path)
outside = os.path.join(root, "secret"); os.makedirs(outside)
png = base64.b64decode(
"iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVR42mNk"
"+M9QDwADhgGAWjR9awAAAABJRU5ErkJggg=="
)
with open(os.path.join(outside, "secret.png"), "wb") as f:
f.write(png)
md = markdown.Markdown(
extensions=["pymdownx.b64"],
extension_configs={"pymdownx.b64": {"base_path": base_path}},
)
html = md.convert('<img src="../secret/secret.png">')
assert base64.b64encode(png).decode() in html, "not leaked"
print("LEAKED:", html)
Output:
LEAKED: <p><img src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQ..."></p>
The base64 of a file outside base_path is present in the output. The absolute-path branch behaves the same way: an absolute src bypasses base_path entirely via os.path.normpath(path). Both were confirmed leaking.
Impact
An application that renders untrusted Markdown with pymdownx.b64 enabled exposes the contents of image-extension files on the server, or any path the process can read, to whoever controls the Markdown and whoever views the output. The reach is bounded by the image-extension check, so it is a targeted file read rather than full arbitrary read, but it still discloses file contents that were never meant to be exposed.
Suggested fix
Resolve the real path and require it to stay within base_path before opening:
file_name = os.path.realpath(os.path.join(base_path, path))
base_real = os.path.realpath(base_path)
if file_name != base_real and not file_name.startswith(base_real + os.sep):
return m.group(0) # leave the tag untouched; do not read outside base_path
The same containment check should apply to the absolute-path branch rather than trusting an absolute src. Using realpath instead of abspath also closes the related symlink-following gap in the snippets handler.
- Record
PYSEC-2026-3609- CVSS 3.1
- 5.3 Medium
CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:L/I:N/A:N - Updated
- 2026-11-02
OSVUBUNTU-CVE-2026-61632
5.3
PyMdown Extensions is a set of extensions for the Python-Markdown markdown project. In versions up to and including 10.21.3, the b64 extension is vulnerable to a path traversal that discloses arbitrary files: it inlines images referenced by <img src="..."> by joining the src onto the configured base_path with os.path.normpath and opening the result directly, without verifying that the resolved path stays inside base_path. As a result, an src containing ../ sequences or an absolute path reads a file outside base_path as long as it has an allowed image extension (.png, .jpg, .jpeg, .gif, .svg), and the file's contents are then base64-encoded into the rendered output, disclosing them. An application that renders untrusted Markdown with pymdownx.b64 enabled can therefore leak the contents of image-extension files readable by the process to whoever controls the Markdown or views the output, a targeted file-read bounded by the extension check. This issue has been fixed in version 11.0.
- Record
UBUNTU-CVE-2026-61632- CVSS 3.1
- 5.3 Medium
CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:L/I:N/A:N - Updated
- 2026-11-02
Affected products
Affected packages
| Package | Affected | Fixed |
|---|---|---|
pymdown-extensionsPyPI |
through 10.21.3 | 11.0.0 |
pymdown-extensionsDebian:12 |
All versions, plus listed versions 10.11.2-110.13-110.13-210.13-310.13-4 Show 9 moreShow less10.19.1-110.19.1-310.8.1-110.9-111.0.1-19.5-29.5-39.5-49.5-5 |
None known |
pymdown-extensionsDebian:13 |
All versions, plus listed versions 10.13-110.13-1+deb13u110.13-210.13-310.13-410.19.1-110.19.1-311.0.1-1 |
None known |
pymdown-extensionsDebian:14 |
before 11.0.1-1, plus listed versions 10.13-110.13-210.13-310.13-410.19.1-110.19.1-3 |
11.0.1-1 |
pymdown-extensionsPyPI |
before 11.0.0, plus listed versions 1.0.01.0.11.11.21.3 Show 45 moreShow less1.41.51.61.6.11.71.810.010.0.110.110.1010.10.110.10.210.1110.11.110.11.210.1210.1310.1410.14.110.14.210.14.310.1510.1610.16.110.1710.17.110.17.210.1810.1910.19.110.210.2.110.2010.20.110.2110.21.210.21.310.310.3.110.410.510.610.710.7.110.8 |
11.0.0 |
python-pymdown-extensionsopenEuler:24.03-LTS-SP4 From an advisory covering 3 CVEs |
before 10.7-3.oe2403sp4 | 10.7-3.oe2403sp4 |
python-pymdown-extensionsopenEuler:24.03-LTS-SP1 From an advisory covering 3 CVEs |
before 10.7-3.oe2403sp1 | 10.7-3.oe2403sp1 |
python-pymdown-extensionsopenEuler:24.03-LTS-SP3 From an advisory covering 3 CVEs |
before 10.7-3.oe2403sp3 | 10.7-3.oe2403sp3 |
python-markdownUbuntu:14.04:LTS |
All versions, plus listed versions 2.3.1-12.3.1-22.4-12.4-1ubuntu0.14.04.1 |
None known |
markdownUbuntu:16.04:LTS |
All versions, plus listed versions 1.0.1-71.0.1-8 |
None known |
Show 13 moreShow less
| Package | Affected | Fixed |
|---|---|---|
python-markdownUbuntu:16.04:LTS |
All versions, plus listed versions 2.6.2-12.6.3-12.6.4-12.6.5-12.6.5-22.6.6-1 |
None known |
markdownUbuntu:18.04:LTS |
All versions, plus listed versions 1.0.1-91.0.1-10 |
None known |
python-markdownUbuntu:18.04:LTS |
All versions, plus listed versions 2.6.9-1 |
None known |
python-markdownUbuntu:20.04:LTS |
All versions, plus listed versions 3.1.1-23.1.1-2build13.1.1-3 |
None known |
markdownUbuntu:20.04:LTS |
All versions, plus listed versions 1.0.1-10 |
None known |
markdownUbuntu:22.04:LTS |
All versions, plus listed versions 1.0.1-10.1 |
None known |
python-markdownUbuntu:22.04:LTS |
All versions, plus listed versions 3.3.4-13.3.6-1 |
None known |
markdownUbuntu:24.04:LTS |
All versions, plus listed versions 1.0.1-12 |
None known |
python-markdownUbuntu:24.04:LTS |
All versions, plus listed versions 3.4.4-13.5.2-1 |
None known |
markdownUbuntu:26.10 |
All versions, plus listed versions 1.0.1-14 |
None known |
python-markdownUbuntu:26.10 |
All versions, plus listed versions 3.10.2-13.10.2-23.10.3-1 |
None known |
markdownUbuntu:26.04:LTS |
All versions, plus listed versions 1.0.1-14 |
None known |
python-markdownUbuntu:26.04:LTS |
All versions, plus listed versions 3.8.2-23.10-13.10-23.10.2-1 |
None known |
History
| Recorded | Changed | Sources |
|---|---|---|
| affected, facts | ||
| first indexed | CVE List, EUVD, GitHub Advisories, NVD, OSV |
Dates are when Every CVE recorded each change. Upstream dates may differ.
In short
CVE-2026-61632 is a medium vulnerability (CVSS 5.3) in Facelessuser Pymdown-extensions, published July 24, 2026. No source lists public exploit code. EPSS puts the chance of exploitation in the next 30 days at 0.40%, the 34th percentile. Fixed in pymdown-extensions 11.0.0.
References
github.com/facelessuser/pymdown-extensions/security/advisories/GHSA-9xwg-3r6f-jcx2
github.com/facelessuser/pymdown-extensions/releases/tag/11.0
github.com/facelessuser/pymdown-extensions/commit/edce35586d11a1ef78bb187bc60497fe6dbf3b64
github.com/advisories/GHSA-9xwg-3r6f-jcx2
github.com/CVEProject/cvelistV5/tree/main/cves/2026/61xxx/CVE-2026-61632.json
nvd.nist.gov/vuln/detail/CVE-2026-61632
Show 10 moreShow less
security-tracker.debian.org/tracker/CVE-2026-61632
github.com/facelessuser/pymdown-extensions
openeuler.org/zh/security/security-bulletins/detail?id=openEuler-SA-2026-4182
nvd.nist.gov/vuln/detail/CVE-2026-46338
nvd.nist.gov/vuln/detail/CVE-2026-67422
openeuler.org/zh/security/security-bulletins/detail?id=openEuler-SA-2026-4183
openeuler.org/zh/security/security-bulletins/detail?id=openEuler-SA-2026-4184
pypi.org/project/pymdown-extensions
ubuntu.com/security/CVE-2026-61632
cve.org/CVERecord?id=CVE-2026-61632
Left out here: 68 versions. The JSON record has them.