Merge branch 'beta'
This commit is contained in:
commit
7ec6b128a7
|
@ -1,139 +1,3 @@
|
|||
# Byte-compiled / optimized / DLL files
|
||||
__pycache__/
|
||||
*.py[cod]
|
||||
*$py.class
|
||||
__pycache__
|
||||
*.pyc
|
||||
|
||||
# C extensions
|
||||
*.so
|
||||
|
||||
# Distribution / packaging
|
||||
.Python
|
||||
build/
|
||||
develop-eggs/
|
||||
dist/
|
||||
downloads/
|
||||
eggs/
|
||||
.eggs/
|
||||
lib/
|
||||
lib64/
|
||||
parts/
|
||||
sdist/
|
||||
var/
|
||||
wheels/
|
||||
share/python-wheels/
|
||||
*.egg-info/
|
||||
.installed.cfg
|
||||
*.egg
|
||||
MANIFEST
|
||||
|
||||
# PyInstaller
|
||||
# Usually these files are written by a python script from a template
|
||||
# before PyInstaller builds the exe, so as to inject date/other infos into it.
|
||||
*.manifest
|
||||
*.spec
|
||||
|
||||
# Installer logs
|
||||
pip-log.txt
|
||||
pip-delete-this-directory.txt
|
||||
|
||||
# Unit test / coverage reports
|
||||
htmlcov/
|
||||
.tox/
|
||||
.nox/
|
||||
.coverage
|
||||
.coverage.*
|
||||
.cache
|
||||
nosetests.xml
|
||||
coverage.xml
|
||||
*.cover
|
||||
*.py,cover
|
||||
.hypothesis/
|
||||
.pytest_cache/
|
||||
cover/
|
||||
|
||||
# Translations
|
||||
*.mo
|
||||
*.pot
|
||||
|
||||
# Django stuff:
|
||||
*.log
|
||||
local_settings.py
|
||||
db.sqlite3
|
||||
db.sqlite3-journal
|
||||
|
||||
# Flask stuff:
|
||||
instance/
|
||||
.webassets-cache
|
||||
|
||||
# Scrapy stuff:
|
||||
.scrapy
|
||||
|
||||
# Sphinx documentation
|
||||
docs/_build/
|
||||
|
||||
# PyBuilder
|
||||
.pybuilder/
|
||||
target/
|
||||
|
||||
# Jupyter Notebook
|
||||
.ipynb_checkpoints
|
||||
|
||||
# IPython
|
||||
profile_default/
|
||||
ipython_config.py
|
||||
|
||||
# pyenv
|
||||
# For a library or package, you might want to ignore these files since the code is
|
||||
# intended to run in multiple environments; otherwise, check them in:
|
||||
# .python-version
|
||||
|
||||
# pipenv
|
||||
# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
|
||||
# However, in case of collaboration, if having platform-specific dependencies or dependencies
|
||||
# having no cross-platform support, pipenv may install dependencies that don't work, or not
|
||||
# install all needed dependencies.
|
||||
#Pipfile.lock
|
||||
|
||||
# PEP 582; used by e.g. github.com/David-OConnor/pyflow
|
||||
__pypackages__/
|
||||
|
||||
# Celery stuff
|
||||
celerybeat-schedule
|
||||
celerybeat.pid
|
||||
|
||||
# SageMath parsed files
|
||||
*.sage.py
|
||||
|
||||
# Environments
|
||||
.env
|
||||
.venv
|
||||
env/
|
||||
venv/
|
||||
ENV/
|
||||
env.bak/
|
||||
venv.bak/
|
||||
|
||||
# Spyder project settings
|
||||
.spyderproject
|
||||
.spyproject
|
||||
|
||||
# Rope project settings
|
||||
.ropeproject
|
||||
|
||||
# mkdocs documentation
|
||||
/site
|
||||
|
||||
# mypy
|
||||
.mypy_cache/
|
||||
.dmypy.json
|
||||
dmypy.json
|
||||
|
||||
# Pyre type checker
|
||||
.pyre/
|
||||
|
||||
# pytype static type analyzer
|
||||
.pytype/
|
||||
|
||||
# Cython debug symbols
|
||||
cython_debug/
|
||||
results/
|
||||
|
|
16
Dockerfile
16
Dockerfile
|
@ -1,16 +0,0 @@
|
|||
#sudo docker build -t tib3rius/autorecon .
|
||||
#mkdir /root/results
|
||||
#Usage: sudo docker run -it -v /root/results:/results --rm --name autorecon-container tib3rius/autorecon 127.0.0.1
|
||||
|
||||
FROM kalilinux/kali-bleeding-edge:latest
|
||||
RUN apt-get update
|
||||
RUN apt-get install sudo git -y
|
||||
RUN sudo apt install python3 -y
|
||||
RUN sudo apt install python3-pip -y
|
||||
RUN python3 -m pip install --user pipx
|
||||
RUN python3 -m pipx ensurepath
|
||||
RUN python3 -m pip install git+https://github.com/Tib3rius/AutoRecon.git
|
||||
RUN sudo apt install seclists curl enum4linux gobuster nbtscan nikto nmap onesixtyone oscanner smbclient smbmap smtp-user-enum snmp sslscan sipvicious tnscmd10g whatweb wkhtmltopdf -y
|
||||
RUN echo "Defaults secure_path="/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/home/kali/.local/bin"" >> /etc/sudoers
|
||||
RUN apt-get -y autoremove && apt-get -y autoclean
|
||||
ENTRYPOINT ["autorecon"]
|
184
LICENSE
184
LICENSE
|
@ -1,11 +1,11 @@
|
|||
GNU GENERAL PUBLIC LICENSE
|
||||
Version 3, 29 June 2007
|
||||
GNU GENERAL PUBLIC LICENSE
|
||||
Version 3, 29 June 2007
|
||||
|
||||
Copyright (C) 2007 Free Software Foundation, Inc. <https://fsf.org/>
|
||||
Everyone is permitted to copy and distribute verbatim copies
|
||||
of this license document, but changing it is not allowed.
|
||||
|
||||
Preamble
|
||||
Preamble
|
||||
|
||||
The GNU General Public License is a free, copyleft license for
|
||||
software and other kinds of works.
|
||||
|
@ -68,7 +68,7 @@ patents cannot be used to render the program non-free.
|
|||
The precise terms and conditions for copying, distribution and
|
||||
modification follow.
|
||||
|
||||
TERMS AND CONDITIONS
|
||||
TERMS AND CONDITIONS
|
||||
|
||||
0. Definitions.
|
||||
|
||||
|
@ -211,26 +211,26 @@ and you may offer support or warranty protection for a fee.
|
|||
produce it from the Program, in the form of source code under the
|
||||
terms of section 4, provided that you also meet all of these conditions:
|
||||
|
||||
a) The work must carry prominent notices stating that you modified
|
||||
it, and giving a relevant date.
|
||||
a) The work must carry prominent notices stating that you modified
|
||||
it, and giving a relevant date.
|
||||
|
||||
b) The work must carry prominent notices stating that it is
|
||||
released under this License and any conditions added under section
|
||||
7. This requirement modifies the requirement in section 4 to
|
||||
"keep intact all notices".
|
||||
b) The work must carry prominent notices stating that it is
|
||||
released under this License and any conditions added under section
|
||||
7. This requirement modifies the requirement in section 4 to
|
||||
"keep intact all notices".
|
||||
|
||||
c) You must license the entire work, as a whole, under this
|
||||
License to anyone who comes into possession of a copy. This
|
||||
License will therefore apply, along with any applicable section 7
|
||||
additional terms, to the whole of the work, and all its parts,
|
||||
regardless of how they are packaged. This License gives no
|
||||
permission to license the work in any other way, but it does not
|
||||
invalidate such permission if you have separately received it.
|
||||
c) You must license the entire work, as a whole, under this
|
||||
License to anyone who comes into possession of a copy. This
|
||||
License will therefore apply, along with any applicable section 7
|
||||
additional terms, to the whole of the work, and all its parts,
|
||||
regardless of how they are packaged. This License gives no
|
||||
permission to license the work in any other way, but it does not
|
||||
invalidate such permission if you have separately received it.
|
||||
|
||||
d) If the work has interactive user interfaces, each must display
|
||||
Appropriate Legal Notices; however, if the Program has interactive
|
||||
interfaces that do not display Appropriate Legal Notices, your
|
||||
work need not make them do so.
|
||||
d) If the work has interactive user interfaces, each must display
|
||||
Appropriate Legal Notices; however, if the Program has interactive
|
||||
interfaces that do not display Appropriate Legal Notices, your
|
||||
work need not make them do so.
|
||||
|
||||
A compilation of a covered work with other separate and independent
|
||||
works, which are not by their nature extensions of the covered work,
|
||||
|
@ -249,46 +249,46 @@ of sections 4 and 5, provided that you also convey the
|
|||
machine-readable Corresponding Source under the terms of this License,
|
||||
in one of these ways:
|
||||
|
||||
a) Convey the object code in, or embodied in, a physical product
|
||||
(including a physical distribution medium), accompanied by the
|
||||
Corresponding Source fixed on a durable physical medium
|
||||
customarily used for software interchange.
|
||||
a) Convey the object code in, or embodied in, a physical product
|
||||
(including a physical distribution medium), accompanied by the
|
||||
Corresponding Source fixed on a durable physical medium
|
||||
customarily used for software interchange.
|
||||
|
||||
b) Convey the object code in, or embodied in, a physical product
|
||||
(including a physical distribution medium), accompanied by a
|
||||
written offer, valid for at least three years and valid for as
|
||||
long as you offer spare parts or customer support for that product
|
||||
model, to give anyone who possesses the object code either (1) a
|
||||
copy of the Corresponding Source for all the software in the
|
||||
product that is covered by this License, on a durable physical
|
||||
medium customarily used for software interchange, for a price no
|
||||
more than your reasonable cost of physically performing this
|
||||
conveying of source, or (2) access to copy the
|
||||
Corresponding Source from a network server at no charge.
|
||||
b) Convey the object code in, or embodied in, a physical product
|
||||
(including a physical distribution medium), accompanied by a
|
||||
written offer, valid for at least three years and valid for as
|
||||
long as you offer spare parts or customer support for that product
|
||||
model, to give anyone who possesses the object code either (1) a
|
||||
copy of the Corresponding Source for all the software in the
|
||||
product that is covered by this License, on a durable physical
|
||||
medium customarily used for software interchange, for a price no
|
||||
more than your reasonable cost of physically performing this
|
||||
conveying of source, or (2) access to copy the
|
||||
Corresponding Source from a network server at no charge.
|
||||
|
||||
c) Convey individual copies of the object code with a copy of the
|
||||
written offer to provide the Corresponding Source. This
|
||||
alternative is allowed only occasionally and noncommercially, and
|
||||
only if you received the object code with such an offer, in accord
|
||||
with subsection 6b.
|
||||
c) Convey individual copies of the object code with a copy of the
|
||||
written offer to provide the Corresponding Source. This
|
||||
alternative is allowed only occasionally and noncommercially, and
|
||||
only if you received the object code with such an offer, in accord
|
||||
with subsection 6b.
|
||||
|
||||
d) Convey the object code by offering access from a designated
|
||||
place (gratis or for a charge), and offer equivalent access to the
|
||||
Corresponding Source in the same way through the same place at no
|
||||
further charge. You need not require recipients to copy the
|
||||
Corresponding Source along with the object code. If the place to
|
||||
copy the object code is a network server, the Corresponding Source
|
||||
may be on a different server (operated by you or a third party)
|
||||
that supports equivalent copying facilities, provided you maintain
|
||||
clear directions next to the object code saying where to find the
|
||||
Corresponding Source. Regardless of what server hosts the
|
||||
Corresponding Source, you remain obligated to ensure that it is
|
||||
available for as long as needed to satisfy these requirements.
|
||||
d) Convey the object code by offering access from a designated
|
||||
place (gratis or for a charge), and offer equivalent access to the
|
||||
Corresponding Source in the same way through the same place at no
|
||||
further charge. You need not require recipients to copy the
|
||||
Corresponding Source along with the object code. If the place to
|
||||
copy the object code is a network server, the Corresponding Source
|
||||
may be on a different server (operated by you or a third party)
|
||||
that supports equivalent copying facilities, provided you maintain
|
||||
clear directions next to the object code saying where to find the
|
||||
Corresponding Source. Regardless of what server hosts the
|
||||
Corresponding Source, you remain obligated to ensure that it is
|
||||
available for as long as needed to satisfy these requirements.
|
||||
|
||||
e) Convey the object code using peer-to-peer transmission, provided
|
||||
you inform other peers where the object code and Corresponding
|
||||
Source of the work are being offered to the general public at no
|
||||
charge under subsection 6d.
|
||||
e) Convey the object code using peer-to-peer transmission, provided
|
||||
you inform other peers where the object code and Corresponding
|
||||
Source of the work are being offered to the general public at no
|
||||
charge under subsection 6d.
|
||||
|
||||
A separable portion of the object code, whose source code is excluded
|
||||
from the Corresponding Source as a System Library, need not be
|
||||
|
@ -362,28 +362,28 @@ for which you have or can give appropriate copyright permission.
|
|||
add to a covered work, you may (if authorized by the copyright holders of
|
||||
that material) supplement the terms of this License with terms:
|
||||
|
||||
a) Disclaiming warranty or limiting liability differently from the
|
||||
terms of sections 15 and 16 of this License; or
|
||||
a) Disclaiming warranty or limiting liability differently from the
|
||||
terms of sections 15 and 16 of this License; or
|
||||
|
||||
b) Requiring preservation of specified reasonable legal notices or
|
||||
author attributions in that material or in the Appropriate Legal
|
||||
Notices displayed by works containing it; or
|
||||
b) Requiring preservation of specified reasonable legal notices or
|
||||
author attributions in that material or in the Appropriate Legal
|
||||
Notices displayed by works containing it; or
|
||||
|
||||
c) Prohibiting misrepresentation of the origin of that material, or
|
||||
requiring that modified versions of such material be marked in
|
||||
reasonable ways as different from the original version; or
|
||||
c) Prohibiting misrepresentation of the origin of that material, or
|
||||
requiring that modified versions of such material be marked in
|
||||
reasonable ways as different from the original version; or
|
||||
|
||||
d) Limiting the use for publicity purposes of names of licensors or
|
||||
authors of the material; or
|
||||
d) Limiting the use for publicity purposes of names of licensors or
|
||||
authors of the material; or
|
||||
|
||||
e) Declining to grant rights under trademark law for use of some
|
||||
trade names, trademarks, or service marks; or
|
||||
e) Declining to grant rights under trademark law for use of some
|
||||
trade names, trademarks, or service marks; or
|
||||
|
||||
f) Requiring indemnification of licensors and authors of that
|
||||
material by anyone who conveys the material (or modified versions of
|
||||
it) with contractual assumptions of liability to the recipient, for
|
||||
any liability that these contractual assumptions directly impose on
|
||||
those licensors and authors.
|
||||
f) Requiring indemnification of licensors and authors of that
|
||||
material by anyone who conveys the material (or modified versions of
|
||||
it) with contractual assumptions of liability to the recipient, for
|
||||
any liability that these contractual assumptions directly impose on
|
||||
those licensors and authors.
|
||||
|
||||
All other non-permissive additional terms are considered "further
|
||||
restrictions" within the meaning of section 10. If the Program as you
|
||||
|
@ -618,9 +618,9 @@ an absolute waiver of all civil liability in connection with the
|
|||
Program, unless a warranty or assumption of liability accompanies a
|
||||
copy of the Program in return for a fee.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
How to Apply These Terms to Your New Programs
|
||||
How to Apply These Terms to Your New Programs
|
||||
|
||||
If you develop a new program, and you want it to be of the greatest
|
||||
possible use to the public, the best way to achieve this is to make it
|
||||
|
@ -631,31 +631,31 @@ to attach them to the start of each source file to most effectively
|
|||
state the exclusion of warranty; and each file should have at least
|
||||
the "copyright" line and a pointer to where the full notice is found.
|
||||
|
||||
<one line to give the program's name and a brief idea of what it does.>
|
||||
Copyright (C) <year> <name of author>
|
||||
<one line to give the program's name and a brief idea of what it does.>
|
||||
Copyright (C) <year> <name of author>
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
Also add information on how to contact you by electronic and paper mail.
|
||||
|
||||
If the program does terminal interaction, make it output a short
|
||||
notice like this when it starts in an interactive mode:
|
||||
|
||||
<program> Copyright (C) <year> <name of author>
|
||||
This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
|
||||
This is free software, and you are welcome to redistribute it
|
||||
under certain conditions; type `show c' for details.
|
||||
<program> Copyright (C) <year> <name of author>
|
||||
This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
|
||||
This is free software, and you are welcome to redistribute it
|
||||
under certain conditions; type `show c' for details.
|
||||
|
||||
The hypothetical commands `show w' and `show c' should show the appropriate
|
||||
parts of the General Public License. Of course, your program's commands
|
||||
|
|
525
README.md
525
README.md
|
@ -1,18 +1,10 @@
|
|||
# AutoRecon v2 Beta!
|
||||
|
||||
Please consider trying out the AutoRecon v2 beta, an almost complete rewrite which fixes numerous bugs and makes AutoRecon more powerful, more customizable, and easier to configure: https://github.com/Tib3rius/AutoRecon/tree/beta
|
||||
|
||||
---
|
||||
|
||||
[](https://repology.org/project/autorecon/versions)
|
||||
|
||||
> It's like bowling with bumpers. - [@ippsec](https://twitter.com/ippsec)
|
||||
|
||||
# AutoRecon
|
||||
|
||||
AutoRecon is a multi-threaded network reconnaissance tool which performs automated enumeration of services. It is intended as a time-saving tool for use in CTFs and other penetration testing environments (e.g. OSCP). It may also be useful in real-world engagements.
|
||||
|
||||
The tool works by firstly performing port scans / service detection scans. From those initial results, the tool will launch further enumeration scans of those services using a number of different tools. For example, if HTTP is found, nikto will be launched (as well as many others).
|
||||
The tool works by firstly performing port scans / service detection scans. From those initial results, the tool will launch further enumeration scans of those services using a number of different tools. For example, if HTTP is found, feroxbuster will be launched (as well as many others).
|
||||
|
||||
Everything in the tool is highly configurable. The default configuration performs **no automated exploitation** to keep the tool in line with OSCP exam rules. If you wish to add automatic exploit tools to the configuration, you do so at your own risk. The author will not be held responsible for negative actions that result from the mis-use of this tool.
|
||||
|
||||
|
@ -22,21 +14,22 @@ AutoRecon was inspired by three tools which the author used during the OSCP labs
|
|||
|
||||
## Features
|
||||
|
||||
* Supports multiple targets in the form of IP addresses, IP ranges (CIDR notation), and resolvable hostnames.
|
||||
* Can scan targets concurrently, utilizing multiple processors if they are available.
|
||||
* Customizable port scanning profiles for flexibility in your initial scans.
|
||||
* Customizable service enumeration commands and suggested manual follow-up commands.
|
||||
* Supports multiple targets in the form of IP addresses, IP ranges (CIDR notation), and resolvable hostnames. IPv6 is also supported.
|
||||
* Can scan multiple targets concurrently, utilizing multiple processors if they are available.
|
||||
* Advanced plugin system allowing for easy creation of new scans.
|
||||
* Customizable port scanning plugins for flexibility in your initial scans.
|
||||
* Customizable service scanning plugins for further enumeration.
|
||||
* Suggested manual follow-up commands for when automation makes little sense.
|
||||
* Ability to limit port scanning to a combination of TCP/UDP ports.
|
||||
* Ability to skip port scanning phase by suppling information about services which should be open.
|
||||
* Global and per-scan pattern matching which highlights and extracts important information from the noise.
|
||||
* An intuitive directory structure for results gathering.
|
||||
* Full logging of commands that were run, along with errors if they fail.
|
||||
* Global and per-scan pattern matching so you can highlight/extract important information from the noise.
|
||||
|
||||
## Quick Install (Docker)
|
||||
|
||||
If you don't have a Kali instance, you can quickly install AutoRecon using the Dockerfile in the repository. Simply download the Dockerfile, and run the following command from the same directory:
|
||||
|
||||
```bash
|
||||
sudo docker build -t tib3rius/autorecon .
|
||||
```
|
||||
* A powerful config file lets you use your favorite settings every time.
|
||||
* A tagging system that lets you include or exclude certain plugins.
|
||||
* Global and per-target timeouts in case you only have limited time.
|
||||
* Three levels of verbosity, controllable by command-line options, and during scans using Up/Down arrows.
|
||||
* Colorized output for distinguishing separate pieces of information. Can be turned off for accessibility reasons.
|
||||
|
||||
## Requirements
|
||||
|
||||
|
@ -53,18 +46,9 @@ $ sudo apt install python3
|
|||
$ sudo apt install python3-pip
|
||||
```
|
||||
|
||||
Additionally, if you experience any issues with the stability of the `python3-pip` installation (as reported by a number of people installing `pip3` via `apt` on the OSCP distribution of Kali), you can install it manually as follows:
|
||||
|
||||
```bash
|
||||
$ curl https://bootstrap.pypa.io/get-pip.py -o get-pip.py
|
||||
$ python3 get-pip.py
|
||||
```
|
||||
|
||||
The `pip3` command should now be usable.
|
||||
|
||||
### `pipx`
|
||||
|
||||
Further, it's recommended you use `pipx` to manage your python packages; this installs each python package in it's own virtualenv, and makes it available in the global context, which avoids conflicting package dependencies and the resulting instability. To summarise the installation instructions:
|
||||
Further, it's recommended you use `pipx` to manage your python packages; this installs each python package in it's own virtualenv, and makes it available in the global context, which avoids conflicting package dependencies and the resulting instability. To summarize the installation instructions:
|
||||
|
||||
```bash
|
||||
$ sudo apt install python3-venv
|
||||
|
@ -74,33 +58,7 @@ $ python3 -m pipx ensurepath
|
|||
|
||||
You will have to re-source your ~/.bashrc or ~/.zshrc file (or open a new tab) after running these commands in order to use pipx.
|
||||
|
||||
Note that if you want to elevate privileges to run a program installed with `pipx`, with `sudo`, you have two options:
|
||||
|
||||
1. Append the appropriate path to your execution command, using _one_ of the following examples (recommended):
|
||||
|
||||
```bash
|
||||
$ sudo env "PATH=$PATH" autorecon [OPTIONS]
|
||||
$ sudo $(which autorecon) [OPTIONS]
|
||||
```
|
||||
|
||||
To make this easier, you could add the following alias to your `~/.profile` (or equivalent):
|
||||
|
||||
```
|
||||
alias sudo="sudo env \"PATH=$PATH\""
|
||||
```
|
||||
|
||||
2. Add the `pipx` binary path to the `secure_path` set in `/etc/sudoers`
|
||||
|
||||
```bash
|
||||
sudo visudo /etc/sudoers
|
||||
```
|
||||
|
||||
Update the `secure_path` directive as follows:
|
||||
```
|
||||
Defaults secure_path="/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/home/kali/.local/bin"
|
||||
```
|
||||
|
||||
If you're not using Kali Linux, make sure to adjust the path to the relevant user. Further detail on the installation of `pipx` is available in their installation instructions available [here](https://pipxproject.github.io/pipx/installation/). Please refer to this for any issues you experience.
|
||||
Note that if you want to run AutoRecon via pipx using sudo, you'll have to install pipx and AutoRecon using sudo as well.
|
||||
|
||||
### Supporting packages
|
||||
|
||||
|
@ -118,26 +76,27 @@ Additionally the following commands may need to be installed, depending on your
|
|||
curl
|
||||
enum4linux
|
||||
feroxbuster
|
||||
impacket-scripts
|
||||
nbtscan
|
||||
nikto
|
||||
nmap
|
||||
onesixtyone
|
||||
oscanner
|
||||
redis-tools
|
||||
smbclient
|
||||
smbmap
|
||||
smtp-user-enum
|
||||
snmpwalk
|
||||
sslscan
|
||||
svwar
|
||||
tnscmd10g
|
||||
whatweb
|
||||
wkhtmltoimage
|
||||
wkhtmltopdf
|
||||
```
|
||||
|
||||
On Kali Linux, you can ensure these are all installed using the following command:
|
||||
|
||||
```bash
|
||||
$ sudo apt install seclists curl enum4linux feroxbuster nbtscan nikto nmap onesixtyone oscanner smbclient smbmap smtp-user-enum snmp sslscan sipvicious tnscmd10g whatweb wkhtmltopdf
|
||||
$ sudo apt install seclists curl enum4linux feroxbuster impacket-scripts nbtscan nikto nmap onesixtyone oscanner redis-tools smbclient smbmap snmp sslscan sipvicious tnscmd10g whatweb wkhtmltopdf
|
||||
```
|
||||
|
||||
## Installation
|
||||
|
@ -146,6 +105,8 @@ Ensure you have all of the requirements installed as per the previous section.
|
|||
|
||||
### Using `pipx` (recommended)
|
||||
|
||||
If installing using pipx, you'll need to run the installation command as root or with sudo in order to be able to run autorecon using sudo:
|
||||
|
||||
```bash
|
||||
$ pipx install git+https://github.com/Tib3rius/AutoRecon.git
|
||||
```
|
||||
|
@ -160,13 +121,13 @@ $ sudo python3 -m pip install git+https://github.com/Tib3rius/AutoRecon.git
|
|||
|
||||
### Manual
|
||||
|
||||
If you'd prefer not to use `pip` or `pipx`, you can always still install and execute `autorecon.py` manually as a script. First install the dependencies:
|
||||
If you'd prefer not to use `pip` or `pipx`, you can always still install and execute `autorecon.py` manually as a script. From within the AutoRecon directory, install the dependencies:
|
||||
|
||||
```bash
|
||||
$ python3 -m pip install -r requirements.txt
|
||||
```
|
||||
|
||||
You will then be able to run the `autorecon.py` script (from `<AUTORECON_ROOT_DIR>/src/autorecon`):
|
||||
You will then be able to run the `autorecon.py` script:
|
||||
|
||||
```bash
|
||||
$ python3 autorecon.py [OPTIONS] 127.0.0.1
|
||||
|
@ -174,184 +135,133 @@ $ python3 autorecon.py [OPTIONS] 127.0.0.1
|
|||
|
||||
See detailed usage options below.
|
||||
|
||||
### Upgrading
|
||||
|
||||
If you installed using pipx or pip it is recommended that prior to upgrading, you remove all config files stored in ~/.config/AutoRecon. Note that if you run AutoRecon using sudo, you'll also have to delete the config files in /root/.config/AutoRecon.
|
||||
|
||||
## Usage
|
||||
|
||||
AutoRecon uses Python 3 specific functionality and does not support Python 2.
|
||||
|
||||
```
|
||||
usage: autorecon [-h] [-t TARGET_FILE] [-ct <number>] [-cs <number>]
|
||||
[--profile PROFILE_NAME] [-o OUTPUT_DIR] [--single-target]
|
||||
[--only-scans-dir] [--heartbeat HEARTBEAT]
|
||||
[--nmap NMAP | --nmap-append NMAP_APPEND] [-v]
|
||||
[--disable-sanity-checks]
|
||||
[targets [targets ...]]
|
||||
usage: autorecon [-t TARGET_FILE] [-p PORTS] [-m MAX_SCANS] [-mp MAX_PORT_SCANS] [-c CONFIG_FILE] [-g GLOBAL_FILE]
|
||||
[--tags TAGS] [--exclude-tags TAGS] [--port-scans PLUGINS] [--service-scans PLUGINS]
|
||||
[--reports PLUGINS] [--plugins-dir PLUGINS_DIR] [--add-plugins-dir PLUGINS_DIR] [-l [TYPE]] [-o OUTDIR]
|
||||
[--single-target] [--only-scans-dir] [--create-port-dirs] [--heartbeat HEARTBEAT] [--timeout TIMEOUT]
|
||||
[--target-timeout TARGET_TIMEOUT] [--nmap NMAP | --nmap-append NMAP_APPEND] [--proxychains]
|
||||
[--disable-sanity-checks] [--disable-keyboard-control] [--force-services SERVICE [SERVICE ...]]
|
||||
[--accessible] [-v] [--version] [--curl.path VALUE]
|
||||
[--dirbuster.tool {feroxbuster,gobuster,dirsearch,ffuf,dirb}] [--dirbuster.wordlist VALUE [VALUE ...]]
|
||||
[--dirbuster.threads VALUE] [--dirbuster.ext VALUE] [--onesixtyone.community-strings VALUE]
|
||||
[--global.username-wordlist VALUE] [--global.password-wordlist VALUE] [--global.domain VALUE] [-h]
|
||||
[targets ...]
|
||||
|
||||
Network reconnaissance tool to port scan and automatically enumerate services
|
||||
found on multiple targets.
|
||||
Network reconnaissance tool to port scan and automatically enumerate services found on multiple targets.
|
||||
|
||||
positional arguments:
|
||||
targets IP addresses (e.g. 10.0.0.1), CIDR notation (e.g.
|
||||
10.0.0.1/24), or resolvable hostnames (e.g. foo.bar)
|
||||
to scan.
|
||||
targets IP addresses (e.g. 10.0.0.1), CIDR notation (e.g. 10.0.0.1/24), or resolvable hostnames (e.g.
|
||||
foo.bar) to scan.
|
||||
|
||||
optional arguments:
|
||||
-h, --help show this help message and exit
|
||||
-t TARGET_FILE, --targets TARGET_FILE
|
||||
Read targets from file.
|
||||
-ct <number>, --concurrent-targets <number>
|
||||
The maximum number of target hosts to scan
|
||||
concurrently. Default: 5
|
||||
-cs <number>, --concurrent-scans <number>
|
||||
The maximum number of scans to perform per target
|
||||
host. Default: 10
|
||||
--profile PROFILE_NAME
|
||||
The port scanning profile to use (defined in port-
|
||||
scan-profiles.toml). Default: default
|
||||
-o OUTPUT_DIR, --output OUTPUT_DIR
|
||||
-p PORTS, --ports PORTS
|
||||
Comma separated list of ports / port ranges to scan. Specify TCP/UDP ports by prepending list
|
||||
with T:/U: To scan both TCP/UDP, put port(s) at start or specify B: e.g.
|
||||
53,T:21-25,80,U:123,B:123. Default: None
|
||||
-m MAX_SCANS, --max-scans MAX_SCANS
|
||||
The maximum number of concurrent scans to run. Default: 50
|
||||
-mp MAX_PORT_SCANS, --max-port-scans MAX_PORT_SCANS
|
||||
The maximum number of concurrent port scans to run. Default: 10 (approx 20% of max-scans unless
|
||||
specified)
|
||||
-c CONFIG_FILE, --config CONFIG_FILE
|
||||
Location of AutoRecon's config file. Default: /home/tib3rius/.config/AutoRecon/config.toml
|
||||
-g GLOBAL_FILE, --global-file GLOBAL_FILE
|
||||
Location of AutoRecon's global file. Default: /home/tib3rius/.config/AutoRecon/global.toml
|
||||
--tags TAGS Tags to determine which plugins should be included. Separate tags by a plus symbol (+) to group
|
||||
tags together. Separate groups with a comma (,) to create multiple groups. For a plugin to be
|
||||
included, it must have all the tags specified in at least one group. Default: default
|
||||
--exclude-tags TAGS Tags to determine which plugins should be excluded. Separate tags by a plus symbol (+) to group
|
||||
tags together. Separate groups with a comma (,) to create multiple groups. For a plugin to be
|
||||
excluded, it must have all the tags specified in at least one group. Default: None
|
||||
--port-scans PLUGINS Override --tags / --exclude-tags for the listed PortScan plugins (comma separated). Default:
|
||||
None
|
||||
--service-scans PLUGINS
|
||||
Override --tags / --exclude-tags for the listed ServiceScan plugins (comma separated). Default:
|
||||
None
|
||||
--reports PLUGINS Override --tags / --exclude-tags for the listed Report plugins (comma separated). Default: None
|
||||
--plugins-dir PLUGINS_DIR
|
||||
The location of the plugins directory. Default: /home/tib3rius/.config/AutoRecon/plugins
|
||||
--add-plugins-dir PLUGINS_DIR
|
||||
The location of an additional plugins directory to add to the main one. Default: None
|
||||
-l [TYPE], --list [TYPE]
|
||||
List all plugins or plugins of a specific type. e.g. --list, --list port, --list service
|
||||
-o OUTDIR, --output OUTDIR
|
||||
The output directory for results. Default: results
|
||||
--single-target Only scan a single target. A directory named after the
|
||||
target will not be created. Instead, the directory
|
||||
structure will be created within the output directory.
|
||||
Default: false
|
||||
--only-scans-dir Only create the "scans" directory for results. Other
|
||||
directories (e.g. exploit, loot, report) will not be
|
||||
created. Default: false
|
||||
--single-target Only scan a single target. A directory named after the target will not be created. Instead, the
|
||||
directory structure will be created within the output directory. Default: False
|
||||
--only-scans-dir Only create the "scans" directory for results. Other directories (e.g. exploit, loot, report)
|
||||
will not be created. Default: False
|
||||
--create-port-dirs Create directories for ports within the "scans" directory (e.g. scans/tcp80, scans/udp53) and
|
||||
store results in these directories. Default: True
|
||||
--heartbeat HEARTBEAT
|
||||
Specifies the heartbeat interval (in seconds) for task
|
||||
status messages. Default: 60
|
||||
--nmap NMAP Override the {nmap_extra} variable in scans. Default:
|
||||
-vv --reason -Pn
|
||||
Specifies the heartbeat interval (in seconds) for scan status messages. Default: 60
|
||||
--timeout TIMEOUT Specifies the maximum amount of time in minutes that AutoRecon should run for. Default: None
|
||||
--target-timeout TARGET_TIMEOUT
|
||||
Specifies the maximum amount of time in minutes that a target should be scanned for before
|
||||
abandoning it and moving on. Default: None
|
||||
--nmap NMAP Override the {nmap_extra} variable in scans. Default: -vv --reason -Pn
|
||||
--nmap-append NMAP_APPEND
|
||||
Append to the default {nmap_extra} variable in scans.
|
||||
-v, --verbose Enable verbose output. Repeat for more verbosity.
|
||||
Append to the default {nmap_extra} variable in scans. Default: -T4
|
||||
--proxychains Use if you are running AutoRecon via proxychains. Default: False
|
||||
--disable-sanity-checks
|
||||
Disable sanity checks that would otherwise prevent the
|
||||
scans from running. Default: false
|
||||
Disable sanity checks that would otherwise prevent the scans from running. Default: False
|
||||
--disable-keyboard-control
|
||||
Disables keyboard control ([s]tatus, Up, Down) if you are in SSH or Docker.
|
||||
--force-services SERVICE [SERVICE ...]
|
||||
A space separated list of services in the following style: tcp/80/http tcp/443/https/secure
|
||||
--accessible Attempts to make AutoRecon output more accessible to screenreaders. Default: False
|
||||
-v, --verbose Enable verbose output. Repeat for more verbosity.
|
||||
--version Prints the AutoRecon version and exits.
|
||||
-h, --help Show this help message and exit.
|
||||
|
||||
plugin arguments:
|
||||
These are optional arguments for certain plugins.
|
||||
|
||||
--curl.path VALUE The path on the web server to curl. Default: /
|
||||
--dirbuster.tool {feroxbuster,gobuster,dirsearch,ffuf,dirb}
|
||||
The tool to use for directory busting. Default: feroxbuster
|
||||
--dirbuster.wordlist VALUE [VALUE ...]
|
||||
The wordlist(s) to use when directory busting. Separate multiple wordlists with spaces. Default:
|
||||
['/usr/share/seclists/Discovery/Web-Content/common.txt', '/usr/share/seclists/Discovery/Web-
|
||||
Content/big.txt', '/usr/share/seclists/Discovery/Web-Content/raft-large-words.txt']
|
||||
--dirbuster.threads VALUE
|
||||
The number of threads to use when directory busting. Default: 10
|
||||
--dirbuster.ext VALUE
|
||||
The extensions you wish to fuzz (no dot, comma separated). Default: txt,html,php,asp,aspx,jsp
|
||||
--onesixtyone.community-strings VALUE
|
||||
The file containing a list of community strings to try. Default:
|
||||
/usr/share/seclists/Discovery/SNMP/common-snmp-community-strings-onesixtyone.txt
|
||||
|
||||
global plugin arguments:
|
||||
These are optional arguments that can be used by all plugins.
|
||||
|
||||
--global.username-wordlist VALUE
|
||||
A wordlist of usernames, useful for bruteforcing. Default: /usr/share/seclists/Usernames/top-
|
||||
usernames-shortlist.txt
|
||||
--global.password-wordlist VALUE
|
||||
A wordlist of passwords, useful for bruteforcing. Default:
|
||||
/usr/share/seclists/Passwords/darkweb2017-top100.txt
|
||||
--global.domain VALUE
|
||||
The domain to use (if known). Used for DNS and/or Active Directory. Default: None
|
||||
```
|
||||
|
||||
### Examples
|
||||
|
||||
**Scanning a single target:**
|
||||
|
||||
```
|
||||
$ autorecon 127.0.0.1
|
||||
[*] Scanning target 127.0.0.1
|
||||
[*] Running service detection nmap-full-tcp on 127.0.0.1
|
||||
[*] Running service detection nmap-top-20-udp on 127.0.0.1
|
||||
[*] Running service detection nmap-quick on 127.0.0.1
|
||||
[*] Service detection nmap-quick on 127.0.0.1 finished successfully
|
||||
[*] [127.0.0.1] ssh found on tcp/22
|
||||
[*] [127.0.0.1] http found on tcp/80
|
||||
[*] [127.0.0.1] rpcbind found on tcp/111
|
||||
[*] [127.0.0.1] postgresql found on tcp/5432
|
||||
[*] Running task tcp/22/nmap-ssh on 127.0.0.1
|
||||
[*] Running task tcp/80/nmap-http on 127.0.0.1
|
||||
[*] Running task tcp/80/curl-index on 127.0.0.1
|
||||
[*] Running task tcp/80/curl-robots on 127.0.0.1
|
||||
[*] Running task tcp/80/whatweb on 127.0.0.1
|
||||
[*] Running task tcp/80/nikto on 127.0.0.1
|
||||
[*] Running task tcp/111/nmap-nfs on 127.0.0.1
|
||||
[*] Task tcp/80/curl-index on 127.0.0.1 finished successfully
|
||||
[*] Task tcp/80/curl-robots on 127.0.0.1 finished successfully
|
||||
[*] Task tcp/22/nmap-ssh on 127.0.0.1 finished successfully
|
||||
[*] Task tcp/80/whatweb on 127.0.0.1 finished successfully
|
||||
[*] Task tcp/111/nmap-nfs on 127.0.0.1 finished successfully
|
||||
[*] Task tcp/80/nmap-http on 127.0.0.1 finished successfully
|
||||
[*] Task tcp/80/nikto on 127.0.0.1 finished successfully
|
||||
[*] Service detection nmap-top-20-udp on 127.0.0.1 finished successfully
|
||||
[*] Service detection nmap-full-tcp on 127.0.0.1 finished successfully
|
||||
[*] [127.0.0.1] http found on tcp/5984
|
||||
[*] [127.0.0.1] rtsp found on tcp/5985
|
||||
[*] Running task tcp/5984/nmap-http on 127.0.0.1
|
||||
[*] Running task tcp/5984/curl-index on 127.0.0.1
|
||||
[*] Running task tcp/5984/curl-robots on 127.0.0.1
|
||||
[*] Running task tcp/5984/whatweb on 127.0.0.1
|
||||
[*] Running task tcp/5984/nikto on 127.0.0.1
|
||||
[*] Task tcp/5984/curl-index on 127.0.0.1 finished successfully
|
||||
[*] Task tcp/5984/curl-robots on 127.0.0.1 finished successfully
|
||||
[*] Task tcp/5984/whatweb on 127.0.0.1 finished successfully
|
||||
[*] Task tcp/5984/nikto on 127.0.0.1 finished successfully
|
||||
[*] Task tcp/5984/nmap-http on 127.0.0.1 finished successfully
|
||||
[*] Finished scanning target 127.0.0.1
|
||||
```
|
||||
|
||||
The default port scan profile first performs a full TCP port scan, a top 20 UDP port scan, and a top 1000 TCP port scan. You may ask why AutoRecon scans the top 1000 TCP ports at the same time as a full TCP port scan (which also scans those ports). The reason is simple: most open ports will generally be in the top 1000, and we want to start enumerating services quickly, rather than wait for Nmap to scan every single port. As you can see, all the service enumeration scans actually finish before the full TCP port scan is done. While there is a slight duplication of efforts, it pays off by getting actual enumeration results back to the tester quicker.
|
||||
|
||||
Note that the actual command line output will be colorized if your terminal supports it.
|
||||
|
||||
**Scanning multiple targets**
|
||||
|
||||
```
|
||||
$ autorecon 192.168.1.100 192.168.1.1/30 localhost
|
||||
[*] Scanning target 192.168.1.100
|
||||
[*] Scanning target 192.168.1.1
|
||||
[*] Scanning target 192.168.1.2
|
||||
[*] Scanning target localhost
|
||||
[*] Running service detection nmap-quick on 192.168.1.100
|
||||
[*] Running service detection nmap-quick on localhost
|
||||
[*] Running service detection nmap-top-20-udp on 192.168.1.100
|
||||
[*] Running service detection nmap-quick on 192.168.1.1
|
||||
[*] Running service detection nmap-quick on 192.168.1.2
|
||||
[*] Running service detection nmap-top-20-udp on 192.168.1.1
|
||||
[*] Running service detection nmap-full-tcp on 192.168.1.100
|
||||
[*] Running service detection nmap-top-20-udp on localhost
|
||||
[*] Running service detection nmap-top-20-udp on 192.168.1.2
|
||||
[*] Running service detection nmap-full-tcp on localhost
|
||||
[*] Running service detection nmap-full-tcp on 192.168.1.1
|
||||
[*] Running service detection nmap-full-tcp on 192.168.1.2
|
||||
...
|
||||
```
|
||||
|
||||
AutoRecon supports multiple targets per scan, and will expand IP ranges provided in CIDR notation. By default, only 5 targets will be scanned at a time, with 10 scans per target.
|
||||
|
||||
**Scanning multiple targets with advanced options**
|
||||
|
||||
```
|
||||
$ autorecon -ct 2 -cs 2 -vv -o outputdir 192.168.1.100 192.168.1.1/30 localhost
|
||||
[*] Scanning target 192.168.1.100
|
||||
[*] Scanning target 192.168.1.1
|
||||
[*] Running service detection nmap-quick on 192.168.1.100 with nmap -vv --reason -Pn -sV -sC --version-all -oN "/root/outputdir/192.168.1.100/scans/_quick_tcp_nmap.txt" -oX "/root/outputdir/192.168.1.100/scans/_quick_tcp_nmap.xml" 192.168.1.100
|
||||
[*] Running service detection nmap-quick on 192.168.1.1 with nmap -vv --reason -Pn -sV -sC --version-all -oN "/root/outputdir/192.168.1.1/scans/_quick_tcp_nmap.txt" -oX "/root/outputdir/192.168.1.1/scans/_quick_tcp_nmap.xml" 192.168.1.1
|
||||
[*] Running service detection nmap-top-20-udp on 192.168.1.100 with nmap -vv --reason -Pn -sU -A --top-ports=20 --version-all -oN "/root/outputdir/192.168.1.100/scans/_top_20_udp_nmap.txt" -oX "/root/outputdir/192.168.1.100/scans/_top_20_udp_nmap.xml" 192.168.1.100
|
||||
[*] Running service detection nmap-top-20-udp on 192.168.1.1 with nmap -vv --reason -Pn -sU -A --top-ports=20 --version-all -oN "/root/outputdir/192.168.1.1/scans/_top_20_udp_nmap.txt" -oX "/root/outputdir/192.168.1.1/scans/_top_20_udp_nmap.xml" 192.168.1.1
|
||||
[-] [192.168.1.1 nmap-quick] Starting Nmap 7.70 ( https://nmap.org ) at 2019-03-01 17:25 EST
|
||||
[-] [192.168.1.100 nmap-quick] Starting Nmap 7.70 ( https://nmap.org ) at 2019-03-01 17:25 EST
|
||||
[-] [192.168.1.100 nmap-top-20-udp] Starting Nmap 7.70 ( https://nmap.org ) at 2019-03-01 17:25 EST
|
||||
[-] [192.168.1.1 nmap-top-20-udp] Starting Nmap 7.70 ( https://nmap.org ) at 2019-03-01 17:25 EST
|
||||
[-] [192.168.1.1 nmap-quick] NSE: Loaded 148 scripts for scanning.
|
||||
[-] [192.168.1.1 nmap-quick] NSE: Script Pre-scanning.
|
||||
[-] [192.168.1.1 nmap-quick] NSE: Starting runlevel 1 (of 2) scan.
|
||||
[-] [192.168.1.1 nmap-quick] Initiating NSE at 17:25
|
||||
[-] [192.168.1.1 nmap-quick] Completed NSE at 17:25, 0.00s elapsed
|
||||
[-] [192.168.1.1 nmap-quick] NSE: Starting runlevel 2 (of 2) scan.
|
||||
[-] [192.168.1.1 nmap-quick] Initiating NSE at 17:25
|
||||
[-] [192.168.1.1 nmap-quick] Completed NSE at 17:25, 0.00s elapsed
|
||||
[-] [192.168.1.1 nmap-quick] Initiating ARP Ping Scan at 17:25
|
||||
[-] [192.168.1.100 nmap-quick] NSE: Loaded 148 scripts for scanning.
|
||||
[-] [192.168.1.100 nmap-quick] NSE: Script Pre-scanning.
|
||||
[-] [192.168.1.100 nmap-quick] NSE: Starting runlevel 1 (of 2) scan.
|
||||
[-] [192.168.1.100 nmap-quick] Initiating NSE at 17:25
|
||||
[-] [192.168.1.100 nmap-quick] Completed NSE at 17:25, 0.00s elapsed
|
||||
[-] [192.168.1.100 nmap-quick] NSE: Starting runlevel 2 (of 2) scan.
|
||||
[-] [192.168.1.100 nmap-quick] Initiating NSE at 17:25
|
||||
[-] [192.168.1.100 nmap-quick] Completed NSE at 17:25, 0.00s elapsed
|
||||
[-] [192.168.1.100 nmap-quick] Initiating ARP Ping Scan at 17:25
|
||||
...
|
||||
```
|
||||
|
||||
In this example, the -ct option limits the number of concurrent targets to 2, and the -cs option limits the number of concurrent scans per target to 2. The -vv option makes the output very verbose, showing the output of every scan being run. The -o option sets a custom output directory for scan results to be saved.
|
||||
|
||||
### Verbosity
|
||||
|
||||
AutoRecon supports three levels of verbosity:
|
||||
AutoRecon supports four levels of verbosity:
|
||||
|
||||
* (none) Minimal output. AutoRecon will announce when target scans start and finish, as well as which services were identified.
|
||||
* (-v) Verbose output. AutoRecon will additionally specify the exact commands which are being run, as well as highlighting any patterns which are matched in command output.
|
||||
* (-vv) Very verbose output. AutoRecon will output everything. Literally every line from all commands which are currently running. When scanning multiple targets concurrently, this can lead to a ridiculous amount of output. It is not advised to use -vv unless you absolutely need to see live output from commands.
|
||||
* (none) Minimal output. AutoRecon will announce when scanning targets starts / ends.
|
||||
* (-v) Verbose output. AutoRecon will additionally announce when plugins start running, and report open ports and identified services.
|
||||
* (-vv) Very verbose output. AutoRecon will additionally specify the exact commands which are being run by plugins, highlight any patterns which are matched in command output, and announce when plugins end.
|
||||
* (-vvv) Very, very verbose output. AutoRecon will output everything. Literally every line from all commands which are currently running. When scanning multiple targets concurrently, this can lead to a ridiculous amount of output. It is not advised to use -vvv unless you absolutely need to see live output from commands.
|
||||
|
||||
Note: You can change the verbosity of AutoRecon mid-scan by pressing the up and down arrow keys.
|
||||
|
||||
### Results
|
||||
|
||||
|
@ -367,9 +277,9 @@ By default, results will be stored in the ./results directory. A new sub directo
|
|||
│ ├── proof.txt
|
||||
│ └── screenshots/
|
||||
└── scans/
|
||||
├── _commands.log
|
||||
├── _manual_commands.txt
|
||||
└── xml/
|
||||
├── _commands.log
|
||||
├── _manual_commands.txt
|
||||
└── xml/
|
||||
```
|
||||
|
||||
The exploit directory is intended to contain any exploit code you download / write for the target.
|
||||
|
@ -392,171 +302,6 @@ If output matches a defined pattern, a file called \_patterns.log will also appe
|
|||
|
||||
The scans/xml directory stores any XML output (e.g. from Nmap scans) separately from the main scan outputs, so that the scans directory itself does not get too cluttered.
|
||||
|
||||
### Port Scan profiles
|
||||
|
||||
The port-scan-profiles.toml file is where you can define the initial port scans / service detection commands. The configuration file uses the TOML format, which is explained here: https://github.com/toml-lang/toml
|
||||
|
||||
Here is an example profile called "quick":
|
||||
|
||||
```toml
|
||||
[quick]
|
||||
|
||||
[quick.nmap-quick]
|
||||
|
||||
[quick.nmap-quick.service-detection]
|
||||
command = 'nmap {nmap_extra} -sV --version-all -oN "{scandir}/_quick_tcp_nmap.txt" -oX "{scandir}/xml/_quick_tcp_nmap.xml" {address}'
|
||||
pattern = '^(?P<port>\d+)\/(?P<protocol>(tcp|udp))(.*)open(\s*)(?P<service>[\w\-\/]+)(\s*)(.*)$'
|
||||
|
||||
[quick.nmap-top-20-udp]
|
||||
|
||||
[quick.nmap-top-20-udp.service-detection]
|
||||
command = 'nmap {nmap_extra} -sU -A --top-ports=20 --version-all -oN "{scandir}/_top_20_udp_nmap.txt" -oX "{scandir}/xml/_top_20_udp_nmap.xml" {address}'
|
||||
pattern = '^(?P<port>\d+)\/(?P<protocol>(tcp|udp))(.*)open(\s*)(?P<service>[\w\-\/]+)(\s*)(.*)$'
|
||||
```
|
||||
|
||||
Note that indentation is optional, it is used here purely for aesthetics. The "quick" profile defines a scan called "nmap-quick". This scan has a service-detection command which uses nmap to scan the top 1000 TCP ports. The command uses two references: {scandir} is the location of the scans directory for the target, and {address} is the address of the target.
|
||||
|
||||
A regex pattern is defined which matches three named groups (port, protocol, and service) in the output. Every service-detection command must have a corresponding pattern that matches all three of those groups. AutoRecon will attempt to do some checks and refuse to scan if any of these groups are missing.
|
||||
|
||||
An almost identical scan called "nmap-top-20-udp" is also defined. This scans the top 20 UDP ports.
|
||||
|
||||
Here is a more complicated example:
|
||||
|
||||
```toml
|
||||
[udp]
|
||||
|
||||
[udp.udp-top-20]
|
||||
|
||||
[udp.udp-top-20.port-scan]
|
||||
command = 'unicornscan -mU -p 631,161,137,123,138,1434,445,135,67,53,139,500,68,520,1900,4500,514,49152,162,69 {address} 2>&1 | tee "{scandir}/_top_20_udp_unicornscan.txt"'
|
||||
pattern = '^UDP open\s*[\w-]+\[\s*(?P<port>\d+)\].*$'
|
||||
|
||||
[udp.udp-top-20.service-detection]
|
||||
command = 'nmap {nmap_extra} -sU -A -p {ports} --version-all -oN "{scandir}/_top_20_udp_nmap.txt" -oX "{scandir}/xml/_top_20_udp_nmap.xml" {address}'
|
||||
pattern = '^(?P<port>\d+)\/(?P<protocol>(udp))(.*)open(\s*)(?P<service>[\w\-\/]+)(\s*)(.*)$'
|
||||
```
|
||||
|
||||
In this example, a profile called "udp" defines a scan called "udp-top-20". This scan has two commands, one is a port-scan and the other is a service-detection. When a port-scan command is defined, it will always be run first. The corresponding pattern must match a named group "port" which extracts the port number from the output.
|
||||
|
||||
The service-detection will be run after the port-scan command has finished, and uses a new reference: {ports}. This reference is a comma-separated string of all the ports extracted by the port-scan command. Note that the same three named groups (port, protocol, and service) are defined in the service-detection pattern.
|
||||
|
||||
Both the port-scan and the service-detection commands use the {scandir} and {address} references.
|
||||
|
||||
Note that if a port-scan command is defined without a corresponding service-detection command, AutoRecon will refuse to scan.
|
||||
|
||||
This more complicated example is only really useful if you want to use unicornscan's speed in conjuction with nmap's service detection abilities. If you are content with using Nmap for both port scanning and service detection, you do not need to use this setup.
|
||||
|
||||
### Service Scans
|
||||
|
||||
The service-scans.toml file is where you can define service enumeration scans and other manual commands associated with certain services.
|
||||
|
||||
Here is an example of a simple configuration:
|
||||
|
||||
```toml
|
||||
[ftp]
|
||||
|
||||
service-names = [
|
||||
'^ftp',
|
||||
'^ftp\-data'
|
||||
]
|
||||
|
||||
[[ftp.scan]]
|
||||
name = 'nmap-ftp'
|
||||
command = 'nmap {nmap_extra} -sV -p {port} --script="(ftp* or ssl*) and not (brute or broadcast or dos or external or fuzzer)" -oN "{scandir}/{protocol}_{port}_ftp_nmap.txt" -oX "{scandir}/xml/{protocol}_{port}_ftp_nmap.xml" {address}'
|
||||
|
||||
[[ftp.scan.pattern]]
|
||||
description = 'Anonymous FTP Enabled!'
|
||||
pattern = 'Anonymous FTP login allowed'
|
||||
|
||||
[[ftp.manual]]
|
||||
description = 'Bruteforce logins:'
|
||||
commands = [
|
||||
'hydra -L "{username_wordlist}" -P "{password_wordlist}" -e nsr -s {port} -o "{scandir}/{protocol}_{port}_ftp_hydra.txt" ftp://{address}',
|
||||
'medusa -U "{username_wordlist}" -P "{password_wordlist}" -e ns -n {port} -O "{scandir}/{protocol}_{port}_ftp_medusa.txt" -M ftp -h {address}'
|
||||
]
|
||||
```
|
||||
|
||||
Note that indentation is optional, it is used here purely for aesthetics. The service "ftp" is defined here. The service-names array contains regex strings which should match the service name from the service-detection scans. Regex is used to be as flexible as possible. The service-names array works on a whitelist basis; as long as one of the regex strings matches, the service will get scanned.
|
||||
|
||||
An optional ignore-service-names array can also be defined, if you want to blacklist certain regex strings from matching.
|
||||
|
||||
The ftp.scan section defines a single scan, named nmap-ftp. This scan defines a command which runs nmap with several ftp-related scripts. Several references are used here:
|
||||
* {nmap_extra} by default is set to "-vv --reason -Pn" but this can be overridden or appended to using the --nmap or --nmap-append command line options respectively. If the protocol is UDP, "-sU" will also be appended.
|
||||
* {port} is the port that the service is running on.
|
||||
* {scandir} is the location of the scans directory for the target.
|
||||
* {protocol} is the protocol being used (either tcp or udp).
|
||||
* {address} is the address of the target.
|
||||
|
||||
A pattern is defined for the nmap-ftp scan, which matches the simple pattern "Anonymous FTP login allowed". In the event that this pattern matches output of the nmap-ftp command, the pattern description ("Anonymous FTP Enabled!") will be saved to the \_patterns.log file in the scans directory. A special reference {match} can be used in the description to reference the entire match, or the first capturing group.
|
||||
|
||||
The ftp.manual section defines a group of manual commands. This group contains a description for the user, and a commands array which contains the commands that a user can run. Two new references are defined here: {username_wordlist} and {password_wordlist} which are configured at the very top of the service-scans.toml file, and default to a username and password wordlist provided by SecLists.
|
||||
|
||||
Here is a more complicated configuration:
|
||||
|
||||
```toml
|
||||
[smb]
|
||||
|
||||
service-names = [
|
||||
'^smb',
|
||||
'^microsoft\-ds',
|
||||
'^netbios'
|
||||
]
|
||||
|
||||
[[smb.scan]]
|
||||
name = 'nmap-smb'
|
||||
command = 'nmap {nmap_extra} -sV -p {port} --script="(nbstat or smb* or ssl*) and not (brute or broadcast or dos or external or fuzzer)" --script-args="unsafe=1" -oN "{scandir}/{protocol}_{port}_smb_nmap.txt" -oX "{scandir}/xml/{protocol}_{port}_smb_nmap.xml" {address}'
|
||||
|
||||
[[smb.scan]]
|
||||
name = 'enum4linux'
|
||||
command = 'enum4linux -a -M -l -d {address} 2>&1 | tee "{scandir}/enum4linux.txt"'
|
||||
run_once = true
|
||||
ports.tcp = [139, 389, 445]
|
||||
ports.udp = [137]
|
||||
|
||||
[[smb.scan]]
|
||||
name = 'nbtscan'
|
||||
command = 'nbtscan -rvh {address} 2>&1 | tee "{scandir}/nbtscan.txt"'
|
||||
run_once = true
|
||||
ports.udp = [137]
|
||||
|
||||
[[smb.scan]]
|
||||
name = 'smbclient'
|
||||
command = 'smbclient -L\\ -N -I {address} 2>&1 | tee "{scandir}/smbclient.txt"'
|
||||
run_once = true
|
||||
ports.tcp = [139, 445]
|
||||
|
||||
[[smb.scan]]
|
||||
name = 'smbmap-share-permissions'
|
||||
command = 'smbmap -H {address} -P {port} 2>&1 | tee -a "{scandir}/smbmap-share-permissions.txt"; smbmap -u null -p "" -H {address} -P {port} 2>&1 | tee -a "{scandir}/smbmap-share-permissions.txt"'
|
||||
|
||||
[[smb.scan]]
|
||||
name = 'smbmap-list-contents'
|
||||
command = 'smbmap -H {address} -P {port} -R 2>&1 | tee -a "{scandir}/smbmap-list-contents.txt"; smbmap -u null -p "" -H {address} -P {port} -R 2>&1 | tee -a "{scandir}/smbmap-list-contents.txt"'
|
||||
|
||||
[[smb.scan]]
|
||||
name = 'smbmap-execute-command'
|
||||
command = 'smbmap -H {address} -P {port} -x "ipconfig /all" 2>&1 | tee -a "{scandir}/smbmap-execute-command.txt"; smbmap -u null -p "" -H {address} -P {port} -x "ipconfig /all" 2>&1 | tee -a "{scandir}/smbmap-execute-command.txt"'
|
||||
|
||||
[[smb.manual]]
|
||||
description = 'Nmap scans for SMB vulnerabilities that could potentially cause a DoS if scanned (according to Nmap). Be careful:'
|
||||
commands = [
|
||||
'nmap {nmap_extra} -sV -p {port} --script="smb-vuln-ms06-025" --script-args="unsafe=1" -oN "{scandir}/{protocol}_{port}_smb_ms06-025.txt" -oX "{scandir}/xml/{protocol}_{port}_smb_ms06-025.xml" {address}',
|
||||
'nmap {nmap_extra} -sV -p {port} --script="smb-vuln-ms07-029" --script-args="unsafe=1" -oN "{scandir}/{protocol}_{port}_smb_ms07-029.txt" -oX "{scandir}/xml/{protocol}_{port}_smb_ms07-029.xml" {address}',
|
||||
'nmap {nmap_extra} -sV -p {port} --script="smb-vuln-ms08-067" --script-args="unsafe=1" -oN "{scandir}/{protocol}_{port}_smb_ms08-067.txt" -oX "{scandir}/xml/{protocol}_{port}_smb_ms08-067.xml" {address}'
|
||||
]
|
||||
```
|
||||
|
||||
The main difference here is that several scans have some new settings:
|
||||
|
||||
* The ports.tcp array defines a whitelist of TCP ports which the command can be run against. If the service is detected on a port that is not in the whitelist, the command will not be run against it.
|
||||
* The ports.udp array defines a whitelist of UDP ports which the command can be run against. It operates in the same way as the ports.tcp array.
|
||||
|
||||
Why do these settings even exist? Well, some commands will only run against specific ports, and can't be told to run against any other ports. enum4linux for example, will only run against TCP ports 139, 389, and 445, and UDP port 137.
|
||||
|
||||
In fact, enum4linux will always try these ports when it is run. So if the SMB service is found on TCP ports 139 and 445, AutoRecon may attempt to run enum4linux twice for no reason. This is why the third setting exists:
|
||||
|
||||
* If run_once is set to true, the command will only ever run once for that target, even if the SMB service is found on multiple ports.
|
||||
|
||||
## Testimonials
|
||||
|
||||
> AutoRecon was invaluable during my OSCP exam, in that it saved me from the tedium of executing my active information gathering commands myself. I was able to start on a target with all of the information I needed clearly laid in front of me. I would strongly recommend this utility for anyone in the PWK labs, the OSCP exam, or other environments such as VulnHub or HTB. It is a great tool for both people just starting down their journey into OffSec and seasoned veterans alike. Just make sure that somewhere between those two points you take the time to learn what's going on "under the hood" and how / why it scans what it does.
|
||||
|
@ -591,7 +336,7 @@ In fact, enum4linux will always try these ports when it is run. So if the SMB se
|
|||
>
|
||||
>\- d0hnuts (rooted 5/5 exam hosts)
|
||||
|
||||
> Autorecon is not just any other tool, it is a recon correlation framwork for engagements. This helped me fire a whole bunch of scans while I was working on other targets. This can help a lot in time management. This assisted me to own 4/5 boxes in pwk exam! Result: Passed!
|
||||
> Autorecon is not just any other tool, it is a recon correlation framweork for engagements. This helped me fire a whole bunch of scans while I was working on other targets. This can help a lot in time management. This assisted me to own 4/5 boxes in pwk exam! Result: Passed!
|
||||
>
|
||||
>\- Wh0ami (rooted 4/5 exam hosts)
|
||||
|
||||
|
|
|
@ -0,0 +1,4 @@
|
|||
from autorecon.main import main
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
|
@ -0,0 +1,71 @@
|
|||
import appdirs, os
|
||||
|
||||
config_dir = appdirs.user_config_dir('AutoRecon')
|
||||
|
||||
configurable_keys = [
|
||||
'ports',
|
||||
'max_scans',
|
||||
'max_port_scans',
|
||||
'tags',
|
||||
'exclude_tags',
|
||||
'port_scans',
|
||||
'service_scans',
|
||||
'reports',
|
||||
'plugins_dir',
|
||||
'add_plugins-dir',
|
||||
'outdir',
|
||||
'single_target',
|
||||
'only_scans_dir',
|
||||
'create_port_dirs',
|
||||
'heartbeat',
|
||||
'timeout',
|
||||
'target_timeout',
|
||||
'nmap',
|
||||
'nmap_append',
|
||||
'proxychains',
|
||||
'disable_sanity_checks',
|
||||
'disable_keyboard_control',
|
||||
'force_services',
|
||||
'accessible',
|
||||
'verbose'
|
||||
]
|
||||
|
||||
configurable_boolean_keys = [
|
||||
'single_target',
|
||||
'only_scans_dir',
|
||||
'create_port_dirs',
|
||||
'proxychains',
|
||||
'disable_sanity_checks',
|
||||
'accessible'
|
||||
]
|
||||
|
||||
config = {
|
||||
'protected_classes': ['autorecon', 'target', 'service', 'commandstreamreader', 'plugin', 'portscan', 'servicescan', 'global', 'pattern'],
|
||||
'config_dir': config_dir,
|
||||
'global_file': os.path.join(config_dir, 'global.toml'),
|
||||
'ports': None,
|
||||
'max_scans': 50,
|
||||
'max_port_scans': None,
|
||||
'tags': 'default',
|
||||
'exclude_tags': None,
|
||||
'port_scans': None,
|
||||
'service_scans': None,
|
||||
'reports': None,
|
||||
'plugins_dir': os.path.join(config_dir, 'plugins'),
|
||||
'add_plugins_dir': None,
|
||||
'outdir': 'results',
|
||||
'single_target': False,
|
||||
'only_scans_dir': False,
|
||||
'create_port_dirs': False,
|
||||
'heartbeat': 60,
|
||||
'timeout': None,
|
||||
'target_timeout': None,
|
||||
'nmap': '-vv --reason -Pn',
|
||||
'nmap_append': '',
|
||||
'proxychains': False,
|
||||
'disable_sanity_checks': False,
|
||||
'disable_keyboard_control': False,
|
||||
'force_services': None,
|
||||
'accessible': False,
|
||||
'verbose': 0
|
||||
}
|
|
@ -0,0 +1,19 @@
|
|||
# Configure regular AutoRecon options at the top of this file.
|
||||
|
||||
create-port-dirs = true
|
||||
nmap-append = '-T4'
|
||||
# verbose = 1
|
||||
# max-scans = 30
|
||||
|
||||
# Configure global options here.
|
||||
# [global]
|
||||
# username-wordlist = '/usr/share/seclists/Usernames/cirt-default-usernames.txt'
|
||||
|
||||
# Configure plugin options here.
|
||||
# [dirbuster]
|
||||
# threads = 50
|
||||
# wordlist = [
|
||||
# '/usr/share/seclists/Discovery/Web-Content/common.txt',
|
||||
# '/usr/share/seclists/Discovery/Web-Content/big.txt',
|
||||
# '/usr/share/wordlists/dirbuster/directory-list-2.3-medium.txt'
|
||||
# ]
|
|
@ -0,0 +1,135 @@
|
|||
from autorecon.plugins import ServiceScan
|
||||
from autorecon.io import error
|
||||
from shutil import which
|
||||
|
||||
class NmapMongoDB(ServiceScan):
|
||||
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
self.name = "Nmap MongoDB"
|
||||
self.tags = ['default', 'safe', 'databases']
|
||||
|
||||
def configure(self):
|
||||
self.match_service_name('^mongod')
|
||||
|
||||
async def run(self, service):
|
||||
await service.execute('nmap {nmap_extra} -sV -p {port} --script="banner,(mongodb* or ssl*) and not (brute or broadcast or dos or external or fuzzer)" -oN "{scandir}/{protocol}_{port}_mongodb_nmap.txt" -oX "{scandir}/xml/{protocol}_{port}_mongodb_nmap.xml" {address}')
|
||||
|
||||
class NmapMSSQL(ServiceScan):
|
||||
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
self.name = "Nmap MSSQL"
|
||||
self.tags = ['default', 'safe', 'databases']
|
||||
|
||||
def configure(self):
|
||||
self.match_service_name(['^mssql', '^ms\-sql'])
|
||||
|
||||
def manual(self, service, plugin_was_run):
|
||||
if service.target.ipversion == 'IPv4':
|
||||
service.add_manual_command('(sqsh) interactive database shell:', 'sqsh -U <username> -P <password> -S {address}:{port}')
|
||||
|
||||
async def run(self, service):
|
||||
await service.execute('nmap {nmap_extra} -sV -p {port} --script="banner,(ms-sql* or ssl*) and not (brute or broadcast or dos or external or fuzzer)" --script-args="mssql.instance-port={port},mssql.username=sa,mssql.password=sa" -oN "{scandir}/{protocol}_{port}_mssql_nmap.txt" -oX "{scandir}/xml/{protocol}_{port}_mssql_nmap.xml" {address}')
|
||||
|
||||
class NmapMYSQL(ServiceScan):
|
||||
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
self.name = "Nmap MYSQL"
|
||||
self.tags = ['default', 'safe', 'databases']
|
||||
|
||||
def configure(self):
|
||||
self.match_service_name('^mysql')
|
||||
|
||||
def manual(self, service, plugin_was_run):
|
||||
if service.target.ipversion == 'IPv4':
|
||||
service.add_manual_command('(sqsh) interactive database shell:', 'sqsh -U <username> -P <password> -S {address}:{port}')
|
||||
|
||||
async def run(self, service):
|
||||
await service.execute('nmap {nmap_extra} -sV -p {port} --script="banner,(mysql* or ssl*) and not (brute or broadcast or dos or external or fuzzer)" -oN "{scandir}/{protocol}_{port}_mysql_nmap.txt" -oX "{scandir}/xml/{protocol}_{port}_mysql_nmap.xml" {address}')
|
||||
|
||||
class NmapOracle(ServiceScan):
|
||||
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
self.name = "Nmap Oracle"
|
||||
self.tags = ['default', 'safe', 'databases']
|
||||
|
||||
def configure(self):
|
||||
self.match_service_name('^oracle')
|
||||
|
||||
def manual(self, service, plugin_was_run):
|
||||
service.add_manual_command('Brute-force SIDs using Nmap:', 'nmap {nmap_extra} -sV -p {port} --script="banner,oracle-sid-brute" -oN "{scandir}/{protocol}_{port}_oracle_sid-brute_nmap.txt" -oX "{scandir}/xml/{protocol}_{port}_oracle_sid-brute_nmap.xml" {address}')
|
||||
|
||||
async def run(self, service):
|
||||
await service.execute('nmap {nmap_extra} -sV -p {port} --script="banner,(oracle* or ssl*) and not (brute or broadcast or dos or external or fuzzer)" -oN "{scandir}/{protocol}_{port}_oracle_nmap.txt" -oX "{scandir}/xml/{protocol}_{port}_oracle_nmap.xml" {address}')
|
||||
|
||||
class OracleTNScmd(ServiceScan):
|
||||
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
self.name = "Oracle TNScmd"
|
||||
self.tags = ['default', 'safe', 'databases']
|
||||
|
||||
def configure(self):
|
||||
self.match_service_name('^oracle')
|
||||
|
||||
def check(self):
|
||||
if which('tnscmd10g') is None:
|
||||
error('The tnscmd10g program could not be found. Make sure it is installed. (On Kali, run: sudo apt install tnscmd10g)')
|
||||
|
||||
async def run(self, service):
|
||||
if service.target.ipversion == 'IPv4':
|
||||
await service.execute('tnscmd10g ping -h {address} -p {port} 2>&1', outfile='{protocol}_{port}_oracle_tnscmd_ping.txt')
|
||||
await service.execute('tnscmd10g version -h {address} -p {port} 2>&1', outfile='{protocol}_{port}_oracle_tnscmd_version.txt')
|
||||
|
||||
class OracleScanner(ServiceScan):
|
||||
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
self.name = "Oracle Scanner"
|
||||
self.tags = ['default', 'safe', 'databases']
|
||||
|
||||
def configure(self):
|
||||
self.match_service_name('^oracle')
|
||||
|
||||
def check(self):
|
||||
if which('oscanner') is None:
|
||||
error('The oscanner program could not be found. Make sure it is installed. (On Kali, run: sudo apt install oscanner)')
|
||||
|
||||
async def run(self, service):
|
||||
await service.execute('oscanner -v -s {address} -P {port} 2>&1', outfile='{protocol}_{port}_oracle_scanner.txt')
|
||||
|
||||
class OracleODAT(ServiceScan):
|
||||
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
self.name = "Oracle ODAT"
|
||||
self.tags = ['default', 'safe', 'databases']
|
||||
|
||||
def configure(self):
|
||||
self.match_service_name('^oracle')
|
||||
|
||||
def manual(self, service, plugin_was_run):
|
||||
service.add_manual_commands('Install ODAT (https://github.com/quentinhardy/odat) and run the following commands:', [
|
||||
'python odat.py tnscmd -s {address} -p {port} --ping',
|
||||
'python odat.py tnscmd -s {address} -p {port} --version',
|
||||
'python odat.py tnscmd -s {address} -p {port} --status',
|
||||
'python odat.py sidguesser -s {address} -p {port}',
|
||||
'python odat.py passwordguesser -s {address} -p {port} -d <sid> --accounts-file accounts/accounts_multiple.txt',
|
||||
'python odat.py tnspoison -s {address} -p {port} -d <sid> --test-module'
|
||||
])
|
||||
|
||||
class OraclePatator(ServiceScan):
|
||||
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
self.name = "Oracle Patator"
|
||||
self.tags = ['default', 'databases']
|
||||
|
||||
def configure(self):
|
||||
self.match_service_name('^oracle')
|
||||
|
||||
def manual(self, service, plugin_was_run):
|
||||
service.add_manual_command('Install Oracle Instant Client (https://github.com/rapid7/metasploit-framework/wiki/How-to-get-Oracle-Support-working-with-Kali-Linux) and then bruteforce with patator:', 'patator oracle_login host={address} port={port} user=COMBO00 password=COMBO01 0=/usr/share/seclists/Passwords/Default-Credentials/oracle-betterdefaultpasslist.txt -x ignore:code=ORA-01017 -x ignore:code=ORA-28000')
|
|
@ -0,0 +1,103 @@
|
|||
from autorecon.plugins import PortScan
|
||||
from autorecon.io import info, error
|
||||
from autorecon.config import config
|
||||
import os, re
|
||||
|
||||
class QuickTCPPortScan(PortScan):
|
||||
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
self.name = 'Top TCP Ports'
|
||||
self.description = 'Performs an Nmap scan of the top 1000 TCP ports.'
|
||||
self.type = 'tcp'
|
||||
self.tags = ['default', 'default-port-scan']
|
||||
self.priority = 0
|
||||
|
||||
async def run(self, target):
|
||||
if target.ports: # Don't run this plugin if there are custom ports.
|
||||
return []
|
||||
|
||||
if config['proxychains']:
|
||||
traceroute_os = ''
|
||||
else:
|
||||
traceroute_os = ' -A --osscan-guess'
|
||||
|
||||
process, stdout, stderr = await target.execute('nmap {nmap_extra} -sV -sC --version-all' + traceroute_os + ' -oN "{scandir}/_quick_tcp_nmap.txt" -oX "{scandir}/xml/_quick_tcp_nmap.xml" {address}', blocking=False)
|
||||
services = await target.extract_services(stdout)
|
||||
await process.wait()
|
||||
return services
|
||||
|
||||
class AllTCPPortScan(PortScan):
|
||||
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
self.name = 'All TCP Ports'
|
||||
self.description = 'Performs an Nmap scan of all TCP ports.'
|
||||
self.type = 'tcp'
|
||||
self.specific_ports = True
|
||||
self.tags = ['default', 'default-port-scan', 'long']
|
||||
|
||||
async def run(self, target):
|
||||
if config['proxychains']:
|
||||
traceroute_os = ''
|
||||
else:
|
||||
traceroute_os = ' -A --osscan-guess'
|
||||
|
||||
if target.ports:
|
||||
if target.ports['tcp']:
|
||||
process, stdout, stderr = await target.execute('nmap {nmap_extra} -sV -sC --version-all' + traceroute_os + ' -p ' + target.ports['tcp'] + ' -oN "{scandir}/_full_tcp_nmap.txt" -oX "{scandir}/xml/_full_tcp_nmap.xml" {address}', blocking=False)
|
||||
else:
|
||||
return []
|
||||
else:
|
||||
process, stdout, stderr = await target.execute('nmap {nmap_extra} -sV -sC --version-all' + traceroute_os + ' -p- -oN "{scandir}/_full_tcp_nmap.txt" -oX "{scandir}/xml/_full_tcp_nmap.xml" {address}', blocking=False)
|
||||
services = []
|
||||
while True:
|
||||
line = await stdout.readline()
|
||||
if line is not None:
|
||||
match = re.search('^Discovered open port ([0-9]+)/tcp', line)
|
||||
if match:
|
||||
info('Discovered open port {bmagenta}tcp/' + match.group(1) + '{rst} on {byellow}' + target.address + '{rst}', verbosity=1)
|
||||
service = target.extract_service(line)
|
||||
if service:
|
||||
services.append(service)
|
||||
else:
|
||||
break
|
||||
await process.wait()
|
||||
return services
|
||||
|
||||
class Top100UDPPortScan(PortScan):
|
||||
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
self.name = 'Top 100 UDP Ports'
|
||||
self.description = 'Performs an Nmap scan of the top 100 UDP ports.'
|
||||
self.type = 'udp'
|
||||
self.specific_ports = True
|
||||
self.tags = ['default', 'default-port-scan', 'long']
|
||||
|
||||
async def run(self, target):
|
||||
# Only run UDP scan if user is root.
|
||||
if os.getuid() == 0:
|
||||
if target.ports:
|
||||
if target.ports['udp']:
|
||||
process, stdout, stderr = await target.execute('nmap {nmap_extra} -sU -A --osscan-guess -p ' + target.ports['udp'] + ' -oN "{scandir}/_custom_ports_udp_nmap.txt" -oX "{scandir}/xml/_custom_ports_udp_nmap.xml" {address}', blocking=False)
|
||||
else:
|
||||
return []
|
||||
else:
|
||||
process, stdout, stderr = await target.execute('nmap {nmap_extra} -sU -A --top-ports 100 -oN "{scandir}/_top_100_udp_nmap.txt" -oX "{scandir}/xml/_top_100_udp_nmap.xml" {address}', blocking=False)
|
||||
services = []
|
||||
while True:
|
||||
line = await stdout.readline()
|
||||
if line is not None:
|
||||
match = re.search('^Discovered open port ([0-9]+)/udp', line)
|
||||
if match:
|
||||
info('Discovered open port {bmagenta}udp/' + match.group(1) + '{rst} on {byellow}' + target.address + '{rst}', verbosity=1)
|
||||
service = target.extract_service(line)
|
||||
if service:
|
||||
services.append(service)
|
||||
else:
|
||||
break
|
||||
await process.wait()
|
||||
return services
|
||||
else:
|
||||
error('UDP scan requires AutoRecon be run with root privileges.')
|
|
@ -0,0 +1,57 @@
|
|||
from autorecon.plugins import ServiceScan
|
||||
|
||||
class NmapDNS(ServiceScan):
|
||||
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
self.name = 'Nmap DNS'
|
||||
self.tags = ['default', 'safe', 'dns']
|
||||
|
||||
def configure(self):
|
||||
self.match_service_name('^domain')
|
||||
|
||||
async def run(self, service):
|
||||
await service.execute('nmap {nmap_extra} -sV -p {port} --script="banner,(dns* or ssl*) and not (brute or broadcast or dos or external or fuzzer)" -oN "{scandir}/{protocol}_{port}_dns_nmap.txt" -oX "{scandir}/xml/{protocol}_{port}_dns_nmap.xml" {address}')
|
||||
|
||||
class DNSZoneTransfer(ServiceScan):
|
||||
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
self.name = 'DNS Zone Transfer'
|
||||
self.tags = ['default', 'safe', 'dns']
|
||||
|
||||
def configure(self):
|
||||
self.match_service_name('^domain')
|
||||
|
||||
async def run(self, service):
|
||||
if self.get_global('domain'):
|
||||
await service.execute('dig AXFR -p {port} @{address} ' + self.get_global('domain'), outfile='{protocol}_{port}_dns_zone-transfer-domain.txt')
|
||||
if service.target.type == 'hostname':
|
||||
await service.execute('dig AXFR -p {port} @{address} {address}', outfile='{protocol}_{port}_dns_zone-transfer-hostname.txt')
|
||||
await service.execute('dig AXFR -p {port} @{address}', outfile='{protocol}_{port}_dns_zone-transfer.txt')
|
||||
|
||||
class DNSReverseLookup(ServiceScan):
|
||||
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
self.name = 'DNS Reverse Lookup'
|
||||
self.tags = ['default', 'safe', 'dns']
|
||||
|
||||
def configure(self):
|
||||
self.match_service_name('^domain')
|
||||
|
||||
async def run(self, service):
|
||||
await service.execute('dig -p {port} -x {address} @{address}', outfile='{protocol}_{port}_dns_reverse-lookup.txt')
|
||||
|
||||
class NmapMulticastDNS(ServiceScan):
|
||||
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
self.name = 'Nmap Multicast DNS'
|
||||
self.tags = ['default', 'safe', 'dns']
|
||||
|
||||
def configure(self):
|
||||
self.match_service_name(['^mdns', '^zeroconf'])
|
||||
|
||||
async def run(self, service):
|
||||
await service.execute('nmap {nmap_extra} -sV -p {port} --script="banner,(dns* or ssl*) and not (brute or broadcast or dos or external or fuzzer)" -oN "{scandir}/{protocol}_{port}_multicastdns_nmap.txt" -oX "{scandir}/xml/{protocol}_{port}_multicastdns_nmap.xml" {address}')
|
|
@ -0,0 +1,30 @@
|
|||
from autorecon.plugins import ServiceScan
|
||||
|
||||
class NmapFTP(ServiceScan):
|
||||
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
self.name = 'Nmap FTP'
|
||||
self.tags = ['default', 'safe', 'ftp']
|
||||
|
||||
def configure(self):
|
||||
self.match_service_name(['^ftp', '^ftp\-data'])
|
||||
|
||||
async def run(self, service):
|
||||
await service.execute('nmap {nmap_extra} -sV -p {port} --script="banner,(ftp* or ssl*) and not (brute or broadcast or dos or external or fuzzer)" -oN "{scandir}/{protocol}_{port}_ftp_nmap.txt" -oX "{scandir}/xml/{protocol}_{port}_ftp_nmap.xml" {address}')
|
||||
|
||||
class BruteforceFTP(ServiceScan):
|
||||
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
self.name = "Bruteforce FTP"
|
||||
self.tags = ['default', 'ftp']
|
||||
|
||||
def configure(self):
|
||||
self.match_service_name(['^ftp', '^ftp\-data'])
|
||||
|
||||
def manual(self, service, plugin_was_run):
|
||||
service.add_manual_commands('Bruteforce logins:', [
|
||||
'hydra -L "' + self.get_global('username_wordlist', default='/usr/share/seclists/Usernames/top-usernames-shortlist.txt') + '" -P "' + self.get_global('password_wordlist', default='/usr/share/seclists/Passwords/darkweb2017-top100.txt') + '" -e nsr -s {port} -o "{scandir}/{protocol}_{port}_ftp_hydra.txt" ftp://{addressv6}',
|
||||
'medusa -U "' + self.get_global('username_wordlist', default='/usr/share/seclists/Usernames/top-usernames-shortlist.txt') + '" -P "' + self.get_global('password_wordlist', default='/usr/share/seclists/Passwords/darkweb2017-top100.txt') + '" -e ns -n {port} -O "{scandir}/{protocol}_{port}_ftp_medusa.txt" -M ftp -h {addressv6}'
|
||||
])
|
|
@ -0,0 +1,48 @@
|
|||
from autorecon.plugins import PortScan
|
||||
from autorecon.targets import Service
|
||||
import re
|
||||
|
||||
class GuesPortScan(PortScan):
|
||||
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
self.name = 'Guess TCP Ports'
|
||||
self.type = 'tcp'
|
||||
self.description = 'Performs an Nmap scan of the all TCP ports but guesses services based off the port found. Can be quicker. Proper service matching is performed at the end of the scan.'
|
||||
self.tags = ['guess-port-scan', 'long']
|
||||
self.priority = 0
|
||||
|
||||
async def run(self, target):
|
||||
if target.ports:
|
||||
if target.ports['tcp']:
|
||||
process, stdout, stderr = await target.execute('nmap {nmap_extra} -A --osscan-guess --version-all -p ' + target.ports['tcp'] + ' -oN "{scandir}/_custom_ports_tcp_nmap.txt" -oX "{scandir}/xml/_custom_ports_tcp_nmap.xml" {address}', blocking=False)
|
||||
else:
|
||||
return []
|
||||
else:
|
||||
process, stdout, stderr = await target.execute('nmap {nmap_extra} -A --osscan-guess --version-all -p- -oN "{scandir}/_quick_tcp_nmap.txt" -oX "{scandir}/xml/_quick_tcp_nmap.xml" {address}', blocking=False)
|
||||
|
||||
insecure_ports = {
|
||||
'20':'ftp', '21':'ftp', '22':'ssh', '23':'telnet', '25':'smtp', '53':'domain', '69':'tftp', '79':'finger', '80':'http', '88':'kerberos', '109':'pop3', '110':'pop3', '111':'rpcbind', '119':'nntp', '135':'msrpc', '139':'netbios-ssn', '143':'imap', '161':'snmp', '220':'imap', '389':'ldap', '433':'nntp', '445':'smb', '587':'smtp', '631':'ipp', '873':'rsync', '1098':'java-rmi', '1099':'java-rmi', '1433':'mssql', '1521':'oracle', '2049':'nfs', '2483':'oracle', '3020':'smb', '3306':'mysql', '3389':'rdp', '3632':'distccd', '5060':'asterisk', '5500':'vnc', '5900':'vnc', '5985':'wsman', '6379':'redis', '8080':'http-proxy', '27017':'mongod', '27018':'mongod', '27019':'mongod'
|
||||
}
|
||||
secure_ports = {
|
||||
'443':'https', '465':'smtp', '563':'nntp', '585':'imaps', '593':'msrpc', '636':'ldap', '989':'ftp', '990':'ftp', '992':'telnet', '993':'imaps', '995':'pop3s', '2484':'oracle', '5061':'asterisk', '5986':'wsman'
|
||||
}
|
||||
|
||||
services = []
|
||||
while True:
|
||||
line = await stdout.readline()
|
||||
if line is not None:
|
||||
match = re.match('^Discovered open port ([0-9]+)/tcp', line)
|
||||
if match:
|
||||
if match.group(1) in insecure_ports.keys():
|
||||
await target.add_service(Service('tcp', match.group(1), insecure_ports[match.group(1)]))
|
||||
elif match.group(1) in secure_ports.keys():
|
||||
await target.add_service(Service('tcp', match.group(1), secure_ports[match.group(1)], True))
|
||||
service = target.extract_service(line)
|
||||
if service is not None:
|
||||
services.append(service)
|
||||
else:
|
||||
break
|
||||
|
||||
await process.wait()
|
||||
return services
|
|
@ -0,0 +1,214 @@
|
|||
from autorecon.plugins import ServiceScan
|
||||
from autorecon.io import error, info, fformat
|
||||
from shutil import which
|
||||
import os
|
||||
|
||||
class NmapHTTP(ServiceScan):
|
||||
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
self.name = "Nmap HTTP"
|
||||
self.tags = ['default', 'safe', 'http']
|
||||
|
||||
def configure(self):
|
||||
self.match_service_name('^http')
|
||||
self.match_service_name('^nacn_http$', negative_match=True)
|
||||
self.add_pattern('Server: ([^\n]+)', description='Identified HTTP Server: {match}')
|
||||
self.add_pattern('WebDAV is ENABLED', description='WebDAV is enabled')
|
||||
|
||||
async def run(self, service):
|
||||
await service.execute('nmap {nmap_extra} -sV -p {port} --script="banner,(http* or ssl*) and not (brute or broadcast or dos or external or http-slowloris* or fuzzer)" -oN "{scandir}/{protocol}_{port}_{http_scheme}_nmap.txt" -oX "{scandir}/xml/{protocol}_{port}_{http_scheme}_nmap.xml" {address}')
|
||||
|
||||
class BruteforceHTTP(ServiceScan):
|
||||
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
self.name = "Bruteforce HTTP"
|
||||
self.tags = ['default', 'http']
|
||||
|
||||
def configure(self):
|
||||
self.match_service_name('^http')
|
||||
self.match_service_name('^nacn_http$', negative_match=True)
|
||||
|
||||
def manual(self, service, plugin_was_run):
|
||||
service.add_manual_commands('Credential bruteforcing commands (don\'t run these without modifying them):', [
|
||||
'hydra -L "' + self.get_global('username_wordlist', default='/usr/share/seclists/Usernames/top-usernames-shortlist.txt') + '" -P "' + self.get_global('password_wordlist', default='/usr/share/seclists/Passwords/darkweb2017-top100.txt') + '" -e nsr -s {port} -o "{scandir}/{protocol}_{port}_{http_scheme}_auth_hydra.txt" {http_scheme}-get://{addressv6}/path/to/auth/area',
|
||||
'medusa -U "' + self.get_global('username_wordlist', default='/usr/share/seclists/Usernames/top-usernames-shortlist.txt') + '" -P "' + self.get_global('password_wordlist', default='/usr/share/seclists/Passwords/darkweb2017-top100.txt') + '" -e ns -n {port} -O "{scandir}/{protocol}_{port}_{http_scheme}_auth_medusa.txt" -M http -h {addressv6} -m DIR:/path/to/auth/area',
|
||||
'hydra -L "' + self.get_global('username_wordlist', default='/usr/share/seclists/Usernames/top-usernames-shortlist.txt') + '" -P "' + self.get_global('password_wordlist', default='/usr/share/seclists/Passwords/darkweb2017-top100.txt') + '" -e nsr -s {port} -o "{scandir}/{protocol}_{port}_{http_scheme}_form_hydra.txt" {http_scheme}-post-form://{addressv6}/path/to/login.php:username=^USER^&password=^PASS^:invalid-login-message',
|
||||
'medusa -U "' + self.get_global('username_wordlist', default='/usr/share/seclists/Usernames/top-usernames-shortlist.txt') + '" -P "' + self.get_global('password_wordlist', default='/usr/share/seclists/Passwords/darkweb2017-top100.txt') + '" -e ns -n {port} -O "{scandir}/{protocol}_{port}_{http_scheme}_form_medusa.txt" -M web-form -h {addressv6} -m FORM:/path/to/login.php -m FORM-DATA:"post?username=&password=" -m DENY-SIGNAL:"invalid login message"'
|
||||
])
|
||||
|
||||
class Curl(ServiceScan):
|
||||
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
self.name = "Curl"
|
||||
self.tags = ['default', 'safe', 'http']
|
||||
|
||||
def configure(self):
|
||||
self.add_option("path", default="/", help="The path on the web server to curl. Default: %(default)s")
|
||||
self.match_service_name('^http')
|
||||
self.match_service_name('^nacn_http$', negative_match=True)
|
||||
self.add_pattern('(?i)powered[ -]by[^\n]+')
|
||||
|
||||
async def run(self, service):
|
||||
if service.protocol == 'tcp':
|
||||
await service.execute('curl -sSik {http_scheme}://{addressv6}:{port}' + self.get_option('path'), outfile='{protocol}_{port}_{http_scheme}_curl.html')
|
||||
|
||||
class CurlRobots(ServiceScan):
|
||||
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
self.name = "Curl Robots"
|
||||
self.tags = ['default', 'safe', 'http']
|
||||
|
||||
def configure(self):
|
||||
self.match_service_name('^http')
|
||||
self.match_service_name('^nacn_http$', negative_match=True)
|
||||
|
||||
async def run(self, service):
|
||||
if service.protocol == 'tcp':
|
||||
_, stdout, _ = await service.execute('curl -sSikf {http_scheme}://{addressv6}:{port}/robots.txt', future_outfile='{protocol}_{port}_{http_scheme}_curl-robots.txt')
|
||||
lines = await stdout.readlines()
|
||||
|
||||
if lines:
|
||||
filename = fformat('{scandir}/{protocol}_{port}_{http_scheme}_curl-robots.txt')
|
||||
with open(filename, mode='wt', encoding='utf8') as robots:
|
||||
robots.write('\n'.join(lines))
|
||||
else:
|
||||
info('{bblue}[' + fformat('{tag}') + ']{rst} There did not appear to be a robots.txt file in the webroot (/).')
|
||||
|
||||
class DirBuster(ServiceScan):
|
||||
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
self.name = "Directory Buster"
|
||||
self.slug = 'dirbuster'
|
||||
self.priority = 0
|
||||
self.tags = ['default', 'safe', 'long', 'http']
|
||||
|
||||
def configure(self):
|
||||
self.add_choice_option('tool', default='feroxbuster', choices=['feroxbuster', 'gobuster', 'dirsearch', 'ffuf', 'dirb'], help='The tool to use for directory busting. Default: %(default)s')
|
||||
self.add_list_option('wordlist', default=['/usr/share/seclists/Discovery/Web-Content/common.txt', '/usr/share/seclists/Discovery/Web-Content/big.txt', '/usr/share/seclists/Discovery/Web-Content/raft-large-words.txt'], help='The wordlist(s) to use when directory busting. Separate multiple wordlists with spaces. Default: %(default)s')
|
||||
self.add_option('threads', default=10, help='The number of threads to use when directory busting. Default: %(default)s')
|
||||
self.add_option('ext', default='txt,html,php,asp,aspx,jsp', help='The extensions you wish to fuzz (no dot, comma separated). Default: %(default)s')
|
||||
self.match_service_name('^http')
|
||||
self.match_service_name('^nacn_http$', negative_match=True)
|
||||
|
||||
def check(self):
|
||||
tool = self.get_option('tool')
|
||||
if tool == 'feroxbuster':
|
||||
if which('feroxbuster') is None:
|
||||
error('The feroxbuster program could not be found. Make sure it is installed. (On Kali, run: sudo apt install feroxbuster)')
|
||||
elif tool == 'gobuster':
|
||||
if which('gobuster') is None:
|
||||
error('The gobuster program could not be found. Make sure it is installed. (On Kali, run: sudo apt install gobuster)')
|
||||
elif tool == 'dirsearch':
|
||||
if which('dirsearch') is None:
|
||||
error('The dirsearch program could not be found. Make sure it is installed. (On Kali, run: sudo apt install dirsearch)')
|
||||
|
||||
async def run(self, service):
|
||||
dot_extensions = ','.join(['.' + x for x in self.get_option('ext').split(',')])
|
||||
for wordlist in self.get_option('wordlist'):
|
||||
name = os.path.splitext(os.path.basename(wordlist))[0]
|
||||
if self.get_option('tool') == 'feroxbuster':
|
||||
await service.execute('feroxbuster -u {http_scheme}://{addressv6}:{port}/ -t ' + str(self.get_option('threads')) + ' -w ' + wordlist + ' -x "' + self.get_option('ext') + '" -v -k -n -q -o "{scandir}/{protocol}_{port}_{http_scheme}_feroxbuster_' + name + '.txt"')
|
||||
elif self.get_option('tool') == 'gobuster':
|
||||
await service.execute('gobuster dir -u {http_scheme}://{addressv6}:{port}/ -t ' + str(self.get_option('threads')) + ' -w ' + wordlist + ' -e -k -x "' + self.get_option('ext') + '" -z -o "{scandir}/{protocol}_{port}_{http_scheme}_gobuster_' + name + '.txt"')
|
||||
elif self.get_option('tool') == 'dirsearch':
|
||||
if service.target.ipversion == 'IPv6':
|
||||
error('dirsearch does not support IPv6.')
|
||||
else:
|
||||
await service.execute('dirsearch -u {http_scheme}://{address}:{port}/ -t ' + str(self.get_option('threads')) + ' -e "' + self.get_option('ext') + '" -f -q -w ' + wordlist + ' --format=plain -o "{scandir}/{protocol}_{port}_{http_scheme}_dirsearch_' + name + '.txt"')
|
||||
elif self.get_option('tool') == 'ffuf':
|
||||
await service.execute('ffuf -u {http_scheme}://{addressv6}:{port}/FUZZ -t ' + str(self.get_option('threads')) + ' -w ' + wordlist + ' -e "' + dot_extensions + '" -v -noninteractive | tee {scandir}/{protocol}_{port}_{http_scheme}_ffuf_' + name + '.txt')
|
||||
elif self.get_option('tool') == 'dirb':
|
||||
await service.execute('dirb {http_scheme}://{addressv6}:{port}/ ' + wordlist + ' -l -r -S -X ",' + dot_extensions + '" -o "{scandir}/{protocol}_{port}_{http_scheme}_dirb_' + name + '.txt"')
|
||||
|
||||
def manual(self, service, plugin_was_run):
|
||||
dot_extensions = ','.join(['.' + x for x in self.get_option('ext').split(',')])
|
||||
if self.get_option('tool') == 'feroxbuster':
|
||||
service.add_manual_command('(feroxbuster) Multi-threaded recursive directory/file enumeration for web servers using various wordlists:', [
|
||||
'feroxbuster -u {http_scheme}://{addressv6}:{port} -t ' + str(self.get_option('threads')) + ' -w /usr/share/wordlists/dirbuster/directory-list-2.3-medium.txt -x "' + self.get_option('ext') + '" -v -k -n -o {scandir}/{protocol}_{port}_{http_scheme}_feroxbuster_dirbuster.txt'
|
||||
])
|
||||
elif self.get_option('tool') == 'gobuster':
|
||||
service.add_manual_command('(gobuster v3) Multi-threaded directory/file enumeration for web servers using various wordlists:', [
|
||||
'gobuster dir -u {http_scheme}://{addressv6}:{port}/ -t ' + str(self.get_option('threads')) + ' -w /usr/share/wordlists/dirbuster/directory-list-2.3-medium.txt -e -k -x "' + self.get_option('ext') + '" -z -o "{scandir}/{protocol}_{port}_{http_scheme}_gobuster_dirbuster.txt"'
|
||||
])
|
||||
elif self.get_option('tool') == 'dirsearch':
|
||||
if service.target.ipversion == 'IPv4':
|
||||
service.add_manual_command('(dirsearch) Multi-threaded recursive directory/file enumeration for web servers using various wordlists:', [
|
||||
'dirsearch -u {http_scheme}://{address}:{port}/ -t ' + str(self.get_option('threads')) + ' -r -e "' + self.get_option('ext') + '" -f -w /usr/share/wordlists/dirbuster/directory-list-2.3-medium.txt --format=plain --output="{scandir}/{protocol}_{port}_{http_scheme}_dirsearch_dirbuster.txt"'
|
||||
])
|
||||
elif self.get_option('tool') == 'ffuf':
|
||||
service.add_manual_command('(ffuf) Multi-threaded recursive directory/file enumeration for web servers using various wordlists:', [
|
||||
'ffuf -u {http_scheme}://{addressv6}:{port}/FUZZ -t ' + str(self.get_option('threads')) + ' -w /usr/share/seclists/Discovery/Web-Content/directory-list-2.3-medium.txt -e "' + dot_extensions + '" -v -noninteractive | tee {scandir}/{protocol}_{port}_{http_scheme}_ffuf_dirbuster.txt'
|
||||
])
|
||||
elif self.get_option('tool') == 'dirb':
|
||||
service.add_manual_command('(dirb) Recursive directory/file enumeration for web servers using various wordlists:', [
|
||||
'dirb {http_scheme}://{addressv6}:{port}/ /usr/share/wordlists/dirbuster/directory-list-2.3-medium.txt -l -r -S -X ",' + dot_extensions + '" -o "{scandir}/{protocol}_{port}_{http_scheme}_dirb_dirbuster.txt"'
|
||||
])
|
||||
|
||||
class Nikto(ServiceScan):
|
||||
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
self.name = 'nikto'
|
||||
self.tags = ['default', 'safe', 'long', 'http']
|
||||
|
||||
def configure(self):
|
||||
self.match_service_name('^http')
|
||||
self.match_service_name('^nacn_http$', negative_match=True)
|
||||
|
||||
def manual(self, service, plugin_was_run):
|
||||
if service.target.ipversion == 'IPv4':
|
||||
service.add_manual_command('(nikto) old but generally reliable web server enumeration tool:', 'nikto -ask=no -h {http_scheme}://{address}:{port} 2>&1 | tee "{scandir}/{protocol}_{port}_{http_scheme}_nikto.txt"')
|
||||
|
||||
class WhatWeb(ServiceScan):
|
||||
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
self.name = "whatweb"
|
||||
self.tags = ['default', 'safe', 'http']
|
||||
|
||||
def configure(self):
|
||||
self.match_service_name('^http')
|
||||
self.match_service_name('^nacn_http$', negative_match=True)
|
||||
|
||||
async def run(self, service):
|
||||
if service.protocol == 'tcp' and service.target.ipversion == 'IPv4':
|
||||
await service.execute('whatweb --color=never --no-errors -a 3 -v {http_scheme}://{address}:{port} 2>&1', outfile='{protocol}_{port}_{http_scheme}_whatweb.txt')
|
||||
|
||||
class WkHTMLToImage(ServiceScan):
|
||||
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
self.name = "wkhtmltoimage"
|
||||
self.tags = ['default', 'safe', 'http']
|
||||
|
||||
def configure(self):
|
||||
self.match_service_name('^http')
|
||||
self.match_service_name('^nacn_http$', negative_match=True)
|
||||
|
||||
def check(self):
|
||||
if which('wkhtmltoimage') is None:
|
||||
error('The wkhtmltoimage program could not be found. Make sure it is installed. (On Kali, run: sudo apt install wkhtmltopdf)')
|
||||
|
||||
async def run(self, service):
|
||||
if which('wkhtmltoimage') is not None:
|
||||
if service.protocol == 'tcp':
|
||||
await service.execute('wkhtmltoimage --format png {http_scheme}://{addressv6}:{port}/ {scandir}/{protocol}_{port}_{http_scheme}_screenshot.png')
|
||||
|
||||
class WPScan(ServiceScan):
|
||||
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
self.name = 'WPScan'
|
||||
self.tags = ['default', 'safe', 'http']
|
||||
|
||||
def configure(self):
|
||||
self.match_service_name('^http')
|
||||
self.match_service_name('^nacn_http$', negative_match=True)
|
||||
|
||||
def manual(self, service, plugin_was_run):
|
||||
service.add_manual_command('(wpscan) WordPress Security Scanner (useful if WordPress is found):', 'wpscan --url {http_scheme}://{addressv6}:{port}/ --no-update -e vp,vt,tt,cb,dbe,u,m --plugins-detection aggressive --plugins-version-detection aggressive -f cli-no-color 2>&1 | tee "{scandir}/{protocol}_{port}_{http_scheme}_wpscan.txt"')
|
|
@ -0,0 +1,17 @@
|
|||
from autorecon.plugins import ServiceScan
|
||||
|
||||
class NmapKerberos(ServiceScan):
|
||||
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
self.name = "Nmap Kerberos"
|
||||
self.tags = ['default', 'safe', 'kerberos', 'active-directory']
|
||||
|
||||
def configure(self):
|
||||
self.match_service_name(['^kerberos', '^kpasswd'])
|
||||
|
||||
async def run(self, service):
|
||||
if self.get_global('domain') and self.get_global('username-wordlist'):
|
||||
await service.execute('nmap {nmap_extra} -sV -p {port} --script="banner,krb5-enum-users" --script-args krb5-enum-users.realm="' + self.get_global('domain') + '",userdb="' + self.get_global('username-wordlist') + '" -oN "{scandir}/{protocol}_{port}_kerberos_nmap.txt" -oX "{scandir}/xml/{protocol}_{port}_kerberos_nmap.xml" {address}')
|
||||
else:
|
||||
await service.execute('nmap {nmap_extra} -sV -p {port} --script="banner,krb5-enum-users" -oN "{scandir}/{protocol}_{port}_kerberos_nmap.txt" -oX "{scandir}/xml/{protocol}_{port}_kerberos_nmap.xml" {address}')
|
|
@ -0,0 +1,29 @@
|
|||
from autorecon.plugins import ServiceScan
|
||||
|
||||
class NmapLDAP(ServiceScan):
|
||||
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
self.name = "Nmap LDAP"
|
||||
self.tags = ['default', 'safe', 'ldap', 'active-directory']
|
||||
|
||||
def configure(self):
|
||||
self.match_service_name('^ldap')
|
||||
|
||||
async def run(self, service):
|
||||
await service.execute('nmap {nmap_extra} -sV -p {port} --script="banner,(ldap* or ssl*) and not (brute or broadcast or dos or external or fuzzer)" -oN "{scandir}/{protocol}_{port}_ldap_nmap.txt" -oX "{scandir}/xml/{protocol}_{port}_ldap_nmap.xml" {address}')
|
||||
|
||||
class LDAPSearch(ServiceScan):
|
||||
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
self.name = 'LDAP Search'
|
||||
self.tags = ['default', 'safe', 'ldap', 'active-directory']
|
||||
|
||||
def configure(self):
|
||||
self.match_service_name('^ldap')
|
||||
|
||||
def manual(self, service, plugin_was_run):
|
||||
service.add_manual_command('ldapsearch command (modify before running):', [
|
||||
'ldapsearch -x -D "<username>" -w "<password>"" -p {port} -h {address} -b "dc=example,dc=com" -s sub "(objectclass=*) 2>&1 | tee > "{scandir}/{protocol}_{port}_ldap_all-entries.txt"'
|
||||
])
|
|
@ -0,0 +1,188 @@
|
|||
from autorecon.plugins import ServiceScan
|
||||
from autorecon.io import fformat
|
||||
|
||||
class NmapCassandra(ServiceScan):
|
||||
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
self.name = "Nmap Cassandra"
|
||||
self.tags = ['default', 'safe', 'cassandra']
|
||||
|
||||
def configure(self):
|
||||
self.match_service_name('^apani1')
|
||||
|
||||
async def run(self, service):
|
||||
await service.execute('nmap {nmap_extra} -sV -p {port} --script="banner,(cassandra* or ssl*) and not (brute or broadcast or dos or external or fuzzer)" -oN "{scandir}/{protocol}_{port}_cassandra_nmap.txt" -oX "{scandir}/xml/{protocol}_{port}_cassandra_nmap.xml" {address}')
|
||||
|
||||
class NmapCUPS(ServiceScan):
|
||||
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
self.name = "Nmap CUPS"
|
||||
self.tags = ['default', 'safe', 'cups']
|
||||
|
||||
def configure(self):
|
||||
self.match_service_name('^ipp')
|
||||
|
||||
async def run(self, service):
|
||||
await service.execute('nmap {nmap_extra} -sV -p {port} --script="banner,(cups* or ssl*) and not (brute or broadcast or dos or external or fuzzer)" -oN "{scandir}/{protocol}_{port}_cups_nmap.txt" -oX "{scandir}/xml/{protocol}_{port}_cups_nmap.xml" {address}')
|
||||
|
||||
class NmapDistccd(ServiceScan):
|
||||
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
self.name = "Nmap distccd"
|
||||
self.tags = ['default', 'safe', 'distccd']
|
||||
|
||||
def configure(self):
|
||||
self.match_service_name('^distccd')
|
||||
|
||||
async def run(self, service):
|
||||
await service.execute('nmap {nmap_extra} -sV -p {port} --script="banner,distcc-cve2004-2687" --script-args="distcc-cve2004-2687.cmd=id" -oN "{scandir}/{protocol}_{port}_distcc_nmap.txt" -oX "{scandir}/xml/{protocol}_{port}_distcc_nmap.xml" {address}')
|
||||
|
||||
class NmapFinger(ServiceScan):
|
||||
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
self.name = "Nmap finger"
|
||||
self.tags = ['default', 'safe', 'finger']
|
||||
|
||||
def configure(self):
|
||||
self.match_service_name('^finger')
|
||||
|
||||
async def run(self, service):
|
||||
await service.execute('nmap {nmap_extra} -sV -p {port} --script="banner,finger" -oN "{scandir}/{protocol}_{port}_finger_nmap.txt" -oX "{scandir}/xml/{protocol}_{port}_finger_nmap.xml" {address}')
|
||||
|
||||
class NmapIMAP(ServiceScan):
|
||||
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
self.name = "Nmap IMAP"
|
||||
self.tags = ['default', 'safe', 'imap', 'email']
|
||||
|
||||
def configure(self):
|
||||
self.match_service_name('^imap')
|
||||
|
||||
async def run(self, service):
|
||||
await service.execute('nmap {nmap_extra} -sV -p {port} --script="banner,(imap* or ssl*) and not (brute or broadcast or dos or external or fuzzer)" -oN "{scandir}/{protocol}_{port}_imap_nmap.txt" -oX "{scandir}/xml/{protocol}_{port}_imap_nmap.xml" {address}')
|
||||
|
||||
class NmapIrc(ServiceScan):
|
||||
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
self.name = 'Nmap IRC'
|
||||
self.tags = ['default', 'safe', 'irc']
|
||||
|
||||
def configure(self):
|
||||
self.match_service_name('^irc')
|
||||
|
||||
async def run(self, service):
|
||||
await service.execute('nmap {nmap_extra} -sV --script irc-botnet-channels,irc-info,irc-unrealircd-backdoor -oN "{scandir}/{protocol}_{port}_irc_nmap.txt" -oX "{scandir}/xml/{protocol}_{port}_irc_nmap.xml" -p {port} {address}')
|
||||
|
||||
class NmapNNTP(ServiceScan):
|
||||
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
self.name = "Nmap NNTP"
|
||||
self.tags = ['default', 'safe', 'nntp']
|
||||
|
||||
def configure(self):
|
||||
self.match_service_name('^nntp')
|
||||
|
||||
async def run(self, service):
|
||||
await service.execute('nmap {nmap_extra} -sV -p {port} --script="banner,nntp-ntlm-info" -oN "{scandir}/{protocol}_{port}_nntp_nmap.txt" -oX "{scandir}/xml/{protocol}_{port}_nntp_nmap.xml" {address}')
|
||||
|
||||
class NmapPOP3(ServiceScan):
|
||||
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
self.name = "Nmap POP3"
|
||||
self.tags = ['default', 'safe', 'pop3', 'email']
|
||||
|
||||
def configure(self):
|
||||
self.match_service_name('^pop3')
|
||||
|
||||
async def run(self, service):
|
||||
await service.execute('nmap {nmap_extra} -sV -p {port} --script="banner,(pop3* or ssl*) and not (brute or broadcast or dos or external or fuzzer)" -oN "{scandir}/{protocol}_{port}_pop3_nmap.txt" -oX "{scandir}/xml/{protocol}_{port}_pop3_nmap.xml" {address}')
|
||||
|
||||
class NmapRMI(ServiceScan):
|
||||
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
self.name = "Nmap RMI"
|
||||
self.tags = ['default', 'safe', 'rmi']
|
||||
|
||||
def configure(self):
|
||||
self.match_service_name(['^java\-rmi', '^rmiregistry'])
|
||||
|
||||
async def run(self, service):
|
||||
await service.execute('nmap {nmap_extra} -sV -p {port} --script="banner,rmi-vuln-classloader,rmi-dumpregistry" -oN "{scandir}/{protocol}_{port}_rmi_nmap.txt" -oX "{scandir}/xml/{protocol}_{port}_rmi_nmap.xml" {address}')
|
||||
|
||||
class NmapTelnet(ServiceScan):
|
||||
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
self.name = 'Nmap Telnet'
|
||||
self.tags = ['default', 'safe', 'telnet']
|
||||
|
||||
def configure(self):
|
||||
self.match_service_name('^telnet')
|
||||
|
||||
async def run(self, service):
|
||||
await service.execute('nmap {nmap_extra} -sV -p {port} --script="banner,telnet-encryption,telnet-ntlm-info" -oN "{scandir}/{protocol}_{port}_telnet-nmap.txt" -oX "{scandir}/xml/{protocol}_{port}_telnet_nmap.xml" {address}')
|
||||
|
||||
class NmapTFTP(ServiceScan):
|
||||
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
self.name = 'Nmap TFTP'
|
||||
self.tags = ['default', 'safe', 'tftp']
|
||||
|
||||
def configure(self):
|
||||
self.match_service_name('^tftp')
|
||||
|
||||
async def run(self, service):
|
||||
await service.execute('nmap {nmap_extra} -sV -p {port} --script="banner,tftp-enum" -oN "{scandir}/{protocol}_{port}_tftp-nmap.txt" -oX "{scandir}/xml/{protocol}_{port}_tftp_nmap.xml" {address}')
|
||||
|
||||
class NmapVNC(ServiceScan):
|
||||
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
self.name = 'Nmap VNC'
|
||||
self.tags = ['default', 'safe', 'vnc']
|
||||
|
||||
def configure(self):
|
||||
self.match_service_name('^vnc')
|
||||
|
||||
async def run(self, service):
|
||||
await service.execute('nmap {nmap_extra} -sV -p {port} --script="banner,(vnc* or realvnc* or ssl*) and not (brute or broadcast or dos or external or fuzzer)" --script-args="unsafe=1" -oN "{scandir}/{protocol}_{port}_vnc_nmap.txt" -oX "{scandir}/xml/{protocol}_{port}_vnc_nmap.xml" {address}')
|
||||
|
||||
class WinRMDetection(ServiceScan):
|
||||
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
self.name = 'WinRM Detection'
|
||||
self.tags = ['default', 'safe', 'winrm']
|
||||
|
||||
def configure(self):
|
||||
self.match_service_name('^wsman')
|
||||
self.match_service('tcp', [5985, 5986], '^http')
|
||||
|
||||
async def run(self, service):
|
||||
filename = fformat('{scandir}/{protocol}_{port}_winrm-detection.txt')
|
||||
with open(filename, mode='wt', encoding='utf8') as winrm:
|
||||
winrm.write('WinRM was possibly detected running on ' + service.protocol + ' port ' + str(service.port) + '.\nCheck _manual_commands.txt for manual commands you can run against this service.')
|
||||
|
||||
def manual(self, service, plugin_was_run):
|
||||
service.add_manual_commands('Bruteforce logins:', [
|
||||
'crackmapexec winrm {address} -d ' + self.get_global('domain', default='<domain>') + ' -u ' + self.get_global('username_wordlist', default='/usr/share/seclists/Usernames/top-usernames-shortlist.txt') + ' -p ' + self.get_global('password_wordlist', default='/usr/share/seclists/Passwords/darkweb2017-top100.txt')
|
||||
])
|
||||
|
||||
service.add_manual_commands('Check login (requires credentials):', [
|
||||
'crackmapexec winrm {address} -d ' + self.get_global('domain', default='<domain>') + ' -u <username> -p <password> -x "whoami"'
|
||||
])
|
||||
|
||||
service.add_manual_commands('Evil WinRM (gem install evil-winrm):', [
|
||||
'evil-winrm -u <user> -p <password> -i {address}',
|
||||
'evil-winrm -u <user> -H <hash> -i {address}'
|
||||
])
|
|
@ -0,0 +1,40 @@
|
|||
from autorecon.plugins import ServiceScan
|
||||
|
||||
class NmapNFS(ServiceScan):
|
||||
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
self.name = "Nmap NFS"
|
||||
self.tags = ['default', 'safe', 'nfs']
|
||||
|
||||
def configure(self):
|
||||
self.match_service_name(['^nfs', '^rpcbind'])
|
||||
|
||||
async def run(self, service):
|
||||
await service.execute('nmap {nmap_extra} -sV -p {port} --script="banner,(rpcinfo or nfs*) and not (brute or broadcast or dos or external or fuzzer)" -oN "{scandir}/{protocol}_{port}_nfs_nmap.txt" -oX "{scandir}/xml/{protocol}_{port}_nfs_nmap.xml" {address}')
|
||||
|
||||
class Showmount(ServiceScan):
|
||||
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
self.name = "showmount"
|
||||
self.tags = ['default', 'safe', 'nfs']
|
||||
|
||||
def configure(self):
|
||||
self.match_service_name(['^nfs', '^rpcbind'])
|
||||
|
||||
async def run(self, service):
|
||||
await service.execute('showmount -e {address} 2>&1', outfile='{protocol}_{port}_showmount.txt')
|
||||
|
||||
class NmapMountd(ServiceScan):
|
||||
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
self.name = "Nmap Mountd"
|
||||
self.tags = ['default', 'safe', 'nfs']
|
||||
|
||||
def configure(self):
|
||||
self.match_service_name('^mountd')
|
||||
|
||||
async def run(self, service):
|
||||
await service.execute('nmap {nmap_extra} -sV -p {port} --script="banner,nfs* and not (brute or broadcast or dos or external or fuzzer)" -oN "{scandir}/{protocol}_{port}_mountd_nmap.txt" -oX "{scandir}/xml/{protocol}_{port}_mountd_nmap.xml" {address}')
|
|
@ -0,0 +1,30 @@
|
|||
from autorecon.plugins import ServiceScan
|
||||
|
||||
class NmapRDP(ServiceScan):
|
||||
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
self.name = "Nmap RDP"
|
||||
self.tags = ['default', 'safe', 'rdp']
|
||||
|
||||
def configure(self):
|
||||
self.match_service_name(['^rdp', '^ms\-wbt\-server', '^ms\-term\-serv'])
|
||||
|
||||
async def run(self, service):
|
||||
await service.execute('nmap {nmap_extra} -sV -p {port} --script="banner,(rdp* or ssl*) and not (brute or broadcast or dos or external or fuzzer)" -oN "{scandir}/{protocol}_{port}_rdp_nmap.txt" -oX "{scandir}/xml/{protocol}_{port}_rdp_nmap.xml" {address}')
|
||||
|
||||
class BruteforceRDP(ServiceScan):
|
||||
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
self.name = "Bruteforce RDP"
|
||||
self.tags = ['default', 'rdp']
|
||||
|
||||
def configure(self):
|
||||
self.match_service_name(['^rdp', '^ms\-wbt\-server', '^ms\-term\-serv'])
|
||||
|
||||
def manual(self, service, plugin_was_run):
|
||||
service.add_manual_commands('Bruteforce logins:', [
|
||||
'hydra -L "' + self.get_global('username_wordlist', default='/usr/share/seclists/Usernames/top-usernames-shortlist.txt') + '" -P "' + self.get_global('password_wordlist', default='/usr/share/seclists/Passwords/darkweb2017-top100.txt') + '" -e nsr -s {port} -o "{scandir}/{protocol}_{port}_rdp_hydra.txt" rdp://{addressv6}',
|
||||
'medusa -U "' + self.get_global('username_wordlist', default='/usr/share/seclists/Usernames/top-usernames-shortlist.txt') + '" -P "' + self.get_global('password_wordlist', default='/usr/share/seclists/Passwords/darkweb2017-top100.txt') + '" -e ns -n {port} -O "{scandir}/{protocol}_{port}_rdp_medusa.txt" -M rdp -h {addressv6}'
|
||||
])
|
|
@ -0,0 +1,37 @@
|
|||
from autorecon.plugins import ServiceScan
|
||||
from autorecon.io import error
|
||||
from shutil import which
|
||||
|
||||
class NmapRedis(ServiceScan):
|
||||
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
self.name = 'Nmap Redis'
|
||||
self.tags = ['default', 'safe', 'redis']
|
||||
|
||||
def configure(self):
|
||||
self.match_service_name('^redis$')
|
||||
|
||||
async def run(self, service):
|
||||
await service.execute('nmap {nmap_extra} -sV -p {port} --script="banner,redis-info" -oN "{scandir}/{protocol}_{port}_redis_nmap.txt" -oX "{scandir}/xml/{protocol}_{port}_redis_nmap.xml" {address}')
|
||||
|
||||
class RedisCli(ServiceScan):
|
||||
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
self.name = 'Redis Cli'
|
||||
self.tags = ['default', 'safe', 'redis']
|
||||
|
||||
def configure(self):
|
||||
self.match_service_name('^redis$')
|
||||
|
||||
def check(self):
|
||||
if which('redis-cli') is None:
|
||||
error('The redis-cli program could not be found. Make sure it is installed. (On Kali, run: sudo apt install redis-tools)')
|
||||
|
||||
async def run(self, service):
|
||||
if which('redis-cli') is not None:
|
||||
_, stdout, _ = await service.execute('redis-cli -p {port} -h {address} INFO', outfile='{protocol}_{port}_redis_info.txt')
|
||||
if not (await stdout.readline()).startswith('NOAUTH Authentication required'):
|
||||
await service.execute('redis-cli -p {port} -h {address} CONFIG GET \'*\'', outfile='{protocol}_{port}_redis_config.txt')
|
||||
await service.execute('redis-cli -p {port} -h {address} CLIENT LIST', outfile='{protocol}_{port}_redis_client-list.txt')
|
|
@ -0,0 +1,163 @@
|
|||
from autorecon.plugins import Report
|
||||
from autorecon.config import config
|
||||
from xml.sax.saxutils import escape
|
||||
import os, glob
|
||||
|
||||
class CherryTree(Report):
|
||||
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
self.name = 'CherryTree'
|
||||
self.tags = []
|
||||
|
||||
async def run(self, targets):
|
||||
if len(targets) > 1:
|
||||
report = os.path.join(config['outdir'], 'report.xml.ctd')
|
||||
elif len(targets) == 1:
|
||||
report = os.path.join(targets[0].reportdir, 'report.xml.ctd')
|
||||
else:
|
||||
return
|
||||
|
||||
with open(report, 'w') as output:
|
||||
output.writelines('<?xml version="1.0" encoding="UTF-8"?>\n<cherrytree>\n')
|
||||
for target in targets:
|
||||
output.writelines('<node name="' + escape(target.address) + '" is_bold="1" custom_icon_id="1">\n')
|
||||
|
||||
files = [os.path.abspath(filename) for filename in glob.iglob(os.path.join(target.scandir, '**/*'), recursive=True) if os.path.isfile(filename) and filename.endswith(('.txt', '.html'))]
|
||||
|
||||
if target.scans['ports']:
|
||||
output.writelines('<node name="Port Scans" custom_icon_id="2">\n')
|
||||
for scan in target.scans['ports'].keys():
|
||||
if len(target.scans['ports'][scan]['commands']) > 0:
|
||||
output.writelines('<node name="PortScan: ' + escape(target.scans['ports'][scan]['plugin'].name) + '" custom_icon_id="21">\n')
|
||||
for command in target.scans['ports'][scan]['commands']:
|
||||
output.writelines('<rich_text>' + escape(command[0]))
|
||||
for filename in files:
|
||||
if filename in command[0] or (command[1] is not None and filename == command[1]) or (command[2] is not None and filename == command[2]):
|
||||
output.writelines('\n\n' + escape(filename) + ':\n\n')
|
||||
with open(filename, 'r') as file:
|
||||
output.writelines(escape(file.read()) + '\n')
|
||||
output.writelines('</rich_text>\n')
|
||||
output.writelines('</node>\n')
|
||||
output.writelines('</node>\n')
|
||||
if target.scans['services']:
|
||||
output.writelines('<node name="Services" custom_icon_id="2">\n')
|
||||
for service in target.scans['services'].keys():
|
||||
output.writelines('<node name="Service: ' + escape(service.tag()) + '" custom_icon_id="3">\n')
|
||||
for plugin in target.scans['services'][service].keys():
|
||||
if len(target.scans['services'][service][plugin]['commands']) > 0:
|
||||
output.writelines('<node name="' + escape(target.scans['services'][service][plugin]['plugin'].name) + '" custom_icon_id="21">\n')
|
||||
for command in target.scans['services'][service][plugin]['commands']:
|
||||
output.writelines('<rich_text>' + escape(command[0]))
|
||||
for filename in files:
|
||||
if filename in command[0] or (command[1] is not None and filename == command[1]) or (command[2] is not None and filename == command[2]):
|
||||
output.writelines('\n\n' + escape(filename) + ':\n\n')
|
||||
with open(filename, 'r') as file:
|
||||
output.writelines(escape(file.read()) + '\n')
|
||||
output.writelines('</rich_text>\n')
|
||||
output.writelines('</node>\n')
|
||||
output.writelines('</node>\n')
|
||||
output.writelines('</node>\n')
|
||||
|
||||
manual_commands = os.path.join(target.scandir, '_manual_commands.txt')
|
||||
if os.path.isfile(manual_commands):
|
||||
output.writelines('<node name="Manual Commands" custom_icon_id="22">\n')
|
||||
with open(manual_commands, 'r') as file:
|
||||
output.writelines('<rich_text>' + escape(file.read()) + '</rich_text>\n')
|
||||
output.writelines('</node>\n')
|
||||
|
||||
patterns = os.path.join(target.scandir, '_patterns.log')
|
||||
if os.path.isfile(patterns):
|
||||
output.writelines('<node name="Patterns" custom_icon_id="10">\n')
|
||||
with open(patterns, 'r') as file:
|
||||
output.writelines('<rich_text>' + escape(file.read()) + '</rich_text>\n')
|
||||
output.writelines('</node>\n')
|
||||
|
||||
commands = os.path.join(target.scandir, '_commands.log')
|
||||
if os.path.isfile(commands):
|
||||
output.writelines('<node name="Commands" custom_icon_id="21">\n')
|
||||
with open(commands, 'r') as file:
|
||||
output.writelines('<rich_text>' + escape(file.read()) + '</rich_text>\n')
|
||||
output.writelines('</node>\n')
|
||||
|
||||
errors = os.path.join(target.scandir, '_errors.log')
|
||||
if os.path.isfile(errors):
|
||||
output.writelines('<node name="Errors" custom_icon_id="57">\n')
|
||||
with open(errors, 'r') as file:
|
||||
output.writelines('<rich_text>' + escape(file.read()) + '</rich_text>\n')
|
||||
output.writelines('</node>\n')
|
||||
output.writelines('</node>\n')
|
||||
|
||||
output.writelines('</cherrytree>')
|
||||
|
||||
class Markdown(Report):
|
||||
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
self.name = 'Markdown'
|
||||
|
||||
async def run(self, targets):
|
||||
if len(targets) > 1:
|
||||
report = os.path.join(config['outdir'], 'report.md')
|
||||
elif len(targets) == 1:
|
||||
report = os.path.join(targets[0].reportdir, 'report.md')
|
||||
else:
|
||||
return
|
||||
|
||||
os.makedirs(report, exist_ok=True)
|
||||
|
||||
for target in targets:
|
||||
os.makedirs(os.path.join(report, target.address), exist_ok=True)
|
||||
|
||||
files = [os.path.abspath(filename) for filename in glob.iglob(os.path.join(target.scandir, '**/*'), recursive=True) if os.path.isfile(filename) and filename.endswith(('.txt', '.html'))]
|
||||
|
||||
if target.scans['ports']:
|
||||
os.makedirs(os.path.join(report, target.address, 'Port Scans'), exist_ok=True)
|
||||
for scan in target.scans['ports'].keys():
|
||||
if len(target.scans['ports'][scan]['commands']) > 0:
|
||||
with open(os.path.join(report, target.address, 'Port Scans', 'PortScan - ' + target.scans['ports'][scan]['plugin'].name + '.md'), 'w') as output:
|
||||
for command in target.scans['ports'][scan]['commands']:
|
||||
output.writelines('```bash\n' + command[0] + '\n```')
|
||||
for filename in files:
|
||||
if filename in command[0] or (command[1] is not None and filename == command[1]) or (command[2] is not None and filename == command[2]):
|
||||
output.writelines('\n\n[' + filename + '](file://' + filename + '):\n\n')
|
||||
with open(filename, 'r') as file:
|
||||
output.writelines('```\n' + file.read() + '\n```\n')
|
||||
if target.scans['services']:
|
||||
os.makedirs(os.path.join(report, target.address, 'Services'), exist_ok=True)
|
||||
for service in target.scans['services'].keys():
|
||||
os.makedirs(os.path.join(report, target.address, 'Services', 'Service - ' + service.tag().replace('/', '-')), exist_ok=True)
|
||||
for plugin in target.scans['services'][service].keys():
|
||||
if len(target.scans['services'][service][plugin]['commands']) > 0:
|
||||
with open(os.path.join(report, target.address, 'Services', 'Service - ' + service.tag().replace('/', '-'), target.scans['services'][service][plugin]['plugin'].name + '.md'), 'w') as output:
|
||||
for command in target.scans['services'][service][plugin]['commands']:
|
||||
output.writelines('```bash\n' + command[0] + '\n```')
|
||||
for filename in files:
|
||||
if filename in command[0] or (command[1] is not None and filename == command[1]) or (command[2] is not None and filename == command[2]):
|
||||
output.writelines('\n\n[' + filename + '](file://' + filename + '):\n\n')
|
||||
with open(filename, 'r') as file:
|
||||
output.writelines('```\n' + file.read() + '\n```\n')
|
||||
|
||||
manual_commands = os.path.join(target.scandir, '_manual_commands.txt')
|
||||
if os.path.isfile(manual_commands):
|
||||
with open(os.path.join(report, target.address, 'Manual Commands' + '.md'), 'w') as output:
|
||||
with open(manual_commands, 'r') as file:
|
||||
output.writelines('```bash\n' + file.read() + '\n```')
|
||||
|
||||
patterns = os.path.join(target.scandir, '_patterns.log')
|
||||
if os.path.isfile(patterns):
|
||||
with open(os.path.join(report, target.address, 'Patterns' + '.md'), 'w') as output:
|
||||
with open(patterns, 'r') as file:
|
||||
output.writelines(file.read())
|
||||
|
||||
commands = os.path.join(target.scandir, '_commands.log')
|
||||
if os.path.isfile(commands):
|
||||
with open(os.path.join(report, target.address, 'Commands' + '.md'), 'w') as output:
|
||||
with open(commands, 'r') as file:
|
||||
output.writelines('```bash\n' + file.read() + '\n```')
|
||||
|
||||
errors = os.path.join(target.scandir, '_errors.log')
|
||||
if os.path.isfile(errors):
|
||||
with open(os.path.join(report, target.address, 'Errors' + '.md'), 'w') as output:
|
||||
with open(errors, 'r') as file:
|
||||
output.writelines('```\n' + file.read() + '\n```')
|
|
@ -0,0 +1,42 @@
|
|||
from autorecon.plugins import ServiceScan
|
||||
from autorecon.io import error, warn
|
||||
|
||||
class NmapRPC(ServiceScan):
|
||||
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
self.name = "Nmap MSRPC"
|
||||
self.tags = ['default', 'rpc']
|
||||
|
||||
def configure(self):
|
||||
self.match_service_name(['^msrpc', '^rpcbind', '^erpc'])
|
||||
|
||||
async def run(self, service):
|
||||
await service.execute('nmap {nmap_extra} -sV -p {port} --script="banner,msrpc-enum,rpc-grind,rpcinfo" -oN "{scandir}/{protocol}_{port}_rpc_nmap.txt" -oX "{scandir}/xml/{protocol}_{port}_rpc_nmap.xml" {address}')
|
||||
|
||||
class RPCClient(ServiceScan):
|
||||
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
self.name = "rpcclient"
|
||||
self.tags = ['default', 'safe', 'rpc']
|
||||
|
||||
def configure(self):
|
||||
self.match_service_name(['^msrpc', '^rpcbind', '^erpc'])
|
||||
|
||||
def manual(self, service, plugin_was_run):
|
||||
service.add_manual_command('RPC Client:', 'rpcclient -p {port} -U "" {address}')
|
||||
|
||||
class RPCDump(ServiceScan):
|
||||
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
self.name = 'rpcdump'
|
||||
self.tags = ['default', 'safe', 'rpc']
|
||||
|
||||
def configure(self):
|
||||
self.match_service_name(['^msrpc', '^rpcbind', '^erpc'])
|
||||
|
||||
async def run(self, service):
|
||||
if service.protocol == 'tcp':
|
||||
await service.execute('impacket-rpcdump -port {port} {address}', outfile='{protocol}_{port}_rpc_rpcdump.txt')
|
|
@ -0,0 +1,27 @@
|
|||
from autorecon.plugins import ServiceScan
|
||||
|
||||
class NmapRsync(ServiceScan):
|
||||
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
self.name = 'Nmap Rsync'
|
||||
self.tags = ['default', 'safe', 'rsync']
|
||||
|
||||
def configure(self):
|
||||
self.match_service_name('^rsync')
|
||||
|
||||
async def run(self, service):
|
||||
await service.execute('nmap {nmap_extra} -sV -p {port} --script="banner,(rsync* or ssl*) and not (brute or broadcast or dos or external or fuzzer)" -oN "{scandir}/{protocol}_{port}_rsync_nmap.txt" -oX "{scandir}/xml/{protocol}_{port}_rsync_nmap.xml" {address}')
|
||||
|
||||
class RsyncList(ServiceScan):
|
||||
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
self.name = 'Rsync List Files'
|
||||
self.tags = ['default', 'safe', 'rsync']
|
||||
|
||||
def configure(self):
|
||||
self.match_service_name('^rsync')
|
||||
|
||||
async def run(self, service):
|
||||
await service.execute('rsync -av --list-only rsync://{addressv6}:{port}', outfile='{protocol}_{port}_rsync_file_list.txt')
|
|
@ -0,0 +1,28 @@
|
|||
from autorecon.plugins import ServiceScan
|
||||
|
||||
class NmapSIP(ServiceScan):
|
||||
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
self.name = "Nmap SIP"
|
||||
self.tags = ['default', 'safe', 'sip']
|
||||
|
||||
def configure(self):
|
||||
self.match_service_name('^asterisk')
|
||||
|
||||
async def run(self, service):
|
||||
await service.execute('nmap {nmap_extra} -sV -p {port} --script="banner,sip-enum-users,sip-methods" -oN "{scandir}/{protocol}_{port}_sip_nmap.txt" -oX "{scandir}/xml/{protocol}_{port}_sip_nmap.xml" {address}')
|
||||
|
||||
class SIPVicious(ServiceScan):
|
||||
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
self.name = "SIPVicious"
|
||||
self.tags = ['default', 'safe', 'sip']
|
||||
|
||||
def configure(self):
|
||||
self.match_service_name('^asterisk')
|
||||
|
||||
def manual(self, service, plugin_was_run):
|
||||
if service.target.ipversion == 'IPv4':
|
||||
service.add_manual_command('svwar:', 'svwar -D -m INVITE -p {port} {address}')
|
|
@ -0,0 +1,104 @@
|
|||
from autorecon.plugins import ServiceScan
|
||||
|
||||
class NmapSMB(ServiceScan):
|
||||
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
self.name = "Nmap SMB"
|
||||
self.tags = ['default', 'safe', 'smb', 'active-directory']
|
||||
|
||||
def configure(self):
|
||||
self.match_service_name(['^smb', '^microsoft\-ds', '^netbios'])
|
||||
|
||||
async def run(self, service):
|
||||
await service.execute('nmap {nmap_extra} -sV -p {port} --script="banner,(nbstat or smb* or ssl*) and not (brute or broadcast or dos or external or fuzzer)" -oN "{scandir}/{protocol}_{port}_smb_nmap.txt" -oX "{scandir}/xml/{protocol}_{port}_smb_nmap.xml" {address}')
|
||||
|
||||
class SMBVuln(ServiceScan):
|
||||
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
self.name = "SMB Vulnerabilities"
|
||||
self.tags = ['unsafe', 'smb', 'active-directory']
|
||||
|
||||
def configure(self):
|
||||
self.match_service_name(['^smb', '^microsoft\-ds', '^netbios'])
|
||||
|
||||
async def run(self, service):
|
||||
await service.execute('nmap {nmap_extra} -sV -p {port} --script="smb-vuln-ms06-025" --script-args="unsafe=1" -oN "{scandir}/{protocol}_{port}_smb_ms06-025.txt" -oX "{scandir}/xml/{protocol}_{port}_smb_ms06-025.xml" {address}')
|
||||
await service.execute('nmap {nmap_extra} -sV -p {port} --script="smb-vuln-ms07-029" --script-args="unsafe=1" -oN "{scandir}/{protocol}_{port}_smb_ms07-029.txt" -oX "{scandir}/xml/{protocol}_{port}_smb_ms07-029.xml" {address}')
|
||||
await service.execute('nmap {nmap_extra} -sV -p {port} --script="smb-vuln-ms08-067" --script-args="unsafe=1" -oN "{scandir}/{protocol}_{port}_smb_ms08-067.txt" -oX "{scandir}/xml/{protocol}_{port}_smb_ms08-067.xml" {address}')
|
||||
|
||||
def manual(self, service, plugin_was_run):
|
||||
if not plugin_was_run: # Only suggest these if they weren't run.
|
||||
service.add_manual_commands('Nmap scans for SMB vulnerabilities that could potentially cause a DoS if scanned (according to Nmap). Be careful:', [
|
||||
'nmap {nmap_extra} -sV -p {port} --script="smb-vuln-ms06-025" --script-args="unsafe=1" -oN "{scandir}/{protocol}_{port}_smb_ms06-025.txt" -oX "{scandir}/xml/{protocol}_{port}_smb_ms06-025.xml" {address}',
|
||||
'nmap {nmap_extra} -sV -p {port} --script="smb-vuln-ms07-029" --script-args="unsafe=1" -oN "{scandir}/{protocol}_{port}_smb_ms07-029.txt" -oX "{scandir}/xml/{protocol}_{port}_smb_ms07-029.xml" {address}',
|
||||
'nmap {nmap_extra} -sV -p {port} --script="smb-vuln-ms08-067" --script-args="unsafe=1" -oN "{scandir}/{protocol}_{port}_smb_ms08-067.txt" -oX "{scandir}/xml/{protocol}_{port}_smb_ms08-067.xml" {address}'
|
||||
])
|
||||
|
||||
class Enum4Linux(ServiceScan):
|
||||
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
self.name = "Enum4Linux"
|
||||
self.tags = ['default', 'safe', 'active-directory']
|
||||
|
||||
def configure(self):
|
||||
self.match_service_name(['^ldap', '^smb', '^microsoft\-ds', '^netbios'])
|
||||
self.match_port('tcp', [139, 389, 445])
|
||||
self.match_port('udp', 137)
|
||||
self.run_once(True)
|
||||
|
||||
async def run(self, service):
|
||||
if service.target.ipversion == 'IPv4':
|
||||
await service.execute('enum4linux -a -M -l -d {address} 2>&1', outfile='enum4linux.txt')
|
||||
|
||||
class NBTScan(ServiceScan):
|
||||
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
self.name = "nbtscan"
|
||||
self.tags = ['default', 'safe', 'netbios', 'active-directory']
|
||||
|
||||
def configure(self):
|
||||
self.match_service_name(['^smb', '^microsoft\-ds', '^netbios'])
|
||||
self.match_port('udp', 137)
|
||||
self.run_once(True)
|
||||
|
||||
async def run(self, service):
|
||||
if service.target.ipversion == 'IPv4':
|
||||
await service.execute('nbtscan -rvh {address} 2>&1', outfile='nbtscan.txt')
|
||||
|
||||
class SMBClient(ServiceScan):
|
||||
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
self.name = "SMBClient"
|
||||
self.tags = ['default', 'safe', 'smb', 'active-directory']
|
||||
|
||||
def configure(self):
|
||||
self.match_service_name(['^smb', '^microsoft\-ds', '^netbios'])
|
||||
self.match_port('tcp', [139, 445])
|
||||
self.run_once(True)
|
||||
|
||||
async def run(self, service):
|
||||
await service.execute('smbclient -L\\\\ -N -I {address} 2>&1', outfile='smbclient.txt')
|
||||
|
||||
class SMBMap(ServiceScan):
|
||||
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
self.name = "SMBMap"
|
||||
self.tags = ['default', 'safe', 'smb', 'active-directory']
|
||||
|
||||
def configure(self):
|
||||
self.match_service_name(['^smb', '^microsoft\-ds', '^netbios'])
|
||||
|
||||
async def run(self, service):
|
||||
if service.target.ipversion == 'IPv4':
|
||||
await service.execute('smbmap -H {address} -P {port} 2>&1', outfile='smbmap-share-permissions.txt')
|
||||
await service.execute('smbmap -u null -p "" -H {address} -P {port} 2>&1', outfile='smbmap-share-permissions.txt')
|
||||
await service.execute('smbmap -H {address} -P {port} -R 2>&1', outfile='smbmap-list-contents.txt')
|
||||
await service.execute('smbmap -u null -p "" -H {address} -P {port} -R 2>&1', outfile='smbmap-list-contents.txt')
|
||||
await service.execute('smbmap -H {address} -P {port} -x "ipconfig /all" 2>&1', outfile='smbmap-execute-command.txt')
|
||||
await service.execute('smbmap -u null -p "" -H {address} -P {port} -x "ipconfig /all" 2>&1', outfile='smbmap-execute-command.txt')
|
|
@ -0,0 +1,33 @@
|
|||
from autorecon.plugins import ServiceScan
|
||||
|
||||
class NmapSMTP(ServiceScan):
|
||||
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
self.name = "Nmap SMTP"
|
||||
self.tags = ['default', 'safe', 'smtp', 'email']
|
||||
|
||||
def configure(self):
|
||||
self.match_service_name('^smtp')
|
||||
|
||||
async def run(self, service):
|
||||
await service.execute('nmap {nmap_extra} -sV -p {port} --script="banner,(smtp* or ssl*) and not (brute or broadcast or dos or external or fuzzer)" -oN "{scandir}/{protocol}_{port}_smtp_nmap.txt" -oX "{scandir}/xml/{protocol}_{port}_smtp_nmap.xml" {address}')
|
||||
|
||||
class SMTPUserEnum(ServiceScan):
|
||||
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
self.name = 'SMTP-User-Enum'
|
||||
self.tags = ['default', 'safe', 'smtp', 'email']
|
||||
|
||||
def configure(self):
|
||||
self.match_service_name('^smtp')
|
||||
|
||||
async def run(self, service):
|
||||
await service.execute('hydra smtp-enum://{addressv6}:{port}/vrfy -L "' + self.get_global('username_wordlist', default='/usr/share/seclists/Usernames/top-usernames-shortlist.txt') + '" 2>&1', outfile='{protocol}_{port}_smtp_user-enum_hydra_vrfy.txt')
|
||||
await service.execute('hydra smtp-enum://{addressv6}:{port}/expn -L "' + self.get_global('username_wordlist', default='/usr/share/seclists/Usernames/top-usernames-shortlist.txt') + '" 2>&1', outfile='{protocol}_{port}_smtp_user-enum_hydra_expn.txt')
|
||||
|
||||
def manual(self, service, plugin_was_run):
|
||||
service.add_manual_command('Try User Enumeration using "RCPT TO". Replace <TARGET-DOMAIN> with the target\'s domain name:', [
|
||||
'hydra smtp-enum://{addressv6}:{port}/rcpt -L "' + self.get_global('username_wordlist', default='/usr/share/seclists/Usernames/top-usernames-shortlist.txt') + '" -o "{scandir}/{protocol}_{port}_smtp_user-enum_hydra_rcpt.txt" -p <TARGET-DOMAIN>'
|
||||
])
|
|
@ -0,0 +1,53 @@
|
|||
from autorecon.plugins import ServiceScan
|
||||
|
||||
class NmapSNMP(ServiceScan):
|
||||
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
self.name = "Nmap SNMP"
|
||||
self.tags = ['default', 'safe', 'snmp']
|
||||
|
||||
def configure(self):
|
||||
self.match_service_name('^snmp')
|
||||
|
||||
async def run(self, service):
|
||||
await service.execute('nmap {nmap_extra} -sV -p {port} --script="banner,(snmp* or ssl*) and not (brute or broadcast or dos or external or fuzzer)" -oN "{scandir}/{protocol}_{port}_snmp-nmap.txt" -oX "{scandir}/xml/{protocol}_{port}_snmp_nmap.xml" {address}')
|
||||
|
||||
class OneSixtyOne(ServiceScan):
|
||||
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
self.name = "OneSixtyOne"
|
||||
self.tags = ['default', 'safe', 'snmp']
|
||||
|
||||
def configure(self):
|
||||
self.match_service_name('^snmp')
|
||||
self.match_port('udp', 161)
|
||||
self.run_once(True)
|
||||
self.add_option('community-strings', default='/usr/share/seclists/Discovery/SNMP/common-snmp-community-strings-onesixtyone.txt', help='The file containing a list of community strings to try. Default: %(default)s')
|
||||
|
||||
async def run(self, service):
|
||||
if service.target.ipversion == 'IPv4':
|
||||
await service.execute('onesixtyone -c ' + service.get_option('community-strings') + ' -dd {address} 2>&1', outfile='{protocol}_{port}_snmp_onesixtyone.txt')
|
||||
|
||||
class SNMPWalk(ServiceScan):
|
||||
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
self.name = "SNMPWalk"
|
||||
self.tags = ['default', 'safe', 'snmp']
|
||||
|
||||
def configure(self):
|
||||
self.match_service_name('^snmp')
|
||||
self.match_port('udp', 161)
|
||||
self.run_once(True)
|
||||
|
||||
async def run(self, service):
|
||||
await service.execute('snmpwalk -c public -v 1 {address} 2>&1', outfile='{protocol}_{port}_snmp_snmpwalk.txt')
|
||||
await service.execute('snmpwalk -c public -v 1 {address} 1.3.6.1.2.1.25.1.6.0 2>&1', outfile='{protocol}_{port}_snmp_snmpwalk_system_processes.txt')
|
||||
await service.execute('snmpwalk -c public -v 1 {address} 1.3.6.1.2.1.25.4.2.1.2 2>&1', outfile='{scandir}/{protocol}_{port}_snmp_snmpwalk_running_processes.txt')
|
||||
await service.execute('snmpwalk -c public -v 1 {address} 1.3.6.1.2.1.25.4.2.1.4 2>&1', outfile='{protocol}_{port}_snmp_snmpwalk_process_paths.txt')
|
||||
await service.execute('snmpwalk -c public -v 1 {address} 1.3.6.1.2.1.25.2.3.1.4 2>&1', outfile='{protocol}_{port}_snmp_snmpwalk_storage_units.txt')
|
||||
await service.execute('snmpwalk -c public -v 1 {address} 1.3.6.1.2.1.25.2.3.1.4 2>&1', outfile='{protocol}_{port}_snmp_snmpwalk_software_names.txt')
|
||||
await service.execute('snmpwalk -c public -v 1 {address} 1.3.6.1.4.1.77.1.2.25 2>&1', outfile='{protocol}_{port}_snmp_snmpwalk_user_accounts.txt')
|
||||
await service.execute('snmpwalk -c public -v 1 {address} 1.3.6.1.2.1.6.13.1.3 2>&1', outfile='{protocol}_{port}_snmp_snmpwalk_tcp_ports.txt')
|
|
@ -0,0 +1,30 @@
|
|||
from autorecon.plugins import ServiceScan
|
||||
|
||||
class NmapSSH(ServiceScan):
|
||||
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
self.name = "Nmap SSH"
|
||||
self.tags = ['default', 'safe', 'ssh']
|
||||
|
||||
def configure(self):
|
||||
self.match_service_name('^ssh')
|
||||
|
||||
async def run(self, service):
|
||||
await service.execute('nmap {nmap_extra} -sV -p {port} --script="banner,ssh2-enum-algos,ssh-hostkey,ssh-auth-methods" -oN "{scandir}/{protocol}_{port}_ssh_nmap.txt" -oX "{scandir}/xml/{protocol}_{port}_ssh_nmap.xml" {address}')
|
||||
|
||||
class BruteforceSSH(ServiceScan):
|
||||
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
self.name = "Bruteforce SSH"
|
||||
self.tags = ['default', 'ssh']
|
||||
|
||||
def configure(self):
|
||||
self.match_service_name('ssh')
|
||||
|
||||
def manual(self, service, plugin_was_run):
|
||||
service.add_manual_command('Bruteforce logins:', [
|
||||
'hydra -L "' + self.get_global('username_wordlist', default='/usr/share/seclists/Usernames/top-usernames-shortlist.txt') + '" -P "' + self.get_global('password_wordlist', default='/usr/share/seclists/Passwords/darkweb2017-top100.txt') + '" -e nsr -s {port} -o "{scandir}/{protocol}_{port}_ssh_hydra.txt" ssh://{addressv6}',
|
||||
'medusa -U "' + self.get_global('username_wordlist', default='/usr/share/seclists/Usernames/top-usernames-shortlist.txt') + '" -P "' + self.get_global('password_wordlist', default='/usr/share/seclists/Passwords/darkweb2017-top100.txt') + '" -e ns -n {port} -O "{scandir}/{protocol}_{port}_ssh_medusa.txt" -M ssh -h {addressv6}'
|
||||
])
|
|
@ -0,0 +1,16 @@
|
|||
from autorecon.plugins import ServiceScan
|
||||
|
||||
class SSLScan(ServiceScan):
|
||||
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
self.name = "SSL Scan"
|
||||
self.tags = ['default', 'safe', 'ssl', 'tls']
|
||||
|
||||
def configure(self):
|
||||
self.match_all_service_names(True)
|
||||
self.require_ssl(True)
|
||||
|
||||
async def run(self, service):
|
||||
if service.protocol == 'tcp' and service.secure:
|
||||
await service.execute('sslscan --show-certificate --no-colour {addressv6}:{port} 2>&1', outfile='{protocol}_{port}_sslscan.html')
|
|
@ -0,0 +1,22 @@
|
|||
[global.username-wordlist]
|
||||
default = '/usr/share/seclists/Usernames/top-usernames-shortlist.txt'
|
||||
help = 'A wordlist of usernames, useful for bruteforcing. Default: %(default)s'
|
||||
|
||||
[global.password-wordlist]
|
||||
default = '/usr/share/seclists/Passwords/darkweb2017-top100.txt'
|
||||
help = 'A wordlist of passwords, useful for bruteforcing. Default: %(default)s'
|
||||
|
||||
[global.domain]
|
||||
help = 'The domain to use (if known). Used for DNS and/or Active Directory. Default: %(default)s'
|
||||
|
||||
# Configure global pattern matching here.
|
||||
[[pattern]]
|
||||
description = 'Nmap script found a potential vulnerability. ({match})'
|
||||
pattern = 'State: (?:(?:LIKELY\_?)?VULNERABLE)'
|
||||
|
||||
[[pattern]]
|
||||
pattern = '(?i)unauthorized'
|
||||
|
||||
[[pattern]]
|
||||
description = 'CVE Identified: {match}'
|
||||
pattern = '(CVE-\d{4}-\d{4,7})'
|
|
@ -0,0 +1,166 @@
|
|||
import asyncio, colorama, os, re, string, sys, unidecode
|
||||
from colorama import Fore, Style
|
||||
from autorecon.config import config
|
||||
|
||||
def slugify(name):
|
||||
return re.sub(r'[\W_]+', '-', unidecode.unidecode(name).lower()).strip('-')
|
||||
|
||||
def e(*args, frame_index=1, **kvargs):
|
||||
frame = sys._getframe(frame_index)
|
||||
|
||||
vals = {}
|
||||
|
||||
vals.update(frame.f_globals)
|
||||
vals.update(frame.f_locals)
|
||||
vals.update(kvargs)
|
||||
|
||||
return string.Formatter().vformat(' '.join(args), args, vals)
|
||||
|
||||
def fformat(s):
|
||||
return e(s, frame_index=3)
|
||||
|
||||
def cprint(*args, color=Fore.RESET, char='*', sep=' ', end='\n', frame_index=1, file=sys.stdout, printmsg=True, verbosity=0, **kvargs):
|
||||
if printmsg and verbosity > config['verbose']:
|
||||
return ''
|
||||
frame = sys._getframe(frame_index)
|
||||
|
||||
vals = {
|
||||
'bgreen': Fore.GREEN + Style.BRIGHT,
|
||||
'bred': Fore.RED + Style.BRIGHT,
|
||||
'bblue': Fore.BLUE + Style.BRIGHT,
|
||||
'byellow': Fore.YELLOW + Style.BRIGHT,
|
||||
'bmagenta': Fore.MAGENTA + Style.BRIGHT,
|
||||
|
||||
'green': Fore.GREEN,
|
||||
'red': Fore.RED,
|
||||
'blue': Fore.BLUE,
|
||||
'yellow': Fore.YELLOW,
|
||||
'magenta': Fore.MAGENTA,
|
||||
|
||||
'bright': Style.BRIGHT,
|
||||
'srst': Style.NORMAL,
|
||||
'crst': Fore.RESET,
|
||||
'rst': Style.NORMAL + Fore.RESET
|
||||
}
|
||||
|
||||
if config['accessible']:
|
||||
vals = {'bgreen':'', 'bred':'', 'bblue':'', 'byellow':'', 'bmagenta':'', 'green':'', 'red':'', 'blue':'', 'yellow':'', 'magenta':'', 'bright':'', 'srst':'', 'crst':'', 'rst':''}
|
||||
|
||||
vals.update(frame.f_globals)
|
||||
vals.update(frame.f_locals)
|
||||
vals.update(kvargs)
|
||||
|
||||
unfmt = ''
|
||||
if char is not None and not config['accessible']:
|
||||
unfmt += color + '[' + Style.BRIGHT + char + Style.NORMAL + ']' + Fore.RESET + sep
|
||||
unfmt += sep.join(args)
|
||||
|
||||
fmted = unfmt
|
||||
|
||||
for attempt in range(10):
|
||||
try:
|
||||
fmted = string.Formatter().vformat(unfmt, args, vals)
|
||||
break
|
||||
except KeyError as err:
|
||||
key = err.args[0]
|
||||
unfmt = unfmt.replace('{' + key + '}', '{{' + key + '}}')
|
||||
|
||||
if printmsg:
|
||||
print(fmted, sep=sep, end=end, file=file)
|
||||
else:
|
||||
return fmted
|
||||
|
||||
def debug(*args, color=Fore.GREEN, sep=' ', end='\n', file=sys.stdout, **kvargs):
|
||||
if verbose >= 2:
|
||||
if config['accessible']:
|
||||
args = ('Debug:',) + args
|
||||
cprint(*args, color=color, char='-', sep=sep, end=end, file=file, frame_index=2, **kvargs)
|
||||
|
||||
def info(*args, sep=' ', end='\n', file=sys.stdout, **kvargs):
|
||||
cprint(*args, color=Fore.BLUE, char='*', sep=sep, end=end, file=file, frame_index=2, **kvargs)
|
||||
|
||||
def warn(*args, sep=' ', end='\n', file=sys.stderr,**kvargs):
|
||||
if config['accessible']:
|
||||
args = ('Warning:',) + args
|
||||
cprint(*args, color=Fore.YELLOW, char='!', sep=sep, end=end, file=file, frame_index=2, **kvargs)
|
||||
|
||||
def error(*args, sep=' ', end='\n', file=sys.stderr, **kvargs):
|
||||
if config['accessible']:
|
||||
args = ('Error:',) + args
|
||||
cprint(*args, color=Fore.RED, char='!', sep=sep, end=end, file=file, frame_index=2, **kvargs)
|
||||
|
||||
def fail(*args, sep=' ', end='\n', file=sys.stderr, **kvargs):
|
||||
if config['accessible']:
|
||||
args = ('Failure:',) + args
|
||||
cprint(*args, color=Fore.RED, char='!', sep=sep, end=end, file=file, frame_index=2, **kvargs)
|
||||
exit(-1)
|
||||
|
||||
class CommandStreamReader(object):
|
||||
|
||||
def __init__(self, stream, target, tag, patterns=None, outfile=None):
|
||||
self.stream = stream
|
||||
self.target = target
|
||||
self.tag = tag
|
||||
self.lines = []
|
||||
self.patterns = patterns or []
|
||||
self.outfile = outfile
|
||||
self.ended = False
|
||||
|
||||
# Empty files that already exist.
|
||||
if self.outfile != None:
|
||||
with open(self.outfile, 'w'): pass
|
||||
|
||||
# Read lines from the stream until it ends.
|
||||
async def _read(self):
|
||||
while True:
|
||||
if self.stream.at_eof():
|
||||
break
|
||||
try:
|
||||
line = (await self.stream.readline()).decode('utf8').rstrip()
|
||||
except ValueError:
|
||||
error('{bright}[{yellow}' + self.target.address + '{crst}/{bgreen}' + self.tag + '{crst}]{rst} A line was longer than 64 KiB and cannot be processed. Ignoring.')
|
||||
continue
|
||||
|
||||
if line != '':
|
||||
info('{bright}[{yellow}' + self.target.address + '{crst}/{bgreen}' + self.tag + '{crst}]{rst} ' + line.replace('{', '{{').replace('}', '}}'), verbosity=3)
|
||||
|
||||
# Check lines for pattern matches.
|
||||
for p in self.patterns:
|
||||
matches = p.pattern.findall(line)
|
||||
for match in matches:
|
||||
async with self.target.lock:
|
||||
with open(os.path.join(self.target.scandir, '_patterns.log'), 'a') as file:
|
||||
if p.description:
|
||||
info('{bright}[{yellow}' + self.target.address + '{crst}/{bgreen}' + self.tag + '{crst}]{rst} {bmagenta}' + p.description.replace('{match}', match) + '{rst}', verbosity=2)
|
||||
file.writelines(p.description.replace('{match}', match) + '\n\n')
|
||||
else:
|
||||
info('{bright}[{yellow}' + self.target.address + '{crst}/{bgreen}' + self.tag + '{crst}]{rst} {bmagenta}Matched Pattern: ' + match + '{rst}', verbosity=2)
|
||||
file.writelines('Matched Pattern: ' + match + '\n\n')
|
||||
|
||||
if self.outfile is not None:
|
||||
with open(self.outfile, 'a') as writer:
|
||||
writer.write(line + '\n')
|
||||
self.lines.append(line)
|
||||
self.ended = True
|
||||
|
||||
# Read a line from the stream cache.
|
||||
async def readline(self):
|
||||
while True:
|
||||
try:
|
||||
return self.lines.pop(0)
|
||||
except IndexError:
|
||||
if self.ended:
|
||||
return None
|
||||
else:
|
||||
await asyncio.sleep(0.1)
|
||||
|
||||
# Read all lines from the stream cache.
|
||||
async def readlines(self):
|
||||
lines = []
|
||||
while True:
|
||||
line = await self.readline()
|
||||
if line is not None:
|
||||
lines.append(line)
|
||||
else:
|
||||
break
|
||||
return lines
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,355 @@
|
|||
import asyncio, inspect, os, re, sys
|
||||
from typing import final
|
||||
from autorecon.config import config
|
||||
from autorecon.io import slugify, error, fail, CommandStreamReader
|
||||
from autorecon.targets import Service
|
||||
|
||||
class Pattern:
|
||||
|
||||
def __init__(self, pattern, description=None):
|
||||
self.pattern = pattern
|
||||
self.description = description
|
||||
|
||||
class Plugin(object):
|
||||
|
||||
def __init__(self):
|
||||
self.name = None
|
||||
self.slug = None
|
||||
self.description = None
|
||||
self.tags = ['default']
|
||||
self.priority = 1
|
||||
self.patterns = []
|
||||
self.autorecon = None
|
||||
self.disabled = False
|
||||
|
||||
@final
|
||||
def add_option(self, name, default=None, help=None):
|
||||
self.autorecon.add_argument(self, name, metavar='VALUE', default=default, help=help)
|
||||
|
||||
@final
|
||||
def add_constant_option(self, name, const, default=None, help=None):
|
||||
self.autorecon.add_argument(self, name, action='store_const', const=const, default=default, help=help)
|
||||
|
||||
@final
|
||||
def add_true_option(self, name, help=None):
|
||||
self.autorecon.add_argument(self, name, action='store_true', help=help)
|
||||
|
||||
@final
|
||||
def add_false_option(self, name, help=None):
|
||||
self.autorecon.add_argument(self, name, action='store_false', help=help)
|
||||
|
||||
@final
|
||||
def add_list_option(self, name, default=None, help=None):
|
||||
self.autorecon.add_argument(self, name, nargs='+', metavar='VALUE', default=default, help=help)
|
||||
|
||||
@final
|
||||
def add_choice_option(self, name, choices, default=None, help=None):
|
||||
if not isinstance(choices, list):
|
||||
fail('The choices argument for ' + self.name + '\'s ' + name + ' choice option should be a list.')
|
||||
self.autorecon.add_argument(self, name, choices=choices, default=default, help=help)
|
||||
|
||||
@final
|
||||
def get_option(self, name):
|
||||
# TODO: make sure name is simple.
|
||||
name = self.slug.replace('-', '_') + '.' + slugify(name).replace('-', '_')
|
||||
|
||||
if name in vars(self.autorecon.args):
|
||||
return vars(self.autorecon.args)[name]
|
||||
else:
|
||||
return None
|
||||
|
||||
@final
|
||||
def get_global_option(self, name, default=None):
|
||||
name = 'global.' + slugify(name).replace('-', '_')
|
||||
|
||||
if name in vars(self.autorecon.args):
|
||||
if vars(self.autorecon.args)[name] is None:
|
||||
if default:
|
||||
return default
|
||||
else:
|
||||
return None
|
||||
else:
|
||||
return vars(self.autorecon.args)[name]
|
||||
else:
|
||||
if default:
|
||||
return default
|
||||
return None
|
||||
|
||||
@final
|
||||
def get_global(self, name, default=None):
|
||||
return self.get_global_option(name, default)
|
||||
|
||||
@final
|
||||
def add_pattern(self, pattern, description=None):
|
||||
try:
|
||||
compiled = re.compile(pattern)
|
||||
if description:
|
||||
self.patterns.append(Pattern(compiled, description=description))
|
||||
else:
|
||||
self.patterns.append(Pattern(compiled))
|
||||
except re.error:
|
||||
fail('Error: The pattern "' + pattern + '" in the plugin "' + self.name + '" is invalid regex.')
|
||||
|
||||
class PortScan(Plugin):
|
||||
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
self.type = None
|
||||
self.specific_ports = False
|
||||
|
||||
async def run(self, target):
|
||||
raise NotImplementedError
|
||||
|
||||
class ServiceScan(Plugin):
|
||||
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
self.ports = {'tcp':[], 'udp':[]}
|
||||
self.ignore_ports = {'tcp':[], 'udp':[]}
|
||||
self.services = []
|
||||
self.service_names = []
|
||||
self.ignore_service_names = []
|
||||
self.run_once_boolean = False
|
||||
self.require_ssl_boolean = False
|
||||
|
||||
@final
|
||||
def match_service(self, protocol, port, name, negative_match=False):
|
||||
protocol = protocol.lower()
|
||||
if protocol not in ['tcp', 'udp']:
|
||||
print('Invalid protocol.')
|
||||
sys.exit(1)
|
||||
|
||||
if not isinstance(port, list):
|
||||
port = [port]
|
||||
|
||||
port = list(map(int, port))
|
||||
|
||||
if not isinstance(name, list):
|
||||
name = [name]
|
||||
|
||||
valid_regex = True
|
||||
for r in name:
|
||||
try:
|
||||
re.compile(r)
|
||||
except re.error:
|
||||
print('Invalid regex: ' + r)
|
||||
valid_regex = False
|
||||
|
||||
if not valid_regex:
|
||||
sys.exit(1)
|
||||
|
||||
service = {'protocol': protocol, 'port': port, 'name': name, 'negative_match': negative_match}
|
||||
self.services.append(service)
|
||||
|
||||
@final
|
||||
def match_port(self, protocol, port, negative_match=False):
|
||||
protocol = protocol.lower()
|
||||
if protocol not in ['tcp', 'udp']:
|
||||
print('Invalid protocol.')
|
||||
sys.exit(1)
|
||||
else:
|
||||
if not isinstance(port, list):
|
||||
port = [port]
|
||||
|
||||
port = list(map(int, port))
|
||||
|
||||
if negative_match:
|
||||
self.ignore_ports[protocol] = list(set(self.ignore_ports[protocol] + port))
|
||||
else:
|
||||
self.ports[protocol] = list(set(self.ports[protocol] + port))
|
||||
|
||||
@final
|
||||
def match_service_name(self, name, negative_match=False):
|
||||
if not isinstance(name, list):
|
||||
name = [name]
|
||||
|
||||
valid_regex = True
|
||||
for r in name:
|
||||
try:
|
||||
re.compile(r)
|
||||
except re.error:
|
||||
print('Invalid regex: ' + r)
|
||||
valid_regex = False
|
||||
|
||||
if valid_regex:
|
||||
if negative_match:
|
||||
self.ignore_service_names = list(set(self.ignore_service_names + name))
|
||||
else:
|
||||
self.service_names = list(set(self.service_names + name))
|
||||
else:
|
||||
sys.exit(1)
|
||||
|
||||
@final
|
||||
def require_ssl(self, boolean):
|
||||
self.require_ssl_boolean = boolean
|
||||
|
||||
@final
|
||||
def run_once(self, boolean):
|
||||
self.run_once_boolean = boolean
|
||||
|
||||
@final
|
||||
def match_all_service_names(self, boolean):
|
||||
if boolean:
|
||||
# Add a "match all" service name.
|
||||
self.match_service_name('.*')
|
||||
|
||||
class Report(Plugin):
|
||||
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
|
||||
class AutoRecon(object):
|
||||
|
||||
def __init__(self):
|
||||
self.pending_targets = []
|
||||
self.scanning_targets = []
|
||||
self.completed_targets = []
|
||||
self.plugins = {}
|
||||
self.__slug_regex = re.compile('^[a-z0-9\-]+$')
|
||||
self.plugin_types = {'port':[], 'service':[], 'report':[]}
|
||||
self.port_scan_semaphore = None
|
||||
self.service_scan_semaphore = None
|
||||
self.argparse = None
|
||||
self.argparse_group = None
|
||||
self.args = None
|
||||
self.missing_services = []
|
||||
self.taglist = []
|
||||
self.tags = []
|
||||
self.excluded_tags = []
|
||||
self.patterns = []
|
||||
self.errors = False
|
||||
self.lock = asyncio.Lock()
|
||||
self.load_slug = None
|
||||
self.load_module = None
|
||||
|
||||
def add_argument(self, plugin, name, **kwargs):
|
||||
# TODO: make sure name is simple.
|
||||
name = '--' + plugin.slug + '.' + slugify(name)
|
||||
|
||||
if self.argparse_group is None:
|
||||
self.argparse_group = self.argparse.add_argument_group('plugin arguments', description='These are optional arguments for certain plugins.')
|
||||
self.argparse_group.add_argument(name, **kwargs)
|
||||
|
||||
def extract_service(self, line, regex):
|
||||
if regex is None:
|
||||
regex = '^(?P<port>\d+)\/(?P<protocol>(tcp|udp))(.*)open(\s*)(?P<service>[\w\-\/]+)(\s*)(.*)$'
|
||||
match = re.search(regex, line)
|
||||
if match:
|
||||
protocol = match.group('protocol').lower()
|
||||
port = int(match.group('port'))
|
||||
service = match.group('service')
|
||||
secure = True if 'ssl' in service or 'tls' in service else False
|
||||
|
||||
if service.startswith('ssl/') or service.startswith('tls/'):
|
||||
service = service[4:]
|
||||
|
||||
return Service(protocol, port, service, secure)
|
||||
else:
|
||||
return None
|
||||
|
||||
async def extract_services(self, stream, regex):
|
||||
if not isinstance(stream, CommandStreamReader):
|
||||
print('Error: extract_services must be passed an instance of a CommandStreamReader.')
|
||||
sys.exit(1)
|
||||
|
||||
services = []
|
||||
while True:
|
||||
line = await stream.readline()
|
||||
if line is not None:
|
||||
service = self.extract_service(line, regex)
|
||||
if service:
|
||||
services.append(service)
|
||||
else:
|
||||
break
|
||||
return services
|
||||
|
||||
def register(self, plugin, filename):
|
||||
if plugin.disabled:
|
||||
return
|
||||
|
||||
if plugin.name is None:
|
||||
fail('Error: Plugin with class name "' + plugin.__class__.__name__ + '" in ' + filename + ' does not have a name.')
|
||||
|
||||
for _, loaded_plugin in self.plugins.items():
|
||||
if plugin.name == loaded_plugin.name:
|
||||
fail('Error: Duplicate plugin name "' + plugin.name + '" detected in ' + filename + '.', file=sys.stderr)
|
||||
|
||||
if plugin.slug is None:
|
||||
plugin.slug = slugify(plugin.name)
|
||||
elif not self.__slug_regex.match(plugin.slug):
|
||||
fail('Error: provided slug "' + plugin.slug + '" in ' + filename + ' is not valid (must only contain lowercase letters, numbers, and hyphens).', file=sys.stderr)
|
||||
|
||||
if plugin.slug in config['protected_classes']:
|
||||
fail('Error: plugin slug "' + plugin.slug + '" in ' + filename + ' is a protected string. Please change.')
|
||||
|
||||
if plugin.slug not in self.plugins:
|
||||
|
||||
for _, loaded_plugin in self.plugins.items():
|
||||
if plugin is loaded_plugin:
|
||||
fail('Error: plugin "' + plugin.name + '" in ' + filename + ' already loaded as "' + loaded_plugin.name + '" (' + str(loaded_plugin) + ')', file=sys.stderr)
|
||||
|
||||
configure_function_found = False
|
||||
run_coroutine_found = False
|
||||
manual_function_found = False
|
||||
|
||||
for member_name, member_value in inspect.getmembers(plugin, predicate=inspect.ismethod):
|
||||
if member_name == 'configure':
|
||||
configure_function_found = True
|
||||
elif member_name == 'run' and inspect.iscoroutinefunction(member_value):
|
||||
if len(inspect.getfullargspec(member_value).args) != 2:
|
||||
fail('Error: the "run" coroutine in the plugin "' + plugin.name + '" in ' + filename + ' should have two arguments.', file=sys.stderr)
|
||||
run_coroutine_found = True
|
||||
elif member_name == 'manual':
|
||||
if len(inspect.getfullargspec(member_value).args) != 3:
|
||||
fail('Error: the "manual" function in the plugin "' + plugin.name + '" in ' + filename + ' should have three arguments.', file=sys.stderr)
|
||||
manual_function_found = True
|
||||
|
||||
if not run_coroutine_found and not manual_function_found:
|
||||
fail('Error: the plugin "' + plugin.name + '" in ' + filename + ' needs either a "manual" function, a "run" coroutine, or both.', file=sys.stderr)
|
||||
|
||||
if issubclass(plugin.__class__, PortScan):
|
||||
if plugin.type is None:
|
||||
fail('Error: the PortScan plugin "' + plugin.name + '" in ' + filename + ' requires a type (either tcp or udp).')
|
||||
else:
|
||||
plugin.type = plugin.type.lower()
|
||||
if plugin.type not in ['tcp', 'udp']:
|
||||
fail('Error: the PortScan plugin "' + plugin.name + '" in ' + filename + ' has an invalid type (should be tcp or udp).')
|
||||
self.plugin_types["port"].append(plugin)
|
||||
elif issubclass(plugin.__class__, ServiceScan):
|
||||
self.plugin_types["service"].append(plugin)
|
||||
elif issubclass(plugin.__class__, Report):
|
||||
self.plugin_types["report"].append(plugin)
|
||||
else:
|
||||
fail('Plugin "' + plugin.name + '" in ' + filename + ' is neither a PortScan, ServiceScan, nor a Report.', file=sys.stderr)
|
||||
|
||||
plugin.tags = [tag.lower() for tag in plugin.tags]
|
||||
|
||||
# Add plugin tags to tag list.
|
||||
[self.taglist.append(t) for t in plugin.tags if t not in self.tags]
|
||||
|
||||
plugin.autorecon = self
|
||||
if configure_function_found:
|
||||
plugin.configure()
|
||||
self.plugins[plugin.slug] = plugin
|
||||
else:
|
||||
fail('Error: plugin slug "' + plugin.slug + '" in ' + filename + ' is already assigned.', file=sys.stderr)
|
||||
|
||||
async def execute(self, cmd, target, tag, patterns=None, outfile=None, errfile=None):
|
||||
if patterns:
|
||||
combined_patterns = self.patterns + patterns
|
||||
else:
|
||||
combined_patterns = self.patterns
|
||||
|
||||
process = await asyncio.create_subprocess_shell(
|
||||
cmd,
|
||||
stdin=open('/dev/null'),
|
||||
stdout=asyncio.subprocess.PIPE,
|
||||
stderr=asyncio.subprocess.PIPE)
|
||||
|
||||
cout = CommandStreamReader(process.stdout, target, tag, patterns=combined_patterns, outfile=outfile)
|
||||
cerr = CommandStreamReader(process.stderr, target, tag, patterns=combined_patterns, outfile=errfile)
|
||||
|
||||
asyncio.create_task(cout._read())
|
||||
asyncio.create_task(cerr._read())
|
||||
|
||||
return process, cout, cerr
|
|
@ -0,0 +1,195 @@
|
|||
import asyncio, inspect, os
|
||||
from typing import final
|
||||
from autorecon.config import config
|
||||
from autorecon.io import e, info
|
||||
|
||||
class Target:
|
||||
|
||||
def __init__(self, address, ip, ipversion, type, autorecon):
|
||||
self.address = address
|
||||
self.ip = ip
|
||||
self.ipversion = ipversion
|
||||
self.type = type
|
||||
self.autorecon = autorecon
|
||||
self.basedir = ''
|
||||
self.reportdir = ''
|
||||
self.scandir = ''
|
||||
self.lock = asyncio.Lock()
|
||||
self.ports = None
|
||||
self.pending_services = []
|
||||
self.services = []
|
||||
self.scans = {'ports':{}, 'services':{}}
|
||||
self.running_tasks = {}
|
||||
|
||||
async def add_service(self, service):
|
||||
async with self.lock:
|
||||
self.pending_services.append(service)
|
||||
|
||||
def extract_service(self, line, regex=None):
|
||||
return self.autorecon.extract_service(line, regex)
|
||||
|
||||
async def extract_services(self, stream, regex=None):
|
||||
return await self.autorecon.extract_services(stream, regex)
|
||||
|
||||
async def execute(self, cmd, blocking=True, outfile=None, errfile=None, future_outfile=None):
|
||||
target = self
|
||||
|
||||
# Create variables for command references.
|
||||
address = target.address
|
||||
addressv6 = target.address
|
||||
ipaddress = target.ip
|
||||
ipaddressv6 = target.ip
|
||||
scandir = target.scandir
|
||||
|
||||
nmap_extra = target.autorecon.args.nmap
|
||||
if target.autorecon.args.nmap_append:
|
||||
nmap_extra += ' ' + target.autorecon.args.nmap_append
|
||||
|
||||
if target.ipversion == 'IPv6':
|
||||
nmap_extra += ' -6'
|
||||
if addressv6 == target.ip:
|
||||
addressv6 = '[' + addressv6 + ']'
|
||||
ipaddressv6 = '[' + ipaddressv6 + ']'
|
||||
|
||||
plugin = inspect.currentframe().f_back.f_locals['self']
|
||||
|
||||
if config['proxychains']:
|
||||
nmap_extra += ' -sT'
|
||||
|
||||
cmd = e(cmd)
|
||||
tag = plugin.slug
|
||||
|
||||
info('Port scan {bblue}' + plugin.name + ' {green}(' + tag + '){rst} is running the following command against {byellow}' + address + '{rst}: ' + cmd, verbosity=2)
|
||||
|
||||
if outfile is not None:
|
||||
outfile = os.path.join(target.scandir, e(outfile))
|
||||
|
||||
if errfile is not None:
|
||||
errfile = os.path.join(target.scandir, e(errfile))
|
||||
|
||||
if future_outfile is not None:
|
||||
future_outfile = os.path.join(target.scandir, e(future_outfile))
|
||||
|
||||
target.scans['ports'][tag]['commands'].append([cmd, outfile if outfile is not None else future_outfile, errfile])
|
||||
|
||||
async with target.lock:
|
||||
with open(os.path.join(target.scandir, '_commands.log'), 'a') as file:
|
||||
file.writelines(cmd + '\n\n')
|
||||
|
||||
process, stdout, stderr = await target.autorecon.execute(cmd, target, tag, patterns=plugin.patterns, outfile=outfile, errfile=errfile)
|
||||
|
||||
target.running_tasks[tag]['processes'].append({'process': process, 'stderr': stderr, 'cmd': cmd})
|
||||
|
||||
# If process should block, sleep until stdout and stderr have finished.
|
||||
if blocking:
|
||||
while (not (stdout.ended and stderr.ended)):
|
||||
await asyncio.sleep(0.1)
|
||||
await process.wait()
|
||||
|
||||
return process, stdout, stderr
|
||||
|
||||
class Service:
|
||||
|
||||
def __init__(self, protocol, port, name, secure=False):
|
||||
self.target = None
|
||||
self.protocol = protocol.lower()
|
||||
self.port = int(port)
|
||||
self.name = name
|
||||
self.secure = secure
|
||||
self.manual_commands = {}
|
||||
|
||||
@final
|
||||
def tag(self):
|
||||
return self.protocol + '/' + str(self.port) + '/' + self.name
|
||||
|
||||
@final
|
||||
def full_tag(self):
|
||||
return self.protocol + '/' + str(self.port) + '/' + self.name + '/' + ('secure' if self.secure else 'insecure')
|
||||
|
||||
@final
|
||||
def add_manual_commands(self, description, commands):
|
||||
if not isinstance(commands, list):
|
||||
commands = [commands]
|
||||
if description not in self.manual_commands:
|
||||
self.manual_commands[description] = []
|
||||
|
||||
# Merge in new unique commands, while preserving order.
|
||||
[self.manual_commands[description].append(m) for m in commands if m not in self.manual_commands[description]]
|
||||
|
||||
@final
|
||||
def add_manual_command(self, description, command):
|
||||
self.add_manual_commands(description, command)
|
||||
|
||||
@final
|
||||
async def execute(self, cmd, blocking=True, outfile=None, errfile=None, future_outfile=None):
|
||||
target = self.target
|
||||
|
||||
# Create variables for command references.
|
||||
address = target.address
|
||||
addressv6 = target.address
|
||||
ipaddress = target.ip
|
||||
ipaddressv6 = target.ip
|
||||
scandir = target.scandir
|
||||
protocol = self.protocol
|
||||
port = self.port
|
||||
name = self.name
|
||||
|
||||
if config['create_port_dirs']:
|
||||
scandir = os.path.join(scandir, protocol + str(port))
|
||||
os.makedirs(scandir, exist_ok=True)
|
||||
os.makedirs(os.path.join(scandir, 'xml'), exist_ok=True)
|
||||
|
||||
# Special cases for HTTP.
|
||||
http_scheme = 'https' if 'https' in self.name or self.secure is True else 'http'
|
||||
|
||||
nmap_extra = target.autorecon.args.nmap
|
||||
if target.autorecon.args.nmap_append:
|
||||
nmap_extra += ' ' + target.autorecon.args.nmap_append
|
||||
|
||||
if protocol == 'udp':
|
||||
nmap_extra += ' -sU'
|
||||
|
||||
if target.ipversion == 'IPv6':
|
||||
nmap_extra += ' -6'
|
||||
if addressv6 == target.ip:
|
||||
addressv6 = '[' + addressv6 + ']'
|
||||
ipaddressv6 = '[' + ipaddressv6 + ']'
|
||||
|
||||
if config['proxychains'] and protocol == 'tcp':
|
||||
nmap_extra += ' -sT'
|
||||
|
||||
plugin = inspect.currentframe().f_back.f_locals['self']
|
||||
cmd = e(cmd)
|
||||
tag = self.tag() + '/' + plugin.slug
|
||||
plugin_tag = tag
|
||||
if plugin.run_once_boolean:
|
||||
plugin_tag = plugin.slug
|
||||
|
||||
info('Service scan {bblue}' + plugin.name + ' {green}(' + tag + '){rst} is running the following command against {byellow}' + address + '{rst}: ' + cmd, verbosity=2)
|
||||
|
||||
if outfile is not None:
|
||||
outfile = os.path.join(scandir, e(outfile))
|
||||
|
||||
if errfile is not None:
|
||||
errfile = os.path.join(scandir, e(errfile))
|
||||
|
||||
if future_outfile is not None:
|
||||
future_outfile = os.path.join(scandir, e(future_outfile))
|
||||
|
||||
target.scans['services'][self][plugin_tag]['commands'].append([cmd, outfile if outfile is not None else future_outfile, errfile])
|
||||
|
||||
async with target.lock:
|
||||
with open(os.path.join(target.scandir, '_commands.log'), 'a') as file:
|
||||
file.writelines(cmd + '\n\n')
|
||||
|
||||
process, stdout, stderr = await target.autorecon.execute(cmd, target, tag, patterns=plugin.patterns, outfile=outfile, errfile=errfile)
|
||||
|
||||
target.running_tasks[tag]['processes'].append({'process': process, 'stderr': stderr, 'cmd': cmd})
|
||||
|
||||
# If process should block, sleep until stdout and stderr have finished.
|
||||
if blocking:
|
||||
while (not (stdout.ended and stderr.ended)):
|
||||
await asyncio.sleep(0.1)
|
||||
await process.wait()
|
||||
|
||||
return process, stdout, stderr
|
|
@ -1,42 +1,54 @@
|
|||
[[package]]
|
||||
category = "main"
|
||||
description = "A small Python module for determining appropriate platform-specific dirs, e.g. a \"user data dir\"."
|
||||
name = "appdirs"
|
||||
version = "1.4.4"
|
||||
description = "A small Python module for determining appropriate platform-specific dirs, e.g. a \"user data dir\"."
|
||||
category = "main"
|
||||
optional = false
|
||||
python-versions = "*"
|
||||
version = "1.4.3"
|
||||
|
||||
[[package]]
|
||||
category = "main"
|
||||
description = "Cross-platform colored terminal text."
|
||||
name = "colorama"
|
||||
version = "0.4.4"
|
||||
description = "Cross-platform colored terminal text."
|
||||
category = "main"
|
||||
optional = false
|
||||
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*"
|
||||
version = "0.4.3"
|
||||
|
||||
[[package]]
|
||||
category = "main"
|
||||
description = "Python Library for Tom's Obvious, Minimal Language"
|
||||
name = "toml"
|
||||
version = "0.10.2"
|
||||
description = "Python Library for Tom's Obvious, Minimal Language"
|
||||
category = "main"
|
||||
optional = false
|
||||
python-versions = "*"
|
||||
version = "0.10.0"
|
||||
python-versions = ">=2.6, !=3.0.*, !=3.1.*, !=3.2.*"
|
||||
|
||||
[[package]]
|
||||
name = "unidecode"
|
||||
version = "1.3.1"
|
||||
description = "ASCII transliterations of Unicode text"
|
||||
category = "main"
|
||||
optional = false
|
||||
python-versions = ">=3.5"
|
||||
|
||||
[metadata]
|
||||
content-hash = "7359d313673568959d8011a312ea74ee6c3c3f3a2a5d81f690b87f6236d051ec"
|
||||
lock-version = "1.1"
|
||||
python-versions = "^3.7"
|
||||
content-hash = "681db41aa556d6d3f79e1e8ee0107bccd078e39c8db7e6e0159860c96ea93c5b"
|
||||
|
||||
[metadata.files]
|
||||
appdirs = [
|
||||
{file = "appdirs-1.4.3-py2.py3-none-any.whl", hash = "sha256:d8b24664561d0d34ddfaec54636d502d7cea6e29c3eaf68f3df6180863e2166e"},
|
||||
{file = "appdirs-1.4.3.tar.gz", hash = "sha256:9e5896d1372858f8dd3344faf4e5014d21849c756c8d5701f78f8a103b372d92"},
|
||||
{file = "appdirs-1.4.4-py2.py3-none-any.whl", hash = "sha256:a841dacd6b99318a741b166adb07e19ee71a274450e68237b4650ca1055ab128"},
|
||||
{file = "appdirs-1.4.4.tar.gz", hash = "sha256:7d5d0167b2b1ba821647616af46a749d1c653740dd0d2415100fe26e27afdf41"},
|
||||
]
|
||||
colorama = [
|
||||
{file = "colorama-0.4.3-py2.py3-none-any.whl", hash = "sha256:7d73d2a99753107a36ac6b455ee49046802e59d9d076ef8e47b61499fa29afff"},
|
||||
{file = "colorama-0.4.3.tar.gz", hash = "sha256:e96da0d330793e2cb9485e9ddfd918d456036c7149416295932478192f4436a1"},
|
||||
{file = "colorama-0.4.4-py2.py3-none-any.whl", hash = "sha256:9f47eda37229f68eee03b24b9748937c7dc3868f906e8ba69fbcbdd3bc5dc3e2"},
|
||||
{file = "colorama-0.4.4.tar.gz", hash = "sha256:5941b2b48a20143d2267e95b1c2a7603ce057ee39fd88e7329b0c292aa16869b"},
|
||||
]
|
||||
toml = [
|
||||
{file = "toml-0.10.0-py2.7.egg", hash = "sha256:f1db651f9657708513243e61e6cc67d101a39bad662eaa9b5546f789338e07a3"},
|
||||
{file = "toml-0.10.0-py2.py3-none-any.whl", hash = "sha256:235682dd292d5899d361a811df37e04a8828a5b1da3115886b73cf81ebc9100e"},
|
||||
{file = "toml-0.10.0.tar.gz", hash = "sha256:229f81c57791a41d65e399fc06bf0848bab550a9dfd5ed66df18ce5f05e73d5c"},
|
||||
{file = "toml-0.10.2-py2.py3-none-any.whl", hash = "sha256:806143ae5bfb6a3c6e736a764057db0e6a0e05e338b5630894a5f779cabb4f9b"},
|
||||
{file = "toml-0.10.2.tar.gz", hash = "sha256:b3bda1d108d5dd99f4a20d24d9c348e91c4db7ab1b749200bded2f839ccbe68f"},
|
||||
]
|
||||
unidecode = [
|
||||
{file = "Unidecode-1.3.1-py3-none-any.whl", hash = "sha256:5f58926b9125b499f8ab6816828e737578fa3e31fa24d351a3ab7f4b7c064ab0"},
|
||||
{file = "Unidecode-1.3.1.tar.gz", hash = "sha256:6efac090bf8f29970afc90caf4daae87b172709b786cb1b4da2d0c0624431ecc"},
|
||||
]
|
||||
|
|
|
@ -1,28 +1,26 @@
|
|||
[tool.poetry]
|
||||
name = "autorecon"
|
||||
version = "1.0.0"
|
||||
description = "A multi-threaded network reconaissance tool which performs automated enumeration of services"
|
||||
version = "2.0.0"
|
||||
description = "A multi-threaded network reconnaissance tool which performs automated enumeration of services."
|
||||
authors = ["Tib3rius"]
|
||||
license = "GPL-3.0-only"
|
||||
readme = "README.md"
|
||||
homepage = "https://github.com/Tib3rius/AutoRecon"
|
||||
repository = "https://github.com/Tib3rius/AutoRecon"
|
||||
license = "GNU GPL v3"
|
||||
exclude = ["autorecon.py"]
|
||||
packages = [
|
||||
{include = "autorecon", from = "src"},
|
||||
{ include = "autorecon" },
|
||||
]
|
||||
|
||||
[tool.poetry.dependencies]
|
||||
python = "^3.7"
|
||||
toml = "^0.10.0"
|
||||
colorama = "^0.4.3"
|
||||
appdirs = "^1.4.3"
|
||||
appdirs = "^1.4.4"
|
||||
colorama = "^0.4.4"
|
||||
toml = "^0.10.2"
|
||||
Unidecode = "^1.3.1"
|
||||
|
||||
[tool.poetry.dev-dependencies]
|
||||
|
||||
|
||||
[tool.poetry.scripts]
|
||||
autorecon = 'autorecon.autorecon:main'
|
||||
autorecon = "autorecon.main:main"
|
||||
|
||||
[build-system]
|
||||
requires = ["poetry>=0.12"]
|
||||
build-backend = "poetry.masonry.api"
|
||||
requires = ["poetry-core>=1.0.0"]
|
||||
build-backend = "poetry.core.masonry.api"
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
appdirs
|
||||
colorama
|
||||
toml
|
||||
appdirs
|
||||
unidecode
|
||||
|
|
|
@ -1,885 +0,0 @@
|
|||
#!/usr/bin/env python3
|
||||
#
|
||||
# AutoRecon is a multi-threaded network reconnaissance tool which performs automated enumeration of services.
|
||||
#
|
||||
# This program can be redistributed and/or modified under the terms of the
|
||||
# GNU General Public License, either version 3 of the License, or (at your
|
||||
# option) any later version.
|
||||
#
|
||||
|
||||
import atexit
|
||||
import argparse
|
||||
import asyncio
|
||||
import colorama
|
||||
from colorama import Fore, Style
|
||||
from concurrent.futures import ProcessPoolExecutor, as_completed, FIRST_COMPLETED
|
||||
from datetime import datetime
|
||||
import ipaddress
|
||||
import os
|
||||
import re
|
||||
import socket
|
||||
import string
|
||||
import sys
|
||||
import time
|
||||
import toml
|
||||
import termios
|
||||
import appdirs
|
||||
import shutil
|
||||
|
||||
# Globals
|
||||
verbose = 0
|
||||
nmap = "-vv --reason -Pn"
|
||||
srvname = ""
|
||||
heartbeat_interval = 60
|
||||
port_scan_profile = None
|
||||
port_scan_profiles_config = None
|
||||
service_scans_config = None
|
||||
global_patterns = []
|
||||
username_wordlist = "/usr/share/seclists/Usernames/top-usernames-shortlist.txt"
|
||||
password_wordlist = "/usr/share/seclists/Passwords/darkweb2017-top100.txt"
|
||||
single_target = False
|
||||
only_scans_dir = False
|
||||
|
||||
|
||||
def _quit():
|
||||
TERM_FLAGS = termios.tcgetattr(sys.stdin.fileno())
|
||||
termios.tcsetattr(sys.stdin.fileno(), termios.TCSADRAIN, TERM_FLAGS)
|
||||
|
||||
|
||||
def _init():
|
||||
global port_scan_profiles_config
|
||||
global service_scans_config
|
||||
global global_patterns
|
||||
|
||||
atexit.register(_quit)
|
||||
appname = "AutoRecon"
|
||||
rootdir = os.path.dirname(os.path.realpath(__file__))
|
||||
default_config_dir = os.path.join(rootdir, "config")
|
||||
config_dir = appdirs.user_config_dir(appname)
|
||||
port_scan_profiles_config_file = os.path.join(config_dir, "port-scan-profiles.toml")
|
||||
service_scans_config_file = os.path.join(config_dir, "service-scans.toml")
|
||||
global_patterns_config_file = os.path.join(config_dir, "global-patterns.toml")
|
||||
|
||||
# Confirm this directory exists; if not, populate it with the default configurations
|
||||
if not os.path.exists(config_dir):
|
||||
os.makedirs(config_dir, exist_ok=True)
|
||||
shutil.copy(
|
||||
os.path.join(default_config_dir, "port-scan-profiles-default.toml"),
|
||||
port_scan_profiles_config_file,
|
||||
)
|
||||
shutil.copy(
|
||||
os.path.join(default_config_dir, "service-scans-default.toml"),
|
||||
service_scans_config_file,
|
||||
)
|
||||
shutil.copy(
|
||||
os.path.join(default_config_dir, "global-patterns-default.toml"),
|
||||
global_patterns_config_file,
|
||||
)
|
||||
|
||||
|
||||
with open(port_scan_profiles_config_file, "r") as p:
|
||||
try:
|
||||
port_scan_profiles_config = toml.load(p)
|
||||
|
||||
if len(port_scan_profiles_config) == 0:
|
||||
fail(
|
||||
"There do not appear to be any port scan profiles configured in the {port_scan_profiles_config_file} config file."
|
||||
)
|
||||
|
||||
except toml.decoder.TomlDecodeError as e:
|
||||
fail(
|
||||
"Error: Couldn't parse {port_scan_profiles_config_file} config file. Check syntax and duplicate tags."
|
||||
)
|
||||
|
||||
with open(service_scans_config_file, "r") as c:
|
||||
try:
|
||||
service_scans_config = toml.load(c)
|
||||
except toml.decoder.TomlDecodeError as e:
|
||||
fail(
|
||||
"Error: Couldn't parse service-scans.toml config file. Check syntax and duplicate tags."
|
||||
)
|
||||
|
||||
with open(global_patterns_config_file, "r") as p:
|
||||
try:
|
||||
global_patterns = toml.load(p)
|
||||
if "pattern" in global_patterns:
|
||||
global_patterns = global_patterns["pattern"]
|
||||
else:
|
||||
global_patterns = []
|
||||
except toml.decoder.TomlDecodeError as e:
|
||||
fail(
|
||||
"Error: Couldn't parse global-patterns.toml config file. Check syntax and duplicate tags."
|
||||
)
|
||||
|
||||
if "username_wordlist" in service_scans_config:
|
||||
if isinstance(service_scans_config["username_wordlist"], str):
|
||||
username_wordlist = service_scans_config["username_wordlist"]
|
||||
|
||||
if "password_wordlist" in service_scans_config:
|
||||
if isinstance(service_scans_config["password_wordlist"], str):
|
||||
password_wordlist = service_scans_config["password_wordlist"]
|
||||
|
||||
|
||||
def e(*args, frame_index=1, **kvargs):
|
||||
frame = sys._getframe(frame_index)
|
||||
|
||||
vals = {}
|
||||
|
||||
vals.update(frame.f_globals)
|
||||
vals.update(frame.f_locals)
|
||||
vals.update(kvargs)
|
||||
|
||||
return string.Formatter().vformat(' '.join(args), args, vals)
|
||||
|
||||
def cprint(*args, color=Fore.RESET, char='*', sep=' ', end='\n', frame_index=1, file=sys.stdout, **kvargs):
|
||||
frame = sys._getframe(frame_index)
|
||||
|
||||
vals = {
|
||||
'bgreen': Fore.GREEN + Style.BRIGHT,
|
||||
'bred': Fore.RED + Style.BRIGHT,
|
||||
'bblue': Fore.BLUE + Style.BRIGHT,
|
||||
'byellow': Fore.YELLOW + Style.BRIGHT,
|
||||
'bmagenta': Fore.MAGENTA + Style.BRIGHT,
|
||||
|
||||
'green': Fore.GREEN,
|
||||
'red': Fore.RED,
|
||||
'blue': Fore.BLUE,
|
||||
'yellow': Fore.YELLOW,
|
||||
'magenta': Fore.MAGENTA,
|
||||
|
||||
'bright': Style.BRIGHT,
|
||||
'srst': Style.NORMAL,
|
||||
'crst': Fore.RESET,
|
||||
'rst': Style.NORMAL + Fore.RESET
|
||||
}
|
||||
|
||||
vals.update(frame.f_globals)
|
||||
vals.update(frame.f_locals)
|
||||
vals.update(kvargs)
|
||||
|
||||
unfmt = ''
|
||||
if char is not None:
|
||||
unfmt += color + '[' + Style.BRIGHT + char + Style.NORMAL + ']' + Fore.RESET + sep
|
||||
unfmt += sep.join(args)
|
||||
|
||||
fmted = unfmt
|
||||
|
||||
for attempt in range(10):
|
||||
try:
|
||||
fmted = string.Formatter().vformat(unfmt, args, vals)
|
||||
break
|
||||
except KeyError as err:
|
||||
key = err.args[0]
|
||||
unfmt = unfmt.replace('{' + key + '}', '{{' + key + '}}')
|
||||
|
||||
print(fmted, sep=sep, end=end, file=file)
|
||||
|
||||
def debug(*args, color=Fore.BLUE, sep=' ', end='\n', file=sys.stdout, **kvargs):
|
||||
if verbose >= 2:
|
||||
cprint(*args, color=color, char='-', sep=sep, end=end, file=file, frame_index=2, **kvargs)
|
||||
|
||||
def info(*args, sep=' ', end='\n', file=sys.stdout, **kvargs):
|
||||
cprint(*args, color=Fore.GREEN, char='*', sep=sep, end=end, file=file, frame_index=2, **kvargs)
|
||||
|
||||
def warn(*args, sep=' ', end='\n', file=sys.stderr, **kvargs):
|
||||
cprint(*args, color=Fore.YELLOW, char='!', sep=sep, end=end, file=file, frame_index=2, **kvargs)
|
||||
|
||||
def error(*args, sep=' ', end='\n', file=sys.stderr, **kvargs):
|
||||
cprint(*args, color=Fore.RED, char='!', sep=sep, end=end, file=file, frame_index=2, **kvargs)
|
||||
|
||||
def fail(*args, sep=' ', end='\n', file=sys.stderr, **kvargs):
|
||||
cprint(*args, color=Fore.RED, char='!', sep=sep, end=end, file=file, frame_index=2, **kvargs)
|
||||
exit(-1)
|
||||
|
||||
def calculate_elapsed_time(start_time):
|
||||
elapsed_seconds = round(time.time() - start_time)
|
||||
|
||||
m, s = divmod(elapsed_seconds, 60)
|
||||
h, m = divmod(m, 60)
|
||||
|
||||
elapsed_time = []
|
||||
if h == 1:
|
||||
elapsed_time.append(str(h) + ' hour')
|
||||
elif h > 1:
|
||||
elapsed_time.append(str(h) + ' hours')
|
||||
|
||||
if m == 1:
|
||||
elapsed_time.append(str(m) + ' minute')
|
||||
elif m > 1:
|
||||
elapsed_time.append(str(m) + ' minutes')
|
||||
|
||||
if s == 1:
|
||||
elapsed_time.append(str(s) + ' second')
|
||||
elif s > 1:
|
||||
elapsed_time.append(str(s) + ' seconds')
|
||||
else:
|
||||
elapsed_time.append('less than a second')
|
||||
|
||||
return ', '.join(elapsed_time)
|
||||
|
||||
|
||||
async def read_stream(stream, target, tag='?', patterns=[], color=Fore.BLUE):
|
||||
address = target.address
|
||||
while True:
|
||||
line = ""
|
||||
try:
|
||||
line = await stream.readline()
|
||||
except ValueError:
|
||||
continue
|
||||
|
||||
if line:
|
||||
line = str(line.rstrip(), 'utf8', 'ignore')
|
||||
debug(color + '[' + Style.BRIGHT + address + ' ' + tag + Style.NORMAL + '] ' + Fore.RESET + '{line}', color=color)
|
||||
|
||||
for p in global_patterns:
|
||||
matches = re.findall(p['pattern'], line)
|
||||
if 'description' in p:
|
||||
for match in matches:
|
||||
if verbose >= 1:
|
||||
info('Task {bgreen}{tag}{rst} on {byellow}{address}{rst} - {bmagenta}' + p['description'].replace('{match}', '{bblue}{match}{crst}{bmagenta}') + '{rst}')
|
||||
async with target.lock:
|
||||
with open(os.path.join(target.scandir, '_patterns.log'), 'a') as file:
|
||||
file.writelines(e('{tag} - ' + p['description'] + '\n\n'))
|
||||
else:
|
||||
for match in matches:
|
||||
if verbose >= 1:
|
||||
info('Task {bgreen}{tag}{rst} on {byellow}{address}{rst} - {bmagenta}Matched Pattern: {bblue}{match}{rst}')
|
||||
async with target.lock:
|
||||
with open(os.path.join(target.scandir, '_patterns.log'), 'a') as file:
|
||||
file.writelines(e('{tag} - Matched Pattern: {match}\n\n'))
|
||||
|
||||
for p in patterns:
|
||||
matches = re.findall(p['pattern'], line)
|
||||
if 'description' in p:
|
||||
for match in matches:
|
||||
if verbose >= 1:
|
||||
info('Task {bgreen}{tag}{rst} on {byellow}{address}{rst} - {bmagenta}' + p['description'].replace('{match}', '{bblue}{match}{crst}{bmagenta}') + '{rst}')
|
||||
async with target.lock:
|
||||
with open(os.path.join(target.scandir, '_patterns.log'), 'a') as file:
|
||||
file.writelines(e('{tag} - ' + p['description'] + '\n\n'))
|
||||
else:
|
||||
for match in matches:
|
||||
if verbose >= 1:
|
||||
info('Task {bgreen}{tag}{rst} on {byellow}{address}{rst} - {bmagenta}Matched Pattern: {bblue}{match}{rst}')
|
||||
async with target.lock:
|
||||
with open(os.path.join(target.scandir, '_patterns.log'), 'a') as file:
|
||||
file.writelines(e('{tag} - Matched Pattern: {match}\n\n'))
|
||||
else:
|
||||
break
|
||||
|
||||
async def run_cmd(semaphore, cmd, target, tag='?', patterns=[]):
|
||||
async with semaphore:
|
||||
address = target.address
|
||||
scandir = target.scandir
|
||||
|
||||
info('Running task {bgreen}{tag}{rst} on {byellow}{address}{rst}' + (' with {bblue}{cmd}{rst}' if verbose >= 1 else ''))
|
||||
|
||||
async with target.lock:
|
||||
with open(os.path.join(scandir, '_commands.log'), 'a') as file:
|
||||
file.writelines(e('{cmd}\n\n'))
|
||||
|
||||
start_time = time.time()
|
||||
process = await asyncio.create_subprocess_shell(cmd, stdout=asyncio.subprocess.PIPE, stderr=asyncio.subprocess.PIPE, executable='/bin/bash')
|
||||
async with target.lock:
|
||||
target.running_tasks.append(tag)
|
||||
|
||||
await asyncio.wait([
|
||||
read_stream(process.stdout, target, tag=tag, patterns=patterns),
|
||||
read_stream(process.stderr, target, tag=tag, patterns=patterns, color=Fore.RED)
|
||||
])
|
||||
|
||||
await process.wait()
|
||||
async with target.lock:
|
||||
target.running_tasks.remove(tag)
|
||||
elapsed_time = calculate_elapsed_time(start_time)
|
||||
|
||||
if process.returncode != 0:
|
||||
error('Task {bred}{tag}{rst} on {byellow}{address}{rst} returned non-zero exit code: {process.returncode}')
|
||||
async with target.lock:
|
||||
with open(os.path.join(scandir, '_errors.log'), 'a') as file:
|
||||
file.writelines(e('[*] Task {tag} returned non-zero exit code: {process.returncode}. Command: {cmd}\n'))
|
||||
else:
|
||||
info('Task {bgreen}{tag}{rst} on {byellow}{address}{rst} finished successfully in {elapsed_time}')
|
||||
|
||||
return {'returncode': process.returncode, 'name': 'run_cmd'}
|
||||
|
||||
async def parse_port_scan(stream, tag, target, pattern):
|
||||
address = target.address
|
||||
ports = []
|
||||
|
||||
while True:
|
||||
line = await stream.readline()
|
||||
if line:
|
||||
line = str(line.rstrip(), 'utf8', 'ignore')
|
||||
debug(Fore.BLUE + '[' + Style.BRIGHT + address + ' ' + tag + Style.NORMAL + '] ' + Fore.RESET + '{line}', color=Fore.BLUE)
|
||||
|
||||
parse_match = re.search(pattern, line)
|
||||
if parse_match:
|
||||
ports.append(parse_match.group('port'))
|
||||
|
||||
|
||||
for p in global_patterns:
|
||||
matches = re.findall(p['pattern'], line)
|
||||
if 'description' in p:
|
||||
for match in matches:
|
||||
if verbose >= 1:
|
||||
info('Task {bgreen}{tag}{rst} on {byellow}{address}{rst} - {bmagenta}' + p['description'].replace('{match}', '{bblue}{match}{crst}{bmagenta}') + '{rst}')
|
||||
async with target.lock:
|
||||
with open(os.path.join(target.scandir, '_patterns.log'), 'a') as file:
|
||||
file.writelines(e('{tag} - ' + p['description'] + '\n\n'))
|
||||
else:
|
||||
for match in matches:
|
||||
if verbose >= 1:
|
||||
info('Task {bgreen}{tag}{rst} on {byellow}{address}{rst} - {bmagenta}Matched Pattern: {bblue}{match}{rst}')
|
||||
async with target.lock:
|
||||
with open(os.path.join(target.scandir, '_patterns.log'), 'a') as file:
|
||||
file.writelines(e('{tag} - Matched Pattern: {match}\n\n'))
|
||||
else:
|
||||
break
|
||||
|
||||
return ports
|
||||
|
||||
async def parse_service_detection(stream, tag, target, pattern):
|
||||
address = target.address
|
||||
services = []
|
||||
|
||||
while True:
|
||||
line = await stream.readline()
|
||||
if line:
|
||||
line = str(line.rstrip(), 'utf8', 'ignore')
|
||||
debug(Fore.BLUE + '[' + Style.BRIGHT + address + ' ' + tag + Style.NORMAL + '] ' + Fore.RESET + '{line}', color=Fore.BLUE)
|
||||
|
||||
parse_match = re.search(pattern, line)
|
||||
if parse_match:
|
||||
services.append((parse_match.group('protocol').lower(), int(parse_match.group('port')), parse_match.group('service')))
|
||||
|
||||
for p in global_patterns:
|
||||
matches = re.findall(p['pattern'], line)
|
||||
if 'description' in p:
|
||||
for match in matches:
|
||||
if verbose >= 1:
|
||||
info('Task {bgreen}{tag}{rst} on {byellow}{address}{rst} - {bmagenta}' + p['description'].replace('{match}', '{bblue}{match}{crst}{bmagenta}') + '{rst}')
|
||||
async with target.lock:
|
||||
with open(os.path.join(target.scandir, '_patterns.log'), 'a') as file:
|
||||
file.writelines(e('{tag} - ' + p['description'] + '\n\n'))
|
||||
else:
|
||||
for match in matches:
|
||||
if verbose >= 1:
|
||||
info('Task {bgreen}{tag}{rst} on {byellow}{address}{rst} - {bmagenta}Matched Pattern: {bblue}{match}{rst}')
|
||||
async with target.lock:
|
||||
with open(os.path.join(target.scandir, '_patterns.log'), 'a') as file:
|
||||
file.writelines(e('{tag} - Matched Pattern: {match}\n\n'))
|
||||
else:
|
||||
break
|
||||
|
||||
return services
|
||||
|
||||
async def run_portscan(semaphore, tag, target, service_detection, port_scan=None):
|
||||
async with semaphore:
|
||||
|
||||
address = target.address
|
||||
scandir = target.scandir
|
||||
nmap_extra = nmap
|
||||
|
||||
ports = ''
|
||||
if port_scan is not None:
|
||||
command = e(port_scan[0])
|
||||
pattern = port_scan[1]
|
||||
|
||||
info('Running port scan {bgreen}{tag}{rst} on {byellow}{address}{rst}' + (' with {bblue}{command}{rst}' if verbose >= 1 else ''))
|
||||
|
||||
async with target.lock:
|
||||
with open(os.path.join(scandir, '_commands.log'), 'a') as file:
|
||||
file.writelines(e('{command}\n\n'))
|
||||
|
||||
start_time = time.time()
|
||||
process = await asyncio.create_subprocess_shell(command, stdout=asyncio.subprocess.PIPE, stderr=asyncio.subprocess.PIPE, executable='/bin/bash')
|
||||
async with target.lock:
|
||||
target.running_tasks.append(tag)
|
||||
|
||||
output = [
|
||||
parse_port_scan(process.stdout, tag, target, pattern),
|
||||
read_stream(process.stderr, target, tag=tag, color=Fore.RED)
|
||||
]
|
||||
|
||||
results = await asyncio.gather(*output)
|
||||
|
||||
await process.wait()
|
||||
async with target.lock:
|
||||
target.running_tasks.remove(tag)
|
||||
elapsed_time = calculate_elapsed_time(start_time)
|
||||
|
||||
if process.returncode != 0:
|
||||
error('Port scan {bred}{tag}{rst} on {byellow}{address}{rst} returned non-zero exit code: {process.returncode}')
|
||||
async with target.lock:
|
||||
with open(os.path.join(scandir, '_errors.log'), 'a') as file:
|
||||
file.writelines(e('[*] Port scan {tag} returned non-zero exit code: {process.returncode}. Command: {command}\n'))
|
||||
return {'returncode': process.returncode}
|
||||
else:
|
||||
info('Port scan {bgreen}{tag}{rst} on {byellow}{address}{rst} finished successfully in {elapsed_time}')
|
||||
|
||||
ports = results[0]
|
||||
if len(ports) == 0:
|
||||
return {'returncode': -1}
|
||||
|
||||
ports = ','.join(ports)
|
||||
|
||||
command = e(service_detection[0])
|
||||
pattern = service_detection[1]
|
||||
|
||||
info('Running service detection {bgreen}{tag}{rst} on {byellow}{address}{rst}' + (' with {bblue}{command}{rst}' if verbose >= 1 else ''))
|
||||
|
||||
async with target.lock:
|
||||
with open(os.path.join(scandir, '_commands.log'), 'a') as file:
|
||||
file.writelines(e('{command}\n\n'))
|
||||
|
||||
start_time = time.time()
|
||||
process = await asyncio.create_subprocess_shell(command, stdout=asyncio.subprocess.PIPE, stderr=asyncio.subprocess.PIPE, executable='/bin/bash')
|
||||
async with target.lock:
|
||||
target.running_tasks.append(tag)
|
||||
|
||||
output = [
|
||||
parse_service_detection(process.stdout, tag, target, pattern),
|
||||
read_stream(process.stderr, target, tag=tag, color=Fore.RED)
|
||||
]
|
||||
|
||||
results = await asyncio.gather(*output)
|
||||
|
||||
await process.wait()
|
||||
async with target.lock:
|
||||
target.running_tasks.remove(tag)
|
||||
elapsed_time = calculate_elapsed_time(start_time)
|
||||
|
||||
if process.returncode != 0:
|
||||
error('Service detection {bred}{tag}{rst} on {byellow}{address}{rst} returned non-zero exit code: {process.returncode}')
|
||||
async with target.lock:
|
||||
with open(os.path.join(scandir, '_errors.log'), 'a') as file:
|
||||
file.writelines(e('[*] Service detection {tag} returned non-zero exit code: {process.returncode}. Command: {command}\n'))
|
||||
else:
|
||||
info('Service detection {bgreen}{tag}{rst} on {byellow}{address}{rst} finished successfully in {elapsed_time}')
|
||||
|
||||
services = results[0]
|
||||
|
||||
return {'returncode': process.returncode, 'name': 'run_portscan', 'services': services}
|
||||
|
||||
async def start_heartbeat(target, period=60):
|
||||
while True:
|
||||
await asyncio.sleep(period)
|
||||
async with target.lock:
|
||||
tasks = target.running_tasks
|
||||
count = len(tasks)
|
||||
|
||||
tasks_list = ''
|
||||
if verbose >= 1:
|
||||
tasks_list = ': {bgreen}' + ', '.join(tasks) + '{rst}'
|
||||
|
||||
current_time = datetime.now().strftime('%H:%M:%S')
|
||||
|
||||
if count > 1:
|
||||
info('{bgreen}[{current_time}]{rst} - There are {byellow}{count}{rst} tasks still running on {byellow}{target.address}{rst}' + tasks_list)
|
||||
elif count == 1:
|
||||
info('{bgreen}[{current_time}]{rst} - There is {byellow}1{rst} task still running on {byellow}{target.address}{rst}' + tasks_list)
|
||||
|
||||
async def scan_services(loop, semaphore, target):
|
||||
address = target.address
|
||||
scandir = target.scandir
|
||||
pending = []
|
||||
|
||||
heartbeat = loop.create_task(start_heartbeat(target, period=heartbeat_interval))
|
||||
|
||||
for profile in port_scan_profiles_config:
|
||||
if profile == port_scan_profile:
|
||||
for scan in port_scan_profiles_config[profile]:
|
||||
service_detection = (port_scan_profiles_config[profile][scan]['service-detection']['command'], port_scan_profiles_config[profile][scan]['service-detection']['pattern'])
|
||||
if 'port-scan' in port_scan_profiles_config[profile][scan]:
|
||||
port_scan = (port_scan_profiles_config[profile][scan]['port-scan']['command'], port_scan_profiles_config[profile][scan]['port-scan']['pattern'])
|
||||
pending.append(run_portscan(semaphore, scan, target, service_detection, port_scan))
|
||||
else:
|
||||
pending.append(run_portscan(semaphore, scan, target, service_detection))
|
||||
break
|
||||
|
||||
services = []
|
||||
|
||||
while True:
|
||||
if not pending:
|
||||
heartbeat.cancel()
|
||||
break
|
||||
|
||||
done, pending = await asyncio.wait(pending, return_when=FIRST_COMPLETED)
|
||||
|
||||
for task in done:
|
||||
result = task.result()
|
||||
|
||||
if result['returncode'] == 0:
|
||||
if result['name'] == 'run_portscan':
|
||||
for service_tuple in result['services']:
|
||||
if service_tuple not in services:
|
||||
services.append(service_tuple)
|
||||
else:
|
||||
continue
|
||||
|
||||
protocol = service_tuple[0]
|
||||
port = service_tuple[1]
|
||||
service = service_tuple[2]
|
||||
|
||||
info('Found {bmagenta}{service}{rst} on {bmagenta}{protocol}/{port}{rst} on target {byellow}{address}{rst}')
|
||||
|
||||
if not only_scans_dir:
|
||||
with open(os.path.join(target.reportdir, 'notes.txt'), 'a') as file:
|
||||
file.writelines(e('[*] {service} found on {protocol}/{port}.\n\n\n\n'))
|
||||
|
||||
if protocol == 'udp':
|
||||
nmap_extra = nmap + " -sU"
|
||||
else:
|
||||
nmap_extra = nmap
|
||||
|
||||
secure = True if 'ssl' in service or 'tls' in service else False
|
||||
|
||||
# Special cases for HTTP.
|
||||
scheme = 'https' if 'https' in service or 'ssl' in service or 'tls' in service else 'http'
|
||||
|
||||
if service.startswith('ssl/') or service.startswith('tls/'):
|
||||
service = service[4:]
|
||||
|
||||
for service_scan in service_scans_config:
|
||||
# Skip over configurable variables since the python toml parser cannot iterate over tables only.
|
||||
if service_scan in ['username_wordlist', 'password_wordlist']:
|
||||
continue
|
||||
|
||||
ignore_service = False
|
||||
if 'ignore-service-names' in service_scans_config[service_scan]:
|
||||
for ignore_service_name in service_scans_config[service_scan]['ignore-service-names']:
|
||||
if re.search(ignore_service_name, service):
|
||||
ignore_service = True
|
||||
break
|
||||
|
||||
if ignore_service:
|
||||
continue
|
||||
|
||||
matched_service = False
|
||||
|
||||
if 'service-names' in service_scans_config[service_scan]:
|
||||
for service_name in service_scans_config[service_scan]['service-names']:
|
||||
if re.search(service_name, service):
|
||||
matched_service = True
|
||||
break
|
||||
|
||||
if not matched_service:
|
||||
continue
|
||||
|
||||
if 'manual' in service_scans_config[service_scan]:
|
||||
heading = False
|
||||
with open(os.path.join(scandir, '_manual_commands.txt'), 'a') as file:
|
||||
for manual in service_scans_config[service_scan]['manual']:
|
||||
if 'description' in manual:
|
||||
if not heading:
|
||||
file.writelines(e('[*] {service} on {protocol}/{port}\n\n'))
|
||||
heading = True
|
||||
description = manual['description']
|
||||
file.writelines(e('\t[-] {description}\n\n'))
|
||||
if 'commands' in manual:
|
||||
if not heading:
|
||||
file.writelines(e('[*] {service} on {protocol}/{port}\n\n'))
|
||||
heading = True
|
||||
for manual_command in manual['commands']:
|
||||
manual_command = e(manual_command)
|
||||
file.writelines('\t\t' + e('{manual_command}\n\n'))
|
||||
if heading:
|
||||
file.writelines('\n')
|
||||
|
||||
if 'scan' in service_scans_config[service_scan]:
|
||||
for scan in service_scans_config[service_scan]['scan']:
|
||||
|
||||
if 'name' in scan:
|
||||
name = scan['name']
|
||||
if 'command' in scan:
|
||||
tag = e('{protocol}/{port}/{name}')
|
||||
command = scan['command']
|
||||
|
||||
if 'ports' in scan:
|
||||
port_match = False
|
||||
|
||||
if protocol == 'tcp':
|
||||
if 'tcp' in scan['ports']:
|
||||
for tcp_port in scan['ports']['tcp']:
|
||||
if port == tcp_port:
|
||||
port_match = True
|
||||
break
|
||||
elif protocol == 'udp':
|
||||
if 'udp' in scan['ports']:
|
||||
for udp_port in scan['ports']['udp']:
|
||||
if port == udp_port:
|
||||
port_match = True
|
||||
break
|
||||
|
||||
if port_match == False:
|
||||
warn(Fore.YELLOW + '[' + Style.BRIGHT + tag + Style.NORMAL + '] Scan cannot be run against {protocol} port {port}. Skipping.' + Fore.RESET)
|
||||
continue
|
||||
|
||||
if 'run_once' in scan and scan['run_once'] == True:
|
||||
scan_tuple = (name,)
|
||||
if scan_tuple in target.scans:
|
||||
warn(Fore.YELLOW + '[' + Style.BRIGHT + tag + ' on ' + address + Style.NORMAL + '] Scan should only be run once and it appears to have already been queued. Skipping.' + Fore.RESET)
|
||||
continue
|
||||
else:
|
||||
target.scans.append(scan_tuple)
|
||||
else:
|
||||
scan_tuple = (protocol, port, service, name)
|
||||
if scan_tuple in target.scans:
|
||||
warn(Fore.YELLOW + '[' + Style.BRIGHT + tag + ' on ' + address + Style.NORMAL + '] Scan appears to have already been queued, but it is not marked as run_once in service-scans.toml. Possible duplicate tag? Skipping.' + Fore.RESET)
|
||||
continue
|
||||
else:
|
||||
target.scans.append(scan_tuple)
|
||||
|
||||
patterns = []
|
||||
if 'pattern' in scan:
|
||||
patterns = scan['pattern']
|
||||
|
||||
pending.add(asyncio.ensure_future(run_cmd(semaphore, e(command), target, tag=tag, patterns=patterns)))
|
||||
|
||||
def scan_host(target, concurrent_scans, outdir):
|
||||
start_time = time.time()
|
||||
info('Scanning target {byellow}{target.address}{rst}')
|
||||
|
||||
if single_target:
|
||||
basedir = os.path.abspath(outdir)
|
||||
else:
|
||||
basedir = os.path.abspath(os.path.join(outdir, target.address + srvname))
|
||||
target.basedir = basedir
|
||||
os.makedirs(basedir, exist_ok=True)
|
||||
|
||||
if not only_scans_dir:
|
||||
exploitdir = os.path.abspath(os.path.join(basedir, 'exploit'))
|
||||
os.makedirs(exploitdir, exist_ok=True)
|
||||
|
||||
lootdir = os.path.abspath(os.path.join(basedir, 'loot'))
|
||||
os.makedirs(lootdir, exist_ok=True)
|
||||
|
||||
reportdir = os.path.abspath(os.path.join(basedir, 'report'))
|
||||
target.reportdir = reportdir
|
||||
os.makedirs(reportdir, exist_ok=True)
|
||||
|
||||
open(os.path.abspath(os.path.join(reportdir, 'local.txt')), 'a').close()
|
||||
open(os.path.abspath(os.path.join(reportdir, 'proof.txt')), 'a').close()
|
||||
|
||||
screenshotdir = os.path.abspath(os.path.join(reportdir, 'screenshots'))
|
||||
os.makedirs(screenshotdir, exist_ok=True)
|
||||
|
||||
scandir = os.path.abspath(os.path.join(basedir, 'scans'))
|
||||
target.scandir = scandir
|
||||
os.makedirs(scandir, exist_ok=True)
|
||||
|
||||
os.makedirs(os.path.abspath(os.path.join(scandir, 'xml')), exist_ok=True)
|
||||
|
||||
# Use a lock when writing to specific files that may be written to by other asynchronous functions.
|
||||
target.lock = asyncio.Lock()
|
||||
|
||||
# Get event loop for current process.
|
||||
loop = asyncio.get_event_loop()
|
||||
|
||||
# Create a semaphore to limit number of concurrent scans.
|
||||
semaphore = asyncio.Semaphore(concurrent_scans)
|
||||
|
||||
try:
|
||||
loop.run_until_complete(scan_services(loop, semaphore, target))
|
||||
elapsed_time = calculate_elapsed_time(start_time)
|
||||
info('Finished scanning target {byellow}{target.address}{rst} in {elapsed_time}')
|
||||
except KeyboardInterrupt:
|
||||
sys.exit(1)
|
||||
|
||||
class Target:
|
||||
def __init__(self, address):
|
||||
self.address = address
|
||||
self.basedir = ''
|
||||
self.reportdir = ''
|
||||
self.scandir = ''
|
||||
self.scans = []
|
||||
self.lock = None
|
||||
self.running_tasks = []
|
||||
|
||||
|
||||
|
||||
def main():
|
||||
global single_target
|
||||
global only_scans_dir
|
||||
global port_scan_profile
|
||||
global heartbeat_interval
|
||||
global nmap
|
||||
global srvname
|
||||
global verbose
|
||||
|
||||
_init()
|
||||
parser = argparse.ArgumentParser(description='Network reconnaissance tool to port scan and automatically enumerate services found on multiple targets.')
|
||||
parser.add_argument('targets', action='store', help='IP addresses (e.g. 10.0.0.1), CIDR notation (e.g. 10.0.0.1/24), or resolvable hostnames (e.g. foo.bar) to scan.', nargs="*")
|
||||
parser.add_argument('-t', '--targets', action='store', type=str, default='', dest='target_file', help='Read targets from file.')
|
||||
parser.add_argument('-ct', '--concurrent-targets', action='store', metavar='<number>', type=int, default=5, help='The maximum number of target hosts to scan concurrently. Default: %(default)s')
|
||||
parser.add_argument('-cs', '--concurrent-scans', action='store', metavar='<number>', type=int, default=10, help='The maximum number of scans to perform per target host. Default: %(default)s')
|
||||
parser.add_argument('--profile', action='store', default='default', dest='profile_name', help='The port scanning profile to use (defined in port-scan-profiles.toml). Default: %(default)s')
|
||||
parser.add_argument('-o', '--output', action='store', default='results', dest='output_dir', help='The output directory for results. Default: %(default)s')
|
||||
parser.add_argument('--single-target', action='store_true', default=False, help='Only scan a single target. A directory named after the target will not be created. Instead, the directory structure will be created within the output directory. Default: false')
|
||||
parser.add_argument('--only-scans-dir', action='store_true', default=False, help='Only create the "scans" directory for results. Other directories (e.g. exploit, loot, report) will not be created. Default: false')
|
||||
parser.add_argument('--heartbeat', action='store', type=int, default=60, help='Specifies the heartbeat interval (in seconds) for task status messages. Default: %(default)s')
|
||||
nmap_group = parser.add_mutually_exclusive_group()
|
||||
nmap_group.add_argument('--nmap', action='store', default='-vv --reason -Pn', help='Override the {nmap_extra} variable in scans. Default: %(default)s')
|
||||
nmap_group.add_argument('--nmap-append', action='store', default='', help='Append to the default {nmap_extra} variable in scans.')
|
||||
parser.add_argument('-v', '--verbose', action='count', default=0, help='Enable verbose output. Repeat for more verbosity.')
|
||||
parser.add_argument('--disable-sanity-checks', action='store_true', default=False, help='Disable sanity checks that would otherwise prevent the scans from running. Default: false')
|
||||
parser.error = lambda s: fail(s[0].upper() + s[1:])
|
||||
args = parser.parse_args()
|
||||
|
||||
single_target = args.single_target
|
||||
only_scans_dir = args.only_scans_dir
|
||||
|
||||
errors = False
|
||||
|
||||
if args.concurrent_targets <= 0:
|
||||
error('Argument -ch/--concurrent-targets: must be at least 1.')
|
||||
errors = True
|
||||
|
||||
concurrent_scans = args.concurrent_scans
|
||||
|
||||
if concurrent_scans <= 0:
|
||||
error('Argument -ct/--concurrent-scans: must be at least 1.')
|
||||
errors = True
|
||||
|
||||
port_scan_profile = args.profile_name
|
||||
|
||||
found_scan_profile = False
|
||||
for profile in port_scan_profiles_config:
|
||||
if profile == port_scan_profile:
|
||||
found_scan_profile = True
|
||||
for scan in port_scan_profiles_config[profile]:
|
||||
if 'service-detection' not in port_scan_profiles_config[profile][scan]:
|
||||
error('The {profile}.{scan} scan does not have a defined service-detection section. Every scan must at least have a service-detection section defined with a command and a corresponding pattern that extracts the protocol (TCP/UDP), port, and service from the result.')
|
||||
errors = True
|
||||
else:
|
||||
if 'command' not in port_scan_profiles_config[profile][scan]['service-detection']:
|
||||
error('The {profile}.{scan}.service-detection section does not have a command defined. Every service-detection section must have a command and a corresponding pattern that extracts the protocol (TCP/UDP), port, and service from the results.')
|
||||
errors = True
|
||||
else:
|
||||
if '{ports}' in port_scan_profiles_config[profile][scan]['service-detection']['command'] and 'port-scan' not in port_scan_profiles_config[profile][scan]:
|
||||
error('The {profile}.{scan}.service-detection command appears to reference a port list but there is no port-scan section defined in {profile}.{scan}. Define a port-scan section with a command and corresponding pattern that extracts port numbers from the result, or replace the reference with a static list of ports.')
|
||||
errors = True
|
||||
|
||||
if 'pattern' not in port_scan_profiles_config[profile][scan]['service-detection']:
|
||||
error('The {profile}.{scan}.service-detection section does not have a pattern defined. Every service-detection section must have a command and a corresponding pattern that extracts the protocol (TCP/UDP), port, and service from the results.')
|
||||
errors = True
|
||||
else:
|
||||
if not all(x in port_scan_profiles_config[profile][scan]['service-detection']['pattern'] for x in ['(?P<port>', '(?P<protocol>', '(?P<service>']):
|
||||
error('The {profile}.{scan}.service-detection pattern does not contain one or more of the following matching groups: port, protocol, service. Ensure that all three of these matching groups are defined and capture the relevant data, e.g. (?P<port>\d+)')
|
||||
errors = True
|
||||
|
||||
if 'port-scan' in port_scan_profiles_config[profile][scan]:
|
||||
if 'command' not in port_scan_profiles_config[profile][scan]['port-scan']:
|
||||
error('The {profile}.{scan}.port-scan section does not have a command defined. Every port-scan section must have a command and a corresponding pattern that extracts the port from the results.')
|
||||
errors = True
|
||||
|
||||
if 'pattern' not in port_scan_profiles_config[profile][scan]['port-scan']:
|
||||
error('The {profile}.{scan}.port-scan section does not have a pattern defined. Every port-scan section must have a command and a corresponding pattern that extracts the port from the results.')
|
||||
errors = True
|
||||
else:
|
||||
if '(?P<port>' not in port_scan_profiles_config[profile][scan]['port-scan']['pattern']:
|
||||
error('The {profile}.{scan}.port-scan pattern does not contain a port matching group. Ensure that the port matching group is defined and captures the relevant data, e.g. (?P<port>\d+)')
|
||||
errors = True
|
||||
break
|
||||
|
||||
if not found_scan_profile:
|
||||
error('Argument --profile: must reference a port scan profile defined in {port_scan_profiles_config_file}. No such profile found: {port_scan_profile}')
|
||||
errors = True
|
||||
|
||||
heartbeat_interval = args.heartbeat
|
||||
|
||||
nmap = args.nmap
|
||||
if args.nmap_append:
|
||||
nmap += " " + args.nmap_append
|
||||
|
||||
outdir = args.output_dir
|
||||
srvname = ''
|
||||
verbose = args.verbose
|
||||
|
||||
raw_targets = args.targets
|
||||
targets = []
|
||||
|
||||
if len(args.target_file) > 0:
|
||||
if not os.path.isfile(args.target_file):
|
||||
error('The target file {args.target_file} was not found.')
|
||||
sys.exit(1)
|
||||
try:
|
||||
with open(args.target_file, 'r') as f:
|
||||
lines = f.read()
|
||||
for line in lines.splitlines():
|
||||
line = line.strip()
|
||||
if line.startswith('#') or len(line) == 0: continue
|
||||
if line not in raw_targets:
|
||||
raw_targets.append(line)
|
||||
except OSError:
|
||||
error('The target file {args.target_file} could not be read.')
|
||||
sys.exit(1)
|
||||
|
||||
for target in raw_targets:
|
||||
try:
|
||||
ip = str(ipaddress.ip_address(target))
|
||||
|
||||
if ip not in targets:
|
||||
targets.append(ip)
|
||||
except ValueError:
|
||||
|
||||
try:
|
||||
target_range = ipaddress.ip_network(target, strict=False)
|
||||
if not args.disable_sanity_checks and target_range.num_addresses > 256:
|
||||
error(target + ' contains ' + str(target_range.num_addresses) + ' addresses. Check that your CIDR notation is correct. If it is, re-run with the --disable-sanity-checks option to suppress this check.')
|
||||
errors = True
|
||||
else:
|
||||
for ip in target_range.hosts():
|
||||
ip = str(ip)
|
||||
if ip not in targets:
|
||||
targets.append(ip)
|
||||
except ValueError:
|
||||
|
||||
try:
|
||||
ip = socket.gethostbyname(target)
|
||||
|
||||
if target not in targets:
|
||||
targets.append(target)
|
||||
except socket.gaierror:
|
||||
error(target + ' does not appear to be a valid IP address, IP range, or resolvable hostname.')
|
||||
errors = True
|
||||
|
||||
if len(targets) == 0:
|
||||
error('You must specify at least one target to scan!')
|
||||
errors = True
|
||||
|
||||
if single_target and len(targets) != 1:
|
||||
error('You cannot provide more than one target when scanning in single-target mode.')
|
||||
sys.exit(1)
|
||||
|
||||
if not args.disable_sanity_checks and len(targets) > 256:
|
||||
error('A total of ' + str(len(targets)) + ' targets would be scanned. If this is correct, re-run with the --disable-sanity-checks option to suppress this check.')
|
||||
errors = True
|
||||
|
||||
if errors:
|
||||
sys.exit(1)
|
||||
|
||||
with ProcessPoolExecutor(max_workers=args.concurrent_targets) as executor:
|
||||
start_time = time.time()
|
||||
futures = []
|
||||
|
||||
for address in targets:
|
||||
target = Target(address)
|
||||
futures.append(executor.submit(scan_host, target, concurrent_scans, outdir))
|
||||
|
||||
try:
|
||||
for future in as_completed(futures):
|
||||
future.result()
|
||||
except KeyboardInterrupt:
|
||||
for future in futures:
|
||||
future.cancel()
|
||||
executor.shutdown(wait=False)
|
||||
sys.exit(1)
|
||||
|
||||
elapsed_time = calculate_elapsed_time(start_time)
|
||||
info('{bgreen}Finished scanning all targets in {elapsed_time}!{rst}')
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
|
@ -1,8 +0,0 @@
|
|||
# Patterns defined in this file will be checked against every line of output (e.g. port scans and service scans)
|
||||
|
||||
[[pattern]]
|
||||
description = 'Nmap script found a potential vulnerability. ({match})'
|
||||
pattern = 'State: (?:(?:LIKELY\_?)?VULNERABLE)'
|
||||
|
||||
[[pattern]]
|
||||
pattern = '(?i)unauthorized'
|
|
@ -1,45 +0,0 @@
|
|||
[default]
|
||||
|
||||
[default.nmap-quick]
|
||||
|
||||
[default.nmap-quick.service-detection]
|
||||
command = 'nmap {nmap_extra} -sV -sC --version-all -oN "{scandir}/_quick_tcp_nmap.txt" -oX "{scandir}/xml/_quick_tcp_nmap.xml" {address}'
|
||||
pattern = '^(?P<port>\d+)\/(?P<protocol>(tcp|udp))(.*)open(\s*)(?P<service>[\w\-\/]+)(\s*)(.*)$'
|
||||
|
||||
[default.nmap-full-tcp]
|
||||
|
||||
[default.nmap-full-tcp.service-detection]
|
||||
command = 'nmap {nmap_extra} -A --osscan-guess --version-all -p- -oN "{scandir}/_full_tcp_nmap.txt" -oX "{scandir}/xml/_full_tcp_nmap.xml" {address}'
|
||||
pattern = '^(?P<port>\d+)\/(?P<protocol>(tcp|udp))(.*)open(\s*)(?P<service>[\w\-\/]+)(\s*)(.*)$'
|
||||
|
||||
[default.nmap-top-20-udp]
|
||||
|
||||
[default.nmap-top-20-udp.service-detection]
|
||||
command = 'nmap {nmap_extra} -sU -A --top-ports=20 --version-all -oN "{scandir}/_top_20_udp_nmap.txt" -oX "{scandir}/xml/_top_20_udp_nmap.xml" {address}'
|
||||
pattern = '^(?P<port>\d+)\/(?P<protocol>(tcp|udp))(.*)open(\s*)(?P<service>[\w\-\/]+)(\s*)(.*)$'
|
||||
|
||||
[quick]
|
||||
|
||||
[quick.nmap-quick]
|
||||
|
||||
[quick.nmap-quick.service-detection]
|
||||
command = 'nmap {nmap_extra} -sV --version-all -oN "{scandir}/_quick_tcp_nmap.txt" -oX "{scandir}/xml/_quick_tcp_nmap.xml" {address}'
|
||||
pattern = '^(?P<port>\d+)\/(?P<protocol>(tcp|udp))(.*)open(\s*)(?P<service>[\w\-\/]+)(\s*)(.*)$'
|
||||
|
||||
[quick.nmap-top-20-udp]
|
||||
|
||||
[quick.nmap-top-20-udp.service-detection]
|
||||
command = 'nmap {nmap_extra} -sU -A --top-ports=20 --version-all -oN "{scandir}/_top_20_udp_nmap.txt" -oX "{scandir}/xml/_top_20_udp_nmap.xml" {address}'
|
||||
pattern = '^(?P<port>\d+)\/(?P<protocol>(tcp|udp))(.*)open(\s*)(?P<service>[\w\-\/]+)(\s*)(.*)$'
|
||||
|
||||
[udp]
|
||||
|
||||
[udp.udp-top-20]
|
||||
|
||||
[udp.udp-top-20.port-scan]
|
||||
command = 'unicornscan -mU -p 631,161,137,123,138,1434,445,135,67,53,139,500,68,520,1900,4500,514,49152,162,69 {address} 2>&1 | tee "{scandir}/_top_20_udp_unicornscan.txt"'
|
||||
pattern = '^UDP open\s*[\w-]+\[\s*(?P<port>\d+)\].*$'
|
||||
|
||||
[udp.udp-top-20.service-detection]
|
||||
command = 'nmap {nmap_extra} -sU -A -p {ports} --version-all -oN "{scandir}/_top_20_udp_nmap.txt" -oX "{scandir}/xml/_top_20_udp_nmap.xml" {address}'
|
||||
pattern = '^(?P<port>\d+)\/(?P<protocol>(udp))(.*)open(\s*)(?P<service>[\w\-\/]+)(\s*)(.*)$'
|
|
@ -1,586 +0,0 @@
|
|||
# Configurable Variables
|
||||
username_wordlist = '/usr/share/seclists/Usernames/top-usernames-shortlist.txt'
|
||||
password_wordlist = '/usr/share/seclists/Passwords/darkweb2017-top100.txt'
|
||||
|
||||
[all-services] # Define scans here that you want to run against all services.
|
||||
|
||||
service-names = [
|
||||
'.+'
|
||||
]
|
||||
|
||||
[[all-services.scan]]
|
||||
name = 'sslscan'
|
||||
command = 'if [ "{secure}" == "True" ]; then sslscan --show-certificate --no-colour {address}:{port} 2>&1 | tee "{scandir}/{protocol}_{port}_sslscan.txt"; fi'
|
||||
|
||||
[cassandra]
|
||||
|
||||
service-names = [
|
||||
'^apani1'
|
||||
]
|
||||
|
||||
[[cassandra.scan]]
|
||||
name = 'nmap-cassandra'
|
||||
command = 'nmap {nmap_extra} -sV -p {port} --script="banner,(cassandra* or ssl*) and not (brute or broadcast or dos or external or fuzzer)" -oN "{scandir}/{protocol}_{port}_cassandra_nmap.txt" -oX "{scandir}/xml/{protocol}_{port}_cassandra_nmap.xml" {address}'
|
||||
|
||||
[cups]
|
||||
|
||||
service-names = [
|
||||
'^ipp'
|
||||
]
|
||||
|
||||
[[cups.scan]]
|
||||
name = 'nmap-cups'
|
||||
command = 'nmap {nmap_extra} -sV -p {port} --script="banner,(cups* or ssl*) and not (brute or broadcast or dos or external or fuzzer)" -oN "{scandir}/{protocol}_{port}_cups_nmap.txt" -oX "{scandir}/xml/{protocol}_{port}_cups_nmap.xml" {address}'
|
||||
|
||||
[distcc]
|
||||
|
||||
service-names = [
|
||||
'^distccd'
|
||||
]
|
||||
|
||||
[[distcc.scan]]
|
||||
name = 'nmap-distcc'
|
||||
command = 'nmap {nmap_extra} -sV -p {port} --script="banner,distcc-cve2004-2687" --script-args="distcc-cve2004-2687.cmd=id" -oN "{scandir}/{protocol}_{port}_distcc_nmap.txt" -oX "{scandir}/xml/{protocol}_{port}_distcc_nmap.xml" {address}'
|
||||
|
||||
[dns]
|
||||
|
||||
service-names = [
|
||||
'^domain'
|
||||
]
|
||||
|
||||
[[dns.scan]]
|
||||
name = 'nmap-dns'
|
||||
command = 'nmap {nmap_extra} -sV -p {port} --script="banner,(dns* or ssl*) and not (brute or broadcast or dos or external or fuzzer)" -oN "{scandir}/{protocol}_{port}_dns_nmap.txt" -oX "{scandir}/xml/{protocol}_{port}_dns_nmap.xml" {address}'
|
||||
|
||||
[finger]
|
||||
|
||||
service-names = [
|
||||
'^finger'
|
||||
]
|
||||
|
||||
[[finger.scan]]
|
||||
name = 'nmap-finger'
|
||||
command = 'nmap {nmap_extra} -sV -p {port} --script="banner,finger" -oN "{scandir}/{protocol}_{port}_finger_nmap.txt" -oX "{scandir}/xml/{protocol}_{port}_finger_nmap.xml" {address}'
|
||||
|
||||
[ftp]
|
||||
|
||||
service-names = [
|
||||
'^ftp',
|
||||
'^ftp\-data'
|
||||
]
|
||||
|
||||
[[ftp.scan]]
|
||||
name = 'nmap-ftp'
|
||||
command = 'nmap {nmap_extra} -sV -p {port} --script="banner,(ftp* or ssl*) and not (brute or broadcast or dos or external or fuzzer)" -oN "{scandir}/{protocol}_{port}_ftp_nmap.txt" -oX "{scandir}/xml/{protocol}_{port}_ftp_nmap.xml" {address}'
|
||||
|
||||
[[ftp.scan.pattern]]
|
||||
description = 'Anonymous FTP Enabled!'
|
||||
pattern = 'Anonymous FTP login allowed'
|
||||
|
||||
[[ftp.manual]]
|
||||
description = 'Bruteforce logins:'
|
||||
commands = [
|
||||
'hydra -L "{username_wordlist}" -P "{password_wordlist}" -e nsr -s {port} -o "{scandir}/{protocol}_{port}_ftp_hydra.txt" ftp://{address}',
|
||||
'medusa -U "{username_wordlist}" -P "{password_wordlist}" -e ns -n {port} -O "{scandir}/{protocol}_{port}_ftp_medusa.txt" -M ftp -h {address}'
|
||||
]
|
||||
|
||||
[http]
|
||||
|
||||
service-names = [
|
||||
'^http',
|
||||
]
|
||||
|
||||
ignore-service-names = [
|
||||
'^nacn_http$'
|
||||
]
|
||||
|
||||
[[http.scan]]
|
||||
name = 'nmap-http'
|
||||
command = 'nmap {nmap_extra} -sV -p {port} --script="banner,(http* or ssl*) and not (brute or broadcast or dos or external or http-slowloris* or fuzzer)" -oN "{scandir}/{protocol}_{port}_http_nmap.txt" -oX "{scandir}/xml/{protocol}_{port}_{scheme}_nmap.xml" {address}'
|
||||
|
||||
[[http.scan.pattern]]
|
||||
description = 'Identified HTTP Server: {match}'
|
||||
pattern = 'Server: ([^\n]+)'
|
||||
|
||||
[[http.scan.pattern]]
|
||||
description = 'WebDAV is enabled'
|
||||
pattern = 'WebDAV is ENABLED'
|
||||
|
||||
[[http.scan]]
|
||||
name = 'curl-index'
|
||||
command = 'curl -sSik {scheme}://{address}:{port}/ -m 10 2>&1 | tee "{scandir}/{protocol}_{port}_{scheme}_index.html"'
|
||||
|
||||
[[http.scan.pattern]]
|
||||
pattern = '(?i)Powered by [^\n]+'
|
||||
|
||||
[[http.scan]]
|
||||
name = 'curl-robots'
|
||||
command = 'curl -sSik {scheme}://{address}:{port}/robots.txt -m 10 2>&1 | tee "{scandir}/{protocol}_{port}_{scheme}_robots.txt"'
|
||||
|
||||
[[http.scan]]
|
||||
name = 'wkhtmltoimage'
|
||||
command = 'if hash wkhtmltoimage 2> /dev/null; then wkhtmltoimage --format png {scheme}://{address}:{port}/ {scandir}/{protocol}_{port}_{scheme}_screenshot.png; fi'
|
||||
|
||||
[[http.scan]]
|
||||
name = 'whatweb'
|
||||
command = 'whatweb --color=never --no-errors -a 3 -v {scheme}://{address}:{port} 2>&1 | tee "{scandir}/{protocol}_{port}_{scheme}_whatweb.txt"'
|
||||
|
||||
[[http.scan]]
|
||||
name = 'feroxbuster'
|
||||
command = 'feroxbuster -u {scheme}://{address}:{port} -t 10 -w /usr/share/seclists/Discovery/Web-Content/common.txt -x "txt,html,php,asp,aspx,jsp" -v -k -n -o {scandir}/{protocol}_{port}_{scheme}_feroxbuster.txt'
|
||||
|
||||
[[http.manual]]
|
||||
description = '(nikto) old but generally reliable web server enumeration tool'
|
||||
commands = [
|
||||
'nikto -ask=no -h {scheme}://{address}:{port} 2>&1 | tee "{scandir}/{protocol}_{port}_{scheme}_nikto.txt"'
|
||||
]
|
||||
|
||||
[[http.manual]]
|
||||
description = '(feroxbuster) Multi-threaded recursive directory/file enumeration for web servers using various wordlists:'
|
||||
commands = [
|
||||
'feroxbuster -u {scheme}://{address}:{port} -t 10 -w /usr/share/seclists/Discovery/Web-Content/big.txt -x "txt,html,php,asp,aspx,jsp" -v -k -n -o {scandir}/{protocol}_{port}_{scheme}_feroxbuster_big.txt',
|
||||
'feroxbuster -u {scheme}://{address}:{port} -t 10 -w /usr/share/wordlists/dirbuster/directory-list-2.3-medium.txt -x "txt,html,php,asp,aspx,jsp" -v -k -n -o {scandir}/{protocol}_{port}_{scheme}_feroxbuster_dirbuster.txt'
|
||||
]
|
||||
|
||||
[[http.manual]]
|
||||
description = '(dirsearch) Multi-threaded recursive directory/file enumeration for web servers using various wordlists:'
|
||||
commands = [
|
||||
'dirsearch -u {scheme}://{address}:{port}/ -t 16 -r -e txt,html,php,asp,aspx,jsp -f -w /usr/share/seclists/Discovery/Web-Content/big.txt --plain-text-report="{scandir}/{protocol}_{port}_{scheme}_dirsearch_big.txt"',
|
||||
'dirsearch -u {scheme}://{address}:{port}/ -t 16 -r -e txt,html,php,asp,aspx,jsp -f -w /usr/share/wordlists/dirbuster/directory-list-2.3-medium.txt --plain-text-report="{scandir}/{protocol}_{port}_{scheme}_dirsearch_dirbuster.txt"'
|
||||
]
|
||||
|
||||
[[http.manual]]
|
||||
description = '(dirb) Recursive directory/file enumeration for web servers using various wordlists (same as dirsearch above):'
|
||||
commands = [
|
||||
'dirb {scheme}://{address}:{port}/ /usr/share/seclists/Discovery/Web-Content/big.txt -l -r -S -X ",.txt,.html,.php,.asp,.aspx,.jsp" -o "{scandir}/{protocol}_{port}_{scheme}_dirb_big.txt"',
|
||||
'dirb {scheme}://{address}:{port}/ /usr/share/wordlists/dirbuster/directory-list-2.3-medium.txt -l -r -S -X ",.txt,.html,.php,.asp,.aspx,.jsp" -o "{scandir}/{protocol}_{port}_{scheme}_dirb_dirbuster.txt"'
|
||||
]
|
||||
|
||||
[[http.manual]]
|
||||
description = '(gobuster v3) Directory/file enumeration for web servers using various wordlists (same as dirb above):'
|
||||
commands = [
|
||||
'gobuster dir -u {scheme}://{address}:{port}/ -w /usr/share/seclists/Discovery/Web-Content/big.txt -e -k -s "200,204,301,302,307,403,500" -x "txt,html,php,asp,aspx,jsp" -z -o "{scandir}/{protocol}_{port}_{scheme}_gobuster_big.txt"',
|
||||
'gobuster dir -u {scheme}://{address}:{port}/ -w /usr/share/wordlists/dirbuster/directory-list-2.3-medium.txt -e -k -s "200,204,301,302,307,403,500" -x "txt,html,php,asp,aspx,jsp" -z -o "{scandir}/{protocol}_{port}_{scheme}_gobuster_dirbuster.txt"'
|
||||
]
|
||||
|
||||
[[http.manual]]
|
||||
description = '(gobuster v1 & v2) Directory/file enumeration for web servers using various wordlists (same as dirb above):'
|
||||
commands = [
|
||||
'gobuster -u {scheme}://{address}:{port}/ -w /usr/share/seclists/Discovery/Web-Content/big.txt -e -k -l -s "200,204,301,302,307,403,500" -x "txt,html,php,asp,aspx,jsp" -o "{scandir}/{protocol}_{port}_{scheme}_gobuster_big.txt"',
|
||||
'gobuster -u {scheme}://{address}:{port}/ -w /usr/share/wordlists/dirbuster/directory-list-2.3-medium.txt -e -k -l -s "200,204,301,302,307,403,500" -x "txt,html,php,asp,aspx,jsp" -o "{scandir}/{protocol}_{port}_{scheme}_gobuster_dirbuster.txt"'
|
||||
]
|
||||
|
||||
[[http.manual]]
|
||||
description = '(wpscan) WordPress Security Scanner (useful if WordPress is found):'
|
||||
commands = [
|
||||
'wpscan --url {scheme}://{address}:{port}/ --no-update -e vp,vt,tt,cb,dbe,u,m --plugins-detection aggressive --plugins-version-detection aggressive -f cli-no-color 2>&1 | tee "{scandir}/{protocol}_{port}_{scheme}_wpscan.txt"'
|
||||
]
|
||||
|
||||
[[http.manual]]
|
||||
description = "Credential bruteforcing commands (don't run these without modifying them):"
|
||||
commands = [
|
||||
'hydra -L "{username_wordlist}" -P "{password_wordlist}" -e nsr -s {port} -o "{scandir}/{protocol}_{port}_{scheme}_auth_hydra.txt" {scheme}-get://{address}/path/to/auth/area',
|
||||
'medusa -U "{username_wordlist}" -P "{password_wordlist}" -e ns -n {port} -O "{scandir}/{protocol}_{port}_{scheme}_auth_medusa.txt" -M http -h {address} -m DIR:/path/to/auth/area',
|
||||
'hydra -L "{username_wordlist}" -P "{password_wordlist}" -e nsr -s {port} -o "{scandir}/{protocol}_{port}_{scheme}_form_hydra.txt" {scheme}-post-form://{address}/path/to/login.php:username=^USER^&password=^PASS^:invalid-login-message',
|
||||
'medusa -U "{username_wordlist}" -P "{password_wordlist}" -e ns -n {port} -O "{scandir}/{protocol}_{port}_{scheme}_form_medusa.txt" -M web-form -h {address} -m FORM:/path/to/login.php -m FORM-DATA:"post?username=&password=" -m DENY-SIGNAL:"invalid login message"',
|
||||
]
|
||||
|
||||
[imap]
|
||||
|
||||
service-names = [
|
||||
'^imap'
|
||||
]
|
||||
|
||||
[[imap.scan]]
|
||||
name = 'nmap-imap'
|
||||
command = 'nmap {nmap_extra} -sV -p {port} --script="banner,(imap* or ssl*) and not (brute or broadcast or dos or external or fuzzer)" -oN "{scandir}/{protocol}_{port}_imap_nmap.txt" -oX "{scandir}/xml/{protocol}_{port}_imap_nmap.xml" {address}'
|
||||
|
||||
[kerberos]
|
||||
|
||||
service-names = [
|
||||
'^kerberos',
|
||||
'^kpasswd'
|
||||
]
|
||||
|
||||
[[kerberos.scan]]
|
||||
name = 'nmap-kerberos'
|
||||
command = 'nmap {nmap_extra} -sV -p {port} --script="banner,krb5-enum-users" -oN "{scandir}/{protocol}_{port}_kerberos_nmap.txt" -oX "{scandir}/xml/{protocol}_{port}_kerberos_nmap.xml" {address}'
|
||||
|
||||
[ldap]
|
||||
|
||||
service-names = [
|
||||
'^ldap'
|
||||
]
|
||||
|
||||
[[ldap.scan]]
|
||||
name = 'nmap-ldap'
|
||||
command = 'nmap {nmap_extra} -sV -p {port} --script="banner,(ldap* or ssl*) and not (brute or broadcast or dos or external or fuzzer)" -oN "{scandir}/{protocol}_{port}_ldap_nmap.txt" -oX "{scandir}/xml/{protocol}_{port}_ldap_nmap.xml" {address}'
|
||||
|
||||
[[ldap.scan]]
|
||||
name = 'enum4linux'
|
||||
command = 'enum4linux -a -M -l -d {address} 2>&1 | tee "{scandir}/enum4linux.txt"'
|
||||
run_once = true
|
||||
ports.tcp = [139, 389, 445]
|
||||
ports.udp = [137]
|
||||
|
||||
[[ldap.manual]]
|
||||
description = 'ldapsearch command (modify before running)'
|
||||
commands = [
|
||||
'ldapsearch -x -D "<username>" -w "<password>"" -p {port} -h {address} -b "dc=example,dc=com" -s sub "(objectclass=*) 2>&1 | tee > "{scandir}/{protocol}_{port}_ldap_all-entries.txt"'
|
||||
]
|
||||
|
||||
[mongodb]
|
||||
|
||||
service-names = [
|
||||
'^mongod'
|
||||
]
|
||||
|
||||
[[mongodb.scan]]
|
||||
name = 'nmap-mongodb'
|
||||
command = 'nmap {nmap_extra} -sV -p {port} --script="banner,(mongodb* or ssl*) and not (brute or broadcast or dos or external or fuzzer)" -oN "{scandir}/{protocol}_{port}_mongodb_nmap.txt" -oX "{scandir}/xml/{protocol}_{port}_mongodb_nmap.xml" {address}'
|
||||
|
||||
[mssql]
|
||||
|
||||
service-names = [
|
||||
'^mssql',
|
||||
'^ms\-sql'
|
||||
]
|
||||
|
||||
[[mssql.scan]]
|
||||
name = 'nmap-mssql'
|
||||
command = 'nmap {nmap_extra} -sV -p {port} --script="banner,(ms-sql* or ssl*) and not (brute or broadcast or dos or external or fuzzer)" --script-args="mssql.instance-port={port},mssql.username=sa,mssql.password=sa" -oN "{scandir}/{protocol}_{port}_mssql_nmap.txt" -oX "{scandir}/xml/{protocol}_{port}_mssql_nmap.xml" {address}'
|
||||
|
||||
[[mssql.manual]]
|
||||
description = '(sqsh) interactive database shell'
|
||||
commands = [
|
||||
'sqsh -U <username> -P <password> -S {address}:{port}'
|
||||
]
|
||||
|
||||
[mysql]
|
||||
|
||||
service-names = [
|
||||
'^mysql'
|
||||
]
|
||||
|
||||
[[mysql.scan]]
|
||||
name = 'nmap-mysql'
|
||||
command = 'nmap {nmap_extra} -sV -p {port} --script="banner,(mysql* or ssl*) and not (brute or broadcast or dos or external or fuzzer)" -oN "{scandir}/{protocol}_{port}_mysql_nmap.txt" -oX "{scandir}/xml/{protocol}_{port}_mysql_nmap.xml" {address}'
|
||||
|
||||
[nfs]
|
||||
|
||||
service-names = [
|
||||
'^nfs',
|
||||
'^rpcbind'
|
||||
]
|
||||
|
||||
[[nfs.scan]]
|
||||
name = 'nmap-nfs'
|
||||
command = 'nmap {nmap_extra} -sV -p {port} --script="banner,(rpcinfo or nfs*) and not (brute or broadcast or dos or external or fuzzer)" -oN "{scandir}/{protocol}_{port}_nfs_nmap.txt" -oX "{scandir}/xml/{protocol}_{port}_nfs_nmap.xml" {address}'
|
||||
|
||||
[[nfs.scan]]
|
||||
name = 'showmount'
|
||||
command = 'showmount -e {address} 2>&1 | tee "{scandir}/{protocol}_{port}_showmount.txt"'
|
||||
|
||||
[nntp]
|
||||
|
||||
service-names = [
|
||||
'^nntp'
|
||||
]
|
||||
|
||||
[[nntp.scan]]
|
||||
name = 'nmap-nntp'
|
||||
command = 'nmap {nmap_extra} -sV -p {port} --script="banner,nntp-ntlm-info" -oN "{scandir}/{protocol}_{port}_nntp_nmap.txt" -oX "{scandir}/xml/{protocol}_{port}_nntp_nmap.xml" {address}'
|
||||
|
||||
[oracle]
|
||||
|
||||
service-names = [
|
||||
'^oracle'
|
||||
]
|
||||
|
||||
[[oracle.scan]]
|
||||
name = 'nmap-oracle'
|
||||
command = 'nmap {nmap_extra} -sV -p {port} --script="banner,(oracle* or ssl*) and not (brute or broadcast or dos or external or fuzzer)" -oN "{scandir}/{protocol}_{port}_oracle_nmap.txt" -oX "{scandir}/xml/{protocol}_{port}_oracle_nmap.xml" {address}'
|
||||
|
||||
[[oracle.scan]]
|
||||
name = 'oracle-tnscmd-ping'
|
||||
command = 'tnscmd10g ping -h {address} -p {port} 2>&1 | tee "{scandir}/{protocol}_{port}_oracle_tnscmd_ping.txt"'
|
||||
|
||||
[[oracle.scan]]
|
||||
name = 'oracle-tnscmd-version'
|
||||
command = 'tnscmd10g version -h {address} -p {port} 2>&1 | tee "{scandir}/{protocol}_{port}_oracle_tnscmd_version.txt"'
|
||||
|
||||
[[oracle.scan]]
|
||||
name = 'oracle-tnscmd-version'
|
||||
command = 'tnscmd10g version -h {address} -p {port} 2>&1 | tee "{scandir}/{protocol}_{port}_oracle_tnscmd_version.txt"'
|
||||
|
||||
[[oracle.scan]]
|
||||
name = 'oracle-scanner'
|
||||
command = 'oscanner -v -s {address} -P {port} 2>&1 | tee "{scandir}/{protocol}_{port}_oracle_scanner.txt"'
|
||||
|
||||
[[oracle.manual]]
|
||||
description = 'Brute-force SIDs using Nmap'
|
||||
command = 'nmap {nmap_extra} -sV -p {port} --script="banner,oracle-sid-brute" -oN "{scandir}/{protocol}_{port}_oracle_sid-brute_nmap.txt" -oX "{scandir}/xml/{protocol}_{port}_oracle_sid-brute_nmap.xml" {address}'
|
||||
|
||||
[[oracle.manual]]
|
||||
description = 'Install ODAT (https://github.com/quentinhardy/odat) and run the following commands:'
|
||||
commands = [
|
||||
'python odat.py tnscmd -s {address} -p {port} --ping',
|
||||
'python odat.py tnscmd -s {address} -p {port} --version',
|
||||
'python odat.py tnscmd -s {address} -p {port} --status',
|
||||
'python odat.py sidguesser -s {address} -p {port}',
|
||||
'python odat.py passwordguesser -s {address} -p {port} -d <sid> --accounts-file accounts/accounts_multiple.txt',
|
||||
'python odat.py tnspoison -s {address} -p {port} -d <sid> --test-module'
|
||||
]
|
||||
|
||||
[[oracle.manual]]
|
||||
description = 'Install Oracle Instant Client (https://github.com/rapid7/metasploit-framework/wiki/How-to-get-Oracle-Support-working-with-Kali-Linux) and then bruteforce with patator:'
|
||||
commands = [
|
||||
'patator oracle_login host={address} port={port} user=COMBO00 password=COMBO01 0=/usr/share/seclists/Passwords/Default-Credentials/oracle-betterdefaultpasslist.txt -x ignore:code=ORA-01017 -x ignore:code=ORA-28000'
|
||||
]
|
||||
|
||||
[pop3]
|
||||
|
||||
service-names = [
|
||||
'^pop3'
|
||||
]
|
||||
|
||||
[[pop3.scan]]
|
||||
name = 'nmap-pop3'
|
||||
command = 'nmap {nmap_extra} -sV -p {port} --script="banner,(pop3* or ssl*) and not (brute or broadcast or dos or external or fuzzer)" -oN "{scandir}/{protocol}_{port}_pop3_nmap.txt" -oX "{scandir}/xml/{protocol}_{port}_pop3_nmap.xml" {address}'
|
||||
|
||||
[rdp]
|
||||
|
||||
service-names = [
|
||||
'^rdp',
|
||||
'^ms\-wbt\-server',
|
||||
'^ms\-term\-serv'
|
||||
]
|
||||
|
||||
[[rdp.scan]]
|
||||
name = 'nmap-rdp'
|
||||
command = 'nmap {nmap_extra} -sV -p {port} --script="banner,(rdp* or ssl*) and not (brute or broadcast or dos or external or fuzzer)" -oN "{scandir}/{protocol}_{port}_rdp_nmap.txt" -oX "{scandir}/xml/{protocol}_{port}_rdp_nmap.xml" {address}'
|
||||
|
||||
[[rdp.manual]]
|
||||
description = 'Bruteforce logins:'
|
||||
commands = [
|
||||
'hydra -L "{username_wordlist}" -P "{password_wordlist}" -e nsr -s {port} -o "{scandir}/{protocol}_{port}_rdp_hydra.txt" rdp://{address}',
|
||||
'medusa -U "{username_wordlist}" -P "{password_wordlist}" -e ns -n {port} -O "{scandir}/{protocol}_{port}_rdp_medusa.txt" -M rdp -h {address}'
|
||||
]
|
||||
|
||||
[rmi]
|
||||
|
||||
service-names = [
|
||||
'^java\-rmi',
|
||||
'^rmiregistry'
|
||||
]
|
||||
|
||||
[[rmi.scan]]
|
||||
name = 'nmap-rmi'
|
||||
command = 'nmap {nmap_extra} -sV -p {port} --script="banner,rmi-vuln-classloader,rmi-dumpregistry" -oN "{scandir}/{protocol}_{port}_rmi_nmap.txt" -oX "{scandir}/xml/{protocol}_{port}_rmi_nmap.xml" {address}'
|
||||
|
||||
[rpc]
|
||||
|
||||
service-names = [
|
||||
'^msrpc',
|
||||
'^rpcbind',
|
||||
'^erpc'
|
||||
]
|
||||
|
||||
[[rpc.scan]]
|
||||
name = 'nmap-msrpc'
|
||||
command = 'nmap {nmap_extra} -sV -p {port} --script="banner,msrpc-enum,rpc-grind,rpcinfo" -oN "{scandir}/{protocol}_{port}_rpc_nmap.txt" -oX "{scandir}/xml/{protocol}_{port}_rpc_nmap.xml" {address}'
|
||||
|
||||
[[rpc.manual]]
|
||||
description = 'RPC Client:'
|
||||
commands = [
|
||||
'rpcclient -p {port} -U "" {address}'
|
||||
]
|
||||
|
||||
[sip]
|
||||
|
||||
service-names = [
|
||||
'^asterisk'
|
||||
]
|
||||
|
||||
[[sip.scan]]
|
||||
name = 'nmap-sip'
|
||||
command = 'nmap {nmap_extra} -sV -p {port} --script="banner,sip-enum-users,sip-methods" -oN "{scandir}/{protocol}_{port}_sip_nmap.txt" -oX "{scandir}/xml/{protocol}_{port}_sip_nmap.xml" {address}'
|
||||
|
||||
[[sip.scan]]
|
||||
name = 'svwar'
|
||||
command = 'svwar -D -m INVITE -p {port} {address}'
|
||||
|
||||
[ssh]
|
||||
|
||||
service-names = [
|
||||
'^ssh'
|
||||
]
|
||||
|
||||
[[ssh.scan]]
|
||||
name = 'nmap-ssh'
|
||||
command = 'nmap {nmap_extra} -sV -p {port} --script="banner,ssh2-enum-algos,ssh-hostkey,ssh-auth-methods" -oN "{scandir}/{protocol}_{port}_ssh_nmap.txt" -oX "{scandir}/xml/{protocol}_{port}_ssh_nmap.xml" {address}'
|
||||
|
||||
[[ssh.manual]]
|
||||
description = 'Bruteforce logins:'
|
||||
commands = [
|
||||
'hydra -L "{username_wordlist}" -P "{password_wordlist}" -e nsr -s {port} -o "{scandir}/{protocol}_{port}_ssh_hydra.txt" ssh://{address}',
|
||||
'medusa -U "{username_wordlist}" -P "{password_wordlist}" -e ns -n {port} -O "{scandir}/{protocol}_{port}_ssh_medusa.txt" -M ssh -h {address}'
|
||||
]
|
||||
[smb]
|
||||
|
||||
service-names = [
|
||||
'^smb',
|
||||
'^microsoft\-ds',
|
||||
'^netbios'
|
||||
]
|
||||
|
||||
[[smb.scan]]
|
||||
name = 'nmap-smb'
|
||||
command = 'nmap {nmap_extra} -sV -p {port} --script="banner,(nbstat or smb* or ssl*) and not (brute or broadcast or dos or external or fuzzer)" --script-args="unsafe=1" -oN "{scandir}/{protocol}_{port}_smb_nmap.txt" -oX "{scandir}/xml/{protocol}_{port}_smb_nmap.xml" {address}'
|
||||
|
||||
[[smb.scan]]
|
||||
name = 'enum4linux'
|
||||
command = 'enum4linux -a -M -l -d {address} 2>&1 | tee "{scandir}/enum4linux.txt"'
|
||||
run_once = true
|
||||
ports.tcp = [139, 389, 445]
|
||||
ports.udp = [137]
|
||||
|
||||
[[smb.scan]]
|
||||
name = 'nbtscan'
|
||||
command = 'nbtscan -rvh {address} 2>&1 | tee "{scandir}/nbtscan.txt"'
|
||||
run_once = true
|
||||
ports.udp = [137]
|
||||
|
||||
[[smb.scan]]
|
||||
name = 'smbclient'
|
||||
command = 'smbclient -L\\ -N -I {address} 2>&1 | tee "{scandir}/smbclient.txt"'
|
||||
run_once = true
|
||||
ports.tcp = [139, 445]
|
||||
|
||||
[[smb.scan]]
|
||||
name = 'smbmap-share-permissions'
|
||||
command = 'smbmap -H {address} -P {port} 2>&1 | tee -a "{scandir}/smbmap-share-permissions.txt"; smbmap -u null -p "" -H {address} -P {port} 2>&1 | tee -a "{scandir}/smbmap-share-permissions.txt"'
|
||||
|
||||
[[smb.scan]]
|
||||
name = 'smbmap-list-contents'
|
||||
command = 'smbmap -H {address} -P {port} -R 2>&1 | tee -a "{scandir}/smbmap-list-contents.txt"; smbmap -u null -p "" -H {address} -P {port} -R 2>&1 | tee -a "{scandir}/smbmap-list-contents.txt"'
|
||||
|
||||
[[smb.scan]]
|
||||
name = 'smbmap-execute-command'
|
||||
command = 'smbmap -H {address} -P {port} -x "ipconfig /all" 2>&1 | tee -a "{scandir}/smbmap-execute-command.txt"; smbmap -u null -p "" -H {address} -P {port} -x "ipconfig /all" 2>&1 | tee -a "{scandir}/smbmap-execute-command.txt"'
|
||||
|
||||
[[smb.manual]]
|
||||
description = 'Nmap scans for SMB vulnerabilities that could potentially cause a DoS if scanned (according to Nmap). Be careful:'
|
||||
commands = [
|
||||
'nmap {nmap_extra} -sV -p {port} --script="smb-vuln-ms06-025" --script-args="unsafe=1" -oN "{scandir}/{protocol}_{port}_smb_ms06-025.txt" -oX "{scandir}/xml/{protocol}_{port}_smb_ms06-025.xml" {address}',
|
||||
'nmap {nmap_extra} -sV -p {port} --script="smb-vuln-ms07-029" --script-args="unsafe=1" -oN "{scandir}/{protocol}_{port}_smb_ms07-029.txt" -oX "{scandir}/xml/{protocol}_{port}_smb_ms07-029.xml" {address}',
|
||||
'nmap {nmap_extra} -sV -p {port} --script="smb-vuln-ms08-067" --script-args="unsafe=1" -oN "{scandir}/{protocol}_{port}_smb_ms08-067.txt" -oX "{scandir}/xml/{protocol}_{port}_smb_ms08-067.xml" {address}'
|
||||
]
|
||||
|
||||
[smtp]
|
||||
|
||||
service-names = [
|
||||
'^smtp'
|
||||
]
|
||||
|
||||
[[smtp.scan]]
|
||||
name = 'nmap-smtp'
|
||||
command = 'nmap {nmap_extra} -sV -p {port} --script="banner,(smtp* or ssl*) and not (brute or broadcast or dos or external or fuzzer)" -oN "{scandir}/{protocol}_{port}_smtp_nmap.txt" -oX "{scandir}/xml/{protocol}_{port}_smtp_nmap.xml" {address}'
|
||||
|
||||
[[smtp.scan]]
|
||||
name = 'smtp-user-enum'
|
||||
command = 'smtp-user-enum -M VRFY -U "{username_wordlist}" -t {address} -p {port} 2>&1 | tee "{scandir}/{protocol}_{port}_smtp_user-enum.txt"'
|
||||
|
||||
[snmp]
|
||||
|
||||
service-names = [
|
||||
'^snmp'
|
||||
]
|
||||
|
||||
[[snmp.scan]]
|
||||
name = 'nmap-snmp'
|
||||
command = 'nmap {nmap_extra} -sV -p {port} --script="banner,(snmp* or ssl*) and not (brute or broadcast or dos or external or fuzzer)" -oN "{scandir}/{protocol}_{port}_snmp-nmap.txt" -oX "{scandir}/xml/{protocol}_{port}_snmp_nmap.xml" {address}'
|
||||
|
||||
[[snmp.scan]]
|
||||
name = 'onesixtyone'
|
||||
command = 'onesixtyone -c /usr/share/seclists/Discovery/SNMP/common-snmp-community-strings-onesixtyone.txt -dd {address} 2>&1 | tee "{scandir}/{protocol}_{port}_snmp_onesixtyone.txt"'
|
||||
run_once = true
|
||||
ports.udp = [161]
|
||||
|
||||
[[snmp.scan]]
|
||||
name = 'snmpwalk'
|
||||
command = 'snmpwalk -c public -v 1 {address} 2>&1 | tee "{scandir}/{protocol}_{port}_snmp_snmpwalk.txt"'
|
||||
run_once = true
|
||||
ports.udp = [161]
|
||||
|
||||
[[snmp.scan]]
|
||||
name = 'snmpwalk-system-processes'
|
||||
command = 'snmpwalk -c public -v 1 {address} 1.3.6.1.2.1.25.1.6.0 2>&1 | tee "{scandir}/{protocol}_{port}_snmp_snmpwalk_system_processes.txt"'
|
||||
run_once = true
|
||||
ports.udp = [161]
|
||||
|
||||
[[snmp.scan]]
|
||||
name = 'snmpwalk-running-processes'
|
||||
command = 'snmpwalk -c public -v 1 {address} 1.3.6.1.2.1.25.4.2.1.2 2>&1 | tee "{scandir}/{protocol}_{port}_snmp_snmpwalk_running_processes.txt"'
|
||||
run_once = true
|
||||
ports.udp = [161]
|
||||
|
||||
[[snmp.scan]]
|
||||
name = 'snmpwalk-process-paths'
|
||||
command = 'snmpwalk -c public -v 1 {address} 1.3.6.1.2.1.25.4.2.1.4 2>&1 | tee "{scandir}/{protocol}_{port}_snmp_snmpwalk_process_paths.txt"'
|
||||
run_once = true
|
||||
ports.udp = [161]
|
||||
|
||||
[[snmp.scan]]
|
||||
name = 'snmpwalk-storage-units'
|
||||
command = 'snmpwalk -c public -v 1 {address} 1.3.6.1.2.1.25.2.3.1.4 2>&1 | tee "{scandir}/{protocol}_{port}_snmp_snmpwalk_storage_units.txt"'
|
||||
run_once = true
|
||||
ports.udp = [161]
|
||||
|
||||
[[snmp.scan]]
|
||||
name = 'snmpwalk-software-names'
|
||||
command = 'snmpwalk -c public -v 1 {address} 1.3.6.1.2.1.25.6.3.1.2 2>&1 | tee "{scandir}/{protocol}_{port}_snmp_snmpwalk_software_names.txt"'
|
||||
run_once = true
|
||||
ports.udp = [161]
|
||||
|
||||
[[snmp.scan]]
|
||||
name = 'snmpwalk-user-accounts'
|
||||
command = 'snmpwalk -c public -v 1 {address} 1.3.6.1.4.1.77.1.2.25 2>&1 | tee "{scandir}/{protocol}_{port}_snmp_snmpwalk_user_accounts.txt"'
|
||||
run_once = true
|
||||
ports.udp = [161]
|
||||
|
||||
[[snmp.scan]]
|
||||
name = 'snmpwalk-tcp-ports'
|
||||
command = 'snmpwalk -c public -v 1 {address} 1.3.6.1.2.1.6.13.1.3 2>&1 | tee "{scandir}/{protocol}_{port}_snmp_snmpwalk_tcp_ports.txt"'
|
||||
run_once = true
|
||||
ports.udp = [161]
|
||||
|
||||
[telnet]
|
||||
|
||||
service-names = [
|
||||
'^telnet'
|
||||
]
|
||||
|
||||
[[telnet.scan]]
|
||||
name = 'nmap-telnet'
|
||||
command = 'nmap {nmap_extra} -sV -p {port} --script="banner,telnet-encryption,telnet-ntlm-info" -oN "{scandir}/{protocol}_{port}_telnet-nmap.txt" -oX "{scandir}/xml/{protocol}_{port}_telnet_nmap.xml" {address}'
|
||||
|
||||
[tftp]
|
||||
|
||||
service-names = [
|
||||
'^tftp'
|
||||
]
|
||||
|
||||
[[tftp.scan]]
|
||||
name = 'nmap-tftp'
|
||||
command = 'nmap {nmap_extra} -sV -p {port} --script="banner,tftp-enum" -oN "{scandir}/{protocol}_{port}_tftp-nmap.txt" -oX "{scandir}/xml/{protocol}_{port}_tftp_nmap.xml" {address}'
|
||||
|
||||
[vnc]
|
||||
|
||||
service-names = [
|
||||
'^vnc'
|
||||
]
|
||||
|
||||
[[vnc.scan]]
|
||||
name = 'nmap-vnc'
|
||||
command = 'nmap {nmap_extra} -sV -p {port} --script="banner,(vnc* or realvnc* or ssl*) and not (brute or broadcast or dos or external or fuzzer)" --script-args="unsafe=1" -oN "{scandir}/{protocol}_{port}_vnc_nmap.txt" -oX "{scandir}/xml/{protocol}_{port}_vnc_nmap.xml" {address}'
|
Loading…
Reference in New Issue