Sandbox escape via various forms of "format" in RestrictedPython
Description
RestrictedPython fails to block Python's format functionality, allowing untrusted code to read arbitrary objects via recursive attribute lookup.
AI Insight
LLM-synthesized narrative grounded in this CVE's description and references.
RestrictedPython fails to block Python's format functionality, allowing untrusted code to read arbitrary objects via recursive attribute lookup.
Vulnerability
RestrictedPython is a restricted execution environment for Python intended to run untrusted code. However, it does not adequately restrict Python's format functionality. An attacker who can control the format string can perform recursive attribute lookup and subscription on accessible objects, leading to critical information disclosure [1][4].
Exploitation
The vulnerability is exposed through str.format(), str.format_map(), and string.Formatter, accessible via string instances or the class itself. An attacker with the ability to provide a format string can craft it to traverse object attributes and read sensitive data, such as __dict__ or __class__.__mro__ [2]. Prior to the fix, RestrictedPython allowed these methods without proper guards [2].
Impact
Successful exploitation allows an attacker to read any object reachable through attribute access from objects they already control. This can lead to leakage of secrets like environment variables, file contents, or internal application state, bypassing the sandbox entirely [4].
Mitigation
The vulnerability affects all known versions of RestrictedPython. It has been patched in commits included in versions 5.4 and 6.2 [2][4]. Users should upgrade immediately, as no workarounds are available [1][4].
AI Insight generated on May 20, 2026. Synthesized from this CVE's description and the cited reference URLs; citations are validated against the source bundle.
Affected packages
Versions sourced from the GitHub Security Advisory.
| Package | Affected versions | Patched versions |
|---|---|---|
RestrictedPythonPyPI | < 5.4 | 5.4 |
RestrictedPythonPyPI | >= 6.0, < 6.2 | 6.2 |
Affected products
2- Range: < 5.4
Patches
37031e2d8d83ePreparing release 5.4
2 files changed · +2 −2
CHANGES.rst+1 −1 modified@@ -1,7 +1,7 @@ Changes ======= -5.4 (unreleased) +5.4 (2023-08-30) ---------------- Fixes
setup.py+1 −1 modified@@ -32,7 +32,7 @@ def read(*rnames): setup( name='RestrictedPython', - version='5.4.dev0', + version='5.4', url='https://github.com/zopefoundation/RestrictedPython', license='ZPL 2.1', description=(
311f2f7eee49- prepare release 6.1
2 files changed · +2 −2
CHANGES.rst+1 −1 modified@@ -1,7 +1,7 @@ Changes ======= -6.1 (unreleased) +6.1 (2023-07-08) ---------------- - Restrict access to some attributes accessible via the ``inspect`` module.
setup.py+1 −1 modified@@ -32,7 +32,7 @@ def read(*rnames): setup( name='RestrictedPython', - version='6.1.dev0', + version='6.1', url='https://github.com/zopefoundation/RestrictedPython', license='ZPL 2.1', description=(
4134aedcff17Merge pull request from GHSA-xjw2-6jm9-rf67
6 files changed · +90 −13
CHANGES.rst+5 −0 modified@@ -22,6 +22,11 @@ Fixes - Forbid using some attributes providing access to restricted Python internals. +- Fix information disclosure problems through + Python's "format" functionality + (``format`` and ``format_map`` methods on ``str`` and its instances, + ``string.Formatter``). + 6.0 (2022-11-03) ----------------
src/RestrictedPython/Guards.py+4 −2 modified@@ -246,9 +246,11 @@ def safer_getattr(object, name, default=None, getattr=getattr): http://lucumr.pocoo.org/2016/12/29/careful-with-str-format/ """ - if isinstance(object, str) and name == 'format': + if name in ('format', 'format_map') and ( + isinstance(object, str) or + (isinstance(object, type) and issubclass(object, str))): raise NotImplementedError( - 'Using format() on a %s is not safe.' % object.__class__.__name__) + 'Using the format*() methods of `str` is not safe') if name.startswith('_'): raise AttributeError( '"{name}" is an invalid attribute name because it '
src/RestrictedPython/Utilities.py+15 −1 modified@@ -18,7 +18,21 @@ utility_builtins = {} -utility_builtins['string'] = string + +class _AttributeDelegator: + def __init__(self, mod, *excludes): + """delegate attribute lookups outside *excludes* to module *mod*.""" + self.__mod = mod + self.__excludes = excludes + + def __getattr__(self, attr): + if attr in self.__excludes: + raise NotImplementedError( + f"{self.__mod.__name__}.{attr} is not safe") + return getattr(self.__mod, attr) + + +utility_builtins['string'] = _AttributeDelegator(string, "Formatter") utility_builtins['math'] = math utility_builtins['random'] = random utility_builtins['whrandom'] = random
tests/builtins/test_utilities.py+4 −1 modified@@ -5,7 +5,10 @@ def test_string_in_utility_builtins(): from RestrictedPython.Utilities import utility_builtins - assert utility_builtins['string'] is string + + # we no longer provide access to ``string`` itself, only to + # a restricted view of it + assert utility_builtins['string'].__name__ == string.__name__ def test_math_in_utility_builtins():
tests/test_Guards.py+49 −9 modified@@ -160,7 +160,7 @@ def test_Guards__guarded_unpack_sequence__1(mocker): """ -def test_Guards__safer_getattr__1(): +def test_Guards__safer_getattr__1a(): """It prevents using the format method of a string. format() is considered harmful: @@ -171,17 +171,57 @@ def test_Guards__safer_getattr__1(): } with pytest.raises(NotImplementedError) as err: restricted_exec(STRING_DOT_FORMAT_DENIED, glb) - assert 'Using format() on a str is not safe.' == str(err.value) + assert 'Using the format*() methods of `str` is not safe' == str(err.value) -UNICODE_DOT_FORMAT_DENIED = """\ -a = u'Hello {}' -b = a.format(u'world') +# contributed by Ward Theunisse +STRING_DOT_FORMAT_MAP_DENIED = """\ +a = 'Hello {foo.__dict__}' +b = a.format_map({foo:str}) """ -def test_Guards__safer_getattr__2(): - """It prevents using the format method of a unicode. +def test_Guards__safer_getattr__1b(): + """It prevents using the format method of a string. + + format() is considered harmful: + http://lucumr.pocoo.org/2016/12/29/careful-with-str-format/ + """ + glb = { + '__builtins__': safe_builtins, + } + with pytest.raises(NotImplementedError) as err: + restricted_exec(STRING_DOT_FORMAT_MAP_DENIED, glb) + assert 'Using the format*() methods of `str` is not safe' == str(err.value) + + +# contributed by Abhishek Govindarasu +STR_DOT_FORMAT_DENIED = """\ +str.format('{0.__class__.__mro__[1]}', int) +""" + + +def test_Guards__safer_getattr__1c(): + """It prevents using the format method of a string. + + format() is considered harmful: + http://lucumr.pocoo.org/2016/12/29/careful-with-str-format/ + """ + glb = { + '__builtins__': safe_builtins, + } + with pytest.raises(NotImplementedError) as err: + restricted_exec(STR_DOT_FORMAT_DENIED, glb) + assert 'Using the format*() methods of `str` is not safe' == str(err.value) + + +STR_DOT_FORMAT_MAP_DENIED = """\ +str.format_map('Hello {foo.__dict__}', {'foo':str}) +""" + + +def test_Guards__safer_getattr__1d(): + """It prevents using the format method of a string. format() is considered harmful: http://lucumr.pocoo.org/2016/12/29/careful-with-str-format/ @@ -190,8 +230,8 @@ def test_Guards__safer_getattr__2(): '__builtins__': safe_builtins, } with pytest.raises(NotImplementedError) as err: - restricted_exec(UNICODE_DOT_FORMAT_DENIED, glb) - assert 'Using format() on a str is not safe.' == str(err.value) + restricted_exec(STR_DOT_FORMAT_MAP_DENIED, glb) + assert 'Using the format*() methods of `str` is not safe' == str(err.value) SAFER_GETATTR_ALLOWED = """\
tests/test_Utilities.py+13 −0 modified@@ -1,5 +1,8 @@ +import pytest + from RestrictedPython.Utilities import reorder from RestrictedPython.Utilities import test +from RestrictedPython.Utilities import utility_builtins def test_Utilities__test_1(): @@ -30,3 +33,13 @@ def test_Utilities__reorder_1(): _with = [('k2', 'v2'), ('k3', 'v3')] without = [('k2', 'v2'), ('k4', 'v4')] assert reorder(s, _with, without) == [('k3', 'v3')] + + +def test_Utilities_string_Formatter(): + """Access to ``string.Formatter`` is denied.""" + string = utility_builtins["string"] + # access successful in principle + assert string.ascii_lowercase == 'abcdefghijklmnopqrstuvwxyz' + with pytest.raises(NotImplementedError) as exc: + string.Formatter + assert 'string.Formatter is not safe' == str(exc.value)
Vulnerability mechanics
Root cause
"Python's format string functionality allows recursive attribute lookup and subscription traversal on accessible objects, which RestrictedPython fails to restrict."
Attack vector
An attacker who can supply a format string to the `format` or `format_map` method of a string, or who can use `string.Formatter`, can exploit Python's format functionality to recursively traverse attribute lookups and subscriptions on accessible objects [CWE-74]. This allows reading attributes and items of objects that should be restricted, leading to information disclosure. The attacker needs to be able to execute code within RestrictedPython's sandbox and control the format string argument.
Affected code
The vulnerability exists in the `format` and `format_map` methods of `str` (and `unicode`) and in `string.Formatter`, all of which are exposed in RestrictedPython's restricted execution environment. The advisory does not specify a particular file or function path beyond these Python built-in formatting capabilities.
What the fix does
The advisory states that the fix is included in commit `4134aedcff1` [patch_id=1707375], which is part of releases 5.4 and 6.2. The patch itself is not shown in the provided bundle, but the advisory indicates it addresses the format string information disclosure. The fix likely restricts or sanitizes access to attributes and items via format strings, preventing recursive traversal of object attributes and subscriptions.
Preconditions
- authThe attacker must be able to execute Python code within the RestrictedPython sandbox.
- inputThe attacker must be able to control the format string passed to str.format(), str.format_map(), or string.Formatter.
Generated on May 23, 2026. Inputs: CWE entries + fix-commit diffs from this CVE's patches. Citations validated against bundle.
References
4- github.com/advisories/GHSA-xjw2-6jm9-rf67ghsaADVISORY
- nvd.nist.gov/vuln/detail/CVE-2023-41039ghsaADVISORY
- github.com/zopefoundation/RestrictedPython/commit/4134aedcff17c977da7717693ed89ce56d54c120ghsax_refsource_MISCWEB
- github.com/zopefoundation/RestrictedPython/security/advisories/GHSA-xjw2-6jm9-rf67ghsax_refsource_CONFIRMWEB
News mentions
0No linked articles in our index yet.