Classes
Classes are the most intuitive pollution target. When a class object C is accessible, the attacker can modify C.v, and from then on every read of cls.v or self.v that does not have a shadowing instance attribute returns the polluted value.
Access mechanism: attribute lookup via the MRO
Python’s attribute resolution follows the Method Resolution Order (MRO). When instance.attr is read, the lookup tries the instance’s own __dict__ first, then walks up the class chain:
instance.attr
1. instance.__dict__['attr'] # instance dict
2. type(instance).__dict__['attr'] # class dict
3. base.__dict__['attr'] # base classes (MRO)
If the attribute is not on the instance, the lookup falls back to the class. Polluting C.v therefore changes the value that every self.v and cls.v read returns, for every instance of C that does not shadow it:
class C:
def __init__(self):
self.v # falls back to cls.v when not set on the instance
@classmethod
def other(cls):
cls.v # always reads from the class
Example: DoS via __getattribute__
class User:
def __init__(self, name):
self.name = name
u = User("alice")
u.name # returns "alice"
# Attacker payload (Attr-Set on User.__getattribute__):
# User.__getattribute__ = "not_a_function"
u.name # raises TypeError: 'str' object is not callable
Every attribute access on every User instance now raises, because Python invokes __getattribute__ on the class for every read. This pattern is detailed on the DoS gadgets page.