SSL Debugging in WSO2 ESB

Secure Sockets Layer (SSL) is the most widely used protocol for implementing cryptography on the Web. SSL provides a secure enhancement to the standard TCP/IP sockets protocol used for Internet communications. The secure sockets layer is added between the transport layer and the application layer in the standard TCP/IP protocol stack. SSL is used to transferring sensitive information over a network in safe manner. [1]


We have encountered many situations where we need to check which SSL version(s) are being used/supported by our ESB.  Recently I involved in a production issue in which an older version of our ESB was vulnerable to the POODLE (Padding Oracle On Downgraded Legacy Encryption) attack[2][3] since it could NOT be configured to support newer SSL versions. Due to that limitation, it was supporting the default SSLv3 and TLSv1 versions where older SSLv3 protocol version is vulnerable to the POODLE attack. SSL is the precursor of the TLS protocol.


Communication using SSL begins with an exchange of information between the client and the server. This exchange of information is called the SSL handshake. The process of authenticating and establishing an encrypted channel between the client and the server is depicted below.






TLS Handshake [1]



The above process involves two protocols. The Handshake protocols manage this process of exchanging key generation material. Once hashing and encryption keys are ready for use, the Record Layer takes over. It uses the Record Protocol to secure application data, using the keys created in the Handshake process [4].


First let me drill down the Handshake protocol functionality.  The SSL messages are sent in the following order:
  1. Client hello - The client sends the server information including the highest version of SSL it supports and a list of the cipher suites it supports. (TLS 1.0 is indicated as SSL 3.1.) The cipher suite information includes cryptographic algorithms and key sizes.
  2. Server hello - The server chooses the highest version of SSL and the best cipher suite that both the client and server support and sends this information to the client.
  3. Certificate - The server sends the client a certificate or a certificate chain. A certificate chain typically begins with the server's public key certificate and ends with the certificate authority's root certificate. This message is optional, but is used whenever server authentication is required.
  4. Certificate request - If the server needs to authenticate the client, it sends the client a certificate request. In Internet applications, this message is rarely sent.
  5. Server key exchange - The server sends the client a server key exchange message when the public key information sent in 3) above is not sufficient for key exchange.
  6. Server hello done - The server tells the client that it is finished with its initial negotiation messages.
  7. Certificate - If the server requests a certificate from the client in Message 4, the client sends its certificate chain, just as the server did in Message 3.
  8. Note: Only a few Internet server applications ask for a certificate from the client.
  9. Client key exchange - The client generates information used to create a key to use for symmetric encryption. For RSA, the client then encrypts this key information with the server's public key and sends it to the server.
  10. Certificate verify - This message is sent when a client presents a certificate as above. Its purpose is to allow the server to complete the process of authenticating the client. When this message is used, the client sends information that it digitally signs using a cryptographic hash function. When the server decrypts this information with the client's public key, the server is able to authenticate the client.
  11. Change cipher spec - The client sends a message telling the server to change to encrypted mode.
  12. Finished - The client tells the server that it is ready for secure data communication to begin.
  13. Change cipher spec - The server sends a message telling the client to change to encrypted mode.
  14. Finished - The server tells the client that it is ready for secure data communication to begin. This is the end of the SSL handshake.
  15. Encrypted data - The client and the server communicate using the symmetric encryption algorithm and the cryptographic hash function negotiated in Messages 1 and 2, and using the secret key that the client sent to the server in Message 8.
  16. Close Messages - At the end of the connection, each side will send a close_notify message to inform the peer that the connection is closed.

For more details on this TLS handshake and record protocols please refer to the article [1], since it is NOT the intention of this article.


How POODLE vulnerability occurs
With the Client Hello message the client sends it’s highest SSL/TLS version. By default our ESB supports SSLv3 and TLSv1 protocol versions. Assume the client first sends TLSv1 which is the highest protocol version it supports. Then the man in the middle possibly an attacker may intercept this and sends a response saying SSLv3, and this leads the client confine to the SSLv3 version. This older/obsoleted SSLv3 version of the protocol is vulnerable to the POODLE [1], [2] attack. The above process is called as a downgrade dance [1].



You may use the TestSSLServer.jar[2] to check the SSL version used by your service/process given the port number on which it runs. For an example in WSO2 ESB NIOSSLListener/PassThroughHttpSSLListener runs on port 8243 by default and this jar file can be used to verify the SSL version it supports/uses. But in WSO2 ESB NIOSSLSender/PassThroughHttpSSLSender does not run on a port number and this method can NOT be used to verify the SSL version it uses/supports. That is where SSL debugging support in ESB comes into play. WSO2 ESB contains this support and you only need to start it with a specific option which will be mentioned later in this article.


The sample I am going to explain is depicted below.



Prerequisites:
JDK 1.6.*, Ant 1.9.*, WSO2 ESB 4.8.1


First let us start with the backend service. Take an instance of WSO2 ESB 4.8.1 version. Go to the \$ESB_HOME/samples/axis2Server/src/SimpleStockQuoteService and enter the ‘ant’ command to install and deploy the SimpleStockQuoteService to the backend axis2 server[5]. Then start the backend axis2 service and ESB instance using the commands given below.


To start the axis2Server move into the \$ESB_HOME/samples/axis2Server directory and enter ./axis2server.sh command.


Note: You may use export JAVA_HOME=/path/to/your/jdk to set the JAVA HOME environment variable only to this particular shell. Otherwise you may set it in the .bashrc file permanently which will be applicable for all the shells.


Go to the https://localhost:9002/services and verify that SimpleStockQuoteService is deployed successfully. The WSDL definition should be available at the https://localhost:9002/services/SimpleStockQuoteService?wsdl.


Then let’s get another ESB 4.8.1 instance and open the axis2.xml file resides under the \$ESB_HOME/repository/conf/axis2 directory. Add the <parameter name="HttpsProtocols">TLSv1</parameter> entry inside the PassThroughHttpSSLListener and PassThroughHttpSSLSender elements. Note that if you are using an older version of the ESB which does NOT support Passthrough transport you may need to add the above tag inside the HttpCoreNIOSSLListener and HttpCoreNIOSSLSender elements. Then start the ESB instance using the command given below.


./wso2server.sh -Djavax.net.debug=ssl:handshake -DapplyPatches


where [-DapplyPatches] is optional for newer ESB versions such as 4.8.1. Also if you need to  print all the SSL handshake sequences, you may use  -Djavax.net.debug=all jvm argument.


Then create a proxy service to send our classical Stock Quote requests to the above SimpleStockQuote backend service. A sample configuration is given below.





Let me use a CURL[6] client to send a POST request to the secure endpoint of the Proxy service created above with an appropriate payload. Alternatively you may use SOAP UI for this purpose instead of the command line curl client.


A sample SOAP request with the payload is given below.



Now we are done with the client end and about to put the beast into work.
Save the above SOAP request in your file system and name it as getQuote.xml. Then move in to the location where you saved the SOAP request file using the shell/terminal and enter the following curl command. This will send a getQuote POST request to the secure(https) endpoint of the proxy service created above.



curl --tlsv1.0 -k -v -H "Content-Type: application/soap+xml; charset=utf-8" -H "SOAPAction:urn:getQuote"  -d @getQuote.xml -X POST https://localhost:8243/services/TestProxy



Then monitor the ESB console for SSL debug logs related to the handshake sequence.


As mentioned above you may see the ClientHello, ServerHello and Cipher Exchange steps. Important parts of the log is attached below.


Using SSLEngineImpl.
https-Listener I/O dispatcher-1, READ: TLSv1 Handshake, length = 194
*** ClientHello, TLSv1
RandomCookie:  GMT: 320338548 bytes = { 132, 242, 210, 159, 198, 249, 141, 220, 27, 134, 185, 119, 181, 197, 200, 248, 53, 134, 119, 75, 174, 187, 88, 108, 84, 255, 78, 212 }
Session ID:  {}
Cipher Suites: [Unknown 0xc0:0x14, Unknown 0xc0:0xa, TLS_DHE_RSA_WITH_AES_256_CBC_SHA, TLS_DHE_DSS_WITH_AES_256_CBC_SHA, Unknown 0x0:0x88, Unknown 0x0:0x87, Unknown 0xc0:0xf, Unknown 0xc0:0x5, TLS_RSA_WITH_AES_256_CBC_SHA, Unknown 0x0:0x84, Unknown 0xc0:0x12, Unknown 0xc0:0x8, SSL_DHE_RSA_WITH_3DES_EDE_CBC_SHA, SSL_DHE_DSS_WITH_3DES_EDE_CBC_SHA, Unknown 0xc0:0xd, Unknown 0xc0:0x3, SSL_RSA_WITH_3DES_EDE_CBC_SHA, Unknown 0xc0:0x13, Unknown 0xc0:0x9, TLS_DHE_RSA_WITH_AES_128_CBC_SHA, TLS_DHE_DSS_WITH_AES_128_CBC_SHA, Unknown 0x0:0x9a, Unknown 0x0:0x99, Unknown 0x0:0x45, Unknown 0x0:0x44, Unknown 0xc0:0xe, Unknown 0xc0:0x4, TLS_RSA_WITH_AES_128_CBC_SHA, Unknown 0x0:0x96, Unknown 0x0:0x41, Unknown 0x0:0xff]
Compression Methods:  { 0 }

%% Created:  [Session-1, TLS_DHE_RSA_WITH_AES_256_CBC_SHA]
*** ServerHello, TLSv1
RandomCookie:  GMT: 1401699316 bytes = { 174, 9, 2, 123, 7, 141, 23, 255, 76, 128, 2, 139, 108, 129, 109, 184, 160, 237, 9, 14, 93, 28, 163, 96, 75, 114, 54, 92 }
Session ID:  {84, 140, 60, 244, 46, 139, 117, 109, 108, 164, 233, 121, 48, 145, 145, 31, 17, 193, 204, 95, 243, 85, 171, 65, 147, 72, 163, 212, 162, 30, 73, 200}
Cipher Suite: TLS_DHE_RSA_WITH_AES_256_CBC_SHA
Compression Method: 0
***
Cipher suite:  TLS_DHE_RSA_WITH_AES_256_CBC_SHA
*** Certificate chain

….


*** ServerHelloDone
*** ClientKeyExchange, RSA PreMasterSecret, TLSv1
Random Secret:  { 3, 1, 123, 96, 22, 15, 65, 214, 180, 220, 236, 212, 21, 5, 188, 184, 200, 148, 17, 209, 98, 207, 124, 206, 134, 53, 157, 61, 106, 218, 138, 19, 20, 59, 221, 17, 88, 251, 3, 109, 27, 97, 109, 28, 178, 224, 97, 98 }





https-Sender I/O dispatcher-1, WRITE: TLSv1 Application Data, length = 278
https-Sender I/O dispatcher-1, WRITE: TLSv1 Application Data, length = 320
https-Listener I/O dispatcher-1, WRITE: TLSv1 Application Data, length = 135
https-Listener I/O dispatcher-1, WRITE: TLSv1 Application Data, length = 1045
https-Listener I/O dispatcher-1, READ: TLSv1 Alert, length = 32
https-Listener I/O dispatcher-1, RECV TLSv1 ALERT:  warning, close_notify
https-Listener I/O dispatcher-1, closeInboundInternal()
https-Listener I/O dispatcher-1, closeOutboundInternal()
https-Listener I/O dispatcher-1, SEND TLSv1 ALERT:  warning, description = close_notify
https-Listener I/O dispatcher-1, WRITE: TLSv1 Alert, length = 32
https-Sender I/O dispatcher-1, called closeOutbound()
https-Sender I/O dispatcher-1, closeOutboundInternal()
https-Sender I/O dispatcher-1, SEND TLSv1 ALERT:  warning, description = close_notify
https-Sender I/O dispatcher-1, WRITE: TLSv1 Alert, length = 18





Note that https Listener and Sender is now using TLSv1 as configured above in axis2.xml transport sender and receiver elements.


Then force the curl to use SSLv3 protocol using the following command and monitor the ESB console logs again.



curl --sslv3 -k -v -H "Content-Type: application/soap+xml; charset=utf-8" -H "SOAPAction:urn:getQuote"  -d @getQuote.xml -X POST https://localhost:8243/services/TestProxy



You may get the following output.


Using SSLEngineImpl.
https-Listener I/O dispatcher-2, READ: SSLv3 Handshake, length = 105
*** ClientHello, SSLv3
RandomCookie:  GMT: 352097752 bytes = { 174, 37, 145, 129, 32, 24, 248, 100, 81, 251, 17, 160, 138, 24, 141, 35, 255, 200, 165, 226, 30, 217, 9, 120, 234, 115, 190, 177 }
Session ID:  {}
Cipher Suites: [Unknown 0xc0:0x14, Unknown 0xc0:0xa, TLS_DHE_RSA_WITH_AES_256_CBC_SHA, TLS_DHE_DSS_WITH_AES_256_CBC_SHA, Unknown 0x0:0x88, Unknown 0x0:0x87, Unknown 0xc0:0xf, Unknown 0xc0:0x5, TLS_RSA_WITH_AES_256_CBC_SHA, Unknown 0x0:0x84, Unknown 0xc0:0x12, Unknown 0xc0:0x8, SSL_DHE_RSA_WITH_3DES_EDE_CBC_SHA, SSL_DHE_DSS_WITH_3DES_EDE_CBC_SHA, Unknown 0xc0:0xd, Unknown 0xc0:0x3, SSL_RSA_WITH_3DES_EDE_CBC_SHA, Unknown 0xc0:0x13, Unknown 0xc0:0x9, TLS_DHE_RSA_WITH_AES_128_CBC_SHA, TLS_DHE_DSS_WITH_AES_128_CBC_SHA, Unknown 0x0:0x9a, Unknown 0x0:0x99, Unknown 0x0:0x45, Unknown 0x0:0x44, Unknown 0xc0:0xe, Unknown 0xc0:0x4, TLS_RSA_WITH_AES_128_CBC_SHA, Unknown 0x0:0x96, Unknown 0x0:0x41, Unknown 0x0:0xff]
Compression Methods:  { 0 }


***
https-Listener I/O dispatcher-2, fatal error: 40: Client requested protocol SSLv3 not enabled or not supported
javax.net.ssl.SSLHandshakeException: Client requested protocol SSLv3 not enabled or not supported
https-Listener I/O dispatcher-2, SEND TLSv1 ALERT:  fatal, description = handshake_failure
https-Listener I/O dispatcher-2, WRITE: TLSv1 Alert, length = 2
https-Listener I/O dispatcher-2, fatal: engine already closed.  Rethrowing javax.net.ssl.SSLHandshakeException: Client requested protocol SSLv3 not enabled or not supported
[2014-12-13 19:05:21,035] ERROR - ServerHandler I/O error: Client requested protocol SSLv3 not enabled or not supported
javax.net.ssl.SSLHandshakeException: Client requested protocol SSLv3 not enabled or not supported
at com.sun.net.ssl.internal.ssl.Handshaker.checkThrown(Handshaker.java:997)


...



How Can we get rid of POODLE vulnerability

With the above configuration set, WSO2 ESB does NOT support SSLv3 protocol version which is vulnerable to the POODLE attack. With this simple configuration you can force the ESB to support newer version(s) of TLS protocol in order to get rid of the POODLE attack. Now an attacker in the middle of the communication can NOT conduct a downgrade dance successfully since the ESB does NOT support vulnerable SSLv3 version anymore. Any such attempt will ended up with a handshake failure alert as shown above.



As an exercise next time you may change this configuration to the older SSLv3 version. For this shutdown the ESB instance in which you created the proxy service. Find the axis2.xml file and under transport receiver and sender elements mentioned above change the SSL protocol version to SSLv3 and restart the ESB as above. Use the curl client to send a request to the secured endpoint of the proxy service and analyse the console output.


You can configure the ESB to support multiple TLS versions using a comma separated list as shown below. Also take a note that only JDK 1.7 supports this kind of configuration.








References


Comments

Popular posts from this blog

Introducing Java Reactive Extentions in to a SpringBoot Micro Service

Optimal binary search trees

Combining the emissions of multiple Observables together using RxJava Zip operator in a Spring Boot Micro service