LoRA adapter_config.json base_model_name_or_path blindly trusted as model source in tasks/AutoChem/code/experiment.py · Issue #30 · InternScience/InternAgent · GitHub
Skip to content

LoRA adapter_config.json base_model_name_or_path blindly trusted as model source in tasks/AutoChem/code/experiment.py #30

Open

Description

@3em0

1. Summary

tasks/AutoChem/code/experiment.py (train(), lines 251-255) loads the base model of a LoRA fine-tuning run from the base_model_name_or_path field of the supplied adapter's adapter_config.json, without validating it against the operator-provided --pretrained_model_path or any allowlist. A shared/crafted LoRA adapter therefore silently redirects the training pipeline to an attacker-chosen Hugging Face Hub repository: AutoModel.from_pretrained() fetches the second repository's config.json and weights over the network and the PEFT model is assembled on top of them. The crafted repository needs to be data-only (config + SafeTensors, no .py, no pickle, no trust_remote_code), so this is model-provenance poisoning rather than code execution. The same pattern is copied in tasks/AutoChem/run_0/experiment.py (lines 261-265).

2. Affected version / environment

InternScience/InternAgent commit fa8c3ee (current main HEAD, last commit dated 2026-07-29; no fixed release known). Verified on Windows 11 + WSL2 Ubuntu 24.04, CPU-only, with Python 3.12.3, torch 2.8.0+cpu, transformers 4.57.1, peft 0.21.2, huggingface_hub 0.34.3 (versions per the repository requirements.txt where pinned).

3. Steps to reproduce

  1. Generate two LoRA adapters that are byte-identical except for one JSON field: adapter-positive sets "base_model_name_or_path": "attacker-mirror/poisoned-base" (a Hub repo id), adapter-negative points the same field to a local directory. The referenced repo contains only config.json (654 B) and model.safetensors (239408 B) - no Python file, remote code not enabled. (make_poc.py in the attached poc bundle generates everything.)
  2. Serve the attacker repo from a local look-alike Hub endpoint and set HF_ENDPOINT to it (keeps the test off the real Hub): python fake_hf_hub.py fixtures/hub-repo-attacker 8755 > hub_server.log 2>&1 & then export HF_ENDPOINT=http://127.0.0.1:8755.
  3. Run the loading block of tasks/AutoChem/code/experiment.py against adapter-positive (run_victim.py in the attached bundle executes lines 251-255 verbatim; the original train() cannot reach this block on a CPU-only machine because its deepspeed/NCCL distributed setup runs first). The PEFT model assembles successfully and reports its base-model provenance as attacker-mirror/poisoned-base; the endpoint log records HEAD+GET of config.json and model.safetensors from the victim process.
  4. Negative control: run the same loader against adapter-negative with HF_ENDPOINT=http://127.0.0.1:9 (unreachable). The run completes identically from the local base and issues zero Hub requests (endpoint log line count unchanged), isolating the single JSON field as the decision that triggers the remote fetch.

4. Expected behavior

The training script uses the operator-selected base model (--pretrained_model_path), or at minimum fails loudly when the adapter declares a different, untrusted base model.

5. Actual behavior

The adapter-declared base model silently wins: a Hub repository id is fetched over the network (config + weights) and becomes the foundation of the fine-tuning run, with no warning and no comparison against the operator-provided base path.

6. Impact

Integrity of trained artifacts: whoever shares a LoRA adapter (community release, collaborator hand-off, agent-generated adapter saved to disk) also silently chooses the base weights of the next fine-tuning run, enabling model poisoning and provenance subversion. The data-only scenario reported here does not yield code execution (no .py sidecar, trust_remote_code not enabled); note that the same trusted-field pattern combined with trust_remote_code repositories would escalate to code execution, which is why the field should be validated now.

7. Suggested fix

At tasks/AutoChem/code/experiment.py:254, treat lora_config.base_model_name_or_path as untrusted: prefer the operator-provided args.pretrained_model_path when present, fail or warn loudly on mismatch, restrict accepted values to an allowlist (local paths / trusted org namespaces / pinned commit hashes), and mirror the change in tasks/AutoChem/run_0/experiment.py:264.

8. Additional context (PoC / logs, redacted)

Attached bundle (poc/): make_poc.py (deterministic fixture generator), run_victim.py (verbatim execution of the defective lines, with the deepspeed preamble omitted and flagged in a docstring), fake_hf_hub.py (local Hub look-alike that logs every request), fixtures/ (adapter-positive, adapter-negative, hub-repo-attacker, second_local, SHA256SUMS.txt). Positive and negative adapter weight files are byte-identical (same SHA256); the attacker repo content is byte-identical to the benign local base, so the only functional difference is the base_model_name_or_path value. Full write-up with terminal screenshots: available on request / see the linked report.

poc.zip

link:
https://github.com/3em0/cve_repo/blob/main/2026/InternAgent_fa8c3eed_Untrusted_LoRA_Adapter_Base-Model_Remote_Fetch_in_AutoChem_experiment.py_CWE-20.md

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions