Skip to content

Commit

Permalink
Add: NASL builtin function for open privileged sockets with udp and tcp
Browse files Browse the repository at this point in the history
  • Loading branch information
Kraemii committed Nov 7, 2024
1 parent 002562d commit f4ed020
Show file tree
Hide file tree
Showing 8 changed files with 182 additions and 29 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down
39 changes: 26 additions & 13 deletions rust/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down Expand Up @@ -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"] }
Expand All @@ -78,22 +81,21 @@ 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 }
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"
Expand All @@ -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"]

Expand Down
4 changes: 2 additions & 2 deletions rust/src/nasl/builtin/network/README.md
Original file line number Diff line number Diff line change
@@ -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
Expand All @@ -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
Expand Down
4 changes: 3 additions & 1 deletion rust/src/nasl/builtin/network/network.rs
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,9 @@ fn this_host(context: &Context) -> Result<String, FunctionErrorKind> {

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
Expand Down
10 changes: 3 additions & 7 deletions rust/src/nasl/builtin/network/network_utils.rs
Original file line number Diff line number Diff line change
Expand Up @@ -40,16 +40,12 @@ pub fn bind_local_socket(dst: &SocketAddr) -> io::Result<UdpSocket> {
}

/// Return the source IP address given the destination IP address
pub fn get_source_ip(dst: IpAddr, port: u16) -> Result<IpAddr, FunctionErrorKind> {
pub fn get_source_ip(dst: IpAddr, port: u16) -> io::Result<IpAddr> {
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
Expand Down
94 changes: 92 additions & 2 deletions rust/src/nasl/builtin/network/socket.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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<i64>,
) -> Result<NaslValue, FunctionErrorKind> {
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<i64>,
) -> Result<NaslValue, FunctionErrorKind> {
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<NaslValue, FunctionErrorKind> {
Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -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"),
Expand Down
37 changes: 36 additions & 1 deletion rust/src/nasl/builtin/network/tcp.rs
Original file line number Diff line number Diff line change
@@ -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<ClientConnection>,
Expand Down Expand Up @@ -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<Self> {
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<SocketAddr> {
self.stream.get_ref().tcp.local_addr()
Expand Down
15 changes: 15 additions & 0 deletions rust/src/nasl/builtin/network/udp.rs
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,21 @@ impl UdpConnection {
})
}

pub fn new_priv(addr: IpAddr, sport: u16, dport: u16) -> io::Result<Self> {
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);
}
Expand Down

0 comments on commit f4ed020

Please sign in to comment.