Compare commits

..
Author SHA1 Message Date
Brandon 9717fc661f Merge pull request #174 from caass/patch-1
Allow compilation on windows (#171)
2020-08-27 20:36:29 +01:00
Cassandra Fridkin a0119cbece Skip tests that deal with ulimit on windows 2020-08-27 14:45:08 -04:00
Cassandra Fridkin 7a3d900eed Revert "Try to DRY out code"
This reverts commit 87b802d7ae.
2020-08-27 14:32:43 -04:00
Cassandra Fridkin 87b802d7ae Try to DRY out code 2020-08-27 14:23:13 -04:00
Cass 69bdff2ce2 semicolon
that's what i get for using github to edit the file smh
2020-08-27 14:13:37 -04:00
Cass 0a8d108c3a Add a windows-specific warning
We should probably let folks know it's gonna be slow that the Docker alternative exists.
2020-08-27 13:19:08 -04:00
Cass 24c713a634 Allow compilation on windows (#171) 2020-08-27 13:14:25 -04:00
Brandon 1745be38ce conditional use statement in main 2020-08-27 12:11:23 +01:00
Brandon 07c0236a75 cargo fmt 2020-08-27 11:48:54 +01:00
Brandon b91d632657 made windows conditional import 2020-08-27 11:37:44 +01:00
Brandon 7df1b72e0c changed rlimit typehint to u32 2020-08-27 00:59:32 +01:00
Brandon 4bbd3943bd changes
that enable windows support
2020-08-26 17:04:23 +01:00
16 changed files with 559 additions and 730 deletions

No files matched your search

-67
View File
@@ -115,73 +115,6 @@
"doc",
"design"
]
},
{
"login": "Ferryistaken",
"name": "Alessandro Ferrari",
"avatar_url": "https://avatars3.githubusercontent.com/u/47927670?v=4",
"profile": "https://github.com/Ferryistaken",
"contributions": [
"content"
]
},
{
"login": "Phenomite",
"name": "Phenomite",
"avatar_url": "https://avatars2.githubusercontent.com/u/8285537?v=4",
"profile": "https://github.com/Phenomite",
"contributions": [
"content"
]
},
{
"login": "SuperSandro2000",
"name": "Sandro",
"avatar_url": "https://avatars2.githubusercontent.com/u/7258858?v=4",
"profile": "https://supersandro.de/",
"contributions": [
"content",
"bug",
"code"
]
},
{
"login": "caass",
"name": "Cass",
"avatar_url": "https://avatars2.githubusercontent.com/u/25358963?v=4",
"profile": "https://swag.lgbt",
"contributions": [
"platform",
"code",
"bug"
]
},
{
"login": "niklasmohrin",
"name": "Niklas Mohrin",
"avatar_url": "https://avatars0.githubusercontent.com/u/47574893?v=4",
"profile": "https://github.com/niklasmohrin",
"contributions": [
"doc"
]
},
{
"login": "tim77",
"name": "Artem Polishchuk",
"avatar_url": "https://avatars0.githubusercontent.com/u/5614476?v=4",
"profile": "https://liberapay.com/Artem4/",
"contributions": [
"platform"
]
},
{
"login": "buermarc",
"name": "buermarc",
"avatar_url": "https://avatars2.githubusercontent.com/u/44375277?v=4",
"profile": "https://github.com/buermarc",
"contributions": [
"code"
]
}
],
"contributorsPerLine": 7,
-2
View File
@@ -1,2 +0,0 @@
.git
Dockerfile
+1 -1
View File
@@ -1,4 +1,4 @@
github: bee-san
github: brandonskerritt
patreon: brandonskerritt
ko_fi: hacker
custom: ["https://monzo.me/bee", "https://paypal.me/brandonskerritt"]
-4
View File
@@ -1,4 +0,0 @@
template: |
## What’s Changed
$CHANGES
-3
View File
@@ -1,3 +0,0 @@
[submodule "src/rustscan_scripting_engine"]
path = src/rustscan_scripting_engine
url = https://github.com/RustScan/rustscan_scripting_engine
+42 -23
View File
@@ -1,37 +1,56 @@
language: rust
os:
- linux
- osx
- linux
- osx
- windows
arch:
- amd64
- arm64
- amd64
- arm64
services:
- docker
- docker
before_install:
- rustup component add rustfmt
# cargo test
rust:
- stable
- beta
- stable
- beta
jobs:
fast_finish: true
# Cargo fmt
script:
- cargo build
- cargo test
- cargo fmt -- --check
# automatic deployment to cargo on release
# deploy:
# provider: releases
# api_key: "GITHUB OAUTH TOKEN"
# file: "FILE TO UPLOAD"
# skip_cleanup: true
# on:
# tags: true
# after_success:
# - wget https://raw.githubusercontent.com/DiscordHooks/travis-ci-discord-webhook/master/send.sh
# - chmod +x send.sh
# - ./send.sh success $WEBHOOK_URL
# only deploy on master branch
# on:
# branch: master
# provider: cargo
# token: $crates
after_failure:
- wget https://raw.githubusercontent.com/DiscordHooks/travis-ci-discord-webhook/master/send.sh
- chmod +x send.sh
- "./send.sh failure $WEBHOOK_URL"
after_success: |
if [ -n "${GITHUB_TOKEN}" ]; then
[ "${TRAVIS_BRANCH} " = master ] &&
[ "${TRAVIS_PULL_REQUEST}" = false ] &&
cargo doc &&
echo "<meta http-equiv=refresh content=0;url=rustscan/index.html>" > target/doc/index.html &&
sudo pip install ghp-import &&
ghp-import -n target/doc &&
git push -fq https://${GITHUB_TOKEN}@github.com/${TRAVIS_REPO_SLUG}.git gh-pages
fi
env:
global:
secure: Gf24LG7Hr3Q0drWyPrvISTL35Z57DxdS/wIOo7v+ZdauzY3Qjvv3Lh4TY//C4sdN0g1CLj4yjg7LQOqF4XRdh2Qah7JZPv/i2B5uz9nwjTsoRzmmMWML9VtfT1u53nSFqsNeXbvpmoNG9rfMXcoiyd+Lv+EgniHphDEBlXvS9n9KMcpOi1F2x7bsH0EK37wQYKMTpCCd5TPUPdZou7BJNiZS3ikRLpTRFF7uIN4gYtRH9kU5uBSphWgnJ6eBm2ItzZcUPOgrjAoF4RkmZAzGG4idDh1Qkf3No3nZwT3U5NSk8h7owtJ4oADlRgYqYKCB4YcC9cAKCJgDsyMlKKMkEyagvw1seFnBLCY3ljsW4EklpVEZ0rVqZOx4pTkIaxIxytLTC9pHmdDSnhdhFknOcVAQQWjg+nAZfD07QUeAqUdr0i/vxQoZitvbNY0znzEarycE1waKvwlkiB4gt385U1CBfYldydnOnRKkdtLNrCLClu13HU0djmSXMhojQJhTuNxTHtZAf5fKcWIWwkVbNZ5MNKYjQh4rKufqW3o1iHphB8dfAGsDEPv0RQMtVbu6H4+clCouLkFlxHOIrrOJ5SXQ8cIATPhnJR7y4cSDdRaN0DRLRjeLAIKlbOGBtNuFC+BftocaTY5qN42aKAMqQcT30ycfiLuRVJNiQJ5pkto=
- wget https://raw.githubusercontent.com/DiscordHooks/travis-ci-discord-webhook/master/send.sh
- chmod +x send.sh
- ./send.sh failure $WEBHOOK_URL
+16 -16
View File
@@ -14,22 +14,22 @@ appearance, race, religion, or sexual identity and orientation.
Examples of behavior that contributes to creating a positive environment
include:
- Using welcoming and inclusive language
- Being respectful of differing viewpoints and experiences
- Gracefully accepting constructive criticism
- Focusing on what is best for the community
- Showing empathy towards other community members
* Using welcoming and inclusive language
* Being respectful of differing viewpoints and experiences
* Gracefully accepting constructive criticism
* Focusing on what is best for the community
* Showing empathy towards other community members
Examples of unacceptable behavior by participants include:
- The use of sexualized language or imagery and unwelcome sexual attention or
advances
- Trolling, insulting/derogatory comments, and personal or political attacks
- Public or private harassment
- Publishing others' private information, such as a physical or electronic
address, without explicit permission
- Other conduct which could reasonably be considered inappropriate in a
professional setting
* The use of sexualized language or imagery and unwelcome sexual attention or
advances
* Trolling, insulting/derogatory comments, and personal or political attacks
* Public or private harassment
* Publishing others' private information, such as a physical or electronic
address, without explicit permission
* Other conduct which could reasonably be considered inappropriate in a
professional setting
## Our Responsibilities
@@ -55,7 +55,7 @@ further defined and clarified by project maintainers.
## Enforcement
Instances of abusive, harassing, or otherwise unacceptable behavior may be
reported by contacting the project team at our discord via a private message to any of the owners. All
reported by contacting the project team at brandon@skerritt.blog. All
complaints will be reviewed and investigated and will result in a response that
is deemed necessary and appropriate to the circumstances. The project team is
obligated to maintain confidentiality with regard to the reporter of an incident.
@@ -67,10 +67,10 @@ members of the project's leadership.
## Attribution
This Code of Conduct is adapted from the Contributor Covenant homepage, version 1.4,
This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4,
available at https://www.contributor-covenant.org/version/1/4/code-of-conduct.html
homepage: https://www.contributor-covenant.org
[homepage]: https://www.contributor-covenant.org
For answers to common questions about this code of conduct, see
https://www.contributor-covenant.org/faq
Generated
+5 -84
View File
@@ -208,19 +208,6 @@ version = "0.1.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822"
[[package]]
name = "cidr-utils"
version = "0.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "89f7b6372f3c783f6d44c0da283d3694af4104967bf2371cb3bdc9d09e1cfe00"
dependencies = [
"debug-helper",
"num-bigint",
"num-traits",
"once_cell",
"regex",
]
[[package]]
name = "clap"
version = "2.33.2"
@@ -279,12 +266,6 @@ dependencies = [
"lazy_static",
]
[[package]]
name = "debug-helper"
version = "0.3.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9a8a5bb894f24f42c247f19b25928a88e31867c0f84552c05df41a9dd527435e"
[[package]]
name = "dirs"
version = "3.0.1"
@@ -540,36 +521,6 @@ dependencies = [
"fastrand",
]
[[package]]
name = "num-bigint"
version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b7f3fc75e3697059fb1bc465e3d8cca6cf92f56854f201158b3f9c77d5a3cfa0"
dependencies = [
"autocfg",
"num-integer",
"num-traits",
]
[[package]]
name = "num-integer"
version = "0.1.43"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8d59457e662d541ba17869cf51cf177c0b5f0cbf476c66bdc90bf1edac4f875b"
dependencies = [
"autocfg",
"num-traits",
]
[[package]]
name = "num-traits"
version = "0.2.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ac267bcc07f48ee5f8935ab0d24f316fb722d7a1292e2913f0cc196b29ffd611"
dependencies = [
"autocfg",
]
[[package]]
name = "num_cpus"
version = "1.13.0"
@@ -808,11 +759,10 @@ dependencies = [
[[package]]
name = "rustscan"
version = "1.8.0"
version = "1.7.1"
dependencies = [
"ansi_term 0.12.1",
"async-std",
"cidr-utils",
"colored",
"colorful",
"dirs",
@@ -822,11 +772,8 @@ dependencies = [
"log",
"rand",
"rlimit",
"serde",
"serde_derive",
"shell-words",
"structopt",
"toml",
]
[[package]]
@@ -835,23 +782,6 @@ version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ea6a9290e3c9cf0f18145ef7ffa62d68ee0bf5fcd651017e586dc7fd5da448c2"
[[package]]
name = "serde"
version = "1.0.115"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e54c9a88f2da7238af84b5101443f0c0d0a3bbdc455e34a5c9497b1903ed55d5"
[[package]]
name = "serde_derive"
version = "1.0.115"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "609feed1d0a73cc36a0182a840a9b37b4a82f0b1150369f0536a9e3f2a31dc48"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "shell-words"
version = "1.0.0"
@@ -884,9 +814,9 @@ checksum = "8ea5119cdb4c55b55d432abb513a0429384878c15dde60cc77b1c99de1a95a6a"
[[package]]
name = "structopt"
version = "0.3.17"
version = "0.3.16"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6cc388d94ffabf39b5ed5fadddc40147cb21e605f53db6f8f36a625d27489ac5"
checksum = "de5472fb24d7e80ae84a7801b7978f95a19ec32cb1876faea59ab711eb901976"
dependencies = [
"clap",
"lazy_static",
@@ -895,9 +825,9 @@ dependencies = [
[[package]]
name = "structopt-derive"
version = "0.4.10"
version = "0.4.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5e2513111825077552a6751dfad9e11ce0fba07d7276a3943a037d7e93e64c5f"
checksum = "1e0eb37335aeeebe51be42e2dc07f031163fbabfa6ac67d7ea68b5c2f68d5f99"
dependencies = [
"heck",
"proc-macro-error",
@@ -944,15 +874,6 @@ dependencies = [
"lazy_static",
]
[[package]]
name = "toml"
version = "0.5.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ffc92d160b1eef40665be3a05630d003936a3bc7da7421277846c2613e92c71a"
dependencies = [
"serde",
]
[[package]]
name = "unicode-segmentation"
version = "1.6.0"
+5 -7
View File
@@ -1,6 +1,6 @@
[package]
name = "rustscan"
version = "1.8.0"
version = "1.7.1"
authors = ["Bee <[email protected]>"]
edition = "2018"
description = "Faster Nmap Scanning with Rust"
@@ -18,10 +18,9 @@ exclude = [
[dependencies]
colored = "2.0.0"
structopt = "0.3.17"
structopt = "0.3.16"
async-std = "1.6.3"
futures = "0.3"
rlimit = "0.4.0"
shell-words = "1.0.0"
log = "0.4.0"
env_logger = "0.7.1"
@@ -30,10 +29,9 @@ gcd = "2.0.1"
rand = "0.7.3"
colorful = "0.2.1"
ansi_term = "0.12.1"
toml = "0.5.6"
serde = "1.0.115"
serde_derive = "1.0.115"
cidr-utils = "0.5.0"
[target."cfg(not(windows))".dependencies]
rlimit = "0.4.0"
[package.metadata.deb]
depends = "$auto, nmap"
+13 -7
View File
@@ -1,16 +1,22 @@
FROM rust:alpine as builder
RUN apk add --no-cache build-base
RUN apk update && \
apk add build-base && \
rm -rf /var/cache/apk/*
WORKDIR /usr/src/rustscan
COPY . .
RUN cargo install --path .
FROM alpine:3.12
FROM alpine
LABEL author="Hydragyrum <https://github.com/Hydragyrum>"
RUN addgroup -S rustscan && \
adduser -S -G rustscan rustscan && \
ulimit -n 100000 && \
apk add --no-cache nmap nmap-scripts wget
RUN ulimit -n 100000 && \
apk update && \
apk add \
nmap nmap-scripts wget \
&& \
rm -rf /var/cache/apk/*
COPY --from=builder /usr/local/cargo/bin/rustscan /usr/local/bin/rustscan
RUN addgroup -S rustscan && \
adduser -S -G rustscan rustscan
USER rustscan
ENTRYPOINT [ "/usr/local/bin/rustscan" ]
ENTRYPOINT ["/usr/local/bin/rustscan"]
+309 -50
View File
@@ -5,14 +5,15 @@
<a href="https://github.com/RustScan/RustScan#-usage">Usage Guide</a>
⬅️
<br>
<img src="pictures/rustscan.png" height=400px width=400px>
<img src="https://github.com/RustScan/RustScan/blob/master/pictures/rustscan.png?raw=true">
</p>
<p align="center">
<u><b> 👽 The Modern Day Port Scanner 👽
<u><b> Turns a 17 minutes Nmap scan into 19 seconds. </b></u><br> Find all open ports <b>fast</b> with RustScan, automatically pipe them into Nmap.
</p>
<p align="center">
<img alt="AUR version" src="https://img.shields.io/aur/version/rustscan">
<img src="https://img.shields.io/badge/Built%20with-Rust-Purple">
<img alt="GitHub commit activity" src="https://img.shields.io/github/commit-activity/w/brandonskerritt/rustscan">
<img alt="GitHub All Releases" src="https://img.shields.io/github/downloads/rustscan/rustscan/total?label=GitHub%20Downloads">
<img alt="Crates.io" src="https://img.shields.io/crates/d/rustscan?label=Cargo%20Downloads">
<img alt="Discord" src="https://img.shields.io/discord/736614461313515576">
@@ -20,49 +21,322 @@
</p>
<hr>
| <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:alpine` <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` |
| <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:alpine` <p>[Usage](https://github.com/RustScan/RustScan#docker-whale) | [Read the install guide](https://github.com/brandonskerritt/RustScan/blob/master/README.md#%EF%B8%8F-debian--kali) | `yay -S rustscan` | `cargo install rustscan`
<hr>
# 🤔 What is this?
1. Find ports quickly using Rustscan (**3 seconds at its fastest**).
2. Automatically runs `nmap` on those ports.
3. ???
4. Profit!
RustScan is the port scanner for the modern world. Built with speed & you in mind.
<table>
<tr>
<th>Name</th>
<th>⚡ Nmap <b>with RustScan</b> ⚡ </th>
<th>🐢 Nmap 🐢</th>
</tr>
<tr>
<th>Gif</th>
<td><img src="https://github.com/RustScan/RustScan/blob/master/pictures/with_rustscan.gif?raw=true" alt="The guy she tells you not to worry about"></td>
<td><img src="https://github.com/RustScan/RustScan/blob/master/pictures/without_rustscan.gif?raw=true" alt="You"></td>
</tr>
<tr>
<th>Time</th>
<td><b>39 seconds</b></td>
<td><b>17 minutes and 41 seconds</b></td>
</tr>
</table>
![pictures](pictures/with_rustscan.gif)
<sub><b>Note</b> This is an older gif. RustScan's current top speed is 3 seconds for all 65k ports. This gif is 26 seconds.</sub><br>
RustScans **only** job is to reduce the friction between finding open ports and inputting them into nmap.
# ✨ Features
- Scans all 65k ports in **3 seconds** (max speed) **or as slow as you'd like**
- Saves you time by automatically piping it into Nmap. No more manual copying and pasting!
- Adaptive Learning System. The scanner learns how _you_ use it and improves itself over time.
- Intuitive Scripting Engine support. Write scripts in Python!
- IPv6, CIDR, top ports, multiple IP scanning, host resolution, and more!
* Scans all 65k ports in **8 seconds** (on 10k batch size).
* Saves you time by automatically piping it into Nmap. No more manual copying and pasting!
* Does one thing and does it well. **Only** purpose is to improve Nmap, not replace it!
* Let's you choose what Nmap commands to run, or uses the default.
* IPv6 Support
## ‼️ Important Links
| Installation Guide | Documentation | Discord |
| -------------------------------------------------------------------------------------- | -------------------------------------------------------- | ---------------------------------------- |
| 📖 [Installation Guide](https://github.com/RustScan/RustScan#-full-installation-guide) | 📚 [Documentation](https://rustscan.github.io/RustScan/) | 🦜 [Discord](https://discord.gg/GFrQsGy) |
| Installation Guide | Documentation | Discord |
| ------------------ | ------------- | ------- |
| 📖 [Installation Guide](https://github.com/RustScan/RustScan#-full-installation-guide) | 📚 [Documentation](https://github.com/RustScan/RustScan/issues/89) | 🦜 [Discord](https://discord.gg/GFrQsGy)
## 🙋 Table of Contents
* 📖 [Installation Guide](https://github.com/RustScan/RustScan#-full-installation-guide)
* 🐋 [Docker Usage](https://github.com/RustScan/RustScan#docker-whale)
* 🦜 [Discord](https://discord.gg/GFrQsGy)
* 🤸 [Usage](https://github.com/RustScan/RustScan#-usage)
* 🎪 [Community](https://github.com/RustScan/RustScan#-community)
# 🔭 Why RustScan?
RustScan is a port scanner built for the modern day world. Initially created to aid in CTFs, but now expanded to real world usage.
One of RustScan's most popular features is the Nmap extension. Quickly scan ports with RustScan, run Nmap on the found ports.
**RustScan running in 8 seconds and finding all open ports out of 65k.**
Why spend time running fast scans and manually copying the ports, or waiting for a 20 minute scan to finish when you can just do all 65k ports in less than a minute?
![gif](https://github.com/RustScan/RustScan/blob/master/pictures/8seconds.gif?raw=true)
# Packages
**RustScan running in 8 seconds and finding all open ports out of 65k.**
## 📊 RustScan vs Nmap vs MassScan
| **Name** | RustScan | Nmap | Masscan |
| ------------------------------------------------------------------------------------------ | -------- | ---- | ------- |
| Fast | ✅ | ❌ | ✅ |
| Actually useful | ❌ | ✅ | ❌ |
| Realises it's not useful, and pipes the only useful data into the only useful port scanner | ✅ | ❌ | ❌ |
# 📖 Full Installation Guide
**You need Nmap**. If you have Kali Linux or Parrot OS installed, you already have Nmap. If not, [follow the nmap install guide](https://nmap.org/download.html).
The easiest way to install RustScan is to use one of the packages provided for your system, such as HomeBrew or Yay for Arch Linux.
The most universal way is to use `cargo`, Rust's built in package manager (think Pip but for Rust). [Follow this guide to installing Rust & Cargo](https://doc.rust-lang.org/cargo/getting-started/installation.html).
If you face any issues at all, please leave a GitHub issue. I have only tested this on Linux, so there may be issues for Mac OS or Windows.
Note: sometimes Rust doesn't add Cargo to the path. Please see [this issue](https://github.com/rust-lang/rustup/issues/2436) for how to fix that.
## 🖥️ Debian / Kali
Download the .deb file from the releases page:
[https://github.com/brandonskerritt/RustScan/releases](https://github.com/brandonskerritt/RustScan/releases)
Run the commpand `dpkg -i` on the file.
Note: sometimes you can double click the file to achieve the same result.
## Docker :whale:
Docker is the recommended way of installing RustScan. This is because:
* It has a high open file descriptor limit, which is one of the [main problems](https://github.com/RustScan/RustScan/issues/40) with RustScan. Now you don't have to fiddle around trying to understand your OS.
* It works on all systems, regardless of OS. Even Windows, which we don't officially support.
* The Docker image uses the latest build from Cargo, our main source-of-truth package. This means that you will always be using the latest version.
* No need to install Rust, Cargo, or Nmap.
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 info, however, note that we have two Docker images:
```
rustscan/rustscan:alpine
rustscan/rustscan:latest
```
We strongly recommend using the `alpine` tag, as this is the latest major - stable - release of RustScan. This READMDE uses the `alpine` 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:
```
docker run -it --rm --name rustscan rustscan/rustscan:alpine <rustscan arguments here> <ip address to scan>
```
Note: this will scan the Docker's localhost, not your own.
Once done, you will no longer need to re-download the image (except when RustScan updates) and can use RustScan like a normal application.
You will have to run this command every time, so we suggest aliasing it to something memorable.
```
alias rustscan='docker run -it --rm --name rustscan rustscan/rustscan:alpine <rustscan arguments here> <ip address to scan>'
```
Then we can:
```
rustscan 127.0.0.1 -t 500 -b 1500 -- -A
```
#### To build your own image:
Download the repo:
```
git clone https://github.com/RustScan/RustScan.git
```
Ensure you navigate to the download location of the repo:
```
cd /path/to/download/RustScan
```
Build away!
```
docker build -t <yourimagename> .
```
## 🍺 HomeBrew
**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:
```
brew tap brandonskerritt/rustscan
```
Install it:
```
brew install rustscan
```
## 🔧 Building it yourself
1. Git clone the repo.
2. Install Rust. You can do this with `curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh` which I took from the Rust website https://www.rust-lang.org/tools/install
3. cd into the Git repo, and run `cargo build --release`
4. The binary is located at `target/release/rustscan`
5. Symlink to the binary or something. Whatever you want!
## 🦊 Community Distributions
Here are all of RustScan's community distributions.
If you maintain a community distribution and want it listed here, leave an issue / pull request / Discord message or however you want to let us know.
* [OpenSuse](https://software.opensuse.org/package/rustscan?search_term=rustscan)
[![Packaging status](https://repology.org/badge/vertical-allrepos/rustscan.svg)](https://repology.org/project/rustscan/versions)
# 🤸 Usage
```
rustscan -h
```
```
rustscan 1.7.1
Fast Port Scanner built in Rust. WARNING Do not use this program against sensitive infrastructure since the specified
server may not be able to handle this many socket connections at once. - Discord https://discord.gg/GFrQsGy - GitHub
https://github.com/RustScan/RustScan
USAGE:
rustscan [FLAGS] [OPTIONS] <ips>... [-- <command>...]
FLAGS:
-h, --help Prints help information
-q, --quiet Quiet mode. Only output the ports. No Nmap. Useful for grep or outputting to a file
-V, --version Prints version information
OPTIONS:
-b, --batch-size <batch-size> The batch size for port scanning, it increases or slows the speed of scanning.
Depends on the open file limit of your OS. If you do 65535 it will do every port
at the same time. Although, your OS may not support this [default: 4500]
--scan-order <scan-order> The order of scanning to be performed. The "serial" option will scan ports in
ascending order while the "random" option will scan ports randomly [default:
serial] [possible values: Serial, Random]
-t, --timeout <timeout> The timeout in milliseconds before a port is assumed to be closed [default: 1500]
-u, --ulimit <ulimit> Automatically ups the ULIMIT with the value you provided
ARGS:
<ips>... A list of comma separated IP addresses to be scanned
<command>... 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'. This command adds -Pn -vvv -p $PORTS automatically to
nmap. For things like --script '(safe and vuln)' enclose it in quotations marks \"'(safe and
vuln)'\"")
```
The format is `rustscan -b 500 -T 1500 192.168.0.1` to scan 192.168.0.1 with 500 batch size with a timeout of 1500ms. The timeout is how long RustScan waits for a response until it assumes the port is closed.
The batch size determines how fast RustScan is. Set it to 65k, and it will scan all 65k ports at the same time. This means at at 65k batch size, RustScan will take TIMEOUT long to scan all ports. Essentially, if timeout is 1000ms, **RustScan can scan in 1 second**.
Your operating system may not support this, but it is worth it to play around and see where your open file limit is. Shortly I will be releasing a dockerised version with a much larger open file limit, so this will be possible.
## ⚠️ WARNING
This program, by default, scans 5000 ports at a time (5000 per second).
This may cause damage to a server, or may make it incredibly obvious you are scanning the server.
There are 2 ways to deal with this;
1. Decrease batch size
`rustscan -b 10` will run 10 port scans for 1 second, and then another 10 for 1 second and so on.
2. Increase timeout
`rustscan -T 5000` will mean RustScan waits 5 seconds until it scans the next ports.
You can also use both of these at the same time, to make it as slow or as fast as you want. A fun favourite is 65535 batch size with 1 second timeout. Theoretically, this scans all 65535 ports in 1 second.
**Please** do not use this tool against sensitive servers. It is designed mainly for Capture the Flag events, not real world servers with sensitive data.
## 🚨 Thread Panicked at Main: Too Many Open Files
This is the most common error found in RustScan.
The open file limit is how many open sockets you can have at any given time.
This limit changes from OS to OS.
RustScan does not automatically create defaults (other than 5000) like Nmap does with their -T1, -T2 system.
By figuring out for yourself the optimal batch size, you will know that RustScan is the most optimised port scanner for your system.
There are 2 things you can do:
1. Decrease batch size
2. Increase open file limit
Decreasing batch size slows down the program, so as long as it isn't too drastic, this is a good option.
Run these 3 commands:
```
ulimit -a
ulimit -Hn
ulimit -Sn
```
They will give you an idea on the open file limit of your OS.
If it says "250", run `rustscan -b 240` for a batch size of 240.
Increasing the open file limit increases speed, but poses danger. Although, **opening more file sockets on the specified IP address may damage it**.
To open more, set the ulimit to a higher number:
```
ulimit -n 5000
```
**Mac OS**
Mac OS has, from what I can tell, a naturally very low open file descriptor limit. The limit for Ubuntu is 8800. The limit for Mac OS is 255!
In this case, I would say it is safe to increase the open file limit. As most Linux based OS' have limits in the thousands.
Although, if this breaks anything, please don't blame me.
**Windows Subsystem for Linux**
Windows Subsystem for Linux does not support ulimit (see issue #39).
The best way is to use it on a host computer, in Docker, or in a VM that isn't WSL.
**Automatic Ulimit updating**
We are currently working on automatic Ulimit updating. If it is too high, it will lower itself. If it is too low, it will suggest a higher Ulimit. Watch [this issue](https://github.com/brandonskerritt/RustScan/issues/25) for more.
## 🔌 Nmap Custom Flags
To run your own nmap commands, end the RustScan command with `-- -A` where `--` indicates "end of RustScan flags, please do not parse anything further" and any flags after that will be entered into nmap.
RustScan automatically runs `nmap -vvv -p $PORTS $IP`. To make it run `-A`, execute the command `rustscan 127.0.0.1 -- -A`.
If you want to run commands such as `--script (vuln and safe)`, you will need to enclose it in quotations like so `--script '"(vuln and safe) or default"'`.
## 🎯 Increasing speed / accuracy
* Batch size
This increases speed, by allowing us to process more at once. Something experimental I am working on is changing the open file limit. You can do this manually with `ulimit -n 70000` and then running rustscan with `-b 65535`. This _should_ scan all 65535 ports at the exact same time. But this is extremely experimental.
For non-experimental speed increases, slowly increase the batch size until it no longer gets open ports, or it breaks.
* Accuracy (and some speed)
To increase accuracy, the easiest way is to increase the timeout. The default is 1.5 seconds, by setting it to 4 seconds (4000) we are telling RustScan "if we do not hear back from a port in 4 seconds, assume it is closed".
Decreasing accuracy gives some speed bonus, but my testing found that batch size dramatically changed the speed whereas timeout did, but not so much.
# 🎪 Community
@@ -75,21 +349,19 @@ Before contributing, read our [code of conduct](https://github.com/RustScan/Rust
TL;DR if you abuse members of our community you will be **perma-banned** 🤗
RustScan has 2 major labels for GitHub issues you should look at:
- Good First issue
These are issues for newcomers to open source!
[https://github.com/RustScan/RustScan/issues?q=is%3Aopen+is%3Aissue+label%3A%22good+first+issue%22](https://github.com/RustScan/RustScan/issues?q=is%3Aopen+is%3Aissue+label%3A%22good+first+issue%22)
- Help wanted
These are issues that aren't really for newcomers, but we could still do wiht help!
[https://github.com/RustScan/RustScan/issues?q=is%3Aopen+is%3Aissue+label%3A%22good+first+issue%22+label%3A%22help+wanted%22](https://github.com/RustScan/RustScan/issues?q=is%3Aopen+is%3Aissue+label%3A%22good+first+issue%22+label%3A%22help+wanted%22)
* Good First issue
These are issues for newcomers to open source!
[https://github.com/RustScan/RustScan/issues?q=is%3Aopen+is%3Aissue+label%3A%22good+first+issue%22](https://github.com/RustScan/RustScan/issues?q=is%3Aopen+is%3Aissue+label%3A%22good+first+issue%22)
* Help wanted
These are issues that aren't really for newcomers, but we could still do wiht help!
[https://github.com/RustScan/RustScan/issues?q=is%3Aopen+is%3Aissue+label%3A%22good+first+issue%22+label%3A%22help+wanted%22](https://github.com/RustScan/RustScan/issues?q=is%3Aopen+is%3Aissue+label%3A%22good+first+issue%22+label%3A%22help+wanted%22)
If you want to, solve the issue or comment on the issue for help.
The flow for contributing to open source software is:
- Fork the repo
- Make changes
- Pull request to the repo
* Fork the repo
* Make changes
* Pull request to the repo
And then comment on the issue that you've done.
@@ -100,17 +372,14 @@ If you have any feature suggestions or bugs, leave a GitHub issue. We welcome an
We communicate over Discord. [Click here](https://discord.gg/GFrQsGy) to join our Discord community!
## Rewarding you
I cannot pay you :-( But, I can place your GitHub profile on the README under `#Contributors` as a thank you! :)
Please read the [contributing.md file](contributing.md)
## Contributors ✨
<!-- ALL-CONTRIBUTORS-BADGE:START - Do not remove or modify this section -->
[![All Contributors](https://img.shields.io/badge/all_contributors-18-orange.svg?style=flat-square)](#contributors-)
[![All Contributors](https://img.shields.io/badge/all_contributors-11-orange.svg?style=flat-square)](#contributors-)
<!-- ALL-CONTRIBUTORS-BADGE:END -->
Thanks goes to these wonderful people ([emoji key](https://allcontributors.org/docs/en/emoji-key)):
@@ -133,21 +402,11 @@ Thanks goes to these wonderful people ([emoji key](https://allcontributors.org/d
<td align="center"><a href="https://tgotwig.me"><img src="https://avatars0.githubusercontent.com/u/30773779?v=4" width="100px;" alt=""/><br /><sub><b>Thomas Gotwig</b></sub></a><br /><a href="#platform-TGotwig" title="Packaging/porting to new platform">📦</a></td>
<td align="center"><a href="https://github.com/remigourdon"><img src="https://avatars3.githubusercontent.com/u/2874133?v=4" width="100px;" alt=""/><br /><sub><b>Rémi Gourdon</b></sub></a><br /><a href="https://github.com/RustScan/RustScan/commits?author=remigourdon" title="Documentation">📖</a> <a href="https://github.com/RustScan/RustScan/commits?author=remigourdon" title="Code">💻</a></td>
<td align="center"><a href="https://cmnatic.co.uk"><img src="https://avatars3.githubusercontent.com/u/4163116?v=4" width="100px;" alt=""/><br /><sub><b>Ben (CMNatic)</b></sub></a><br /><a href="https://github.com/RustScan/RustScan/commits?author=cmnatic" title="Code">💻</a> <a href="https://github.com/RustScan/RustScan/commits?author=cmnatic" title="Documentation">📖</a> <a href="#design-cmnatic" title="Design">🎨</a></td>
<td align="center"><a href="https://github.com/Ferryistaken"><img src="https://avatars3.githubusercontent.com/u/47927670?v=4" width="100px;" alt=""/><br /><sub><b>Alessandro Ferrari</b></sub></a><br /><a href="#content-Ferryistaken" title="Content">🖋</a></td>
<td align="center"><a href="https://github.com/Phenomite"><img src="https://avatars2.githubusercontent.com/u/8285537?v=4" width="100px;" alt=""/><br /><sub><b>Phenomite</b></sub></a><br /><a href="#content-Phenomite" title="Content">🖋</a></td>
<td align="center"><a href="https://supersandro.de/"><img src="https://avatars2.githubusercontent.com/u/7258858?v=4" width="100px;" alt=""/><br /><sub><b>Sandro</b></sub></a><br /><a href="#content-SuperSandro2000" title="Content">🖋</a> <a href="https://github.com/RustScan/RustScan/issues?q=author%3ASuperSandro2000" title="Bug reports">🐛</a> <a href="https://github.com/RustScan/RustScan/commits?author=SuperSandro2000" title="Code">💻</a></td>
</tr>
<tr>
<td align="center"><a href="https://swag.lgbt"><img src="https://avatars2.githubusercontent.com/u/25358963?v=4" width="100px;" alt=""/><br /><sub><b>Cass</b></sub></a><br /><a href="#platform-caass" title="Packaging/porting to new platform">📦</a> <a href="https://github.com/RustScan/RustScan/commits?author=caass" title="Code">💻</a> <a href="https://github.com/RustScan/RustScan/issues?q=author%3Acaass" title="Bug reports">🐛</a></td>
<td align="center"><a href="https://github.com/niklasmohrin"><img src="https://avatars0.githubusercontent.com/u/47574893?v=4" width="100px;" alt=""/><br /><sub><b>Niklas Mohrin</b></sub></a><br /><a href="https://github.com/RustScan/RustScan/commits?author=niklasmohrin" title="Documentation">📖</a></td>
<td align="center"><a href="https://liberapay.com/Artem4/"><img src="https://avatars0.githubusercontent.com/u/5614476?v=4" width="100px;" alt=""/><br /><sub><b>Artem Polishchuk</b></sub></a><br /><a href="#platform-tim77" title="Packaging/porting to new platform">📦</a></td>
<td align="center"><a href="https://github.com/buermarc"><img src="https://avatars2.githubusercontent.com/u/44375277?v=4" width="100px;" alt=""/><br /><sub><b>buermarc</b></sub></a><br /><a href="https://github.com/RustScan/RustScan/commits?author=buermarc" title="Code">💻</a></td>
</tr>
</table>
<!-- markdownlint-enable -->
<!-- prettier-ignore-end -->
<!-- ALL-CONTRIBUTORS-LIST:END -->
This project follows the [all-contributors](https://github.com/all-contributors/all-contributors) specification. Contributions of any kind welcome!
Binary file not shown.

Before

Width:  |  Height:  |  Size: 29 KiB

After

Width:  |  Height:  |  Size: 12 KiB

-368
View File
@@ -1,368 +0,0 @@
use serde_derive::Deserialize;
use std::fs;
use structopt::{clap::arg_enum, StructOpt};
const LOWEST_PORT_NUMBER: u16 = 1;
const TOP_PORT_NUMBER: u16 = 65535;
arg_enum! {
/// Represents the strategy in which the port scanning will run.
/// - Serial will run from start to end, for example 1 to 1_000.
/// - Random will randomize the order in which ports will be scanned.
#[derive(Deserialize, Debug, StructOpt, Clone, PartialEq)]
pub enum ScanOrder {
Serial,
Random,
}
}
/// Represents the range of ports to be scanned.
#[derive(Deserialize, Debug, Clone, PartialEq)]
pub struct PortRange {
pub start: u16,
pub end: u16,
}
fn parse_range(input: &str) -> Result<PortRange, String> {
let range = input
.split("-")
.map(|x| x.parse::<u16>())
.collect::<Result<Vec<u16>, std::num::ParseIntError>>();
if range.is_err() {
return Err(String::from(
"the range format must be 'start-end'. Example: 1-1000.",
));
}
match range.unwrap().as_slice() {
[start, end] => Ok(PortRange {
start: *start,
end: *end,
}),
_ => Err(String::from(
"the range format must be 'start-end'. Example: 1-1000.",
)),
}
}
#[derive(StructOpt, Debug)]
#[structopt(name = "rustscan", setting = structopt::clap::AppSettings::TrailingVarArg)]
/// Fast Port Scanner built in Rust.
/// WARNING Do not use this program against sensitive infrastructure since the
/// specified server may not be able to handle this many socket connections at once.
/// - Discord https://discord.gg/GFrQsGy
/// - GitHub https://github.com/RustScan/RustScan
pub struct Opts {
/// A list of comma separated CIDRs, IPs, or hosts to be scanned.
#[structopt(use_delimiter = true)]
pub addresses: Vec<String>,
/// A list of comma separed ports to be scanned. Example: 80,443,8080.
#[structopt(short, long, use_delimiter = true)]
pub ports: Option<Vec<u16>>,
/// A range of ports with format start-end. Example: 1-1000.
#[structopt(short, long, conflicts_with = "ports", parse(try_from_str = parse_range))]
pub range: Option<PortRange>,
/// Whether to ignore the configuration file or not.
#[structopt(short, long)]
pub no_config: bool,
/// Quiet mode. Only output the ports. No Nmap. Useful for grep or outputting to a file.
#[structopt(short, long)]
pub quiet: bool,
/// Accessible mode. Turns off features which negatively affect screen readers.
#[structopt(long)]
pub accessible: bool,
/// Turns off Nmap.
#[structopt(long)]
pub no_nmap: bool,
/// The batch size for port scanning, it increases or slows the speed of
/// scanning. Depends on the open file limit of your OS. If you do 65535
/// it will do every port at the same time. Although, your OS may not
/// support this.
#[structopt(short, long, default_value = "4500")]
pub batch_size: u16,
/// The timeout in milliseconds before a port is assumed to be closed.
#[structopt(short, long, default_value = "1500")]
pub timeout: u32,
/// Automatically ups the ULIMIT with the value you provided.
#[structopt(short, long)]
pub ulimit: Option<rlimit::rlim>,
/// The order of scanning to be performed. The "serial" option will
/// scan ports in ascending order while the "random" option will scan
/// ports randomly.
#[structopt(long, possible_values = &ScanOrder::variants(), case_insensitive = true, default_value = "serial")]
pub scan_order: ScanOrder,
/// 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'.
/// This command adds -Pn -vvv -p $PORTS automatically to nmap.
/// For things like --script '(safe and vuln)' enclose it in quotations marks \"'(safe and vuln)'\"")
#[structopt(last = true)]
pub command: Vec<String>,
}
impl Opts {
pub fn read() -> Self {
let mut opts = Opts::from_args();
if opts.ports.is_none() && opts.range.is_none() {
opts.range = Some(PortRange {
start: LOWEST_PORT_NUMBER,
end: TOP_PORT_NUMBER,
});
}
opts
}
/// Reads the command line arguments into an Opts struct and merge
/// values found within the user configuration file.
pub fn merge(&mut self, config: &Config) {
if !self.no_config {
self.merge_required(&config);
self.merge_optional(&config);
}
}
fn merge_required(&mut self, config: &Config) {
macro_rules! merge_required {
($($field: ident),+) => {
$(
if let Some(e) = &config.$field {
self.$field = e.clone();
}
)+
}
}
merge_required!(addresses, quiet, accessible, batch_size, timeout, scan_order, command);
}
fn merge_optional(&mut self, config: &Config) {
macro_rules! merge_optional {
($($field: ident),+) => {
$(
if config.$field.is_some() {
self.$field = config.$field.clone();
}
)+
}
}
merge_optional!(ports, range, ulimit);
}
}
/// 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.
#[derive(Debug, Deserialize)]
pub struct Config {
addresses: Option<Vec<String>>,
ports: Option<Vec<u16>>,
range: Option<PortRange>,
quiet: Option<bool>,
accessible: Option<bool>,
batch_size: Option<u16>,
timeout: Option<u32>,
no_nmap: Option<bool>,
ulimit: Option<rlimit::rlim>,
scan_order: Option<ScanOrder>,
command: Option<Vec<String>>,
}
impl Config {
/// Reads the configuration file with TOML format and parses it into a
/// Config struct.
///
/// # Format
///
/// addresses = ["127.0.0.1", "127.0.0.1"]
/// ports = [80, 443, 8080]
/// quiet = true
/// scan_order: "Serial"
///
pub fn read() -> Self {
let mut paths = Vec::new();
paths.push(match dirs::home_dir() {
Some(mut path) => {
path.push(".rustscan.toml");
path
}
None => panic!("Could not infer config file path."),
});
paths.push(match dirs::config_dir() {
Some(mut path) => {
path.push("rustscan");
path.push("config.toml");
path
}
None => panic!("Could not infer config file path."),
});
let mut contents: Option<String> = None;
for path in paths.iter().rev() {
contents = match fs::read_to_string(path) {
Ok(content) => {
contents = Some(content);
break;
}
Err(_) => None,
};
}
if contents.is_none() {
contents = Some(String::new());
}
let config: Config = match toml::from_str(&contents.unwrap()) {
Ok(config) => config,
Err(e) => {
println!("Found {} in configuration file.\nAborting scan.\n", e);
std::process::exit(1);
}
};
config
}
}
#[cfg(test)]
mod tests {
use super::{Config, Opts, PortRange, ScanOrder};
#[test]
fn opts_no_merge_when_config_is_ignored() {
let mut opts = Opts {
addresses: vec![],
ports: None,
range: None,
quiet: false,
batch_size: 0,
timeout: 0,
ulimit: None,
command: vec![],
accessible: false,
no_nmap: false,
scan_order: ScanOrder::Serial,
no_config: true,
};
let config = Config {
addresses: Some(vec!["127.0.0.1".to_owned()]),
ports: None,
range: None,
quiet: 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.quiet, false);
assert_eq!(opts.accessible, false);
assert_eq!(opts.timeout, 0);
assert_eq!(opts.command, vec![] as Vec<String>);
assert_eq!(opts.scan_order, ScanOrder::Serial);
}
#[test]
fn opts_merge_required_arguments() {
let mut opts = Opts {
addresses: vec![],
ports: None,
range: None,
quiet: false,
batch_size: 0,
timeout: 0,
ulimit: None,
command: vec![],
accessible: false,
scan_order: ScanOrder::Serial,
no_nmap: false,
no_config: false,
};
let config = Config {
addresses: Some(vec!["127.0.0.1".to_owned()]),
ports: None,
no_nmap: Some(false),
range: None,
quiet: 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);
assert_eq!(opts.addresses, config.addresses.unwrap());
assert_eq!(opts.quiet, config.quiet.unwrap());
assert_eq!(opts.timeout, config.timeout.unwrap());
assert_eq!(opts.command, config.command.unwrap());
assert_eq!(opts.accessible, config.accessible.unwrap());
assert_eq!(opts.scan_order, config.scan_order.unwrap());
}
#[test]
fn opts_merge_optional_arguments() {
let mut opts = Opts {
addresses: vec![],
ports: None,
range: None,
quiet: false,
batch_size: 0,
timeout: 0,
ulimit: None,
command: vec![],
accessible: false,
scan_order: ScanOrder::Serial,
no_nmap: false,
no_config: false,
};
let config = Config {
addresses: None,
ports: Some(vec![80, 403]),
range: Some(PortRange {
start: 1,
end: 1_000,
}),
quiet: 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);
assert_eq!(opts.ports, config.ports);
assert_eq!(opts.range, config.range);
assert_eq!(opts.ulimit, config.ulimit);
}
}
+168 -97
View File
@@ -2,62 +2,166 @@ extern crate shell_words;
mod tui;
mod input;
use input::{Config, Opts, PortRange, ScanOrder};
mod scanner;
use scanner::Scanner;
mod port_strategy;
use port_strategy::PortStrategy;
use cidr_utils::cidr::IpCidr;
use colorful::Color;
use colorful::Colorful;
use futures::executor::block_on;
use rlimit::Resource;
use rlimit::{getrlimit, setrlimit};
#[cfg(not(target_os = "windows"))]
use rlimit::{getrlimit, setrlimit, Resource};
use std::collections::HashMap;
use std::net::ToSocketAddrs;
#[cfg(not(target_os = "windows"))]
use std::convert::TryInto;
use std::process::Command;
use std::{net::IpAddr, time::Duration};
use std::str::FromStr;
use std::{net::IpAddr, net::ToSocketAddrs, time::Duration};
use structopt::{clap::arg_enum, StructOpt};
extern crate colorful;
extern crate dirs;
const LOWEST_PORT_NUMBER: u16 = 1;
const TOP_PORT_NUMBER: u16 = 65535;
// Average value for Ubuntu
const DEFAULT_FILE_DESCRIPTORS_LIMIT: rlimit::rlim = 8000;
const DEFAULT_FILE_DESCRIPTORS_LIMIT: u32 = 8000;
// Safest batch size based on experimentation
const AVERAGE_BATCH_SIZE: rlimit::rlim = 3000;
const AVERAGE_BATCH_SIZE: u32 = 3000;
#[macro_use]
extern crate log;
arg_enum! {
#[derive(Debug, StructOpt)]
pub enum ScanOrder {
Serial,
Random,
}
}
#[derive(Debug)]
pub struct PortRange {
start: u16,
end: u16,
}
fn parse_range(input: &str) -> Result<PortRange, String> {
let range = input
.split("-")
.map(|x| x.parse::<u16>())
.collect::<Result<Vec<u16>, std::num::ParseIntError>>();
if range.is_err() {
return Err(String::from(
"the range format must be 'start-end'. Example: 1-1000.",
));
}
match range.unwrap().as_slice() {
[start, end] => Ok(PortRange {
start: *start,
end: *end,
}),
_ => Err(String::from(
"the range format must be 'start-end'. Example: 1-1000.",
)),
}
}
#[derive(StructOpt, Debug)]
#[structopt(name = "rustscan", setting = structopt::clap::AppSettings::TrailingVarArg)]
/// Fast Port Scanner built in Rust.
/// WARNING Do not use this program against sensitive infrastructure since the
/// specified server may not be able to handle this many socket connections at once.
/// - Discord https://discord.gg/GFrQsGy
/// - GitHub https://github.com/RustScan/RustScan
struct Opts {
/// A list of comma separated IP addresses or hosts to be scanned.
#[structopt(use_delimiter = true, required = true)]
ips_or_hosts: Vec<String>,
/// A list of comma separed ports to be scanned. Example: 80,443,8080.
#[structopt(short, long, use_delimiter = true)]
ports: Option<Vec<u16>>,
/// A range of ports with format start-end. Example: 1-1000.
#[structopt(short, long, conflicts_with = "ports", parse(try_from_str = parse_range))]
range: Option<PortRange>,
///Quiet mode. Only output the ports. No Nmap. Useful for grep or outputting to a file.
#[structopt(short, long)]
quiet: bool,
//Accessible mode. Turns off features which negatively affect screen readers.
#[structopt(short, long)]
accessible: bool,
/// The batch size for port scanning, it increases or slows the speed of
/// scanning. Depends on the open file limit of your OS. If you do 65535
/// it will do every port at the same time. Although, your OS may not
/// support this.
#[structopt(short, long, default_value = "4500")]
batch_size: u16,
/// The timeout in milliseconds before a port is assumed to be closed.
#[structopt(short, long, default_value = "1500")]
timeout: u32,
/// Automatically ups the ULIMIT with the value you provided.
#[structopt(short, long)]
ulimit: Option<u32>,
/// The order of scanning to be performed. The "serial" option will
/// scan ports in ascending order while the "random" option will scan
/// ports randomly.
#[structopt(long, possible_values = &ScanOrder::variants(), case_insensitive = true, default_value = "serial")]
scan_order: ScanOrder,
/// 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'.
/// This command adds -Pn -vvv -p $PORTS automatically to nmap.
/// For things like --script '(safe and vuln)' enclose it in quotations marks \"'(safe and vuln)'\"")
#[structopt(last = true)]
command: Vec<String>,
}
#[cfg(not(tarpaulin_include))]
/// Faster Nmap scanning with Rust
/// If you're looking for the actual scanning, check out the module Scanner
fn main() {
env_logger::init();
let mut opts: Opts = Opts::read();
let config = Config::read();
opts.merge(&config);
dbg!(&opts);
info!("Starting up");
let mut opts = Opts::from_args();
info!("Main() `opts` arguments are {:?}", opts);
if opts.ports.is_none() && opts.range.is_none() {
opts.range = Some(PortRange {
start: LOWEST_PORT_NUMBER,
end: TOP_PORT_NUMBER,
});
}
info!("Mains() `opts` arguments are {:?}", opts);
if !opts.quiet && !opts.accessible {
print_opening();
}
let ips: Vec<IpAddr> = parse_addresses(&opts);
let ips: Vec<IpAddr> = parse_ips(&opts);
if ips.is_empty() {
warning!("No IPs could be resolved, aborting scan.", false);
std::process::exit(1);
}
let ulimit: rlimit::rlim = adjust_ulimit_size(&opts);
let ulimit: u32 = adjust_ulimit_size(&opts);
let batch_size: u16 = infer_batch_size(&opts, ulimit);
let scanner = Scanner::new(
@@ -88,7 +192,7 @@ fn main() {
let x = format!("{} Looks like I didn't find any open ports for {:?}. This is usually caused by a high batch size.
\n*I used {} batch size, consider lowering to {} with {} or a comfortable number for your system.
\n Alternatively, increase the timeout if your ping is high. Rustscan -t 2000 for 2000 milliseconds (2s) timeout.\n",
\n Alternatively, increase the timeout if your ping is high. Rustscan -t 2000 for 2000 second timeout.\n",
"ERROR",
ip,
opts.batch_size,
@@ -182,12 +286,12 @@ fn build_nmap_arguments<'a>(
arguments
}
fn parse_addresses(opts: &Opts) -> Vec<IpAddr> {
fn parse_ips(opts: &Opts) -> Vec<IpAddr> {
let mut ips: Vec<IpAddr> = Vec::new();
for ip_or_host in &opts.addresses {
match IpCidr::from_str(ip_or_host) {
Ok(cidr) => cidr.iter().for_each(|ip| ips.push(ip)),
for ip_or_host in &opts.ips_or_hosts {
match IpAddr::from_str(ip_or_host) {
Ok(ip) => ips.push(ip),
_ => match format!("{}:{}", &ip_or_host, 80).to_socket_addrs() {
Ok(mut iter) => ips.push(iter.nth(0).unwrap().ip()),
_ => {
@@ -201,9 +305,10 @@ fn parse_addresses(opts: &Opts) -> Vec<IpAddr> {
ips
}
fn adjust_ulimit_size(opts: &Opts) -> rlimit::rlim {
#[cfg(not(target_os = "windows"))]
fn adjust_ulimit_size(opts: &Opts) -> u32 {
if opts.ulimit.is_some() {
let limit: rlimit::rlim = opts.ulimit.unwrap();
let limit: rlimit::rlim = opts.ulimit.unwrap().into();
match setrlimit(Resource::NOFILE, limit, limit) {
Ok(_) => {
@@ -218,15 +323,22 @@ fn adjust_ulimit_size(opts: &Opts) -> rlimit::rlim {
let (rlim, _) = getrlimit(Resource::NOFILE).unwrap();
rlim
rlim.try_into().unwrap()
}
fn infer_batch_size(opts: &Opts, ulimit: rlimit::rlim) -> u16 {
let mut batch_size: rlimit::rlim = opts.batch_size.into();
// Rlimit does not support Windows
// set to 1000 if Windows is used
#[cfg(target_os = "windows")]
fn adjust_ulimit_size(_opts: &Opts) -> u32 {
1000
}
fn infer_batch_size(opts: &Opts, ulimit: u32) -> u16 {
let mut batch_size: u32 = opts.batch_size.into();
// Adjust the batch size when the ulimit value is lower than the desired batch size
if ulimit < batch_size {
warning!("File limit is lower than default batch size. Consider upping with --ulimit. May cause harm to sensitive servers",
if ulimit < batch_size && !(cfg!(windows)) {
warning!("File limit is lower than default batch size. Consider upping with --ulimt. May cause harm to sensitive servers",
opts.quiet
);
@@ -237,7 +349,11 @@ fn infer_batch_size(opts: &Opts, ulimit: rlimit::rlim) -> u16 {
// ulimit is smaller than aveage batch size
// user must have very small ulimit
// decrease batch size to half of ulimit
warning!("Your file limit is very small, which negatively impacts RustScan's speed. Use the Docker image, or up the Ulimit with '--ulimit 5000'. ");
if !(cfg!(windows)) {
warning!("Your file limit is very small, which negatively impacts RustScan's speed. Use the Docker image, or up the Ulimit with '--ulimit 5000'. ");
} else {
warning!("Windows is known to be much slower than scanning on Unix systems. Use the Docker image if you need faster speeds.");
}
info!("Halving batch_size because ulimit is smaller than average batch size");
batch_size = ulimit / 2
} else if ulimit > DEFAULT_FILE_DESCRIPTORS_LIMIT {
@@ -261,15 +377,13 @@ 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, ScanOrder,
};
use std::net::Ipv4Addr;
use crate::{adjust_ulimit_size, infer_batch_size, parse_ips, print_opening, Opts, ScanOrder};
#[cfg(not(target_os = "windows"))]
#[test]
fn batch_size_lowered() {
let opts = Opts {
addresses: vec!["127.0.0.1".to_owned()],
ips_or_hosts: vec!["127.0.0.1".to_owned()],
ports: None,
range: None,
quiet: true,
@@ -279,18 +393,17 @@ mod tests {
command: Vec::new(),
accessible: false,
scan_order: ScanOrder::Serial,
no_config: false,
no_nmap: false,
};
let batch_size = infer_batch_size(&opts, 120);
assert!(batch_size < 50_000);
}
#[cfg(not(target_os = "windows"))]
#[test]
fn batch_size_lowered_average_size() {
let opts = Opts {
addresses: vec!["127.0.0.1".to_owned()],
ips_or_hosts: vec!["127.0.0.1".to_owned()],
ports: None,
range: None,
quiet: true,
@@ -300,19 +413,18 @@ mod tests {
command: Vec::new(),
accessible: false,
scan_order: ScanOrder::Serial,
no_config: false,
no_nmap: false,
};
let batch_size = infer_batch_size(&opts, 9_000);
assert!(batch_size == 3_000);
}
#[cfg(not(target_os = "windows"))]
#[test]
fn batch_size_equals_ulimit_lowered() {
// because ulimit and batch size are same size, batch size is lowered
// to ULIMIT - 100
let opts = Opts {
addresses: vec!["127.0.0.1".to_owned()],
ips_or_hosts: vec!["127.0.0.1".to_owned()],
ports: None,
range: None,
quiet: true,
@@ -322,18 +434,17 @@ mod tests {
command: Vec::new(),
accessible: false,
scan_order: ScanOrder::Serial,
no_config: false,
no_nmap: false,
};
let batch_size = infer_batch_size(&opts, 5_000);
assert!(batch_size == 4_900);
}
#[cfg(not(target_os = "windows"))]
#[test]
fn batch_size_adjusted_2000() {
// ulimit == batch_size
let opts = Opts {
addresses: vec!["127.0.0.1".to_owned()],
ips_or_hosts: vec!["127.0.0.1".to_owned()],
ports: None,
range: None,
quiet: true,
@@ -343,8 +454,6 @@ mod tests {
command: Vec::new(),
accessible: false,
scan_order: ScanOrder::Serial,
no_config: false,
no_nmap: false,
};
let batch_size = adjust_ulimit_size(&opts);
@@ -359,7 +468,7 @@ mod tests {
#[test]
fn test_high_ulimit_no_quiet_mode() {
let opts = Opts {
addresses: vec!["127.0.0.1".to_owned()],
ips_or_hosts: vec!["127.0.0.1".to_owned()],
ports: None,
range: None,
quiet: false,
@@ -369,8 +478,6 @@ mod tests {
command: Vec::new(),
accessible: true,
scan_order: ScanOrder::Serial,
no_config: false,
no_nmap: false,
};
infer_batch_size(&opts, 1_000_000);
@@ -379,9 +486,9 @@ mod tests {
}
#[test]
fn parse_correct_addresses() {
fn parse_correct_ips_or_hosts() {
let opts = Opts {
addresses: vec!["127.0.0.1".to_owned(), "192.168.0.0/30".to_owned()],
ips_or_hosts: vec!["127.0.0.1".to_owned(), "google.com".to_owned()],
ports: None,
range: None,
quiet: true,
@@ -391,27 +498,16 @@ mod tests {
command: Vec::new(),
accessible: false,
scan_order: ScanOrder::Serial,
no_config: false,
no_nmap: false,
};
let ips = parse_addresses(&opts);
let ips = parse_ips(&opts);
assert_eq!(
ips,
[
Ipv4Addr::new(127, 0, 0, 1),
Ipv4Addr::new(192, 168, 0, 0),
Ipv4Addr::new(192, 168, 0, 1),
Ipv4Addr::new(192, 168, 0, 2),
Ipv4Addr::new(192, 168, 0, 3)
]
);
assert_eq!(2, ips.len());
}
#[test]
fn parse_correct_host_addresses() {
fn parse_correct_and_incorrect_ips_or_hosts() {
let opts = Opts {
addresses: vec!["google.com".to_owned()],
ips_or_hosts: vec!["127.0.0.1".to_owned(), "im_wrong".to_owned()],
ports: None,
range: None,
quiet: true,
@@ -421,18 +517,16 @@ mod tests {
command: Vec::new(),
accessible: false,
scan_order: ScanOrder::Serial,
no_config: false,
no_nmap: false,
};
let ips = parse_addresses(&opts);
let ips = parse_ips(&opts);
assert_eq!(ips.len(), 1);
assert_eq!(1, ips.len());
}
#[test]
fn parse_correct_and_incorrect_addresses() {
fn parse_incorrect_ips_or_hosts() {
let opts = Opts {
addresses: vec!["127.0.0.1".to_owned(), "im_wrong".to_owned()],
ips_or_hosts: vec!["im_wrong".to_owned(), "300.10.1.1".to_owned()],
ports: None,
range: None,
quiet: true,
@@ -442,32 +536,9 @@ mod tests {
command: Vec::new(),
accessible: false,
scan_order: ScanOrder::Serial,
no_config: false,
no_nmap: false,
};
let ips = parse_addresses(&opts);
let ips = parse_ips(&opts);
assert_eq!(ips, [Ipv4Addr::new(127, 0, 0, 1),]);
}
#[test]
fn parse_incorrect_addresses() {
let opts = Opts {
addresses: vec!["im_wrong".to_owned(), "300.10.1.1".to_owned()],
ports: None,
range: None,
quiet: 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,
};
let ips = parse_addresses(&opts);
assert_eq!(ips.is_empty(), true);
assert_eq!(0, ips.len());
}
}
View File
Whitespace-only changes.