Back to skill

Security audit

Alibabacloud Avatar Video

Security checks for vulnerabilities and agentic risk

Overview

This appears to be a legitimate Alibaba Cloud media-generation skill, but it needs Review because it can send credentials and sensitive media to environment-controlled endpoints and has weak download safeguards.

Install only if you are comfortable sending selected media, prompts, scripts, and cloud credentials through this skill's Alibaba Cloud workflows. Use least-privilege temporary credentials, verify endpoint environment variables are official Alibaba Cloud hosts before running, avoid sensitive personal media unless you understand provider handling and signed URL exposure, and treat download mode cautiously until host allowlisting and bounded downloads are added.

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 (4)

T09 · Insecure Skill Coding Practices

Error
Location
scripts/animate_anyone.py:44
Finding

Configurable DashScope Base URL Can Expose Bearer Credentials and Submitted Content

Content
View full analysis
dict: key = os.environ.get("DASHSCOPE_API_KEY") if not key: raise RuntimeError("DASHSCOPE_API_KEY not set") h = { "Authorization": f"Bearer {key}", "Content-Type": "application/json", "User-Agent": USER_AGENT, } if async_mode: h["X-DashScope-Async"] = "enable" return h def wait_task(task_id: str, interval: int = 10, max_wait: int = 1800) -> dict: """Poll until task SUCCEEDED/FAILED, return full output dict.""" url = f"{BASE_URL}/api/v1/tasks/{task_id}" start = time.time() while time.time() - start < max_wait: r = requests.get(url, headers=_headers(), timeout=60) ``` Authenticated submission from the same file: ```python r = requests.post( f"{BASE_URL}/api/v1/services/aigc/image2video/aa-detect", headers=_headers(async_mode=False), json={"model": "animate-anyone-detect-gen2", "input": {"image_url": image_url}}, timeout=60, ) ``` The same design is used by the other listed DashScope scripts. For example, `scripts/text_to_image.py` contains: ```python BASE_URL = os.getenv("DASHSCOPE_BASE_URL", "https://dashscope.aliyuncs.com") def _headers(async_mode: bool = False) -> dict: key = os.environ.get("DASHSCOPE_API_KEY") if not key: raise RuntimeError("DASHSCOPE_API_KEY not set") h = { "Authorizatio ...[truncated 2436 chars]
Remediation
View remediation

T09 · Insecure Skill Coding Practices

Error
Location
scripts/animate_anyone.py:123
Finding

Configurable OSS Endpoint Can Redirect Authenticated Uploads and Sensitive Media

Content
View full analysis
str: """Upload file to OSS and return signed GET URL (default 3 days).""" import oss2 auth = oss2.Auth( os.environ["ALIBABA_CLOUD_ACCESS_KEY_ID"], os.environ["ALIBABA_CLOUD_ACCESS_KEY_SECRET"], ) bucket_name = os.environ["OSS_BUCKET"] endpoint = os.environ.get("OSS_ENDPOINT", "oss-cn-beijing.aliyuncs.com") endpoint = endpoint.replace("https://", "").replace("http://", "").rstrip("/") bucket = oss2.Bucket(auth, f"https://{endpoint}", bucket_name) key = f"human-avatar/{Path(local_path).name}" print(f"[oss] uploading {Path(local_path).name} …") bucket.put_object_from_file(key, local_path) url = bucket.sign_url("GET", key, expires) print(f"[oss] signed_url ok ({expires//3600}h)") return url ``` Equivalent endpoint construction and upload logic is present in the other listed scripts. ### Technical Analysis `OSS_ENDPOINT` is treated as a trusted Alibaba Cloud endpoint after only removing an optional scheme prefix. The code does not verify that the resulting hostname belongs to an expected OSS region or custom domain. The OSS SDK authenticates requests using `ALIBABA_CLOUD_ACCESS_KEY_ID` and `ALIBABA_CLOUD_ACCESS_KEY_SECRET`, then uploads local portrait images, audio, or videos to the configured destination. If the endpoint is malicious, the destination can receive sensitive user media and cloud-authenticated request material. Although cryptographic request signing generally does not transmit the raw access-key secret, a malic ...[truncated 1676 chars]
Remediation
View remediation

T09 · Insecure Skill Coding Practices

Error
Location
scripts/avatar_video.py:31
Finding

Configurable LingMou Endpoint Can Receive AccessKey-Signed Requests and Video Scripts

Content
View full analysis
Remediation
View remediation

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/input_validation.py:23
Finding

Insufficient Result URL Validation Enables SSRF and Unbounded File Downloads

Content
View full analysis
str: u = url.strip() parsed = urlparse(u) if parsed.scheme not in ("http", "https"): raise ValueError(f"{field} must use http or https scheme: {url!r}") if not parsed.netloc: raise ValueError(f"{field} must include a host: {url!r}") return u ``` Representative download sink from `scripts/live_portrait.py`: ```python if args.download: out_path = resolve_under_cwd(args.output, field="--output") safe_url = validate_http_https_url(video_url, field="result video URL") print(f"Downloading → {out_path} …") with urllib.request.urlopen(safe_url, timeout=300) as response: with open(out_path, 'wb') as f: f.write(response.read()) size_kb = out_path.stat().st_size // 1024 print(f"Saved {out_path} ({size_kb}KB)") ``` Equivalent full-response downloads are used in the other listed scripts. ### Technical Analysis The result URL validator accepts any HTTP or HTTPS host, including loopback addresses, private networks, link-local addresses, cloud metadata services, and attacker-controlled domains. `urllib.request.urlopen()` follows HTTP redirects, but redirect destinations are not revalidated. The scripts read the entire response into memory using `response.read()` and then write it to disk without enforcing a maximum byte count, validating `Content-Length`, or limiting the decompressed ...[truncated 2233 chars]
Remediation
View remediation
MAX_DOWNLOAD: raise ValueError("Download exceeds configured limit") output.write(chunk) ``` 7. Check `Content-Length` before downloading when present, while still enforcing the streaming limit because that header is untrusted. 8. Download to a temporary file and atomically rename it only after successful size and media-format validation. 9. Apply separate reasonable maximum sizes for images, audio, and videos. 10. Validate expected media type and, where appropriate, inspect the resulting file with a safe parser before treating it as generated media. ]]>
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Trigger AbuseOverly Broad Trigger, Shadow Command Trigger, Keyword Baiting Trigger
  • 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
Findings (52)

Tainted flow: 'url' from os.getenv (line 162, credential/environment) → requests.get (network output)

Critical
Category
Data Flow
Confidence
90% confidence
Finding

Task polling uses a URL built from the same environment-controlled BASE_URL and sends the Authorization header on every poll. If BASE_URL is malicious, the code repeatedly leaks the API key and task metadata, amplifying credential exposure.

Content

Scanner excerpt · scripts/animate_anyone.py (reported line 165)May include surrounding context.

python
url = f"{BASE_URL}/api/v1/tasks/{task_id}"
    start = time.time()
    while time.time() - start < max_wait:
        r = requests.get(url, headers=_headers(), timeout=60)
        r.raise_for_status()
        data = r.json()
        out = data.get("output", {})

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

Critical
Category
Data Flow
Confidence
90% confidence
Finding

The code allows the destination API host to be overridden via DASHSCOPE_BASE_URL from the environment, then sends authenticated requests with the Bearer API key to that host. If an attacker can influence the environment or deployment configuration, they can redirect requests and exfiltrate credentials and user media references to an attacker-controlled endpoint.

Content

Scanner excerpt · scripts/animate_anyone.py (reported line 188)May include surrounding context.

python
Returns output dict with check_pass, bodystyle.
    """
    print(f"\n[step1] aa-detect …")
    r = requests.post(
        f"{BASE_URL}/api/v1/services/aigc/image2video/aa-detect",
        headers=_headers(async_mode=False),
        json={"model": "animate-anyone-detect-gen2", "input": {"image_url": image_url}},

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

Critical
Category
Data Flow
Confidence
90% confidence
Finding

This request inherits the same environment-controlled BASE_URL behavior and includes authenticated headers. A hostile base URL would receive the API token and user-supplied video URL, enabling credential theft and redirection of sensitive workflow data.

Content

Scanner excerpt · scripts/animate_anyone.py (reported line 216)May include surrounding context.

python
Returns template_id str.
    """
    print(f"\n[step2] aa-template-generation …")
    r = requests.post(
        f"{BASE_URL}/api/v1/services/aigc/image2video/aa-template-generation/",
        headers=_headers(async_mode=True),
        json={"model": "animate-anyone-template-gen2", "input": {"video_url": video_url}},

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

Critical
Category
Data Flow
Confidence
90% confidence
Finding

The environment-controlled BASE_URL is again used for an authenticated POST, exposing the API key and workflow inputs to any attacker-controlled endpoint configured through the environment. In a skill handling user media generation, this increases the sensitivity because media URLs and derived identifiers may be disclosed alongside credentials.

Content

Scanner excerpt · scripts/animate_anyone.py (reported line 251)May include surrounding context.

python
"input": {"image_url": image_url, "template_id": template_id},
        "parameters": {"use_ref_img_bg": use_ref_img_bg, "video_ratio": video_ratio},
    }
    r = requests.post(
        f"{BASE_URL}/api/v1/services/aigc/image2video/video-synthesis/",
        headers=_headers(async_mode=True),
        json=payload,

Tainted flow: 'url' from os.environ.get (line 100, credential/environment) → requests.get (network output)

Critical
Category
Data Flow
Confidence
90% confidence
Finding

Task polling uses a URL built from the same environment-controlled BASE_URL and sends authenticated headers on every poll. A malicious or compromised environment can redirect these GET requests to an attacker-controlled endpoint, leaking the bearer token and task metadata while also enabling arbitrary outbound requests.

Content

Scanner excerpt · scripts/image_to_video.py (reported line 71)May include surrounding context.

python
url = f"{BASE_URL}/api/v1/tasks/{task_id}"
    start = time.time()
    while time.time() - start < max_wait:
        r = requests.get(url, headers=_headers(async_mode=False), timeout=30)
        r.raise_for_status()
        data = r.json()
        out = data.get("output", {})

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

Critical
Category
Data Flow
Confidence
90% confidence
Finding

The HTTP POST target is derived from DASHSCOPE_BASE_URL, an environment variable, and the request includes the Authorization bearer token. If an attacker can influence the environment, they can redirect authenticated API traffic to an arbitrary host and capture credentials and submitted media metadata, creating an SSRF-style exfiltration path for secrets.

Content

Scanner excerpt · scripts/image_to_video.py (reported line 184)May include surrounding context.

python
payload = {"model": model, "input": inp, "parameters": params}

    print(f"\n[i2v] submit  model={model}  resolution={resolution}  duration={duration}s")
    r = requests.post(endpoint, headers=_headers(async_mode=True), json=payload, timeout=60)
    r.raise_for_status()
    data = r.json()
    task_id = (data.get("output") or {}).get("task_id")

Tainted flow: 'url' from os.environ.get (line 115, credential/environment) → requests.get (network output)

Critical
Category
Data Flow
Confidence
90% confidence
Finding

The polling URL is derived from BASE_URL and reused with authenticated GET requests, so the same endpoint-redirection issue applies during task status polling. An attacker controlling the environment could harvest bearer tokens or manipulate responses to steer application behavior. The risk is amplified because this code trusts returned JSON to drive workflow decisions.

Content

Scanner excerpt · scripts/live_portrait.py (reported line 75)May include surrounding context.

python
start = time.time()
    while time.time() - start < max_wait:
        try:
            r = requests.get(url, headers=_headers(), timeout=30)
            r.raise_for_status()
            data = r.json()
            out = data.get("output", {})

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

Critical
Category
Data Flow
Confidence
92% confidence
Finding

BASE_URL is taken from an environment variable and used to construct the API endpoint that receives the Authorization bearer token. If an attacker can influence the environment, they can redirect requests and exfiltrate the DashScope API key and submitted media URLs to an arbitrary host. In this skill context, the code handles cloud credentials and signed media URLs, which makes endpoint redirection materially more sensitive than a normal configurable base URL.

Content

Scanner excerpt · scripts/live_portrait.py (reported line 184)May include surrounding context.

python
Raises ValueError if check fails.
    """
    print(f"\n[step1] liveportrait-detect …")
    r = requests.post(
        f"{BASE_URL}/api/v1/services/aigc/image2video/face-detect",
        headers=_headers(async_mode=False),
        json={"model": "liveportrait-detect", "input": {"image_url": image_url}},

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

Critical
Category
Data Flow
Confidence
92% confidence
Finding

This request sends the bearer token and generation payload to a URL derived from the environment-controlled BASE_URL. A malicious override could redirect authenticated requests to an attacker-controlled service, exposing credentials, signed OSS URLs, and user media references. Because this skill performs media generation and may process sensitive portrait/audio data, the confidentiality impact is elevated.

Content

Scanner excerpt · scripts/live_portrait.py (reported line 233)May include surrounding context.

python
"head_move_strength": head_move_strength,
        },
    }
    r = requests.post(
        f"{BASE_URL}/api/v1/services/aigc/image2video/video-synthesis/",
        headers=_headers(async_mode=True),
        json=payload,

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

Critical
Category
Data Flow
Confidence
90% confidence
Finding

Credentials or environment variables flow to a network sink. This is a high-confidence indicator of credential exfiltration.

Content

Scanner excerpt · scripts/portrait_animate.py (reported line 67)May include surrounding context.

python
"input": {"image_url": image_url},
        "parameters": {"ratio": ratio},
    }
    r = requests.post(url, headers=_headers(), json=payload, timeout=120)
    r.raise_for_status()
    data = r.json()
    out = data.get("output", {})

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

Critical
Category
Data Flow
Confidence
90% confidence
Finding

Credentials or environment variables flow to a network sink. This is a high-confidence indicator of credential exfiltration.

Content

Scanner excerpt · scripts/portrait_animate.py (reported line 88)May include surrounding context.

python
},
        "parameters": {"style_level": style_level},
    }
    r = requests.post(url, headers=_headers(async_mode=True), json=payload, timeout=120)
    r.raise_for_status()
    data = r.json()
    task_id = data.get("output", {}).get("task_id")

Tainted flow: 'url' from os.getenv (line 98, credential/environment) → requests.get (network output)

Critical
Category
Data Flow
Confidence
90% confidence
Finding

Credentials or environment variables flow to a network sink. This is a high-confidence indicator of credential exfiltration.

Content

Scanner excerpt · scripts/portrait_animate.py (reported line 101)May include surrounding context.

python
url = f"{BASE_URL}/api/v1/tasks/{task_id}"
    start = time.time()
    while time.time() - start < max_wait:
        r = requests.get(url, headers=_headers(), timeout=60)
        r.raise_for_status()
        data = r.json()
        status = data.get("output", {}).get("task_status")

Tainted flow: 'url' from os.getenv (line 48, credential/environment) → requests.get (network output)

Critical
Category
Data Flow
Confidence
91% confidence
Finding

The polling URL is built from DASHSCOPE_BASE_URL, an environment variable, and then requested with authorization headers containing the API key. If an attacker can influence the environment, they can redirect requests to an arbitrary host and capture the bearer token or force the agent to communicate with unintended internal or external services. In an agent/skill context, environment-driven endpoint override is more dangerous because skills often run with privileged API credentials in shared automation environments.

Content

Scanner excerpt · scripts/text_to_image.py (reported line 51)May include surrounding context.

python
url = f"{BASE_URL}/api/v1/tasks/{task_id}"
    start = time.time()
    while time.time() - start < max_wait:
        r = requests.get(url, headers=_headers(), timeout=30)
        r.raise_for_status()
        data = r.json()
        status = data.get("output", {}).get("task_status", "UNKNOWN")

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

Critical
Category
Data Flow
Confidence
94% confidence
Finding

The synchronous generation endpoint is derived from DASHSCOPE_BASE_URL and called with the Authorization bearer token. A malicious or compromised environment can point this request to an attacker-controlled server, causing credential exfiltration and unintended outbound access. Because this skill is specifically designed to call cloud APIs, the presence of a configurable base URL materially increases SSRF-style risk and secret exposure.

Content

Scanner excerpt · scripts/text_to_image.py (reported line 111)May include surrounding context.

python
# Synchronous (wan2.6)
        endpoint = f"{BASE_URL}/api/v1/services/aigc/multimodal-generation/generation"
        print(f"[t2i] sync call  model={model}  size={size}  n={n}")
        r = requests.post(endpoint, headers=_headers(), json=payload, timeout=120)
        r.raise_for_status()
        data = r.json()
        images = []

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

Critical
Category
Data Flow
Confidence
94% confidence
Finding

The asynchronous generation request uses an endpoint assembled from an environment variable and sends the API key in the request headers. If the base URL is attacker-controlled, this becomes a straightforward way to leak credentials and direct the skill to arbitrary network destinations. The agent context makes this more dangerous because users may trigger network actions indirectly without visibility into the configured runtime environment.

Content

Scanner excerpt · scripts/text_to_image.py (reported line 126)May include surrounding context.

python
# Async (wan2.5 and below) — also works for wan2.6
        endpoint = f"{BASE_URL}/api/v1/services/aigc/image-generation/generation"
        print(f"[t2i] async call  model={model}  size={size}  n={n}")
        r = requests.post(endpoint, headers=_headers(async_mode=True), json=payload, timeout=60)
        r.raise_for_status()
        data = r.json()
        task_id = data.get("output", {}).get("task_id")

Tainted flow: 'safe_url' from os.getenv (line 173, credential/environment) → urllib.request.urlopen (network output)

Critical
Category
Data Flow
Confidence
90% confidence
Finding

Credentials or environment variables flow to a network sink. This is a high-confidence indicator of credential exfiltration.

Content

Scanner excerpt · scripts/text_to_image.py (reported line 175)May include surrounding context.

python
if args.download:
            safe_url = validate_http_https_url(url, field="image URL")
            filename = out_dir / f"t2i_{int(time.time())}_{i+1}.png"
            with urllib.request.urlopen(safe_url, timeout=300) as response:
                with open(filename, 'wb') as f:
                    f.write(response.read())
            size_kb = filename.stat().st_size // 1024

Tp4

High
Category
MCP Tool Poisoning
Confidence
98% confidence
Finding

The code chunk is narrowly focused on LingMou broadcast-template-based avatar video creation. It does support part of the declared description: random template selection, public-template copying, script/text variable filling, task submission, polling, and download. However, it does not implement the majority of the seven declared capabilities, especially any DashScope-based video/speech/image generation features (LivePortrait, EMO, AnimateAnyone, T2I, I2V, Qwen TTS). Therefore the declared description materially overstates what this code chunk actually does.

Content

No source excerpt is available for this finding.

Tp4

High
Category
MCP Tool Poisoning
Confidence
93% confidence
Finding

This code’s actual purpose is a wrapper/orchestrator for three modes: emo, aa, and lingmou. That partially aligns with the declared description, specifically EMO, AnimateAnyone, and LingMou video generation. However, the declared description presents a broader seven-capability skill including LivePortrait, T2I, I2V, and Qwen TTS, none of which are implemented or invoked in the supplied code chunk. There is also no explicit two-step LivePortrait flow or T2I→I2V pipeline here. The code does not exhibit harmful undeclared behavior; the mismatch is that the description overstates functionality relative to this chunk.

Content

No source excerpt is available for this finding.

Undeclared Tool Scope

Medium
Category
MCP Least Privilege
Confidence
91% confidence
Finding

The skill advertises capabilities that require shell, network, filesystem, and environment-variable access, yet it does not declare any explicit tool scope or permission boundaries. This creates an over-privileged and non-transparent execution model where a host agent may invoke sensitive capabilities without clear restriction or review.

Content

No source excerpt is available for this finding.

Vague Triggers

Medium
Category
Not specified by scanner
Confidence
94% confidence
Finding

The trigger language is broad enough to match many common user requests involving images, video, or speech, increasing the chance the skill is invoked in contexts the user did not intend. Because the skill can upload media to external cloud services and run shell-based media processing, over-triggering can cause unintended data handling and unexpected external transmission.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
94% confidence
Finding

The skill openly relies on API keys, OSS uploads, and user-supplied public URLs, but it does not present a clear user-facing disclosure that images, audio, video, and text may be transmitted to Alibaba Cloud services or stored in object storage. In a multimedia skill handling potentially sensitive personal media, lack of explicit disclosure and consent materially increases privacy and data-exposure risk.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
91% confidence
Finding

The document explicitly requires public image/video URLs and states that local files are automatically converted and uploaded for processing, but it does not clearly warn users that their media will be sent to external cloud services and may become publicly accessible. In a skill that handles user portraits and videos, this creates a meaningful privacy and data-exposure risk, especially if users assume files are processed locally or remain private.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
91% confidence
Finding

The documentation instructs sending publicly accessible portrait and audio URLs to Alibaba Cloud's external DashScope API, which involves transferring biometric and voice data off-platform. Because the skill is specifically designed for talking-head and speech generation, the data is likely sensitive, and the docs provide no privacy warning, consent guidance, retention notice, or data-handling limitations to help users assess that exposure.

Content

No source excerpt is available for this finding.

External Transmission

Medium
Category
Data Exfiltration
Confidence
50% confidence
Finding

Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.

Content

Scanner excerpt · references/lingmou-api.md (reported line 9)May include surrounding context.

md
- Batch query broadcast videos:
  `https://help.aliyun.com/zh/avatar/avatar-application/developer-reference/api-lingmou-2025-05-27-listbroadcastvideosbyid`
- Get broadcast template:
  `https://api.aliyun.com/api/LingMou/2025-05-27/GetBroadcastTemplate`
- List broadcast templates:
  `https://api.aliyun.com/api/LingMou/2025-05-27/ListBroadcastTemplates`

External Transmission

Medium
Category
Data Exfiltration
Confidence
50% confidence
Finding

Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.

Content

Scanner excerpt · references/lingmou-api.md (reported line 11)May include surrounding context.

md
- Batch query broadcast videos:
  `https://help.aliyun.com/zh/avatar/avatar-application/developer-reference/api-lingmou-2025-05-27-listbroadcastvideosbyid`
- Get broadcast template:
  `https://api.aliyun.com/api/LingMou/2025-05-27/GetBroadcastTemplate`
- List broadcast templates:
  `https://api.aliyun.com/api/LingMou/2025-05-27/ListBroadcastTemplates`