Skip to content

Authorization bypass on admin endpoints and /ws/system/logs — session tokens skip RequireScopes

High
lin-snow published GHSA-hmmq-qh6g-6wgh Apr 13, 2026

Package

ech0

Affected versions

<= v4.5.0

Patched versions

4.5.1

Description

Summary

internal/middleware/scope.go::RequireScopes short-circuits with ctx.Next() whenever the JWT typ is session. Every /api/login token is a session token. None of the affected handlers (GetSystemLogs, GetVisitorStats, CheckUpdate, GetAllUsers, WSSubscribeSystemLogs) does its own IsAdmin check, and /ws/system/logs is registered under WSRouterGroup, which has no middleware at all. Result: any logged-in non-admin reads system logs (HTTP and live WebSocket), visitor stats, update info, and every other user's email. The fix in v4.4.3 (commit 6df56730) only added RequireScopes, which doesn't apply to session tokens, so the original advisory PoCs still succeed in v4.5.0.

Details

Three independent gaps, all required for the bypass:

  1. internal/middleware/scope.go:14-22RequireScopes returns Next() immediately for session tokens, before checking scope/audience. The intent (PR #195: "session = interactive UI") is fine if admin enforcement moves to handlers; it didn't.
  2. No IsAdmin check in the affected handlers/services:
    • internal/handler/dashboard/dashboard.go::GetSystemLogs / GetVisitorStats / CheckUpdate / WSSubscribeSystemLogs / SSESubscribeSystemLogs
    • internal/service/user/user.go::GetAllUsers (line 405)
      grep -rn "IsAdmin" internal/handler/dashboard/ internal/service/dashboard/ → 0 hits. Other functions in the same user.go file (UpdateUser, UpdateUserAdmin, DeleteUser) check IsAdmin/IsOwner explicitly, so this is missed-coverage, not intentional.
  3. internal/router/router.go:38ws := r.Group("/ws") has no .Use(...) anywhere. The WSSubscribeSystemLogs handler only calls jwtUtil.ParseToken(token) and never verifies IsAdmin / scope / audience. CheckOrigin is also return true, enabling Cross-Site WebSocket Hijacking.

The v4.4.3 patch (commit 6df56730) added RequireScopes(ScopeAdminSettings) to /api/system/logs and /api/system/logs/stream only. Both advisories' published PoCs used a session token, which still passes RequireScopes, so the patch is structurally ineffective. /ws/system/logs was never modified.

PoC

poc.zip

Please extract the uploaded compressed file before proceeding

  1. docker compose up -d --build
  2. ./poc.sh
  3. python3 poc_ws.py
스크린샷 2026-04-13 오후 8 50 08

Impact

  • Live system log tail for any logged-in user via /ws/system/logs and /api/system/logs[/stream]: failed-login username enumeration, denied-action stack traces with internal CI paths (/home/runner/work/Ech0/Ech0/...) and full Gin middleware chain, audit trail of every admin action.
  • PII: /api/users returns username + email + admin flag for every non-owner user to any logged-in user.

Severity

High

CVE ID

No known CVE

Weaknesses

Missing Authorization

The product does not perform an authorization check when an actor attempts to access a resource or perform an action. Learn more on MITRE.

Credits