This year I gave a speech at SuriCon - the annual Suricata users’ conference - about how Suricata can protect VoIP networks.
More precisely, the speech was centered on the SIP parser I wrote in Rust and has been introduced in Suricata 5.0
Let’s start from the basic and discover what we can do.
The SIP (Session Initial Protocol) is a protocol defined by IETF in RFC3261 and is the main actor in VoIP networks.
Its role is to setup a multimedia session, a call, between two endpoints but then it has no idea of what’s going on over the connection.
Since SIP can’t carry on all the job by itself, it works along with other protocols such as:
Real-Time Protocol (RTP): a specialized application layer for transporting audio and video when real-time streaming is needed
RTP control protocol (RTCP): provide information about RTP packet delivery and manage the quality of voice service
Session Description Protocol (SDP): specify which types of media the SIP clients in the session can actually support
The picture below shows the set-up of a call between two endpoints:
Alice's . . . . . . . . . . . . . . . . . . . . Bob's
As we can see, this protocol is based around request/response transactions, where each transaction is made up of a request and at least one response, and some methods and status code are defined.
Looking at Figure 2, we can notice that SIP messages are similar to HTTP, in fact most of the header fields, encoding rules and status code are reused and the URI syntax is similar to SMTP.
INVITE sip:firstname.lastname@example.org SIP/2.0
I won’t go further with the details of the protocol to not make this post really long but let’s take a look at the security issues related with it.
The main issue of SIP is that it’s not designed to be secure, the protocol itself does not require any security and although security is mentioned SIP RFC, it’s not neither required nor mandatory.
Being a text-based protocol plays a critical role in security, because messages are sent in clear and an attacker can easily collect, modify or replay them.
Also, since integrity is not supported, these attacks not detected.
This is even caused by the lack of authentication because is not required, and if present, it’s weak.
This protocol characteristic leads voip networks to be vulnerable to some attacks, and to be able to detect them we must know which they are.
Let’s list some of them:
- Denial Of Services: flooding of INVITE or REGISTER messages
- Extensions enumeration: identify live SIP extensions (brute forcing)
- Eavesdropping: listening to a conversation without the consent
- Message tampering: intercepts and modifies SIP messages
- Session tear-down: BYE messages are sent to the participants
Now we understood why voip networks are vulnerable which are the attacks they are exposed to, we can talk about countermeasures.
Like I did for the attacks, this below is a list of countermeasure that can be employed:
- Encryption with SIPS and SRTP, implementation of SIP/RTP over TLS
- Provide strong authentication between SIP components
- Monitor SIP messages (extensions scanning, malformed messages, malicious teardown, …)
- Monitor unusual calling patterns
We covered a good theoretical part about SIP, let’s get concrete now and start with the practice.
As I mentioned at the start of this post, I wrote a parser in Rust for Suricata to detect SIP traffic on port 5060 for UDP transport protocol.
At this moment, basic logging is provided also. Request and response messages are logged except for headers, which are parsed but not logged (yet).
In general, application layers logging in Suricata permits to turn on/off an extended or custom logging, which means that you can have more fields in your events or choice which fields to log.
Along with parsing and logging features, I implemented new sticky buffers, keywords that can be used with rules, to inspect on SIP traffic.
In this section I’m going to shortly describe the new sticky buffers introduced:
sip.method: matches on the method found in a SIP request
alert sip any any -> any any (sip.method; content:"INVITE"; sid:1;)
sip.uri: matches on the URI found in a SIP request
alert sip any any -> any any (sip.uri; content:"sip:yoursipserver.net"; sid:1;)
sip.request_line: forces the whole SIP request line to be inspected
alert sip any any -> any any (sip.request_line; content:"REGISTER sip:yoursipserver.net SIP/2.0"; sid:1;)
sip.response_line: forces the whole SIP response line to be inspected
alert sip any any -> any any (sip.response_line; content:"SIP/2.0 200 OK"; sid:1;)
sip.stat_code: matches on the status code found in a SIP response
alert sip any any -> any any (sip.stat_code; content:"200"; sid:1;)
sip.stat_msg: matches on the status message found in a SIP response
alert sip any any -> any any (sip.stat_msg; content:"OK"; sid:1;)
sip.protocol: matches on the protocol field from a SIP request or response
alert sip any any -> any any (sip.protocol; content:"SIP/2.0"; sid:1;)
There are two common type of DoS attacks for VoIP networks:
INVITE and REGISTER flooding.
An excess of INVITE messages could be an indication that someone is trying to make too many calls and the rule below will fire if 100 INVITE messages are sent in a short period of time, 60 seconds.
alert sip any any -> $SIP_IP $SIP_PORTS (msg:"INVITE flooding"; sip.method; content:"INVITE"; threshold: type both, track by_src, count 100, seconds 60; rev:1; sid:1;)
Instead an high number of REGISTER flooding can be an indication of brute-forcing attack.
Similarly, the rule above can be used but with a slight change at the method:
alert sip any any -> $SIP_IP $SIP_PORTS (msg:"REGISTER flooding"; sip.method; content:"REGISTER"; threshold: type both, track by_src, count 100, seconds 60; rev:1; sid:1;)
There is another way to detect REGISTER flooding, we can inspect the SIP response and look for 401 status code, which is the status code used when a user tries to make a connection with wrong credentials.
alert sip any any -> $SIP_IP $SIP_PORTS (msg:"REGISTER brute-force"; sip.stat_code; content:"401"; threshold: type both, track by_src, count 100, seconds 60; rev:1; sid:1;)
Although Suricata is able to detect SIP traffic, there is more work that could be done.
For instance, the parser can be extended to detect SIP traffic over TCP.
I already sent a pull request for this.
Logging part can be improved with extended/custom logging and adding headers in the log, and maybe some other sticky buffers can be introduced.
I hope you find it interesting, if you have any feedback or ideas about this work do not hesitate to contact me. :)