GHSA-5Q8V-J673-M5V4

Vulnerability from github – Published: 2026-03-07 02:10 – Updated: 2026-03-07 02:10
VLAI
Summary
Firefly III user API endpoints expose all users' information to any authenticated user (IDOR)
Details

Summary

The User management API endpoints (GET /api/v1/users and GET /api/v1/users/{id}) are accessible to any authenticated user without admin/owner role verification, exposing all users' email addresses, roles, and account status.

Affected Endpoints

  1. GET /api/v1/users (UserController::index, line 94) — Lists ALL users with full details. No role check.
  2. GET /api/v1/users/{id} (UserController::show, line 126) — Shows any user's details by ID. No role check.

Root Cause (1-of-N Inconsistency)

Other methods in the same controller properly check for the 'owner' role:

  • store()UserStoreRequest::authorize() checks auth()->user()->hasRole('owner')
  • destroy() — Explicitly checks $this->repository->hasRole($admin, 'owner')

But index() and show() have no role check at all. The route group at routes/api.php:734-747 has no admin middleware, only the global auth:api middleware.

Exposed Data

The UserTransformer (line 40-54) returns: - email — user's email address - role — user's role (owner/demo) - blocked — account blocked status - blocked_code — block reason - created_at / updated_at — timestamps

Impact

Any authenticated user can: 1. Enumerate ALL user accounts in the instance 2. Harvest email addresses for phishing/social engineering 3. Identify admin/owner accounts by role 4. Determine which accounts are blocked

Exploitation

# List all users
curl -H "Authorization: Bearer <any_user_token>" https://instance/api/v1/users

# View specific user details
curl -H "Authorization: Bearer <any_user_token>" https://instance/api/v1/users/1

Suggested Fix

Add owner role checks to index() and show(), or restrict the route group with admin middleware:

// Option 1: Add check in controller methods
public function show(User $user): JsonResponse
{
    if (!$this->repository->hasRole(auth()->user(), 'owner') && auth()->user()->id !== $user->id) {
        throw new FireflyException('200025: No access to function.');
    }
    // ...
}

// Option 2: Add middleware to route group
Route::group(['middleware' => ['admin'], ...], ...)
Show details on source website

{
  "affected": [
    {
      "database_specific": {
        "last_known_affected_version_range": "\u003c= 6.5.0"
      },
      "package": {
        "ecosystem": "Packagist",
        "name": "grumpydictator/firefly-iii"
      },
      "ranges": [
        {
          "events": [
            {
              "introduced": "6.4.23"
            },
            {
              "fixed": "6.5.1"
            }
          ],
          "type": "ECOSYSTEM"
        }
      ]
    }
  ],
  "aliases": [],
  "database_specific": {
    "cwe_ids": [
      "CWE-863"
    ],
    "github_reviewed": true,
    "github_reviewed_at": "2026-03-07T02:10:45Z",
    "nvd_published_at": null,
    "severity": "MODERATE"
  },
  "details": "### Summary\n\nThe User management API endpoints (`GET /api/v1/users` and `GET /api/v1/users/{id}`) are accessible to any authenticated user without admin/owner role verification, exposing all users\u0027 email addresses, roles, and account status.\n\n### Affected Endpoints\n\n1. **GET /api/v1/users** (UserController::index, line 94) \u2014 Lists ALL users with full details. No role check.\n2. **GET /api/v1/users/{id}** (UserController::show, line 126) \u2014 Shows any user\u0027s details by ID. No role check.\n\n### Root Cause (1-of-N Inconsistency)\n\nOther methods in the same controller properly check for the \u0027owner\u0027 role:\n\n- `store()` \u2014 `UserStoreRequest::authorize()` checks `auth()-\u003euser()-\u003ehasRole(\u0027owner\u0027)` \u2713\n- `destroy()` \u2014 Explicitly checks `$this-\u003erepository-\u003ehasRole($admin, \u0027owner\u0027)` \u2713\n\nBut `index()` and `show()` have no role check at all. The route group at `routes/api.php:734-747` has no admin middleware, only the global `auth:api` middleware.\n\n### Exposed Data\n\nThe `UserTransformer` (line 40-54) returns:\n- `email` \u2014 user\u0027s email address\n- `role` \u2014 user\u0027s role (owner/demo)\n- `blocked` \u2014 account blocked status\n- `blocked_code` \u2014 block reason\n- `created_at` / `updated_at` \u2014 timestamps\n\n### Impact\n\nAny authenticated user can:\n1. Enumerate ALL user accounts in the instance\n2. Harvest email addresses for phishing/social engineering\n3. Identify admin/owner accounts by role\n4. Determine which accounts are blocked\n\n### Exploitation\n\n```bash\n# List all users\ncurl -H \"Authorization: Bearer \u003cany_user_token\u003e\" https://instance/api/v1/users\n\n# View specific user details\ncurl -H \"Authorization: Bearer \u003cany_user_token\u003e\" https://instance/api/v1/users/1\n```\n\n### Suggested Fix\n\nAdd owner role checks to `index()` and `show()`, or restrict the route group with admin middleware:\n\n```php\n// Option 1: Add check in controller methods\npublic function show(User $user): JsonResponse\n{\n    if (!$this-\u003erepository-\u003ehasRole(auth()-\u003euser(), \u0027owner\u0027) \u0026\u0026 auth()-\u003euser()-\u003eid !== $user-\u003eid) {\n        throw new FireflyException(\u0027200025: No access to function.\u0027);\n    }\n    // ...\n}\n\n// Option 2: Add middleware to route group\nRoute::group([\u0027middleware\u0027 =\u003e [\u0027admin\u0027], ...], ...)\n```",
  "id": "GHSA-5q8v-j673-m5v4",
  "modified": "2026-03-07T02:10:45Z",
  "published": "2026-03-07T02:10:45Z",
  "references": [
    {
      "type": "WEB",
      "url": "https://github.com/firefly-iii/firefly-iii/security/advisories/GHSA-5q8v-j673-m5v4"
    },
    {
      "type": "PACKAGE",
      "url": "https://github.com/firefly-iii/firefly-iii"
    }
  ],
  "schema_version": "1.4.0",
  "severity": [
    {
      "score": "CVSS:4.0/AV:N/AC:L/AT:N/PR:L/UI:N/VC:H/VI:N/VA:N/SC:N/SI:N/SA:N/E:P",
      "type": "CVSS_V4"
    }
  ],
  "summary": "Firefly III user API endpoints expose all users\u0027 information to any authenticated user (IDOR)"
}



Log in or create an account to share your comment.




Tags
Taxonomy of the tags.


Loading…

Loading…

Loading…

Forecast uses a logistic model when the trend is rising, or an exponential decay model when the trend is falling. Fitted via linearized least squares.

Sightings

Author Source Type Date Other

Nomenclature

  • Seen: The vulnerability was mentioned, discussed, or observed by the user.
  • Confirmed: The vulnerability has been validated from an analyst's perspective.
  • Published Proof of Concept: A public proof of concept is available for this vulnerability.
  • Exploited: The vulnerability was observed as exploited by the user who reported the sighting.
  • Patched: The vulnerability was observed as successfully patched by the user who reported the sighting.
  • Not exploited: The vulnerability was not observed as exploited by the user who reported the sighting.
  • Not confirmed: The user expressed doubt about the validity of the vulnerability.
  • Not patched: The vulnerability was not observed as successfully patched by the user who reported the sighting.

Loading…

Detection rules are retrieved from Rulezet.

Loading…

Loading…

Related by attack behaviour

Vulnerabilities whose description is nearest to this one in the vector space of the CIRCL/vulnerability-attack-technique-biencoder model. This is a similarity search over the bi-encoder space (plain cosine), not a classification, and it has no measured accuracy.


Loading…