Skip to main content
Sandro Gauci

Sandro Gauci, Enable Security

Exploiting CVE-2022-0778, a bug in OpenSSL vis-à-vis WebRTC platforms

Executive summary (TL;DR)

Exploiting CVE-2022-0778 in a WebRTC context requires that you get a few things right first. But once that is sorted, DoS (in RTC) is the new RCE!

How I got social engineered into looking at CVE-2022-0778

A few days ago, Philipp Hancke, self-proclaimed purveyor of the dark side of WebRTC, messaged me privately with a very simple question: “are you offering a DTLS scanner by chance?”

He explained how in the context of WebRTC it would be a bit difficult since you need to get signaling right, ICE (that dance with STUN and other funny things) and finally, you get to do your DTLS scans. He added that he hopes that these difficulties raise the bar for exploiting latest OpenSSL CVE.

The OpenSSL advisory issued on 15th March explains how the vulnerability that was fixed has to do with abuse of the BN_mod_sqrt() function that causes it to loop forever for non-prime moduli.

Although we have done some interesting things with DTLS (e.g. see Slack’s DTLS certificate reuse), we never actually developed this into our toolset. The main reason for this is that, as he said, it gets pretty complicated to do it the right way. So, although SIPVicious PRO does support DTLS-SRTP, it does not yet do any interesting DTLS specific security tests.

A quick search on Github revealed that proof-of-concept exploit code for this particular vulnerability, CVE-2022-0778, was actually readily available. The researchers not only published scripts to create the malicious certificate, but also dropped an example malicious certificate so that all we have to do to reproduce the issue is to run the following:

openssl x509 -in certs/cert.der.new -inform DER -text -noout

Execute the above on a system with a slightly outdated version of OpenSSL, and you’ll notice that openssl never returns and gets busy doing CPU cycles until you kill it.

And so, back to our story, I told Philipp that some very kind people did indeed produce a proof of concept and put it out there. His response was that:

the good news (is) that at least it isn’t as simple as “dump a udp packet” since the client certificate is only sent after the server hello

I slept over it, but the next day I was up pretty early and by 6am, grinning, I sent my good friend the following:

just reproduced it against rtpengine on our demo server

Can’t be that bad, it is just a denial of service!

The majority of denial of service vulnerabilities are not considered a big deal. You might have an HTTP server that is serving a web application. If the server happens to be parsing client certificates on TLS, if attacked with the CVE-2022-0778 vulnerability, you just restart the server since its no longer responding. Naturally there are various cases where this could be a problem but they tend to be fringe or case-specific since mutual authentication on TLS is not that often used for most web serving. If a website is unavailable for a period of time, most visitors can come back later when it is back up. Maybe this explains why on CVSS 2.0 this vulnerability is given a rating of 5 (medium), while on CVSS 3.x it gets a 7.5 (high).

However, the severity in my opinion is quite different in the case of real-time communications. The whole point of VoIP and WebRTC platforms is that they allow real-time communications. When no longer available, RTC looses its whole purpose and therefore its actual value. Disconnected calls need to be restarted and conversations stall. Those video conference calls that needed to happen in real-time can no longer take place. And service providers that charge by the second (such as telcos or CPaaS), loose money every second their service is unavailable.

This is why we think that this vulnerability and similar ones, in the context of WebRTC, should be considered critical.

Watch and marvel

Therefore, after reproducing the issue, I realized that I needed to record what I did to highlight the severity. Here’s the video we uploaded on Youtube:

Quick transcription:

Hi, welcome to a demonstration of the latest OpenSSL vulnerability, the CVE-2022-0778, as it might affect a WebRTC infrastructure. This vulnerability is well documented and involves certificate parsing. It is a very interesting vulnerability, I am not really going into the details but I just wanted to show how it might affect a WebRTC infrastructure where the DTLS has to be parsed to check the certificate validation.

If we go to our demo server, we can show an example WebRTC infrastructure and we can make calls. The system involves Kamailio for signalling and rtpengine for handling the media, which is what we’re interested in here. To be able to decrypt SRTP, rtpengine needs to first change DTLS certificates with my web browser and therefore it needs to parse DTLS certificates using the OpenSSL library.

So I’m going to just show that I can make a call here by calling extension 1200. Once that happens, you can hear the echo application being used which is on Asterisk behind the Kamailio server. This (the media) is passing through rtpengine and the SRTP is being decrypted by rtpengine. We now know that the system works.

Next I’m switching to the console. In the upper hand we have log files from our demo server which are being generated and therefore we can monitor the system. On the lower part, I have the demo prepared. I am going to first clone a git repository with an exploit for this vulnerability, making it very easy for us (to demonstrate this issue). In the certs directory there are a couple of certificates but we’re going to use the cert.der.new which is the certificate that reproduces this security issue. If I use SIPVicious PRO sip utils call tool to setup a call, just like we did from the web browser before. And we’re going to call extension 1200 again with the correct credentials, and specify SRTP-DTLS. We’re going to setup the media to use SRTP and we’re going to use DTLS for the key exchange for SRTP. If we do that from the certs directory, our hacked version of SIPVicious PRO, which has been specially created for this demo, is going to use the certs.der.new instead of the normally generated self-signed certificate.

On the upper part we can see that Kamailio has lost connection to rtpengine, meaning that rtpengine is no longer working. If we switch to htop we can see that rtpengine is running at 100% CPU. If we go to the web browser again, and try to start another call we can see that no call goes through this time.

This is a demo showing rtpengine which is using a vulnerable version of OpenSSL. This vulnerability in a WebRTC context will probably affect other systems that use OpenSSL to decode DTLS certificates. So, if you’re a system administrator, developer etc who takes care of such systems, make sure to update your OpenSSL library!

Thank you for watching.

How did we do that?

The video makes it look rather easy to abuse this vulnerability and it certainly is when you have a tool that automates everything for you. But reproducing this issue in a WebRTC context, in most cases, cannot be done by writing a few lines of bash or python. This is because, as my friend Philipp rightfully point out, there are a number of prerequisites that need to be satisfied before one gets to send the malicious certificate over DTLS.

In WebRTC, signalling is usually done in the web browser. It is not specified in an RFC, unlike VoIP protocols such as SIP. As a result, each platform may (and often does!) use its own signalling protocol. This means that writing one tool to reproduce this issue for all WebRTC platforms, is a hard problem. The easiest way to do that at the moment appears to be to (ab)use a web browser. We did not go that route for this demo.

Once you have signalling figured out, you need to do ICE to figure out how to send and receive SRTP. Once that is working, DTLS handshakes can occur where the client, server hello packets are exchanged and then the certificates are sent by the DTLS client and the server. This is the stage when the vulnerability could be exploited.

So in the demo video I made use of SIPVicious PRO and the target server was running a WebRTC environment built on top of Kamailio and rtpengine. The signalling protocol used here is actually SIP which is handled by Kamailio’s built-in websocket server, while rtpengine is able to handle DTLS-SRTP thus proxying the media. The reason why I made use of this setup is that SIPVicious PRO already knows how to initiate calls using SIP over websocket, then do ICE and finally start sending and receiving media using DTLS-SRTP.

At this point, all I had to do is to make sure that we can send the malformed certificate over DTLS. So my first attempt looked like the following:

sipvicious sip utils call udp://demo.sipvicious.pro:5060 -u 1000:1500 -e 1200 --srtp dtls:cert.der.new:ec.key
ERRO[2022-04-07 07:54:16] could not decode public key                  
INFO[2022-04-07 07:54:16] test complete

We’re using the standard Go pkcs7 library to decode x509 certificates which has a check for malformed certificates and therefore failed to give me what I wanted. After a wholesome double espresso, I realized that I needed to somehow convince SIPVicious PRO to load this certificate but not necessarily parse it.

We use the pion DTLS library to do our WebRTC magic. To send these malformed certificates I hacked this library by adding a few lines to send certificates loaded from local disk. This overrides the default behavior of generating a self-signed certificate or loading a well-formed certificate and key from disk.

Here’s how that change looked like for flight4handler.go:

 	case state.cipherSuite.AuthenticationType() == CipherSuiteAuthenticationTypeCertificate:
		...
+		certBytes := certificate.Certificate
+		if _, err := os.Stat("cert.der.new"); err == nil {
+			fmt.Println("Found cert.der.new - using that instead of generated self-signed cert")
+			b, _ := ioutil.ReadFile("cert.der.new")
+			certBytes = [][]byte{b}
+		}
 		pkts = append(pkts, &packet{
 			record: &recordlayer.RecordLayer{
 				Header: recordlayer.Header{
@@ -274,7 +282,7 @@ func flight4Generate(c flightConn, state *State, cache *handshakeCache, cfg *han
 				},
 				Content: &handshake.Handshake{
 					Message: &handshake.MessageCertificate{
-						Certificate: certificate.Certificate,
+						Certificate: certBytes,
 					},
 				},
 			},

Similar changes may need to be done for flight5handler.go.

If the dear reader wants to reproduce this issue on their own, on a platform that uses any other signalling protocol, then they’ll need to implement the basics of that protocol to get a call working first. Then, one could plug in their hacked version of their favourite DTLS library of choice and off to the races! Or simply convince a web browser to load such certificates of course.

Which other WebRTC platforms are affected?

This issue is likely to affect a large number of platforms simply because OpenSSL is so ubiquitous. On the opensource front, we see the following relying on OpenSSL:

  • Janus
  • Mediasoup
  • Kurento
  • Asterisk
  • FreeSWITCH

Naturally, since I did not actually test any of the above (yet!) against any CVE-2022-0778 exploit code, I cannot claim that they’ll be vulnerable. But it does seem like a fair assumption. Another caveat is that Janus, FreeSWITCH, PJSIP (for Asterisk) and libSRTP may be linked to BoringSSL (and possibly others) instead of OpenSSL, which is typically not vulnerable.

Some people will invariably ask - and what about web browsers? Google’s Chrome uses BoringSSL while Firefox uses NSS neither of which are listed as vulnerable to this particular issue.

If anyone knows more about this please do correct me.

How do you address this?

Since we’re using Docker to run our demo server, to fix this we just had to rebuild rtpengine. In the process, we also upgraded it to the latest version of the software.

The following command did the job:

docker-compose build --no-cache rtpengine

This pulled down the latest Ubuntu fixes for OpenSSL library and built fresh rtpengine binaries linking to the fixed version. Naturally I tested before and after to validate the fix. After the fix, I could see the following lines in the logs, as expected:

WARNING: [qDbycCNfhUfn2Y4J port 36580]: [crypto] DTLS: Peer certificate rejected - fingerprint mismatch
ERR: [qDbycCNfhUfn2Y4J port 36580]: [crypto] DTLS error: 1 (certificate verify failed)
ERR: [qDbycCNfhUfn2Y4J port 36580]: [srtp] DTLS error on local port 36580

So - to address this vulnerability, do as the OpenSSL advisory says:

OpenSSL 1.0.2 users should upgrade to 1.0.2zd (premium support customers only)

OpenSSL 1.1.1 users should upgrade to 1.1.1n

OpenSSL 3.0 users should upgrade to 3.0.2

Our newsletter

Credits and further reading


Sandro Gauci

Sandro Gauci

CEO, Chief Mischief Officer at Enable Security

Sandro Gauci leads the operations and research at Enable Security. He is the original developer of SIPVicious OSS, the SIP security testing toolset. His role is to focus on the vision of the company, design offensive security tools and engage in security research and testing. Therefore, he is the proud owner of the title of Chief Mischief Officer at Enable Security.

He offers public office hours and is reachable here.