| tiêu đề | DataLinkDC dinky v1.2.5 (commit ce7678e) Path Traversal |
|---|
| Mô tả | Required fields (Submission Policy: vendor, product, affected versions, vulnerability class)
- **Vendor**: DataLinkDC
- **Product**: dinky (dinky-admin)
- **Affected version(s)**: up to and including released tag v1.2.5 (reviewed at commit ce7678eaa13722541f7d1eeda7ffbf8c9f753e20; v1.2.5 was the newest release at the time of review)
- **Vulnerability class**: Path traversal — arbitrary file overwrite / deletion (CWE-22)
Preconditions
- **Authentication / preconditions**: any authenticated dinky user can trigger this. The controller carries a class-level `@SaCheckLogin` only (`CatalogueController.java:73`); the upload route adds no `@SaCheckPermission`, so no role beyond a valid session is required — the filesystem chain involves no tenant or permission escalation.
- **CVSS**: assigned by VulDB during moderation; not claimed by the submitter.
Optional vector hint only (not a claim), to speed the review: CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:N/I:H/A:H
Title (one line)
Multipart filename path traversal in the Dinky catalogue zip upload overwrites and deletes files outside the dinky root directory
Description (technical, moderator-ready)
Multipart filename path traversal in the Dinky catalogue zip upload overwrites and deletes files outside the dinky root directory
Data flow: multipart `filename` → `file.getOriginalFilename()` → `zipPath = filePath + File.separator + fileName` → `FileUtil.writeBytes(file.getBytes(), zipPath)` → `finally { FileUtil.del(zipFile); }`
Affected interfaces
| Interface |
|---|
| POST /api/catalogue/upload/{id} — multipart form field `file`; the client-controlled Content-Disposition `filename` is the injection vector (`../` escapes the dinky root directory) |
Primary route detail:
```
POST /api/catalogue/upload/{id}
Content-Type: multipart/form-data
file=@<zip>;filename=../<name> <- the client-controlled Content-Disposition filename
```
Any authenticated dinky user can overwrite and then delete arbitrary files writable by the dinky OS account — including application jars and configs under the install directory and `../`-reached files outside it — because the route concatenates the client-controlled multipart filename onto the dinky root directory with no normalization or boundary check, writes the uploaded zip bytes to that path, and deletes the same attacker-named path in a `finally` block. A pre-existing file at the escaped path is destroyed by the overwrite-then-delete chain.
Root cause
1. `String fileName = file.getOriginalFilename();` (`CatalogueController.java:89`) — Spring/Tomcat pass the client's Content-Disposition filename through unsanitized; `..` separators are preserved.
2. `String zipPath = filePath + File.separator + fileName;` (`CatalogueController.java:90`) — plain string concatenation with the dinky root dir (shipped default `DirConstant.getRootPath()` = `user.dir`); no normalization and no boundary check, so `../` escapes the root.
3. `FileUtil.writeBytes(file.getBytes(), zipPath)` (`CatalogueController.java:99`) writes the attacker's bytes to the escaped path; `FileUtil.del(zipFile)` on the same `new File(zipPath)` runs in the `finally` block (`CatalogueController.java:105`), so the operation ends with the attacker-named path deleted — together an overwrite-then-delete of any file writable by the dinky OS user, and a deletion of any pre-existing file at the path.
4. The archive-entry writes inside the subsequent unzip are protected by hutool 5.8.31 `checkSlip` (`ZipReader.java:171-172`); this issue is the *filename* path, which bypasses hutool entirely through raw `java.io.File` construction.
Missing guard: nothing validates that the final canonical path stays inside the dinky root directory before the write and the delete.
Proof of concept
Baseline: dinky v1.2.5 official distribution, self-hosted instance on the reporter's own host at 127.0.0.1:18888 (H2 default profile), authenticated with a seeded administrator session. Victim file `work/cv4probe7740` (7 bytes, content `victim`) placed **outside** the dinky root dir (`work/dk-dist`); payload `/tmp/plain7737.zip`, a minimal harmless zip with entry `a.txt`.
**1. Upload with a traversing filename**
```sh
$ curl -s -X POST -H "dinky-token: <admin session>" \
-F "file=@/tmp/plain7737.zip;filename=../cv4probe7740" \
"http://127.0.0.1:18888/api/catalogue/upload/1"
```
**2. Positive artifact — the victim file outside the root is overwritten and deleted**
- Before: `work/cv4probe7740` exists, 7 bytes.
- After: `ls: cannot access work/cv4probe7740: No such file or directory` — the file was overwritten with the attacker's zip bytes (`FileUtil.writeBytes` inside `try`) and then deleted by the `finally { FileUtil.del(zipFile); }` block.
- Independent proof the raw filename (separators and `..`) reached the filesystem: the zip's entry `a.txt` was extracted into the root dir — `work/dk-dist/a.txt` existed after the request.
**3. Negative control**
The hutool 5.8.31 `checkSlip` guard inside `ZipUtil.unzip` blocked archive-entry escape attempts (entry paths did not escape the root), and a request with `filename=../probe7738.zip` left no file anywhere after completion (the write-then-delete window closes in `finally`) — the exposed primitive is exactly the filename write plus the resulting delete, not a zip-slip.
Impact
See the interface and root-cause sections: an actor who satisfies the stated preconditions makes the application write attacker-controlled bytes to, and then delete, a file path of the attacker's choosing outside the intended root directory, crossing the trust boundary described above. A logged-in user can destroy (delete) arbitrary files, or briefly overwrite them with attacker content, anywhere the dinky OS account can write — application jars/configs under the install dir, `../`-reaching user files — causing loss of integrity and availability of the deployment.
References
- Project repository: https://github.com/DataLinkDC/dinky
- Security policy: `SECURITY.md` in that repository.
Coordinated disclosure: vendor notified 2026-10-06 via GitHub Private Vulnerability Reporting (https://github.com/DataLinkDC/dinky/security/advisories/GHSA-2jp7-3h24-h6r7); not publicly disclosed; no CVE assigned. Duplicate check: public advisories and the issue tracker were searched before reporting. |
|---|
| Nguồn | ⚠️ https://github.com/DataLinkDC/dinky/security/advisories/GHSA-2jp7-3h24-h6r7 |
|---|
| Người dùng | lucasg2g (UID 84737) |
|---|
| Đệ trình | 06/10/2026 14:45 (cách đây 1 tháng) |
|---|
| Kiểm duyệt | 07/11/2026 20:25 (1 month later) |
|---|
| Trạng thái | được chấp nhận |
|---|
| Mục VulDB | 432647 [DataLinkDC Dinky đến 1.2.5 Catalogue Upload CatalogueController.java file.getOriginalFilename filename duyệt thư mục] |
|---|
| điểm | 20 |
|---|