Skip to content
Digital Infrastructure Filemanager Path Traversal Rw

Digital Infrastructure Filemanager Path Traversal Rw

hackmd.io • October 14, 2026

# CVE-2026-110115, CVE-2026-110116: risesoft-y9 Digital-Infrastructure file manager: unauthenticated path traversal in retrieveFileStream and storeFile for arbitrary file read and write

![type](

![CWE](

![CWE2](

![CWE3](

![component](

![severity](

![status](

**Reported by:** hieuPenguinnn, [github.com/hieuPenguinnn](

**Project:** [risesoft-y9/Digital-Infrastructure]( file manager microservice (`fileManager`), `Y9FileController`

**Affected:** confirmed in release `v9.6.7` (commit `b9e1868755b801416cdb1be3587b4d8e5846a0a0`) and the latest tag `v9.6.11` (commit `b9dddb7c80d15c48ba912a6454fe926d6d191830`). The two sinks are byte-identical across both revisions. Same sink pattern as CVE-2025-7108, which was published against 9.6.0 through 9.6.7, so plausibly present across the 9.6.x line. Earlier range not bisected.

**Class:** CWE-22 Path Traversal, CWE-73 External Control of File Name or Path, CWE-306 Missing Authentication for Critical Function

**Status:** Coordinated disclosure in progress. No CVE assigned yet.

The file manager microservice of risesoft-y9 Digital-Infrastructure ships a single controller, `Y9FileController`, whose request handlers build a filesystem path by string concatenation of a configured root directory with two request parameters, `fullPath` and `fileName`, and then read or write that path with no normalization and no containment check. The known CVE-2025-7108 covers only the `deleteFile` handler. The same controller has two sibling handlers with the same defect that the existing CVE does not cover. `retrieveFileStream` at `/fileManager/rest/retrieveFileStream` streams the resolved file back to the caller, which gives arbitrary file read. `storeFile` at `/fileManager/rest/storeFile` writes an uploaded multipart body to the resolved path after creating any missing parent directories, which gives arbitrary file write.

The microservice declares no Spring Security starter and no authentication filter in its build file, so these handlers are reachable without credentials by any client that can reach the service, the same exposure model under which CVE-2025-7108 was published as a remote path traversal. An unauthenticated caller can read any file the service process can open, including the application configuration that holds database, Redis, and Nacos credentials, and can write a file to any path the process can write, which can lead to code execution when the written file lands in a location the server later serves or runs. Both handlers are present and unchanged in the latest tag at the time of writing.

The controller is `net.risesoft.y9public.controller.Y9FileController` in the `fileManager` webapp module. It holds a configured root path and a repository, and every path-building handler follows the same two-step construction.

### 1. The configured root and the direct concatenation

The root comes from `Y9FileManagerProperties`, bound to the prefix `y9.app.file-manager`, with a default value.

@ConfigurationProperties(prefix = "y9.app.file-manager", ignoreInvalidFields = true, ignoreUnknownFields = true)

private String fileRoot = "d:/y9config/y9filestore";

Each handler builds the working directory as `new File(getFileRoot() + fullPath)`, where `fullPath` is a raw request parameter. Java `File` concatenation does not normalize `..` segments or reject absolute values, so a `fullPath` that climbs above the root, or a `fileName` that is itself a relative traversal, resolves to a location outside `fileRoot`. Nothing in the controller canonicalizes the result or checks that it stays inside `fileRoot`.

### 2. `retrieveFileStream` is an arbitrary file read sink

The handler is mapped at `/rest/retrieveFileStream` and takes `fullPath` and `fileName` directly from the request.

@RequestMapping(value = "/rest/retrieveFileStream")

public void retrieveFileStream(String fullPath, String fileName, HttpServletRequest request,

HttpServletResponse response) throws Exception {

ServletOutputStream out = response.getOutputStream();

File path = new File(y9FileManagerProperties.getFileRoot() + fullPath);

// ... Content-disposition header set from fileName ...

response.setContentType("application/octet-stream");

The resolved `file` is the attacker-controlled path. The only gate is `file.exists()`. When the file exists, `FileUtils.copyFile(file, out)` at line 159 copies its full byte content to the HTTP response. There is no check that `file` is inside `fileRoot`, no allowlist, and no identifier lookup. A request whose `fullPath` points above the configured root and whose `fileName` names a target file returns that file. This reads any file the service process can open.

### 3. `storeFile` is an arbitrary file write sink

The handler is mapped at `/rest/storeFile` and takes a multipart body plus the same two raw parameters.

public String storeFile(MultipartFile multipartFile, String fullPath, String fileName) throws Exception {

File path = new File(y9FileManagerProperties.getFileRoot() + fullPath);

`path.mkdirs()` at line 173 creates the traversed directory chain if it does not exist, and `multipartFile.transferTo(file)` at line 175 writes the uploaded bytes to the attacker-controlled path. There is no restriction on the destination directory, no restriction on the file extension, and no containment check. This writes attacker-supplied content to any path the service process can write.

### 4. The same pattern already carries a CVE on the third handler

`deleteFile` at `/rest/deleteFile` uses the identical construction and is the subject of CVE-2025-7108.

@RequestMapping(value = "/rest/deleteFile")

public String deleteFile(String fullPath, String fileName, HttpServletResponse response) throws Exception {

File path = new File(y9FileManagerProperties.getFileRoot() + fullPath);

CVE-2025-7108 is scoped to `deleteFile` only. The read sink `retrieveFileStream` and the write sink `storeFile` the same root cause in the same class and are not covered by that CVE or by any other published advisory for this project. The read and write primitives are more severe than the delete primitive, because a read discloses secrets and a write can lead to code execution.

### 5. No authentication on the microservice

The `fileManager` webapp build file declares its dependencies as the file repository and REST store support modules, the Nacos common module, commons-io, and `spring-boot-starter-web`. It declares no `spring-boot-starter-security` and no project security starter, so Spring Security is not on the classpath and no authentication filter is registered for these routes.

net.risesoft risenet-y9boot-support-file-jpa-repository

net.risesoft risenet-y9boot-support-file-service-rest

net.risesoft risenet-y9boot-common-nacos

commons-io commons-io

org.springframework.boot spring-boot-starter-web

The application class `FileManagerApplication` is a plain `@SpringBootApplication` with no security configuration, and the context path is `/fileManager` from `application.yml`. The handlers are therefore reachable by an unauthenticated caller, which matches the remote-exploitation model already accepted for CVE-2025-7108 on the sibling handler.

participant RS as retrieveFileStream handler

AT->>FM: GET rest retrieveFileStream with fullPath above the root and fileName of a secret file

FM->>RS: bind fullPath and fileName parameters

RS->>FS: build fileRoot plus fullPath then fileName, no containment check

FS-->>RS: file bytes when the path exists

RS-->>AT: response body contains the target file content

AT->>FM: POST rest storeFile multipart body with fullPath above the root and chosen fileName

FM->>SS: bind multipart body, fullPath and fileName

SS->>FS: mkdirs on traversed path then transferTo writes attacker bytes

> Preconditions: the attacker can reach the `fileManager` HTTP service at its `/fileManager` context path. The service performs no authentication of its own, so no account is required. The default file root is `d:/y9config/y9filestore` on a Windows deployment and an operator-set path elsewhere. Impact is bounded by the filesystem permissions of the service process.

> The request examples below are illustrative and were not executed against any deployment during this static review.

1. **Locate the service.** Identify the host and port that serve the `fileManager` context path. In a default layout the file manager is one microservice in the platform, reachable directly or through the front gateway route for file operations.

2. **Read an arbitrary file.** Issue a GET to `/fileManager/rest/retrieveFileStream` with `fullPath` set to a value that climbs out of the configured root and into the target directory, and `fileName` set to the target file name. On a Linux deployment, a `fullPath` of repeated parent-directory segments reaching the filesystem root followed by `etc` and a `fileName` of `passwd` returns the password file. The handler checks only that the resolved file exists, then streams its bytes. Reading the service configuration file discloses the database, Redis, and Nacos credentials that `application.yml` holds in cleartext.

GET /fileManager/rest/retrieveFileStream?fullPath=/../../../../../../etc&fileName=passwd HTTP/1.1

3. **Write an arbitrary file.** Issue a multipart POST to `/fileManager/rest/storeFile` with a part named `multipartFile` carrying the content to write, a `fullPath` that resolves to the chosen destination directory, and a `fileName` for the written file. `mkdirs()` creates the destination chain, and `transferTo` writes the bytes. There is no extension or directory restriction.

POST /fileManager/rest/storeFile HTTP/1.1

Content-Type: multipart/form-data; boundary=X

Content-Disposition: form-data; name="fullPath"

Content-Disposition: form-data; name="fileName"

Content-Disposition: form-data; name="multipartFile"; filename="payload.jsp"

4. **State change.** After step 2 the attacker holds the content of a file outside the intended store. After step 3 a new file exists at the attacker-chosen path. When that path is a directory the application serves as static content or executes as a view, the write becomes code execution. The `application.yml` of this module registers static resource locations that include a filesystem directory and a JSP view resolver, which are candidate targets on a default Windows deployment, subject to the process write permissions and the servlet container layout.

This finding was confirmed by static source review only. No repository was cloned or downloaded to a local machine, no target code or proof-of-concept was executed, and no live deployment was contacted. Runtime validation was not performed, so the code-execution outcome of the write primitive is reasoned from the source and the module configuration, not observed.

Pinned source confirmed at release `v9.6.7`, commit `b9e1868755b801416cdb1be3587b4d8e5846a0a0`:

- `Y9FileController.java:136-160` defines `retrieveFileStream`, with the concatenation at line 141, the file build at line 142, and the read sink `FileUtils.copyFile(file, out)` at line 159.

- `Y9FileController.java:168-177` defines `storeFile`, with the concatenation at line 172, `mkdirs()` at line 173, the file build at line 174, and the write sink `multipartFile.transferTo(file)` at line 175.

- `Y9FileController.java:36-48` defines the `deleteFile` handler already covered by CVE-2025-7108, shown for the shared pattern.

- `Y9FileManagerProperties.java:17-22` sets the default root `d:/y9config/y9filestore`.

- `risenet-y9boot-webapp-filemanager/pom.xml` lists no Spring Security dependency.

- `risenet-y9boot-webapp-filemanager/src/main/resources/application.yml` sets the context path `/fileManager` and defines static resource locations and the JSP view resolver.

The two sinks are byte-identical at the latest tag `v9.6.11`, commit `b9dddb7c80d15c48ba912a6454fe926d6d191830`, where the module path moved to `y9-digitalbase-module/y9-module-filemanager/`. The build file at that tag still declares no security dependency. The sinks are therefore unfixed in the latest tagged code at the time of writing.

An unauthenticated caller who can reach the `fileManager` service obtains two primitives against the server filesystem, bounded by the service process permissions.

**Arbitrary file read (`retrieveFileStream`).** Any file the process can open is returned in full. This includes the service configuration, which in this project holds database credentials, the Redis password, and the Nacos password in cleartext, along with any other secret, key material, or business data on reachable paths. This is a direct confidentiality breach and a strong lead into the rest of the platform through the leaked credentials.

**Arbitrary file write (`storeFile`).** Any path the process can write receives attacker-chosen bytes, with parent directories created on demand and no extension restriction. The impact rises to remote code execution when the written file lands in a directory the server serves as content or executes as a view, for example a JSP under a servlet path or a file under a configured static location. It can also overwrite configuration or application files to corrupt or subvert the service.

**Reachability.** Exploitation requires network reach to the `fileManager` HTTP endpoint. The service itself enforces no authentication, so where the endpoint is exposed directly or routed through the gateway, no credentials are needed. This is the same exposure under which CVE-2025-7108 was published for the sibling `deleteFile` handler.

What is **not** established here: the code-execution step of the write primitive was not demonstrated at runtime and depends on the deployment's servlet layout and the process write permissions, so it is reasoned from source rather than observed. No claim is made of privilege escalation beyond what the service process already holds, and no database authentication bypass is claimed.

**Where exactly:** `Y9FileController.java:141-142` and `:159` for the read handler, `Y9FileController.java:172-175` for the write handler, and `:40-42` for the already-reported delete handler. The common defect is that the resolved path is never checked for containment inside `fileRoot`, and the service has no authentication layer.

**Short-term (primary fix), canonicalize the resolved path and reject anything outside the configured root** in every handler that accepts `fullPath` or `fileName`.

--- a/.../y9public/controller/Y9FileController.java

+++ b/.../y9public/controller/Y9FileController.java

- File path = new File(y9FileManagerProperties.getFileRoot() + fullPath);

- File file = new File(path, fileName);

+ File root = new File(y9FileManagerProperties.getFileRoot()).getCanonicalFile();

+ File file = new File(new File(root, fullPath), fileName).getCanonicalFile();

+ if (!file.toPath().startsWith(root.toPath())) {

+ throw new SecurityException("resolved path escapes the file root");

Apply the same canonical-root containment check in `storeFile` before `mkdirs()` and `transferTo`, and in `deleteFile` before `delete()`. Reject `fullPath` or `fileName` values that contain parent-directory segments or an absolute prefix.

**Medium-term,** stop exposing a raw filesystem path through the HTTP parameters at all. Address stored files by their database identifier, as the `download`, `downloadFile`, and `downloadFiles` handlers already do through `Y9FileStoreRepository.findById`, and resolve the on-disk path server-side from the stored record. This removes attacker control of the path entirely.

**Defense in depth,** place the `fileManager` service behind authentication and restrict its network exposure so the `/rest/*` endpoints are not reachable by untrusted callers. Add the project security starter or an equivalent filter, and add a regression test that a `fullPath` or `fileName` carrying traversal is rejected by every handler.

**Recommended:** the canonical-root containment check is the primary fix and closes all three handlers at once. Addressing files by database identifier removes the raw-path surface entirely, and adding authentication plus network restriction contains the service even if a future handler reintroduces the pattern.

- Pinned source at release `v9.6.7`, commit `b9e1868755b801416cdb1be3587b4d8e5846a0a0`:

- [`Y9FileController.java` retrieveFileStream and storeFile](

- [`Y9FileManagerProperties.java` default root](

- [`pom.xml` dependencies, no security starter](

- [`application.yml` context path and static locations](

- Latest tag `v9.6.11`, commit `b9dddb7c80d15c48ba912a6454fe926d6d191830`: [`Y9FileController.java` unchanged sinks](

- Related CVE on the sibling handler: [CVE-2025-7108]( path traversal in the `deleteFile` function of `Y9FileController`, versions 9.6.0 through 9.6.7. The read and write handlers in this report are not covered by that CVE.

- Weaknesses: [CWE-22 Path Traversal]( [CWE-73 External Control of File Name or Path]( [CWE-306 Missing Authentication for Critical Function](

- No public advisory covered `retrieveFileStream` or `storeFile` at the time of writing. The repository GitHub advisories page had no matching entry and targeted searches found nothing beyond CVE-2025-7108 for the delete handler.

Reported by hieuPenguinnn, [github.com/hieuPenguinnn](