diff --git a/portscout.py b/portscout.py new file mode 100644 index 0000000..cfb2362 --- /dev/null +++ b/portscout.py @@ -0,0 +1,134 @@ +#!/bin/python3 +import os +import sys +import socket +import argparse +import concurrent.futures +from termcolor import colored +from datetime import datetime as dt + +# Define the list of the top 100 commonly used ports +TOP_PORTS = [ + 20, 21, 22, 23, 25, 53, 67, 68, 69, 80, 81, 110, 111, 115, 123, + 135, 137, 138, 139, 143, 161, 162, 179, 194, 213, 443, 465, 514, + 515, 520, 530, 531, 532, 540, 554, 587, 601, 631, 636, 646, 660, + 669, 674, 688, 691, 694, 700, 707, 750, 751, 760, 761, 762, 780, + 800, 808, 843, 873, 902, 989, 990, 991, 992, 993, 994, 995, 1080, + 1194, 1723, 3306, 3389, 5432, 5900, 6000, 6379, 6466, 8080, 8443, + 8888, 9000, 9090, 9200, 9300, 9418 +] + + +def exit_program() -> None: + """Exit the program.""" + sys.exit() + + +def get_arguments(): + """Parse command line arguments.""" + parser = argparse.ArgumentParser(description="Get arguments for port scanner") + parser.add_argument('-H', '--host', dest='HOST', help="IP address of host machine", required=True) + parser.add_argument('-o', '--output', dest='OUTPUT', help="Write the output into a file") + parser.add_argument('-p', '--port', dest='PORTS', help="Ports to scan, input in the form -") + parser.add_argument('-v', '--verbose', action='store_true', help="Increase output verbosity") + return parser.parse_args() + + +def check_port(port: int) -> bool: + """Check if a port is open.""" + with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as sock: + socket.setdefaulttimeout(0.5) # Timeout of 0.5 seconds for quicker response + result = sock.connect_ex((target, port)) + return result == 0 + + +def display_conf_data() -> None: + """Display configuration data for the scan.""" + print(colored("-" * 50, 'cyan')) + print(colored("Scanning Configuration", 'cyan')) + print(colored("-" * 50, 'cyan')) + print(f"Target Host: {target}") + + # Indicate if the top ports are being used + if port_start == 1 and port_end == 65535: + print(f"Ports Range: Top 100 Ports ({min(TOP_PORTS)} - {max(TOP_PORTS)})") + else: + print(f"Ports Range: {port_start} - {port_end}") + + print(f"Output File: {output_file if output_file else 'Not specified'}") + print(colored("-" * 50, 'cyan')) + + +def find_open_ports(): + """Find open ports using multithreading.""" + try: + with concurrent.futures.ThreadPoolExecutor(max_workers=200) as executor: + futures = {executor.submit(check_port, port): port for port in range(port_start, port_end + 1)} + + for future in concurrent.futures.as_completed(futures): + port = futures[future] + if future.result(): + if store_open_ports: + store_open_ports.write(f"{target}:{port} => OPEN\n") + print(colored(f"{target}:{port} => OPEN", 'green')) + open_ports.append(port) + elif args.verbose: # Print closed ports if verbosity is enabled + print(colored(f"{target}:{port} => CLOSED", 'yellow')) + + print(colored(f"\n{"-" * 50}\nScan Summary\n{"-" * 50}\n", 'magenta')) + print(colored(f"Total Open Ports: {len(open_ports)}", 'cyan')) + print(colored(f"Open Ports: {open_ports if open_ports else 'None found'}", 'cyan')) + + except KeyboardInterrupt: + print(colored("\n <- Keyboard Interruption - Terminating scan ->", "red")) + exit_program() + except socket.gaierror: + print(colored(" ! Host name could not be resolved! ", "red")) + exit_program() + except socket.error as e: + print(colored(f" ! Could not connect to host/ip {target}: {e}", "red")) + exit_program() + + +if __name__ == '__main__': + start_time = dt.now() + print(colored("Time started: " + str(start_time), 'blue')) + + args = get_arguments() + target = args.HOST + + # Set default port range to top 100 ports + if args.PORTS: + ports = args.PORTS + if "-" in ports: + port_start, port_end = map(int, ports.split("-")) + else: + port_start = int(ports) + port_end = port_start + else: + port_start, port_end = min(TOP_PORTS), max(TOP_PORTS) # Default to top ports + + # Check the start and end port values + if port_start > port_end: + port_start, port_end = port_end, port_start + + # Debugging outputs for the port range + print(colored(f"Scanning ports from {port_start} to {port_end}", 'yellow')) + + # Handle output file + output_file = args.OUTPUT + store_open_ports = open(f"{output_file}", "a") if output_file else None + + display_conf_data() + open_ports = [] + + find_open_ports() + + if store_open_ports: + store_open_ports.close() # Close the output file if it was opened + + end_time = dt.now() + total_time = end_time - start_time + print(colored(f"\nScanning completed in {total_time}", 'blue')) + print(colored("=" * 35, 'blue')) + exit_program() diff --git a/scanner.py b/scanner.py deleted file mode 100644 index 5fdf77f..0000000 --- a/scanner.py +++ /dev/null @@ -1,118 +0,0 @@ -#!/bin/python3 -import sys -import socket -import argparse -import subprocess -from termcolor import colored, cprint -from datetime import datetime as dt - -# clear your terminal -# subprocess.call('clear', shell=True) - -def exit() -> None: - sys.exit() - -def usage() -> None: - print(colored(f""" - => Usage <= - {"-"*50} - Syntax: - > python scanner_v2.py --host - > python scanner_v2.py --host -v - > python scanner_v2.py --host -o - > python scanner_v2.py --host -p - - > python scanner_v2.py --host -p - -v -o - {"-"*50} - """, 'green')) - exit() - -def get_arguments(): - parser = argparse.ArgumentParser(description="Get arguments for port scanner") - parser.add_argument("--host", dest="HOST", help="IP address of host machine") - parser.add_argument('-o', '--output', dest='OUTPUT', help="Write the output into a file") - parser.add_argument('-p', '--port', dest='PORTS', help="ports to scan, input in the form -", default="FULL") - # parser.add_argument('-v', '--verbose', default='VERBOSE', help="increase output verbosity", action="store_true") - args = parser.parse_args() - if not args.HOST: - print(colored(f"HOST IP address is required.", 'red')) - usage() - return args - -target = "" -port_start, port_end, count = 0, 100, 0 #default data -open_ports = [] -output_file = None -store_open_ports = '' - -def display_conf_data() -> None: - print("-" * 50) - print("Scanning host(IPv4) -> " + target) - # print("Target Ipv4: " + target) - print(f"Ports Range: {port_start}-{port_end}") - print(f"Output file: {output_file}") - print("-" * 50) - -def find_open_ports(): - try: - for port in range(port_start, port_end+1): - sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) - socket.setdefaulttimeout(1) - result = sock.connect_ex((target, port)) - if result == 0: - if output_file: - store_open_ports.write(f"{target}:{port} => OPEN\n") - print(f"{target}:{port} => OPEN") - open_ports.append(port) - global count - count = count + 1 - else: - print(f"{target}:{port} => CLOSED") - - sock.close() - - print(f''' -{"-"*50} -Total Open Ports: {count} -Open Ports: {open_ports} - ''') - - except KeyboardInterrupt: - print(colored("\n <- Keyboard Interruption - Terminating scan ->", "red")) - exit() - except socket.gaierror: - print(colored(" !Host name could not be resolved! ", "red")) - exit() - except socket.error: - print(colored(f" !Could not connect to host/ip {target} ", "red")) - exit() - -if __name__ == '__main__': - start_time = dt.now() - print("Time started: " + str(start_time)) - args = get_arguments() - target = args.HOST - ports = args.PORTS - if "-" in ports: - port_start, port_end = ports.split("-") - port_start = int(port_start) - port_end = int(port_end) - elif ports == "FULL": #complete port scan - port_start, port_end = 1, 65535 - else: - port_start = int(ports) or port_start - # check the start and end port values, if they are in same order or not - if port_start > port_end: - port_start, port_end = port_end, port_start - - if args.OUTPUT: - output_file = args.OUTPUT - store_open_ports = open(f"{output_file}", "a") - - display_conf_data() - find_open_ports() - - end_time = dt.now() - total_time = end_time - start_time - print(f"Scanning completed in {total_time}") - print("="*35) - exit()