| tiêu đề | DataLinkDC dinky v1.2.5 (unfixed through master 2026-09-18) Server-Side Request Forgery (SSRF) |
|---|
| 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, and unfixed at master as of 2026-09-18 (reviewed at released tag v1.2.5, commit ce7678eaa13722541f7d1eeda7ffbf8c9f753e20)
- **Vulnerability class**: Server-Side Request Forgery with full response read-back (CWE-918)
Preconditions
- **Authentication / preconditions**: none — anyone who can reach the port can call it. No session, cookie or token header is required; the servlet mappings `/api/flink_web/proxy/*` and `/api/flink_web/*` are registered by default (`FlinkWebProxyServletConfiguration.java:36-43`) and no servlet-level authentication exists in the shipped configuration (H2 default profile, fresh install).
- **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:N/UI:N/S:C/C:H/I:N/A:N
Title (one line)
Unauthenticated SSRF in the Dinky Flink web UI proxy servlet via the caller-controlled _authority query parameter
Description (technical, moderator-ready)
Unauthenticated SSRF in the Dinky Flink web UI proxy servlet via the caller-controlled _authority query parameter
Data flow: query `_authority` → `FlinkWebURITemplateProxyServlet.getNewTargetUri()` fills the template `http://{_authority}/#/job/running/{_jid}/overview` → smiley `ProxyServlet`/Apache HttpClient dial → upstream response body streamed back to the caller
Affected interfaces
| Interface |
|---|
| GET /api/flink_web/proxy?_authority=<host:port>&_jid=<anything> (also POST, and the sibling mapping /api/flink_web/*; query parameters `_authority`, `_jid`) |
Primary route detail:
```
GET /api/flink_web/proxy?_authority=<host:port>&_jid=<anything>
```
Anonymous. No session, cookie or token header is required. An anonymous network caller can make dinky-admin open an HTTP connection to any host:port the server can reach and read the complete response body, because the Flink web UI proxy is registered as a raw servlet whose upstream authority is taken verbatim from the request query string. The route never reaches sa-token's login enforcement: that check is a Spring MVC `HandlerInterceptor` (`AppConfig.java:77-94`), and a `ServletRegistrationBean` servlet bypasses the Spring MVC dispatch chain entirely, while dinky-admin registers no servlet `Filter` at all.
Root cause
1. `FlinkWebProxyServletConfiguration.servletRegistrationBean()` (`dinky-admin/src/main/java/org/dinky/configure/FlinkWebProxyServletConfiguration.java:36-43`) registers `FlinkWebURITemplateProxyServlet` (a smiley `ProxyServlet` subclass) as a raw servlet with the target template `http://{_authority}/#/job/running/{_jid}/overview` (TARGET_URI, `:33`) and mappings `/api/flink_web/proxy/*`, `/api/flink_web/*` (`:39`).
2. `FlinkWebURITemplateProxyServlet.getNewTargetUri()` (`dinky-admin/src/main/java/org/dinky/utils/FlinkWebURITemplateProxyServlet.java:137-153`) substitutes `{_authority}` and `{_jid}` in the template with values parsed from the **request query string** (`getParams`, `:155-169`) — the upstream authority is fully caller-controlled.
3. `service()` builds `new URI(newTargetUri)` and hands the request to the smiley `ProxyServlet` superclass, which dials the target via Apache HttpClient and streams the upstream **response body back to the caller**.
4. Missing guard: sa-token's login enforcement is registered as a Spring MVC interceptor on `/api/**` + `/openapi/**` (`dinky-admin/src/main/java/org/dinky/configure/AppConfig.java:77-94`), which never executes for a `ServletRegistrationBean` servlet, and no `Filter`/`FilterRegistrationBean`/`@WebFilter` exists anywhere in dinky-admin — so unlike the sibling `FlinkProxyController` (a `@SaCheckLogin`-annotated MVC controller under `/api/flink/**`), this proxy is reachable with no credentials. There is also no pinning of `_authority` to cluster addresses registered in dinky's own store and no scheme/host validation of the substituted value.
Proof of concept
Baseline: released tag v1.2.5 (commit `ce7678eaa13722541f7d1eeda7ffbf8c9f753e20`), official `dinky-release-1.20-1.2.5.tar.gz` dist, self-hosted instance on the reporter's own host, JDK 11, shipped-default H2 profile, HTTP on `127.0.0.1:18888`; harmless unique-marker canary listener returning `CV4-DINKY-CANARY-BODY-7731` bound to loopback-only `127.0.0.1:18903`.
**1. Unauthenticated dial with response read-back (no cookie, no token header)**
```sh
$ curl -s "http://127.0.0.1:18888/api/flink_web/proxy?_authority=127.0.0.1:18903&_jid=canary7731"
-> CV4-DINKY-CANARY-BODY-7731
```
**2. Application-originated connection (canary log, loopback-only listener)**
```sh
$ cat canary.log
2026-09-16T23:54:36.116052 FROM ('127.0.0.1', 51822) REQ: GET / HTTP/1.1
```
(The template `http://{_authority}/#/job/running/{_jid}/overview` dials `/` of the attacker-chosen authority `127.0.0.1:18903`; only the dinky JVM on the same host could reach the loopback-bound listener.)
**3. Negative control (the login gate exists on the MVC sibling but not here)** — the same unauthenticated posture on the sibling controller route `/api/flink/**` is refused with `{"code":7,"msg":"Can Not Read Token"}`, proving sa-token enforcement is active for `/api/**` MVC handlers and simply never runs for this raw servlet.
Impact
See the interface and root-cause sections: an actor who satisfies the stated preconditions makes the server perform a request on a destination of the attacker's choosing and reads the full response body back, crossing the trust boundary described above. An unauthenticated attacker can aim dinky-admin's network position at any host/port reachable from the server process (internal admin panels, metadata services, cloud-instance localhost-only services) and read the full response — a complete internal-pivot/read primitive. Internal reachability was proven by construction: a canary listener bound to loopback-only `127.0.0.1:18903` (unreachable from off-host) was contacted by the dinky JVM and its body returned to the curl caller.
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-wchp-mjj9-wxqh); 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-wchp-mjj9-wxqh |
|---|
| Người dùng | lucasg2g (UID 84737) |
|---|
| Đệ trình | 06/10/2026 14:46 (cách đây 1 tháng) |
|---|
| Kiểm duyệt | 04/11/2026 15:17 (29 days later) |
|---|
| Trạng thái | được chấp nhận |
|---|
| Mục VulDB | 431072 [DataLinkDC Dinky đến 1.2.5 flink_web Proxy Endpoint FlinkWebURITemplateProxyServlet.java FlinkWebURITemplateProxyServlet.getNewTargetUri _authority nâng cao đặc quyền] |
|---|
| điểm | 20 |
|---|