OpenSIPS intrusion detection

A popular way to control intrusion attempts is fail2ban framework. There is a tutorial that explains how to setup fail2ban with OpenSIPS. This solution is easy to setup but, on my humble opinion, it has certain limitations and disadvantages: 
  • Fail2ban is parsing log files and if OpenSIPS will start to generate lots of logs (debug level 4 for example) there is a good chance that fail2ban will fail to detect intruder's IP. Besides, fail2ban uses regex pattern when checking logs, and it brings some extra charge on the system.
  • Fail2ban is using iptables to block intruder by IP address. This seems to be correct, but what if user configure its softphone with wrong credentials and after several attempts PC address will be blocked. If server with OpenSIPS is providing other services, like DNS, then it would bring problems not related to telephony and problems like this are not easy to debug. 
  • If you are using cluster of OpenSIPS servers with lots of nodes, there is no way (AFAK) to share information about intruders' IPs. And each node would have to do the same work to detect intruder. Also if you have to release banned IP, you have to do it on each node manually one by one.
  • Fail2ban is an external tool, and if it crashes or if someone stops iptables for any reason, then server become vulnerable for intruders. 
Meanwhile, OpenSIPS has everything you need to detect and block intruders for certain period of time.
This sould work much faster then fail2ban and more reliable. And using cache db like cassandra or redis for storing IPs, would allow to share banned IPs between all cluster nodes right away when an intruder is detected. 

The simple configuration of OpenSIPS IDS will take several additional lines of code for REGISTER request authentication and INVITE proxy authorization. Following examples are using Redis cache DB for storing attempts and intruders IPs. Usernames and passwords are also stored in Redis that's why I am using here pv_www_authorize and pv_proxy_authorize functions.

Here is an implementation for REGISTER requests:


  1. [line: 4] Try to verify credentials
  2. [line: 8] Increment tries counter for username in "To:" header. "To:" header data is used by OpenSIPS to extract username. This information is stored for 30 seconds: cache_add("redis", "auth:$tU:tries",1,30); Please note, that each time this function is called and counter is incremented, the expire time also is reset. So 30 seconds is quite enough to control resend of packets.
  3. [lines: 12-18] After that we check if tries limit has reached: if($var(tries) > 10), then store intruder IP in cache DB for 10 minute (600 sec): cache_store("redis","intruder:$tU","$si", 600);. Then script sends response 403 and exits.
  4. In case if user successfully identified, its location will be saved. In this case tries counter for this user will expire in 30 seconds and next time user send register request, counter will starts with 1.
The same logic should be implemented with proxy authentication for INVITE requests:


Next step is to verify if requests are sent by intruder. We do this in the very top of the main route by calling route "intruders_detect". Here is a source code of the route:

The logic is pretty simple. If user is found in intruders list, then we got its IP address in pseudo variable $avp(ipaddr) and compare it to the source IP of the request. This is important to check IP address because intruder can compromise real users by using their usernames in From/To headers.


The working example can be found here:https://github.com/staskobzar/opensips-ids 
To setup example install OpenSIPS with redis module. You also will need sipp on another machine. Add user "8888" with password "foobar" with redis CLI: redis-cli set user:8888 foobar. Then run sipp script "startuac.sh" on another machine and watch the opensips logs. You should see something like this in logs:


1-96644@192.168.1.151|REGISTER| Challeng auth for user 8888
1-96644@192.168.1.151|REGISTER| Total tries for user 8888 is 1
1-96644@192.168.1.151|REGISTER| Challeng auth for user 8888
1-96644@192.168.1.151|REGISTER| Total tries for user 8888 is 2
2-96644@192.168.1.151|REGISTER| Challeng auth for user 8888
2-96644@192.168.1.151|REGISTER| Total tries for user 8888 is 3
2-96644@192.168.1.151|REGISTER| Challeng auth for user 8888
2-96644@192.168.1.151|REGISTER| Total tries for user 8888 is 4
3-96644@192.168.1.151|REGISTER| Challeng auth for user 8888
3-96644@192.168.1.151|REGISTER| Total tries for user 8888 is 5
3-96644@192.168.1.151|REGISTER| Challeng auth for user 8888
3-96644@192.168.1.151|REGISTER| Total tries for user 8888 is 6
4-96644@192.168.1.151|REGISTER| Challeng auth for user 8888
4-96644@192.168.1.151|REGISTER| Total tries for user 8888 is 7
4-96644@192.168.1.151|REGISTER| Challeng auth for user 8888
4-96644@192.168.1.151|REGISTER| Total tries for user 8888 is 8
5-96644@192.168.1.151|REGISTER| Challeng auth for user 8888
5-96644@192.168.1.151|REGISTER| Total tries for user 8888 is 9
5-96644@192.168.1.151|REGISTER| Challeng auth for user 8888
5-96644@192.168.1.151|REGISTER| Total tries for user 8888 is 10
6-96644@192.168.1.151|REGISTER| Challeng auth for user 8888
6-96644@192.168.1.151|REGISTER| Total tries for user 8888 is 11
6-96644@192.168.1.151|REGISTER| Tries limit reached. Intruder detected. Ban user for 10min.
7-96644@192.168.1.151|REGISTER| Intruder with IP 192.168.1.151 detected, intruder:8888
8-96644@192.168.1.151|REGISTER| Intruder with IP 192.168.1.151 detected, intruder:8888
9-96644@192.168.1.151|REGISTER| Intruder with IP 192.168.1.151 detected, intruder:8888
10-96644@192.168.1.151|REGISTER| Intruder with IP 192.168.1.151 detected, intruder:8888

You can check intruders list with command: redis-cli keys \* | grep intruder


One thing that is not implemented is sending alerts with emails when intruder detected. I deliberately have not put any solution here because there several possible ways to solve it. It can be done using msg_exec from exec module to execute mail command. It also can be done with some cronjob that will search intruders records in redis db and send emails. It is possible even to use rabitmq or datagram events interface to communicate with external services.

That's it.  

Comments

Popular posts from this blog

Asterisk Queues Realtime Dashboard with amiws and Vue

YAML documents parsing with libyaml in C