VYPR
High severityNVD Advisory· Published Aug 30, 2023· Updated Oct 1, 2024

Sandbox escape via various forms of "format" in RestrictedPython

CVE-2023-41039

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.

PackageAffected versionsPatched versions
RestrictedPythonPyPI
< 5.45.4
RestrictedPythonPyPI
>= 6.0, < 6.26.2

Affected products

2

Patches

3
7031e2d8d83e

Preparing 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=(
    
4134aedcff17

Merge 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

News mentions

0

No linked articles in our index yet.