PHP2Shell: Read our blog on the critical RCE vulnerabilities affecting 70% of the Internet
Vulnerability DatabaseGHSA-c8r6-23w7-5q9p

GHSA-c8r6-23w7-5q9p: 
Ruby vulnerability analysis and mitigation

Vulnerability Details

File: lib/doorkeeper/oauth/client/credentials.rb Line: 9-14 (Client::Credentials.from_request) Affected component: Client authentication, used by POST /oauth/token, POST /oauth/revoke, and POST /oauth/introspect CWE: CWE-287 — Improper Authentication Severity: Low CVSS: 4.0 — CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:U/C:L/I:L/A:N

Root Cause

RFC 6749 §2.3 states: "The client MUST NOT use more than one authentication method in each request." Client::Credentials.from_request does not enforce this:

Credentials = Struct.new(:uid, :secret) do
  class << self
    def from_request(request, *credentials_methods)
      credentials_methods.inject(nil) do |_, method|
        method = self.method(method) if method.is_a?(Symbol)
        credentials = Credentials.new(*method.call(request))
        break credentials if credentials.present?
      end
    end
    ...

With the default client_credentials_methods of %i[from_basic from_params] (lib/doorkeeper/config.rb:600), this walks extraction methods in order and simply returns the first one that yields non-blank credentials — any other credentials present in the same request are silently discarded with no error, warning, or log entry.

Attack Scenario

  1. A request to POST /oauth/token (or /oauth/revoke, /oauth/introspect) carries both an Authorization: Basic <client_A_creds> header and client_id/client_secret form parameters for a different, unrelated client B.
  2. Doorkeeper authenticates the request as client A (Basic auth is checked first) and completely ignores client B's credentials — with no signal to the caller or to server-side logging that two distinct client identities were presented in the same request.
  3. This matters most in integration patterns where a gateway/proxy layer attaches its own Basic-auth credentials in front of Doorkeeper while forwarding caller-controlled body credentials for a different client — the caller's intended client identity is silently overridden with no indication that happened, producing incorrect client attribution for token issuance/revocation/introspection.

Dynamically Confirmed (v5.9.3, spec/dummy Rails app)

Registered two confidential OAuth applications (Client A, Client B), then:

curl -u "<Client A uid>:<Client A secret>" -X POST http://localhost:3099/oauth/token \
  --data-urlencode "grant_type=client_credentials" \
  --data-urlencode "client_id=<Client B uid>" \
  --data-urlencode "client_secret=<Client B secret>"

Result: HTTP 200, access token issued. Inspecting the token server-side confirmed token.application_id belonged to Client A (the Basic-auth identity) — Client B's complete, valid credentials in the body were silently discarded with no error.

Impact

  • Type: CWE-287 — Improper Authentication (RFC 6749 §2.3 violation)
  • Auth required: No additional privilege beyond having valid credentials for at least one of the two identities presented
  • Consequence: Ambiguous/incorrect client identity resolution with no server-side signal; in gateway/proxy integration patterns this can cause tokens to be issued, revoked, or introspected under an unintended client identity

Vulnerable Code

def from_request(request, *credentials_methods)
  credentials_methods.inject(nil) do |_, method|
    method = self.method(method) if method.is_a?(Symbol)
    credentials = Credentials.new(*method.call(request))
    break credentials if credentials.present?
  end
end
def from_request(request, *credentials_methods)
  matches = credentials_methods.filter_map do |method|
    method = self.method(method) if method.is_a?(Symbol)
    credentials = Credentials.new(*method.call(request))
    credentials if credentials.present?
  end
  raise Errors::DoorkeeperError, "multiple client authentication methods" if matches.size > 1
  matches.first
end

Note: this exact defect class was already identified and fixed by the maintainers in the unreleased 6.0 client-authentication registry (commit 4289785e, "Reject requests using multiple client authentication methods (RFC 6749 §2.3)"). That fix applies to the new registry-based mechanism introduced during 6.0 development and does not cover the Client::Credentials path used by every currently released version (including the latest stable 5.9.3), since that file does not exist on main anymore. This report requests the equivalent fix be applied to the maintenance branch(es) serving currently released versions.

Verification

Dynamically confirmed on v5.9.3 in a local Rails lab (spec/dummy app, Puma, Ruby 3.3.8) on 2026-08-01 — see reproduction steps above.


Source: NVD

Related Ruby vulnerabilities:

CVE ID

Severity

Score

Technologies

Component name

CISA KEV exploit

Has fix

Published date

GHSA-9h43-g84r-c93mHIGH7.4
  • Ruby logoRuby
  • doorkeeper-openid_connect
NoYesOct 21, 2026
GHSA-wgw9-qhqg-8hv5MEDIUM5.3
  • Ruby logoRuby
  • sanitize
NoYesOct 21, 2026
GHSA-pq4g-8rxf-4g29MEDIUM5.3
  • Ruby logoRuby
  • crass
NoYesOct 21, 2026
GHSA-c62j-746x-gfcrMEDIUM4.8
  • Ruby logoRuby
  • doorkeeper
NoYesOct 21, 2026
GHSA-c8r6-23w7-5q9pLOWN/A
  • Ruby logoRuby
  • doorkeeper
NoNoOct 21, 2026

Free Vulnerability Assessment

Benchmark your Cloud Security Posture

Evaluate your cloud security practices across 9 security domains to benchmark your risk level and identify gaps in your defenses.

Request assessment

Get a personalized demo

Ready to see Wiz in action?

"Best User Experience I have ever seen, provides full visibility to cloud workloads."
David EstlickCISO
"Wiz provides a single pane of glass to see what is going on in our cloud environments."
Adam FletcherChief Security Officer
"We know that if Wiz identifies something as critical, it actually is."
Greg PoniatowskiHead of Threat and Vulnerability Management