CVE-2020-5268 (C#, published 2020-04-21T17:15Z)
Description: value: In Saml2 Authentication Services for ASP.NET versions before 1.0.2, and between 2.0.0 and 2.6.0, there is a vulnerability in how tokens are validated in some cases. Saml2 tokens are usually used as bearer tokens - a caller that presents a token is assumed to be the subject of the token. There is also support in the Saml2 protocol for issuing tokens that is tied to a subject through other means, e.g. holder-of-key where possession of a private key must be proved. The Sustainsys.Saml2 library incorrectly treats all incoming tokens as bearer tokens, even though they have another subject confirmation method specified. This could be used by an attacker that could get access to Saml2 tokens with another subject confirmation method than bearer. The attacker could then use such a token to create a log in session. This vulnerability is patched in versions 1.0.2 and 2.7.0.
Severity: MEDIUM (CVSS v3 base score: 7.3)
Weakness: CWE-287 \u2014 Improper Authentication
When an actor claims to have a given identity, the product does not prove or insufficiently proves that the claim is correct.
Repository: https://github.com/Sustainsys/Saml2
Files changed:
Sustainsys.Saml2/Configuration/SPOptions.cs
Sustainsys.Saml2/SAML2P/Saml2PSecurityTokenHandler.cs
Sustainsys.Saml2/SAML2P/Saml2Response.cs
Tests/Tests.Shared/Saml2P/Saml2ResponseTests.cs
Tests/Tests.Shared/WebSSO/Saml2ArtifactBindingTests.cs
Commit message: Fix token replay detection
- Fixes #1107
- Fixes #711


Vulnerable code:
﻿using Sustainsys.Saml2.Metadata;
			MetadataCacheDuration = new XsdDuration(hours: 1);
                if(value == null)
                saml2PSecurityTokenHandler = value; 
                if(saml2PSecurityTokenHandler != null)
                if(value == null)
			= new List<AttributeConsumingService>();
                foreach(var cert in metaDataCertificates)
        
        
                if(!XmlHelpers.KnownSigningAlgorithms.Contains(value))
    }
		// Overridden to fix the fact that the base class version uses NotBefore as the token replay expiry time
		// Due to the fact that we can't override the ValidateToken function (it's overridden in the base class!)
		// we have to parse the token again.
		// This can be removed when:
		// https://github.com/AzureAD/azure-activedirectory-identitymodel-extensions-for-dotnet/issues/898
		// is fixed.
		protected override void ValidateTokenReplay(DateTime? expirationTime, string securityToken, TokenValidationParameters validationParameters)
		{
			var saml2Token = ReadSaml2Token(securityToken);
			base.ValidateTokenReplay(saml2Token.Assertion.Conditions.NotOnOrAfter,
				securityToken, validationParameters);
		}

		protected override Saml2SecurityToken ValidateSignature(string token, TokenValidationParameters validationParameters)
			// Just skip signature validation -- we do this elsewhere
			return ReadSaml2Token(token);
    }
            Assert.Inconclusive("Deliberately ignored test for now");

            Assert.Inconclusive("Ingored for now");

            StubServer.LastArtifactResolutionSoapActionHeader = null;


Fixed code:
﻿using Microsoft.IdentityModel.Tokens;
using Sustainsys.Saml2.Metadata;
using Sustainsys.Saml2.Tokens;
            MetadataCacheDuration = new XsdDuration(hours: 1);
                if (value == null)
                saml2PSecurityTokenHandler = value;
                if (saml2PSecurityTokenHandler != null)
                if (value == null)
            = new List<AttributeConsumingService>();
                foreach (var cert in metaDataCertificates)


                if (!XmlHelpers.KnownSigningAlgorithms.Contains(value))

        private ITokenReplayCache tokenReplayCache;
        public ITokenReplayCache TokenReplayCache 
        { 
            get
            {
                if(tokenReplayCache == null)
                {
                    tokenReplayCache = new TokenReplayCache();
                }
                return tokenReplayCache;
            }
            set
            {
                tokenReplayCache = value;
            }
        }
}
		// Override and build our own logic. The problem is ValidateTokenReplay that serializes the token back. And that
		// breaks because it expects some optional values to be present.
		public override ClaimsPrincipal ValidateToken(string token, TokenValidationParameters validationParameters, out Microsoft.IdentityModel.Tokens.SecurityToken validatedToken)
			var samlToken = ReadSaml2Token(token);

			ValidateConditions(samlToken, validationParameters);
			ValidateSubject(samlToken, validationParameters);

			var issuer = ValidateIssuer(samlToken.Issuer, samlToken, validationParameters);

			// Just using the assertion id for token replay. As that is part of the signed value it cannot
			// be altered by someone replaying the token.
			ValidateTokenReplay(samlToken.Assertion.Conditions.NotOnOrAfter, samlToken.Assertion.Id.Value, validationParameters);

			// ValidateIssuerSecurityKey not called - we have our own signature validation.

			validatedToken = samlToken;
			var identity = CreateClaimsIdentity(samlToken, issuer, validationParameters);

			return new ClaimsPrincipal(identity);
	}
            validationParameters.RequireAudience = false; // Audience restriction optional in SAML2 spec.
            validationParameters.TokenReplayCache = options.SPOptions.TokenReplayCache;
            validationParameters.ValidateTokenReplay = true;