Compare commits

..
Author SHA1 Message Date
Dmitry SavintsevandBee f74cb456b1 socket_iterator: use itertools.iproducts for ip x ports set (#231)
Replace the current manual iterator-like implementation
with the call to the itertools.iproduct macro
to generate the cartesian product of the IPs and ports.

stdlib reference:
https://docs.rs/itertools/0.9.0/itertools/macro.iproduct.html

Co-authored-by: Bee <[email protected]>
2020-09-20 12:15:46 +01:00
Brandon 492d2fb3b3 fixed accessible issues 2020-09-20 01:25:41 +01:00
bergabmanandBee af53591ccb Added top ports, fixed accessibility. (#230)
* Refactored scanner struct for accessibility, fixed macros

Co-authored-by: Bee <[email protected]>
2020-09-19 18:24:08 +01:00
Bee 6405b43f5a Merge pull request #229 from dmitris/patch-1
fix typo
2020-09-19 00:54:24 +01:00
Dmitry Savintsev 9a71253697 fix typo 2020-09-18 22:13:33 +02:00
7 changed files with 275 additions and 216 deletions

No files matched your search

-78
View File
@@ -43,81 +43,3 @@ pkg/
# End of https://www.toptal.com/developers/gitignore/api/rust,archlinuxpackages
*.deb
# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio, WebStorm and Rider
# Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839
# User-specific stuff
.idea/**/workspace.xml
.idea/**/tasks.xml
.idea/**/usage.statistics.xml
.idea/**/dictionaries
.idea/**/shelf
# Generated files
.idea/**/contentModel.xml
# Sensitive or high-churn files
.idea/**/dataSources/
.idea/**/dataSources.ids
.idea/**/dataSources.local.xml
.idea/**/sqlDataSources.xml
.idea/**/dynamic.xml
.idea/**/uiDesigner.xml
.idea/**/dbnavigator.xml
# Gradle
.idea/**/gradle.xml
.idea/**/libraries
# Gradle and Maven with auto-import
# When using Gradle or Maven with auto-import, you should exclude module files,
# since they will be recreated, and may cause churn. Uncomment if using
# auto-import.
# .idea/artifacts
# .idea/compiler.xml
# .idea/jarRepositories.xml
# .idea/modules.xml
# .idea/*.iml
# .idea/modules
# *.iml
# *.ipr
# CMake
cmake-build-*/
# Mongo Explorer plugin
.idea/**/mongoSettings.xml
# File-based project format
*.iws
# IntelliJ
out/
# mpeltonen/sbt-idea plugin
.idea_modules/
# JIRA plugin
atlassian-ide-plugin.xml
# Cursive Clojure plugin
.idea/replstate.xml
# Crashlytics plugin (for Android Studio and IntelliJ)
com_crashlytics_export_strings.xml
crashlytics.properties
crashlytics-build.properties
fabric.properties
# Editor-based Rest Client
.idea/httpRequests
# Android studio 3.1+ serialized cache file
.idea/caches/build_file_checksums.ser
.idea/*
.idea*
.idea
Generated
+1 -1
View File
@@ -837,7 +837,7 @@ dependencies = [
[[package]]
name = "rustscan"
version = "1.9.0"
version = "1.8.0"
dependencies = [
"ansi_term 0.12.1",
"async-std",
+1 -1
View File
@@ -1,6 +1,6 @@
[package]
name = "rustscan"
version = "1.9.0"
version = "1.8.0"
authors = ["Bee <[email protected]>"]
edition = "2018"
description = "Faster Nmap Scanning with Rust"
+14 -6
View File
@@ -24,7 +24,7 @@
| <p align="center"><a href="https://hub.docker.com/r/cmnatic/rustscan">🐋 Docker (Recommended) </a></p> | <p align="center"><a href="https://github.com/RustScan/RustScan/releases">👩‍💻 Kali / Debian </p> | <p align="center"><a href="https://aur.archlinux.org/packages/rustscan/">🏗️ Arch </a></p> | <p align="center"><a href="https://crates.io/crates/rustscan">🔧 Cargo (Universal) </a></p> |
| ----------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------- |
| <p align="center"><img src="https://github.com/RustScan/RustScan/blob/master/pictures/docker.png?raw=true" /></p> | <p align="center"><img src="https://github.com/RustScan/RustScan/blob/master/pictures/kali.png?raw=true" /></p> | <p align="center"><img src="https://github.com/RustScan/RustScan/blob/master/pictures/arch.png?raw=true" /></p> | <p align="center"><img src="https://github.com/RustScan/RustScan/blob/master/pictures/rust.png?raw=true" /></p> |
| `docker pull rustscan/rustscan:v1.9.0` <p>[Usage](https://github.com/RustScan/RustScan#docker-whale) | [Read the install guide](https://github.com/Rustscan/RustScan/blob/master/README.md#%EF%B8%8F-debian--kali) | `yay -S rustscan` | `cargo install rustscan` |
| `docker pull rustscan/rustscan:v1.8.0` <p>[Usage](https://github.com/RustScan/RustScan#docker-whale) | [Read the install guide](https://github.com/Rustscan/RustScan/blob/master/README.md#%EF%B8%8F-debian--kali) | `yay -S rustscan` | `cargo install rustscan` |
<hr>
@@ -130,22 +130,22 @@ To install Docker, [follow their guide](https://docs.docker.com/engine/install/)
**Once Docker is installed, you can either build your own image using the `Dockerfile` (alpine) provided in the repo, or alternatively, use the published Docker image like below (most convenient)**
Please see our [DockerHub](https://hub.docker.com/repository/docker/rustscan/rustscan) for further our published versions. However, we recommend using our latest release [v1.9.0](https://github.com/RustScan/RustScan/releases/tag/1.9.0)
Please see our [DockerHub](https://hub.docker.com/repository/docker/rustscan/rustscan) for further our published versions. However, we recommend using our latest release [v1.8.0](https://github.com/RustScan/RustScan/releases/tag/1.8.0)
```
Stable and supported: rustscan/rustscan:v1.9.0
Stable and supported: rustscan/rustscan:v1.8.0
Bleeding edge (run at your own risk!): rustscan/rustscan:latest
```
We strongly recommend using the `v1.9.0` tag, as this is the latest major - stable - release of RustScan. This README uses the `v1.9.0` image by default, however, note that the `latest` image is considered experimental.
We strongly recommend using the `v1.8.0` tag, as this is the latest major - stable - release of RustScan. This README uses the `v1.8.0` image by default, however, note that the `latest` image is considered experimental.
#### To get started:
Simply run this command against the IP you want to target:
```bash
docker run -it --rm --name rustscan rustscan/rustscan:v1.9.0 <rustscan arguments here> <ip address to scan>
docker run -it --rm --name rustscan rustscan/rustscan:v1.8.0 <rustscan arguments here> <ip address to scan>
```
Note: this will scan the Docker's localhost, not your own.
@@ -155,7 +155,7 @@ Once done, you will no longer need to re-download the image (except when RustSca
You will have to run this command every time, so we suggest aliasing it to something memorable.
```bash
alias rustscan='docker run -it --rm --name rustscan rustscan/rustscan:v1.9.0'
alias rustscan='docker run -it --rm --name rustscan rustscan/rustscan:v1.8.0'
```
Then we can scan:
@@ -188,7 +188,15 @@ docker build -t <yourimagename> .
**Note for Mac users** Mac OS has a very, very small ulimit size. This will negatively impact RustScan by a significant amount. Please use the Docker container, or tell RustScan to up the ulimit size on every run.
Tap the brew:
```bash
brew tap brandonskerritt/rustscan
```
Install it:
```bash
brew install rustscan
```
+91 -59
View File
@@ -1,7 +1,6 @@
use serde_derive::Deserialize;
use std::collections::HashMap;
use std::fs;
use std::path::PathBuf;
use structopt::{clap::arg_enum, StructOpt};
const LOWEST_PORT_NUMBER: u16 = 1;
@@ -25,7 +24,6 @@ pub struct PortRange {
pub end: u16,
}
#[cfg(not(tarpaulin_include))]
fn parse_range(input: &str) -> Result<PortRange, String> {
let range = input
.split("-")
@@ -106,10 +104,6 @@ pub struct Opts {
#[structopt(long, possible_values = &ScanOrder::variants(), case_insensitive = true, default_value = "serial")]
pub scan_order: ScanOrder,
/// Input file containing IP addresses delimited by new lines
#[structopt(short, long)]
pub input: Option<PathBuf>,
/// The Nmap arguments to run.
/// To use the argument -A, end RustScan's args with '-- -A'.
/// Example: 'rustscan -T 1500 127.0.0.1 -- -A -sC'.
@@ -123,7 +117,6 @@ pub struct Opts {
pub top: bool,
}
#[cfg(not(tarpaulin_include))]
impl Opts {
pub fn read() -> Self {
let mut opts = Opts::from_args();
@@ -190,7 +183,6 @@ impl Opts {
/// Struct used to deserialize the options specified within our config file.
/// These will be further merged with our command line arguments in order to
/// generate the final Opts struct.
#[cfg(not(tarpaulin_include))]
#[derive(Debug, Deserialize)]
pub struct Config {
addresses: Option<Vec<String>>,
@@ -203,11 +195,9 @@ pub struct Config {
no_nmap: Option<bool>,
ulimit: Option<rlimit::rlim>,
scan_order: Option<ScanOrder>,
input: Option<PathBuf>,
command: Option<Vec<String>>,
}
#[cfg(not(tarpaulin_include))]
impl Config {
/// Reads the configuration file with TOML format and parses it into a
/// Config struct.
@@ -249,53 +239,43 @@ impl Config {
#[cfg(test)]
mod tests {
use super::{Config, Opts, PortRange, ScanOrder};
impl Config {
fn default() -> Self {
Self {
addresses: Some(vec!["127.0.0.1".to_owned()]),
ports: None,
range: None,
greppable: Some(true),
batch_size: Some(25_000),
timeout: Some(1_000),
ulimit: None,
no_nmap: Some(false),
command: Some(vec!["-A".to_owned()]),
accessible: Some(true),
scan_order: Some(ScanOrder::Random),
}
}
}
impl Opts {
pub fn default() -> Self {
Self {
addresses: vec![],
ports: None,
range: None,
greppable: true,
batch_size: 0,
timeout: 0,
ulimit: None,
command: vec![],
accessible: false,
no_nmap: false,
scan_order: ScanOrder::Serial,
no_config: true,
top: false,
}
}
}
#[test]
fn opts_no_merge_when_config_is_ignored() {
let mut opts = Opts::default();
let config = Config::default();
let mut opts = Opts {
addresses: vec![],
ports: None,
range: None,
greppable: false,
batch_size: 0,
timeout: 0,
ulimit: None,
command: vec![],
accessible: false,
no_nmap: false,
scan_order: ScanOrder::Serial,
no_config: true,
top: false,
};
let config = Config {
addresses: Some(vec!["127.0.0.1".to_owned()]),
ports: None,
range: None,
greppable: Some(true),
batch_size: Some(25_000),
timeout: Some(1_000),
ulimit: None,
no_nmap: Some(false),
command: Some(vec!["-A".to_owned()]),
accessible: Some(true),
scan_order: Some(ScanOrder::Random),
};
opts.merge(&config);
assert_eq!(opts.addresses, vec![] as Vec<String>);
assert_eq!(opts.greppable, true);
assert_eq!(opts.greppable, false);
assert_eq!(opts.accessible, false);
assert_eq!(opts.timeout, 0);
assert_eq!(opts.command, vec![] as Vec<String>);
@@ -304,8 +284,35 @@ mod tests {
#[test]
fn opts_merge_required_arguments() {
let mut opts = Opts::default();
let config = Config::default();
let mut opts = Opts {
addresses: vec![],
ports: None,
range: None,
greppable: false,
batch_size: 0,
timeout: 0,
ulimit: None,
command: vec![],
accessible: false,
scan_order: ScanOrder::Serial,
no_nmap: false,
no_config: false,
top: false,
};
let config = Config {
addresses: Some(vec!["127.0.0.1".to_owned()]),
ports: None,
no_nmap: Some(false),
range: None,
greppable: Some(true),
batch_size: Some(25_000),
timeout: Some(1_000),
ulimit: None,
command: Some(vec!["-A".to_owned()]),
accessible: Some(true),
scan_order: Some(ScanOrder::Random),
};
opts.merge_required(&config);
@@ -319,13 +326,38 @@ mod tests {
#[test]
fn opts_merge_optional_arguments() {
let mut opts = Opts::default();
let mut config = Config::default();
config.range = Some(PortRange {
start: 1,
end: 1_000,
});
config.ulimit = Some(1_000);
let mut opts = Opts {
addresses: vec![],
ports: None,
range: None,
greppable: false,
batch_size: 0,
timeout: 0,
ulimit: None,
command: vec![],
accessible: false,
scan_order: ScanOrder::Serial,
no_nmap: false,
no_config: false,
top: false,
};
let config = Config {
addresses: None,
ports: None,
range: Some(PortRange {
start: 1,
end: 1_000,
}),
greppable: None,
batch_size: None,
timeout: None,
no_nmap: Some(false),
ulimit: Some(1_000),
command: None,
accessible: None,
scan_order: None,
};
opts.merge_optional(&config);
+164 -67
View File
@@ -21,12 +21,8 @@ use futures::executor::block_on;
use rlimit::Resource;
use rlimit::{getrlimit, setrlimit};
use std::collections::HashMap;
use std::fs::File;
use std::io::prelude::*;
use std::io::BufReader;
use std::net::ToSocketAddrs;
use std::process::Command;
use std::str::FromStr;
use std::{net::IpAddr, time::Duration};
extern crate colorful;
@@ -208,24 +204,6 @@ fn build_nmap_arguments<'a>(
arguments
}
#[cfg(not(tarpaulin_include))]
/// Parses an input file of IPs and uses those
fn read_ips_from_file(ips: String) -> Result<Vec<std::net::IpAddr>, std::io::Error> {
// if we cannot open it as a file, it is not a file so move on
let file = File::open(ips)?;
let reader = BufReader::new(file);
let mut ips: Vec<std::net::IpAddr> = Vec::new();
for str_ip in reader.lines() {
match IpAddr::from_str(&str_ip.unwrap()) {
Ok(x) => ips.push(x),
Err(y) => panic!("File does not contain valid IP address. Error at {}", y),
}
}
Ok(ips)
}
fn parse_addresses(opts: &Opts) -> Vec<IpAddr> {
let mut ips: Vec<IpAddr> = Vec::new();
@@ -235,18 +213,11 @@ fn parse_addresses(opts: &Opts) -> Vec<IpAddr> {
_ => match format!("{}:{}", &ip_or_host, 80).to_socket_addrs() {
Ok(mut iter) => ips.push(iter.nth(0).unwrap().ip()),
_ => {
warning!(
format!("Host {:?} could not be resolved.", ip_or_host),
opts.greppable,
opts.accessible
);
let failed_to_resolve = format!("Host {:?} could not be resolved.", ip_or_host);
warning!(failed_to_resolve, opts.greppable, opts.accessible);
}
},
}
match read_ips_from_file(ip_or_host.to_owned()) {
Ok(x) => ips.extend(x),
_ => (),
}
}
ips
@@ -264,13 +235,7 @@ fn adjust_ulimit_size(opts: &Opts) -> rlimit::rlim {
opts.accessible
);
}
Err(_) => {
warning!(
"ERROR. Failed to set ulimit value.",
opts.greppable,
opts.accessible
);
}
Err(_) => println!("{}", "ERROR. Failed to set ulimit value."),
}
}
@@ -308,8 +273,10 @@ fn infer_batch_size(opts: &Opts, ulimit: rlimit::rlim) -> u16 {
// When the ulimit is higher than the batch size let the user know that the
// batch size can be increased unless they specified the ulimit themselves.
else if ulimit + 2 > batch_size && (opts.ulimit.is_none()) {
detail!(format!("File limit higher than batch size. Can increase speed by increasing batch size '-b {}'.", ulimit - 100),
opts.greppable, opts.accessible);
detail!(format!(
"File limit higher than batch size. Can increase speed by increasing batch size '-b {}'.",
ulimit - 100
), opts.greppable, opts.accessible);
}
batch_size as u16
@@ -317,22 +284,50 @@ fn infer_batch_size(opts: &Opts, ulimit: rlimit::rlim) -> u16 {
#[cfg(test)]
mod tests {
use crate::{adjust_ulimit_size, infer_batch_size, parse_addresses, print_opening, Opts};
use crate::{
adjust_ulimit_size, infer_batch_size, parse_addresses, print_opening, Opts, ScanOrder,
};
use std::net::Ipv4Addr;
#[test]
fn batch_size_lowered() {
let mut opts = Opts::default();
opts.batch_size = 50_000;
let opts = Opts {
addresses: vec!["127.0.0.1".to_owned()],
ports: None,
range: None,
greppable: true,
batch_size: 50_000,
timeout: 1_000,
ulimit: Some(2_000),
command: Vec::new(),
accessible: false,
scan_order: ScanOrder::Serial,
no_config: false,
no_nmap: false,
top: false,
};
let batch_size = infer_batch_size(&opts, 120);
assert!(batch_size < opts.batch_size);
assert!(batch_size < 50_000);
}
#[test]
fn batch_size_lowered_average_size() {
let mut opts = Opts::default();
opts.batch_size = 50_000;
let opts = Opts {
addresses: vec!["127.0.0.1".to_owned()],
ports: None,
range: None,
greppable: true,
batch_size: 50_000,
timeout: 1_000,
ulimit: Some(2_000),
command: Vec::new(),
accessible: false,
scan_order: ScanOrder::Serial,
no_config: false,
no_nmap: false,
top: false,
};
let batch_size = infer_batch_size(&opts, 9_000);
assert!(batch_size == 3_000);
@@ -341,8 +336,21 @@ mod tests {
fn batch_size_equals_ulimit_lowered() {
// because ulimit and batch size are same size, batch size is lowered
// to ULIMIT - 100
let mut opts = Opts::default();
opts.batch_size = 50_000;
let opts = Opts {
addresses: vec!["127.0.0.1".to_owned()],
ports: None,
range: None,
greppable: true,
batch_size: 50_000,
timeout: 1_000,
ulimit: Some(2_000),
command: Vec::new(),
accessible: false,
scan_order: ScanOrder::Serial,
no_config: false,
no_nmap: false,
top: false,
};
let batch_size = infer_batch_size(&opts, 5_000);
assert!(batch_size == 4_900);
@@ -350,36 +358,86 @@ mod tests {
#[test]
fn batch_size_adjusted_2000() {
// ulimit == batch_size
let mut opts = Opts::default();
opts.batch_size = 50_000;
opts.ulimit = Some(2_000);
let opts = Opts {
addresses: vec!["127.0.0.1".to_owned()],
ports: None,
range: None,
greppable: true,
batch_size: 50_000,
timeout: 1_000,
ulimit: Some(2_000),
command: Vec::new(),
accessible: false,
scan_order: ScanOrder::Serial,
no_config: false,
no_nmap: false,
top: false,
};
let batch_size = adjust_ulimit_size(&opts);
assert!(batch_size == 2_000);
}
#[test]
fn test_print_opening_no_panic() {
let mut opts = Opts::default();
opts.ulimit = Some(2_000);
let opts = Opts {
addresses: vec!["127.0.0.1".to_owned()],
ports: None,
range: None,
greppable: true,
batch_size: 50_000,
timeout: 1_000,
ulimit: Some(2_000),
command: Vec::new(),
accessible: false,
scan_order: ScanOrder::Serial,
no_config: false,
no_nmap: false,
top: false,
};
// print opening should not panic
print_opening(&opts);
assert!(1 == 1);
}
#[test]
fn test_high_ulimit_no_greppable_mode() {
let mut opts = Opts::default();
opts.batch_size = 10;
opts.greppable = false;
fn test_high_ulimit_no_quiet_mode() {
let opts = Opts {
addresses: vec!["127.0.0.1".to_owned()],
ports: None,
range: None,
greppable: false,
batch_size: 10,
timeout: 1_000,
ulimit: None,
command: Vec::new(),
accessible: true,
scan_order: ScanOrder::Serial,
no_config: false,
no_nmap: false,
top: false,
};
let batch_size = infer_batch_size(&opts, 1_000_000);
infer_batch_size(&opts, 1_000_000);
assert!(batch_size == opts.batch_size);
assert!(1 == 1);
}
#[test]
fn parse_correct_addresses() {
let mut opts = Opts::default();
opts.addresses = vec!["127.0.0.1".to_owned(), "192.168.0.0/30".to_owned()];
let opts = Opts {
addresses: vec!["127.0.0.1".to_owned(), "192.168.0.0/30".to_owned()],
ports: None,
range: None,
greppable: true,
batch_size: 10,
timeout: 1_000,
ulimit: Some(2_000),
command: Vec::new(),
accessible: false,
scan_order: ScanOrder::Serial,
no_config: false,
no_nmap: false,
top: false,
};
let ips = parse_addresses(&opts);
assert_eq!(
@@ -396,8 +454,21 @@ mod tests {
#[test]
fn parse_correct_host_addresses() {
let mut opts = Opts::default();
opts.addresses = vec!["google.com".to_owned()];
let opts = Opts {
addresses: vec!["google.com".to_owned()],
ports: None,
range: None,
greppable: true,
batch_size: 10,
timeout: 1_000,
ulimit: Some(2_000),
command: Vec::new(),
accessible: false,
scan_order: ScanOrder::Serial,
no_config: false,
no_nmap: false,
top: false,
};
let ips = parse_addresses(&opts);
assert_eq!(ips.len(), 1);
@@ -405,8 +476,21 @@ mod tests {
#[test]
fn parse_correct_and_incorrect_addresses() {
let mut opts = Opts::default();
opts.addresses = vec!["127.0.0.1".to_owned(), "im_wrong".to_owned()];
let opts = Opts {
addresses: vec!["127.0.0.1".to_owned(), "im_wrong".to_owned()],
ports: None,
range: None,
greppable: true,
batch_size: 10,
timeout: 1_000,
ulimit: Some(2_000),
command: Vec::new(),
accessible: false,
scan_order: ScanOrder::Serial,
no_config: false,
no_nmap: false,
top: false,
};
let ips = parse_addresses(&opts);
assert_eq!(ips, [Ipv4Addr::new(127, 0, 0, 1),]);
@@ -414,8 +498,21 @@ mod tests {
#[test]
fn parse_incorrect_addresses() {
let mut opts = Opts::default();
opts.addresses = vec!["im_wrong".to_owned(), "300.10.1.1".to_owned()];
let opts = Opts {
addresses: vec!["im_wrong".to_owned(), "300.10.1.1".to_owned()],
ports: None,
range: None,
greppable: true,
batch_size: 10,
timeout: 1_000,
ulimit: Some(2_000),
command: Vec::new(),
accessible: false,
scan_order: ScanOrder::Serial,
no_config: false,
no_nmap: false,
top: false,
};
let ips = parse_addresses(&opts);
assert_eq!(ips.is_empty(), true);
+4 -4
View File
@@ -26,7 +26,7 @@ pub struct Scanner {
ips: Vec<IpAddr>,
batch_size: u16,
timeout: Duration,
greppable: bool,
quiet: bool,
port_strategy: PortStrategy,
accessible: bool,
}
@@ -36,14 +36,14 @@ impl Scanner {
ips: &[IpAddr],
batch_size: u16,
timeout: Duration,
greppable: bool,
quiet: bool,
port_strategy: PortStrategy,
accessible: bool,
) -> Self {
Self {
batch_size,
timeout,
greppable,
quiet,
port_strategy,
ips: ips.iter().map(|ip| ip.to_owned()).collect(),
accessible,
@@ -110,7 +110,7 @@ impl Scanner {
Err(e) => debug!("Shutdown stream error {}", &e),
_ => {}
}
if !self.greppable {
if !self.quiet {
if self.accessible {
println!("Open {}", socket.to_string());
} else {