Skip to main content
CVE Vulnerability Database
Vulnerability Database/CVE-2026-58489

CVE-2026-58489: HedgeDoc Auth Bypass Vulnerability

CVE-2026-58489 is an authentication bypass flaw in HedgeDoc that allows attackers to export victims' private notes to attacker-controlled GitHub Gists. This article covers technical details, affected versions, and mitigation.

Published:

CVE-2026-58489 Overview

HedgeDoc, an open source real-time collaborative markdown notes application, contains a flaw in the GitHub Gist export flow prior to version 1.11.0. The application generated an OAuth2 state value but only checked its presence, never validating it against the value expected for the user's session. An attacker can forge a callback URL containing their own valid GitHub OAuth code. When HedgeDoc processes the callback, it uses the victim's session to select the note but the attacker's authorization code to determine the destination GitHub account. A logged-in victim who clicks a crafted link exports their private, protected, or limited note directly into a Gist controlled by the attacker. The issue is tracked as [CWE-352] Cross-Site Request Forgery.

Critical Impact

Successful exploitation results in exfiltration of private notes to an attacker-controlled GitHub Gist, leaking confidential content without the victim's awareness.

Affected Products

  • HedgeDoc versions prior to 1.11.0
  • Deployments exposing the GitHub Gist export integration
  • Instances configured with GitHub OAuth credentials for Gist export

Discovery Timeline

  • 2026-07-13 - CVE-2026-58489 published to NVD
  • 2026-07-14 - Last updated in NVD database

Technical Details for CVE-2026-58489

Vulnerability Analysis

The vulnerability resides in HedgeDoc's GitHub Gist export handler. When a user initiates a Gist export, HedgeDoc redirects the browser to GitHub's OAuth authorization endpoint with a generated state parameter. Upon callback, the server checked only that code and state were present in the query string. It never compared the returned state against a value stored in the user's session. This missing binding decouples the OAuth authorization code from the initiating session, enabling a session-mixing attack against the export workflow.

Root Cause

The root cause is the absence of server-side session binding for the OAuth2 state parameter. The Gist export handler discarded the security guarantees state is designed to provide. It failed to store the generated value in req.session and failed to verify equality with the callback value. The redirect URI was also reconstructed from request data rather than pinned to the session, compounding the CSRF exposure.

Attack Vector

An attacker first initiates their own Gist export flow to obtain a valid GitHub OAuth code bound to their account. They craft a callback URL to the victim's HedgeDoc instance embedding that code and any state value. The attacker delivers the link through phishing, chat, or an embedded resource. When an authenticated victim visits the link, HedgeDoc selects the note using the victim's session but exchanges the attacker's code for a token that writes to the attacker's Gist account.

javascript
// Patched handler in lib/response.js
function githubActionGist (req, res, note) {
  const code = req.query.code
  const state = req.query.state
  const sessionsState = req.session.githubGistState
  const redirectUri = req.session.githubGistRedirectUri
  delete req.session.githubGistState
  delete req.session.githubGistRedirectUri
  if (!code || !state || sessionsState !== state) {
    return errors.errorForbidden(res)
  }

  const data = {
    client_id: config.github.clientID,
    client_secret: config.github.clientSecret,
    code,
    redirect_uri: redirectUri
  }
  // ...
}

// Patched initiator in lib/web/note/actions.js
exports.createGist = function createGist (req, res, note) {
  const state = nanoid.nanoid()
  const redirectUri = config.serverURL + '/auth/github/callback/' +
    models.Note.encodeNoteId(note.id) + '/gist'
  req.session.githubGistState = state
  req.session.githubGistRedirectUri = redirectUri
  // ...
}

Source: HedgeDoc commit fbd7307

Detection Methods for CVE-2026-58489

Indicators of Compromise

  • Requests to /auth/github/callback/<noteId>/gist where the referring session never issued a corresponding createGist request.
  • GitHub Gist creation events on unfamiliar external accounts referencing note content from your HedgeDoc instance.
  • Repeated 200 responses on the Gist callback endpoint originating from single-click phishing referrers.

Detection Strategies

  • Inspect HedgeDoc application logs for Gist callback requests lacking a matching preceding authorization request in the same session.
  • Correlate authenticated user activity with outbound GitHub token exchange requests to identify session-mismatched flows.
  • Search web proxy or WAF logs for inbound URLs targeting the Gist callback path with externally supplied code and state parameters.

Monitoring Recommendations

  • Enable verbose session and OAuth callback logging on HedgeDoc reverse proxies for post-incident forensics.
  • Alert on any Gist export callback responses that succeed without a session-issued githubGistState.
  • Review GitHub organization audit logs for unexpected Gist creations attributable to HedgeDoc's OAuth application.

How to Mitigate CVE-2026-58489

Immediate Actions Required

  • Upgrade all HedgeDoc instances to version 1.11.0 or later without delay.
  • If patching is deferred, disable the GitHub Gist export feature by removing GitHub OAuth credentials from configuration.
  • Rotate the GitHub OAuth client_secret after upgrading to invalidate any prior tokens tied to abused sessions.

Patch Information

The fix is applied in HedgeDoc 1.11.0 via commit fbd7307. The patch stores a nanoid-generated state and redirect_uri in req.session during createGist, then enforces sessionsState === state in the callback handler before exchanging the code. See the HedgeDoc Security Advisory GHSA-8v9p-5j95-826j for advisory details.

Workarounds

  • Remove github.clientID and github.clientSecret from config.json to disable the Gist export path entirely.
  • Restrict access to the HedgeDoc instance to trusted networks until the upgrade is applied.
  • Instruct users to avoid clicking untrusted links while authenticated to HedgeDoc.
bash
# Disable GitHub Gist export by unsetting OAuth credentials
unset CMD_GITHUB_CLIENTID
unset CMD_GITHUB_CLIENTSECRET

# Verify HedgeDoc version after upgrade
docker exec hedgedoc node -e "console.log(require('./package.json').version)"
# Expected output: 1.11.0 or later

Disclaimer: This content was generated using AI. While we strive for accuracy, please verify critical information with official sources.

Default Legacy - Prefooter | Experience the World’s Most Advanced Cybersecurity Platform

Experience the Most Advanced Cybersecurity Platform

See how the world’s most intelligent, autonomous cybersecurity platform can protect your organization today and into the future.