CVE-2021-43798 – Path Traversal – Grafana

ISGroup Cybersecurity

Grafana is a widely used open-source platform for analytics and monitoring. The CVE-2021-43798 vulnerability allows unauthenticated malicious actors to read arbitrary files from the server’s file system by exploiting a weakness in how plugin paths are handled. This vulnerability has been actively exploited and is now listed in the CISA Known Exploited Vulnerabilities Catalog. It is critical to apply the available patches immediately.

ProductGrafana, Grafana
Date2025-10-10 13:14:42

Technical Summary

The vulnerability was present in the getPluginAssets function within the pkg/api/plugins.go file. The original code did not perform any validation on the requested file path, but simply took the URL parameter and used it to construct a file path:

func (hs *HTTPServer) getPluginAssets(c *models.ReqContext) {

  pluginID := web.Params(c.Req)[":pluginId"]

        ...........................

  requestedFile := filepath.Clean(web.Params(c.Req)["*"])

  pluginFilePath := filepath.Join(plugin.PluginDir, requestedFile)

        ...........................

  f, err := os.Open(pluginFilePath)

        ...........................

}

The code took the wild-card path provided by the user (web.Params(c.Req)["*"]) and performed a filepath.Clean directly on it, then filepath.Join(plugin.PluginDir, requestedFile). This allowed segments like ..%2f..%2f..%2f..%2fetc/passwd to “escape” the plugin directory after the paths were joined.

The fix is now as follows:

func (hs *HTTPServer) getPluginAssets(c *models.ReqContext) {

  pluginID := web.Params(c.Req)[":pluginId"]

  plugin, exists := hs.pluginStore.Plugin(c.Req.Context(), pluginID)

  if (!exists) {

    c.JsonApiErr(404, "Plugin not found", nil)

    return

  }

  // prepend slash for cleaning relative paths

  requestedFile := filepath.Clean(filepath.Join("/", web.Params(c.Req)["*"]))

  rel, err := filepath.Rel("/", requestedFile)

  if (err != nil) {

    // slash is prepended above therefore this is not expected to fail

    c.JsonApiErr(500, "Failed to get the relative path", err)

    return

  }

  if !plugin.IncludedInSignature(rel) {

    hs.log.Warn("Access to requested plugin file will be forbidden in upcoming Grafana versions as the file "+

      "is not included in the plugin signature", "file", requestedFile)

  }

  absPluginDir, err := filepath.Abs(plugin.PluginDir)

  if (err != nil) {

    c.JsonApiErr(500, "Failed to get plugin absolute path", nil)

    return

  }

  pluginFilePath := filepath.Join(absPluginDir, rel)

  f, err := os.Open(pluginFilePath)

        ...........................

}

Recommendations

  1. Apply the patch immediately: Update all Grafana instances between versions v8.0.0-beta1 and v8.3.0 to the latest version.

  2. Reverse proxy: If updating is not possible, use a reverse proxy in front of Grafana that normalizes the request PATH to mitigate the vulnerability.

  3. Monitoring and analysis: Perform an access audit and review requests to the /public/plugins/ endpoint for anomalies.

  4. Defense in depth: Use Web Application Firewalls (WAF) configured to block path traversal attempts as an additional layer of protection.

[Callforaction-THREAT-Footer]