Infiltrated dot Net

 




A Simple Asterisk Based Toll Fraud Prevention Script
J. Oquendo

After reading about the emerging threats to PBX and toll fraud, I decided to give a primer to administrators of IP PBX servers. The purpose of this rambling is to offer administrators insight into creating a form of "toll fraud prevention" intrusion prevention system. Most IP PBX's differ but most have a mechanism to log information so keep that in mind. As I write this, I'll be using the Asterisk Open Source PBX as the framework for my "VTIPS" Voice Toll-Fraud Intrusion Prevention.

Before I dive into the nitty-gritty, let's have a concise look at how a "device" registers to an IP PBX. This device could be in the form of an analog telephone adapter (ATA), a softphone, a hardphone or a trunk. IP based PBX's such as Asterisk use usernames and passwords to interconnect with one another. In this sense it is no different than an e-mail account. You're given a username, a password and a server to connect to and from to send and receive mail. In the case of the IP PBX, you're given a username, a password and a registrar. (And no Mark - this is not just "how to use strong username/password combos" ;))

Let's have a look at an extension on of my Asterisk servers:

[ext1000]
type=friend
context=mycontext
username=ext1000
secret=mySecretPassword
host=dynamic
qualify=2000
canreinvite=yes
disallow=all
allow=g729
allow=ulaw
host=dynamic
nat=no

We see extension ext1000 has a username of ext1000 and a password (secret) of mySecretPassword. If this were email, an equivalent would look similar to:

[john.doe@thisdomain]
username=john
secret=myEmailPassword

While someone may receive e-mail to "john.doe" what matters is actually the username. This is what is entered in Asterisk's log entries - it is what we need to watch out for. When logfiles start filling up with a large amount of erroneous entries, it's likely someone may be trying to brute force an account on your IP PBX. If you're in a production environment, your log entries can be rather large which makes vigilant monitoring difficult.

Now, there are methods we can use as administrators to make sure that extension 1000 ONLY logs in internally for example, in the host=dynamic portion, we can statically assign an IP address there to ensure that extension 1000 ONLY logs in from whatever address is entered. What about road warriors? Remote connections from say the VP of sales who might be at a hotel in another state. You can say "IPTables" but that too can be problematic.

Assuming you have workers who need connectivity remotely, it can be from anywhere, a hotel, their home, a client. Are you willing to block all and have an administrator make a change at three in the morning for a remote worker who needs access? Kind of counterproductive. For starters, just like e-mail, strong passwords should be used, also just because it is phone, doesn't mean you couldn't assign the username joesremotephone. This is one of the beauties of VoIP.

Ridding yourself of the number scheme is a good practice. Most bots attacking machines for the purposes of toll fraud are generating connection attempts sequentially. Beginning with say ext 100, they'll likely program their "wares" to attempt to connect to say extension 100 first, 101 second, 102 third and so on. This means, if you're used to the numbering scheme, eventually they will reach a valid extension. If you used a bad password, they'll likely break that in minutes as well. So we should immediately seek to remove the potential for this from occurring.

[CompanyExtension1000]
type=friend
context=mycontext
username=CompanyExtension1000
secret=mySecretPassword
host=dynamic
qualify=2000
canreinvite=yes
disallow=all
allow=g729
allow=ulaw
host=dynamic
nat=no

The formatting in: username=CompanyExtension1000 is a more difficult combination to guess than a username=1000 let alone using a real password as opposed to a dictionary based password or a numeric password - wouldn't you agree? With this said, we can feel a little more assured that it would be difficult for a program or someone to brute force an account based off of this scheme. Not full-proof, but far better than username=1000 password=1234. Remember that VoIP won't have a limitation on the kind of password you can use (numeric, alphabetical, etc). Let's see if it worked shall we?


# sed -n '403,415p' /etc/asterisk/sip.conf

[CompanyExtension1000]
type=friend
context=mycontext
username=CompanyExtension1000
secret=mySecretPassword
host=dynamic
qualify=2000
canreinvite=yes
disallow=all
allow=g729
allow=ulaw
host=dynamic
nat=yes

Open up Asterisk: (only relevant information pasted)



myPBX*CLI> sip show peer CompanyExtension1000

myPBX*CLI>


* Name: CompanyExtension1000
Context: mycontext
Dynamic: Yes
Addr->IP: 8.6.4.2 Port 2051
Def. Username: CompanyExtension1000
Status: OK (53 ms)
Useragent: Cisco-CP7960G/8.0
Reg. Contact: sip:CompanyExtension1000@8.6.4.2:2051;line=10r6ruft

I've successfully connected remotely to my IP PBX. I can send and receive calls without a problem whether I'm using a softphone, hardphone, ATA, you name it - I can make calls. So we averted the potential of an automated programming brute forcing without using some form of hybrid attack. So how do we stop that...

All connection attempts are logged as stated previously, the notion is to be able to allow someone to connect remotely without having that person have to notify an adminstrator of the PBX: "I will be in a hotel in Japan next week, when I get there should I call you with the IP address so you can allow me to make calls?" How productive is that. Not everyone can afford to have an uber VPN connection to and from the office either. In the real world, not everyone works in the Fortune XXX environment.

So let's look at a failure:




# tail -n 1 messages|grep -i wro
2009-02-05 12:32:05 Registration from '' failed for '221.123.45.67' - Wrong password

We see from the log entry that username 1234 is having issues however, this is a valid username. An invalid username log entry would look like this:

2009-02-05 12:14:25 Registration from '"1000" ' failed for '22.33.44.55' - Username/auth name mismatch

So how can we mitigate against this username/auth mismatch - this is a big concern. We'd like to be able to watch it in real time however, we might not want to disaffect the machine, we may not want the added memory usage, we just might not want to create a start-up script, whatever the reason, we'd like to be able to check at any given point in time whether someone is attempting to access our PBX using a bogus username combination. We'd also like to ensure if they do by chance guess a name, they're blocked anyway.

So how can we do this effectively. From a system administrator's standpoint, I could monitor my logs using tail in real-time, if I see something, I can block it. That would only work if I was monitoring my terminal 24/7. I could use Splunk, syslog-ng, PERL, Ruby. The list goes on and on. The following started off as a one-liner and evolved into a "Frankenstein" like script that enables me to block repeat offenders. While many would wonder what is my reasoning for not writing an outright program in C or maybe streamlining this to perl, the answer is simple; no two systems are alike, just because I have Perl installed, doesn't mean someone else would. Just because I wrote it in C doesn't guarantee someone else would have the necessary libraries to compile it. What is certain about the script is, if it's on Linux, BSD, Solaris, it will work on those servers period. It uses nothing more than baseline, standard commands.






idjits=`basename $0`
TMPFILE=`mktemp /tmp/${idjits}.XXXXXX` || exit 1
messages=/var/log/asterisk/messages
time=`tail -n 2 /var/log/asterisk/messages | sed -n '1p' | awk '{print $2}' | awk -F : '{print $1":"}'`
day=`tail -n 2 /var/log/asterisk/messages | sed -n '1p' | awk '{print $1}'`


# Go through the logs, parse out today's date then parse out the hour... After this, check
# this time frame for invalid logins and password issues. Sort them to a temporary file.
# awk '{print $11} and $10 yield different things in Asterisk (go figure). So we go through this
# two times - the results of these two will get sorted a third time


echo "grep \"$day $time\" $messages" |sh|\
awk '/Wrong password/{print $11}'|\
sed 's:'\''::g'|sort -u|grep -vi [a-z] >> $TMPFILE


echo "grep \"$day $time\" $messages" |sh|\
awk '/Wrong password/{print $10}' |\
sed 's:'\''::g'|sort -u|grep -vi [a-z] >> $TMPFILE


echo "awk '/mismatch/{print $11}' $messages" |sh|\
grep -vi [a-z]|sed 's:'\''::g'

echo "Creating rules"


for i in `cat $TMPFILE|sed 's:for::g'`

do

echo "iptables -A INPUT -s $i -p udp --dport 5060:5061 -j REJECT --reject-with icmp-host-prohibited" > /etc/dbeats
echo "iptables -A INPUT -s $i -p tcp --dport 5060:5061 -j REJECT --reject-with icmp-host-prohibited" > /etc/dbeats

sort /etc/dbeats2 | uniq > /etc/deadbeats

echo "Flushing firewall"

iptables -F

echo "Reloading normal rules"

sh /etc/firewall

echo "Adding deadbeats"

sh /etc/deadbeats


done


date > /tmp/blocked
echo >> /tmp/blocked

echo "grep \"$day $time\" $messages" |sh|\

awk '/Wrong password/{print $11}'|\
sed 's:'\''::g'|sort -u|grep -vi [a-z] >> /tmp/blocked
echo "grep \"$day $time\" $messages" |sh|\
awk '/Wrong password/{print $10}'|\
sed 's:'\''::g'|sort -u|grep -vi [a-z] | mail -s "Blocked accounts" This e-mail address is being protected from spambots. You need JavaScript enabled to view it


rm -f $TMPFILE

Pretty messy wouldn't you say - but oh so effective. So what is happening amidst all of that mess of a script. Well, the script goes through Asterisk's message logs doing two things, one is, it checks for password anomalies, the second for invalid users and blocks them both. The theory is, there shouldn't really be an issue with someone and their password and there definitely should not be anyone with an invalid username trying to register. So it blocks them immediately (more on this later). At first I created it to look at the logs, parse out the date, go right to the hour, then look at the anomalies. If userX had X amount of bad entries, then block them out, end of story. Then I thought about this, what if my VP fat fingered his password. He'd be blocked, he'd complain, so that wasn't a viable option. "Time based" would work, if it was 3am, he fat fingered his password, he'd fiddle with his device's username and password which would take some time and within an hour, he'd be able to re-register. By the way, the /etc/firewall file is a static list of rules always needing to be implemented for those wondering what that file is. Remember, those blocked are thrown in rules that are contained in /etc/deadbeats. You could go about it by specifying something similar to: if user tries and fails 50 times per hour, then block them (e.g. if [ `wc -l $messages|sed -n '1p' -gt 50 ]), there are different variables you could play with.

So the script works like this:


User --> register --> Bad Information --> Blocked for an hour
User --> fixes information on their own --> register --> service (all is well)
Bot --> random guesses --> register --> blocked for an hour (IP Tables rejects packets)

The notion is that bots would be automated to detect a failure and move on. The script which is running hourly or every five minutes, depending on what I configured in cron would always keep a watchful eye on what is happening. I have a modified version of the above which e-mails me alerts on who was blocked along with information on who may have been attempting to bruteforce an account. My personal crontab entry is for 5 minutes, now my theory is, a bruteforcing bot has five minutes to get into an account, within those five minutes, even if it did manage to bruteforce an account, it will be blocked anyway, in the interim, I will receive an e-mail notifying me of the offending IP address so I can either implement a static rule to always block them, or change passwords. In the event that a bruteforcing occurrence did manage to get into my system, their damage is still minimized for the duration of 5 minutes. Because most bruteforcing automated attacks start usually with a number - which I don't use - the event of them being successful is extremely low.

Now, things to keep in mind are 1) tweak this script to your needs. Don't shoot me an email complaining you blocked yourself out. This should be tested and modified to your individual system. 2) Account Architecture - seriously, you don't need to use 100 with a password of 100 unless you want to be infiltrated within seconds. 3) YMMV - your system differs from mine. This has proven to be more than an effective and cost efficient method of implementing anti-toll-fraud. The notion of having an employee upset for an hour versus forking out thousand of dollars in unauthorized calls is a no brainer.

Re-capping, the conceptual framework is there, I have something almost similar running in a production environment. Since mileage does vary, my version is modified to perform other tasks as well including sending information to OSSIM, performing a twice per day check on registered numbers not to mention to find anomalies in my CDR's, for example, if I see an extension making say 30 calls in a minute, no brainer, I can't think of anyone who'd be able to dial a number consistently in 2 seconds for a minute straight, let alone four minutes.

user --> register --> bad password? (hey poop happens) --> blocked (for an hour)

During this time, the user can attempt to fix their password, double check their entry, contact their support/admin staff.

invalid user --> register --> bogus info --> blocked (period)

No reason to even bother allowing this connection in really but in case is was a PEBKAC issue, these too will be flushed from the firewall in an hour - after the hour is up, if they continue to be make bad attempts (register bogus accounts), they will continue to be blocked after the initial instance. In the meantime, any blocking is mailed to the admins to make sense of at the end of the day which means an admin has ample amount of time to stop a potentially big issue.