GHSA-c7hf-c5p5-5g6h

Suggest an improvement
Source
https://github.com/advisories/GHSA-c7hf-c5p5-5g6h
Import Source
https://github.com/github/advisory-database/blob/main/advisories/github-reviewed/2026/03/GHSA-c7hf-c5p5-5g6h/GHSA-c7hf-c5p5-5g6h.json
JSON Data
https://api.osv.dev/v1/vulns/GHSA-c7hf-c5p5-5g6h
Aliases
Published
2026-03-12T14:47:39Z
Modified
2026-03-14T01:46:47.688138Z
Severity
  • 5.3 (Medium) CVSS_V3 - CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:L/I:N/A:N CVSS Calculator
Summary
Uptime Kuma is Missing Authorization Checks on Ping Badge Endpoint, Leaks Ping times of monitors without needing to be on a status page
Details

Summary

The GET /api/badge/:id/ping/:duration? endpoint in server/routers/api-router.js does not verify that the requested monitor belongs to a public group. All other badge endpoints check AND public = 1 in their SQL query before returning data. The ping endpoint skips this check entirely, allowing unauthenticated users to extract average ping/response time data for private monitors.

Affected Code

File: server/routers/api-router.js, approximately line 304

The ping badge endpoint directly calls UptimeCalculator.getUptimeCalculator(requestedMonitorId) without first checking if the monitor is public. Compare with the status badge endpoint (~line 148) which correctly queries:

SELECT monitor_group.monitor_id FROM monitor_group, `group`
WHERE monitor_group.group_id = `group`.id
AND monitor_group.monitor_id = ?
AND public = 1

Protected vs Vulnerable Endpoints

| Endpoint | Has public=1 check? | |----------|-------------------| | /api/badge/:id/status | Yes | | /api/badge/:id/uptime/:duration? | Yes | | /api/badge/:id/avg-response/:duration? | Yes | | /api/badge/:id/cert-exp | Yes | | /api/badge/:id/response | Yes | | /api/badge/:id/ping/:duration? | No — vulnerable |

PoC

  1. Install Uptime Kuma (tested on latest v2 stable via Docker)
  2. Create an HTTP(s) monitor (e.g., monitoring http://localhost:3001)
  3. Do NOT add the monitor to any public status page or group
  4. Wait for heartbeats to accumulate (~5 minutes)
  5. Query unauthenticated:
    curl http://localhost:3001/api/badge/1/status   → returns N/A (correct, monitor is private)
    curl http://localhost:3001/api/badge/1/ping/24  → returns "Avg. Ping (24h): 10ms" (LEAKED)
    

Impact

An unauthenticated attacker can: - Enumerate private monitor IDs - Extract average response time data for private monitors - Infer existence and reachability of internal monitored services

Suggested Fix

Add the same public monitor check before the UptimeCalculator call:

let publicMonitor = await R.getRow(`
    SELECT monitor_group.monitor_id FROM monitor_group, \`group\`
    WHERE monitor_group.group_id = \`group\`.id
    AND monitor_group.monitor_id = ?
    AND public = 1
`, [requestedMonitorId]);

if (!publicMonitor) {
    badgeValues.message = "N/A";
    badgeValues.color = badgeConstants.naColor;
}

<img width="1228" height="710" alt="Screenshot 2026-02-24 at 4 49 40 PM" src="https://github.com/user-attachments/assets/80aeae2d-be08-449f-8b39-c50da7aaedba" />

<img width="1271" height="770" alt="File Alons til View He" src="https://github.com/user-attachments/assets/d50c9a00-282a-4b79-b5e1-f77afde9223a" />

Database specific
{
    "github_reviewed": true,
    "github_reviewed_at": "2026-03-12T14:47:39Z",
    "cwe_ids": [
        "CWE-862"
    ],
    "severity": "MODERATE",
    "nvd_published_at": "2026-03-12T19:16:16Z"
}
References

Affected packages

npm / uptime-kuma

Package

Affected ranges

Type
SEMVER
Events
Introduced
2.0.0
Fixed
2.2.0

Database specific

source
"https://github.com/github/advisory-database/blob/main/advisories/github-reviewed/2026/03/GHSA-c7hf-c5p5-5g6h/GHSA-c7hf-c5p5-5g6h.json"
last_known_affected_version_range
"<= 2.1.3"