diff --git a/doc/manual/nasl/built-in-functions/network-functions/open_priv_sock_tcp.md b/doc/manual/nasl/built-in-functions/network-functions/open_priv_sock_tcp.md index c62c168cd..0ef585ce9 100644 --- a/doc/manual/nasl/built-in-functions/network-functions/open_priv_sock_tcp.md +++ b/doc/manual/nasl/built-in-functions/network-functions/open_priv_sock_tcp.md @@ -6,11 +6,13 @@ ## SYNOPSIS -*any* **open_priv_sock_tcp**(dport: *int*, sport: *int*); +*any* **open_priv_sock_tcp**(dport: *int*, sport: *int*, timeout: *int*); -**open_priv_sock_tcp** takes two named integer arguments: +**open_priv_sock_tcp** takes three named integer arguments: - dport is the destination port -- sport is the source port, which may be inferior to 1024. +- sport is the source port, which may be inferior to 1024. This argument is optional. + If it is not set, the function will try to open a socket on any port from 1 to 1023. +- timeout: An integer with the timeout value in seconds. The default timeout is controlled by a global value. ## DESCRIPTION diff --git a/rust/Cargo.toml b/rust/Cargo.toml index a209e898a..2b59c327e 100644 --- a/rust/Cargo.toml +++ b/rust/Cargo.toml @@ -6,14 +6,14 @@ license = "GPL-2.0-or-later" [dependencies] aes = "0.8.2" -aes-gcm = { version = "0.10.1"} +aes-gcm = { version = "0.10.1" } anyhow = "1.0.75" async-trait = "0.1.68" base64 = "0.21.2" -cbc = { version = "0.1.2", features = ["alloc"]} +cbc = { version = "0.1.2", features = ["alloc"] } ccm = "0.5.0" chacha20 = "0.9.1" -chrono = { version = "0.4.23", default-features = false, features = ["clock"]} +chrono = { version = "0.4.23", default-features = false, features = ["clock"] } clap = { version = "4.3.0", features = ["derive", "env"] } cmac = "0.7.2" configparser = "3" @@ -57,11 +57,14 @@ rustls = "0.23.5" rustls-pemfile = "2.1.2" rustls-pemfile-old = { version = "1.0.2", package = "rustls-pemfile" } sequoia-ipc = "0.30.1" -sequoia-openpgp = { version ="1.16.1", default-features = false, features = ["crypto-openssl"] } -serde = { version = "1.0", features = ["derive"]} +sequoia-openpgp = { version = "1.16.1", default-features = false, features = [ + "crypto-openssl", +] } +serde = { version = "1.0", features = ["derive"] } serde_json = "1.0.96" sha1 = "0.10.5" sha2 = "0.10.7" +socket2 = "0.5.7" sysinfo = "0.30.5" thiserror = "1.0.62" time = { version = "0", features = ["parsing"] } @@ -78,11 +81,13 @@ rayon = { version = "1.8.0", optional = true } pcap = { version = "1.0.0", optional = true } pnet_base = { version = "0.33.0", optional = true } pnet = { version = "0.33.0", optional = true } -socket2 = {version = "0.5.2", features = ["all"], optional = true} pnet_macros = { version = "0.33.0", optional = true } pnet_macros_support = { version = "0.33.0", optional = true } -libssh-rs = {version = "~0.2", features = ["vendored-openssl", "vendored"], optional = true} +libssh-rs = { version = "~0.2", features = [ + "vendored-openssl", + "vendored", +], optional = true } nasl-function-proc-macro = { path = "crates/nasl-function-proc-macro" } nasl-c-lib = { path = "crates/nasl-c-lib", optional = true } @@ -90,10 +95,7 @@ openssl = { version = "0.10.66", features = ["vendored"] } [workspace] resolver = "2" -members = [ - "crates/smoketest", - "crates/nasl-function-proc-macro", -] +members = ["crates/smoketest", "crates/nasl-function-proc-macro"] [dev-dependencies] tracing-test = "0.2.5" @@ -103,9 +105,20 @@ criterion = "0" dep-graph-parallel = ["rayon", "crossbeam-channel"] openvas_serde_support = [] serde_support = [] -default = ["dep-graph-parallel", "openvas_serde_support", "enforce-no-trailing-arguments", "serde_support"] +default = [ + "dep-graph-parallel", + "openvas_serde_support", + "enforce-no-trailing-arguments", + "serde_support", +] -nasl-builtin-raw-ip = ["pcap", "pnet_base", "pnet", "socket2", "pnet_macros", "pnet_macros_support",] +nasl-builtin-raw-ip = [ + "pcap", + "pnet_base", + "pnet", + "pnet_macros", + "pnet_macros_support", +] nasl-builtin-ssh = ["libssh-rs"] experimental = ["nasl-builtin-raw-ip", "nasl-builtin-ssh", "nasl-c-lib"] diff --git a/rust/src/nasl/builtin/network/README.md b/rust/src/nasl/builtin/network/README.md index 5a8cbfe15..a4e0a6ea8 100644 --- a/rust/src/nasl/builtin/network/README.md +++ b/rust/src/nasl/builtin/network/README.md @@ -1,7 +1,9 @@ ## Implements - open_sock_kdc - open_sock_tcp +- open_priv_sock_tcp - open_sock_udp +- open_priv_sock_udp - close - send - recv @@ -26,8 +28,6 @@ - get_udp_port_state - join_multicast_group - leave_multicast_group -- open_priv_sock_tcp -- open_priv_sock_udp - scanner_get_port - start_denial - end_denial diff --git a/rust/src/nasl/builtin/network/network.rs b/rust/src/nasl/builtin/network/network.rs index b0e92dd30..a5e6f3391 100644 --- a/rust/src/nasl/builtin/network/network.rs +++ b/rust/src/nasl/builtin/network/network.rs @@ -27,7 +27,9 @@ fn this_host(context: &Context) -> Result { let port: u16 = DEFAULT_PORT; - get_source_ip(dst, port).map(|ip| ip.to_string()) + get_source_ip(dst, port) + .map(|ip| ip.to_string()) + .map_err(|_| FunctionErrorKind::Diagnostic("No route to destination".to_string(), None)) } /// Get the host name of the current (attacking) machine diff --git a/rust/src/nasl/builtin/network/network_utils.rs b/rust/src/nasl/builtin/network/network_utils.rs index 995ca8ca3..7884e22c8 100644 --- a/rust/src/nasl/builtin/network/network_utils.rs +++ b/rust/src/nasl/builtin/network/network_utils.rs @@ -40,16 +40,12 @@ pub fn bind_local_socket(dst: &SocketAddr) -> io::Result { } /// Return the source IP address given the destination IP address -pub fn get_source_ip(dst: IpAddr, port: u16) -> Result { +pub fn get_source_ip(dst: IpAddr, port: u16) -> io::Result { let socket = SocketAddr::new(dst, port); let sd = format!("{}:{}", dst, port); let local_socket = bind_local_socket(&socket)?; - local_socket - .connect(sd) - .ok() - .and_then(|_| local_socket.local_addr().ok()) - .and_then(|l_addr| IpAddr::from_str(&l_addr.ip().to_string()).ok()) - .ok_or_else(|| FunctionErrorKind::Diagnostic("No route to destination".to_string(), None)) + local_socket.connect(sd)?; + Ok(local_socket.local_addr()?.ip()) } /// Tests whether a packet sent to IP is LIKELY to route through the diff --git a/rust/src/nasl/builtin/network/socket.rs b/rust/src/nasl/builtin/network/socket.rs index c834fa8a0..923af780a 100644 --- a/rust/src/nasl/builtin/network/socket.rs +++ b/rust/src/nasl/builtin/network/socket.rs @@ -575,6 +575,96 @@ impl NaslSockets { Ok(NaslValue::Number(fd as i64)) } + /// Open a privileged socket to the target host. + /// It takes three named integer arguments: + /// - dport is the destination port + /// - sport is the source port, which may be inferior to 1024. This argument is optional. + /// If it is not set, the function will try to open a socket on any port from 1 to 1023. + /// - timeout: An integer with the timeout value in seconds. The default timeout is controlled by a global value. + #[nasl_function(named(dport, sport))] + fn open_priv_sock_tcp( + &self, + context: &Context, + dport: i64, + sport: Option, + ) -> Result { + let dport = verify_port(dport)?; + + let addr = ipstr2ipaddr(context.target())?; + + // TODO: set timeout to global recv timeout when available + let timeout = Duration::from_secs(10); + + if let Some(sport) = sport { + let sport = verify_port(sport)?; + self.wait_before_next_probe(); + let tcp = TcpConnection::connect_priv(addr, sport, dport, timeout)?; + + let fd = self.add(NaslSocket::Tcp(Box::new(tcp))); + return Ok(NaslValue::Number(fd as i64)); + } + + let mut sport = 1023; + + while sport > 0 { + self.wait_before_next_probe(); + if let Ok(tcp) = TcpConnection::connect_priv(addr, sport, dport, timeout) { + let fd = self.add(NaslSocket::Tcp(Box::new(tcp))); + return Ok(NaslValue::Number(fd as i64)); + } + sport -= 1; + } + Err(FunctionErrorKind::Diagnostic( + format!( + "Unable to open priv socket to {} on any socket from 1-1023", + addr + ), + None, + )) + } + + /// Open a privileged UDP socket to the target host. + /// It takes three named integer arguments: + /// - dport is the destination port + /// - sport is the source port, which may be inferior to 1024. This argument is optional. + /// If it is not set, the function will try to open a socket on any port from 1 to 1023. + #[nasl_function(named(dport, sport))] + fn open_priv_sock_udp( + &self, + context: &Context, + dport: i64, + sport: Option, + ) -> Result { + let dport = verify_port(dport)?; + + let addr = ipstr2ipaddr(context.target())?; + + if let Some(sport) = sport { + let sport = verify_port(sport)?; + let udp = UdpConnection::new_priv(addr, sport, dport)?; + + let fd = self.add(NaslSocket::Udp(udp)); + return Ok(NaslValue::Number(fd as i64)); + } + + let mut sport = 1023; + + while sport > 0 { + if let Ok(udp) = UdpConnection::new_priv(addr, sport, dport) { + let fd = self.add(NaslSocket::Udp(udp)); + return Ok(NaslValue::Number(fd as i64)); + } + sport -= 1; + } + Err(FunctionErrorKind::Diagnostic( + format!( + "Unable to open priv socket to {} on any socket from 1-1023", + addr + ), + None, + )) + } + /// Get the source port of a open socket #[nasl_function] fn get_source_port(&self, socket: usize) -> Result { @@ -638,8 +728,6 @@ impl NaslSockets { } } - /// *any* **ftp_log_in**(user: *string*, pass: *string*, socket: *int*); - /// **ftp_log_in** takes three named arguments: /// - user: is the user name (it has no default value like “anonymous” or “ftp”) /// - pass: is the password (again, no default value like the user e-mail address) @@ -685,7 +773,9 @@ function_set! { ( (NaslSockets::open_sock_kdc, "open_sock_kdc"), (NaslSockets::open_sock_tcp, "open_sock_tcp"), + (NaslSockets::open_priv_sock_tcp, "open_priv_sock_tcp"), (NaslSockets::open_sock_udp, "open_sock_udp"), + (NaslSockets::open_priv_sock_udp, "open_priv_sock_udp"), (NaslSockets::close, "close"), (NaslSockets::send, "send"), (NaslSockets::recv, "recv"), diff --git a/rust/src/nasl/builtin/network/tcp.rs b/rust/src/nasl/builtin/network/tcp.rs index dd32e1822..0ee085415 100644 --- a/rust/src/nasl/builtin/network/tcp.rs +++ b/rust/src/nasl/builtin/network/tcp.rs @@ -1,12 +1,16 @@ use std::{ io::{self, BufRead, BufReader, Read, Write}, net::{IpAddr, SocketAddr, TcpStream}, - os::fd::AsRawFd, + os::fd::{AsRawFd, FromRawFd}, time::Duration, }; use rustls::{ClientConnection, Stream}; +use socket2; + +use super::network_utils::get_source_ip; + struct TcpDataStream { tcp: TcpStream, tls: Option, @@ -128,6 +132,37 @@ impl TcpConnection { Ok(Self::new(TcpDataStream { tcp, tls }, bufsz)) } + pub fn connect_priv( + addr: IpAddr, + sport: u16, + dport: u16, + timeout: Duration, + ) -> io::Result { + let sock = match addr { + IpAddr::V4(_) => socket2::Socket::new( + socket2::Domain::IPV4, + socket2::Type::STREAM, + Some(socket2::Protocol::TCP), + )?, + + IpAddr::V6(_) => socket2::Socket::new( + socket2::Domain::IPV6, + socket2::Type::STREAM, + Some(socket2::Protocol::TCP), + )?, + }; + + let src = get_source_ip(addr, dport)?; + + sock.bind(&SocketAddr::new(src, sport).into())?; + + sock.connect_timeout(&SocketAddr::new(addr, dport).into(), timeout)?; + + let tcp = unsafe { TcpStream::from_raw_fd(sock.as_raw_fd()) }; + + Ok(Self::new(TcpDataStream { tcp, tls: None }, None)) + } + /// Returns the socket address of the local half of this TCP connection. pub fn local_addr(&self) -> io::Result { self.stream.get_ref().tcp.local_addr() diff --git a/rust/src/nasl/builtin/network/udp.rs b/rust/src/nasl/builtin/network/udp.rs index 4fd840899..f82392f3d 100644 --- a/rust/src/nasl/builtin/network/udp.rs +++ b/rust/src/nasl/builtin/network/udp.rs @@ -82,6 +82,21 @@ impl UdpConnection { }) } + pub fn new_priv(addr: IpAddr, sport: u16, dport: u16) -> io::Result { + let sock_addr = SocketAddr::new(addr, sport); + let socket = match sock_addr { + SocketAddr::V4(_) => UdpSocket::bind(format!("0.0.0.0:{}", sport)), + SocketAddr::V6(_) => UdpSocket::bind(format!("[::]:{}", sport)), + }?; + socket.connect(SocketAddr::new(addr, dport))?; + socket.set_read_timeout(Some(Duration::from_secs(1)))?; + Ok(Self { + socket, + buffer: vec![], + flags: None, + }) + } + pub fn set_flags(&mut self, flags: i32) { self.flags = Some(flags); }