T09 · Insecure Skill Coding Practices
- Location
- scripts/ml_model.py:184
- Finding
- Arbitrary Code Execution Through Untrusted Pickle Model Deserialization<![CDATA[ ## Vulnerability Details **File Location**: `scripts/ml_model.py:184-188`, invoked through `scripts/screener.py:339` and `scripts/screener.py:369-374`; an equivalent model-loading path exists in `scripts/portfolio_backtest.py:72-76` **Vulnerability Type**: Unsafe deserialization of a user-selected model file **Risk Level**: High ### Vulnerable Code `scripts/ml_model.py:184-188`: ```python def load_model(path: str) -> dict: import pickle with open(path, "rb") as f: return pickle.load(f) ``` `scripts/screener.py:339`: ```python ap.add_argument("--model", help="ML概率模型路径(.pkl),启用ML排序") ``` `scripts/screener.py:369-374`: ```python if args.model: try: from ml_model import load_model ml_model = load_model(args.model) m = ml_model.get("metrics", {}) print(f"{CYAN}ML模型已加载: {args.model}({m.get('backend','?')} AUC={m.get('auc','?')}){RESET}") ``` `scripts/portfolio_backtest.py:72-76`: ```python def add_ml_probs(samples: List[dict], model_path: str = MODEL_FILE) -> List[dict]: """批量计算 ML 上涨概率""" with open(model_path, "rb") as f: bundle = pickle.load(f) ``` ### Technical Analysis Python pickle is an executable object serialization format rather than a passive data format. A crafted pickle can define reduction operations that invoke attacker-selected Python callables during `pickle.load()`. Execution occurs before the application can inspect the returned model bundle or validate expected keys. The `screener.py --model` argument permits the caller to select the file passed directly to `pickle.load()`. No signature, trusted digest, ownership check, path restriction, or safe deserialization mechanism is applied. Catching exceptions around model loading does not mitigate the issue because a malicious reduction payload executes while deserialization is in progress. The same underlying issue exists in the portfolio backtest model-loading function. Although its command-line integration does no ...[truncated 1495 chars]
- Remediation
- <![CDATA[ ## Remediation Suggestions 1. Replace pickle with non-executable model formats: - Use XGBoost's JSON model format for XGBoost models. - Use a reviewed interchange format such as ONNX where appropriate. - Store model metadata and feature names separately as validated JSON. 2. Do not accept arbitrary pickle paths through `--model`. If legacy pickle support is unavoidable: - Restrict models to an application-owned directory. - Resolve the canonical path and reject files outside that directory. - Verify file ownership and reject group-writable or world-writable files. - Require a cryptographic signature or a trusted SHA-256 digest before loading. - Display an explicit warning that only locally generated, trusted models may be used. 3. Validate the deserialized bundle after authenticity verification: - Require an exact schema. - Verify the expected feature list and model backend. - Reject unknown fields and incompatible versions. 4. Add security regression tests confirming that unsigned, out-of-directory, or permission-unsafe model files are rejected before deserialization. ]]>
