Skip to content

Security: Path Traversal in _load_skill_from_gcs_dir enables arbitrary file write (VRP #499557362) #5603

Closed

Description

@bishtsec

Required Information

Describe the Bug:
The load_skill_from_gcs_dir function in _utils.py extracts blob names from GCS and passes them to os.path.join() without sanitization. Because GCS allows blob names containing traversal sequences (e.g., skills/my_skill/../../../../tmp/malicious.py), a crafted blob name causes os.path.join to discard the intended base directory and write files to arbitrary locations on the host filesystem. This enables arbitrary file write when developers load skills from community-maintained or third-party GCS buckets.

Steps to Reproduce:

  1. Create a public GCS bucket (e.g., gs://test-bucket).
  2. Upload a test file with a traversal blob name, e.g., skills/my_skill/../../../../tmp/adk_traversal_test.txt.
  3. Run the following Python snippet to load the skill:
    from google.adk.skills import load_skill_from_gcs_dir
    load_skill_from_gcs_dir(bucket_name="test-bucket", skill_id="my_skill")
  4. Check your /tmp/ directory. You will see /tmp/adk_traversal_test.txt has been created on the host system, successfully escaping the ADK's intended target directory.

Expected Behavior:
The ADK should sanitize and validate extracted GCS blob names. If a path contains traversal sequences (..) or is absolute, it should raise a ValueError before any file operations occur, ensuring files are only written within the intended local directory.

Observed Behavior:
The unsanitized relative path is passed directly to os.path.join(). If the path contains sufficient ../ sequences, os.path.join resolves it to a location outside the base directory, resulting in an arbitrary file write on the host system.

Environment Details:

  • ADK Library Version: latest (reproduces on current main branch)
  • Desktop OS: All (Linux, macOS, Windows)
  • Python Version: Python 3.x (Standard os.path.join behavior)

Additional Context:
SECURITY NOTICE & EXPLOITABILITY: This issue tracks a vulnerability reported to the Google OSS VRP (Issue #499557362), which has been officially triaged by the Google Security Team.

To demonstrate the critical severity of this bug to the VRP team, I successfully verified that this pattern is actively exploitable in the wild. I identified unclaimed GCS buckets that were hardcoded in existing ADK community repositories (e.g., gs://rad-skills), registered one, and uploaded a benign payload with a traversal blob name.
When a developer clones the repo and loads the skill via the standard workflow, the arbitrary file write chains directly into runpy.run_path(), resulting in live, unauthenticated Remote Code Execution (RCE) on the host machine with zero user interaction beyond running the standard agent.
FIX SUBMITTED: I have already submitted a complete patch that resolves this issue in PR #5281. The PR adds os.path.normpath() validation to block malicious blob names safely. All formatting checks and conflicts have been resolved, and it is awaiting maintainer workflow approval and review.
DISCLOSURE: The planned coordinated disclosure date for this vulnerability is July 3, 2026. I would appreciate it if a maintainer could review and merge PR #5281 prior to this date to protect the ecosystem.
Minimal Reproduction Code:
See Steps to Reproduce above.

Activity

  1. added
    core[Component] This issue is related to the core interface and implementation
    on May 6, 2026
  2. added theissue type on May 6, 2026
  3. surajksharma07 commented on May 8, 2026

    @surajksharma07
    Collaborator

    @cherry-bisht Thanks for the thorough write-up and for already having a patch ready in #5281 — the PurePosixPath ingestion-time check combined with realpath/commonpath at materialization is exactly the right direction here.

    Since the full ADK runtime E2E test wasn't completed (noted in the PR), could you verify the fix holds against the original PoC in an actual ADK session rather than just the standalone script?
    That's the important signal before we move forward.

    Once you confirm both layers block the traversal end-to-end, please do share the results and we'll prioritize the review with the July 3 disclosure window in mind!

    Team is looking more into it.
    Thanks!

  4. added
    request clarification[Status] The maintainer need clarification or more information from the author
    on May 8, 2026
  5. bishtsec commented on May 8, 2026

    @bishtsec
    ContributorAuthor

    @surajksharma07 — E2E verification complete, both layers confirmed.
    Added 7 automated security tests in commit d340122 on PR #5281, covering both defense layers against the original PoC attack vector. All 108 tests pass, 0 regressions.
    Ingestion layer (_utils.py via PurePosixPath):

    .. traversal in blob name → ValueError: Unsafe path ✅
    Absolute path in blob name → ValueError: Unsafe path ✅
    Duplicate normalized path → ValueError: Duplicate normalized path ✅
    Clean blob name → loads correctly ✅

    Materialization layer (skill_toolset.py via realpath + commonpath):

    .. traversal in _files dict → ValueError: Path traversal detected ✅
    Absolute path in _files dict → ValueError: Absolute path rejected ✅
    Safe path → writes correctly inside temp dir ✅

    Both layers independently block the original PoC. The fix is ready for review. Disclosure is July 3 — would appreciate prioritization. Thanks!

  6. added
    needs review[Status] The PR/issue is awaiting review from the maintainer
    and removed
    request clarification[Status] The maintainer need clarification or more information from the author
    on May 8, 2026
  7. surajksharma07 commented on May 8, 2026

    @surajksharma07
    Collaborator

    @cherry-bisht That's a thorough test breakdown — 7 tests across both layers with clean separation between ingestion (PurePosixPath) and materialization (realpath/commonpath) is exactly the coverage we were looking for, and 108/108 with zero regressions is a strong signal.

    Team is looking more into it.
    Thanks!

  8. surajksharma07 commented on May 8, 2026

    @surajksharma07
    Collaborator

    @Jacksunwei Please have a look into it once.

  9. bishtsec commented on May 14, 2026

    @bishtsec
    ContributorAuthor

    @Jacksunwei i understand there’s a significant review load at the moment. If any additional cleanup, refactoring, or changes are required for PR #5281, I’m happy to address them promptly.

    From my testing and review, the patch should be in a mergeable state, but I’d appreciate your feedback on any remaining concerns or edge cases.

    Given that this is a confirmed security vulnerability with demonstrated downstream impact, it would be great if the review could be prioritized ahead of the planned disclosure timeline

  10. added a commit that references this issue on Jun 1, 2026
    cc5d670
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Labels

core[Component] This issue is related to the core interface and implementationneeds review[Status] The PR/issue is awaiting review from the maintainer

Type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions