提出 #1035491: FIT2CLOUD DataEase (open-source BI platform; vulnerable classes in the in-repo `sdk/common` auth filter and `core/core-backend` static-resource server, bundled into the community distribution) audited at released build 2.10.26, commit `0a9eaf6af890bbc91eb情報

タイトルFIT2CLOUD DataEase (open-source BI platform; vulnerable classes in the in-repo `sdk/common` auth filter and `core/core-backend` static-resource server, bundled into the community distribution) audited at released build 2.10.26, commit `0a9eaf6af890bbc91eb
説明Anonymous file upload and readback through the authentication filter's suffix whitelist in DataEase Vendor: FIT2CLOUD Affected versions: audited at released build 2.10.26, commit `0a9eaf6af890bbc91ebcd71a8a8013f917846245` (runtime: production `CoreApplication.jar` v2.10.26); the `endsWithAny` suffix whitelist in `WhitelistUtils` and the shared write/read directories are as shipped at this tag Vulnerability class: Missing authentication for a write endpoint / unrestricted upload of a file type the application itself serves (CWE-434, with the authentication gap CWE-306) Preconditions: Anonymous — no account, token, cookie or share link is required for either the write or the read; every request in the proof was sent without credentials and returned HTTP 200. Deployment preconditions: a default standalone instance (server mode, no servlet context path, default `dataease.path.static-resource`; the server-mode data root `/opt/dataease2.0/data` is hardcoded by `StaticResourceConstants.getHomeData()` with no configuration override). SVG content is still screened by the 2.10.20 content blacklist — the finding is the missing authentication layer, not a content-screening bypass. On a default standalone DataEase instance, any unauthenticated caller can upload files to the static-resource endpoint and fetch them back from the DataEase origin as `image/svg+xml`, because the only authentication filter skips token validation for any request URI that merely ends with the text `svg`. Data flow: multipart part `file` at `POST /de2api/staticResource/upload/{fileId ending in svg}` → `TokenFilter` match=true, no token check (`TokenFilter.java:57,66-68`) → `StaticResourceServer` writes `static-resource/{fileId}.{ext}` under `WORK_DIR` (`StaticResourceServer.java:58-74`) → `GET /de2api/static-resource/{fileId}.svg` served anonymously with `Content-Type: image/svg+xml`. ### Affected interfaces | Interface | |---| | POST /de2api/staticResource/upload/x.svg — multipart part `file`; anonymous when the URI ends with lowercase `svg` | | GET /de2api/static-resource/x.svg.svg — anonymous readback of the stored file, served as `image/svg+xml` | Primary route detail: ``` POST /de2api/staticResource/upload/f1ok.svg HTTP/1.1 Content-Type: multipart/form-data; part `file` (any image content; SVG is content-screened by the 2.10.20 blacklist) ``` Anonymous. No cookie, token, `Authorization` header or link credential is required: 1. `WhitelistUtils.java:59-60` strips the `/de2api` prefix, then `:69` `endsWithAny(requestURI, ".gif",".ico","js",".css","svg","png","jpg","js.map",".otf",".ttf",".woff2")` — the extension entries without a leading dot match any URI whose last characters are that suffix, whatever the route. 2. `TokenFilter` (`FilterConfig`, `/*` order 0, the only auth gate) computes `match = WhitelistUtils.match(...)` (`TokenFilter.java:57`) and on `match=true` calls `filterChain.doFilter` and returns with no token validation (`:66-68`). 3. Spring 6 `PathPatternParser` (verified against the bundled spring-web 6.2.14) captures `x.svg` fully into `{fileId}` — suffix truncation is gone — so the upload handler is reached while the URI satisfies the suffix exemption. ### Root cause 1. The write lands in the served directory: `StaticResourceServer.java:58-74` stores the part as `static-resource/{fileId}.{ext}` under `WORK_DIR` (default `/opt/dataease2.0/data/static-resource`); the write-path guards (`validateUploadFilename`, `writeFileIfAbsent` `CREATE_NEW`) check the path only — no authentication or caller identity anywhere on the path. 2. The read handler (`DeMvcConfig.java:26-29`) serves that same directory, the read prefix is also suffix-whitelisted (`WhitelistUtils.java:72`), and the bundled spring-web mime.types maps `.svg` to `image/svg+xml`. 3. Serving headers do not compensate: `X-Content-Type-Options: nosniff` is present, but the CSP from `HtmlResourceFilter.java:40-41` is `default-src * data: blob: 'unsafe-inline' 'unsafe-eval'; frame-ancestors *`, which does not prevent `javascript:` navigation inside SVG content. Missing guard: the suffix whitelist is designed for static asset reads; applying it by raw suffix text to all URIs — including write APIs whose final path segment is attacker-chosen — turns an asset-reading convenience into an authentication bypass for POST endpoints. The SVG content blacklist added in 2.10.20 screens what authenticated users upload; at 2.10.26 the same endpoint no longer requires any authentication in a default standalone deployment. Baseline: released production `CoreApplication.jar` (v2.10.26, commit `0a9eaf6af890bbc91ebcd71a8a8013f917846245`), self-hosted on `127.0.0.1:18763` under the shipped default data layout. No account was created and no token, cookie or link credential was used anywhere. **1. Anonymous upload (benign 103-byte SVG):** ```sh $ curl -i -X POST 'http://127.0.0.1:18763/de2api/staticResource/upload/f1ok.svg' \ -F '[email protected];type=image/svg+xml' HTTP/1.1 200 {"code":0,"msg":null,"data":null} ``` **2. Stored on disk:** `static-resource/f1ok.svg.svg` (103 bytes) under the application's data root; on-disk content equals the uploaded bytes byte for byte. **3. Anonymous readback, served as SVG from the application origin:** ```sh $ curl -i 'http://127.0.0.1:18763/de2api/static-resource/f1ok.svg.svg' HTTP/1.1 200 Content-Type: image/svg+xml Content-Security-Policy: default-src * data: blob: 'unsafe-inline' 'unsafe-eval'; frame-ancestors * X-Content-Type-Options: nosniff # body byte-identical to the uploaded file ``` **4. Negative controls — the gate works everywhere except the suffix-matching shape:** | Control | Request | Result | |---|---|---| | Non-whitelisted suffix | `POST /de2api/staticResource/upload/f1neg.txt` (same multipart) | `401` + `DE-GATEWAY-FLAG: token is empty for uri {.../f1neg.txt}` | | Protected API without token | `POST /de2api/staticResource/findResourceAsBase64` | `401` + `token is empty` | | Case-sensitivity | `POST /de2api/staticResource/upload/f1negUp.SVG` | `401` + `token is empty` (`endsWith` is case-sensitive; the payload suffix must be lowercase) | All testing was performed against a self-hosted instance on the reporter's own host. No third-party system, production instance or customer data was involved. - **Vendor contact**: [email protected] (FIT2CLOUD security contact email) - **Status**: vendor notified via email 2026-10-09; not publicly disclosed; no CVE assigned - **Duplicate check**: public advisories (NVD/OSV/GitHub) and the project issue tracker were searched before reporting; please tell us if this duplicates something already tracked internally.
ソース⚠️ https://github.com/dataease/dataease
ユーザー
 lucasg2g (UID 84737)
送信2026年10月15日 07:34 (23 日 ago)
モデレーション2026年11月06日 19:27 (23 days later)
ステータス承諾済み
VulDBエントリ432070 [DataEase 迄 2.10.27 Authentication Filter WhitelistUtils.java WhitelistUtils.endsWithAny file 特権昇格]
ポイント20

Do you want to use VulDB in your project?

Use the official API to access entries easily!