Skip to content

Commit

Permalink
Add: NASL bultin network functions
Browse files Browse the repository at this point in the history
The following functions are supported now:
- get_host_ip
- this_host
- this_host_name
- get_mtu
- islocalhost
- islocalnet
- scanner_add_port
- get_mtu
  • Loading branch information
Kraemii committed Aug 21, 2024
1 parent 50e7923 commit 40505cc
Show file tree
Hide file tree
Showing 4 changed files with 226 additions and 8 deletions.
15 changes: 7 additions & 8 deletions rust/nasl-builtin-network/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
190 changes: 190 additions & 0 deletions rust/nasl-builtin-network/src/network.rs
Original file line number Diff line number Diff line change
@@ -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<String, FunctionErrorKind> {
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<bool, FunctionErrorKind> {
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<bool, FunctionErrorKind> {
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::<u8>().map_err(|_| {
FunctionErrorKind::Diagnostic(format!("Invalid netmask {}", netmask_str), None)
})?;
let dst_part = dst_parts[i].parse::<u8>().map_err(|_| {
FunctionErrorKind::Diagnostic(format!("Invalid IP address {}", dst_str), None)
})?;
let src_part = src_parts[i].parse::<u8>().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<String> = ipv6_parts(&dst_str);
let src_parts: Vec<String> = ipv6_parts(&src_str);
let netmask_parts: Vec<String> = 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<NaslFunction> {
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<nasl_builtin_utils::NaslResult> {
lookup(name).map(|x| x(register, context))
}

fn nasl_fn_defined(&self, name: &str) -> bool {
lookup(name).is_some()
}
}
28 changes: 28 additions & 0 deletions rust/nasl-builtin-network/src/network_utils.rs
Original file line number Diff line number Diff line change
Expand Up @@ -147,3 +147,31 @@ pub fn get_netmask_by_local_ip(local_address: IpAddr) -> Result<Option<IpAddr>,
None,
))
}

pub fn expand_ipv6(ip: &str) -> String {
if let Some((left, right)) = ip.split_once("::") {
let left_parts = left.split(':').collect::<Vec<&str>>();
let right_parts = right.split(':').collect::<Vec<&str>>();
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<String> {
expand_ipv6(ip).split(':').map(|s| s.to_string()).collect()
}
1 change: 1 addition & 0 deletions rust/nasl-builtin-std/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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);

Expand Down

0 comments on commit 40505cc

Please sign in to comment.