NETMF / netmf-interpreter

.NET Micro Framework Interpreter
http://netmf.github.io/netmf-interpreter/
Other
487 stars 224 forks source link

SSL : bundle CA certificates or big security issue ? #251

Open ppatierno opened 9 years ago

ppatierno commented 9 years ago

I'm reporting the following issue/discussion from CodePlex (http://netmf.codeplex.com/workitem/2381)

Hello,

before explaining my scenario ... the direct question :

is there a bundle of certification autorithy (CA) certificates in net mf ? Why this question ? Read following ...

I'm working on a project with AMQP protocol (instead of HTTP REST APIs) and Microsoft Azure Service Bus (queues, topics and event hubs) using the AMQP.Net Lite library (http://amqpnetlite.codeplex.com). The Service Bus needs an SSL/TLS based connection for sending and receiving messages but I see that this connection is established in a very simple way ... and it seems without any check ! The library uses the following code to execute authentication ...

sslSocket.AuthenticateAsClient( address.Host, null, noVerification ? SslVerification.NoVerification : SslVerification.VerifyPeer, SslProtocols.Default);

The parameters are ...

Now ... during the SSL handshake, the server sends its certificate (inside a chain with two other CA certificate) and the client needs to verify it. To do that, the client needs a CA certificate so that with its public key it can verify the signature of the server certificate just received.

The AuthenticateAsClient method used in the library doesn't have a collection of X509 certificates as CA certificates (there is another overload with this parameter but not the version used in the library).

So, why the authentication works fine !??

I think that there are two possibilities :

Another strange behavior is that after established TCP connection with the right hostname, if I change at runtime the hostname parameter for AuthenticateAsClient (ex. from "myservice.servicebus.windows.net" to "helloworld") ... the verification works !!! There isn't any check on the hostname in the server certificate !

It seems to be a big security issue ... or I'm wrong because I can't see in the right direction ?

Using my M2Mqtt library, I have always used the AuthenticateAsClient overload that takes CA certificate to be sure to verify the server certificate (MQTT broker).

I already posted this question on GHI forum (https://www.ghielectronics.com/community/forum/topic?id=17816) because I had the doubt that GHI injected CA certificates in their custom firmwares. GHI people replied me that they don't change original SSL stack and they don't inject any CA certificates in the custom firmware.

Thanks, Paolo

smaillet-ms commented 9 years ago

is there a bundle of certification autorithy (CA) certificates in net mf ?

No, Certificates are large binaries and building in all top level certs found in major OS/browsers would take a significant amount of space. Thus, it is up to each device OEM and application writer to include certificates used in the device or application image. While this puts some burden on the application writer to be sure the correct certs are available and used it keeps footprint down to the minimum of what is actually required for a given application.

ppatierno commented 9 years ago

Hi Steve,

I agree with you on what you say about CA certificates (large binaries ...) but it means that the AuthenticateAsClient doesn't work well ! It doesn't execute any verification (it hasn't the CA certificate) even if we provides right parameters (VerifyPeer). The server isn't authenticated in this manner .... what do you think about that ?

Paolo.

smaillet-ms commented 9 years ago

@ppatierno I'll need to look into it some more. Philosophically I think, that if you specify you want validation of the certificate but you haven't given it a cert nor does it have one internally it should report an error. But it's been a while since I thought about this level of the APIs so will need to research it a bit.

ppatierno commented 9 years ago

@smaillet-ms thanks Steve ... as you know there is an overload of AuthenticateAsClient that receives CA certificates so I think that the first one must return an error or you should remove it from the micro framework. The first one is useful on PC where you have CA certificates installed but on .Net Micro Framework you don't have CA certificates installed as you said. For this reason is useful to have only the overload that can receive CA certificate from you as parameter. Another possibility is to use a ServerCertificateValidationCallback as for .Net Micro Framework so that even if without certificates, the developer can coding the validation logic.

I hope to have news on this ! Thanks !

lt72 commented 9 years ago

Hi Paolo,

I looked at the code in some level of details, and merged back to what I remembered from a while ago. I am interested in learning more, and especially see if you have any exploit at hand that you can share the code for. Remember we are using OpenSSL 1.0, and, although, we are definitely looking at upgrading to the latest version, we deemed it was more important to fix the TCP/IP stack reliability issue first, following large customer demand. The OpenSSL stack requires no specific attention to be regenerated, so it could be done in a reasonable amount of time by the community as well. We will get to it eventually, and likely very soon, but we needed first to fix the TCP/IP stack.

Overall we are striving to provide OpenSSL with all information we have, and use the most secure mode that OpenSSL offers. There is no security related operation that is carried out inside the NETMF stack. All operation are delegated to the OpenSSL stack. NETMF is merely the delegator. (Please point me to the code if you found differently). As the OpenSSL stack is re-generated, it should already work right away at the highest security level, because, to the best of my knowledge, no new features have been introruced at the API level, and we are already allowing, and using in our framework, the highest level of security allowed per API spec.

Below is what I found out, please let me know if you spot any issues or missing necessary detail.

First of all, we do use SSL in our framework inside the HTTP stack. There is no other pre-coded usage.

In our HTTP stack, we expose the property

        /// <summary>
        /// Gets or sets the array of certificates used to authenticate https
        /// servers.  These certificates are used only for https connections;
        /// http connections do not require them.
        /// </summary>
        public X509Certificate[] HttpsAuthentCerts
        {
            get { return m_caCerts; }
            set { m_caCerts = value; }
        }

from the HttpWebRequest type. Any user can use, and she should, that property to set the proper CA authority certificate bundle, as per user needs. Under the covers we do nothing more than providing such parameter to the richest overload for AuthenticateAsClient method, as any other NETMF app could do, no limitations there. We then initiate the connection using the highest level of trust that the SSL API offers (in .NET MF terms, that is the enumeration value SslVerification.CertificateRequired, in OpenSSL that is the flag SSL_VERIFY_FAIL_IF_NO_PEER_CERT) and the CA certificates bundle. Initiating a connection with SslVerification.CertificateRequired puts the onus on OpenSSL to enforce that the peer party has a certificate and the certificate is verified. Please note that it is a design choice in OpenSSL 1.0 to ignore this parameter in client mode, whereas it is instead honored in server mode. See OpenSSL docs. This explains very clearly why the authentication works at the OpenSSL level.

Overall, I see that we provide a number of overloads to authenticate a client or server SSL connection, some with higher security constraints than others. We do use TLS wherever possible. And I also see that OpenSSL enforces those constraints differently in client and server mode. Please do note that we are talking here about high level functions in OpenSSL here, very much exposed to scrutiny. I do personally believe that it is unlikely that there is a protocol issue with OpenSSL at this level, but I cannot exclude that that is the case. I would be very interested in looking at a deeper analysis or a repro if you have one.

As for the runtime name change for the target host, that is interesting if you closed the session. If you are re-authenticating with a different host name on the same session, then we are using the SSL context that is already bounded. Even in this instance, we do actually notify SSL of the name change through a call to SSL_set_tlsext_host_name. This features actually calls for SNI support for hostname verification. This is a later addition to OpenSSL, and may not be available in 1.0, which is the version we are using.

ppatierno commented 9 years ago

Hi Lorenzo, first of all thanks for your depth answer.

When I noticed the issue the first thing I did was to deep into the .Net MF source code (4.3 version) and I found what you said ... for this reason my doubt was bigger. However, it could be possible that I lost something so I posted the problem on CodePlex (then on GitHub) for the team.

How the HttpWebRequest works is great and it's the right way to provide the CA certificate to validate the certificate received from the server. Of course it works great if you use (as you said) the richer overload of AuthenticateAsClient method that receives the "ca" parameter (CA certificates array) as MSDN documentation : https://msdn.microsoft.com/en-us/library/hh434090.aspx

The big problem is that the AMQP .Net Lite library doesn't use HTTP (it uses AMQP) so the SslStream class directly. On SslStream, it uses the AuthenticateAsClient overload without "ca" parameter ! You can see this at source code here : https://github.com/Azure/amqpnetlite/blob/master/src/NetMF/TcpTransport.cs

As I posted above, the code is the following :

SslSocket sslSocket = new SslSocket(socket); sslSocket.AuthenticateAsClient( address.Host, null, noVerification ? SslVerification.NoVerification : SslVerification.VerifyPeer, SslProtocols.Default); this.socketTransport = sslSocket;

SslSocket is a class that derives from SslStream. As you can see it's the overload without "ca" certificate parameter.

What's your idea about this code ? How can it work connecting the client to Azure Service Bus ?

If Net MF passes all parameters to OpenSSL layer, could be it an OpenSSL bug ?

Thanks, Paolo

lt72 commented 9 years ago

It seems OpenSSL is forgiving on the client side and not honor the flags, as per here. Also, in theory, the server cert could carry the information for the signing authority. A further analysis would require much time and I am not aware of an exploit at this time. AMQP lite would better call the stricter method, but that would be useless without SSL enforcing the verification.

ppatierno commented 9 years ago

Of course, using Wireshark on my PC I see that the Azure Service Bus server sends a chain (server cert, intermediate CA Microsoft cert, root CA cert). AFAIK, verify the chain means that the client can use the received intermediate CA Microsoft cert to verify the server cert but to verify the intermediate CA Microsoft cert it can't trust the received CA cert but it needs to have a local copy of it. In some cases if you have a copy of the server cert locally, the chain verification is avoided and you can trust the server (but it isn't our case).

I don't know if the received chain is enough to validate a server certificate but I don't think so. I think that a local copy of (at least) the root CA certificate is always needed on the client.

It's a very strange situation, however it's true that even if it's a problem related to OpenSSL ... it's a problem for the .Net Micro Framework that doesn't appear a secure environment about internet connection :-(

I hope to be wrong and that I lost something. I hope there are things I don't know and that it's the right way it works. I'll try to deep into this because I like ... to deep into things :-)

However thanks for your help Lorenzo ... I appreciated your interest in the issue ... even if today we don't have a real answer. Congratulations for your work in .Net Micro Framework that is in part an "Italian" product ;-)

Paolo.

lt72 commented 9 years ago

I am trying to verify this with the 'experts': "...I don't know if the received chain is enough to validate a server certificate....". I have the same question exactly. What I can say right now, is that .NET MF is as secure as OpenSSL is. OpenSSL is a tool we use, so we cannot be more secure than the tools we use. Please note that OpenSSL is a generally perceived as a secure stack, although we know from recent happenings that that is not always the case :-) Still, I would be very surprised if there was a security concern at such a high level of abstraction. This is literally the top level API we are talking about. We will get to the bottom of this, let's see what the 'experts' have to say. Another route we could try is to actually craft a service that tries to spoof another one and see where we get.

ppatierno commented 9 years ago

I agree with you on all ... this is way I always say "I hope I lost something, I hope I didn't understand how things work". My fault should be better than a big issue in OpenSSL. I hope to have from you some news from experts.

Thank you very much Lorenzo !

doingnz commented 9 years ago

The client needs to know something that was not received over the network. If all the "secrets" come across the network, then its open to be spoofed with something that looks real, but is not.

There must be some starting point from which to build trust. For a certificate based system, you need a root certificate that is trusted.

You can see this on Windows where there are several root certificates stored in the Certificate Store. These certificates are self-signed as there is no higher authority to validate their trustworthiness. You should only install a root certificate into your Certificate Store that you trust. Usually a user is prompted to check the hash/fingerprint for the certificate during the import process. Once its installed, then any certificate signed by that root key pair will be considered to be as trusted (within its validity period.)

There are tools on the .web for testing "Man-in-middle" network attacks. In essence a client requests a server certificate, the man-in-middle goes to the real host and fetches the real certificate. It promptly generates a new key hierarchy and new certificates containing the data and parameters in the original certificates. It passes the fake certificates to the client who thinks its talking to the end system server. Secure SSL or TSL sessions are established from the client to the man-in-the-middle and another from the man-in-middle to the server. All data is in the clear in the hands of the man-in-the-middle and can be viewed or modified at will.

To avoid this problem, the client must be able to know for sure it has the true servers certificate, and no in between impostor. If the client does not have a full copy of the true server certificate, it needs to store enough information to let it validate the received incoming certificates claiming to come from the true server.

Options with various levels of security:

  1. the public key of the root certificate of true server. This must be installed securely in the client and only modifiable via an authenticated process. i.e. its treated as if its a secret. Depending on certificate key, this data is about 128 ~ 256 bytes.
  2. the finger print of the root certificate of the true server. size of hash used to verify certificate. Its possible, but much more work to create a fake server certificate that has the same hash as the original server.
  3. the complete certificate of the the server..

I have not looked at OpenSSl and .Net, specifics. Note that the man-in-middle attack does not break the encryption. Each of the two links are secure and other parties cannot view or modify the data. The failure is that the client and server have not authenticated who they are actually communicating with and assume that the claims they receive over the network are not faked.

Hope this helps. Richard

ppatierno commented 9 years ago

Hi Richard thanks for your explanation that confirms my knowledge. The certificate chain received from the server isn't enough and the client needs to have some information locally to trust the server. These information could be the server certificate itself, its fingerprint (I didn't think about it) or the root CA certificate. No one of above information are available to the client based on Net MF so it's very strange that it's able to connect to the Azure Service Bus server. Net MF uses OpenSSL directly so it seems that the problem is there. It's very strange to me that OpenSSL has such big issue. Have we lost something ?

Paolo

lt72 commented 9 years ago

Also confirmation came from my side, so we are all on the same page. The next experiment would be to re-generate the OpenSSL libraries from the v1.0 distribution and make sure that all parameters have been properly set. It is possible that the libs were generated with looser checks than OpenSSL supports. Second experiment would be to try and update to the latest version. I will see to open a work item for the short term, although any help is very much appreciated.