Back to skill

Security audit

alibabacloud-ecs-vpc-publicnetwork-troubleshoot

Security checks for vulnerabilities and agentic risk

Overview

The skill is a disclosed Alibaba Cloud troubleshooting tool, but it persists reusable cloud credentials locally and can silently reuse ambiguous cached credentials.

Install only if you are comfortable with the skill reading Alibaba Cloud credentials and writing reusable credentials to scripts/.sts_cache.json. Use a least-privilege read-only RAM role, prefer short-lived STS credentials, clear the cache after use, verify the account identity before trusting results, and treat any 'Normal' result with execution notes or errors as incomplete rather than confirmed healthy.

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

T09 · Insecure Skill Coding Practices

Error
Location
scripts/sts_create.py:146
Finding
Plaintext Alibaba Cloud credentials are persistently cached on disk<![CDATA[ ## Vulnerability Details **File Location**: `scripts/sts_create.py:146-177` **Vulnerability Type**: Plaintext credential storage **Risk Level**: High ### Vulnerable Code ```python def save_credentials_to_cache(uid: str, result: dict): """ Write credentials to local cache. Multi-UID coexistence: - Overwrite/add entry for current UID, keep other UIDs unchanged - Also clean up expired entries for other UIDs """ if not result.get('success') or not uid: return cache = _read_cache_file() credentials = cache.get('credentials', {}) or {} credentials = { k: v for k, v in credentials.items() if k == str(uid) or _is_credential_valid(v) } credentials[str(uid)] = { 'access_key_id': result.get('access_key_id'), 'access_key_secret': result.get('access_key_secret'), 'security_token': result.get('security_token'), 'expiration': result.get('expiration'), 'cached_at': datetime.utcnow().strftime("%Y-%m-%dT%H:%M:%SZ"), } payload = {'last_active_uid': str(uid), 'credentials': credentials} try: with open(STS_CACHE_FILE, 'w', encoding='utf-8') as f: json.dump(payload, f, indent=2, ensure_ascii=False) os.chmod(STS_CACHE_FILE, 0o600) except OSError as e: print(f"Warning: Cache write failed: {e}", file=sys.stderr) ``` ### Technical Analysis The function stores the full Alibaba Cloud access-key ID, access-key secret, and optional STS security token in an unencrypted JSON file at `scripts/.sts_cache.json`. File mode `0600` limits access to the owning operating-system account, but it does not protect credentials from: - Other Skills, plugins, or processes executing as the same user. - Malware or a compromised development tool running under that account. - Insecure backups, snapshots, or artifact collection. - Accidental packaging of the cache file. - Local privilege escalation or compromise of the owning account. ...[truncated 1809 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Do not persist static access-key secrets. Keep credentials in process memory and pass an authenticated client object through the workflow. 2. Require short-lived STS credentials with a maximum lifetime appropriate for one diagnostic session, preferably no more than 3,600 seconds. 3. If caching is unavoidable, use an operating-system credential store such as Keychain, Secret Service, or Windows Credential Manager rather than plaintext JSON. 4. Create any unavoidable cache atomically with restrictive permissions at creation time, for example by using `os.open` with mode `0o600`, writing to a secure temporary file, and atomically renaming it. 5. Reject credentials without an expiration for local caching. 6. Delete cached credentials immediately after the diagnostic workflow completes and provide explicit cleanup on exceptions and interrupts. 7. Ensure `.sts_cache.json` is excluded from source control, packages, backups, diagnostic bundles, and support artifacts. 8. Warn users not to run the Skill with owner or broadly privileged cloud credentials. ]]>

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/ecs_public_troubleshoot.py:452
Finding
Diagnostic scripts accept ambiguous, stale, or attacker-replaced cached credentials<![CDATA[ ## Vulnerability Details **File Locations**: - `scripts/ecs_public_troubleshoot.py:452-469` - `scripts/vpc_service_public_troubleshoot.py:367-384` **Vulnerability Type**: Insufficient credential-cache validation **Risk Level**: Medium ### Vulnerable Code The same credential-loading logic appears in both diagnostic scripts: ```python def _load_cached_creds(): """Read credentials from sts_create.py local cache (.sts_cache.json). Returns the credential dict for last_active_uid, or None if unavailable. This lets business scripts consume credentials without requiring the caller to export plaintext AK/SK/token on the command line. """ cache_file = os.path.join(os.path.dirname(os.path.abspath(__file__)), ".sts_cache.json") try: with open(cache_file, "r", encoding="utf-8") as f: raw = json.load(f) except (OSError, json.JSONDecodeError): return None creds = raw.get("credentials", {}) or {} uid = raw.get("last_active_uid") entry = creds.get(str(uid)) if uid else None if not entry and creds: entry = next(iter(creds.values()), None) return entry or None ``` The selected entry is subsequently trusted when constructing the API client: ```python if not (ak and sk): cached = _load_cached_creds() if cached: ak = cached.get("access_key_id") sk = cached.get("access_key_secret") token = cached.get("security_token") ``` ### Technical Analysis The business scripts do not verify: - Whether the cached credential has expired. - Whether all required fields have valid types and formats. - Whether `last_active_uid` corresponds to the requested or expected account. - Whether the cache belongs to the current workflow. - Whether the credential identity matches the identity validated by `sts_create.py`. - Whether the cache file is a regular file rather than an unexpected link or replacement. If `last_active_uid` is missing or does not resolve, the code s ...[truncated 1777 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Remove the fallback to `next(iter(creds.values()))`; require an exact, explicit cache-entry selection. 2. Validate expiration before returning an entry, using timezone-aware parsing and an expiry buffer. 3. Reject credentials that have no expiration unless the user explicitly opts into static credentials. 4. Call `GetCallerIdentity` and compare the returned account ID or ARN with the expected UID before any diagnostic query. 5. Bind each cache entry to a session identifier, creation time, expected account, and intended region or workflow. 6. Validate the cache schema and require nonempty string values for the access-key ID and secret. 7. Verify the cache is a regular file owned by the current user and is not group- or world-accessible. 8. Consolidate cache loading in one hardened module so `sts_create.py` and both business scripts enforce identical validation rules. 9. Abort with a clear credential-selection error when the cache is missing, stale, malformed, or ambiguous. ]]>

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/ecs_public_troubleshoot.py:162
Finding
Required cloud-query failures are silently classified as normal<![CDATA[ ## Vulnerability Details **File Locations**: - `scripts/ecs_public_troubleshoot.py:162-181` - `scripts/ecs_public_troubleshoot.py:286-301` - `scripts/vpc_service_public_troubleshoot.py:167-186` - `scripts/vpc_service_public_troubleshoot.py:326-355` **Vulnerability Type**: Fail-open diagnostic error handling **Risk Level**: Medium ### Vulnerable Code Account-query failures default to a normal state: ```python def query_account(client, region_id): """Step 4: Check account UID and overdue status""" try: uid_data = call_api(client, "sts", "2015-04-01", "GetCallerIdentity", region_id=region_id) balance_data = call_cli("bssopenapi", "QueryAccountBalance", region_id) if "_error" in balance_data: raise RuntimeError(balance_data["_error"]) available = float(balance_data.get("Data", {}).get("AvailableAmount", 0)) return { "uid": uid_data.get("AccountId", ""), "available_amount": available, "is_owed": available < 0, "status": "abnormal" if available < 0 else "normal", "error": None, } except Exception as e: return { "uid": "", "available_amount": 0, "is_owed": False, "status": "normal", "error": str(e) } ``` DDoS and Cloud Firewall failures are also mapped to normal: ```python def query_ddos(region_id, ip, instance_type="ecs"): data = call_cli("antiddos-public", "DescribeInstanceIpAddress", region_id, { "DdosRegionId": region_id, "InstanceType": instance_type, "InstanceIp": ip, }) if "_error" in data: return { "ip_status": "unknown", "status": "normal", "error": data["_error"] } ``` ```python def query_cfw(region_id, ip): data = call_cli("cloudfw", "DescribeAssetList", region_id, { "CurrentPage": "1", "PageSize": "1", "SearchItem": ip, ...[truncated 2640 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Introduce an explicit `unknown` or `error` state rather than mapping failed checks to `normal`. 2. Exit with a nonzero status when a required credential, authorization, account, DDoS, Cloud Firewall, or billing check cannot be completed. 3. Ensure the top-level result contains an unambiguous `success: false` field when mandatory checks fail. 4. Distinguish optional supplementary checks from mandatory checks in both code and documentation. 5. Validate CLI output as JSON with the expected schema, not merely a zero process exit status. 6. Propagate subprocess timeout, executable-not-found, malformed-output, authentication, and authorization failures to the top-level workflow. 7. Prevent the renderer from displaying Normal or Abnormal for checks whose evidence could not be obtained; display “Unknown — check failed” instead. 8. Add tests covering permission denial, missing CLI, invalid JSON, timeout, expired credentials, and unavailable endpoints. 9. Align implementation with the documented circuit-breaker requirement: abort instead of silently degrading after authorization or required-query failures. ]]>
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Privilege EscalationExcessive Permissions, Sudo/Root Execution, Credential Access
  • Excessive AgencyUnrestricted Tool Access, Autonomous Decision Making, Scope Creep
  • Rogue AgentSelf-Modification, Session Persistence
  • MCP Least PrivilegeUnderdeclared Capability, Wildcard Permission, Missing Permission Declaration
Findings (19)

Lp3

Medium
Category
MCP Least Privilege
Confidence
88% confidence
Finding
The skill is granted or expects powerful execution capabilities (shell, environment access, file read/write, MCP) while not declaring permissions explicitly. That creates an opaque trust boundary: a caller may believe this is a simple diagnostic skill, but it can run local commands, access credentials, and persist data on disk. In this context, those capabilities are directly used to obtain cloud credentials and execute scripts, so the missing declaration meaningfully increases operational risk.

Tp4

High
Category
MCP Tool Poisoning
Confidence
96% confidence
Finding
The skill claims to perform read-only connectivity diagnostics, but it also acquires credentials, validates identity, and persists temporary access credentials to `scripts/.sts_cache.json` for reuse. That is a material behavioral expansion beyond troubleshooting output generation, and credential caching on disk introduces exposure if the host is shared, compromised, or logs/artifacts are collected. Because the skill handles cloud auth, even temporary tokens can enable unintended access within their privilege scope.

Context-Inappropriate Capability

Medium
Confidence
93% confidence
Finding
The script reads Alibaba Cloud credentials from a local .sts_cache.json file, expanding its authority beyond the explicitly supplied execution context. That creates a hidden credential-acquisition path: if the cache belongs to a different user, account, or prior session, the skill may access cloud resources with unintended privileges and without clear user awareness.

Intent-Code Divergence

Medium
Confidence
90% confidence
Finding
The docstring states the script only operates on a single user-specified resource, but it also performs account-level identity and balance queries. This mismatch is dangerous because it defeats user expectations and consent boundaries, causing the skill to collect broader account metadata than its declared scope.

Description-Behavior Mismatch

High
Confidence
99% confidence
Finding
This file is a credential acquisition and STS issuance utility, but the declared skill is for ECS/VPC public-network troubleshooting. That capability mismatch is dangerous because it introduces privileged credential collection and session creation into a context where operators would reasonably expect read-only network diagnostics, increasing the chance of unnecessary credential exposure and misuse.

Context-Inappropriate Capability

High
Confidence
99% confidence
Finding
The code reads credentials from multiple local sources, validates them, and can assume roles to obtain temporary cloud access. In a network-troubleshooting skill, that is an unjustified expansion of capability that materially increases blast radius: anyone running the skill may unknowingly grant account-level access far beyond what is needed to diagnose connectivity.

Context-Inappropriate Capability

High
Confidence
98% confidence
Finding
The script persists access keys and security tokens to a local cache for later reuse by other scripts. Storing reusable cloud credentials on disk creates an attractive target for local compromise, accidental disclosure, or cross-script abuse, especially because this capability is not justified by the skill's stated public-network diagnosis purpose.

Intent-Code Divergence

Medium
Confidence
82% confidence
Finding
The documentation states that credentials are never printed in plaintext, but the code can include raw CLI stderr or response fragments in error messages. If the CLI or intermediary tooling emits sensitive material on failure, those values could be exposed to stdout, logs, or calling systems, violating the stated security guarantee.

Context-Inappropriate Capability

Medium
Confidence
87% confidence
Finding
The script performs diagnostics but also silently reads reusable cloud credentials from a local .sts_cache.json file. That broadens its trust boundary and can cause the script to operate with credentials the user did not explicitly provide, increasing the risk of unintended account access and secret exposure through local compromise or misuse.

Missing User Warnings

Medium
Confidence
95% confidence
Finding
The skill instructs operators to disable Cloud Firewall protection as a troubleshooting step without any warning, approval gate, scope limitation, or compensating controls. That can weaken perimeter defenses and expose public IPs or SNAT EIPs to attack, especially if users follow the guidance in production environments.

Missing User Warnings

Medium
Confidence
91% confidence
Finding
The script reads credentials from a local cache without any user-facing disclosure in its runtime behavior. In an agent skill context, undisclosed credential sourcing is risky because the operator may believe the script is read-only diagnostics on a specified resource while it can silently authenticate with cached privileges.

Env Variable Harvesting

High
Category
Data Exfiltration
Content
config, or instance metadata can still be picked up.
    """
    ak = os.environ.get("ALIBABA_CLOUD_ACCESS_KEY_ID")
    sk = os.environ.get("ALIBABA_CLOUD_ACCESS_KEY_SECRET")
    token = os.environ.get("ALIBABA_CLOUD_SECURITY_TOKEN")
    # Fall back to sts_create.py local cache when env vars are absent,
    # avoiding the need to export plaintext credentials on the command line.
Confidence
88% confidence
Finding
The script reads a secret access key from the environment and combines that with fallback credential discovery, giving the skill the ability to silently consume privileged credentials present in the host context. In an agent-skill environment, this broad credential intake is risky because the troubleshooting purpose does not require autonomous credential sourcing beyond explicitly authorized session credentials.

Env Variable Harvesting

High
Category
Data Exfiltration
Content
"""
    ak = os.environ.get("ALIBABA_CLOUD_ACCESS_KEY_ID")
    sk = os.environ.get("ALIBABA_CLOUD_ACCESS_KEY_SECRET")
    token = os.environ.get("ALIBABA_CLOUD_SECURITY_TOKEN")
    # Fall back to sts_create.py local cache when env vars are absent,
    # avoiding the need to export plaintext credentials on the command line.
    if not (ak and sk):
Confidence
88% confidence
Finding
The script reads an STS security token from ambient environment variables, allowing it to inherit temporary credentials from the surrounding runtime without explicit user selection. In an agent setting, this increases blast radius because any available token may be used to enumerate account, network, and billing data beyond the immediate target resource.

Env Variable Harvesting

High
Category
Data Exfiltration
Content
AcsClient.__del__ = _quiet_acs_del

    try:
        ak = os.environ.get("ALIBABA_CLOUD_ACCESS_KEY_ID")
        sk = os.environ.get("ALIBABA_CLOUD_ACCESS_KEY_SECRET")
        token = os.environ.get("ALIBABA_CLOUD_SECURITY_TOKEN")
        if ak and sk:
Confidence
96% confidence
Finding
This line reads the Alibaba Cloud access key ID from environment variables as part of a general credential acquisition flow. In isolation that can be legitimate, but in this skill context it is dangerous because the skill is not an authentication utility; it is a network-diagnosis skill, so harvesting ambient credentials from the execution environment is an unnecessary and risky capability.

Env Variable Harvesting

High
Category
Data Exfiltration
Content
try:
        ak = os.environ.get("ALIBABA_CLOUD_ACCESS_KEY_ID")
        sk = os.environ.get("ALIBABA_CLOUD_ACCESS_KEY_SECRET")
        token = os.environ.get("ALIBABA_CLOUD_SECURITY_TOKEN")
        if ak and sk:
            if token:
Confidence
96% confidence
Finding
This line reads the Alibaba Cloud access key secret from the environment, which is highly sensitive material. Because the skill's purpose is troubleshooting public-network connectivity rather than managing authentication, accessing ambient secrets expands exposure without clear necessity and could facilitate unauthorized cloud access if combined with the surrounding logic.

Env Variable Harvesting

High
Category
Data Exfiltration
Content
try:
        ak = os.environ.get("ALIBABA_CLOUD_ACCESS_KEY_ID")
        sk = os.environ.get("ALIBABA_CLOUD_ACCESS_KEY_SECRET")
        token = os.environ.get("ALIBABA_CLOUD_SECURITY_TOKEN")
        if ak and sk:
            if token:
                # AcsClient has no set_security_token method; STS tokens must be
Confidence
96% confidence
Finding
This line reads a security token from the environment, enabling the script to consume temporary but still powerful session credentials. In this context, that is still credential harvesting: the skill can appropriate ambient cloud identity that is unrelated to the narrow public-network troubleshooting purpose, making misuse or overreach more likely.

Credential Access

High
Category
Privilege Escalation
Content
def main():
    parser = argparse.ArgumentParser(
        description="Get Alibaba Cloud access credentials (for customer self-service use)",
        formatter_class=argparse.RawDescriptionHelpFormatter,
        epilog="""
Examples:
Confidence
94% confidence
Finding
The script is explicitly designed to obtain Alibaba Cloud access credentials, which is a privileged capability. That is materially inconsistent with the declared skill purpose and increases the risk that a user invokes a troubleshooting tool that actually performs credential collection and privilege acquisition behind the scenes.

Session Persistence

Medium
Category
Rogue Agent
Content
def save_credentials_to_cache(uid: str, result: dict):
    """
    Write credentials to local cache. Multi-UID coexistence:
    - Overwrite/add entry for current UID, keep other UIDs unchanged
    - Also clean up expired entries for other UIDs
    """
    if not result.get('success') or not uid:
Confidence
95% confidence
Finding
This code path adds credential entries for multiple UIDs to a persistent local cache, enabling session reuse across runs. Persistent session material increases the window in which a local attacker, another process, or an unintended downstream script can reuse cloud access without reauthentication.

Session Persistence

Medium
Category
Rogue Agent
Content
CACHE_EXPIRY_BUFFER = 60


# ---------------- Cache Read/Write ----------------

def _read_cache_file() -> dict:
    """
Confidence
91% confidence
Finding
The cache read/write subsystem is part of a broader design for maintaining reusable credential state on disk. While persistence itself is not always malicious, in this skill context it amplifies risk because a network troubleshooting tool should not quietly maintain credential sessions for later consumption by other scripts.

Static analysis

No suspicious patterns detected.