User Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/83.0.4103.61 Safari/537.36
Steps to reproduce:
Step 1. Checkout the latest version of NSS code and build it. Revision ID: 661e3e3f6ba515a36fc97038164979a216c9f87b
Step 2. Run ssl_gtests.sh test to create the data selfserv tool needed.
HOST=localhost DOMSUF=localdomain USE_64=1 ./nss/tests/ssl_gtests/ssl_gtests.sh
Step 3. Run slefserv in TLS 1.3 mode.
LD_LIBRARY_PATH="$NSS_DIR/lib" "$NSS_DIR/bin/selfserv" -n rsa -p 4433 -d ~/nss-dev/tests_results/security/localhost.1/ssl_gtests/ -v -V tls1.3:tls1.3
Step 4. Config environment for PoC (named ccs_dos_poc.py in the attachments).
Python version: 3.8
Package requirements: tlslite-ng==0.8.0-alpha37
Change the host and port variables in the PoC code according to your environment.
Step 5. Run PoC code.
On server side, you will see the CPU usage of NSS server process reaches 100% immediately and keeps. However, the CPU usage of the PoC process on the client side is very low.
It is a remote server-side DoS (Denial of Service) issue. An unauthorized attacker can make a DoS attack to a NSS TLS 1.3 server remotely in a very high efficiency.
NSS server should handle CCS message in TLS 1.3 more carefully to prevent this kind of attack. I will explain this issue as detailed as possible.
Section 1. Issue Analysis
In TLS 1.3, CCS message is used only for compatibility purposes:
NSS did followed the RFC 8446, but it's a relatively loose state machine check and there are no other limits. It allows an attacker to send CCS messages in a row after ClientHello message. If an attacker put multiple CCS messages in a single tcp packet, the NSS server will stuck in a loop for many times to process the messages. The relevant code is in the ssl3_HandleRecord() function of /nss/lib/ssl/ss3con.c:
Normally, if an attacker need to keep sending packets for a remote DoS attack, it's not considered as a security vulnerability because the processing power requirement for client and server is basically the same. But in this issue, the server requires much more processing power than the client because an attacker can put multiple CCS messages in a single TCP packet. It's like a CCS message bomb. The server needs to loop for thousands of times to process a single TCP packet while the client only needs to do a raw socket sending.
A very good example of this kind of vulnerability is the SSL Renegotiation DoS problem (CVE-2011-1473, CVE-2011-5094) in which the processing power difference is only about 10 times. The issue here is more serious from the processing power difference perspective. Here's some information about the SSL Renegotiation DoS problem:
Section 2. Fix suggestion
A good example of how to handle CCS message properly in TLS 1.3 is the OpenSSL implementation. In OpenSSL, they limit the max consecutive CCS message. The way OpenSSL did this is a little bit complicated. First, OpenSSL treated CCS message in TLS 1.3 as empty record and get empty record count. Then, the empty record count is compared to MAX_EMPTY_RECORDS constant which is 32. In the code comment, they also explained the reason why the did this is to prevent similar attack we talk about here: "MAX_EMPTY_RECORDS defines the number of consecutive, empty records that will be processed per call to ssl3_get_record. Without this limit an attacker could send empty records at a faster rate than we can process and cause ssl3_get_record to loop forever."
The key point to address this issue is to limit the consecutive CCS messages numbers.
Section 3. Conclusion
The key point of the issue is the disparity processing power requiring on server and client which makes the remote DoS attack possible. Comparison with the SSL Renegotiation DoS problem and the OpenSSL implementation may help you to see this issue clearly. Hope this issue will be fixed in NSS.