We spend our time searching for security and hate it when we get it.
John Steinbeck
Security for your Asterisk system is critical, especially if the system is exposed to the internet. There is a lot of money to be made by attackers in exploiting systems to make free phone calls. This chapter provides advice on how to provide stronger security for your VoIP deployment.
If you expose your Asterisk system to the public internet, one of the things you will almost certainly see is a scan for valid accounts. Example 22-1 contains log entries from one of the authors’ production Asterisk systems.1 This scan began with checking various common usernames, then later went on to scan for numbered accounts. It is common for people to name SIP accounts the same as extensions on the PBX. This scan takes advantage of that fact.
Use non-numeric usernames for your VoIP accounts to make them harder to guess. For example, in this book we use the MAC address of a SIP phone as its account name in Asterisk.
[Aug 22 15:17:15] NOTICE[25690] chan_sip.c: Registration from '"123"<sip:[email protected]>' failed for '203.86.167.220:5061' - No matching peer found [Aug 22 15:17:15] NOTICE[25690] chan_sip.c: Registration from '"1234"<sip:[email protected]>' failed for '203.86.167.220:5061' - No matching peer found [Aug 22 15:17:15] NOTICE[25690] chan_sip.c: Registration from '"12345"<sip:[email protected]>' failed for '203.86.167.220:5061' - No matching peer found ... [Aug 22 15:17:17] NOTICE[25690] chan_sip.c: Registration from '"100"<sip:[email protected]>' failed for '203.86.167.220:5061' - No matching peer found [Aug 22 15:17:17] NOTICE[25690] chan_sip.c: Registration from '"101"<sip:[email protected]>' failed for '203.86.167.220:5061' - No matching peer found
The logs on any system will be full of intrusion attempts. This is simply the nature of connecting systems to the internet. In this chapter, we will discuss some of the ways to configure your system so that it will have robust mechanisms to deal with these things.
The first section of this chapter discussed scanning for usernames. Even if you have usernames that are difficult to guess, it is critical that you have strong passwords as well. If an attacker is able to obtain a valid username, they will likely attempt to brute-force the password. Strong passwords make this much more difficult.
The default authentication scheme of the SIP protocol is weak. Authentication is done using an MD5 challenge-and-response mechanism. If an attacker is able to capture any call traffic, such as a SIP call made from a laptop on an open wireless network, it will be much easier to work on brute-forcing the password, since it will not require authentication requests to the server.
The previous two sections discussed attacks involving scanning for valid usernames and brute-forcing passwords. Fail2ban is an application that can watch your Asterisk logs and update firewall rules to block the source of an attack in response to too many failed authentication attempts.
Use Fail2ban when exposing Voice over IP services on untrusted networks. It will automatically update the firewall rules to block the sources of attacks.
Fail2ban is available as a package in many distributions. Alternatively, you can install it from source by downloading it from the Fail2ban website. To install Fail2ban on RHEL, you must have the EPEL repository enabled (which was handled during Chapter 3). You can install Fail2ban by running the following command:
$
sudo yum install fail2ban
The installation of Fail2ban from a package will include a startup script to ensure that it runs when the machine boots up. If you install from source, make sure that you take the necessary steps to ensure that Fail2ban is always running.
First up, we’ll want to configure the security log in Asterisk, which Fail2ban is able to make use of.
$ sudo vim /etc/asterisk/logger.conf
Uncomment the (or add a) line that reads security => security
, and edit the dateformat
so Fail2ban understands the
logfile.
[general] exec_after_rotate=gzip -9 ${filename}.2; dateformat = %F %T [logfiles] ;debug => debug security => security ;console => notice,warning,error,verbose console => notice,warning,error,debug messages => notice,warning,error full => notice,warning,error,debug,verbose,dtmf,fax
Then reload the Asterisk logger:
$ sudo asterisk -rx 'logger reload'
Since current versions of Fail2ban already come with an Asterisk jail definition, all we need to do is enable it:
The current best practice is to create a file /etc/fail2ban/jail.local for this purpose (technically you can put it in /etc/fail2ban/jail.conf, but this is more likely to be overwritten):
$ sudo vim /etc/fail2ban/jail.local [asterisk] enabled = true filter = asterisk action = iptables-allports[name=ASTERISK, protocol=all] sendmail[name=ASTERISK, [email protected], [email protected]] logpath = /var/log/asterisk/messages /var/log/asterisk/security maxretry = 5 findtime = 21600 bantime = 86400
We’ve set up the ban for 24 hours, but you can do longer or
shorter times as well if you prefer (the bantime
is defined in seconds, so calculate
accordingly). Since most attacking hosts move on after a few hours,
there’s no harm in unblocking an IP after 24 hours. If the host attacks
again, they’ll be blocked again.
Oh, you might also want to tell it to ignore your IP (or any other IP addresses that are OK to receive connection attempts from). If you haven’t yet accidentally gotten yourself blocked because you were doing some lab work and misregistering, don’t worry, you will eventually do this to yourself (unless, of course, you create an ignore list for appropriate IPs).
[DEFAULT] ignoreip = <ip address(es), separated by commas> [asterisk] enabled = true filter = asterisk action = iptables-allports[name=ASTERISK, protocol=all] sendmail[name=ASTERISK, [email protected], [email protected]] logpath = /var/log/asterisk/messages /var/log/asterisk/security maxretry = 5 findtime = 21600 bantime = 86400
Restart Fail2ban and you’re good to go.
$ sudo systemctl reload fail2ban
Test it out if you can, from an IP address you don’t mind being
blocked (for example, an extra computer in your lab that can be the test
subject for this). Attempt to register using bad credentials, and after
five attempts (or whatever you set maxretry
to), that IP should be
blocked.
You can see what addresses the Asterisk jail is blocking with the command:
$ sudo fail2ban-client status asterisk
And if you want to unblock an IP,2 the following command should do so.
$ sudo fail2ban-client set asterisk unbanip ip to unban
More information about Fail2ban can be found at the Fail2ban wiki.
The Asterisk dialplan is another area where taking security into consideration is critical. The dialplan can be broken down into multiple contexts to provide access control to extensions. For example, you may want to allow your office phones to make calls out through your service provider. However, you do not want to allow anonymous callers that come into your main company menu to be able to then dial out through your service provider. Use contexts to ensure that only the callers you intend have access to services that cost you money.
Build dialplan contexts with great care. Also, avoid putting any
extensions that could cost you money in the [default]
context.
One of the more recent Asterisk dialplan vulnerabilities to have been discovered and published is the idea of dialplan injection. A dialplan injection vulnerability begins with an extension that has a pattern that ends with the match-all character, a period. Take this extension as an example:
exten => _X.,1,Dial(PJSIP/otherserver/${EXTEN},30)
The pattern for this extension matches all
extensions (of any length) that begin with a digit. Patterns like this are
pretty common and convenient. The extension then sends this call over to
another server using the IAX2 protocol, with a dial timeout of 30 seconds.
Note the usage of the ${EXTEN}
variable
here. That’s where the vulnerability exists.
In the world of Voice over IP, there is no reason that a dialed extension must be numeric. In fact, it is quite common using SIP to be able to dial someone by name. Since it is possible for non-numeric characters to be a part of a dialed extension, what would happen if someone sent a call to this extension?
1234&DAHDI/g1/12565551212
A call like this is an attempt at
exploiting a dialplan injection vulnerability. In the previous extension
definition, once ${EXTEN}
has been
evaluated, the actual Dial()
statement
that will be executed is:
exten => _X.,1,Dial(PJSIP/otherserver/1234&DAHDI/g1/12565551212,30)
If the system has a PRI configured, this call will cause a call to go out on the PRI to a number chosen by the attacker, even though you did not explicitly grant access to the PRI to that caller. This problem can quickly cost you a whole lot of money.
There are several approaches to avoiding this problem. The first and easiest approach is to always use strict pattern matching. If you know the length of extensions you are expecting and expect only numeric extensions, use a strict numeric pattern match. For example, this would work if you are expecting four-digit numeric extensions only:
exten => _XXXX,1,Dial(PJSIP/otherserver/${EXTEN},30)
Another approach to mitigating dialplan
injection vulnerabilities is by using the FILTER()
dialplan function. Perhaps you would like to
allow numeric extensions of any length. FILTER()
makes that easy to achieve
safely:
exten => _X.,1,Set(SAFE_EXTEN=${FILTER(0-9A-F,${EXTEN})}) same => n,Dial(PJSIP/otherserver/${SAFE_EXTEN},30)
For more information about the syntax for the FILTER()
dialplan function, see the output of
the core show function FILTER
command at the Asterisk
CLI.
A more comprehensive (but also complex) approach might be to have all dialed digits validated by functions outside of your dialplan (for example, database queries that validate the dialed string against user permissions, routing patterns, restriction tables, and so forth). This is a powerful concept, but beyond the scope of this book.
Be wary of dialplan injection vulnerabilities. Use strict pattern
matching or use the FILTER()
dialplan
function to avoid these problems.
To secure AGI, AMI, and ARI, you will need to carefully consider the following recommended practices:
Only allow connections directly to the API from
localhost/127.0.0.1
.
Use an appropriate framework in between the Asterisk API and your client application, and handle connection security through the framework.
Control access to the framework and the system through strict firewall rules.
Beyond that, the same sort of security rules and best practices apply that you would follow in any mission-critical web application.
There are other useful features in
Asterisk that can be used to
mitigate the risk of attacks. The first is to use the permit
and deny
options to build access control lists (ACLs) for privileged accounts. Consider a PBX that has SIP
phones on a local network, but also accepts SIP calls from the public
internet. Calls coming in over the internet are only granted access to the
main company menu, while local SIP phones have the ability to make
outbound calls that cost you money. In this case, it is a very good idea
to set ACLs to ensure that only devices on your local network can use the
accounts for the phones.
In your ps_endpoints
table, the permit
and deny
options allow you to specify IP addresses,
but you can also point to a label in the /etc/asterisk/acl.conf file. In fact, ACLs are
accepted almost everywhere that connections to IP services are configured.
For example, another useful place for ACLs is in /etc/asterisk/manager.conf, to restrict AMI
accounts to the single host that is supposed to be using the manager
interface.
ACLs can be defined in /etc/asterisk/acl.conf.
[named_acl_1] deny=0.0.0.0/0.0.0.0 permit=10.1.1.50 permit=10.1.1.55 [named_acl_2] ; Named ACLs support IPv6, as well. deny=:: permit=::1/128 [local_phones] deny=0.0.0.0/0.0.0.0 permit=192.168.0.0/255.255.0.0
Once named ACLs have been defined in acl.conf, have Asterisk load them using the
reload acl
command. Once loaded, they should be
available via the Asterisk CLI:
*CLI>
module reload acl
*CLI>
acl show
acl --- named_acl_1 named_acl_2 local_phones*CLI>
acl show named_acl_1
ACL: named_acl_1 --------------------------------------------- 0: deny - 0.0.0.0/0.0.0.0 1: allow - 10.1.1.50/255.255.255.255 2: allow - 10.1.1.55/255.255.255.255
Now, instead of having to potentially repeat the
same permit
and deny
entries in multiple places, you can apply
an ACL by its name. You will find an acl
field in the ps_endpoints
table, which you can use to point
to a named ACL in the acl.conf file.
mysql> select id,transport,aors,context,disallow,allow,acl from ps_endpoints; |id |transport |aors |context|disallow|allow |acl | |0000f30A0A01|transport-udp|0000f30A0A01|sets |all |ulaw |NULL| |0000f30B0B02|transport-udp|0000f30B0B02|sets |all |ulaw |NULL| |SOFTPHONE_A |transport-udp|SOFTPHONE_A |sets |all |ulaw,h264,vp8|NULL| |SOFTPHONE_B |transport-udp|SOFTPHONE_B |sets |all |ulaw,h264,vp8|NULL| mysql> update ps_endpoints set acl='local_phones' where id in ('0000f30A0A01','0000f30B0B02','SOFTPHONE_A','SOFTPHONE_B') ; mysql> select id,transport,aors,context,disallow,allow,acl from ps_endpoints; |id |transport |aors |context|disallow|allow |acl | |0000f30A0A01|transport-udp|0000f30A0A01|sets |all |ulaw |local_phones| |0000f30B0B02|transport-udp|0000f30B0B02|sets |all |ulaw |local_phones| |SOFTPHONE_A |transport-udp|SOFTPHONE_A |sets |all |ulaw,h264,vp8|local_phones| |SOFTPHONE_B |transport-udp|SOFTPHONE_B |sets |all |ulaw,h264,vp8|local_phones|
Use ACLs when possible on all privileged accounts for network services.
Another way you can mitigate security risk is
by configuring call limits. The recommended method for implementing call limits is to
use the GROUP()
and GROUP_COUNT()
dialplan functions. Here is an example that
limits the number of calls from each SIP peer to no more than two at a
time:
exten => _X.,1,Set(GROUP(users)=${CHANNEL(endpoint)}) same => n,NoOp(${CHANNEL(endpoint)} : ${GROUP_COUNT(${CHANNEL(endpoint)})} calls) same => n,GotoIf($[${GROUP_COUNT(${CHANNEL(endpoint)})} > 2]?denied:continue) same => n(denied),NoOp(There are too many calls up already. Hang up.) same => n,HangUp() same => n(continue),NoOp(continue processing call as normal here ...)
Use call limits to ensure that if an account is compromised, it cannot be used to make hundreds of phone calls at a time.
Some security vulnerabilities require modifications to the Asterisk source code to resolve. When those issues are discovered, the Asterisk development team puts out new releases that contain only fixes for the security issues, to allow for quick and easy upgrades. When this occurs, the Asterisk development team also publishes a security advisory document that discusses the details of the vulnerability. We recommend that you subscribe to the asterisk-announce mailing list to make sure that you know about these issues when they come up.
Subscribe to the asterisk-announce list to stay up to date on Asterisk security vulnerabilities.
One of the most popular tools for SIP account scanning and password cracking is SIPVicious. We strongly encourage that you take a look at it and use it to audit your own systems. If your system is exposed to the internet, others will likely run SIPVicious against it, so make sure that you do that first.
There is a maxim in the technology industry that states, “As soon as something is made idiot-proof, nature will invent a better idiot.” The point of this statement is that no development effort can be considered complete. There is always room for improvement.
When it comes to security, you must always bear in mind that the people who are looking to take advantage of your system are highly motivated. No matter how secure your system is, somebody will always be looking to crack it.
We’re not advocating paranoia, but we are suggesting that what we have written here is by no means the final word on VoIP security. While we have tried to be as comprehensive as we can be in this book, you must accept responsibility for the security of your system.
The criminals are working hard to find weaknesses and exploit them.