Troubleshooting · Kamailio 5.7 / 5.8

friendly-scanner and SIP brute-force attacks hitting Kamailio

Any public SIP port gets scanned within minutes. The aim is to spend as little CPU and memory on scanners as possible, tell them nothing, and ban anyone guessing passwords.

Production down right now? Call +44 161 820 1210. We answer 24/7.

What you're seeing

sngrep / log
REGISTER sip:203.0.113.10 SIP/2.0
Via: SIP/2.0/UDP 192.0.2.66:5160;branch=z9hG4bK-3521960414
From: "100" <sip:[email protected]>;tag=3434313732
User-Agent: friendly-scanner

ALERT: pike blocking REGISTER from sip:[email protected] (IP:192.0.2.66:5160)

You'll see bursts of REGISTER/INVITE/OPTIONS to numeric users, often thousands a minute from one address, and CDRs with international INVITEs from unknown sources if something is open.

Likely causes

Most common first.

  1. Scanners get real replies. Every 401, 403 or 404 confirms a live SIP server and helps enumerate users. Known scanner user agents should be dropped silently, before any transaction or database lookup.
  2. No per-IP rate limit. The pike module counts requests per source IP over a sampling window and flags sources over the threshold. Without it one address can send thousands of requests a second and every one gets processed.
  3. Failed authentication isn't counted. Password guessing uses the scanner's normal rate, so pike rarely catches it. Count auth_check() failures per source IP in an htable and ban after a threshold.
  4. Bans live only inside Kamailio. An htable ban still costs a packet receive and parse per request. For persistent attackers, push the ban to the firewall with fail2ban (or nftables sets) keyed on Kamailio's log line.

How to fix

1. Drop scanners and flooders in REQINIT

kamailio.cfg
loadmodule "pike.so"
loadmodule "htable.so"
loadmodule "ipops.so"

modparam("pike", "sampling_time_unit", 2)
modparam("pike", "reqs_density_per_unit", 16)
modparam("pike", "remove_latency", 4)

modparam("htable", "htable", "ipban=>size=8;autoexpire=300;")
# initval=0 makes $shtinc() count from zero; without it a missing key stays $null
modparam("htable", "htable", "authfail=>size=8;autoexpire=900;initval=0;")

route[REQINIT] {
    set_reply_no_connect();
    force_rport();

    # carriers and PBXs (198.51.100.0/24 here) are never rate-limited or banned
    if (!is_in_subnet("$si", "198.51.100.0/24")) {
        if ($sht(ipban=>$si) != $null) {
            exit;
        }
        if (!pike_check_req()) {
            xlog("L_ALERT", "ALERT: pike blocking $rm from $fu (IP:$si:$sp)\n");
            $sht(ipban=>$si) = 1;
            exit;
        }
        if ($ua =~ "friendly|scanner|sipcli|sipvicious|VaxSIPUserAgent|pplsip|sipsak") {
            $sht(ipban=>$si) = 1;
            exit;
        }
    }

    # ... keep the rest of the stock REQINIT: max-forwards check,
    # OPTIONS keepalive reply and sanity_check() ...
}

This is the stock 5.8 WITH_ANTIFLOOD logic, extended to ban scanner user agents and to exempt your own networks. exit without a reply means the scanner gets nothing back. route(REQINIT) is the first thing request_route runs, before t_check_trans() or anything that creates state.

2. Ban after repeated authentication failures

kamailio.cfg
route[AUTH] {
    if (is_method("REGISTER") || from_uri == myself) {
        if (!auth_check("$fd", "subscriber", "1")) {
            $var(rc) = $rc;
            # -2 wrong password, -3 unknown user
            if ($var(rc) == -2 || $var(rc) == -3) {
                $var(fails) = $shtinc(authfail=>$si);
                if ($var(fails) >= 10) {
                    xlog("L_ALERT", "ALERT: auth ban $si after $var(fails) failures\n");
                    $sht(ipban=>$si) = 1;
                    exit;
                }
            }
            auth_challenge("$fd", "0");
            exit;
        }
        if (!is_method("REGISTER|PUBLISH")) {
            consume_credentials();
        }
    }
    # never drop this: without it the proxy relays for anyone
    if (!(uri == myself || from_uri == myself)) {
        sl_send_reply("403", "Not relaying");
        exit;
    }
}

3. Push bans to the firewall

/etc/fail2ban/filter.d/kamailio.conf
[Definition]
failregex = ALERT: pike blocking .* \(IP:<HOST>:\d+\)
            ALERT: auth ban <HOST> after
/etc/fail2ban/jail.d/kamailio.local
[kamailio]
enabled  = true
filter   = kamailio
logpath  = /var/log/kamailio.log
maxretry = 1
bantime  = 3600
port     = 5060,5061
protocol = all

Point logpath at wherever syslog writes Kamailio's messages. Keep is_in_subnet() and fail2ban's ignoreip in step with each other so you never ban your own carriers.

Still stuck?

Get a Kamailio engineer on it now

Emergency Kamailio support is £120/hour, 24/7. You talk to an engineer who has run Kamailio in production since 2005, not a ticket queue, and we'll sign your NDA before you send configs or traces. See pricing, or how our Kamailio support works.

Call us Email