Added range feature and and dns resolving
All checks were successful
continuous-integration/drone/push Build is passing
All checks were successful
continuous-integration/drone/push Build is passing
This commit is contained in:
parent
4edb7b08ab
commit
2c8ef69905
@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "portnut"
|
name = "portnut"
|
||||||
version = "0.1.0"
|
version = "0.1.1"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
|
|
||||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||||
|
@ -34,16 +34,17 @@ Stressing 127.0.0.1 using 100 concurrent threads per port for 30s
|
|||||||
|
|
||||||
See the help :
|
See the help :
|
||||||
```
|
```
|
||||||
1648 justine@portnut > target/release/portnut -h
|
justine@portnut > target/release/portnut -h
|
||||||
A simple TCP port scanner / stresser. If stressing, you can pass a payload via a pipe
|
A simple TCP port scanner / stresser. If stressing, you can pass a payload via a pipe
|
||||||
|
|
||||||
Usage: portnut [OPTIONS] --address <ADDRESS>
|
Usage: portnut [OPTIONS] --address <ADDRESS>
|
||||||
|
|
||||||
Options:
|
Options:
|
||||||
-a, --address <ADDRESS> IP address to scan
|
-a, --address <ADDRESS> IP address or hostname to scan
|
||||||
-t, --timeout <TIMEOUT> Timeout for each connection in seconds [default: 1]
|
-t, --timeout <TIMEOUT> Timeout for each connection in seconds [default: 1]
|
||||||
-w, --wait <WAIT> Number of milliseconds to wait in between scans when scanning OR duration of stress when stress testing (in seconds) [default: 30]
|
-w, --wait <WAIT> Number of milliseconds to wait in between scans when scanning OR duration of stress when stress testing (in seconds) [default: 30]
|
||||||
-p, --ports <PORTS>... Ports to stress / scan, separated by commas (22,80)
|
-p, --ports <PORTS>... Ports to stress / scan, separated by commas (22,80)
|
||||||
|
-r, --range Set this flag to treat the ports as a range rather than a list
|
||||||
-s, --stress Set this flag to stress the ports instead of scanning them
|
-s, --stress Set this flag to stress the ports instead of scanning them
|
||||||
-c, --cthreads <CTHREADS> How many threads per port when stressing (Concurrent Threads) [default: 5]
|
-c, --cthreads <CTHREADS> How many threads per port when stressing (Concurrent Threads) [default: 5]
|
||||||
-h, --help Print help
|
-h, --help Print help
|
||||||
|
69
src/main.rs
69
src/main.rs
@ -1,10 +1,11 @@
|
|||||||
use clap::Parser;
|
use clap::Parser;
|
||||||
use std::io::prelude::*;
|
use std::io::prelude::*;
|
||||||
use std::io::{self, Read};
|
use std::io::{self, Read};
|
||||||
use std::net::{SocketAddr, TcpStream};
|
use std::net::{SocketAddr, TcpStream, ToSocketAddrs};
|
||||||
use std::time::{Duration, Instant};
|
use std::time::{Duration, Instant};
|
||||||
use std::thread;
|
use std::thread;
|
||||||
use atty::Stream;
|
use atty::Stream;
|
||||||
|
use std::io::BufReader;
|
||||||
|
|
||||||
|
|
||||||
fn main() -> std::io::Result<()> {
|
fn main() -> std::io::Result<()> {
|
||||||
@ -12,7 +13,7 @@ fn main() -> std::io::Result<()> {
|
|||||||
let address = args.address;
|
let address = args.address;
|
||||||
let timeout = Duration::from_secs(args.timeout);
|
let timeout = Duration::from_secs(args.timeout);
|
||||||
let sleep = Duration::from_millis(args.wait);
|
let sleep = Duration::from_millis(args.wait);
|
||||||
let ports = args.ports;
|
let ports = handle_ports(args.ports.clone(), &args.range);
|
||||||
let threads = args.cthreads;
|
let threads = args.cthreads;
|
||||||
|
|
||||||
//Reading the payload from stdin (if applicable)
|
//Reading the payload from stdin (if applicable)
|
||||||
@ -34,17 +35,33 @@ fn main() -> std::io::Result<()> {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//Returns a correct list of ports, according to range.
|
||||||
|
//If range is true, we send a range of ports. Otherwise
|
||||||
|
//we send the original back
|
||||||
|
fn handle_ports(ports: Vec<u32>, range: &bool) -> Vec<u32> {
|
||||||
|
if *range {
|
||||||
|
if ports.iter().count() != 2 {
|
||||||
|
panic!("A range must be given only a start and an end value.");
|
||||||
|
}
|
||||||
|
let ret: Vec<u32> = (ports[0]..ports[1]).collect();
|
||||||
|
ret
|
||||||
|
} else {
|
||||||
|
ports
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
///Scan the ports on address by attempting a tcp connection.
|
///Scan the ports on address by attempting a tcp connection.
|
||||||
fn tcp_scan(address: String, timeout: Duration, sleep: Duration, ports: Vec<u32>) -> std::io::Result<()> {
|
fn tcp_scan(address: String, timeout: Duration, sleep: Duration, ports: Vec<u32>) -> std::io::Result<()> {
|
||||||
println!("Scanning {}", address);
|
println!("Scanning {}", address);
|
||||||
|
|
||||||
for i in ports {
|
for i in ports {
|
||||||
let socket = format!("{}:{}", &address, i);
|
let socket = resolve_add(&address.as_str(), &i);
|
||||||
let socket = socket.parse::<SocketAddr>().unwrap();
|
|
||||||
match TcpStream::connect_timeout(&socket, timeout) {
|
match TcpStream::connect_timeout(&socket, timeout) {
|
||||||
Ok(mut s) => {
|
Ok(mut _s) => {
|
||||||
println!("TCP/{} => ACCEPT", i);
|
println!("TCP/{} => ACCEPT", i);
|
||||||
s.write(b"Some padding")?;
|
//s.write(b"Some padding")?;
|
||||||
}
|
}
|
||||||
Err(_) => {
|
Err(_) => {
|
||||||
println!("TCP/{} => REJECT", i);
|
println!("TCP/{} => REJECT", i);
|
||||||
@ -58,8 +75,18 @@ fn tcp_scan(address: String, timeout: Duration, sleep: Duration, ports: Vec<u32>
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
///Try to stress the remote machine by attempting to every given port at once and sending random
|
///Takes either an hostname or an IP address and port, and resolve to a socket address.
|
||||||
///data
|
///DNS recordings can lead to multiple entries : we take the first one.
|
||||||
|
fn resolve_add(host: &str, port: &u32) -> SocketAddr {
|
||||||
|
let details = format!("{}:{}", host, port);
|
||||||
|
let server: Vec<_> = details
|
||||||
|
.to_socket_addrs()
|
||||||
|
.expect("Can't resolve your address")
|
||||||
|
.collect();
|
||||||
|
return server[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
///Try to stress the remote machine by attempting to every given port at once and sending payload
|
||||||
fn tcp_stress(payload: String, address: String, timeout: Duration, interval: Duration, ports: Vec<u32>, threads: u32) -> std::io::Result<()> {
|
fn tcp_stress(payload: String, address: String, timeout: Duration, interval: Duration, ports: Vec<u32>, threads: u32) -> std::io::Result<()> {
|
||||||
let mut handles = vec![];
|
let mut handles = vec![];
|
||||||
|
|
||||||
@ -72,20 +99,33 @@ fn tcp_stress(payload: String, address: String, timeout: Duration, interval: Dur
|
|||||||
let pay = payload.clone();
|
let pay = payload.clone();
|
||||||
let int = interval * 1000;
|
let int = interval * 1000;
|
||||||
let end_time = Instant::now() + int;
|
let end_time = Instant::now() + int;
|
||||||
|
//pay.push_str("\r\n");
|
||||||
|
|
||||||
|
|
||||||
handles.push(thread::spawn( move || {
|
handles.push(thread::spawn( move || {
|
||||||
let socket = format!("{}:{}", &add, &port);
|
let socket = resolve_add(&add.as_str(), &port);
|
||||||
let socket = socket.parse::<SocketAddr>().unwrap();
|
|
||||||
loop {
|
loop {
|
||||||
//Check if it is time to stop
|
//Check if it is time to stop
|
||||||
if Instant::now() >= end_time { break; }
|
if Instant::now() >= end_time { break; }
|
||||||
match TcpStream::connect_timeout(&socket, timeout) {
|
match TcpStream::connect_timeout(&socket, timeout) {
|
||||||
Ok(mut s) => {
|
Ok(mut s) => {
|
||||||
s.write(pay.as_bytes()).unwrap();
|
//Remote port answers
|
||||||
|
//Writing
|
||||||
|
s.write_all(pay.as_bytes()).unwrap();
|
||||||
s.flush().unwrap();
|
s.flush().unwrap();
|
||||||
//s.read(&mut [0; 128]).unwrap();
|
|
||||||
|
//Reading
|
||||||
|
s.set_read_timeout(Some(Duration::from_millis(500))).unwrap();
|
||||||
|
let mut reader = BufReader::new(s.try_clone().unwrap());
|
||||||
|
let mut answer = String::new();
|
||||||
|
let count = reader.read_line(&mut answer).unwrap_or(0);
|
||||||
|
if count > 0 {
|
||||||
|
println!("Read {} bytes for port {} -> {}", count, &port, answer);
|
||||||
|
}
|
||||||
|
|
||||||
|
//Sleeping
|
||||||
thread::sleep(Duration::from_millis(10));
|
thread::sleep(Duration::from_millis(10));
|
||||||
|
|
||||||
},
|
},
|
||||||
Err(_) => (),
|
Err(_) => (),
|
||||||
}
|
}
|
||||||
@ -107,7 +147,7 @@ fn tcp_stress(payload: String, address: String, timeout: Duration, interval: Dur
|
|||||||
#[derive(Debug, Parser)]
|
#[derive(Debug, Parser)]
|
||||||
#[command(author, version, about, long_about = None)]
|
#[command(author, version, about, long_about = None)]
|
||||||
struct Args {
|
struct Args {
|
||||||
///IP address to scan
|
///IP address or hostname to scan
|
||||||
#[arg(short, long)]
|
#[arg(short, long)]
|
||||||
address: String,
|
address: String,
|
||||||
///Timeout for each connection in seconds
|
///Timeout for each connection in seconds
|
||||||
@ -120,6 +160,9 @@ struct Args {
|
|||||||
///Ports to stress / scan, separated by commas (22,80)
|
///Ports to stress / scan, separated by commas (22,80)
|
||||||
#[arg(short, long, value_parser, num_args=1.., value_delimiter=',')]
|
#[arg(short, long, value_parser, num_args=1.., value_delimiter=',')]
|
||||||
ports: Vec<u32>,
|
ports: Vec<u32>,
|
||||||
|
///Set this flag to treat the ports as a range rather than a list
|
||||||
|
#[arg(long, short, action, default_value_t = false)]
|
||||||
|
range: bool,
|
||||||
///Set this flag to stress the ports instead of scanning them
|
///Set this flag to stress the ports instead of scanning them
|
||||||
#[arg(long, short, action, default_value_t = false)]
|
#[arg(long, short, action, default_value_t = false)]
|
||||||
stress: bool,
|
stress: bool,
|
||||||
|
Loading…
x
Reference in New Issue
Block a user