LaunchServer FileServerHandler has an unauthenticated path traversal issue

説明

Summary

An unauthenticated path traversal in the LaunchServer HTTP file server (FileServerHandler) lets any remote actor read any file readable by the LaunchServer process (e.g. ../../../../etc/passwd). This is a generic arbitrary-file-read primitive, so the fix must address the traversal itself, not any specific file.

The readable files include the server's own secrets, which turns this from information disclosure into full compromise: the ECDSA private key that signs access JWTs (.keys/ecdsa_id), the refresh-token salt (.keys/legacySalt), and LaunchServer.json (database credentials). With the signing key an attacker mints a valid access token for any account, including admins. That is a full authentication bypass. Pre-auth, default config, port 9274.

Affected: GravitLauncher LaunchServer ≤ 5.7.11 (the LaunchServer application; the published pro.gravit.launcher:*-api Maven artifacts do not contain the vulnerable code).

Details

In FileServerHandler.channelRead0:

path = Paths.get(IOHelper.getPathFromUrlFragment(uri)).normalize().toString().substring(1); // line 194
File file = base.resolve(path).toFile();                                                     // line 200 - no second normalize()

substring(1) blindly strips a leading slash, assuming the request-target always starts with /. Netty's HttpServerCodec accepts a request-target without a leading slash verbatim (decoderResult().isSuccess() == true). For such a target, normalize() cannot collapse the leading .., substring(1) turns ../ into ./ (leaving the remaining ..), and base.resolve(path), which is not re-normalized, resolves outside updatesDir.

file.isHidden() (line 201) is checked only on the final path component, so targets that don't start with a dot (ecdsa_id, rsa_id, legacySalt, LaunchServer.json) are served even with showHiddenFiles=false.

The file server is enabled by default (netty.fileServerEnabled=true) and bound to 0.0.0.0:9274. No auth handler precedes FileServerHandler; WebSocketServerProtocolHandler("/api") forwards non-WebSocket / non-/api requests down to it, so the attack is a plain HTTP GET (no WebSocket).

PoC

Reproduced on a from-source build of v5.7.11 (Netty 4.2.12).
Must use a raw socket. curl/browsers/HTTP libraries normalize the path and prepend /, hitting the safe branch (false "not reproducible").

printf 'GET ../../.keys/ecdsa_id HTTP/1.1\r\nHost: x\r\n\r\n' | nc <host> 9274

Returns the raw ECDSA private-key bytes. Same for ../../.keys/rsa_id, ../../.keys/legacySalt, ../../LaunchServer.json. %2e%2e/... (no leading slash) also works. Depth-robust arbitrary read: ../../../../../../etc/passwd.
Control (confirms the root cause): GET /../../.keys/ecdsa_id (WITH leading slash) → 404. Only the no-leading-slash form escapes.

Impact

Unauthenticated remote read of any file the process can access. What that exposes:
- .keys/ecdsa_id: the key that signs access JWTs. With it, an attacker mints a valid token for any account, including admins, so this is a full authentication bypass.
- .keys/legacySalt: lets an attacker forge refresh tokens.
- LaunchServer.json: database credentials.
- Any other file readable by the process (config, logs, system files).

Deployment note: a normalizing L7 reverse proxy (stock nginx location / { proxy_pass ...; }) rejects the no-leading-slash request (400) and collapses leading-slash traversal, blocking the primary vector. But the default bind is 0.0.0.0:9274, so protection relies on firewalling the backend port; L4/TCP proxies (HAProxy TCP, nginx stream, CF Spectrum) and direct exposure remain exploitable.

Suggested fix

  1. Re-normalize() after base.resolve(path) and verify resolved.startsWith(base).
  2. Reject request-targets that don't start with / (400).
  3. Default-bind to 127.0.0.1; store .keys outside updatesDir.

基本情報

タイプ
reviewed
深刻度
critical
GitHub 上のアドバイザリ
アドバイザリを開く ↗
リポジトリのアドバイザリ
リポジトリのアドバイザリを開く ↗
ソースコード
ソースを見る ↗
公開(アドバイザリ)
2026-07-02 20:49:18 UTC
更新
2026-07-02 20:49:20 UTC
GitHub レビュー済み
2026-07-02 20:49:18 UTC

EPSS Score

No EPSS score in this advisory JSON.

CVSS Scores

Base score Version Severity Vector
9.8 3.1
CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H クリックして展開
攻撃ベクター (AV:N)
インターネットなど、ルーティングされたネットワーク越しに遠隔から悪用しうる。端末の前にいる必要はない。
攻撃の複雑さ (AC:L)
攻撃者が条件を満たせば、レース条件や珍しい構成に依存せずに再現しやすい。
必要な権限 (PR:N)
事前のログインや昇格は不要で、匿名アクセスのまま踏み台にしうる。
ユーザーの関与 (UI:N)
メールのリンクを開く、マクロを有効にするなど、被害者の協力がなくても成立しうる。
スコープ (S:U)
影響は脆弱コンポーネントと同一のセキュリティ権限・信頼境界の内側に収まる。
機密性への影響 (C:H)
広範な機微データの読み取りや持ち出しが現実的。
完全性への影響 (I:H)
権限の奪取や広範なログ改竄など、システムの信頼根拠を揺るがす改ざんが現実的。
可用性への影響 (A:H)
長時間のサービス停止、データ損壊による復旧不能に近い状態など、利用者に著しい不便を与えうる。

Identifiers

CWEs

CWE id Name
CWE-22 Improper Limitation of a Pathname to a Restricted Directory ('Path Traversal')
CWE-200 Exposure of Sensitive Information to an Unauthorized Actor
CWE-522 Insufficiently Protected Credentials

Credits

  • getclaude (reporter)

Affected packages (1)

Vulnerable version ranges and first patched releases as published by GitHub.

Ecosystem Package Vulnerable range First patched Vulnerable functions
maven pro.gravit.launcher:launchserver-api <= 5.7.11

References

cvelogic Threat Intelligence