From 6e083d771cd00d5a8e6b98b813ddf3631bdbb238 Mon Sep 17 00:00:00 2001 From: vesche Date: Tue, 8 Feb 2022 17:52:20 -0600 Subject: [PATCH] v2.1.6 - Use black code formatting, remove t1shopper, discontinue docker, and misc edits --- Dockerfile | 4 - README.md | 29 +---- scanless/__init__.py | 6 +- scanless/cli.py | 101 +++++++++-------- scanless/core.py | 248 ++++++++++++++++++++++------------------- scanless/exceptions.py | 3 - setup.py | 55 ++++----- 7 files changed, 212 insertions(+), 234 deletions(-) delete mode 100644 Dockerfile diff --git a/Dockerfile b/Dockerfile deleted file mode 100644 index c70966c..0000000 --- a/Dockerfile +++ /dev/null @@ -1,4 +0,0 @@ -FROM python:3.7.0-alpine3.8 -RUN pip install scanless -USER 1000:1000 -ENTRYPOINT ["scanless"] diff --git a/README.md b/README.md index 6fb8f3a..a29c22a 100644 --- a/README.md +++ b/README.md @@ -8,7 +8,6 @@ This is a Python 3 command-line utility and library for using websites that can * [ipfingerprints](http://www.ipfingerprints.com/portscan.php) * [spiderip](https://spiderip.com/online-port-scan.php) * [standingtech](https://portscanner.standingtech.com/) -* [t1shopper](http://www.t1shopper.com/tools/port-scan/) * [viewdns](http://viewdns.info/) * [yougetsignal](http://www.yougetsignal.com/tools/open-ports/) @@ -47,13 +46,12 @@ $ scanless --list | ipfingerprints | https://www.ipfingerprints.com | | spiderip | https://spiderip.com | | standingtech | https://portscanner.standingtech.com | -| t1shopper | http://www.t1shopper.com | | viewdns | https://viewdns.info | | yougetsignal | https://www.yougetsignal.com | +----------------+--------------------------------------+ $ scanless -t scanme.nmap.org -s spiderip -Running scanless v2.1.4... +Running scanless v2.1.6... spiderip: PORT STATE SERVICE @@ -123,28 +121,3 @@ Nmap done: 1 IP address (1 host up) scanned in 0.11 seconds } ] ``` - -## Docker - -**Note from the repo author:** I did not create, nor do I maintain Docker support or the Dockerfile for scanless. It was a nice community addition. If it's broken please open an issue or submit a pull request and I'll take a look. Thank you! - -### Build - -To build the Docker image, run: -```shell -$ docker build -t scanless . -``` - -### Usage - -To use the Docker image previously created, run the following with whichever options you want like `--help`: -``` -$ docker run --rm -it scanless --help -``` - -If that long command is too troublesome, you can make an alias like so: `alias scanless="docker run --rm -it scanless"` and then run `scanless` as you would normally: -``` -$ scanless --help -$ scanless -l -$ scanless -t scanme.nmap.org -s yougetsignal -``` diff --git a/scanless/__init__.py b/scanless/__init__.py index c40b1ad..dfe197d 100644 --- a/scanless/__init__.py +++ b/scanless/__init__.py @@ -1,11 +1,9 @@ -"""scanless""" - # _____ __ ____ ____ _ ___ _____ _____ # / ___/ / ] / || \ | | / _]/ ___// ___/ -# ( \_ / / | o || _ || | / [_( \_( \_ +# ( \_ / / | o || _ || | / [_( \_( \_ # \__ |/ / | || | || |___ | _]\__ |\__ | # / \ / \_ | _ || | || || [_ / \ |/ \ | # \ \ || | || | || || |\ |\ | # \___|\____||__|__||__|__||_____||_____| \___| \___| -from scanless.core import Scanless \ No newline at end of file +from scanless.core import Scanless diff --git a/scanless/cli.py b/scanless/cli.py index 8c24526..0b43835 100644 --- a/scanless/cli.py +++ b/scanless/cli.py @@ -1,12 +1,10 @@ -"""scanless.cli""" - import crayons import argparse from random import choice from scanless.core import Scanless -SCAN_LIST = '''\ +SCAN_LIST = """\ +----------------+--------------------------------------+ | Scanner Name | Website | +----------------+--------------------------------------+ @@ -15,68 +13,73 @@ | pingeu | https://ping.eu | | spiderip | https://spiderip.com | | standingtech | https://portscanner.standingtech.com | -| t1shopper | http://www.t1shopper.com | | viewdns | https://viewdns.info | | yougetsignal | https://www.yougetsignal.com | -+----------------+--------------------------------------+''' -VERSION = '2.1.5' ++----------------+--------------------------------------+""" +VERSION = "2.1.6" sl = Scanless(cli_mode=True) def get_parser(): parser = argparse.ArgumentParser( - description='scanless, an online port scan scraper.' + description="scanless, an online port scan scraper." ) parser.add_argument( - '-v', '--version', - action='store_true', - help='display the current version' + "-v", + "--version", + action="store_true", + help="display the current version" ) parser.add_argument( - '-t', '--target', - help='ip or domain to scan', + "-t", + "--target", + help="ip or domain to scan", type=str ) parser.add_argument( - '-s', '--scanner', - default='hackertarget', - help='scanner to use (default: hackertarget)', - type=str + "-s", + "--scanner", + default="hackertarget", + help="scanner to use (default: hackertarget)", + type=str, ) parser.add_argument( - '-r', '--random', - action='store_true', - help='use a random scanner' + "-r", + "--random", + action="store_true", + help="use a random scanner" ) parser.add_argument( - '-l', '--list', - action='store_true', - help='list scanners' - ) + "-l", + "--list", + action="store_true", + help="list scanners") parser.add_argument( - '-a', '--all', - action='store_true', - help='use all the scanners' + "-a", + "--all", + action="store_true", + help="use all the scanners" ) parser.add_argument( - '-d', '--debug', - action='store_true', - help='debug mode (cli mode off & show network errors)' + "-d", + "--debug", + action="store_true", + help="debug mode (cli mode off & show network errors)", ) return parser def display(results): - for line in results.split('\n'): + for line in results.split("\n"): if not line: continue - elif 'tcp' in line or 'udp' in line: - if 'open' in line: + elif "tcp" in line or "udp" in line: + if "open" in line: line = crayons.green(line) - elif 'closed' in line: + elif "closed" in line: line = crayons.red(line) - elif 'filtered' in line: + elif "filtered" in line: line = crayons.yellow(line) print(line) @@ -85,39 +88,39 @@ def main(): parser = get_parser() args = vars(parser.parse_args()) - if args['version']: - print(f'v{VERSION}') + if args["version"]: + print(f"v{VERSION}") return - if args['list']: + if args["list"]: print(SCAN_LIST) return - if not args['target']: + if not args["target"]: parser.print_help() return - if args['debug']: + if args["debug"]: sl.cli_mode = False - target = args['target'] - scanner = args['scanner'].lower() + target = args["target"] + scanner = args["scanner"].lower() - print(f'Running scanless v{VERSION}...\n') + print(f"Running scanless v{VERSION}...\n") scanners = sl.scanners.keys() - if args['all']: + if args["all"]: for s in scanners: - print(f'{s}:') - display(sl.scan(target, scanner=s)['raw']) + print(f"{s}:") + display(sl.scan(target, scanner=s)["raw"]) print() return - if args['random']: + if args["random"]: scanner = choice(list(scanners)) if scanner in scanners: - print(f'{scanner}:') - display(sl.scan(target, scanner=scanner)['raw']) + print(f"{scanner}:") + display(sl.scan(target, scanner=scanner)["raw"]) else: - print('Scanner not found, see --list to view all supported scanners.') + print("Scanner not found, see --list to view all supported scanners.") diff --git a/scanless/core.py b/scanless/core.py index a226a49..03692a8 100644 --- a/scanless/core.py +++ b/scanless/core.py @@ -1,5 +1,3 @@ -"""scanless.core""" - import os import re import bs4 @@ -8,27 +6,26 @@ from random import choice from scanless.exceptions import ScannerNotFound, ScannerRequestError -URL_HACKERTARGET = 'https://hackertarget.com/nmap-online-port-scanner/' -URL_IPFINGERPRINTS = 'https://www.ipfingerprints.com/scripts/getPortsInfo.php' -URL_SPIDERIP = 'https://spiderip.com/inc/port_scan.php' -URL_STANDINGTECH = 'https://portscanner.standingtech.com/portscan.php?port={0}&host={1}&protocol=TCP' -URL_T1SHOPPER = 'http://www.t1shopper.com/tools/port-scan/result/' -URL_VIEWDNS = 'https://viewdns.info/portscan/?host={0}' -URL_YOUGETSIGNAL = 'https://ports.yougetsignal.com/short-scan.php' +URL_HACKERTARGET = "https://hackertarget.com/nmap-online-port-scanner/" +URL_IPFINGERPRINTS = "https://www.ipfingerprints.com/scripts/getPortsInfo.php" +URL_SPIDERIP = "https://spiderip.com/inc/port_scan.php" +URL_STANDINGTECH = "https://portscanner.standingtech.com/portscan.php?port={0}&host={1}&protocol=TCP" +URL_VIEWDNS = "https://viewdns.info/portscan/?host={0}" +URL_YOUGETSIGNAL = "https://ports.yougetsignal.com/short-scan.php" pwd = os.path.abspath(os.path.dirname(__file__)) -nmap_file = os.path.join(pwd, 'static/nmap-services.txt') -ua_file = os.path.join(pwd, 'static/user-agents.txt') +nmap_file = os.path.join(pwd, "static/nmap-services.txt") +ua_file = os.path.join(pwd, "static/user-agents.txt") NMAP_SERVICES = open(nmap_file).read().splitlines() USER_AGENTS = open(ua_file).read().splitlines() -OUTPUT_TEMPLATE = 'PORT STATE SERVICE\n{lines}' -NETWORK_ERROR_MSG = 'Network error, see --debug for details.' +OUTPUT_TEMPLATE = "PORT STATE SERVICE\n{lines}" +NETWORK_ERROR_MSG = "Network error, see --debug for details." def lookup_service(port): for line in NMAP_SERVICES: - if f'{port}/tcp' in line: + if f"{port}/tcp" in line: return line.split()[0] @@ -38,23 +35,25 @@ def generate_output(raw_data): for raw in raw_data: p, state = raw service = lookup_service(p) - port = f'{p}/tcp' - lines.append(f'{port:<9} {state:<6} {service}') - return OUTPUT_TEMPLATE.format(lines='\n'.join(lines)) + port = f"{p}/tcp" + lines.append(f"{port:<9} {state:<6} {service}") + return OUTPUT_TEMPLATE.format(lines="\n".join(lines)) def parse(output): parsed_output = list() - for line in output.split('\n'): - if '/tcp' in line or '/udp' in line: + for line in output.split("\n"): + if "/tcp" in line or "/udp" in line: port_str, state, service = line.split() - port, protocol = port_str.split('/') - parsed_output.append({ - 'port': port, - 'state': state, - 'service': service, - 'protocol': protocol, - }) + port, protocol = port_str.split("/") + parsed_output.append( + { + "port": port, + "state": state, + "service": service, + "protocol": protocol, + } + ) return parsed_output @@ -63,170 +62,191 @@ def __init__(self, cli_mode=False): self.cli_mode = cli_mode self.session = requests.Session() self.scanners = { - 'hackertarget': self.hackertarget, - 'ipfingerprints': self.ipfingerprints, - 'spiderip': self.spiderip, - 'standingtech': self.standingtech, - 't1shopper': self.t1shopper, - 'viewdns': self.viewdns, - 'yougetsignal': self.yougetsignal, + "hackertarget": self.hackertarget, + "ipfingerprints": self.ipfingerprints, + "spiderip": self.spiderip, + "standingtech": self.standingtech, + "viewdns": self.viewdns, + "yougetsignal": self.yougetsignal, } - def scan(self, target, scanner='hackertarget'): + def scan(self, target, scanner="hackertarget"): if scanner not in self.scanners: - raise ScannerNotFound(f'Unknown scanner, {scanner}.') + raise ScannerNotFound(f"Unknown scanner, {scanner}.") return self.scanners[scanner](target) def _randomize_user_agent(self): - self.session.headers['User-Agent'] = choice(USER_AGENTS) + self.session.headers["User-Agent"] = choice(USER_AGENTS) - def _request(self, url, payload=None, method='POST'): + def _request(self, url, payload=None, method="POST"): self._randomize_user_agent() try: response = self.session.request(method, url, data=payload, timeout=30) response.raise_for_status() except Exception as e: if self.cli_mode: - return (None, 'ERROR') + return (None, "ERROR") raise ScannerRequestError(e) - return (response.content.decode('utf-8'), 'OK') + return (response.content.decode("utf-8"), "OK") def _return_dict(self, raw_output, parsed_output): - return {'raw': raw_output, 'parsed': parsed_output} + return {"raw": raw_output, "parsed": parsed_output} def hackertarget(self, target): payload = { - 'theinput': target, - 'thetest': 'nmap', - 'name_of_nonce_field': '5a8d0006b9', - '_wp_http_referer': '/nmap-online-port-scanner/' + "theinput": target, + "thetest": "nmap", + "name_of_nonce_field": "5a8d0006b9", + "_wp_http_referer": "/nmap-online-port-scanner/", } scan_results, status = self._request(URL_HACKERTARGET, payload) - if status != 'OK': + if status != "OK": return self._return_dict(NETWORK_ERROR_MSG, list()) - soup = bs4.BeautifulSoup(scan_results, 'html.parser') - output = soup.findAll('pre', {'id': 'formResponse'})[0].string - raw_output = output.replace('\\n', '\n').strip() + soup = bs4.BeautifulSoup(scan_results, "html.parser") + output = soup.findAll("pre", {"id": "formResponse"})[0].string + raw_output = output.replace("\\n", "\n").strip() parsed_output = parse(raw_output) return self._return_dict(raw_output, parsed_output) def ipfingerprints(self, target): payload = { - 'remoteHost': target, - 'start_port': 20, - 'end_port': 512, - 'normalScan': 'No', - 'scan_type': 'connect', - 'ping_type': 'none', - 'os_detect': 'on' + "remoteHost": target, + "start_port": 20, + "end_port": 512, + "normalScan": "No", + "scan_type": "connect", + "ping_type": "none", + "os_detect": "on", } scan_results, status = self._request(URL_IPFINGERPRINTS, payload) - if status != 'OK': + if status != "OK": return self._return_dict(NETWORK_ERROR_MSG, list()) - output = re.sub('<[^<]+?>', '', scan_results) - raw_output = output.replace('\\n','\n').replace('\\/','/')[36:-46].strip() + output = re.sub("<[^<]+?>", "", scan_results) + raw_output = output.replace("\\n", "\n").replace("\\/", "/")[36:-46].strip() parsed_output = parse(raw_output) return self._return_dict(raw_output, parsed_output) def spiderip(self, target): ports = [ - 21, 22, 25, 80, 110, 143, 443, 465, 993, 995, 1433, 3306, 3389, - 5900, 8080, 8443 + 21, + 22, + 25, + 80, + 110, + 143, + 443, + 465, + 993, + 995, + 1433, + 3306, + 3389, + 5900, + 8080, + 8443, ] - payload = {'ip': target, 'language[]': ports} + payload = {"ip": target, "language[]": ports} scan_results, status = self._request(URL_SPIDERIP, payload) - if status != 'OK': + if status != "OK": return self._return_dict(NETWORK_ERROR_MSG, list()) - scan_results = scan_results.split('/images/') + scan_results = scan_results.split("/images/") scan_results.pop(0) raw_data = list() for result, port in zip(scan_results, ports): - if 'open' in result: - raw_data.append((port, 'open')) + if "open" in result: + raw_data.append((port, "open")) else: - raw_data.append((port, 'closed')) + raw_data.append((port, "closed")) raw_output = generate_output(raw_data) parsed_output = parse(raw_output) return self._return_dict(raw_output, parsed_output) def standingtech(self, target): - ports = [ - 21, 22, 23, 25, 80, 110, 139, 143, 443, 445, 1433, 3306, 3389, 5900 - ] + ports = [21, 22, 23, 25, 80, 110, 139, 143, 443, 445, 1433, 3306, 3389, 5900] raw_data = list() for p in ports: scan_results, status = self._request( - URL_STANDINGTECH.format(p, target), - method='GET' + URL_STANDINGTECH.format(p, target), method="GET" ) - if status != 'OK': + if status != "OK": return self._return_dict(NETWORK_ERROR_MSG, list()) - if 'open' in scan_results: - raw_data.append((p, 'open')) - else: - raw_data.append((p, 'closed')) - raw_output = generate_output(raw_data) - parsed_output = parse(raw_output) - return self._return_dict(raw_output, parsed_output) - - def t1shopper(self, target): - ports = [ - 21, 23, 25, 80, 110, 139, 445, 1433, 1521, 1723, 3306, 3389, - 5900, 8080 - ] - payload = {'scan_host': target, 'port_array[]': ports} - scan_results, status = self._request(URL_T1SHOPPER, payload) - if status != 'OK': - return self._return_dict(NETWORK_ERROR_MSG, list()) - soup = bs4.BeautifulSoup(scan_results, 'html.parser') - raw_data = list() - for tt, port in zip(soup.find('pre').find_all('tt'), ports): - if tt.text.find('isn\'t') > -1: - raw_data.append((port, 'closed')) + if "open" in scan_results: + raw_data.append((p, "open")) else: - raw_data.append((port, 'open')) + raw_data.append((p, "closed")) raw_output = generate_output(raw_data) parsed_output = parse(raw_output) return self._return_dict(raw_output, parsed_output) def viewdns(self, target): ports = [ - 21, 22, 23, 25, 53, 80, 110, 139, 143, 443, 445, 1433, 1521, - 3306, 3389 + 21, + 22, + 23, + 25, + 53, + 80, + 110, + 139, + 143, + 443, + 445, + 1433, + 1521, + 3306, + 3389, ] - scan_results, status = self._request(URL_VIEWDNS.format(target), method='GET') - if status != 'OK': + scan_results, status = self._request(URL_VIEWDNS.format(target), method="GET") + if status != "OK": return self._return_dict(NETWORK_ERROR_MSG, list()) - soup = bs4.BeautifulSoup(scan_results, 'html.parser') - table, rows = soup.find('table'), soup.findAll('tr') + soup = bs4.BeautifulSoup(scan_results, "html.parser") + table, rows = soup.find("table"), soup.findAll("tr") raw_data = list() for tr, port in zip(rows[7:22], ports): - cols = str(tr.findAll('td')) - if 'error.GIF' in cols: - raw_data.append((port, 'closed')) + cols = str(tr.findAll("td")) + if "error.GIF" in cols: + raw_data.append((port, "closed")) else: - raw_data.append((port, 'open')) + raw_data.append((port, "open")) raw_output = generate_output(raw_data) parsed_output = parse(raw_output) return self._return_dict(raw_output, parsed_output) def yougetsignal(self, target): ports = [ - 21, 22, 23, 25, 53, 80, 110, 115, 135, 139, 143, 194, 443, 445, - 1433, 3306, 3389, 5632, 5900, 6112 + 21, + 22, + 23, + 25, + 53, + 80, + 110, + 115, + 135, + 139, + 143, + 194, + 443, + 445, + 1433, + 3306, + 3389, + 5632, + 5900, + 6112, ] - payload = {'remoteAddress': target} + payload = {"remoteAddress": target} scan_results, status = self._request(URL_YOUGETSIGNAL, payload) - if status != 'OK': + if status != "OK": return self._return_dict(NETWORK_ERROR_MSG, list()) - soup = bs4.BeautifulSoup(scan_results, 'html.parser') - imgs = soup.findAll('img') + soup = bs4.BeautifulSoup(scan_results, "html.parser") + imgs = soup.findAll("img") raw_data = list() for img, port in zip(imgs, ports): - if 'red' in str(img): - raw_data.append((port, 'closed')) + if "red" in str(img): + raw_data.append((port, "closed")) else: - raw_data.append((port, 'open')) + raw_data.append((port, "open")) raw_output = generate_output(raw_data) parsed_output = parse(raw_output) return self._return_dict(raw_output, parsed_output) diff --git a/scanless/exceptions.py b/scanless/exceptions.py index cf095cd..0bc3090 100644 --- a/scanless/exceptions.py +++ b/scanless/exceptions.py @@ -1,6 +1,3 @@ -"""scanless.exceptions""" - - class ScannerNotFound(Exception): pass diff --git a/setup.py b/setup.py index 81e1263..e5d2c36 100644 --- a/setup.py +++ b/setup.py @@ -4,43 +4,34 @@ from setuptools import setup directory = os.path.abspath(os.path.dirname(__file__)) -with open(os.path.join(directory, 'README.md'), encoding='utf-8') as f: +with open(os.path.join(directory, "README.md"), encoding="utf-8") as f: long_description = f.read() setup( - name='scanless', - packages=[ - 'scanless', - 'scanless.static' - ], - package_data = { - 'scanless.static': ['*.txt'] - }, - version='2.1.5', - description='An online port scan scraper.', + name="scanless", + packages=["scanless", "scanless.static"], + package_data={"scanless.static": ["*.txt"]}, + version="2.1.6", + description="An online port scan scraper.", long_description=long_description, - long_description_content_type='text/markdown', - license='Unlicense', - url='https://github.com/vesche/scanless', - author='Austin Jackson', - author_email='vesche@protonmail.com', + long_description_content_type="text/markdown", + license="Unlicense", + url="https://github.com/vesche/scanless", + author="Austin Jackson", + author_email="vesche@protonmail.com", entry_points={ - 'console_scripts': [ - 'scanless = scanless.cli:main', + "console_scripts": [ + "scanless = scanless.cli:main", ] }, - install_requires=[ - 'beautifulsoup4', - 'crayons', - 'requests' - ], + install_requires=["beautifulsoup4", "crayons", "requests"], classifiers=[ - 'Development Status :: 5 - Production/Stable', - 'Environment :: Console', - 'Intended Audience :: Information Technology', - 'License :: Public Domain', - 'Programming Language :: Python :: 3', - 'Programming Language :: Python :: 3.9', - 'Topic :: Security' - ] -) \ No newline at end of file + "Development Status :: 5 - Production/Stable", + "Environment :: Console", + "Intended Audience :: Information Technology", + "License :: Public Domain", + "Programming Language :: Python :: 3", + "Programming Language :: Python :: 3.9", + "Topic :: Security", + ], +)