From 40505cc46fc9e9216332000d2f85cf39f3863b0d Mon Sep 17 00:00:00 2001 From: Kraemii Date: Wed, 21 Aug 2024 07:58:45 +0200 Subject: [PATCH] Add: NASL bultin network functions The following functions are supported now: - get_host_ip - this_host - this_host_name - get_mtu - islocalhost - islocalnet - scanner_add_port - get_mtu --- rust/nasl-builtin-network/README.md | 15 +- rust/nasl-builtin-network/src/network.rs | 190 ++++++++++++++++++ .../nasl-builtin-network/src/network_utils.rs | 28 +++ rust/nasl-builtin-std/src/lib.rs | 1 + 4 files changed, 226 insertions(+), 8 deletions(-) create mode 100644 rust/nasl-builtin-network/src/network.rs diff --git a/rust/nasl-builtin-network/README.md b/rust/nasl-builtin-network/README.md index 56872f07d..e69e8f907 100644 --- a/rust/nasl-builtin-network/README.md +++ b/rust/nasl-builtin-network/README.md @@ -5,31 +5,30 @@ - close - send - recv +- this_host_name +- get_mtu +- this_host +- islocalhost +- islocalnet +- get_host_ip +- scanner_add_port ## Missing - end_denial - ftp_get_pasv_port - ftp_log_in -- get_host_ip - get_host_open_port -- get_mtu - get_port_state - get_port_transport - get_source_port - get_tcp_port_state - get_udp_port_state -- islocalhost -- islocalnet - join_multicast_group - leave_multicast_group - open_priv_sock_tcp - open_priv_sock_udp - recv_line -- scanner_add_port - scanner_get_port - start_denial -- tcp_ping - telnet_init -- this_host_name -- this_host diff --git a/rust/nasl-builtin-network/src/network.rs b/rust/nasl-builtin-network/src/network.rs new file mode 100644 index 000000000..5a012d969 --- /dev/null +++ b/rust/nasl-builtin-network/src/network.rs @@ -0,0 +1,190 @@ +// SPDX-FileCopyrightText: 2024 Greenbone AG +// +// SPDX-License-Identifier: GPL-2.0-or-later + +use std::process::Command; + +use crate::{ + network_utils::{ + get_netmask_by_local_ip, get_source_ip, ipstr2ipaddr, ipv6_parts, islocalhost, + }, + verify_port, +}; +use nasl_builtin_utils::{Context, FunctionErrorKind, NaslFunction, Register}; +use nasl_function_proc_macro::nasl_function; +use storage::Kb; + +use crate::mtu; + +/// Get the IP address of the currently scanned host +#[nasl_function] +fn get_host_ip(context: &Context) -> String { + context.target().to_string() +} + +/// Get the IP address of the current (attacking) machine depending on which network device is used +#[nasl_function] +fn this_host(context: &Context) -> Result { + let dst = ipstr2ipaddr(context.target())?; + + let port: u16 = 33435; + + get_source_ip(dst, port).map(|ip| ip.to_string()) +} + +/// Get the host name of the current (attacking) machine +#[nasl_function] +fn this_host_name() -> String { + Command::new("uname") + .args(["-n"]) + .output() + .map(|op| String::from_utf8_lossy(&op.stdout).trim().to_string()) + .unwrap_or("".to_string()) +} + +/// get the maximum transition unit for the scanned host +#[nasl_function] +fn get_mtu() -> i64 { + mtu() as i64 +} + +/// check if the currently scanned host is the localhost +#[nasl_function] +fn nasl_islocalhost(context: &Context) -> Result { + let host_ip = ipstr2ipaddr(context.target())?; + Ok(islocalhost(host_ip)) +} + +///Check if the target host is on the same network as the attacking host +#[nasl_function] +fn islocalnet(context: &Context) -> Result { + let dst = ipstr2ipaddr(context.target())?; + let src = get_source_ip(dst, 33435)?; + + let netmask_str = match get_netmask_by_local_ip(src)? { + Some(netmask) => netmask.to_string(), + None => return Ok(false), + }; + + let dst_str = dst.to_string(); + let src_str = src.to_string(); + + if dst.is_ipv4() { + let dst_parts: Vec<&str> = dst_str.split('.').collect(); + let src_parts: Vec<&str> = src_str.split('.').collect(); + let netmask_parts: Vec<&str> = netmask_str.split('.').collect(); + + // Iterate over each octet + for i in 0..4 { + // get octet as u8 + let netmask_part = netmask_parts[i].parse::().map_err(|_| { + FunctionErrorKind::Diagnostic(format!("Invalid netmask {}", netmask_str), None) + })?; + let dst_part = dst_parts[i].parse::().map_err(|_| { + FunctionErrorKind::Diagnostic(format!("Invalid IP address {}", dst_str), None) + })?; + let src_part = src_parts[i].parse::().map_err(|_| { + FunctionErrorKind::Diagnostic(format!("Invalid IP address {}", src_str), None) + })?; + // Iterate over each bit in the octet + let mut n = 128; + while n > 0 { + // If the bit is not set in the netmask, we are done + if netmask_part & n == 0 { + return Ok(true); + } + // If the bit is not the same in the source and destination, we are done + if dst_part & n != src_part & n { + return Ok(false); + } + n = n >> 1; + } + } + } else { + let dst_parts: Vec = ipv6_parts(&dst_str); + let src_parts: Vec = ipv6_parts(&src_str); + let netmask_parts: Vec = ipv6_parts(&netmask_str); + + // Iterate over each IPv6 part + for i in 0..8 { + // get part as u16 + let netmask_part = u16::from_str_radix(&netmask_parts[i], 16).map_err(|_| { + FunctionErrorKind::Diagnostic(format!("Invalid netmask {}", netmask_str), None) + })?; + let dst_part = u16::from_str_radix(&dst_parts[i], 16).map_err(|_| { + FunctionErrorKind::Diagnostic(format!("Invalid IP address {}", dst_str), None) + })?; + let src_part = u16::from_str_radix(&src_parts[i], 16).map_err(|_| { + FunctionErrorKind::Diagnostic(format!("Invalid IP address {}", src_str), None) + })?; + // Iterate over each bit in the part + let mut n = 32768; + while n > 0 { + // If the bit is not set in the netmask, we are done + if netmask_part & n == 0 { + return Ok(true); + } + // If the bit is not the same in the source and destination, we are done + if dst_part & n != src_part & n { + return Ok(false); + } + n = n >> 1; + } + } + } + + Ok(true) +} + +/// Declares an open port on the target host +#[nasl_function(named(port, proto))] +fn scanner_add_port( + port: i64, + protocol: Option<&str>, + context: &Context, +) -> Result<(), FunctionErrorKind> { + let port = verify_port(port)?; + let protocol = protocol.unwrap_or("tcp"); + + context.dispatcher().dispatch( + context.key(), + storage::Field::KB(Kb { + key: format!("Port/{}/{}", protocol, port), + value: storage::types::Primitive::Number(1), + expire: None, + }), + )?; + + Ok(()) +} + +/// Returns found function for key or None when not found +pub fn lookup(key: &str) -> Option { + match key { + "scanner_add_port" => Some(scanner_add_port), + "islocalnet" => Some(islocalnet), + "islocalhost" => Some(nasl_islocalhost), + "this_host" => Some(this_host), + "this_host_name" => Some(this_host_name), + "get_mtu" => Some(get_mtu), + "get_host_ip" => Some(get_host_ip), + _ => None, + } +} + +pub struct Network; + +impl nasl_builtin_utils::NaslFunctionExecuter for Network { + fn nasl_fn_execute( + &self, + name: &str, + register: &Register, + context: &Context, + ) -> Option { + lookup(name).map(|x| x(register, context)) + } + + fn nasl_fn_defined(&self, name: &str) -> bool { + lookup(name).is_some() + } +} diff --git a/rust/nasl-builtin-network/src/network_utils.rs b/rust/nasl-builtin-network/src/network_utils.rs index 38f8a4a31..e4b238684 100644 --- a/rust/nasl-builtin-network/src/network_utils.rs +++ b/rust/nasl-builtin-network/src/network_utils.rs @@ -147,3 +147,31 @@ pub fn get_netmask_by_local_ip(local_address: IpAddr) -> Result, None, )) } + +pub fn expand_ipv6(ip: &str) -> String { + if let Some((left, right)) = ip.split_once("::") { + let left_parts = left.split(':').collect::>(); + let right_parts = right.split(':').collect::>(); + let mut expanded = String::new(); + let missing = 8 - left_parts.len() - right_parts.len(); + for part in left_parts { + expanded.push_str(part); + expanded.push(':'); + } + for _ in 0..missing { + expanded.push_str("0000:"); + } + for part in right_parts { + expanded.push_str(part); + expanded.push(':'); + } + expanded.pop(); + expanded + } else { + ip.to_string() + } +} + +pub fn ipv6_parts(ip: &str) -> Vec { + expand_ipv6(ip).split(':').map(|s| s.to_string()).collect() +} diff --git a/rust/nasl-builtin-std/src/lib.rs b/rust/nasl-builtin-std/src/lib.rs index 458ebd80b..1d6e318ab 100644 --- a/rust/nasl-builtin-std/src/lib.rs +++ b/rust/nasl-builtin-std/src/lib.rs @@ -78,6 +78,7 @@ pub fn nasl_std_functions() -> nasl_builtin_utils::NaslFunctionRegister { .push_register(nasl_builtin_host::Host) .push_register(nasl_builtin_http::NaslHttp::default()) .push_register(nasl_builtin_network::socket::NaslSockets::default()) + .push_register(nasl_builtin_network::network::Network) .push_register(nasl_builtin_cryptographic::Cryptographic) .push_register(nasl_builtin_description::Description);