Search

Find a vulnerability

Search criteria

    Related vulnerabilities

    GHSA-P9F5-H3RX-J5QW

    Vulnerability from github – Published: 2026-06-22 23:59 – Updated: 2026-06-22 23:59
    VLAI
    Summary
    Gogs Missing Authorization in Attachment Download
    Details

    Summary

    In Gogs 0.14.1, GET /attachments/:uuid returns the raw attachment file without verifying whether the requester has view permission for the associated Issue/Comment/Release or the repository. In a test environment with REQUIRE_SIGNIN_VIEW = false, we confirmed that an unauthenticated user can download attachments belonging to a private repository.

    Description

    /attachments/:uuid retrieves an attachment record solely by the UUID provided in the URL and returns the corresponding local file without performing any authorization checks against the attachment’s parent object (Issue/Comment/Release) or the repository it belongs to. As a result, even attachments under private repositories can be downloaded by an unauthenticated user (or a user without proper permissions) as long as the UUID is known.

    Relevant code (internal/cmd/web.go:306):

    m.Get("/attachments/:uuid", func(c *context.Context) {
        attach, err := database.GetAttachmentByUUID(c.Params(":uuid"))
        if err != nil {
            c.NotFoundOrError(err, "get attachment by UUID")
            return
        } else if !com.IsFile(attach.LocalPath()) {
            c.NotFound()
            return
        }
    
        fr, err := os.Open(attach.LocalPath())
        if err != nil {
            c.Error(err, "open attachment file")
            return
        }
        defer fr.Close()
    
        c.Header().Set("Content-Security-Policy", "default-src 'none'; style-src 'unsafe-inline'; sandbox")
        c.Header().Set("Cache-Control", "public,max-age=86400")
        c.Header().Set("Content-Disposition", fmt.Sprintf(`inline; filename="%s"`, attach.Name))
    
        if _, err = io.Copy(c.Resp, fr); err != nil {
            c.Error(err, "copy from file to response")
            return
        }
    })
    

    The UUID lookup itself also performs no validation tied to repository visibility or user permissions. Authorization is not enforced at this layer.

    Relevant code (internal/database/attachment.go:124):

    // GetAttachmentByUUID returns attachment by given UUID.
    func GetAttachmentByUUID(uuid string) (*Attachment, error) {
        return getAttachmentByUUID(x, uuid)
    }
    

    Preconditions

    • The attacker knows the target attachment’s UUID (i.e., the attachment URL).
    • For unauthenticated exploitation: [auth] REQUIRE_SIGNIN_VIEW = false.
    • Even when REQUIRE_SIGNIN_VIEW = true, exploitation may still be possible because the handler does not check repository-level permissions; a user who can log in but lacks access to the target repository may still retrieve the attachment.

    Steps to Reproduce

    1. Log in as an administrator and create a private repository, e.g. myadmin/idor-attach-1770724346-1a13bb.
    2. Add an attachment to an Issue in that repository and note the attachment UUID (example UUID used during testing: f06d90f8-5b62-4c10-ac8d-f11fdf870b57).
    3. Log out and access the following as an unauthenticated user:

    4. The repository page → 404 Not Found image

    5. The Issue page under that repository → 404 Not Found image

    6. GET /attachments/<uuid>the attachment file is successfully downloaded image

    Minimum Required Privileges

    • REQUIRE_SIGNIN_VIEW = false: none (works without authentication).
    • REQUIRE_SIGNIN_VIEW = true: only the ability to log in (repository view permission is not required in practice).

    Impact

    • Confidential information attached to private repositories or restricted Issues/Releases may be disclosed.

    • Examples include credentials, cryptographic keys, personal data, internal documents, or unpublished source code fragments.

    • While the severity depends on the attachment contents, attachments frequently contain sensitive data, making the potential impact high.
    Show details on source website

    {
      "affected": [
        {
          "database_specific": {
            "last_known_affected_version_range": "\u003c= 0.14.2"
          },
          "package": {
            "ecosystem": "Go",
            "name": "gogs.io/gogs"
          },
          "ranges": [
            {
              "events": [
                {
                  "introduced": "0"
                },
                {
                  "fixed": "0.14.3"
                }
              ],
              "type": "ECOSYSTEM"
            }
          ]
        }
      ],
      "aliases": [
        "CVE-2026-52799"
      ],
      "database_specific": {
        "cwe_ids": [
          "CWE-639",
          "CWE-862"
        ],
        "github_reviewed": true,
        "github_reviewed_at": "2026-06-22T23:59:03Z",
        "nvd_published_at": null,
        "severity": "HIGH"
      },
      "details": "## Summary\n\nIn Gogs 0.14.1, `GET /attachments/:uuid` returns the raw attachment file **without verifying whether the requester has view permission for the associated Issue/Comment/Release or the repository**.\nIn a test environment with `REQUIRE_SIGNIN_VIEW = false`, we confirmed that **an unauthenticated user can download attachments belonging to a private repository**.\n\n## Description\n\n`/attachments/:uuid` retrieves an attachment record solely by the UUID provided in the URL and returns the corresponding local file **without performing any authorization checks** against the attachment\u2019s parent object (Issue/Comment/Release) or the repository it belongs to. As a result, even attachments under private repositories can be downloaded by an unauthenticated user (or a user without proper permissions) as long as the UUID is known.\n\nRelevant code (internal/cmd/web.go:306):\n\n```go\nm.Get(\"/attachments/:uuid\", func(c *context.Context) {\n\tattach, err := database.GetAttachmentByUUID(c.Params(\":uuid\"))\n\tif err != nil {\n\t\tc.NotFoundOrError(err, \"get attachment by UUID\")\n\t\treturn\n\t} else if !com.IsFile(attach.LocalPath()) {\n\t\tc.NotFound()\n\t\treturn\n\t}\n\n\tfr, err := os.Open(attach.LocalPath())\n\tif err != nil {\n\t\tc.Error(err, \"open attachment file\")\n\t\treturn\n\t}\n\tdefer fr.Close()\n\n\tc.Header().Set(\"Content-Security-Policy\", \"default-src \u0027none\u0027; style-src \u0027unsafe-inline\u0027; sandbox\")\n\tc.Header().Set(\"Cache-Control\", \"public,max-age=86400\")\n\tc.Header().Set(\"Content-Disposition\", fmt.Sprintf(`inline; filename=\"%s\"`, attach.Name))\n\n\tif _, err = io.Copy(c.Resp, fr); err != nil {\n\t\tc.Error(err, \"copy from file to response\")\n\t\treturn\n\t}\n})\n```\n\nThe UUID lookup itself also performs **no validation tied to repository visibility or user permissions**. Authorization is not enforced at this layer.\n\nRelevant code (internal/database/attachment.go:124):\n\n```go\n// GetAttachmentByUUID returns attachment by given UUID.\nfunc GetAttachmentByUUID(uuid string) (*Attachment, error) {\n\treturn getAttachmentByUUID(x, uuid)\n}\n```\n\n## Preconditions\n\n* The attacker knows the target attachment\u2019s UUID (i.e., the attachment URL).\n* For unauthenticated exploitation: `[auth] REQUIRE_SIGNIN_VIEW = false`.\n* Even when `REQUIRE_SIGNIN_VIEW = true`, exploitation may still be possible because the handler does not check repository-level permissions; a user who can log in but lacks access to the target repository may still retrieve the attachment.\n\n## Steps to Reproduce\n\n1. Log in as an administrator and create a private repository, e.g. `myadmin/idor-attach-1770724346-1a13bb`.\n2. Add an attachment to an Issue in that repository and note the attachment UUID\n   (example UUID used during testing: `f06d90f8-5b62-4c10-ac8d-f11fdf870b57`).\n3. Log out and access the following as an unauthenticated user:\n\n* The repository page \u2192 404 Not Found\n\u003cimg width=\"1702\" height=\"758\" alt=\"image\" src=\"https://github.com/user-attachments/assets/8fdb1d92-cfc3-4ef8-977e-60ec13f792df\" /\u003e\n\n* The Issue page under that repository \u2192 404 Not Found\n\u003cimg width=\"1983\" height=\"546\" alt=\"image\" src=\"https://github.com/user-attachments/assets/c44c5e69-8ca2-4ea6-a071-62302b7e896f\" /\u003e\n\n* `GET /attachments/\u003cuuid\u003e` \u2192 **the attachment file is successfully downloaded**\n\u003cimg width=\"2007\" height=\"378\" alt=\"image\" src=\"https://github.com/user-attachments/assets/23950ac6-6b3a-42f8-a06b-b9e0cf508d24\" /\u003e\n\n\n\n## Minimum Required Privileges\n\n* `REQUIRE_SIGNIN_VIEW = false`: none (works without authentication).\n* `REQUIRE_SIGNIN_VIEW = true`: only the ability to log in (repository view permission is not required in practice).\n\n## Impact\n\n* Confidential information attached to private repositories or restricted Issues/Releases may be disclosed.\n\n  * Examples include credentials, cryptographic keys, personal data, internal documents, or unpublished source code fragments.\n* While the severity depends on the attachment contents, attachments frequently contain sensitive data, making the potential impact high.",
      "id": "GHSA-p9f5-h3rx-j5qw",
      "modified": "2026-06-22T23:59:03Z",
      "published": "2026-06-22T23:59:03Z",
      "references": [
        {
          "type": "WEB",
          "url": "https://github.com/gogs/gogs/security/advisories/GHSA-p9f5-h3rx-j5qw"
        },
        {
          "type": "WEB",
          "url": "https://github.com/gogs/gogs/pull/8320"
        },
        {
          "type": "WEB",
          "url": "https://github.com/gogs/gogs/commit/d3ca23f9f33d5710472a775d6dcd3a7bb128bb05"
        },
        {
          "type": "PACKAGE",
          "url": "https://github.com/gogs/gogs"
        },
        {
          "type": "WEB",
          "url": "https://github.com/gogs/gogs/releases/tag/v0.14.3"
        }
      ],
      "schema_version": "1.4.0",
      "severity": [
        {
          "score": "CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:N/A:N",
          "type": "CVSS_V3"
        }
      ],
      "summary": "Gogs Missing Authorization in Attachment Download"
    }