Limited ability to spoof SAML authentication with missing audience verification
Description
fleet is an open source device management, built on osquery. Versions prior to 4.9.1 expose a limited ability to spoof SAML authentication with missing audience verification. This impacts deployments using SAML SSO in two specific cases: 1. A malicious or compromised Service Provider (SP) could reuse the SAML response to log into Fleet as a user -- only if the user has an account with the same email in Fleet, _and_ the user signs into the malicious SP via SAML SSO from the same Identity Provider (IdP) configured with Fleet. 2. A user with an account in Fleet could reuse a SAML response intended for another SP to log into Fleet. This is only a concern if the user is blocked from Fleet in the IdP, but continues to have an account in Fleet. If the user is blocked from the IdP entirely, this cannot be exploited. Fleet 4.9.1 resolves this issue. Users unable to upgrade should: Reduce the length of sessions on your IdP to reduce the window for malicious re-use, Limit the amount of SAML Service Providers/Applications used by user accounts with access to Fleet, and When removing access to Fleet in the IdP, delete the Fleet user from Fleet as well.
Affected packages
Versions sourced from the GitHub Security Advisory.
| Package | Affected versions | Patched versions |
|---|---|---|
github.com/fleetdm/fleet/v4Go | < 4.9.1 | 4.9.1 |
Affected products
1Patches
135d5a7b285f1Merge pull request from GHSA-ch68-7cf4-35vr
5 files changed · +63 −18
changes/sso-check-audience+1 −0 added@@ -0,0 +1 @@ +* Check for audience restrictions when validating a SAML request.
server/service/service_sessions.go+5 −1 modified@@ -156,7 +156,11 @@ func (svc *Service) CallbackSSO(ctx context.Context, auth fleet.Auth) (*fleet.SS } // Validate response - validator, err := sso.NewValidator(*metadata) + validator, err := sso.NewValidator(*metadata, sso.WithExpectedAudience( + appConfig.SSOSettings.EntityID, + appConfig.ServerSettings.ServerURL, + appConfig.ServerSettings.ServerURL+svc.config.Server.URLPrefix+"/api/v1/fleet/sso/callback", // ACS + )) if err != nil { return nil, ctxerr.Wrap(ctx, err, "create validator from metadata") }
server/sso/types.go+8 −3 modified@@ -205,9 +205,14 @@ type Assertion struct { } type Conditions struct { - XMLName xml.Name - NotBefore string `xml:",attr"` - NotOnOrAfter string `xml:",attr"` + XMLName xml.Name + NotBefore string `xml:",attr"` + NotOnOrAfter string `xml:",attr"` + AudienceRestriction AudienceRestriction `xml:"AudienceRestriction"` +} + +type AudienceRestriction struct { + Audience string `xml:"Audience"` } type Subject struct {
server/sso/validate.go+22 −3 modified@@ -24,9 +24,10 @@ type Validator interface { } type validator struct { - context *dsig.ValidationContext - clock *dsig.Clock - metadata Metadata + context *dsig.ValidationContext + clock *dsig.Clock + metadata Metadata + expectedAudiences []string } func Clock(clock *dsig.Clock) func(v *validator) { @@ -35,6 +36,12 @@ func Clock(clock *dsig.Clock) func(v *validator) { } } +func WithExpectedAudience(audiences ...string) func(v *validator) { + return func(v *validator) { + v.expectedAudiences = audiences + } +} + // NewValidator is used to validate the response to an auth request. // metadata is from the IDP. func NewValidator(metadata Metadata, opts ...func(v *validator)) (Validator, error) { @@ -86,6 +93,18 @@ func (v *validator) ValidateResponse(auth fleet.Auth) error { if currentTime.Before(notBefore) { return errors.New("response too early") } + + verifiesAudience := false + for _, audience := range v.expectedAudiences { + if info.response.Assertion.Conditions.AudienceRestriction.Audience == audience { + verifiesAudience = true + break + } + } + if !verifiesAudience { + return errors.New("wrong audience:" + info.response.Assertion.Conditions.AudienceRestriction.Audience) + } + if auth.UserID() == "" { return errors.New("missing user id") }
server/sso/validate_test.go+27 −11 modified@@ -49,19 +49,35 @@ func TestValidate(t *testing.T) { require.Nil(t, err) clock := dsig.NewFakeClockAt(tm) - validator, err := NewValidator(testMetadata(), Clock(clock)) - require.Nil(t, err) - require.NotNil(t, validator) - auth, err := DecodeAuthResponse(testResponse) - require.Nil(t, err) + testCases := []struct { + audiences []string + shouldFail bool + }{ + {audiences: []string{"kolide"}, shouldFail: false}, + {audiences: []string{"someotheraudience"}, shouldFail: true}, + {audiences: nil, shouldFail: true}, + } - signed, err := validator.ValidateSignature(auth) - require.Nil(t, err) - require.NotNil(t, signed) + for _, tt := range testCases { + validator, err := NewValidator(testMetadata(), Clock(clock), WithExpectedAudience(tt.audiences...)) + require.Nil(t, err) + require.NotNil(t, validator) - err = validator.ValidateResponse(auth) - assert.Nil(t, err) + auth, err := DecodeAuthResponse(testResponse) + require.Nil(t, err) + + signed, err := validator.ValidateSignature(auth) + require.Nil(t, err) + require.NotNil(t, signed) + + err = validator.ValidateResponse(auth) + if tt.shouldFail { + require.Error(t, err) + } else { + require.NoError(t, err) + } + } } func tamperedResponse(original string) (string, error) { @@ -169,7 +185,7 @@ func TestVerifyValidGoogleResponse(t *testing.T) { tm, err := time.Parse(time.RFC3339, "2017-07-18T14:47:08.035Z") require.Nil(t, err) clock := dsig.NewFakeClockAt(tm) - validator, err := NewValidator(testGoogleMetadata(), Clock(clock)) + validator, err := NewValidator(testGoogleMetadata(), Clock(clock), WithExpectedAudience("kolide.edilok.net")) require.Nil(t, err) require.NotNil(t, validator) auth, err := DecodeAuthResponse(samlResponse)
Vulnerability mechanics
Generated by null/stub on May 9, 2026. Inputs: CWE entries + fix-commit diffs from this CVE's patches. Citations validated against bundle.
References
4- github.com/advisories/GHSA-ch68-7cf4-35vrghsaADVISORY
- nvd.nist.gov/vuln/detail/CVE-2022-23600ghsaADVISORY
- github.com/fleetdm/fleet/commit/35d5a7b285f15ddd47486fa656e8b1acf3d48374ghsax_refsource_MISCWEB
- github.com/fleetdm/fleet/security/advisories/GHSA-ch68-7cf4-35vrghsax_refsource_CONFIRMWEB
News mentions
0No linked articles in our index yet.