CVE-2020-5261 (C#, published 2020-03-25T02:15Z)
Description: value: Saml2 Authentication services for ASP.NET (NuGet package Sustainsys.Saml2) greater than 2.0.0, and less than version 2.5.0 has a faulty implementation of Token Replay Detection. Token Replay Detection is an important defence in depth measure for Single Sign On solutions. The 2.5.0 version is patched. Note that version 1.0.1 is not affected. It has a correct Token Replay Implementation and is safe to use. Saml2 Authentication services for ASP.NET (NuGet package Sustainsys.Saml2) greater than 2.0.0, and less than version 2.5.0 have a faulty implementation of Token Replay Detection. Token Replay Detection is an important defense measure for Single Sign On solutions. The 2.5.0 version is patched. Note that version 1.0.1 and prior versions are not affected. These versions have a correct Token Replay Implementation and are safe to use.
Severity: MEDIUM (CVSS v3 base score: 6.8)
Weakness: CWE-294 \u2014 Authentication Bypass by Capture-replay
A capture-replay flaw exists when the design of the product makes it possible for a malicious user to sniff network traffic and bypass authentication by replaying it to the server in question to the same effect as the original message (or with minor changes).
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;