Back to skill

Security audit

MiniMax Multimodal (Speech + Image)

Security checks for vulnerabilities and agentic risk

Overview

This MiniMax skill mostly does what it says, but it can upload sensitive voice/image files and delete voice assets without clear safeguards.

Install only if you are comfortable sending prompts, audio, images, and generated media requests to MiniMax using your API key. Do not use voice cloning unless you have rights and consent for the voice sample, avoid sensitive local images, and treat the delete command as irreversible because there is no confirmation prompt. The unrestricted image downloader should be hardened before use in untrusted workflows.

Vulnerability Patterns
  • Insecure Skill Coding PracticesFinds exploitable flaws such as hardcoded secrets or command injection
  • Skill Instruction HijackingAlters the agent's session goals or safety constraints when the skill loads
  • Agent Memory PoisoningWrites attacker-controlled rules into memory that affect later sessions
  • Remote Payload Retrieval and ExecutionFetches external code whose behavior can change after review
  • Embedded Malicious CodeShips malicious scripts inside the skill and executes them locally
Findings (1)

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/image.py:91
Finding
Unrestricted Image Download Enables Server-Side Request Forgery and Resource Exhaustion## Vulnerability Details **File Location**: `scripts/image.py`, lines 91-95 **Vulnerability Type**: Server-Side Request Forgery (SSRF), unbounded response handling, and unrestricted file output **Risk Level**: Medium ### Vulnerable Code ```python def download_image(image_url: str, output_path: str) -> str: resp = requests.get(image_url, timeout=60) resp.raise_for_status() with open(output_path, "wb") as f: f.write(resp.content) return output_path ``` ### Technical Analysis The publicly documented `download_image` function performs a server-side request to an arbitrary caller-provided URL. It does not validate: - The URL scheme or destination hostname. - Whether DNS resolution produces a loopback, private, link-local, multicast, or reserved IP address. - Redirect destinations. - The response content type or actual file format. - The maximum response size. - Whether `output_path` is confined to an approved output directory. Consequently, a caller that controls `image_url` can direct the Agent host to request internal or otherwise non-public HTTP services. Redirects followed automatically by `requests` can also lead the request to a prohibited destination. The 60-second timeout limits request duration but not response size. Accessing `resp.content` buffers the complete response in memory before writing it, allowing a large response to consume substantial memory and disk capacity. The same helper is also used for URLs returned by the MiniMax image-generation API. Those URLs cross a remote trust boundary and should not be treated as inherently safe without destination and response validation. ### Attack Path 1. An attacker or untrusted workflow supplies a URL to the documented `download_image` function, or causes a remote API response to contain a hostile download URL. 2. The URL points directly to an internal endpoint, cloud metadata service, loopback service, or an external endpoint that redirects to one. 3. `requests.get()` issues ...[truncated 1315 chars]
Remediation
## Remediation Suggestions 1. Permit only `https` URLs and reject URLs containing embedded credentials or unsupported schemes. 2. Resolve the hostname before connecting and reject every loopback, private, link-local, multicast, unspecified, and reserved IPv4 or IPv6 address. 3. Prevent DNS rebinding by connecting only to the validated address while preserving correct TLS hostname verification, or use a hardened outbound proxy. 4. Disable redirects, or validate the scheme, hostname, and resolved addresses at every redirect hop. 5. Prefer an explicit allowlist of trusted image-delivery domains. If only MiniMax-generated images should be downloaded, document and enforce the expected MiniMax/CDN hosts. 6. Use `stream=True` and enforce a strict maximum byte count before and during download rather than accessing `resp.content`. 7. Validate the response `Content-Type`, then inspect file signatures and decode the image with a safe image library before accepting it. 8. Constrain output files to a dedicated directory. Resolve the final path and verify that it remains beneath that directory; reject traversal and unsafe symlink targets. 9. Use exclusive file creation or an explicit overwrite policy to prevent unintended replacement of existing files. 10. Apply outbound firewall rules that block metadata, loopback, link-local, and private network ranges as defense in depth.
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Taint TrackingDirect Taint Flow, Variable-Mediated Taint Flow, Credential Exfiltration Chain
  • MCP Least PrivilegeUnderdeclared Capability, Wildcard Permission, Missing Permission Declaration
  • MCP Tool PoisoningHidden Instructions, Unicode Deception, Parameter Description Injection
  • Prompt InjectionInstruction Override, Hidden Instructions, Exfiltration Commands
Findings (22)

Tainted flow: 'headers_upload' from os.getenv (line 62, credential/environment) → requests.post (network output)

Critical
Category
Data Flow
Content
with open(audio_file_path, "rb") as f:
        files = {"file": (os.path.basename(audio_file_path), f, "audio/mpeg")}
        data = {"purpose": "audio"}
        resp = requests.post(upload_url, headers=headers_upload, files=files, data=data, timeout=120)
    resp.raise_for_status()
    resp_data = resp.json()
    file_id = resp_data.get("file", {}).get("file_id")
Confidence
90% confidence
Finding
Credentials or environment variables flow to a network sink. This is a high-confidence indicator of credential exfiltration.

Tp4

High
Category
MCP Tool Poisoning
Confidence
96% confidence
Finding
代码行为与声明部分重合于图片生成(文生图/图生图,model 默认为 image-01),这部分是匹配的。但声明将技能描述为同时支持语音和图像的多模态能力,且明确提到 speech-2.8-hd、TTS、音色克隆、音色设计;而提供的代码片段仅包含 image.py,所有逻辑都围绕 /image_generation 接口、图像输入编码、图片下载与保存展开,没有任何音频输入输出、语音模型调用、音色克隆/设计接口或相关参数处理。因此该描述对当前代码片段而言存在实质性夸大,属于描述与实际行为不一致。

Tp4

High
Category
MCP Tool Poisoning
Confidence
96% confidence
Finding
The description presents a multimodal MiniMax skill with both speech and image generation capabilities tied to Token Plan. The supplied code chunk is narrowly a speech API client. It supports TTS, async TTS, querying speech tasks, voice cloning, and voice design, which partially align with the speech portion of the description. However, there is no image generation logic at all—no text-to-image, image-to-image, or image-01 model usage—so a major declared capability is missing from the actual implementation. Additionally, the code exposes voice listing/get/delete operations that are not described. This makes the description materially broader and partly inaccurate relative to the code shown.

Lp3

Medium
Category
MCP Least Privilege
Confidence
87% confidence
Finding
The skill documentation describes use of environment variables and outbound API access, but it does not declare any explicit tool scope such as allowed tools or permissions. That creates an authorization and transparency gap: an agent may invoke network and env capabilities without clear least-privilege boundaries, increasing the chance of unintended secret exposure or external data transmission.

Missing User Warnings

Medium
Confidence
93% confidence
Finding
The skill promotes voice cloning by uploading audio to an external service without any warning about consent, ownership, biometric sensitivity, or legal/privacy implications. Voice data is highly sensitive and can enable impersonation or misuse if users upload third-party recordings without authorization.

Missing User Warnings

Medium
Confidence
86% confidence
Finding
When `image_file` is provided, the code reads the local image, base64-encodes it, and sends it to the MiniMax API via `requests.post`. Although this is part of image editing functionality, the code lacks any inline disclosure, prompt, or warning that a local file's contents will be uploaded to an external service.

External Transmission

Medium
Category
Data Exfiltration
Content
from typing import Optional, Dict, Any, List
import requests

BASE_URL_CN = "https://api.minimaxi.com/v1"
BASE_URL_INT = "https://api.minimax.io/v1"

def get_base_url() -> str:
Confidence
60% confidence
Finding
Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.

External Transmission

Medium
Category
Data Exfiltration
Content
from typing import Optional, Dict, Any, List
import requests

BASE_URL_CN = "https://api.minimaxi.com/v1"
BASE_URL_INT = "https://api.minimax.io/v1"

def get_base_url() -> str:
Confidence
60% confidence
Finding
Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.

External Transmission

Medium
Category
Data Exfiltration
Content
import requests

BASE_URL_CN = "https://api.minimaxi.com/v1"
BASE_URL_INT = "https://api.minimax.io/v1"

def get_base_url() -> str:
    return BASE_URL_CN if os.getenv("MINIMAX_REGION", "cn") == "cn" else BASE_URL_INT
Confidence
60% confidence
Finding
Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.

External Transmission

Medium
Category
Data Exfiltration
Content
import requests

BASE_URL_CN = "https://api.minimaxi.com/v1"
BASE_URL_INT = "https://api.minimax.io/v1"

def get_base_url() -> str:
    return BASE_URL_CN if os.getenv("MINIMAX_REGION", "cn") == "cn" else BASE_URL_INT
Confidence
60% confidence
Finding
Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.

Tainted flow: 'payload' from requests.get (line 74, network input) → requests.post (network output)

Medium
Category
Data Flow
Content
payload = {"model": model, "text": text, "stream": False,
               "voice_setting": {"voice_id": voice_id},
               "audio_setting": {"sample_rate": sample_rate, "bitrate": bitrate, "format": format}}
    resp = requests.post(url, headers=get_headers(), json=payload, timeout=60)
    resp.raise_for_status()
    result = resp.json()
    audio_b64 = result.get("audio_file") or result.get("data", {}).get("audio")
Confidence
65% confidence
Finding
Data from a source is assigned to a variable that is later passed to a sink, creating a variable-mediated taint flow.

Tainted flow: 'payload' from requests.get (line 74, network input) → requests.post (network output)

Medium
Category
Data Flow
Content
payload = {"model": model, "text": text, "stream": False,
               "voice_setting": {"voice_id": voice_id},
               "audio_setting": {"sample_rate": sample_rate, "bitrate": bitrate, "format": format}}
    resp = requests.post(url, headers=get_headers(), json=payload, timeout=60)
    resp.raise_for_status()
    result = resp.json()
    audio_b64 = result.get("audio_file") or result.get("data", {}).get("audio")
Confidence
65% confidence
Finding
Data from a source is assigned to a variable that is later passed to a sink, creating a variable-mediated taint flow.

Tainted flow: 'payload' from requests.get (line 74, network input) → requests.post (network output)

Medium
Category
Data Flow
Content
payload = {"model": model, "text": text, "stream": False,
               "voice_setting": {"voice_id": voice_id},
               "audio_setting": {"sample_rate": sample_rate, "bitrate": bitrate, "format": format}}
    resp = requests.post(url, headers=get_headers(), json=payload, timeout=60)
    resp.raise_for_status()
    result = resp.json()
    audio_b64 = result.get("audio_file") or result.get("data", {}).get("audio")
Confidence
65% confidence
Finding
Data from a source is assigned to a variable that is later passed to a sink, creating a variable-mediated taint flow.

Missing User Warnings

Medium
Confidence
94% confidence
Finding
The voice cloning path uploads a local audio file to a third-party service without any explicit user-facing disclosure, consent checkpoint, or sensitivity warning in the code path. Because audio files may contain personal data or biometric voice information, silent transmission to an external provider creates meaningful privacy and compliance risk.

Tainted flow: 'payload' from requests.get (line 74, network input) → requests.post (network output)

Medium
Category
Data Flow
Content
# Step 2: 调用音色克隆接口,voice_id 填上传后的 file_id
    clone_url = f"{get_base_url()}/voice_clone"
    payload = {"model": model, "voice_id": file_id, "title": title}
    resp2 = requests.post(clone_url, headers=get_headers(), json=payload, timeout=120)
    resp2.raise_for_status()
    return resp2.json().get("voice_id") or resp2.json().get("data", {}).get("voice_id")
Confidence
65% confidence
Finding
Data from a source is assigned to a variable that is later passed to a sink, creating a variable-mediated taint flow.

Tainted flow: 'payload' from requests.get (line 74, network input) → requests.post (network output)

Medium
Category
Data Flow
Content
def list_voices(category: Optional[str] = None) -> List[Dict[str, Any]]:
    url = f"{get_base_url()}/get_voice"
    payload = {} if category is None else {"category": category}
    resp = requests.post(url, headers=get_headers(), json=payload, timeout=30)
    resp.raise_for_status()
    return resp.json().get("voices", [])
Confidence
65% confidence
Finding
Data from a source is assigned to a variable that is later passed to a sink, creating a variable-mediated taint flow.

Description-Behavior Mismatch

Medium
Confidence
92% confidence
Finding
The implementation includes remote voice deletion capability even though the manifest describes generation, cloning, and design workflows. Undocumented destructive actions expand the skill's authority and can surprise users or calling agents, increasing the risk of accidental or unauthorized deletion of remote assets.

Missing User Warnings

Medium
Confidence
95% confidence
Finding
The delete command triggers remote deletion immediately with no confirmation prompt, dry-run, or warning. In a CLI or agent context, this makes accidental destructive actions much more likely and could cause irreversible loss of cloned or designed voice assets.

Missing User Warnings

Low
Confidence
89% confidence
Finding
The image editing documentation allows local file paths or URLs but does not warn that those inputs may be sent to an external provider. Users may unknowingly upload sensitive local images or cause remote URL fetches involving private or regulated content, creating confidentiality and compliance risks.

Natural-Language Policy Violations

Low
Confidence
92% confidence
Finding
Several user-facing help strings such as `文生图`, `图片描述`, and `输出文件路径` force a specific language in the interface. This can violate language or locale policy when the skill does not provide opt-in, localization, or any documented justification for being Chinese-only.

Description-Behavior Mismatch

Low
Confidence
84% confidence
Finding
The client exposes voice inventory and metadata retrieval operations that are not described in the manifest. This broadens accessible capabilities beyond the declared scope, which can undermine least-privilege assumptions and reveal more account information than users expect.

Natural-Language Policy Violations

Low
Confidence
92% confidence
Finding
Several user-facing help messages and argument descriptions are hard-coded in Chinese, which forces a specific language experience for CLI users. There is no opt-in, alternate locale option, or justification that this tool is intended only for a Chinese-speaking or region-specific audience.

Static analysis

No suspicious patterns detected.