Kamailio es un proxy SIP derivado de un proyecto anterior llamado OpenSER. A parte de ser Open Source, es una magnífica herramienta, indispensable si deseas montar un sistema de VoIP medianamente grande, es decir, llegando más allá de las típicas centralitas Asterisk, FreeSwitch o similares que nos permiten gestionar una oficina con X extensiones.
Aprovecho para mandar un saludo a mi compañero Victor Seva (@linuxmaniac) que desde hace poco pertenece al equipo de desarrolladores de Kamailio. 😉
Como comentaba, un proxy SIP nos permite realizar muchísimas cosas, como validar usuarios, reenviar el tráfico a servidores SIP, gestionar llamadas, etc. En este caso lo vamos a utilizar con las mínimas opciones. Simplemente le permitiremos recibir y emitir llamadas, pero con alguna que otra peculiaridad. Nuestra finalidad será la de controlar los escanéos que nos hagan e interceptar todas las llamadas que intenten realizarse a través del sistema, es decir, colocar un honeypot para cazar listillos.
Evidentemente para entender todo esto hay que tener unos mínimos conocimientos de SIP. Si quieres instalar un Kamailio, puedes consultar la documentación aquí: http://www.kamailio.org/wiki/
En primer lugar vamos a editar el fichero kamailio.cfg y vamos a añadir a la ruta REQINIT lo siguiente:
if($ua == "friendly-scanner" || $ua == "sundayddr" || $ua == "sip-scan" || $ua == "iWar" || $ua == "sipsak") { xlog("L_ALERT", "Attack attempt! IP:$si:$sp - R:$ruri - F:$fu - T:$tu - UA:$ua - $rm\n"); }
Lo que conseguimos con esto es que nos alerte ante un escaneo usando los programas típicos. Por ejemplo, con SIPVicious:
$ python svmap.py 192.168.2.18 | SIP Device | User Agent | Fingerprint | --------------------------------------------------------------------- | 192.168.2.18:5060 | kamailio (4.0.1 (x86_64/linux)) | disabled |
En el log del sistema (/var/log/kamailio) vemos esto:
ALERT: <script>: Attack attempt! IP:192.168.2.10:5060 - R:sip:100@192.168.2.18 - F:sip:100@1.1.1.1 - T:sip:100@1.1.1.1 - UA:friendly-scanner - OPTIONS
De esta forma nos enteraremos enseguida si alguien nos realiza un escaneo con svmap. De todas formas, siempre viene bien ocultar nuestra versión de kamailio. así que en el fichero de configuración añadiremos:
####### Global Parameters ######### user_agent_header="User-Agent: kamailio 1.0" server_header="User-Agent: kamailio 1.0"
Y si repetimos la búsqueda:
$ python svmap.py 192.168.2.18 | SIP Device | User Agent | Fingerprint | -------------------------------------------------- | 192.168.2.18:5060 | kamailio 1.0 | disabled |
Una vez que somos capaces de detectar los escaneos, vamos a jugar un poco con el atacante. Lo primero de todo, le vamos a permitir registrarse en nuestro sistema con cualquier usuario y con cualquier contraseña. Para ello, en la ruta REQINIT añadiremos:
if (is_method("REGISTER")) { xlog("L_INFO", "Get Register for user $fU (IP: $si)\n"); # aceptamos todas las peticiones de registro sin verificar la contraseña sl_send_reply("200","OK"); }
Lo que hacemos con esto es permitir el registro de cualquier usuario con cualquier contraseña. Para ello responderemos con un ‘200 OK’, permitiéndoles siempre registrarse.
Si pasamos svcrack podemos ver que siempre ‘descubre’ la contraseña de cualquier usuario:
$ python svcrack.py -u100 -r1-100 -z4 192.168.2.18 | Extension | Password | ----------------------------- | 100 | [no password] | $ python svcrack.py -u324324243 -r1-100 -z4 192.168.2.18 | Extension | Password | ----------------------------- | 324324243 | [no password] |
El motivo por el que dice que no tiene contraseña es porque SIPVicious lo primero que prueba ante un ataque por fuerza bruta, es la contraseña vacía.
Si intentamos registrar un teléfono, veremos que se conecta sin problemas. Y en el log aparecerá algo así:
INFO: <script>: Get Register for user pepelux (IP: 192.168.2.16) INFO: <script>: Get Register for user 200 (IP: 192.168.2.17)
Tras esto, vamos a registrarnos con el usuario 100 (por ejemplo) y lo que haremos será desviar todas las llamadas hacia nosotros. En la ruta REQINIT añadimos:
if (is_method("INVITE")) { xlog("L_INFO", "$fU is trying to call to $rU\n"); $rU = "100"; # siempre nos llamará a nosotros xlog("L_INFO", "Sending call to $rU\n"); }
Y cuando se intente realizar una llamada a cualquier número nos sonará a nosotros:
INFO: <script>: pepelux (with IP:192.168.2.16) is trying to call to 666666666 INFO: <script>: Sending call to 100 INFO: <script>: 200 (with IP:192.168.2.17) is trying to call to 123456 INFO: <script>: Sending call to 100
¿Qué tal si en lugar de desviar la llamada a la extensión 100 la desviamos al número 091? 🙂
Y el fichero completo (kamailio.cfg) que he usado para las pruebas:
#!KAMAILIO ####### Include Local Config If Exists ######### import_file "kamailio-local.cfg" ####### Defined Values ######### #!define FLT_ACC 1 #!define FLT_ACCMISSED 2 #!define FLT_ACCFAILED 3 #!define FLT_NATS 5 #!define FLB_NATB 6 #!define FLB_NATSIPPING 7 ####### Global Parameters ######### user_agent_header="User-Agent: kamailio 1.0" server_header="User-Agent: kamailio 1.0" ### LOG Levels: 3=DBG, 2=INFO, 1=NOTICE, 0=WARN, -1=ERR #!ifdef WITH_DEBUG debug=4 log_stderror=yes #!else debug=2 log_stderror=no #!endif memdbg=5 memlog=5 log_facility=LOG_LOCAL0 fork=yes children=4 port=5060 tcp_connection_lifetime=3605 ####### Modules Section ######## # set paths to location of modules (to sources or installation folders) #!ifdef WITH_SRCPATH mpath="modules_k:modules" #!else mpath="/usr/local/lib/kamailio/modules_k/:/usr/local/lib64/kamailio/modules/" #!endif #!ifdef WITH_MYSQL loadmodule "db_mysql.so" #!endif loadmodule "mi_fifo.so" loadmodule "kex.so" loadmodule "corex.so" loadmodule "tm.so" loadmodule "tmx.so" loadmodule "sl.so" loadmodule "rr.so" loadmodule "pv.so" loadmodule "maxfwd.so" loadmodule "usrloc.so" loadmodule "registrar.so" loadmodule "textops.so" loadmodule "siputils.so" loadmodule "xlog.so" loadmodule "sanity.so" loadmodule "ctl.so" loadmodule "cfg_rpc.so" loadmodule "mi_rpc.so" loadmodule "acc.so" # ----------------- setting module-specific parameters --------------- # ----- mi_fifo params ----- modparam("mi_fifo", "fifo_name", "/tmp/kamailio_fifo") # ----- tm params ----- # auto-discard branches from previous serial forking leg modparam("tm", "failure_reply_mode", 3) # default retransmission timeout: 30sec modparam("tm", "fr_timer", 30000) # default invite retransmission timeout after 1xx: 120sec modparam("tm", "fr_inv_timer", 120000) # ----- rr params ----- # add value to ;lr param to cope with most of the UAs modparam("rr", "enable_full_lr", 1) # do not append from tag to the RR (no need for this script) modparam("rr", "append_fromtag", 0) # ----- registrar params ----- modparam("registrar", "method_filtering", 1) /* uncomment the next line to disable parallel forking via location */ # modparam("registrar", "append_branches", 0) /* uncomment the next line not to allow more than 10 contacts per AOR */ #modparam("registrar", "max_contacts", 10) # max value for expires of registrations modparam("registrar", "max_expires", 3600) # set it to 1 to enable GRUU modparam("registrar", "gruu_enabled", 0) # ----- acc params ----- /* what special events should be accounted ? */ modparam("acc", "early_media", 0) modparam("acc", "report_ack", 0) modparam("acc", "report_cancels", 0) /* by default ww do not adjust the direct of the sequential requests. if you enable this parameter, be sure the enable "append_fromtag" in "rr" module */ modparam("acc", "detect_direction", 0) /* account triggers (flags) */ modparam("acc", "log_flag", FLT_ACC) modparam("acc", "log_missed_flag", FLT_ACCMISSED) modparam("acc", "log_extra", "src_user=$fU;src_domain=$fd;src_ip=$si;" "dst_ouser=$tU;dst_user=$rU;dst_domain=$rd") modparam("acc", "failed_transaction_flag", FLT_ACCFAILED) /* enhanced DB accounting */ ####### 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 { # per request initial checks route(REQINIT); # NAT detection route(NATDETECT); # CANCEL processing if (is_method("CANCEL")) { if (t_check_trans()) { route(RELAY); } exit; } # handle requests within SIP dialogs route(WITHINDLG); ### only initial requests (no To tag) t_check_trans(); # record routing for dialog forming requests (in case they are routed) # - remove preloaded route headers remove_hf("Route"); if (is_method("INVITE|SUBSCRIBE")) record_route(); # dispatch requests to foreign domains route(SIPOUT); # handle registrations route(REGISTRAR); if ($rU==$null) { # request with no Username in RURI sl_send_reply("484","Address Incomplete"); exit; } # user location service route(LOCATION); } route[RELAY] { # enable additional event routes for forwarded requests # - serial forking, RTP relaying handling, a.s.o. if (is_method("INVITE|BYE|SUBSCRIBE|UPDATE")) { if(!t_is_set("branch_route")) t_on_branch("MANAGE_BRANCH"); } if (is_method("INVITE|SUBSCRIBE|UPDATE")) { if(!t_is_set("onreply_route")) t_on_reply("MANAGE_REPLY"); } if (is_method("INVITE")) { if(!t_is_set("failure_route")) t_on_failure("MANAGE_FAILURE"); } if (!t_relay()) { sl_reply_error(); } exit; } # Per SIP request initial checks route[REQINIT] { # Si detectamos el User-Agent de un escáner conocido, mostramos una alerta if($ua == "friendly-scanner" || $ua == "sundayddr" || $ua == "sip-scan" || $ua == "iWar" || $ua == "sipsak") { xlog("L_ALERT", "Attack attempt! IP:$si:$sp - R:$ruri - F:$fu - T:$tu - UA:$ua - $rm\n"); } # Al recibir un intento de registro, devolvemos siempre un OK if (is_method("REGISTER")) { xlog("L_INFO", "Get Register for user $fU (IP: $si)\n"); if ($fU != "100") sl_send_reply("200","OK"); } # Al recibir un intento de llamada, modificamos el destino para que siempre llame a la extensión 100 if (is_method("INVITE")) { xlog("L_INFO", "$fU (with IP:$si) is trying to call to $rU\n"); $rU = "100"; xlog("L_INFO", "Sending call to $rU\n"); } if (!mf_process_maxfwd_header("10")) { sl_send_reply("483","Too Many Hops"); exit; } if(!sanity_check("1511", "7")) { xlog("Malformed SIP message from $si:$sp\n"); exit; } } # Handle requests within SIP dialogs route[WITHINDLG] { if (has_totag()) { # sequential request withing a dialog should # take the path determined by record-routing if (loose_route()) { route(DLGURI); if (is_method("BYE")) { setflag(FLT_ACC); # do accounting ... setflag(FLT_ACCFAILED); # ... even if the transaction fails } else if ( is_method("ACK") ) { # ACK is forwarded statelessy route(NATMANAGE); } else if ( is_method("NOTIFY") ) { # Add Record-Route for in-dialog NOTIFY as per RFC 6665. record_route(); } route(RELAY); } else { if (is_method("SUBSCRIBE") && uri == myself) { # in-dialog subscribe requests exit; } if ( is_method("ACK") ) { if ( t_check_trans() ) { # no loose-route, but stateful ACK; # must be an ACK after a 487 # or e.g. 404 from upstream server route(RELAY); exit; } else { # ACK without matching transaction ... ignore and discard exit; } } sl_send_reply("404","Not here"); } exit; } } # Handle SIP registrations route[REGISTRAR] { if (is_method("REGISTER")) { if(isflagset(FLT_NATS)) { setbflag(FLB_NATB); # uncomment next line to do SIP NAT pinging ## setbflag(FLB_NATSIPPING); } if (!save("location")) sl_reply_error(); exit; } } # USER location service route[LOCATION] { $avp(oexten) = $rU; if (!lookup("location")) { $var(rc) = $rc; t_newtran(); switch ($var(rc)) { case -1: case -3: send_reply("404", "Not Found"); exit; case -2: send_reply("405", "Method Not Allowed"); exit; } } # when routing via usrloc, log the missed calls also if (is_method("INVITE")) { setflag(FLT_ACCMISSED); } route(RELAY); exit; } # Caller NAT detection route route[NATDETECT] { #!ifdef WITH_NAT force_rport(); if (nat_uac_test("19")) { if (is_method("REGISTER")) { fix_nated_register(); } else { add_contact_alias(); } setflag(FLT_NATS); } #!endif return; } # RTPProxy control route[NATMANAGE] { #!ifdef WITH_NAT if (is_request()) { if(has_totag()) { if(check_route_param("nat=yes")) { setbflag(FLB_NATB); } } } if (!(isflagset(FLT_NATS) || isbflagset(FLB_NATB))) return; rtpproxy_manage(); if (is_request()) { if (!has_totag()) { add_rr_param(";nat=yes"); } } if (is_reply()) { if(isbflagset(FLB_NATB)) { add_contact_alias(); } } #!endif return; } # URI update for dialog requests route[DLGURI] { #!ifdef WITH_NAT if(!isdsturiset()) { handle_ruri_alias(); } #!endif return; } # Routing to foreign domains route[SIPOUT] { if (!uri==myself) { append_hf("P-hint: outbound\r\n"); route(RELAY); } } # manage outgoing branches branch_route[MANAGE_BRANCH] { xdbg("new branch [$T_branch_idx] to $ru\n"); route(NATMANAGE); } # manage incoming replies onreply_route[MANAGE_REPLY] { xdbg("incoming reply\n"); if(status=~"[12][0-9][0-9]") route(NATMANAGE); } # manage failure routing cases failure_route[MANAGE_FAILURE] { route(NATMANAGE); if (t_is_canceled()) { exit; } }
Saludos
Que buena publicacion, lo instalare, lo probare, y posteriormente, lo comentare en el sitio web busy-tone.org
Muchas Gracias, muy bueno.
Hola, precioso tutorial. Solo tengo una duda que igual y espero puedas aclararme. Mi pregunta es: que beneficio economico tiene un atacante al robar minutos? Te lo menciono por que en mis tiempos de novato (que aun lo soy, solo que no tanto) me «vaciaron» varios servidores (lo bueno es que los tenia con prepago). Ya que realmente no entiendo que ocurre, de hecho en esos servidores que me comprometieron se guardaron las grabaciones, y solo se escuchaban ruidos «raros». Como dato de donde me metieron el «gol» fue de Sudafrica.
Saludos!
Hola dexter
El beneficio es claro. Imagina que yo vendo minutos y la salida en lugar de usando un proveedor de pago la hago usando tus cuentas hackeadas .. a coste 0 para mi y todo beneficios
Aunque el artículo tiene un tiempo, aprovecho para comentar que las cuentas voip hackeadas también se utilizan para llamar a números premium y obtener buenos beneficios en metálico. Un saludo!