Category Archives: Security

Authentication Vectors and Key Distribution in LTE

Querying Auth Credentials from USIM/SIM cards

LTE has great concepts like NAS that abstract the actual transport layers, so the NAS packet is generated by the UE and then read by the MME.

One thing that’s a real headache about private LTE is the authentication side of things. You’ll probably bash your head against a SIM programmer for some time.

As your probably know when connecting to a network, the UE shares it’s IMSI / TIMSI with the network, and the MME requests authentication information from the HSS using the Authentication Information Request over Diameter.

The HSS then returns a random value (RAND), expected result (XRES), authentication token (AUTN) and a KASME  for generating further keys,

The RAND and AUTN values are sent to the UE, the USIM in the UE calculates the RES (result) and sends it back to the MME. If the RES value received by the MME is equal to the expected RES (XRES) then the subscriber is mutually authenticated.

The osmocom guys have created a cool little utility called osmo-sim-auth, which allows you to simulate the UE’s baseband module’s calls to the USIM to authenticate.

Using this tool I was able to plug a USIM into my USIM reader, using the Diameter client built into PyHSS I was able to ask for Authentication vectors for a UE using the Authentication Information Request to the HSS and was sent back the Authentication Information Answer containing the RAND and AUTN values, as well as the XRES value.

Wireshark Diameter Authentication Information Response message body looking at the E-UTRAN vectors
Diameter – Authentication Information Response showing E-UTRAN Vectors

Then I used the osmo-sim-auth app to query the RES and RAND values against the USIM.

Osmocom's USIM Test tool - osmo-sim-auth

The RES I got back matched the XRES, meaning the HSS and the USIM are in sync (SQNs match) and they mutually authenticated.

Handy little tool!

Information stored on USIM / SIM Card for LTE / EUTRAN / EPC - K key, OP/OPc key and SQN Sequence Number

HSS & USIM Authentication in LTE/NR (4G & 5G)

I talked a bit in my last post about using osmo-sim-auth to authenticate against a USIM / SIM card when it’s not in a phone,

I thought I’d expand a little on how the Crypto side of things works in LTE & NR (also known as 4G & 5G).

Authentication primarily happens in two places, one at each end of the network, the Home Subscriber Server and in the USIM card. Let’s take a look at each of them.

On the USIM

On the USIM we’ve got two values that are entered in when the USIM is provisioned, the K key – Our secret key, and an OPc key (operator key).

These two keys are the basis of all the cryptography that goes on, so should never be divulged.

The only other place to have these two keys in the HSS, which associates each K key and OPc key combination with an IMSI.

The USIM also stores the SQN a sequence number, this is used to prevent replay attacks and is incremented after each authentication challenge, starting at 1 for the first authentication challenge and counting up from there.

On the HSS

On the HSS we have the K key (Secret key), OPc key (Operator key) and SQN (Sequence Number) for each IMSI on our network.

Each time a IMSI authenticates itself we increment the SQN, so the value of the SQN on the HSS and on the USIM should (almost) always match.

Authentication Options

Let’s imagine we’re designing the authentication between the USIM and the Network; let’s look at some options for how we can authenticate everyone and why we use the process we use.

Failed Option 1 – Passwords in the Clear

The HSS could ask the USIM to send it’s K and OPc values, compare them to what the HSS has in place and then either accept or reject the USIM depending on if they match.

The obvious problem with this that to send this information we broadcast our supposedly secret K and OPc keys over the air, so anyone listening would get our secret values, and they’re not so secret anymore.

This is why we don’t use this method.

Failed Option 2 – Basic Crypto

So we’ve seen that sending our keys publicly, is out of the question.

The HSS could ask the USIM to mix it’s K key and OPc key in such a way that only someone with both keys could unmix them.

This is done with some cryptographic black magic, all you need to know is it’s a one way function you enter in values and you get the same result every time with the same input, but you can’t work out the input from the result.

The HSS could then get the USIM to send back the result of mixing up both keys, mix the two keys it knows and compare them.

The HSS mixes the two keys itself, and get’s it’s own result called XRES (Expected Result). If the RES (result) of mixing up the keys by the USIM is matches the result when the HSS mixes the keys in the same way (XRES (Expected Result)), the user is authenticated.

The result of mixing the keys by the USIM is called RES (Result), while the result of the HSS mixing the keys is called XRES (Expected Result).

This is a better solution but has some limitations, because our special mixing of keys gets the same RES each time we put in our OPc and K keys each time a subscriber authenticates to the network the RES (result) of mixing the keys is going to be the same.

This is vulnerable to replay attacks. An attacker don’t need to know the two secret keys (K & OPc) that went into creating the RES (resulting output) , the attacker would just need to know the result of RES, which is sent over the air for anyone to hear.
If the attacker sends the same RES they could still authenticate.

This is why we don’t use this method.

Failed Option 3 – Mix keys & add Random

To prevent these replay attacks we add an element of randomness, so the HSS generates a random string of garbage called RAND, and sends it to the USIM.

The USIM then mixes RAND (the random string) the K key and OPc key and sends back the RES (Result).

Because we introduced a RAND value, every time the RAND is different the RES is different. This prevents against the replay attacks we were vulnerable to in our last example.

If the result the USIM calculated with the K key, OPc key and random data is the same as the USIM calculated with the same K key, OPc key and same random data, the user is authenticated.

While an attacker could reply with the same RES, the random data (RAND) will change each time the user authenticates, meaning that response will be invalid.

While an attacker could reply with the same RES, the random data (RAND) will change each time the user authenticates, meaning that response will be invalid.

The problem here is now the network has authenticated the USIM, the USIM hasn’t actually verified it’s talking to the real network.

This is why we don’t use this method.

GSM authentication worked like this, but in a GSM network you could setup your HLR (The GSM version of a HSS) to allow in every subscriber regardless of what the value of RES they sent back was, meaning it didn’t look at the keys at all, this meant attackers could setup fake base stations to capture users.

Option 4 – Mutual Authentication (Real World*)

So from the previous options we’ve learned:

  • Our network needs to authenticate our subscribers, in a way that can’t be spoofed / replayed so we know who to bill & where to route traffic.
  • Our subscribers need to authenticate the network so they know they can trust it to carry their traffic.

So our USIM needs to authenticate the network, in the same way the network authenticates the USIM.

To do this we introduce a new key for network authentication, called AUTN.

The AUTN key is generated by the HSS by mixing the secret keys and RAND values together, but in a different way to how we mix the keys to get RES. (Otherwise we’d get the same key).

This AUTN key is sent to the USIM along with the RAND value. The USIM runs the same mixing on it’s private keys and RAND the HSS did to generate the AUTN , except this is the USIM generated – An Expected AUTN key (XAUTN). The USIM compares XAUTN and AUTN to make sure they match. If they do, the USIM then knows the network knows their secret keys.

The USIM then does the same mixing it did in the previous option to generate the RES key and send it back.

The network has now authenticated the subscriber (HSS has authenticated the USIM via RES key) and the subscriber has authenticated the USIM (USIM authenticates HSS via AUTN key).

*This is a slightly simplified version of how EUTRAN / LTE authentication works between the HSS and the USIM – In reality there are a few extra values, such as SQN to take into consideration and the USIM talks to to the MME not the HSS directly.

I’ll do a follow up post covering the more nitty-gritty elements, AMF and SQN fields, OP vs OPc keys, SQN Resync, how this information is transfered in the Authentication Information Answer and how KASME keys are used / distributed.

Roll your own USIMs for Private LTE Networks

I wrote a while ago about USIM basics and talked about what each of the fields stored on a USIM manage, but I thought I’d talk a little about my adventures in getting custom USIMs.

I started working on a private LTE project a while ago; RAN hardware (eNodeBs) were on the way, down to a shortlist of a few EPC platforms, but I still needed USIMs before anyone was connecting to the network.

So why are custom USIMs a requirement? Can’t you just use any old USIM/SIMs?

In UMTS / LTE / NR networks there’s mutual network authentication, again I’ve written about this topic before, but unlike GSM where the network authenticates the UE, in later RAN standards, the UE also authenticates the network. (This mitigates any bad actor from setting up their own base stations and having UEs attach to it and have their traffic intercepted).

For roaming to work between carriers they’ve got to have their HSS / DRA connecting to the DRA or HSS of other carriers, to allow roaming subscribers to access the network, otherwise they too would fall foul of the mutual network authentication and the USIM wouldn’t connect to the network.

The first USIMs I purchased online through a popular online marketplace with a focus on connecting you to Chinese manufacturers.
They listed a package of USIMS, a USB reader/writer that supported all the standard USIM form factors and the software to program it, which I purchased.

The USIMs worked fairly well – They are programmable via a card reader and software that, although poorly translated/documented, worked fairly well.

USIM Programming Interface

K and OP/OPc values could be written to the card but not read, while the other values could be read and written from the software, the software also has the ability to sequentially program the USIMs to make bulk operations easier. The pricing worked out about $8 USD per USIM, which although expensive for the quantity and programmable element is pretty reasonable.

Every now and then the Crypto values for some reason or another wouldn’t get updated, which is exactly as irritating as it sounds.

Pretty quickly into the build I learned the USIMs didn’t include an ISIM service on the card, ISIM being the service that runs on the UCCID responsible for IMS / VoLTE authentication.

Again I went looking and reached out to a few manufacturers of USIMs.

The big vendors, Gemalto, Kona, etc, weren’t interested in providing USIMs in quantities less than 100,000 and their USIMs came from the factory pre-programmed, meaning the values could only be changed through remote SIM provisioning, a form of black magic.

In the end I reached out to an OEM manufacturer from China who provided programmable USIM / ISIMs for less than I was paying on the online marketplace and at any quantity I wanted with custom printing options, allocated ICCIDs, etc.

The non-programmable USIMs worked out less than $0.40 USD each in larger quantities, and programmable USIM/ISIMs for about $5 USD.

The software was almost identical except for the additional tab for ISIM operations.

USIM / ISIM programming
ISIM parameters

Smart Card Readers

In theory this software and these USIMs could be programmed by any smart card reader.

In practice, the fact that the ISO standard smart card is the same size as a credit card, means most smart card readers won’t fit the bill.

I tried a few smart card readers, from the one built into my Thinkpad, to a Bluedrive II from one of the USIM vendors, in the end the MCR3516 Smart Card Reader which reads 4FF USIMs (Standard ISO size smart card, full size SIM, Micro SIM and Nano SIM form factors, which saved on so much mucking about with form factor adapters etc.

4FF Smart Card Reader for programming SIM/USIM/ISIM

Future Projects

I’ve got some very calls “Multi Operator Neutral Host” (MoNEH) USIMs from the guys at Telet Research I’m looking forward to playing with,

eSIMs are on my to-do list too, and the supporting infrastructure, as well as Over the Air updating of USIMs.

Kamailio Use Case – SIP Honeypot with SQL Database

In my last few Kamailio Bytes posts, I’ve talked about using the GeoIP2 module to lookup the location of IP Addresses and SQLops and db_mysql to work with relational databases from within Kamailio.

Now we’ll put both together to create something functional you could use in your own deployments. (You’d often find it’s faster to use HTable to store and retrieve data like this, but that’s a conversation for another day)

The Project

We’ll build a SIP honeypot using Kamailio. It’ll listen on a Public IP address for SIP connections from people scanning the internet with malicious intent and log their IPs, so our real SIP softswitches know to ignore them.

We’ll use GeoIP2 to lookup the location of the IP and then store that data into a MySQL database.

Lastly we’ll create a routing block we can use on another Kamailio instance to verify if that the IP address of the received SIP message is not in our blacklist by searching the MySQL database for the source IP.

The Database

In this example I’m going to create a database called “blacklist” with one table called “baddies”, in MySQL I’ll run:

CREATE database blacklist;
CREATE TABLE `baddies` (
	`id` INT NOT NULL AUTO_INCREMENT PRIMARY KEY,
	`ip_address` INT unsigned UNIQUE,
	`hits` INT,
	`last_seen` DATETIME,
	`ua` TEXT,
	`country` TEXT,
	`city` TEXT
);

I’ll setup a MySQL user to INSERT/UPDATE/SELECT data from the MySQL database.

For storing IP addresses in the database we’ll store them as unsigned integers, and then use the INET_ATON('127.0.0.1') MySQL command to encode them from dotted-decimal format, and the INET_NTOA('2130706433') to put them back into dotted decimal.

Modparams

Now we’ll need to configure Kamailio, I’ll continue on from where we left off in the last post on GeoIP2 as we’ll use that to put Geographic data about the IP before adding the MySQL and SQLOps modules:

# ----- SQL params -----
loadmodule "db_mysql.so"
loadmodule "sqlops.so"

#Create a new MySQL database connection called blacklist_db 
modparam("sqlops","sqlcon","blacklist_db=>mysql://root:yourpassword@localhost/blacklist")  

#Set timeouts for MySQL Connections
modparam("db_mysql", "ping_interval", 60)
modparam("db_mysql", "auto_reconnect", 1)
modparam("db_mysql", "timeout_interval", 2)

After loading db_mysql and sqlops we create a new object / connection called blacklist_db with our MySQL Database parameters.

Now after a restart we’ll be connected to our MySQL database.

Honeypot Routing Logic

Now we’ll create a route to log the traffic:

####### Routing Logic ########


/* Main SIP request routing logic
 * - processing of any incoming SIP request starts with this route
 * - note: this is the same as route { ... } */
request_route {

        route(AddToBlacklist);
        sl_reply('200', 'Sure thing boss!');

}
route[AddToBlacklist]{
        xlog("Packet received from IP $si");
        sql_xquery("blacklist_db", "insert into baddies (ip_address, hits, last_seen, ua, country, city) values (2130706433, 10, NOW(), 'testua2', 'Australia', 'Hobart');");
}

Now for each SIP message received a new record will be inserted into the database:

 root@ip-172-31-8-156:/etc/kamailio# mysql -u root -p blacklist -e "select * from baddies;"
 Enter password:
 +----+------------+------+---------------------+---------+-----------+--------+
 | id | ip_address | hits | last_seen           | ua      | country   | city   |
 +----+------------+------+---------------------+---------+-----------+--------+
 |  1 | 2130706433 |   10 | 2019-08-13 02:52:57 | testua2 | Australia | Hobart |
 |  2 | 2130706433 |   10 | 2019-08-13 02:53:01 | testua2 | Australia | Hobart |
 |  3 | 2130706433 |   10 | 2019-08-13 02:53:05 | testua2 | Australia | Hobart |
 +----+------------+------+---------------------+---------+-----------+--------+

This is great but we’re not actually putting the call variables in here, and we’ve got a lot of duplicates, let’s modify our sql_xquery() to include the call variables:

sql_xquery("blacklist_db", "insert into baddies (ip_address, hits, last_seen, ua, country, city) values (INET_ATON('$si'), 10, NOW(), '$ua', 'Australia', 'Hobart');");

Now we’re setting the IP Address value to the Source IP psedovariable ($si) and formatting it using the INET_ATON function in MySQL, setting the last_seen to the current timestamp and setting the user agent to the User Agent psedovariable ($ua).

Let’s restart Kamailio, truncate the data that’s currently in the DB, send some SIP traffic to it and then check the contents:

mysql -u root -p blacklist -e "select *, INET_NTOA(ip_address) from baddies;"

Here you can see we’re starting to get somewhere, the IP, UA and last_seen values are all now correct.

We’re getting multiple entries from the same IP though, instead we just want to increment the hits counter and set the last_seen to the current time, for that we’ll just update the SQL query to set the time to be NOW() and if that IP is already in the database to update the last_seen value and incriment the hits counter:

sql_xquery("blacklist_db", "insert into baddies (ip_address, hits, last_seen, ua, country, city) values (INET_ATON('$si'), 1, NOW(), '$ua', 'Australia', 'Hobart') ON DUPLICATE KEY UPDATE last_seen = NOW(), hits = hits + 1;");

Now we’ve only got one line per IP address, with the hit counters showing how many SIP requests we’ve received from that IP.

Finally we’ll use the GeoIP2 Module to get the geographic info of the source of the traffic, like we’ve talked about in the previous post:

route[AddToBlacklist]{
        xlog("Packet received from IP $si");
        geoip2_match("$si", "src"))
        sql_xquery("blacklist_db", "insert into baddies (ip_address, hits, last_seen, ua, country, city) values (INET_ATON('$si'), 1, NOW(), '$ua', '$gip2(src=>cc)', '$gip2(src=>city)') ON DUPLICATE KEY UPDATE last_seen = NOW(), hits = hits + 1;", "r_sql");

}

The only issue with this is if GeoIP2 doesn’t have a match, no record will be added in the database, so we’ll add a handler for that:

route[AddToBlacklist]{
        xlog("Packet received from IP $si");
        if(geoip2_match("$si", "src")){
                sql_xquery("blacklist_db", "insert into baddies (ip_address, hits, last_seen, ua, country, city) values (INET_ATON('$si'), 1, NOW(), '$ua', '$gip2(src=>cc)', '$gip2(src=>city)') ON DUPLICATE KEY UPDATE last_seen = NOW(), hits = hits + 1;", "r_sql");
        }else{          ##If no match in GeoIP2 leave Country & City fields blank
                sql_xquery("blacklist_db", "insert into baddies (ip_address, hits, last_seen, ua, country, city) values (INET_ATON('$si'), 1, NOW(), '$ua', '', '') ON DUPLICATE KEY UPDATE last_seen = NOW(), hits = hits + 1;", "r_sql");
        }
}

Now let’s check our database again and see how the data looks:

mysql -u root -p blacklist -e "select *, INET_NTOA(ip_address) from baddies;" 

Perfect! Now we’re inserting data into our blacklist from our honeypot. Now we’ll configure a new routing block we can use on another Kamailio instance to see if an IP is in the blacklist.

I left this running on my AWS box for a few hours, and lots of dodgy UAs dropped in to say hello, one of which was very insistent on making calls to Poland…

Querying the Data

Now we’ve got a blacklist it’s only useful if we block the traffic from our malicous actors who we’ve profiled in the database.

You could feed this into BGP to null route the traffic, or hook this into your firewall’s API, but we’re going to do this in Kamailio, so we’ll create a new routing block we can use on a different Kamailio instance – Like a production one – to see if the IP it just received traffic from is in the blacklist.

We’ve already spoken about querying databases in the SQLops Kamailio bytes, but this routing block will query the blacklist database, and if the sender is in the database, one or more records will be returned, so we know they’re bad and will drop their traffic:

route[CheckBlacklist]{

        xlog("Checking blacklist for ip $si");

        #Define a variable containing the SQL query we'll run
        $var(sql) = "select INET_NTOA(ip_address) as ip_address from baddies;";

        #Log the SQL query we're going to run to syslog for easy debugging
        xlog("Query to run is $var(sql)");

        #Query blacklist_db running the query stored in $var(sql) and store the result of the query to result_sql
        sql_query("blacklist_db", "$var(sql)", "result_sql");

        #If more than 0 records were returned from the database, drop the traffic
        if($dbr(result_sql=>rows)>0){
                xlog("This guy is bad news. Dropping traffic from $si");
                exit;
        }else{
                xlog("No criminal record for $si - Allowing to progress");
        }


}

Recap

We’d previously touched on using the GeoIP2 module to lookup the location of IP Addresses and SQLops and db_mysql to work with relational databases from within Kamailio.

This Honeypot use case just put those elements together.

In reality a far better implementation of this would use HTable to store this data, but hopefully this gives you a better understanding of how to actually work with data.

Final Note

I wrote this post about a week ago, and left the config running on an AWS box. I was getting hits to it within the hour, and in the past week I’ve had 172 IPs come and say hello, and some like the FriendlyScanner instance at 159.65.220.215 has sent over 93,000 requests:

mysql> select *, INET_NTOA(ip_address) from baddies;
 +--------+------------+-------+---------------------+----------------------------+---------+---------------------+-----------------------+
 | id     | ip_address | hits  | last_seen           | ua                         | country | city                | INET_NTOA(ip_address) |
 +--------+------------+-------+---------------------+----------------------------+---------+---------------------+-----------------------+
 |      1 | 3070413354 |    23 | 2019-08-23 14:15:40 | friendly-scanner           | CN      | Guangzhou           | 183.2.202.42          |
 |      2 | 1053957678 |     2 | 2019-08-16 00:09:06 | Ozeki VoIP SIP SDK v11.1.2 | FR      | Andresy             | 62.210.30.46          |
 |      3 | 2455302148 |     7 | 2019-08-23 00:05:03 |                      | US      |               | 146.88.240.4          |
 |      5 | 3107280921 |    26 | 2019-08-23 20:46:59 | friendly-scanner           | NL      |               | 185.53.88.25          |
 |      6 | 1308060846 |    53 | 2019-08-23 20:13:12 | friendly-scanner           | NL      |               | 77.247.108.174        |
 |      7 | 2671893706 |    90 | 2019-08-16 02:42:20 | 808                        | US      | North Bergen        | 159.65.220.202        |
 |     14 | 3110176716 |     6 | 2019-08-16 04:44:25 | sipcli/v1.8                | PS      |               | 185.97.135.204        |
 |     22 | 1308061231 |    18 | 2019-08-22 07:24:38 | friendly-scanner           | NL      |               | 77.247.110.47         |
 |     48 | 1347570964 |     8 | 2019-08-17 02:24:36 | friendly-scanner           | NL      |               | 80.82.77.20           |
 |     59 | 3107280933 |  9883 | 2019-08-19 03:03:32 |                      | NL      |               | 185.53.88.37          |
 |   1608 | 1308060849 |    35 | 2019-08-16 03:59:29 | sipcli/v1.8                | NL      |               | 77.247.108.177        |
 |   3967 | 3428686544 |     1 | 2019-08-16 04:03:13 | Asterisk PBX               | US      | Chicago             | 204.93.154.208        |
 |   3994 | 1754805254 |     1 | 2019-08-16 04:03:52 | Asterisk PBX               | US      |               | 104.152.52.6          |
 |   5290 | 3507576938 |     1 | 2019-08-16 04:37:12 | UnYeznuK                   | US      |               | 209.17.96.106         |
 |   7905 | 3107280927 |  3867 | 2019-08-22 11:42:00 |                      | NL      |               | 185.53.88.31          |
 |   8012 | 1356068398 |     1 | 2019-08-16 05:52:59 | Zeeko                      | PL      | Warsaw              | 80.211.246.46         |
 |   8207 | 3107280926 |  1825 | 2019-08-21 06:01:23 |                      | NL      |               | 185.53.88.30          |
 |   8412 | 3070413353 |    10 | 2019-08-23 07:18:04 | friendly-scanner           | CN      | Guangzhou           | 183.2.202.41          |
 |   8415 | 1308060851 |     1 | 2019-08-16 06:13:32 | friendly-scanner           | NL      |               | 77.247.108.179        |
 |   8746 | 1308060835 |    31 | 2019-08-16 08:45:51 | sipcli/v1.8                | NL      |               | 77.247.108.163        |
 |   8755 | 3107280940 |     3 | 2019-08-17 16:21:55 | friendly-scanner           | NL      |               | 185.53.88.44          |
 |   8761 | 1111941599 |     1 | 2019-08-16 07:26:55 | friendly-scanner           | CA      | Montreal            | 66.70.225.223         |
 |   9009 | 1308060832 |     9 | 2019-08-18 00:22:01 | friendly-scanner           | NL      |               | 77.247.108.160        |
 |   9250 | 2746007762 |   805 | 2019-08-20 10:26:04 | pplsip                     | FR      |               | 163.172.192.210       |
 |   9440 | 1308061242 |    16 | 2019-08-23 21:10:59 | friendly-scanner           | NL      |               | 77.247.110.58         |
 |   9458 | 3635051022 |   328 | 2019-08-17 11:17:00 | pplsip                     | US      | Buffalo             | 216.170.122.14        |
 |   9474 | 2959836555 |     8 | 2019-08-23 09:27:08 | Bria 5                     | PL      | Warsaw              | 176.107.133.139       |
 |   9494 | 3107280945 |     7 | 2019-08-22 18:24:01 | friendly-scanner           | NL      |               | 185.53.88.49          |
 |   9516 | 1308061267 |    64 | 2019-08-23 19:38:43 | friendly-scanner           | NL      |               | 77.247.110.83         |
 |   9546 | 1308061199 |  1884 | 2019-08-17 00:19:02 | sipcli/v1.8                | NL      |               | 77.247.110.15         |
 |   9684 | 1308061400 |     7 | 2019-08-23 20:59:58 | friendly-scanner           | NL      |               | 77.247.110.216        |
 |  10088 | 3565227944 |     2 | 2019-08-22 17:47:04 | friendly-scanner           | FR      |               | 212.129.15.168        |
 |  10335 | 1588721057 |     3 | 2019-08-19 12:08:11 | friendly-scanner           | DE      | Frankfurt am Main   | 94.177.245.161        |
 |  10372 | 3107280931 |     8 | 2019-08-21 00:32:14 | friendly-scanner           | NL      |               | 185.53.88.35          |
 |  10440 | 1588717692 |     6 | 2019-08-18 11:37:20 | friendly-scanner           | FR      | Paris               | 94.177.232.124        |
 |  10451 | 3507577090 |     1 | 2019-08-16 18:18:32 | haLoBtKl                   | US      |               | 209.17.97.2           |
 |  10507 | 1505473210 |     1 | 2019-08-16 18:33:49 | pplsip                     | US      |               | 89.187.178.186        |
 |  10653 | 3562251402 |    90 | 2019-08-22 17:24:00 | pplsip                     | FR      | Nuits-Saint-Georges | 212.83.164.138        |
 |  11077 |  861095860 |     1 | 2019-08-16 21:11:03 | friendly-scanner           | FR      |               | 51.83.71.180          |
 |  11157 | 1722098888 |   197 | 2019-08-22 14:11:49 | sipcli/v1.8                | US      |               | 102.165.36.200        |
 |  11408 |   87965623 |    37 | 2019-08-17 12:18:29 | pplsip                     | US      | New York            | 5.62.63.183           |
 |  11589 | 1308061418 |     5 | 2019-08-19 12:34:55 | Telefonadapter             | NL      |               | 77.247.110.234        |
 |  11781 | 3281662327 |     3 | 2019-08-20 14:51:30 | support                    | FR      | Paris               | 195.154.49.119        |
 |  11812 | 2680904312 |     1 | 2019-08-17 01:09:40 | friendly-scanner           | US      | Clifton             | 159.203.90.120        |
 |  11826 |  624027040 |   258 | 2019-08-23 21:31:43 | Telefonadapter             | NL      |               | 37.49.229.160         |
 |  11827 | 1051561183 |     1 | 2019-08-17 01:47:04 | friendly-scanner           | RU      |               | 62.173.140.223        |
 |  11830 | 3514713984 |     3 | 2019-08-18 02:57:23 | sipcli/v1.8                | US      | St Louis            | 209.126.71.128        |
 |  11863 | 2382633440 |     1 | 2019-08-17 03:22:02 | friendly-scanner           | US      | Provo               | 142.4.25.224          |
 |  11895 | 1123085431 |     1 | 2019-08-17 05:05:58 |                      | US      | San Diego           | 66.240.236.119        |
 |  11917 | 3562247345 |     1 | 2019-08-17 05:58:21 | Bria 5                     | FR      | Paris               | 212.83.148.177        |
 |  11951 | 3507577002 |     1 | 2019-08-17 07:18:45 | vCbNSKtv                   | US      |               | 209.17.96.170         |
 |  11997 | 2256000670 |     2 | 2019-08-18 22:15:28 | friendly-scanner           | FR      |               | 134.119.214.158       |
 |  12012 | 2806494445 |     1 | 2019-08-17 09:08:55 | friendly-scanner           | US      | New York            | 167.71.180.237        |
 |  12060 | 1588719724 | 12958 | 2019-08-17 10:34:45 | Asterisk PBX               | FR      | Paris               | 94.177.240.108        |
 |  25027 | 1308060847 |     3 | 2019-08-17 14:05:12 | PortSIP VoIP SDK 11.2      | NL      |               | 77.247.108.175        |
 |  25052 | 1051560788 |     6 | 2019-08-22 23:41:20 | friendly-scanner           | RU      |               | 62.173.139.84         |
 |  25061 | 2769743287 |  3827 | 2019-08-20 13:28:17 |                      | CA      | Toronto             | 165.22.237.183        |
 |  25089 |  624027025 |   237 | 2019-08-23 21:30:21 | Cisco-SIPGateway/IOS-12.x  | NL      |               | 37.49.229.145         |
 |  25135 | 1308060845 |     1 | 2019-08-17 13:13:26 | friendly-scanner           | NL      |               | 77.247.108.173        |
 |  25314 | 1356068336 |     4 | 2019-08-23 06:41:51 |                            | PL      | Warsaw              | 80.211.245.240        |
 |  25342 | 1308061206 |     5 | 2019-08-21 05:50:11 | friendly-scanner           | NL      |               | 77.247.110.22         |
 |  27328 | 3507576890 |     2 | 2019-08-19 20:41:58 | BQDPtxsE                   | US      |               | 209.17.96.58          |
 |  28414 | 3107280932 |     1 | 2019-08-17 22:24:17 | friendly-scanner           | NL      |               | 185.53.88.36          |
 |  29304 | 2745967553 |     2 | 2019-08-21 12:05:24 | friendly-scanner           | FR      |               | 163.172.35.193        |
 |  29317 | 2671893719 | 93202 | 2019-08-18 01:55:38 | friendly-scanner           | US      | North Bergen        | 159.65.220.215        |
 | 122638 |  624634839 |     1 | 2019-08-18 03:44:42 | Ozeki VoIP SIP SDK v11.1.2 | FR      |               | 37.59.43.215          |
 | 122643 | 1308060935 |     1 | 2019-08-18 03:48:16 |                      | NL      |               | 77.247.109.7          |
 | 122692 | 3107280934 |  8470 | 2019-08-18 15:22:11 |                      | NL      |               | 185.53.88.38          |
 | 123190 | 1308061276 |     2 | 2019-08-22 08:42:06 | friendly-scanner           | NL      |               | 77.247.110.92         |
 | 123505 |  860848595 |     1 | 2019-08-18 05:28:07 | friendly-scanner           | CA      |               | 51.79.129.211         |
 | 124707 | 3565236782 |     3 | 2019-08-19 22:04:23 | Careers                    | FR      | Paris               | 212.129.50.46         |
 | 130299 | 3119862842 |     1 | 2019-08-18 12:51:44 | VOIP                       | DK      | Copenhagen          | 185.245.84.58         |
 | 130398 |  394281064 |     1 | 2019-08-18 13:00:27 | friendly-scanner           | US      | Portland            | 23.128.64.104         |
 | 131093 | 3644653804 |     2 | 2019-08-20 08:21:00 | friendly-scanner           | DE      | Frankfurt am Main   | 217.61.0.236          |
 | 131760 | 1588720143 |     1 | 2019-08-18 15:09:48 | friendly-scanner           | FR      | Paris               | 94.177.242.15         |
 | 131891 | 3633377286 |     1 | 2019-08-18 16:03:16 | friendly-scanner           | US      | Dallas              | 216.144.240.6         |
 | 131905 | 3510351397 |    12 | 2019-08-23 14:24:14 | PortSIP VoIP SDK 11.2      | US      | Lansing             | 209.59.182.37         |
 | 131979 | 1356070905 |     1 | 2019-08-18 17:10:47 | friendly-scanner           | PL      | Warsaw              | 80.211.255.249        |
 | 132142 | 1308060872 |     1 | 2019-08-18 19:27:15 | friendly-scanner           | NL      |               | 77.247.108.200        |
 | 132163 | 3507577122 |     1 | 2019-08-18 19:45:08 | jXIjYZTm                   | US      |               | 209.17.97.34          |
 | 132185 | 1308061207 |     2 | 2019-08-19 20:21:17 | friendly-scanner           | NL      |               | 77.247.110.23         |
 | 132244 | 1431941221 |     2 | 2019-08-22 03:46:37 | friendly-scanner           | PL      | �ódź             | 85.89.176.101         |
 | 133058 | 1308061211 |     1 | 2019-08-19 00:03:59 | PBX                        | NL      |               | 77.247.110.27         |
 | 133193 |  861606239 |     1 | 2019-08-19 00:27:24 | friendly-scanner           | FR      |               | 51.91.17.95           |
 | 133194 | 2261891524 |     1 | 2019-08-19 00:27:26 | friendly-scanner           | GB      | London              | 134.209.185.196       |
 | 133247 | 1311362863 |     1 | 2019-08-19 00:35:16 | friendly-scanner           | NL      |               | 78.41.207.47          |
 | 133562 | 3221485468 |     4 | 2019-08-23 00:50:28 | friendly-scanner           | US      | Buffalo             | 192.3.247.156         |
 | 133573 | 2673412367 |     1 | 2019-08-19 01:28:30 | friendly-scanner           | DE      | Frankfurt am Main   | 159.89.9.15           |
 | 134160 |  861605605 |   690 | 2019-08-19 12:20:33 | gffg                       | FR      |               | 51.91.14.229          |
 | 134218 | 3562255759 |     1 | 2019-08-19 05:15:30 | friendly-scanner           | FR      |               | 212.83.181.143        |
 | 134412 | 1053973250 |    22 | 2019-08-23 19:24:58 | friendly-scanner           | FR      | Paris               | 62.210.91.2           |
 | 134556 | 1308061221 |     1 | 2019-08-19 08:19:53 | friendly-scanner           | NL      |               | 77.247.110.37         |
 | 134665 | 1249543663 |     1 | 2019-08-19 10:06:49 | friendly-scanner           | CA      |               | 74.122.133.239        |
 | 134750 |   93785829 |     1 | 2019-08-19 10:52:57 | friendly-scanner           | GB      |               | 5.151.14.229          |
 | 134831 | 1347570977 |     1 | 2019-08-19 11:36:12 |                      | NL      |               | 80.82.77.33           |
 | 134939 | 3225441884 |     3 | 2019-08-23 12:53:33 | friendly-scanner           | US      | Secaucus            | 192.64.86.92          |
 | 134944 | 3276722437 |     1 | 2019-08-19 14:48:33 | friendly-scanner           | IT      |               | 195.78.209.5          |
 | 134945 | 1122091794 |   147 | 2019-08-19 15:48:28 | MizuPhone                  | US      | Schaumburg          | 66.225.195.18         |
 | 135140 | 3000185825 |     1 | 2019-08-19 19:01:47 | friendly-scanner           | TR      | Samsun              | 178.211.51.225        |
 | 135169 | 2745980561 |     1 | 2019-08-19 20:38:42 | friendly-scanner           | FR      |               | 163.172.86.145        |
 | 135205 | 1066345778 |    77 | 2019-08-23 06:04:18 | FreePBX 1.8                | US      | Dallas              | 63.143.37.50          |
 | 135212 | 1053994118 |     1 | 2019-08-19 22:28:37 | friendly-scanner           | FR      |               | 62.210.172.134        |
 | 135218 | 3565239451 |     2 | 2019-08-19 22:49:18 | VaxSIPUserAgent/3.5        | FR      | Le Plessis-Robinson | 212.129.60.155        |
 | 135225 | 3639984622 |     3 | 2019-08-22 20:40:42 | friendly-scanner           | US      | Dallas              | 216.245.193.238       |
 | 135246 | 3000185822 |     1 | 2019-08-19 23:56:45 | friendly-scanner           | TR      | Samsun              | 178.211.51.222        |
 | 135266 | 3562255783 |     1 | 2019-08-20 00:42:30 | friendly-scanner           | FR      |               | 212.83.181.167        |
 | 135284 | 1308061283 |     2 | 2019-08-22 06:51:28 | friendly-scanner           | NL      |               | 77.247.110.99         |
 | 135289 | 2727356702 |     1 | 2019-08-20 01:31:33 | friendly-scanner           | US      | Provo               | 162.144.41.30         |
 | 135324 | 1053992906 |  6758 | 2019-08-23 00:25:28 | fgfdhgfxjfhyjhkj           | FR      |               | 62.210.167.202        |
 | 135353 | 1053988629 |  4944 | 2019-08-23 00:25:51 | fgfdhgfxjfhyjhkj           | FR      |               | 62.210.151.21         |
 | 135695 |  782669768 |     5 | 2019-08-20 04:15:47 | fgfdhgfxjfhyjhkj           | NL      | Uddel               | 46.166.151.200        |
 | 136185 |  782669779 | 35927 | 2019-08-22 20:33:51 | fgfdhgfxjfhyjhkj           | NL      | Uddel               | 46.166.151.211        |
 | 138056 | 3562244647 |     2 | 2019-08-21 06:56:33 | testsip                    | FR      | Guyancourt          | 212.83.138.39         |
 | 141492 | 3281660074 |     2 | 2019-08-23 03:57:42 | Bria 5                     | FR      | Argenteuil          | 195.154.40.170        |
 | 141905 | 2649154441 |  2025 | 2019-08-20 15:24:27 |                      | US      | North Bergen        | 157.230.227.137       |
 | 142082 | 3107280950 |     1 | 2019-08-20 11:55:44 | friendly-scanner           | NL      |               | 185.53.88.54          |
 | 144875 | 3107280956 |     2 | 2019-08-23 03:08:10 | friendly-scanner           | NL      |               | 185.53.88.60          |
 | 144881 | 3281677282 |    74 | 2019-08-20 23:27:29 |                      | FR      |               | 195.154.107.226       |
 | 148332 | 3423277731 |     1 | 2019-08-20 17:15:49 | friendly-scanner           | US      | Seattle             | 204.11.18.163         |
 | 148463 |  866041843 |     1 | 2019-08-20 17:29:43 | friendly-scanner           | FR      | Paris               | 51.158.191.243        |
 | 148935 | 3000019324 |     1 | 2019-08-20 18:17:37 | friendly-scanner           | GB      | London              | 178.208.169.124       |
 | 150006 | 1051562889 |     1 | 2019-08-20 20:02:47 | friendly-scanner           | RU      | Moscow              | 62.173.147.137        |
 | 150459 | 1308060874 |     4 | 2019-08-21 23:39:45 | friendly-scanner           | NL      |               | 77.247.108.202        |
 | 151158 | 3291753334 |     1 | 2019-08-20 22:05:46 | VhPrwfzK                   | US      | Edison              | 196.52.43.118         |
 | 152525 | 1308061269 |     1 | 2019-08-21 00:31:18 | friendly-scanner           | NL      |               | 77.247.110.85         |
 | 152748 | 1588719753 |     2 | 2019-08-21 20:19:51 | friendly-scanner           | FR      | Paris               | 94.177.240.137        |
 | 152938 | 1308061244 |   862 | 2019-08-22 16:25:35 |                      | NL      |               | 77.247.110.60         |
 | 154063 | 3639886154 |   216 | 2019-08-22 02:36:47 | sipcli/v1.8                | US      | Seattle             | 216.244.65.74         |
 | 154100 | 2806460368 |     1 | 2019-08-21 02:06:05 | friendly-scanner           | DE      | Frankfurt am Main   | 167.71.47.208         |
 | 155564 | 1308061232 |   430 | 2019-08-21 04:50:00 |                      | NL      |               | 77.247.110.48         |
 | 156748 | 3107280962 |     1 | 2019-08-21 04:32:50 | friendly-scanner           | NL      |               | 185.53.88.66          |
 | 158028 | 3507576914 |     1 | 2019-08-21 05:49:21 | yMQsDpTp                   | US      |               | 209.17.96.82          |
 | 159649 | 2659999515 |     3 | 2019-08-21 11:24:18 | PortSIP VoIP SDK 11.2      | PS      |               | 158.140.95.27         |
 | 159710 | 2769728503 |     1 | 2019-08-21 09:12:10 | 808                        | US      | North Bergen        | 165.22.179.247        |
 | 160120 | 2755949120 |     1 | 2019-08-21 09:53:12 | friendly-scanner           | DE      |               | 164.68.114.64         |
 | 160495 | 2809328307 |     1 | 2019-08-21 10:31:39 | friendly-scanner           | FR      | Roubaix             | 167.114.242.179       |
 | 160763 | 2807459643 |     2 | 2019-08-23 19:48:17 | friendly-scanner           | DE      | Nuremberg           | 167.86.111.59         |
 | 161033 | 1356048200 |     1 | 2019-08-21 11:27:02 | friendly-scanner           | IT      | Arezzo              | 80.211.167.72         |
 | 162648 | 1245704902 |     1 | 2019-08-21 14:08:05 | friendly-scanner           | US      | Dallas              | 74.63.242.198         |
 | 163133 | 1308060834 |     3 | 2019-08-23 06:01:53 | friendly-scanner           | NL      |               | 77.247.108.162        |
 | 168263 | 2746011496 |   552 | 2019-08-23 14:00:56 | pplsip                     | FR      |               | 163.172.207.104       |
 | 168773 | 2807450735 |     1 | 2019-08-21 21:25:23 | friendly-scanner           | DE      | Nuremberg           | 167.86.76.111         |
 | 169044 | 1308061297 |    17 | 2019-08-23 21:17:33 | FreePBX 1.8                | NL      |               | 77.247.110.113        |
 | 169067 | 3507577202 |     1 | 2019-08-21 21:44:59 | YtsNMLcO                   | US      |               | 209.17.97.114         |
 | 171542 | 1588717792 |     1 | 2019-08-22 00:23:29 | friendly-scanner           | FR      | Paris               | 94.177.232.224        |
 | 171799 | 1053987565 | 12958 | 2019-08-22 00:42:46 | Asterisk PBX               | FR      |               | 62.210.146.237        |
 | 185012 | 1334405281 |     2 | 2019-08-22 00:56:21 | VaxSIPUserAgent/3.5        | FR      |               | 79.137.104.161        |
 | 185327 |   87965663 |    25 | 2019-08-22 07:07:17 | pplsip                     | US      | New York            | 5.62.63.223           |
 | 187784 | 1090198397 |     2 | 2019-08-23 05:41:24 | friendly-scanner           | US      | Fort Lauderdale     | 64.251.27.125         |
 | 187866 | 2731950313 | 12958 | 2019-08-22 04:06:58 | Asterisk PBX               | US      | Provo               | 162.214.64.233        |
 | 202877 | 3107280937 |     1 | 2019-08-22 06:18:42 | friendly-scanner           | NL      |               | 185.53.88.41          |
 | 206150 | 3221475752 |     2 | 2019-08-23 05:25:06 | friendly-scanner           | US      | Buffalo             | 192.3.209.168         |
 | 207013 | 3106407447 |     2 | 2019-08-22 10:45:26 | MizuPhone                  | RU      |               | 185.40.4.23           |
 | 207253 | 3537074702 |     1 | 2019-08-22 11:00:38 | friendly-scanner           | VN      |               | 210.211.122.14        |
 | 207349 | 1611166586 |    75 | 2019-08-23 17:39:14 | Conaito                    | US      | Buffalo             | 96.8.115.122          |
 | 207758 | 2207850059 |     2 | 2019-08-23 07:04:37 | friendly-scanner           | US      | Atlanta             | 131.153.30.75         |
 | 207804 | 3644668999 |     1 | 2019-08-22 11:35:07 | friendly-scanner           | IT      | Ponte San Pietro    | 217.61.60.71          |
 | 208524 | 1066345266 |     1 | 2019-08-22 12:22:59 | friendly-scanner           | US      | Dallas              | 63.143.35.50          |
 | 212570 | 1051564796 |     1 | 2019-08-22 16:38:40 | friendly-scanner           | RU      | Moscow              | 62.173.154.252        |
 | 214517 | 1168277118 |    25 | 2019-08-23 06:00:47 |                      | US      | Dallas              | 69.162.126.126        |
 | 214658 | 3107280948 |     1 | 2019-08-22 18:58:40 | friendly-scanner           | NL      |               | 185.53.88.52          |
 | 215069 | 2382630478 | 12911 | 2019-08-22 19:31:21 | Asterisk PBX               | US      | Provo               | 142.4.14.78           |
 | 229501 | 3507576850 |     1 | 2019-08-22 22:57:06 | VnghmpQZ                   | US      |               | 209.17.96.18          |
 | 229709 | 1446618158 |     1 | 2019-08-22 23:53:04 | friendly-scanner           | BY      | Hrodna              | 86.57.164.46          |
 | 229774 | 1051560609 |     1 | 2019-08-23 00:10:51 | friendly-scanner           | RU      |               | 62.173.138.161        |
 | 229848 |  602425983 |     1 | 2019-08-23 01:44:50 | friendly-scanner           | US      |               | 35.232.74.127         |
 | 229891 | 1051563810 |     1 | 2019-08-23 04:02:30 | friendly-scanner           | RU      | Moscow              | 62.173.151.34         |
 | 229907 | 1551278113 |     1 | 2019-08-23 04:56:25 | vdjbTQHQ                   | LT      |               | 92.118.160.33         |
 | 230115 | 1168270182 |     1 | 2019-08-23 14:01:07 | friendly-scanner           | US      | Dallas              | 69.162.99.102         |
 | 230116 |   87965622 |    31 | 2019-08-23 21:29:53 | pplsip                     | US      | New York            | 5.62.63.182           |
 | 230122 |  392083557 |     1 | 2019-08-23 14:31:38 | friendly-scanner           | US      | Buffalo             | 23.94.184.101         |
 | 230125 | 1870059136 |     1 | 2019-08-23 15:01:48 | friendly-scanner           | IN      |               | 111.118.214.128       |
 +--------+------------+-------+---------------------+----------------------------+---------+---------------------+-----------------------+
 172 rows in set (0.00 sec)

Kamailio Bytes – Geoip2

GeoIP2 allows simple Geo IP location parsing using mmdb files, to allow us to map IP addresses to geographic locations in standardized format.

Getting the GeoIP Data

MaxMind provide GeoIP2 formatted data ready for use, albeit with limited accuracy.

We need to download them from MaxMind and extract them for use, so let’s download the file:

#> wget https://geolite.maxmind.com/download/geoip/database/GeoLite2-City.tar.gz

Next we’ll extract the data:

#> tar -xzvf GeoLite2-City.tar.gz

Next we’ll add the below to our Kamailio config (replace the path to the GeoLite2-City.mmdb to your directory).

loadmodule "geoip2.so"
modparam("geoip2", "path", "/home/ubuntu/GeoLite2-City_20190709/GeoLite2-City.mmdb");

If you’re planning on using this in production you probably want to automate the pulling of this data on a regular basis and keep it in a different directory.

I’ve made a very simple example Kamailio config that shows off some of the features of GeoIP2’s logic and what can be shown, so let’s look at the basics of the module:

if(geoip2_match("$si", "src")){
                xlog("Packet received from IP $si");
                xlog("Country is: $gip2(src=>cc)\n");
}

If we put this at the top of our request_route block every time we recieve a new request we can see the country from which the packet came from.

Let’s take a look at the output of syslog (with my IP removed):

#> tail -f /var/log/syslog
ERROR: <script>: Packet received from IP 203.###.###.###
ERROR: <script>: Country is: AU
ERROR: <script>: City is:  Melbourne
ERROR: <script>: ZIP is:  3004
ERROR: <script>: Regc is:  VIC
ERROR: <script>: Regn is:  Victoria
ERROR: <script>: Metro Code is:  <null>

We can add a bunch more smarts to this and get back a bunch more variables, including city, ZIP code, Lat & Long (Approx), timezone, etc.

        if(geoip2_match("$si", "src")){
                xlog("Packet received from IP $si");
                xlog("Country is: $gip2(src=>cc)\n");
                xlog("City is:  $gip2(src=>city)");
                xlog("ZIP is:  $gip2(src=>zip)");
                xlog("Regc is:  $gip2(src=>regc)");
                xlog("Regn is:  $gip2(src=>regn)");
                xlog("Metro Code is:  $gip2(src=>metro)");

                if($gip2(src=>cc)=="AU"){
                        xlog("Traffic is from Australia");
                }
        }else{
                xlog("No GeoIP Match for  $si");
        }
#> tail -f /var/log/syslog
ERROR: <script>: Packet received from IP ###.###.###.###
ERROR: <script>: Country is: AU
ERROR: <script>: City is:  Melbourne
ERROR: <script>: ZIP is:  3004
ERROR: <script>: Regc is:  VIC
ERROR: <script>: Regn is:  Victoria
ERROR: <script>: Metro Code is:  <null>

Using GeoIP2 you could use different rate limits for domestic users vs overseas users, guess the dialling rules based on the location of the caller and generate alerts if accounts are used outside their standard areas.

We’ll touch upon this again in our next post on RTPengine where we’ll use an RTPengine closes to the area in which the traffic originates.

Full example config on GitHub here.

SMS Security – Banks

The other day I got an SMS from my bank, one of the big 4 Australian Banks.

BANKNAME Alert: Block placed on card ending in XXXX, for suspicious transaction at ‘THING NICK PURCHASED ONLINE’ for $29.00 at 13:56. If genuine, reply ‘Yes’. If Fraud, reply ‘No’.

SMS from bank

They’d detected possible fraud on my card, and were asking me to confirm if it was me or not by texting back.

That is my correct card number, and as it happens I had made an online purchase that was what it was querying.

I was already at my computer, so out of curiosity, opened the SMS Gateway I use, and set the caller ID to be my mobile number (Because spoofing Caller ID is trivial) and replied to the number the bank sent me the SMS from.

My phone beeped again:

Thank you for confirming this transaction is genuine. The block will be removed from your card within the next 20 minutes. No further action is required.

SMS from Bank

So what’s the issue here?

The issue is if someone were to steal your card details, and know your mobile number, they could just keep texting the bank’s verification line the word “yes” from an SMS gateway spoofing your Caller ID, the bank won’t block it.

No system is foolproof, but it seems a bit short sighted by this bank.

Texting back a code would be a better solution, because it would allow you to verify the person texting received the original SMS, or cycling the caller IDs from a big pool would decrease the likelihood of this working

LTE (4G) – EUTRAN – Key Distribution and Hierarchy

We’ve talked a bit in the past few posts about keys, K and all it’s derivatives, such as Kenc, Kint, etc.

Each of these is derived from our single secret key K, known only to the HSS and the USIM.

To minimise the load on the HSS, the HSS transfers some of the key management roles to the MME, without ever actually revealing what the secret key K actually is to the MME.

This means the HSS is only consulted by the MME when a UE/Terminal attaches to the network, and not each time it attaches to different cell etc.

When the UE/Terminal first attaches to the network, as outlined in my previous post, the HSS also generates an additional key it sends to the MME, called K-ASME.

K-ASME is the K key derived value generated by the HSS and sent to the MME. It sands for “Access Security Management Entity” key.

When the MME has the K-ASME it’s then able to generate the other keys for use within the network, for example the Kenb key, used by the eNodeB to generate the keys required for communications.

The USIM generates the K-ASME itself, and as it’s got the same input parameters, the K-ASME generated by the USIM is the same as that generated by the HSS.

The USIM can then give the terminal the K-ASME key, so it can generate the same Kenb key required to generate keys for complete communications.

Showing Kamse generation sequence in LTE.

Image sourced from IMTx: NET02x course on Edx,

LTE (4G) – Ciphering & Integrity of Messages

We’ve already touched on how subscribers are authenticated to the network, how the network is authenticated to subscribers.

Those functions are done “in the clear” meaning anyone listening can get a copy of the data transmitted, and responses could be spoofed or faked.

To prevent this, we want to ensure the data is ciphered (encrypted) and the integrity of the data is ensured (no one has messed with our packets in transmission or is sending fake packets).

Ciphering of Messages

Before being transmitted over the Air interface (Uu) each packet is encrypted to prevent eavesdropping.

This is done by taking the plain text data and a ciphering sequence for that data of the same length as the packet and XORing two.

The terminal and the eNodeB both generate the same ciphering sequence for that data.

This means to get the ciphered version of the packet you simply XOR the Ciphering Sequence and the Plain text data.

To get the plain text from the ciphered packet you simply XOR the ciphered packet and ciphering sequence.

The Ciphering Sequence is made up of parts known only to the Terminal and the Network (eNB), meaning anyone listening can’t deduce the same ciphering sequence.

The Ciphering Sequence is derived from the following input parameters:

  • Key Kenc
  • Packet Number
  • Bearer Number
  • Direction (UL/DL)
  • Packet Size

Is is then ciphered using a ciphering algorithm, 3GPP define two options – AES or SNOW 3G. There is an option to not generate a ciphering sequence at all, but it’s not designed for use in production environments for obvious reasons.

Diagram showing how the ciphering algorithm generates a unique ciphering sequence to be used.

Image sourced from IMTx: NET02x course on Edx,

Ciphering Sequences are never reused, the packet number increments with each packet sent, and therefore a new Cipher Sequence is generated for each.

Someone listening to the air interface (Uu) may be able to deduce packet size, direction and even bearer, but without the packet number and secret key Kenc, the data won’t be readable.

Data Integrity

By using the same ciphering sequence & XOR process outlined above, we also ensure that data has not been manipulated or changed in transmission, or that it’s not a fake message spoofing the terminal or the eNB.

Each frame contains the packet and also a “Message Authentication Code” or “MAC” (Not to be confused with media access control), a 32 bit long cryptographic hash of the contents of the packet.

The sender generates the MAC for each packet and appends it in the frame,

The receiver looks at the contents of the packet and generates it’s own MAC using the same input parameters, if the two MACs (Generated and received) do not match, the packet is discarded.

This allows the receiver to detect corrupted packets, but does not prevent a malicious person from sending their own fake packets,

To prevent this the MAC hash function requires other input parameter as well as the packet itself, such as the secret key Kint, packet number, direction and bearer.

How the MAC is generated in LTE.

Image sourced from IMTx: NET02x course on Edx,

By adding this we ensure that the packet was sourced from a sender with access to all this data – either the terminal or the eNB.

LTE (4G) – Authenticating the Network

In my last post we discussed how the network authenticated a subscriber, now we’ll look at how a subscriber authenticates to a network. There’s a glaring issue there in that the MME could look at the RES and the XRES and just say “Yup, OK” even if the results differed.

To combat this LTE networks have mutual authentication, meaning the network authenticates the subscribers as we’ve discussed, and the subscribers authenticate the network.

To do this our HSS will take the same random key (RAND) we used to authenticate the subscriber, and using a different cryptographic function (called g) take the RAND, the K value and a sequence number called SQN, and using these 3 inputs, generate a new result we’ll call AUTN.

The HSS sends the RAND (same as RAND used to authenticate the subscriber) and the output of AUTN to the MME which forwards it to the eNB to the UE which passes the RAND and AUTH values to the USIM.

The USIM takes the RAND and the K value from the HSS, and it’s expected sequence number. With these 3 values it applies the cryptographic function g generates it’s own AUTN result.

If it matches the AUTN result generated by the HSS, the USIM has authenticated the network.

LTE (4G) – USIM Basics

I’ve been working on private LTE recently, and one of the first barriers you’ll hit will be authentication.

LTE doesn’t allow you to just use any SIM to authenticate to the network, but instead relies on mutual authentication of the UE and the network, so the Network knows it’s talking to the right UE and the UE knows it’s talking to the right network.

So because of this, you have to have full control over the SIM and the network. So let’s take a bit of a dive into USIMs.

So it’s a SIM card right?

As a bit of background; the ever shrinking card we all know as a SIM is a “Universal integrated circuit card” – a microcontroller with it’s own OS that generally has the ability to run Java applets.

One of the Java applets on the card / microcontroller will be the software stack for a SIM, used in GSM networks to authenticate the subscriber.

For UMTS and LTE networks the card would have a USIM software stack allowing it to act as a USIM, the evolved version of the SIM.

Because it’s just software a single card can run both a USIM and SIM software stack, and most do.

As I’m building an LTE network we’ll just talk about the USIM side of things.

USIM’s role in Authentication

When you fire up your mobile handset the baseband module in it communicates with the USIM application on the card.

When it comes time to authenticate to the network, and authenticate the network itself, the baseband module sends the provided challenge information from the network to the USIM which does the crypto magic to generate responses to the authentication challenges issued by the network, and the USIM issues it’s own challenges to the network.

The Baseband module provides the ingredients, but the USIM uses it’s secret recipe / ingredients combo, known only to the USIM and HSS, to perform the authentication.

Because the card challenges the network it means we’ve got mutual authentication of the network.

This prevents anyone from setting up their own radio network from going all Lionel Ritche and saying “Hello, is it me you’re looking for” and having all the UEs attach to the malicious network. (Something that could be done on GSM).

It’s worth noting too that because the USIM handles all this the baseband module, and therefore the mobile handset itself, doesn’t know any of the secret sauce used to negotiate with the network. It just gets the challenge and forwards the ingredients down to the USIM which spits back the correct response to send, without sharing the magic recipe.

This also means operators can implement their own Crypto functions for f and g, so long as the HSS and the USIM know how to generate the RES and AUTN results, it’ll work.

What’s Inside?

Let’s take a look at the information that’s stored on your USIM:

All the GSM stuff for legacy SIM application

Generally USIMs also have the ability to operate as SIMs in a GSM network, after all it’s just a different software stack. We won’t touch on GSM SIMs here.

ICCID

Because a USIM is just an application running on a Universal Integrated Circuit Card, it’s got a ICCID or Universal Integrated Circuit Card ID. Generally this is the long barcode / string of numbers printed on the card itself.

The network generally doesn’t care about this value, but operators may use it for logistics like shipping out cards.

PIN & PUK

PINs and PUKs are codes to unlock the card. If you get the PIN wrong too many times you need the longer PUK to unlock it.

These fields can be written to (when authenticated to the card) but not read directly, only challenged. (You can try a PIN, but you can’t see what it’s set too).

As we mentioned before the terminal will ask the card if that’s correct, but the terminal doesn’t know the PIN either.

IMSI

Each subscriber has an IMSI, an International Mobile Subscriber Identity.

IMSIs are hierarchical, starting with 3 digit Mobile Country Code MCC, then the Mobile Network Code (MNC) (2/3 digits) and finally a Mobile Subscription Identification Number (MSIN), a unique number allocated by the operator to the subscribers in their network.

This means although two subscribers could theoretically have the same MSIN they wouldn’t share the same MNC and MCC so the ISMI would still be unique.

The IMSI never changes, unless the subscriber changes operators when they’ll be issued a new USIM card by the new operator, with a different IMSI (differing MNC).

The MSIN isn’t the same as the phone number / MSISDN Number, but an IMSI generally has a MSISDN associated with it by the network. This allows you to port / change MSISDN numbers without changing the USIM/SIM.

K – Subscriber Key

Subscriber’s secret key known only to the Subscriber and the Authentication Center (AuC/ HSS).

All the authentication rests on the principle that this one single secret key (K) known only to the USIM and the AuC/HHS.

OP – Operator Code

Operator Code – same for all SIMs from a single operator.

Used in combination with K as an input for some authentication / authorisation crypto generation.

Because the Operator Code is common to all subscribers in the network, if this key were to be recovered it could lead to security issues, so instead OPc is generally used.

OPc – Operator Code (Derived)

Instead of giving each USIM the Operator Code a derived operator code can be precomputed when the USIM is written with the K key.

This means the OP is not stored on the USIM.

OPc=Encypt-Algo(OP,Key)

PLMN (Public Land Mobile Network)

The PLMN is the combination of MCC & MNC that identifies the operator’s radio access network (RAN) from other operators.

While there isn’t a specific PLMN field in most USIMs it’s worth understanding as several fields require a PLMN.

HPLMNwAcT (HPLMN selector with Access Technology)

Contains in order of priority, the Home-PLMN codes with the access technology specified.

This allows the USIM to work out which PLMN to attach to and which access technology (RAN), for example if the operator’s PLMN was 50599 we could have:

  • 50599 E-UTRAN
  • 50599 UTRAN

To try 4G and if that fails use 3G.

In situations where operators might partner to share networks in different areas, this could be set to the PLMN of the operator first, then it’s partnered operator second.

OPLMNwACT (Operator controlled PLMN selector with Access
Technology)

This is a list of PLMNs the operator has a roaming agreement with in order of priority and with the access technology.

An operator may roam to Carrier X but only permit UTRAN access, not E-TRAN.

FEHPLMN (Equivalent HPLMN)

Used to define equivalent HPMNs, for example if two carriers merge and still have two PLMNs.

FPLMN (Forbidden PLMN list)

A list of PLMNs the subscriber is not permitted to roam to.

HPPLMN (Higher Priority PLMN search period)

How long in seconds to spend between each PLMN/Access Technology in HPLMNwAcT list.

ACC (Access Control Class)

The ACC allows values from 0-15, and determines the access control class of the subscriber.

In the UK the ACC values is used to restrict civilian access to cell phone networks during emergencies.

Ordinary subscribers have ACC numbers in the range 0 – 9. Higher priority users are allocated numbers 12-14.

During an emergency, some or all access classes in the range 0 – 9 are disabled.

This means service would be could be cut off to the public who have ACC value of 0-9, but those like first responders and emergency services would have a higher ACC value and the network would allow them to attach.

AD (Administrative Data)

Like the ACC field the AD field allows operators to drive test networks without valid paying subscribers attaching to the network.

The defined levels are:

  • ’00’ normal operation.
  • ’80’ type approval operations.
  • ’01’ normal operation + specific facilities.
  • ’81’ type approval operations + specific facilities.
  • ’02’ maintenance (off line).
  • ’04’ cell test operation.

GID 1 / 2 – Group Identifier

Two group identifier fields that allow the operator to identify a group of USIMs for a particular application.

SPN (Service Provider Name)

The SPN is an optional field containing the human-readable name of the network.

The SPN allows MVNOs to provide their own USIMs with their name as the operator on the handset.

ECC (Emergency Call Codes)

Codes up to 6 digits long the subscriber is allowed to dial from home screen / in emergency / while not authenticated etc.

MSISDN

Mobile Station International Subscriber Directory Number. The E.164 formatted phone number of the subscriber.

This is optional, as porting may overwrite this, so it doesn’t always match up.

References:

https://www.etsi.org/deliver/etsi_ts/131100_131199/131102/12.05.00_60/ts_131102v120500p.pdf

Kamailio Bytes – HTable

HTable is Kamailio’s implimentation of Hash Tables a database-like data structure that runs in memory and is very quick.

It’s uses only become apparent when you’ve become exposed to it.

Let’s take an example of protecting against multiple failed registration attempts.

We could create a SQL database called registration attempts, and each time one failed log the time and attempted username.

Then we could set it so before we respond to traffic we query the database, find out how many rows there are that match the username being attempted and if it’s more than a threshold we set we send back a rate limit response.

The problem is that’s fairly resource intensive, the SQL data is read and written from disks and is slow to do both.

Enter HTable, which achieves the same thing with an in-memory database, that’s lightning fast.

Basic Setup

We’ll need to load htable and create an htable called Table1 to store data in:

loadmodule "htable.so"
...
modparam("htable", "htable", "MessageCount=>size=12;initval=0")

Now we’ve initialised a new htable called MessageCount with a size of 12 bytes, and an initial value of 0.

Basic Usage

Now we can put some code in our request_route{} block to increment the MessageCount htable entry each time a new message is received.

request_route {
         $sht(MessageCount=>test) = $sht(MessageCount=>test) + 1;
         xlog("MessageCount is $sht(MessageCount=>test)");
}

$sht(MessageCount=>test) is the logical link to the Htable called MessageCount with a key named test. We’re making that equal itself + 1.

We’re then outputting the content of $sht(MessageCount=>test) to xlog too so we can see it’s value in Syslog.

Now each time a new dialog is started the MessageCount htable key “test” will be incremented.

We can confirm this in Syslog:

ERROR: : MessageCount is 1
ERROR: : MessageCount is 2

We can also check this in kamcmd too:

htable.dump MessageCount

Here we can see in MessageCount there is one key named “test” with a value of 6, and it’s an integer. (You can also store Strings in HTable).

So that’s all well and pointless, but let’s do make it a bit more useful, report on how many SIP transactions we get per IP. Instead of storing our values with the name key “test” we’ll name it based on the Source IP of the message, which lives in Psedovariable $si for Source IP Address.

request_route {
        $sht(MessageCount=>$si) = $sht(MessageCount=>$si) + 1;
        xlog("MessageCount is $sht(MessageCount=>$si)");
}

Now let’s dump the MessageCount again:

htable.dump MessageCount

Done.

Now we can see a count of how many transactions each IP has.

Doing Useful Things

One of the most obvious usage examples of HTable usage is rate limiting authentication attempts. So let’s do that.

We’ll need to create a new htable to contain our AuthCount table:

modparam("htable", "htable", "AuthCount=>size=12;initval=0;autoexpire=360")

I’m calling the boilerplate AUTH block, and I’ve added some logic to increment the AuthCount for each failed auth attempt, and reset it to $null if authentication is successful, thus resetting the counter for that IP Address.

if (is_method("REGISTER") || from_uri==myself) {
        # authenticate requests
        if (!auth_check("$fd", "subscriber", "1")) {
                auth_challenge("$fd", "0");
                $sht(AuthCount=>$si) = $sht(AuthCount=>$si) + 1;
                exit;
        }
        # user authenticated - remove auth header
        if(!is_method("REGISTER|PUBLISH"))
                consume_credentials();
                $sht(AuthCount=>$si) = $null;
}

Now we’ve done that we need to actually stop the traffic if it’s failed too many times. I’ve added the below check into REQINIT block, which I call at the start of processing:

if($sht(AuthCount=>$si) > 5){
        xlog("$si is back again, rate limiting them...");
        sl_send_reply("429", "Rate limiting");
        exit;
}

Now if AuthCount is more than 5, it’ll respond with a Rate Limiting response.

Because in our modparam() setup for AuthCount we set an expiry, after 360 seconds (10 minutes), after 10 minutes all will be forgiven and our blocked UA can register again.

Advanced Usage / Notes

So now we’ve got Kamailio doing rate limiting, it’s probably worth mentioning the Pike module, which can also be used.

You’ll notice if you reboot Kamailio all the htable values are lost, that’s because the hashes are stored in memory, so aren’t persistent.

You have a few options for making this data persistent,

By using DMQ you can Sync data between Kamailio instances including htable values.

modparam("htable", "enable_dmq", 1)

You can also sync it to a database backend:

modparam("htable", "db_url", "mysql://kamailio:kamailiorw@localhost/kamailio")

kamcmd can view, modify & manipulate htable values.

As we’ve seen before we can dump the contents of an htable using:

kamcmd htable.dump MessageCount
This image has an empty alt attribute; its file name is Kamailio-HTable-Dump.png

We can also add new entries & modify existing ones:

kamcmd htable.seti MessageCount ExampleAdd s:999

htable.seti is for setting integer values, we can also use htable.sets to set string values:

htable.sets MessageCount ExampleAdd Iamastring

We can also delete values from here too, which can be super useful for unblocking destinations manually:

htable.delete MessageCount ExampleAdd

As always code from this example is on GitHub. (Please don’t use it in production without modification, Authentication is only called on Register, and it’s just built upon the previous tutorials).

Kamailio documentation for HTable module.

SIP Extensions – RFC4474

Caller-ID spoofing has been an issue in most countries since networks went digital.

SS7 doesn’t provide any caller ID validation facilities, with the assumption that everyone you have peered with you trust the calls from. So because of this it’s up to the originating switch to verify the caller ID selected by the caller is valid and permissible, something that’s not often implemented. Some SIP providers sell the ability to present any number as your CLI as a “feature”.

There’s heaps of news articles on the topic, but I thought it’d be worth talking about RFC4474 – Designed for cryptographically identifying users that originate SIP requests. While almost never used it’s a cool solution to a problem that didn’t take off.

It does this by adding a new header field, called Identity, for conveying a signature used for validating the identity of the caller, and Identity-Info for a reference to the certificate signing authority.

The calling proxy / UA creates a hash of it’s certificate, and inserts that into the SIP message in the Identity header.

The calling proxy / UA also inserts a “Identity-Info” header containing

The called party can then independently get the certificate, create it’s own hash of it, and if they match, then the identity of the caller has been verified.

Kamailio Bytes – Permissions Module

Kamailio’s permissions module is simple to use, and we’ve already touched upon it in the security section in our Kamailio 101 series, but I thought I’d go over some of it’s features in more detail.

At it’s core, Kamailio’s Permissions module is a series of Access Control Lists (ACLs) that can be applied to different sections of your config.

We can manage permissions to do with call routing, for example, is that source allowed to route to that destination.

We can manage registration permissions, for example, is this subnet allowed to register this username.

We can manage URI permissions & address permissions to check if a specific SIP URI or source address is allowed to do something.

We’ll touch on a simple IP Address based ACL setup in this post, but you can find more information in the module documentation itself.

The Setup

We’ll be using a database backend for this (MySQL), setup the usual way.

We’ll need to load the permissions module and setup it’s basic parameters, for more info on setting up the database side of things have a look here.

loadmodule "permissions.so"
...
modparam("permissions", "db_url", DBURL)
modparam("permissions", "db_mode", 1)

Next we’ll need to add some IPs, we could use Serimis for this, or a straight MySQL INSERT, but we’ll use kamctl to add them. (kamcmd can reload addresses but doesn’t currently have the functionality to add them)

kamctl address add 250 10.8.203.139 32 5060 TestServer
kamctl address add 200 192.168.1.0 24 5060 OfficeSubnet

The above example we added a two new address entries,

The first one added a new entry in group 250 of “10.8.203.139”, with a /32 subnet mask (Single IP), on port 5060 with the label “TestServer”,

The second one we added to group 200 was a subnet of 192.168.1.0 with a /24 subnet mask (255 IPs), on port 5060 with the label “OfficeSubnet”

On startup, or when we manually reload the addressTable, Kamailio grabs all the records and stores them in RAM. This makes lookup super fast, but the tradeoff is you have to load the entries, so changes aren’t immediate.

Let’s use Kamcmd to reload the entries and check their status.

kamcmd permissions.addressReload

kamcmd permissions.addressDump

kamcmd permissions.subnetDump

You should see the single IP in the output of the permissions.addressDump and see the subnet on the subnetDump:

Usage

It’s usage is pretty simple, combined with a simple nested if statement.

if (allow_source_address("200")) {
	xlog("Coming from address group 200");
};
if (allow_source_address("250")) {
	xlog("Coming from address group 250");
};

The above example just outputs to xlog with the address group, but we can expand upon this to give us our ACL service.

if (allow_source_address("200")) {
	xlog("Coming from address group 200");
}else if (allow_source_address("250")) {
	xlog("Coming from address group 250");
}else{
        sl_reply("401", "Address not authorised");
        exit;
}

If we put this at the top of our Kamailio config we’ll reply with a 401 response to any traffic not in address group 200 or 250.

Kamailio 101 – Part 9 – Adding Carrier Links

So by now we’ve secured our box and we’re able to route calls between registered endpoints.

Next up we’ll need to add some external connectivity, meaning we can reach destinations that aren’t directly registered on our Kamailio instance.

We’ve signed up with imaginary carrier at “imaginarycarrier.com” so we can make / receive calls from the PSTN using them as a trunk. They’ll be authenticating us based on our Source IP which we’ve let them know.

These days you’d generally authenticate with a carrier by sending a REGISTER message to your carrier so they know your Address on Record, but to keep it simple we won’t be registering to a carrier, as that introduces another Kamailio module.

At the moment, when we receive an INVITE where the destination isn’t registered, we respond with a 404:

sl_reply("404", "User not Registered");     #If looking up location fails reply with 404

But now we’ve got a carrier we can send calls to if the destination isn’t on our PBX, so we won’t need to reply 404 anymore for calls from our users.

So let’s only give the 404 reply to calls in from our carrier (inbound calls), and instead of giving a 404 response to callers from within our organisation, let’s send the calls to the carrier to make an outbound call.

This means calls to any destination that isn’t registered on Kamailio will go to the Carrier.

But we’ll need to still respond with the 404 response if a carrier sends us a call to a destination that isn’t registered, like an inbound call to a user who isn’t registered.

route[ONNETINVITE]{
          if(!lookup("location")){    #Try looking up location
                #If looking up location fails then:
                if(allow_source_address("200")){                            #Check if the request has come from a carrier
                        sl_reply("404", "User not Registered");     #If if it is from a carrier to a destination we don't have registered reply with 404
                        exit;                                       #And exit

                }else{                                              #If it's not from a carrier
                        route(TOCARRIER);                           #Route the call out to the carrier (to make an external call)
                }
        }
            route(RELAY);                  #Relay traffic to endpoint
            exit();                     #Exit

}

Next we’ll need to create our TOCARRIER route,

route[TOCARRIER]{       #Route to send calls to a carrier at 192.168.200.130
        rewritehost("imaginarycarrier.com");    #Rewrite host to be the carrier's IP
        route(RELAY);                   #Route relay

}

So let’s put this together and try and make an outbound call.

Call Flow

Outbound call to Carrier

First we see our UA make the call leg to Kamailio

UA to Kamailio: SIP: INVITE sip:61299999999@kamailio SIP/2.0

Kamailio asks the UA to authenticate itself and send that again, the UA does:

Kamailio to UA: SIP: SIP/2.0 407 Proxy Authentication Required (with challenge)

UA to Kamailio: SIP: INVITE sip:61299999999@kamailio SIP/2.0 (with auth header)

Now Kamailio has authenticated the user it attempts to lookup the location of 61299999999 in the location table.

lookup("location")

As no user is registered as 61299999999 this fails, so Kamailio checks the UA is calling from an address in Address Group 200, which we configured to contain the IP Addresses of all our Carrier’s switches that will be sending us calls.

if(allow_source_address("200"))

It’s not in address group 200, as the from address isn’t one of our Carrier’s IPs, so it ends up at

route(TOCARRIER); 

The route block itself rewrites the host part of the request to be the Carrier’s IP, and then forwards it on to the carrier.

route[TOCARRIER]{       #Route to send calls to a carrier at 192.168.200.130
        rewritehost("imaginarycarrier.com");    #Rewrite host to be the carrier's IP
        route(RELAY);                   #Route relay

}
Kamailio > Carrier: SIP: INVITE sip:[email protected] SIP/2.0

So now the INVITE has been forwarded to imaginarycarrier.com, and because we called route(RELAY); it’ll handle all the in dialog requests.

Inbound call from Carrier

So now we know how an outbound call flows, let’s look at inbound calls from the carrier.

Carrier to Kamailio: INVITE sip:61312341234@kamailio SIP/2.0 

Kamailio responds with a provisional response of 100 Trying

Kamailio to Carrier: SIP: SIP/2.0 100 trying -- your call is important to us

Now Kamailio checks to see the method type – It’s INVITE, and if the source address is in Address Group 200 (As we defined in Part 8), it is, so it calls the route(ONNETINVITE) block.

if(method=="INVITE"){
        if(allow_source_address("200")){        #If from a Carrier IP
                route(ONNETINVITE);          #Call INVITE handling bloc

Once we get to the ONNETINVITE block Kamailio tries to lookup the location, to see if there’s a device registered with a username that matches 61312341234, using:

lookup("location")

There is, so the route(relay) is called, which forwards the INVITE to the IP it has an Address on Record for for 61312341234.

Kamailio > UA: SIP: INVITE sip:[email protected]:50017;ob SIP/2.0

The INVITE is sent to the UA and route(relay) handles all the in dialog requests.

The final scenario we might have is if a call is made to 61299999999 and it’s not registered on Kamailio, let’s take a look at that signalling path:

Carrier > Kamailio: SIP: INVITE sip:61299999999@Kamailio SIP/2.0

The carrier sends the INVITE to Kamailio, Kamailio calls lookup location, which fails to return a location as 61299999999 isn’t registered.

Next we check to see if the call is from a carrier by checking if the source address of the INVITE is equal to address group 200.

if(allow_source_address("200")){                            #Check if the request has come from a carrier
                        sl_reply("404", "User not Registered");     #If if it is from a carrier to a destination we don't have registered reply with 404
                        exit;         

As the source address is in address group 200, the carrier gets the 404 “User not Registered” reply, as we see in the packet capture:

Kamailio > Carrier: SIP: SIP/2.0 404 User not Registered

And that’s it.

I’ve put the complete code from this on Github.

In the next and final part, we’ll recap what we’ve learned.

Next Post – Kamailio 101 – Tutorial 10 – Recap

Other posts in the Kamailio 101 Series:
Kamailio 101 – Tutorial 1 – Introduction

Kamailio 101 – Tutorial 2 – Installation & First Run

Kamailio 101 – Tutorial 3 – Routing Blocks & Structure

Kamailio 101 – Tutorial 4 – Taking Registrations

Kamailio 101 – Tutorial 5 – First Call

Kamailio 101 – Tutorial 6 – Reusing Code

Kamailio 101 – Tutorial 7 – Security in Theory

Kamailio 101 – Tutorial 8 – Security in Practice

Kamailio 101 – Tutorial 9 – Adding Carrier Links

Kamailio 101 – Tutorial 10 – Recap

Message Amplification & UDP Flooding with SIP

SIP was written to be fast and resonably lightweight.

Motorola StarTAC

At the time SIP was created in 1996, Motorola just had launched it’s first flip phone, the web was only 100,000 websites online and I was playing Pokémon.

Security wasn’t so much an afterthought, but rather not something everyone was as conscious of as they are today.

UDP is the protocol of choice for most SIP deployments, which opens it up for Message Amplification attacks.

As the world saw a few years back with DNS Amplification attacks (Good explanation of how Message Amplification works courtesy of Cloudflare), amplification attacks are enabled by DNS requests being smaller than DNS responses, and carrier networks that don’t verify the source of their traffic allowing someone to request a DNS lookup saying they’re from an IP that isn’t theirs, and that IP getting flooded.

SIP is vulnerable to this too, not exactly zero-day exploits, but something that hasn’t been looked at outside of the theoretical sphere, so I thought I’d roll up my sleeves and see how bad it can look.

For starters it’s worth remembering for a Message Amplification attack to work, it’s got to amplify. RF Engineering will teach you that amplification is the ratio of power in to power out, and it’s the same for Message Amplification, the size of the packet we send has to be smaller than the packet received, else we’re just using someone else to do our dirty work, but not amplifying.

Typical Response Sizes

These are small SIP messages I created in Python using sockets, they’re not the absolute smallest you could go, but they were as small as I could go and still get through the basic packet validation / sanity checks.

Some SIP Proxies drop traffic missing required headers while some don’t, I’ve included the required headers.

I’ve pointed the traffic at a Kamailio instance and measured the bytes sent vs bytes returned.

MethodRequest Size (bytes)Response Size (bytes)Gain
OPTIONS1682091.2x
REGISTER3804111x
INVITE1973771.9x
Content Length Mismatch339
400 Missing Required Header in Request300
Max Forwards Exceeded213

So the best we can get is a packet 1.9 times the size of the packet we put in, which means SIP isn’t the best for Message Amplification attacks, but passable, so long as it keeps responding.

INVITE gets our best amplification and we can tune this to get the request smaller.

The Workhorse

Carrier grade SIP servers are pretty powerful machines, able to handle huge amounts of traffic, quite literally hundreds of millions a day, generally split across geographic areas and clustered, all on high quality low loss, low latency IP links.

If you have 20,000 subscribers sending a keep alive every 60 seconds, you’re at 72 million dialogs consisting of two packets each (144,000,000 SIP messages).

So after some stripping down I managed to get a valid INVITE that would be responded too with an auth challenge (407 Proxy Authentication Required) which was 125 bytes on the wire, while the response was 330, giving me a gain of 2.64 times what I put in. (I send 125 bytes, I get back 330)

The Setup

We’ve got 3 IPs we’re dealing with here,

Our victim is on 10.0.1.15. UDP port 5060 won’t even be open for this poor fellow, but he’ll get flooded.

Next is our attacker who’s machine is on 10.0.1.12, but claiming their source IP is 10.0.1.15 (the Victim’s IP)

From here our attacker will be sending SIP traffic to 10.0.1.110 (our “carrier” / SIP server), which will send it’s responses to the victim. I’ve spun up an Asterisk instance because it’s the voice eng version of sticky tape, I’d love to test this against something a Broadsoft platform, but licences are hard to come by.

I setup the Asterisk instance to be single threaded, on a box with just enough resources to run, to try this small scale.

I wrote a threaded Python script that will ramp up the number of messages exponentially, we’ll start by sending one message per second, then two messages per second, and so on.

And we’ll do this until something breaks.

The Results

In short – inconclusive at first, but kinda scary after that.

Asterisk died really quickly. “Exceptionally long queue length” popped up after the first second. Interestingly, the box eventually came good and actually replied to every one of our requests, and even sent a BYE. Cute.

So I modified the script to be a bit less aggressive, a random wait time between 0 and 1 seconds between loops for each thread.

Struggling under the load.

I got about 60 seconds in before Asterisk really stopped responding to traffic.

So I tweaked my script again, enabled multi threading on the “carrier” and tried again.

So here’s the best rate (packets per second) I could get after a lot of tweaking:

Packets per Second – Red = Sent, Green = Received

Peak Receive: 14,000 packets per second
Peak Send: 20,000 packets per second

In terms of packet size – what we really care about, the results were actually pretty promising:

Click for full size

Peak receive rate of 67Mbps, for which we were putting in ~25Mbps.

So can SIP be used for message amplification attacks? Sure.

Is it particulary practical? Not really. There’s easier targets out there for the time being, so VoIP will be spared the worst of it.

But for a carrier weaponisation of carrier SIP server should be a real fear.

Protection for Carriers

Don’t use UDP for your SIP traffic.

It’s easier said than done, I know… But the reasoning for putting SIP on UDP was primarily speed and limited bandwidth, but with more and more fibre in the ground it’s no longer the case.

SIP over TCP (better yet use it as an excuse to move to TLS), will protect you from some of these attacks.

Flood protection is built into most SBCs these days, if your box is being used to hit a specific target, the source IP will be masquerading as the target. So blocking that and not responding is your best bet. Lots of SBCs still respond with a 4xx “Rate Limiting” response instead of just dropping the traffic, ideally you’d disable the nice “Rate liming” response and just drop the traffic.

Traffic modeling, GeoIP blocking and rate limiting per IP & destination port will also help, as well as monitoring.

Ultimately you can’t stop spoofed UDP traffic coming into your network, but you can stop UDP traffic leaving your network, and if everyone did that we wouldn’t be in this mess.

UDP spoofing is made possible by networks that don’t verify that the traffic that’s leaving their network is traffic that is sourced from your network.

Your core routers know what IPs are assigned to your network, and should be configured to drop traffic that’s leaving the network but not coming from those IPs.

IETF came up with this solution, and it’s built into all major router OSes:

Network Ingress Filtering:
Defeating Denial of Service Attacks which employ
IP Source Address Spoofing


Wireshark trace showing a "401 Unauthorized" Response to an IMS REGISTER request, using the AKAv1-MD5 Algorithm

All About IMS Authentication (AKAv1-MD5) in VoLTE Networks

I recently began integrating IMS Authentication functions into PyHSS, and thought I’d share my notes / research into the authentication used by IMS networks & served by a IMS capable HSS.

There’s very little useful info online on AKAv1-MD5 algorithm, but it’s actually fairly simple to understand.

RFC 2617 introduces two authentication methods for HTTP, one is Plain Text and is as it sounds – the password sent over the wire, the other is using Digest scheme authentication. This is the authentication used in standard SIP MD5 auth which I covered ages back in this post.

Authentication and Key Agreement (AKA) is a method for authentication and key distribution in a EUTRAN network. AKA is challenge-response based using symmetric cryptography. AKA runs on the ISIM function of a USIM card.

I’ve covered the AKA process in my post on USIM/HSS authentication.

The Nonce field is the Base64 encoded version of the RAND value and concatenated with the AUTN token from our AKA response. (Often called the Authentication Vectors).

That’s it!

It’s put in the SIP 401 response by the S-CSCF and sent to the UE. (Note, the Cyperhing Key & Integrity Keys are removed by the P-CSCF and used for IPsec SA establishment.

Wireshark trace showing a "401 Unauthorized" Response to an IMS REGISTER request, using the AKAv1-MD5 Algorithm
Click for Full Size version of this image

Kamailio 101 – Part 8 – Security in Practice

In our last post we went over all the theory, now let’s get started implementing these security features.

Kamailio’s core is a basis to start from, but many common needs are covered by special modules that we need to load to handle certain scenarios.

In order to authenticate traffic, we’ll need to have a source of authentication info (auth_db module) and authorization (permissions module). For this we’ll be using MySQL (although you could use text files, PostGres, etc) to store both sets of data, and using phpMyAdmin to make everything a bit more accessible.

We’ll build upon our last tutorial but we’ll install MySQL and phpMyAdmin:

apt-get install mysql-server phpmyadmin

After following along the install prompts we’ll setup our database connection.

Each of the two modules we’ll be using (auth_db and permissions) require a database source. In each we could specify our database info but instead we’ll create a new variable and fill it with our database info so we only need to update it in one place.

Let’s setup a new MySQL user for our Kamailio instance (in production you’d only grant privileges on the DB we’re going to use):

mysql> CREATE USER 'kamailio'@'localhost' IDENTIFIED BY 'my5yhtY7zPJzV8vu';

mysql> GRANT ALL PRIVILEGES ON * . * TO 'kamailio'@'localhost';

mysql> FLUSH PRIVILEGES;

We’ll now use the kamdbctl tool, bundled with Kamailio to create the database tables for us:

kamdbctl create

You’ll be asked for the root password for MySQL and if you want some optional tables (we don’t just yet) and presto, all the tables are now created!

kamdbctl - Creating database tables
Using kamdbctl

We can now login with phpMyAdmin and see the tables we just added:

Adding Database Connectivity to Kamailio

The example config is designed to be nice and modular, so by simply un-commenting the WITH_MYSQL variable and setting the DBURL variable we’ll have set our MySQL database up for the modules we need.

We’ll change:

# *** To enable mysql:
#     - define WITH_MYSQL
#

To:

# *** To enable mysql:
#!define WITH_MYSQL
#

So now we’ve defined an variabled named WITH_MYSQL

You’ll see later in the config there’s conditional (if statement) that looks at if WITH_MYSQL has been defined:

# *** Value defines - IDs used later in config
#!ifdef WITH_MYSQL
# - database URL - used to connect to database server by modules such
#       as: auth_db, acc, usrloc, a.s.o.
#!ifndef DBURL
#!define DBURL "mysql://kamailio:my5yhtY7zPJzV8vu@localhost/kamailio"
#!endif
#!endif

We’ll change the !define DBURL to include the password in the Database connection string,

It breaks up like this:

mysql:// is the database type (you could use text:// for text based DB or pgsql:// for Postgres)

First part is the username:password@host/table

In this case, our username is kamailio, our password is the one we created (my5yhtY7zPJzV8vu), our host is localhost and our table is kamailio

Adding IP Authentication & Challenge / Response Auth

Like we defined the #!define WITH_MYSQL we’ll define two other blocks to add Authentication:

# *** To enable authentication execute:
#     - enable mysql
#     - define WITH_AUTH
#     - add users using 'kamctl'
#
# *** To enable IP authentication execute:
#     - enable mysql
#     - enable authentication
#     - define WITH_IPAUTH

We’ll change to:

# *** To enable authentication execute:
#     - enable mysql
#!define WITH_AUTH
#     - add users using 'kamctl'
#
# *** To enable IP authentication execute:
#     - enable mysql
#     - enable authentication
#!define WITH_IPAUTH

Adding Users & IP Addresses

Now we’ve gone and added these blocks to the code we’ll go about adding some users and IP addresses, to do this we’ll use the kamctl tool.

kamctl is another tool (like kamcmd) used to modify / change the Kamailio config, it’s a shell wrapper for managing Kamailio database among other things.

We do need to setup a few things to get the kamctl working, and to do that we’ve got to edit the kamctlrtc file in the /etc/kamailio directory, to include the details of the database we just setup.

Editing kamctlrc
Edit the file to define our database details

Adding Users

Now we can get to work adding some users from the command line:

kamctl add 61312341234 supersecretpassword
Adding users with Kamctl

Here we can see adding a user with the username 61312341234 and the password supersecretpassword. These will makeup the username and password we’ll have on our SIP endpoints. (We’ll make them match the phone numbers of our trunks to make the routing easier down the track)

We’ll add another user so we can make calls between users when we’re testing later too, we’ll add them using kamctl add USERNAME PASSWORD again.

Now if we have a look in the subscriber table in phpMyAdmin we can see the users we created:

Kamailio Subscribers Table - Users in our databasea

Adding Carrier IPs

Next we’ll add the IP Address our carrier is going to send us calls from, so we can allow them call our users.

Kamailio’s permissions module relies on address groups – This means we could have address group (we’ll just use the number 200), which we decide is for our carriers, and add all our carrier’s IP addresses in here, without needing to put each of them into the config.

We could create another address group 300 and put the subnets of our offices in there and only allow REGISTER messages from IPs in those groups.

We’ll go through how to use the groups later on, but for now we’ll just add a new IP address to group 200, with a /32 subnet mask, on port 5060 with the name “Carrier IP address” so we know what it is.

kamctl address add 200 10.0.1.102 32 5060 "Carrier IP address"

Now if we have a look in the permissions table in Kamailio you’ll see the info we just added.

By now you’ve probably caught on to the fact kamctl is just a command line tool to add data to the database, but it’s useful none the less.

Kamailio Permissions - Address Table (Authenticated by IP Address)

The final step is to reload the permissions address table – This is done when we restart but it’s good to know you can update it without a restart.

kamctl address reload

Adding authentication / authorization to REGISTER messages

Now let’s actually put this all into practice, the first thing we’ll do is call the REQINIT route to make sure our traffic if (reasonably) clean and take care of the basics.

request_route {

        route(REQINIT);         #Call REQINIT (Request Initial) route to filter out the worst of the bad traffic and take care of the basics.

Next we’ll setup how we handle REGISTER traffic, adding an auth challenge and only saving the location if the UA successfully responds to the challenge.

if(method=="REGISTER"){  # authenticate requests
if (!auth_check("$fd", "subscriber", "1")) { #If credentials don't match what we have in Subscriber table
   auth_challenge("$fd", "0");          #Send an Auth Challenge
   exit;                                #Stop processing
 }

save("location");                   #Save the location as an AoR
exit;                               #Stop processing
        }

This may all seem a bit backward, but this is an example from the Kamailio devs we’re using, so it shows “the right way” of doing this, let’s break it down.

We know our if(method==”REGISTER”) means we’ll only do this check for REGISTER messages.

The auth_check checks to see if the presented credentials in our auth header are correct. For the first INVITE we don’t have an auth header so it’s not correct, and if we have an invalid password it’s also not correct. You’ll notice it’s prefixed with a if(!auth_check) meaning this if conditional block is only called if it fails the authentication check, and if we do fail the authentication check we issue an auth_challenge to generate a 401 response with an authentication header and send it back to the UA. Then we exit (stop processing).

As the above had an exit we know we’ll only hit blocks below if our credentials are correct, otherwise we’d just get the auth_challenge and exit from the auth_check block.

So as we know these credentials are correct we’ll save the location as an address on record using the save(“location”) function and exit.

So that’s our REGISTER block now handling & requiring authentication, now after restarting Kamailio we can register SIP devices with the username and password we setup, but if we get the username or password wrong, we’ll get rejected.

We can add extra users using the kamctl add command we touched on earlier.

Authorising / Authenticating INVITE messages

INVITE messages are used to setup sessions (calls), so it’s important we secure this too. At this point we’re authenticating to REGISTER, but not create a call (INVITE).

First let’s add a simple check to see if the INVITE has come from the IP of one of the carriers we defined earlier.

For this we’ll use the allow_source_address() command to see if the source address matches what we defined earlier using kamctl address add to address group 200 in the MySQL database.

    if(method=="INVITE"){
        if(allow_source_address("200")){        #If from a Carrier IP
            if(!lookup("location")){    #Try looking up location
                            sl_reply("404", "User not Registered");     #If looking up location fails reply with 404
                            exit;                                       #And exit
            }

            t_relay();                  #Relay traffic to endpoint
            exit();                     #Exit
        }else{
                sl_reply("403", "Nope. Don't know who you are");
                }
}

So we’ve got a simple if for if the source address is in group 200.

Presto, this works for calls from carriers coming in to registered endpoints! We can get inbound calls.

Small catch is our users can’t dial each other any more, as their IP isn’t in address group 200, they just get the 403 “Don’t know who you are” response.

Now we could go and add the subnet where our users are located, but then there’d be no point in using passwords at all. But before we do this let’s create a new routing module, called INVITE to keep everything pretty.

At the very bottom of our config we’ll add

route[ONNETINVITE]{
          if(!lookup("location")){    #Try looking up location
                            sl_reply("404", "User not Registered");     #If looking up location fails reply with 404
                            exit;                                       #And exit
            }

            t_relay();                  #Relay traffic to endpoint
            exit();                     #Exit

}

And then we’ll remove most of the code in our if(method==”INVITE”){ block and replace it with this:

if(method=="INVITE"){
 if(allow_source_address("200")){        #If from a Carrier IP
    route(ONNETINVITE);          #Call INVITE handling bloc
  }else{
    sl_reply("403", "Nope. Don't know who you are");
    }
 }

Now we’ve made it so we just call ROUTE(INVITE); when we have an INVITE we’ve authenticated. This will save us a lot of extra code when we add our checks to see if the call is from a user we recognize, instead of running through the lookup(“location”) code and relying, we’ll just call route(ONNETINVITE); when we’re happy we know who they are and off we go.

if(method=="INVITE"){
if(allow_source_address("200")){        #If from a Carrier IP
     route(ONNETINVITE);          #Call INVITE handling bloc
}else{
     if (!auth_check("$fd", "subscriber", "1")) { #If credentials don't match what we have in Subscriber table
         auth_challenge("$fd", "0");          #Send an Auth Challenge
         exit;                                #Stop processing
      }
          route(ONNETINVITE);                  #Call invite handling block
 }
}

You may recognize the !auth_check blocks as the same code we used for authenticating REGISTER messages, we’re using it again as it’s the same auth mechanism.

If we pass it we call the route(ONNETINVITE);

If we look at the packet captures we can see our INVITE gets a 407 “Proxy Authentication required” response back from Kamailio.

Kamailio - 401 & REGISTER

And the UA then resends the INVITE with an authentication header with correct username and password and we’re on our way!

And that’s it! Phew.

In production we’d want to handle other types of messages that we’d also want to authenticate, we’ll talk about that further down the line, but keep in mind every feature you add what the security ramifications might be.

Next up we’ll use our new found sense of security to add the ability to call numbers off-net (on the PSTN) via a SIP provider!

Here’s a complete copy of my running code for your reference.

Next Post – Kamailio 101 – Tutorial 9 – Adding Carrier Links | This Post – Kamailio 101 – Tutorial 8 – Security in Practice | Previous Post – Kamailio 101 – Tutorial 7 – Security in Theory| Previous Post – Kamailio 101 – Tutorial 6- Reusing Code| Kamailio 101 – Tutorial 5 – First Call| Kamailio 101 – Tutorial 4- Taking Registrations | Kamailio 101 – Tutorial 2 – Installation & First Run | Kamailio 101 – Tutorial 1 – Introduction