Neutron, our AI engine, scored 97.1% on UC Berkeley's CyberGym benchmark. Learn more

Deep Dive

Deep Dive

Postiz Full Takeover by Any Registered User (CVE-2026-115931, CVE-2026-115932, CVE-2026-115933)

In self-hosted Postiz 2.23.0 to 2.25.2 any registered user can read server files, and in 2.15.0 to 2.25.2 they can forge sign-in tokens to take over other organizations; fixed in 2.25.3.

TL;DR

  • Three critical flaws in the Postiz social-media scheduling application affect any user with an ordinary account. Unrestricted provider-method calls (CVE-2026-115931) and a media path traversal (CVE-2026-115932) let that user read any file the backend can read, including the JWT signing secret, and crash the backend with one request. Shared-secret token forgery (CVE-2026-115933) lets them sign in as any user or as super administrator of any organization. Anthropic's security team found all three; the advisories rate them CVSS v3.1 9.9 (CVE-2026-115931), 9.6 (CVE-2026-115932) and 9.9 (CVE-2026-115933).
  • Self-registration is open in the default configuration, so the attacker needs no existing account, no privilege and no action by anyone else. Every affected instance is self-hosted. The repository had 37,856 GitHub stars and 7,395 forks at the time of writing, a rough proxy for deployment reach.
  • All three flaws were disclosed on October 23, 2026 and fixed within a day: version 2.25.2 fixes the file read (CVE-2026-115931), and version 2.25.3 fixes the token forgery (CVE-2026-115933) and the media path traversal (CVE-2026-115932).
  • Upgrade to 2.25.3 or later, then rotate the JWT signing secret, the database and Redis credentials and every connected provider's API keys. Until you upgrade, set DISABLE_REGISTRATION=true and block POST /integrations/extension-refresh.

Am I affected?

You are affected if you self-host Postiz (the gitroomhq/postiz-app project, typically deployed from its Docker images) and any of these holds:

  • Your instance runs 2.23.0 through 2.25.1, for the unrestricted function calls (CVE-2026-115931), fixed in 2.25.2.
  • Your instance runs 2.23.0 through 2.25.2, for the media path traversal (CVE-2026-115932), fixed in 2.25.3. Version 2.25.2 is still affected.
  • Your instance runs 2.15.0 through 2.25.2, for the forged signed tokens (CVE-2026-115933), fixed in 2.25.3.
Flaw Affected versions Fixed version
CVE-2026-115931, unrestricted provider method calls (file read, backend crash) 2.23.0 through 2.25.1 2.25.2 (October 23, 2026)
CVE-2026-115932, media path traversal (arbitrary file read delivered to the attacker) 2.23.0 through 2.25.2 2.25.3 (October 24, 2026)
CVE-2026-115933, forged signed tokens (session, invitation, password reset, enterprise) 2.15.0 through 2.25.2 2.25.3 (October 24, 2026)

Check which version you deploy and whether self-registration is open:

docker inspect --format '{{.Config.Image}}' $(docker ps -q --filter name=postiz)   # the image tag you run
docker compose config | grep -iE 'image:|DISABLE_REGISTRATION'                    # compose pin and the registration flag

If you pulled a floating latest tag before October 24, 2026, assume the instance was affected. Self-registration is open unless DISABLE_REGISTRATION is explicitly set to true, and even with registration closed, any existing user can exploit all three flaws. Two configuration details shape the file reads:

  • The development Dockerfile and the shipped container run the process as root, so every root-readable file is in reach.
  • The main path-traversal delivery route requires a configured Discord provider, which the shipped docker-compose leaves empty.

How do I fix it?

Update to Postiz 2.25.3 or later, which carries all three fixes, then restart the backend and the publishing worker:

  1. Pin the image to v2.25.3 or newer in your compose file, then run docker compose pull && docker compose up -d --force-recreate to recreate the containers with the patched build.
  2. Rotate the JWT signing secret (the JWT_SECRET environment variable): with the old secret, a token an attacker forged before the upgrade would still validate, and rotating the secret invalidates every session, invitation, password-reset and enterprise token at once.
  3. Rotate the database and Redis credentials and the API keys of every connected provider, because the advisories state the environment, which holds them, is readable through the file-read flaws.
  4. Review the user list for accounts you did not create, and the organization list for unexpected super administrators, both of which the takeover path can leave behind.

If you cannot upgrade today, three measures cut exposure. Each has a limit stated in the advisories:

  • Set DISABLE_REGISTRATION=true. This removes the anonymous path in, but does not protect against any existing user.
  • Block POST /integrations/extension-refresh at the reverse proxy if you do not use channels that rely on the Postiz browser extension. This removes the main token-forgery signing route, but leaves the narrower invitation route.
  • Leave the Discord client id, client secret and bot token unset. This removes the main exfiltration route of the path traversal, but leaves its other two routes.

What is the root cause?

Three separate missing checks, one per flaw, all in the backend's handling of a signed-in user's requests.

CVE-2026-115931: the backend ran provider methods by name with no allowlist. The POST /integrations/function route takes a channel and a function name and invokes that function on the provider object, passing the channel's stored token as the first argument.

Before the fix, the only check was that a method with that name exists on the object. The fix commit 9383f5be restricts the route to methods registered as @Tool or marked with a new @DashboardFunction decorator.

One of the now-blocked internal helpers reads a file whose path arrives in that first argument, so a channel whose stored token is a file path turns the route into a file reader; another helper exits the backend process.

-    // @ts-ignore
-    if (integrationProvider[body.name]) {
+    if (
+      this._integrationManager.isDashboardCallable(
+        integrationProvider,
+        body.name
+      )
+    ) {

CVE-2026-115933: one signing secret, no token-type marker, and a route that signed request data. Postiz signs its session, invitation, password-reset and enterprise tokens with one shared secret and stores nothing in the token that says what kind of token it is.

The browser-extension credentials-refresh route signed a JSON payload taken from the request itself and stored the result where the requesting user could read it back. That gave the user a validly signed token with any claims they chose.

The fix commit a5affe62 stops signing request data (AuthService.signJWT becomes AuthService.fixedEncryption, "stored encrypted like connectSocialMedia does, never signed"). It also adds token-type discrimination. Each token-consuming route now rejects payloads that carry fields belonging to another token type, such as timeLimit for team invitations and expires for password resets.

CVE-2026-115932: a media path was trusted to stay inside the upload directory. When a post is published, Postiz treats a media path that is not a web address as a file on disk. It does not check that the file lies inside the upload directory.

A registered user could store a crafted media path with a post and pass the checks that normally restrict it. The publishing worker then read the file and sent it as an attachment to a Discord server the user controls.

The fix commit 12db9bd2 rejects media names containing / or \, confines the worker's file reads to the upload directory, and routes user-influenced URL fetches through the same SSRF-safe HTTP client the providers use.

How is it exploited?

Three chains require only a self-registered account: a file read (CVE-2026-115931), a token-forgery takeover (CVE-2026-115933) and a media exfiltration (CVE-2026-115932). The reporter demonstrated each end to end against an unmodified build at commit 3a7d28b7ad96:

  • The file read (CVE-2026-115931): register, connect a channel whose stored token is a file path, call the internal read helper through POST /integrations/function, and receive the file's contents as bytes. The demonstration returned a file placed outside the application's directories. It also read the process environment: the JWT secret the backend was started with, the database address, the Redis connection string and the credentials of connected providers.
  • The takeover (CVE-2026-115933): obtain a validly signed token with claims of your choosing through the extension-refresh route. Then present it as one of these:

    • A session for another user. You need not know their id. The token's claims are used as a database filter, so a filter object that matches another user selects that user.
    • An organization invitation with a super-administrator role, for any organization whose id a shared post preview link exposes.
    • A password reset for a user whose id you know.
    • An enterprise provisioning token that grants an organization a lifetime top-tier subscription.

    The demonstration made a second self-registered user super administrator of another user's organization and read its API key. Forged tokens do not expire and cannot be told apart from genuine ones. - The exfiltration (CVE-2026-115932): store a post whose media path points outside the upload directory, publish it, and receive the file byte for byte as an attachment on the attacker's Discord server. A second route leaks whether a path exists and can exhaust the backend's memory by pointing at a device file; a third reaches the same read on local-storage instances.

How does the attack flow?

A self-registered account on an unpatched instance reads the session-signing secret and then signs itself in as anyone.

flowchart LR A["Self-register on the instance
(open by default)"] --> B["Connect a channel whose
stored token is a file path"] B --> C["POST /integrations/function
calls the internal read helper"] C --> D["The environment file returns
with the JWT signing secret"] D --> E["Forge a session token
with any claims"] E --> F["Sign in as any user or as
super administrator of any org"]

The token-forgery flaw reaches the same outcome, signing in as any user or as super administrator of any organization, without the file read. The path-traversal flaw achieves the same file read and sends the file's contents to a Discord server the attacker controls.

What can an attacker do?

An attacker can read any file the backend can read, sign in as any user or as super administrator of any organization, stop the backend with one request, and decrypt data encrypted with the shared secret. The full set, per the advisories' impact statements:

  • Read any file the backend process can read, as raw bytes, so binary files work. On the shipped root-running container, that is every file root can read, including the environment file, mounted secrets and application logs.
  • Sign in as any user, join any organization as super administrator, read its API key and channel list, and schedule posts through its connected social accounts.
  • Reset the password of any user whose id they know.
  • Mint an organization with a lifetime top-tier subscription through the enterprise endpoint.
  • Stop the backend with a single request, repeatable after every restart, through the crash helper (CVE-2026-115931) or the memory-exhaustion route (CVE-2026-115932).
  • Decrypt data the application stores encrypted with the same secret as the JWT signing key.

The conditions are an ordinary account (self-registration supplies one on a default instance), network access to the Postiz web interface, and, for the main exfiltration route, a configured Discord provider. No other user does anything, and no privilege beyond the account is required.

Why does the fix work?

Each fix adds the missing check rather than papering over the route.

  • Function-call allowlist. A caller-supplied name can only reach methods declared safe to call from the dashboard. No internal helper that reads files or exits the process is reachable by name.
  • Encryption instead of signing. The extension-refresh route now encrypts request data instead of signing it, so it can no longer hand out valid signatures. Token-type discrimination means a signed payload is accepted only by the route whose field set it matches.
  • Media path validation. Names with separators are rejected and reads stay inside the upload directory, so a stored post cannot point the publishing worker outside it.

The same fixes also close three narrower routes named in the advisories: the invitation signing route, the path-traversal route that leaks whether a path exists and can exhaust memory, and the local-storage file read.

How do I check if I was compromised?

No indicators of compromise are published for these flaws, and no exploitation in the wild has been reported. Run these checks against the documented impacts:

  • Review the instance's user list for accounts created through self-registration that no administrator invited, and each organization's member list for unexpected super administrators.
  • Check connected channels for ones whose stored token looks like a file path rather than a provider credential, which is the footprint of the file-read chain.
  • Inspect published posts for media paths that resolve outside the upload directory, and audit Discord webhook deliveries with file attachments you did not send.
  • Treat the JWT secret, the database and Redis credentials and every connected provider's API keys as exposed if the instance ran an affected version and had open registration or at least one user account, since any authenticated user can reach the flaws. Rotate them. A forged token cannot be told apart from a genuine one by inspection, so rotation is the reliable step.

Timeline

  • 2026-10-23 17:45 UTC: Anthropic discloses CVE-2026-115932 and CVE-2026-115933 to Postiz with working proofs of concept; Postiz reproduces both within 15 minutes.
  • 2026-10-23 19:45 UTC: Anthropic discloses CVE-2026-115931 with a working proof of concept; Postiz reproduces it and works on fixes.
  • 2026-10-23 21:33 UTC: Postiz pushes and verifies the CVE-2026-115931 fix.
  • 2026-10-23 21:59 UTC: Postiz releases 2.25.2 with the CVE-2026-115931 fix and publishes its advisory.
  • 2026-10-24 12:26 and 13:27 UTC: Postiz pushes and verifies the CVE-2026-115932 and CVE-2026-115933 fixes.
  • 2026-10-24 14:16 UTC: Postiz releases 2.25.3 with both fixes and publishes the advisories.
  • 2026-10-26: this post.

FAQ

Do the three flaws chain?

Yes, and each flaw also reaches the takeover on its own. The file read (CVE-2026-115931) and the path traversal (CVE-2026-115932) each expose the JWT signing secret, and anyone holding that secret can forge valid tokens. The token forgery (CVE-2026-115933) produces validly signed tokens directly, without first reading the secret.

Is the hosted Postiz service affected?

The advisories address the self-hosted postiz-app repository and give affected and fixed versions for it. For any managed offering, the operator's deployment version and patch state decide exposure. For your own instance, check which image your containers run with docker inspect --format '{{.Config.Image}}' $(docker ps -q --filter name=postiz).

We cannot upgrade today. What cuts exposure the most?

Close self-registration (DISABLE_REGISTRATION=true), block POST /integrations/extension-refresh at the reverse proxy if you do not use the browser extension, and leave the Discord provider credentials unset. Each removes one route, and none of the three removes the flaws themselves; existing users remain able to exploit what is left open.

What has to be rotated after the upgrade?

The JWT signing secret first, because forged tokens minted before the upgrade validate until the secret changes, then the database and Redis credentials and every connected provider's API key, because the advisories state the environment holding them is readable. Session invalidation follows from the secret rotation.

Is there a public proof of concept?

No code is published. Each disclosure to Postiz included a working proof of concept, and the reporter demonstrated each of the three chains end to end against an unmodified build, but the advisories describe the mechanics rather than releasing scripts. No exploitation in the wild has been reported.

How were these found?

Anthropic's security team reported all three. Enno Gelhaus coordinated the disclosure through GAdvisory. Nevo David fixed them, and each fix was verified within hours.

References

  • PSA-2026-JNXM56, the CVE-2026-115931 advisory with the CVSS v3.1 9.9 vector, affected range, workarounds, demonstrated impacts and timeline: https://gadvisory.org/advisories/PSA-2026-JNXM56 (checked October 26, 2026)
  • PSA-2026-YT2HMH, the CVE-2026-115933 advisory with the CVSS v3.1 9.9 vector and the takeover impacts: https://gadvisory.org/advisories/PSA-2026-YT2HMH (checked October 26, 2026)
  • PSA-2026-E9AHFV, the CVE-2026-115932 advisory with the CVSS v3.1 9.6 vector and the route-by-route configuration preconditions: https://gadvisory.org/advisories/PSA-2026-E9AHFV (checked October 26, 2026)
  • The GitHub advisories carrying the same three flaws, GHSA-h5r5-h782-vqvp, GHSA-9j6f-8rw3-93c4 and GHSA-rmc2-p2gv-m88f: https://github.com/gitroomhq/postiz-app/security/advisories (checked October 26, 2026)
  • Fix commits: 9383f5be (allowlisted provider methods, CVE-2026-115931), a5affe62 (encryption instead of signing plus token-type discrimination, CVE-2026-115933) and 12db9bd (media path validation and upload-directory confinement, CVE-2026-115932): https://github.com/gitroomhq/postiz-app/commit/9383f5be4d310c346e33a3748a9a7ded64c96fed, https://github.com/gitroomhq/postiz-app/commit/a5affe62c74e39c1e7ac960d50d8418b807d532c, https://github.com/gitroomhq/postiz-app/commit/12db9bd2f428b628a62809669c3d76297e07750a (checked October 26, 2026)
  • Postiz 2.25.2 release notes, October 23, 2026, addressing PSA-2026-JNXM56: https://github.com/gitroomhq/postiz-app/releases/tag/v2.25.2 (checked October 26, 2026)
  • Postiz 2.25.3 release notes, October 24, 2026, addressing PSA-2026-E9AHFV and PSA-2026-YT2HMH: https://github.com/gitroomhq/postiz-app/releases/tag/v2.25.3 (checked October 26, 2026)
  • The Postiz repository, whose star and fork counts (37,856 and 7,395 on October 26, 2026) stand in for the self-hosted deployment reach: https://github.com/gitroomhq/postiz-app (checked October 26, 2026)