From 76e73b4058e5a31d8026e88ef2fdb24ea98b3637 Mon Sep 17 00:00:00 2001 From: serxoz Date: Mon, 5 Dec 2022 17:29:58 +0100 Subject: [PATCH] answer OPTIONS with 404 --- .gitignore | 1 + Cargo.lock | 316 +++++++++++++++++++++++++++++++++++++++++++ Cargo.toml | 9 ++ README.md | 10 ++ doc/sip-packets.txt | 26 ++++ src/main.rs | 96 +++++++++++++ src/sip/mod.rs | 2 + src/sip/not_found.rs | 79 +++++++++++ src/sip/options.rs | 67 +++++++++ 9 files changed, 606 insertions(+) create mode 100644 .gitignore create mode 100644 Cargo.lock create mode 100644 Cargo.toml create mode 100644 README.md create mode 100644 doc/sip-packets.txt create mode 100644 src/main.rs create mode 100644 src/sip/mod.rs create mode 100644 src/sip/not_found.rs create mode 100644 src/sip/options.rs diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..ea8c4bf --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +/target diff --git a/Cargo.lock b/Cargo.lock new file mode 100644 index 0000000..a53a275 --- /dev/null +++ b/Cargo.lock @@ -0,0 +1,316 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "autocfg" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" + +[[package]] +name = "bitflags" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" + +[[package]] +name = "bytes" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dfb24e866b15a1af2a1b663f10c6b6b8f397a84aadb828f12e5b289ec23a3a3c" + +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + +[[package]] +name = "hermit-abi" +version = "0.1.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "62b467343b94ba476dcb2500d242dadbb39557df889310ac77c5d99100aaac33" +dependencies = [ + "libc", +] + +[[package]] +name = "libc" +version = "0.2.138" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "db6d7e329c562c5dfab7a46a2afabc8b987ab9a4834c9d1ca04dc54c1546cef8" + +[[package]] +name = "lock_api" +version = "0.4.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "435011366fe56583b16cf956f9df0095b405b82d76425bc8981c0e22e60ec4df" +dependencies = [ + "autocfg", + "scopeguard", +] + +[[package]] +name = "log" +version = "0.4.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "abb12e687cfb44aa40f41fc3978ef76448f9b6038cad6aef4259d3c095a2382e" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "memchr" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d" + +[[package]] +name = "mio" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e5d732bc30207a6423068df043e3d02e0735b155ad7ce1a6f76fe2baa5b158de" +dependencies = [ + "libc", + "log", + "wasi", + "windows-sys", +] + +[[package]] +name = "num_cpus" +version = "1.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f6058e64324c71e02bc2b150e4f3bc8286db6c83092132ffa3f6b1eab0f9def5" +dependencies = [ + "hermit-abi", + "libc", +] + +[[package]] +name = "parking_lot" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3742b2c103b9f06bc9fff0a37ff4912935851bee6d36f3c02bcc755bcfec228f" +dependencies = [ + "lock_api", + "parking_lot_core", +] + +[[package]] +name = "parking_lot_core" +version = "0.9.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7ff9f3fef3968a3ec5945535ed654cb38ff72d7495a25619e2247fb15a2ed9ba" +dependencies = [ + "cfg-if", + "libc", + "redox_syscall", + "smallvec", + "windows-sys", +] + +[[package]] +name = "pin-project-lite" +version = "0.2.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e0a7ae3ac2f1173085d398531c705756c94a4c56843785df85a60c1a0afac116" + +[[package]] +name = "proc-macro2" +version = "1.0.47" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5ea3d908b0e36316caf9e9e2c4625cdde190a7e6f440d794667ed17a1855e725" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quote" +version = "1.0.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbe448f377a7d6961e30f5955f9b8d106c3f5e449d493ee1b125c1d43c2b5179" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "redox_syscall" +version = "0.2.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fb5a58c1855b4b6819d59012155603f0b22ad30cad752600aadfcb695265519a" +dependencies = [ + "bitflags", +] + +[[package]] +name = "scopeguard" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" + +[[package]] +name = "signal-hook-registry" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e51e73328dc4ac0c7ccbda3a494dfa03df1de2f46018127f60c693f2648455b0" +dependencies = [ + "libc", +] + +[[package]] +name = "sip-tarpit" +version = "0.1.0" +dependencies = [ + "tokio", +] + +[[package]] +name = "smallvec" +version = "1.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a507befe795404456341dfab10cef66ead4c041f62b8b11bbb92bffe5d0953e0" + +[[package]] +name = "socket2" +version = "0.4.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "02e2d2db9033d13a1567121ddd7a095ee144db4e1ca1b1bda3419bc0da294ebd" +dependencies = [ + "libc", + "winapi", +] + +[[package]] +name = "syn" +version = "1.0.105" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "60b9b43d45702de4c839cb9b51d9f529c5dd26a4aff255b42b1ebc03e88ee908" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "tokio" +version = "1.22.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d76ce4a75fb488c605c54bf610f221cea8b0dafb53333c1a67e8ee199dcd2ae3" +dependencies = [ + "autocfg", + "bytes", + "libc", + "memchr", + "mio", + "num_cpus", + "parking_lot", + "pin-project-lite", + "signal-hook-registry", + "socket2", + "tokio-macros", + "winapi", +] + +[[package]] +name = "tokio-macros" +version = "1.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d266c00fde287f55d3f1c3e96c500c362a2b8c695076ec180f27918820bc6df8" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "unicode-ident" +version = "1.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ceab39d59e4c9499d4e5a8ee0e2735b891bb7308ac83dfb4e80cad195c9f6f3" + +[[package]] +name = "wasi" +version = "0.11.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" + +[[package]] +name = "winapi" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" +dependencies = [ + "winapi-i686-pc-windows-gnu", + "winapi-x86_64-pc-windows-gnu", +] + +[[package]] +name = "winapi-i686-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" + +[[package]] +name = "winapi-x86_64-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" + +[[package]] +name = "windows-sys" +version = "0.42.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a3e1820f08b8513f676f7ab6c1f99ff312fb97b553d30ff4dd86f9f15728aa7" +dependencies = [ + "windows_aarch64_gnullvm", + "windows_aarch64_msvc", + "windows_i686_gnu", + "windows_i686_msvc", + "windows_x86_64_gnu", + "windows_x86_64_gnullvm", + "windows_x86_64_msvc", +] + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.42.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41d2aa71f6f0cbe00ae5167d90ef3cfe66527d6f613ca78ac8024c3ccab9a19e" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.42.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dd0f252f5a35cac83d6311b2e795981f5ee6e67eb1f9a7f64eb4500fbc4dcdb4" + +[[package]] +name = "windows_i686_gnu" +version = "0.42.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fbeae19f6716841636c28d695375df17562ca208b2b7d0dc47635a50ae6c5de7" + +[[package]] +name = "windows_i686_msvc" +version = "0.42.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "84c12f65daa39dd2babe6e442988fc329d6243fdce47d7d2d155b8d874862246" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.42.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bf7b1b21b5362cbc318f686150e5bcea75ecedc74dd157d874d754a2ca44b0ed" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.42.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09d525d2ba30eeb3297665bd434a54297e4170c7f1a44cad4ef58095b4cd2028" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.42.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f40009d85759725a34da6d89a94e63d7bdc50a862acf0dbc7c8e488f1edcb6f5" diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000..9d09345 --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,9 @@ +[package] +name = "sip-tarpit" +version = "0.1.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +tokio = { version = "1", features = ["full"] } diff --git a/README.md b/README.md new file mode 100644 index 0000000..486bf0d --- /dev/null +++ b/README.md @@ -0,0 +1,10 @@ +# SIP tarpit +The idea is that a SIP attacker lost more time while trying to crack this "SIP service". +Could be defined as a slow honeypot... + +Initially will be designed against a sipvicious attacker. + +## Answer to OPTIONS +This way will be mapped in the network + + diff --git a/doc/sip-packets.txt b/doc/sip-packets.txt new file mode 100644 index 0000000..8ed6bd9 --- /dev/null +++ b/doc/sip-packets.txt @@ -0,0 +1,26 @@ +OPTIONS sip:100@127.0.0.1 SIP/2.0 +Via: SIP/2.0/UDP 127.0.1.1:5061;branch=z9hG4bK-487618168;rport +Max-Forwards: 70 +To: "sipvicious" +From: "sipvicious";tag=37663030303030313133633401393537303038303638 +User-Agent: friendly-scanner +Call-ID: 447615548427934163033914 +Contact: sip:100@127.0.1.1:5061 +CSeq: 1 OPTIONS +Accept: application/sdp +Content-Length: 0 + +SIP/2.0 404 Not Found +Via: SIP/2.0/UDP 127.0.1.1:5060;rport=65476;received=185.179.142.180;branch=z9hG4bK-3692480612 +Call-ID: 953932568039675157936807 +From: "sipvicious" ;tag=6239623338663563313363370134323538333932373936 +To: "sipvicious" ;tag=z9hG4bK-3692480612 +CSeq: 1 OPTIONS +Accept: application/xpidf+xml, application/cpim-pidf+xml, application/dialog-info+xml, application/simple-message-summary, application/pidf+xml, application/pidf+xml, application/dialog-info+xml, application/s +ple-message-summary, application/sdp, message/sipfrag;version=2.0 +Allow: OPTIONS, REGISTER, SUBSCRIBE, NOTIFY, PUBLISH, INVITE, ACK, BYE, CANCEL, UPDATE, PRACK, MESSAGE, REFER +Supported: 100rel, timer, replaces, norefersub +Accept-Encoding: identity +Accept-Language: en +Server: Asterisk PBX 18.9.0 +Content-Length: 0 diff --git a/src/main.rs b/src/main.rs new file mode 100644 index 0000000..f4f1056 --- /dev/null +++ b/src/main.rs @@ -0,0 +1,96 @@ +/* + * SIP-tarpit + * Listens on 5060 udp by default + * + */ +pub mod sip; + +use std::error::Error; +use std::net::SocketAddr; +use std::{env, io}; +use tokio::net::UdpSocket; +use tokio::time::{sleep, Duration}; +use crate::sip::options::*; +use crate::sip::not_found::*; + +struct Server { + socket: UdpSocket, + buf: Vec, + to_send: Option<(usize, SocketAddr)>, +} + +impl Server { + async fn run(self) -> Result<(), io::Error> { + let Server { + socket, + mut buf, + mut to_send, + } = self; + + loop { + // First we check to see if there's a message we need to answer. + // If so then we try to send it back to the original source, waiting + // until it's writable and we're able to do so. + if let Some((size, peer)) = to_send { + // let amt = socket.send_to(&buf[..size], &peer).await?; + // println!("Echoed {}/{} bytes to {}", amt, size, peer); + // + // test type of message received + let msg = String::from_utf8(buf[..size].to_vec()).unwrap(); + match msg { + msg if msg.contains("OPTIONS") => { + // esperar - tarpit! + sleep(Duration::from_secs(5)).await; + + let options = Options::parse(&msg); + // respondemos con un 404 not found + // o campo via non é como vai... pero ñapa + let not_found = NotFoundPacket { + command: "SIP/2.0 404 Not Found".to_string(), + via: options.via, + call_id: options.call_id, + from: options.from, + to: options.to, + cseq: options.cseq, + accept: Some("application/sdp".to_string()), + allow: Some("OPTIONS, REGISTER, SUBSCRIBE, NOTIFY, PUBLISH, INVITE, ACK, BYE, CANCEL, UPDATE, PRACK, MESSAGE, REFER".to_string()), + supported: Some("100rel, timer, replaces, norefersub".to_string()), + accept_encoding: Some("itentity".to_string()), + accept_language: Some("en".to_string()), + server: Some("Asterisk PBX 18.9.0".to_string()), + content_length: Some(0) + }; + let amt = socket.send_to(¬_found.serialize(), &peer).await?; + println!("Sent {}/{} bytes to {}", amt, ¬_found.serialize().len(), peer); + }, + _ => println!("Packet don't known, yet...") + } + } + + // If we're here then `to_send` is `None`, so we take a look for the + // next message we're going to answer. + to_send = Some(socket.recv_from(&mut buf).await?); + } + } +} + +#[tokio::main] +async fn main() -> Result<(), Box> { + let addr = env::args() + .nth(1) + .unwrap_or_else(|| "127.0.0.1:5063".to_string()); + + let socket = UdpSocket::bind(&addr).await?; + println!("Listening on: {}", socket.local_addr()?); + + let server = Server { + socket, + buf: vec![0; 1024], + to_send: None, + }; + + // This starts the server task. + server.run().await?; + + Ok(()) +} diff --git a/src/sip/mod.rs b/src/sip/mod.rs new file mode 100644 index 0000000..d9e6aef --- /dev/null +++ b/src/sip/mod.rs @@ -0,0 +1,2 @@ +pub mod options; +pub mod not_found; diff --git a/src/sip/not_found.rs b/src/sip/not_found.rs new file mode 100644 index 0000000..77bd2c2 --- /dev/null +++ b/src/sip/not_found.rs @@ -0,0 +1,79 @@ +/* +SIP/2.0 404 Not Found +Via: SIP/2.0/UDP 127.0.1.1:5060;rport=65476;received=185.179.142.180;branch=z9hG4bK-3692480612 +Call-ID: 953932568039675157936807 +From: "sipvicious" ;tag=6239623338663563313363370134323538333932373936 +To: "sipvicious" ;tag=z9hG4bK-3692480612 +CSeq: 1 OPTIONS +Accept: application/xpidf+xml, application/cpim-pidf+xml, application/dialog-info+xml, application/simple-message-summary, application/pidf+xml, application/pidf+xml, application/dialog-info+xml, application/s +ple-message-summary, application/sdp, message/sipfrag;version=2.0 +Allow: OPTIONS, REGISTER, SUBSCRIBE, NOTIFY, PUBLISH, INVITE, ACK, BYE, CANCEL, UPDATE, PRACK, MESSAGE, REFER +Supported: 100rel, timer, replaces, norefersub +Accept-Encoding: identity +Accept-Language: en +Server: Asterisk PBX 18.9.0 +Content-Length: 0 +*/ +#[derive(Debug)] +pub struct NotFoundPacket { + pub command: String, // SIP/2.0 404 Not Found + pub via: String, // SIP/2.0/UDP 127.0.1.1:5061;branch=z9hG4bK-487618168;rport + pub call_id: Option, // Call-ID: 447615548427934163033914 + pub from: String, // "sipvicious";tag=37663030303030313133633401393537303038303638 + pub to: String, // "sipvicious" + pub cseq: String, // 1 OPTIONS + pub accept: Option, // application/sdp + pub allow: Option, // OPTIONS, REGISTER, SUBSCRIBE, NOTIFY, PUBLISH, INVITE... + pub supported: Option, // 100rel, timer, replaces, norefersub + pub accept_encoding: Option, // identity + pub accept_language: Option, // en + pub server: Option, // Asterisk PBX 18.9.0 + pub content_length: Option // 0 +} + +impl NotFoundPacket { + pub fn serialize(&self) -> Vec { + let mut preout = String::new(); + preout.push_str(&self.command); + preout.push_str("\n"); + preout.push_str("Via: "); + preout.push_str(&self.via); + preout.push_str("\n"); + preout.push_str("Call-ID: "); + preout.push_str(&self.call_id.as_ref().unwrap()); + preout.push_str("\n"); + preout.push_str("From: "); + preout.push_str(&self.from); + preout.push_str("\n"); + preout.push_str("To: "); + preout.push_str(&self.to); + preout.push_str("\n"); + preout.push_str("CSeq: "); + preout.push_str(&self.cseq); + preout.push_str("\n"); + preout.push_str("Accept: "); + preout.push_str(&self.accept.as_ref().unwrap()); + preout.push_str("\n"); + preout.push_str("Allow: "); + preout.push_str(&self.allow.as_ref().unwrap()); + preout.push_str("\n"); + preout.push_str("Supported: "); + preout.push_str(&self.supported.as_ref().unwrap()); + preout.push_str("\n"); + preout.push_str("Accept-Encoding: "); + preout.push_str(&self.accept_encoding.as_ref().unwrap()); + preout.push_str("\n"); + preout.push_str("Accept-Language: "); + preout.push_str(&self.accept_language.as_ref().unwrap()); + preout.push_str("\n"); + preout.push_str("Server: "); + preout.push_str(&self.server.as_ref().unwrap()); + preout.push_str("\n"); + preout.push_str("Content-Length: "); + preout.push_str(&self.content_length.unwrap().to_string()); + preout.push_str("\n"); + preout.push_str("\n"); + // println!("{}", preout); + preout.as_bytes().to_vec() + } +} diff --git a/src/sip/options.rs b/src/sip/options.rs new file mode 100644 index 0000000..cc105cc --- /dev/null +++ b/src/sip/options.rs @@ -0,0 +1,67 @@ +// Mandatory: To, From, CSeq, Call-ID, Max-Forwards, and Via; +// +#[derive(Debug)] +pub struct Options { + pub command: String, // OPTIONS sip:100@127.0.0.1 SIP/2.0 + pub via: String, // SIP/2.0/UDP 127.0.1.1:5061;branch=z9hG4bK-487618168;rport + pub max_forwards: i32, // 70 + pub to: String, // "sipvicious" + pub from: String, // "sipvicious";tag=37663030303030313133633401393537303038303638 + pub user_agent: Option, // friendly-scanner + pub call_id: Option, // Call-ID: 447615548427934163033914 + pub contact: Option, // sip:100@127.0.1.1:5061 + pub cseq: String, // 1 OPTIONS + pub accept: Option, // application/sdp + pub content_length: Option // 0 +} + +impl Options { + pub fn new() -> Options { + Options { + command: "OPTIONS".to_string(), + via: String::new(), + max_forwards: 0, + to: String::new(), + from: String::new(), + user_agent: Some(String::new()), + call_id: Some(String::new()), + contact: Some(String::new()), + cseq: String::new(), + accept: Some(String::new()), + content_length: Some(0,) + } + + } + + pub fn parse(msg: &String) -> Options { + let mut packet = Options::new(); + for line in msg.lines() { + if line.len() > 0 { + // split polos dous puntos e comprobar + // si a parte 0 conten via asignar a via e así + let parts = line.split(":").collect::>(); + let header = parts[0].trim(); + let mut content = "".to_owned(); + for p in &parts[1..]{ + content.push_str(p.trim()) + } + match header { + "OPTIONS sip" => packet.command = format!("{}{}", header, content), + "Via" => packet.via = content.to_string(), + "Max-Forwards" => packet.max_forwards = content.parse::().unwrap(), + "To" => packet.to = content.to_string(), + "From" => packet.from = content.to_string(), + "User-Agent" => packet.user_agent = Some(content.to_string()), + "Call-ID" => packet.call_id = Some(content.to_string()), + "Contact" => packet.contact = Some(content.to_string()), + "CSeq" => packet.cseq = content.to_string(), + "Accept" => packet.accept = Some(content.to_string()), + "Content-Length" => packet.content_length = Some(content.parse::().unwrap()), + _ => println!("---> {:?} no contemplada!", header), + } + } + } + // println!("{:?}",packet); + packet + } +}