Hans Petter Selasky alerted the Asterisk developer community about a potential harmful pattern in Asterisk dialplans on February 9th.  His example is as follows:

[from_sip]
exten => _X.,1,Dial(SIP/${EXTEN}@testsip)

He writes: “And if ${EXTEN} = “000@testsip&SIP/333” what turns out to happen then is similar to SQL injection :-(  ”He is exactly right. Many VoIP protocols, including IAX2 and SIP, have a very large allowed character set in the dialed extension, a character set that allows characters that are used as separators to the dial() and the queue() applications, as well as within the dialstring that these applications send to the channel drivers in Asterisk. A user can change the dial options and dial something we should not be able to dial in your system. This article describes the issue in more detail and gives you some help on how to avoid this causing trouble in your Asterisk server.

Using pattern matching and ${EXTEN} ? – be careful!

The core issue here is the use of the pattern matching string in his example, the pattern “_X.” that matches everything after one digit, which includes any alphanumeric pattern. We all have used similar pattern matches for years. I have found several examples in my training material as well as many pages and e-mail answers on the Internet that use the same construction. It doesn’t feel very good and we all need to change the best current practise we demonstrate and help the community to get this right, in order to avoid risking allowing uncontrolled calling through your Asterisk servers.

Asterisk variables are parsed early

Asterisk variables – both global and channel variables – are parsed before the application or function receives it’s arguments. It’s not the dialplan application that parses the variable – much like the Unix shell. Consider this example:

exten => _X., 1, dial(${MYDIALSTRING})

The variable MYDIALSTRING now contains all the arguments to dial – including commas, dialstring and various options to dial. I have many dialplans where I build the dialstring in various macros then call dial() this way. It’s a feature, not a bug. But when you consider this example:

exten => _X., 1, dial(SIP/${EXTEN}@myvoipserviceprovider,20,T)

One can, if one learns by example, assume that the EXTEN variable is limited to just the extension and can’t do anything else. But as Hans Petter pointed out, the EXTEN can contain a lot of stuff, including a full dialstring and an ampersand, which changes this dial() to a forking dial, setting up two or more different channels and calling all of them at the same time. Not what the Asterisk admin expected.

My conclusions on how we should handle this

The discussion  on the mailing list has lead me to a few conclusions (of my own):

  • This is a problem with the dialplans, not the code of Asterisk.  We will not change existing functionality in Asterisk, because that will propably break more than what we fix.
  • We have a responsibility of helping the Asterisk admins to fix this. We can do this by adding functionality and informing admins about this vulnerability and helping them fix it. I have proposed a few changes but got no feedback from other developers. I have also requested a security advisory about this, but it seems to take time to process. Hopefully, we’ll get all fixed.
  • We need help from all involved in the Asterisk eco-system. This is not something that  the development team can solve by itself. We can add documents, READMEs and fix our own examples. But that won’t fix it. We need everyone involved to pump this information out in all the veins that runs through the Asterisk eco-system. Audit your dialplans, fix this issue. And do it now. Everyone that runs a web site with dialplan examples – audit your examples, fix them. Everyone that has published books – publish errata on your web site. Please help us – and do it now.

What can you do to prevent this?

Filter all dialstrings from all VoIP channels before you send them to the dialplan. By using a dialplan function like FILTER() you can make sure that the extension called only contain valid characters (which is up to your dialplan). If you only operate a PSTN gateway, it’s propably “+0123456789″, if you are operating a SIP gateway, you have to be less restrictive.In SIP, the @ at sign is a valid character in the username part of the extension, although this is rarely used since it seems so illogical. A SIP uri like sip:oej@edvina.net@astritech.org doesn’t make much sense on my business card, but is useful in some gateway situations. In an IAX2 dialstring, the @ is used to separate extension from context on a remote server, so the @ can be harmful here.Characters you definitely do not want to let through without knowing exactly what yo do are (this is not claiming to be a definitive list, it’s what I think right now)

@ : | , / $ { } ;

These are used either as separators in dialstrings for various channels, separators in application commands or variable substitutions.

Process to fix this

In order to fix this, you need to update your dialplan. There’s no magic bullet that we can code in Asterisk to fix this, I’m sorry. We all just need to audit our extensions.conf files and the included files. I suggest that you start with the channel configuration files and find out all the contexts you use as incoming contexts from all VoIP channels. Here’s a few code snippets that can help you:

; Context used in sip.conf for local devices
[sipusers-incoming]
;----Match everything
exten => _.,1,gotoif(${REGEX("&,/|@" ${EXTEN})}?badexten,1)
exten => _.,n,goto(sipusers-filtered,${EXTEN},1)
exten => badexten,1,playback(invalid,noanswer)
exten => badexten,n,wait(.5)
exten => badexten,n,congestion

This context is used as an incoming context in sip.conf. We match everything and use the REGEX dialplan function to check for characters that can be harmful. A similar solution that just filters out the characters we don’t want is this:

; Context used in sip.conf for local devices   [sipusers-incoming]

;—-Match everything

exten => _X.,1,goto(sip-filtered,${FILTER(+0123456789,${EXTEN})},1)

This context use the FILTER dialplan function to just keep approved characters and jump to another context and the filtered extension. This will of course stop all use of alphanumeric extensions. You can have them in a separate context with exact matches and include that context in the incoming context.Both of these examples can be used as incoming contexts. Rename your current incoming context to <contextname>-filtered and use one of the above examples with the old context name.Remember that the problem here is pattern matching – using the dot “.” to match anything. Exact matches is are not affected, like

exten=> 300,1,dial(SIP/olle)

Pattern matches where you don’t use ${EXTEN} are not affected either

exten => _X.,1,dial(IAX2/steve)

In this case, we always dial steve, no matter what the requested extension is.

Summary

  • Because of a conflict between allowed characters in the called number or name in many VoIP protocols and the way Asterisk handles channel variables, there is a security risk hidden in many dialplans based on examples provided over the years by the Asterisk developers, trainers and community. The primary risk is that by using an ampersand in the dialstring, a user can access protected resources or misuse the pbx services. However, this is not the only problem, as other characters may cause unexpected or problematic behaviour.
  • The best way to protect the Asterisk servers is to use a filtering function as the first step in the incoming dialplan context, where calls end up from all VoIP trunks, devices and users. In this article, I’ve provided examples using the REGEX and FILTER dialplan functions.
  • Asterisk administrators are adviced to urgently audit their dialplans and add the needed filtering functions. This article is an early call to action and more detailed information will soon be published on Asterisk.org as a security advisory, as well as documentation within coming releases of Asterisk 1.4, 1.6.x and the future Asterisk 1.8 (currently svn trunk). There might also be additional functions added to Asterisk that will help protecting the servers from this kind of misuse. Please update yourself on the mailing list and from one of these sources.
  • If you have questions, please use the asterisk-users mailing list or the #asterisk IRC channel.

As always with security issues that are as simple as this, it’s peculiar that we haven’t realized this problem before. A big thank you to Hans Petter for opening our eyes!

Update: