CVE-2015-2318 (published 2018-01-08T19:29Z)

The TLS stack in Mono before 3.12.1 allows man-in-the-middle attackers to conduct message skipping attacks and consequently impersonate clients by leveraging missing handshake state validation, aka a "SMACK SKIP-TLS" issue.

Severity: MEDIUM
CVSS v2 base score: 6.8 (AV:N/AC:M/Au:N/C:P/I:P/A:P)
CVSS v3 base score: 8.1, HIGH (CVSS:3.0/AV:N/AC:H/PR:N/UI:N/S:U/C:H/I:H/A:H)

Weakness information:
cwe_id: CWE-295
cwe_name: Improper Certificate Validation
description: The product does not validate, or incorrectly validates, a certificate.
extended_description: When a certificate is invalid or malicious, it might allow an attacker to spoof a trusted entity by interfering in the communication path between the host and client. The product might connect to a malicious host while believing it is a trusted host, or the product might be deceived into accepting spoofed data that appears to originate from a trusted host.
         

Fix information:
repo_url: https://github.com/mono/mono
commit_details: repo_url: https://github.com/mono/mono
author: Sebastien Pouliot
author_date: 2015-03-06 10:34:14-05:00
msg: TLS protocol: add handshake state validation
repository: repo_url: https://github.com/mono/mono
repo_name: mono/mono
description: Mono open source ECMA CLI, C# and .NET implementation.
repo_language: C#
owner: mono
file_changes: filename: ClientRecordProtocol.cs
change_type: ModificationType.MODIFY
diff: @@ -129,6 +129,7 @@ private HandshakeMessage createClientHandshakeMessage(HandshakeType type)
 			HandshakeType type, byte[] buffer)
 		{
 			ClientContext context = (ClientContext)this.context;
+			var last = context.LastHandshakeMsg;
 
 			switch (type)
 			{
@@ -148,23 +149,44 @@ private HandshakeMessage createClientHandshakeMessage(HandshakeType type)
 					return null;
 
 				case HandshakeType.ServerHello:
+					if (last != HandshakeType.HelloRequest)
+						break;
 					return new TlsServerHello(this.context, buffer);
 
+					// Optional
 				case HandshakeType.Certificate:
+					if (last != HandshakeType.ServerHello)
+						break;
 					return new TlsServerCertificate(this.context, buffer);
 
+					// Optional
 				case HandshakeType.ServerKeyExchange:
-					return new TlsServerKeyExchange(this.context, buffer);
+					// only for RSA_EXPORT
+					if (last == HandshakeType.Certificate && context.Current.Cipher.IsExportable)
+						return new TlsServerKeyExchange(this.context, buffer);
+					break;
 
+					// Optional
 				case HandshakeType.CertificateRequest:
-					return new TlsServerCertificateRequest(this.context, buffer);
+					if (last == HandshakeType.ServerKeyExchange || last == HandshakeType.Certificate)
+						return new TlsServerCertificateRequest(this.context, buffer);
+					break;
 
 				case HandshakeType.ServerHelloDone:
-					return new TlsServerHelloDone(this.context, buffer);
+					if (last == HandshakeType.CertificateRequest || last == HandshakeType.Certificate || last == HandshakeType.ServerHello)
+						return new TlsServerHelloDone(this.context, buffer);
+					break;
 
 				case HandshakeType.Finished:
-					return new TlsServerFinished(this.context, buffer);
-
+					// depends if a full (ServerHelloDone) or an abbreviated handshake (ServerHello) is being done
+					bool check = context.AbbreviatedHandshake ? (last == HandshakeType.ServerHello) : (last == HandshakeType.ServerHelloDone);
+					// ChangeCipherSpecDone is not an handshake message (it's a content type) but still needs to be happens before finished
+					if (check && context.ChangeCipherSpecDone) {
+						context.ChangeCipherSpecDone = false;
+						return new TlsServerFinished (this.context, buffer);
+					}
+					break;
+					
 				default:
 					throw new TlsException(
 						AlertDescription.UnexpectedMessage,
@@ -172,6 +194,7 @@ private HandshakeMessage createClientHandshakeMessage(HandshakeType type)
 							"Unknown server handshake message received ({0})", 
 							type.ToString()));
 			}
+			throw new TlsException (AlertDescription.HandshakeFailiure, String.Format ("Protocol error, unexpected protocol transition from {0} to {1}", last, type));
 		}
 
 		#endregion

programming_language: Go
filename: Context.cs
change_type: ModificationType.MODIFY
diff: @@ -122,6 +122,8 @@ public bool	ProtocolNegotiated
 			set { this.protocolNegotiated = value; }
 		}
 
+		public bool ChangeCipherSpecDone { get; set; }
+
 		public SecurityProtocolType SecurityProtocol
 		{
 			get 

programming_language: C#
filename: RecordProtocol.cs
change_type: ModificationType.MODIFY
diff: @@ -88,6 +88,8 @@ protected virtual void ProcessChangeCipherSpec ()
 			} else {
 				ctx.StartSwitchingSecurityParameters (false);
 			}
+
+			ctx.ChangeCipherSpecDone = true;
 		}
 
 		public virtual HandshakeMessage GetMessage(HandshakeType type)
@@ -348,9 +350,6 @@ private void InternalReceiveRecordCallback(IAsyncResult asyncResult)
 				// Try to read the Record Content Type
 				int type = internalResult.InitialBuffer[0];
 
-				// Set last handshake message received to None
-				this.context.LastHandshakeMsg = HandshakeType.ClientHello;
-
 				ContentType	contentType	= (ContentType)type;
 				byte[] buffer = this.ReadRecordBuffer(type, record);
 				if (buffer == null)
@@ -458,9 +457,6 @@ public byte[] ReceiveRecord(Stream record)
 			// Try to read the Record Content Type
 			int type = recordTypeBuffer[0];
 
-			// Set last handshake message received to None
-			this.context.LastHandshakeMsg = HandshakeType.ClientHello;
-
 			ContentType	contentType	= (ContentType)type;
 			byte[] buffer = this.ReadRecordBuffer(type, record);
 			if (buffer == null)

programming_language: C#
filename: ServerRecordProtocol.cs
change_type: ModificationType.MODIFY
diff: @@ -33,6 +33,8 @@ namespace Mono.Security.Protocol.Tls
 {
 	internal class ServerRecordProtocol : RecordProtocol
 	{
+		TlsClientCertificate cert;
+		
 		#region Constructors
 
 		public ServerRecordProtocol(
@@ -93,30 +95,45 @@ protected override void ProcessHandshakeMessage(TlsStream handMsg)
 		private HandshakeMessage createClientHandshakeMessage(
 			HandshakeType type, byte[] buffer)
 		{
+			var last = context.LastHandshakeMsg;
 			switch (type)
 			{
 				case HandshakeType.ClientHello:
 					return new TlsClientHello(this.context, buffer);
 
 				case HandshakeType.Certificate:
-					return new TlsClientCertificate(this.context, buffer);
+					if (last != HandshakeType.ClientHello)
+						break;
+					cert = new TlsClientCertificate(this.context, buffer);
+					return cert;
 
 				case HandshakeType.ClientKeyExchange:
-					return new TlsClientKeyExchange(this.context, buffer);
+					if (last == HandshakeType.ClientHello || last == HandshakeType.Certificate)
+						return new TlsClientKeyExchange(this.context, buffer);
+					break;
 
 				case HandshakeType.CertificateVerify:
-					return new TlsClientCertificateVerify(this.context, buffer);
+					if (last == HandshakeType.ClientKeyExchange && cert != null)
+						return new TlsClientCertificateVerify(this.context, buffer);
+					break;
 
 				case HandshakeType.Finished:
-					return new TlsClientFinished(this.context, buffer);
-
+					// Certificates are optional, but if provided, they should send a CertificateVerify
+					bool check = (cert == null) ? (last == HandshakeType.ClientKeyExchange) : (last == HandshakeType.CertificateVerify);
+					// ChangeCipherSpecDone is not an handshake message (it's a content type) but still needs to be happens before finished
+					if (check && context.ChangeCipherSpecDone) {
+						context.ChangeCipherSpecDone = false;
+						return new TlsClientFinished(this.context, buffer);
+					}
+					break;
+					
 				default:
-					throw new TlsException(
-						AlertDescription.UnexpectedMessage,
-						String.Format(CultureInfo.CurrentUICulture,
-							"Unknown server handshake message received ({0})", 
-							type.ToString()));
+					throw new TlsException(AlertDescription.UnexpectedMessage, String.Format(CultureInfo.CurrentUICulture,
+														 "Unknown server handshake message received ({0})", 
+														 type.ToString()));
+					break;
 			}
+			throw new TlsException (AlertDescription.HandshakeFailiure, String.Format ("Protocol error, unexpected protocol transition from {0} to {1}", last, type));
 		}
 
 		private HandshakeMessage createServerHandshakeMessage(

programming_language: Go