T09 · Insecure Skill Coding Practices
Error
- Location
- assets/strategy_template.py:4978
- Finding
- Arbitrary Code Execution Through Unsafe Pickle Deserialization<![CDATA[ ## Vulnerability Details **File Location**: `assets/strategy_template.py:4978-5059` **Vulnerability Type**: Unsafe deserialization of mutable local files **Risk Level**: High ### Vulnerable Code ```python import pickle NOTEBOOK_PATH='' def initialize(context): NOTEBOOK_PATH = get_research_path()#+'xsz/'#'/home/fly/notebook/' # Persistence: attempt to initialize pickle files if not is_trade(): with open(NOTEBOOK_PATH+'count.pkl','wb') as f: pickle.dump(1,f,-1) with open(NOTEBOOK_PATH+'firstcount.pkl','wb') as f: pickle.dump(0,f,-1) try: with open(NOTEBOOK_PATH+'count.pkl','rb') as f: g.count = pickle.load(f) log.info("Strategy restart initialization, current strategy trading day read from file: %s" % (g.count)) with open(NOTEBOOK_PATH+'firstcount.pkl','rb') as f: g.trade_count = pickle.load(f) log.info("Strategy restart initialization, strategy has run for %s trading days" % (g.trade_count)) except Exception as e: log.error("Failed to read count and firstcount files: %s" % (e)) ``` ### Technical Analysis Python pickle is not a data-only serialization format. During `pickle.load()`, specially constructed objects can invoke attacker-selected Python callables through reduction opcodes. Consequently, loading a pickle file is equivalent to executing code supplied by whoever controls that file. The strategy loads `count.pkl` and `firstcount.pkl` from the path returned by `get_research_path()` without validating file ownership, permissions, provenance, integrity, or expected structure. Although backtest mode overwrites these files before loading them, live trading mode does not necessarily do so. A pre-existing or replaced file can therefore reach `pickle.load()` directly. The exception handler does not mitigate the vulnerability because payload execution occurs during deserialization, before a malicious object must retur ...[truncated 1413 chars]
- Remediation
- <![CDATA[ ## Remediation Suggestions 1. Remove all uses of `pickle.load()` for persistent counters or other externally mutable state. 2. Store primitive values in a data-only format such as JSON: ```python import json with open(counter_path, "r", encoding="utf-8") as f: value = json.load(f) if not isinstance(value, int) or value < 0: raise ValueError("Invalid counter value") ``` 3. Use a dedicated private state directory rather than a broadly shared research directory. 4. Restrict directory and file permissions to the account running the strategy. 5. Write updates atomically by creating a temporary file in the same directory and replacing the destination only after a successful flush. 6. If state authenticity matters, verify it with a keyed MAC whose key is stored outside the writable state directory. 7. Validate every loaded field against an explicit schema, including type and acceptable numeric range. 8. Document the state files and fail safely if their ownership or permissions are unexpected. ]]>
