Class Pollution (Python's Prototype Pollution)

Basic Example

Check how is possible to pollute classes of objects with strings:

class Company: pass
class Developer(Company): pass
class Entity(Developer): pass

c = Company()
d = Developer()
e = Entity()

print(c) #<__main__.Company object at 0x1043a72b0>
print(d) #<__main__.Developer object at 0x1041d2b80>
print(e) #<__main__.Entity object at 0x1041d2730>

e.__class__.__qualname__ = 'Polluted_Entity'

print(e) #<__main__.Polluted_Entity object at 0x1041d2730>

e.__class__.__base__.__qualname__ = 'Polluted_Developer'
e.__class__.__base__.__base__.__qualname__ = 'Polluted_Company'

print(d) #<__main__.Polluted_Developer object at 0x1041d2b80>
print(c) #<__main__.Polluted_Company object at 0x1043a72b0>

Basic Vulnerability Example

Gadget Examples

Creating class property default value to RCE (subprocess)
Polluting other classes and global vars through globals
Arbitrary subprocess execution
Overwritting __kwdefaults__

__kwdefaults__ is a special attribute of all functions, based on Python documentation, it is a “mapping of any default values for keyword-only parameters”. Polluting this attribute allows us to control the default values of keyword-only parameters of a function, these are the function’s parameters that come after * or *args.

Overwriting Flask secret across files

So, if you can do a class pollution over an object defined in the main python file of the web but whose class is defined in a different file than the main one. Because in order to access __globals__ in the previous payloads you need to access the class of the object or methods of the class, you will be able to access the globals in that file, but not in the main one. Therefore, you won't be able to access the Flask app global object that defined the secret key in the main page:

In this scenario you need a gadget to traverse files to get to the main one to access the global object app.secret_key to change the Flask secret key and be able to escalate privileges knowing this key.

A payload like this one from this writeup:

Use this payload to change app.secret_key (the name in your app might be different) to be able to sign new and more privileges flask cookies.

Check also the following page for more read only gadgets:

Python Internal Read Gadgets

References

Last updated