#!/usr/bin/python3 from socket import * import socks import argparse import time from ipaddress import * ## This function tries to open a connection on a specific port, returns True if successful def connScan(host, port, wait, notor): ## If notor is set to True, it doesn't use the socks proxy if notor: sckt = socket(AF_INET, SOCK_STREAM) else: sckt = socks.socksocket() sckt.settimeout(wait) ## Try to connect, return True on success and False on failure try: sckt.connect((host, port)) sckt.close() return True except KeyboardInterrupt: exit() except: return False ## This function goes throgh all ports and calls connScan for each one, returns list of open ports def portScan(host, ports, wait, notor): openports = list() for p in ports: if p > 65535: return openports elif connScan(host, p, wait, notor): openports.append(p) return openports ## This function goes throgh all hosts and calls portScan for each one, returns dictionary of hosts with open ports def hostScan(host, ports, wait, notor): ret = dict() ## Check if python version 3 try: host = unicode(host) except: pass ## Check if host is a network range, don't use tor for private IPs try: ips = ip_network(host) if ips.num_addresses > 1: for ip in ips.hosts(): if ip.is_private: ret[str(ip)] = portScan(str(ip), ports, wait, True) else: ret[str(ip)] = portScan(str(ip), ports, wait, notor) else: if ips.is_private: ret[str(host)] = portScan(str(host), ports, wait, True) else: ret[str(host)] = portScan(str(host), ports, wait, notor) ## Otherwise scan host as usual except: if host == 'localhost': ret[str(host)] = portScan(str(host), ports, wait, True) else: ret[str(host)] = portScan(str(host), ports, wait, notor) return ret def main(): ## Record time of program starting in seconds startTime = time.time() ## Define and parse the arguments parser = argparse.ArgumentParser(description="Simple stateful port scanner that works over Tor") parser.add_argument("--version", dest="version", help="print version information and exit", action="store_true") parser.add_argument("HOSTS", help="IP address or domain to scan", default="empty_host", nargs="?") parser.add_argument("-H", "--hosts", metavar="HOSTS", dest="tgtHost", help="IP address or domain to scan", default="empty_host_option") parser.add_argument("-p", "--ports", metavar="PORTS", dest="tgtPort", help="ports to scan, seperated by a comma", default="20-25,53,80-85,443-445,8080,8333,9050,9150") parser.add_argument("-t", "--timeout", metavar="TIMEOUT", dest="sockTimeout", type=int, help="seconds to wait before connection timeout for each port", default=3) parser.add_argument("--clearnet", dest="clearnet", help="don't use Tor for scanning, connect directly instead", action="store_true") parser.add_argument("--torport", metavar="TORPORT", dest="torPort", type=int, help="port on which Tor is listening on", default="9050") parser.add_argument("--output", metavar="OUTFILE", dest="outFile", help="write scan results to output file", default="empty_outfile") args = parser.parse_args() ## Version argument if args.version: print("Tmap 1.0.0") print("License GPLv3+: GNU GPL version 3 or later ") print("This is free software: you are free to change and redistribute it.") print("There is NO WARRANTY, to the extent permitted by law.") exit() ## Set Tor as default Tor proxy for the scanner socks.setdefaultproxy(socks.PROXY_TYPE_SOCKS5, "127.0.0.1", args.torPort) ## Combine HOST and --hosts values if args.HOSTS == "empty_host": if args.tgtHost == "empty_host_option": parser.print_help() print ("Host must be specified") exit() else: args.HOSTS = args.tgtHost else: if args.tgtHost != "empty_host_option": args.HOSTS = args.HOSTS + "," + args.tgtHost ## Load specified ports into PORTS list PORTS = list() for p in args.tgtPort.split(","): ## If p is not a range, add it to the list of ports to scan if "-" not in p: try: p = int(p) PORTS.append(p) except: parser.print_help() print("Ports must be integers") exit() ## If p is a range, add a range of ports to the list of ports to scan else: try: p = list(map(int, p.split("-"))) except: parser.print_help() print("Ports in a range must be integers") exit() ## Range needs to be defined as exactly two integers separated by "-" if len(p) != 2 or p[0] > p[1]: parser.print_help() print("Port range improperly defined") exit() else: p = list(range(p[0],p[1]+1)) PORTS += p ## Load other variables HOSTS = args.HOSTS.split(",") WAIT_TIME = args.sockTimeout CLEARNET = False OUTFILE = args.outFile if args.clearnet: CLEARNET=True ## Check if Tor is running if CLEARNET is False if CLEARNET == False: if connScan("127.0.0.1", args.torPort, 3, True): pass else: print("Tor is not running on port {}.".format(args.torPort)) exit() ## Checking for file output if OUTFILE != "empty_outfile": f = open(OUTFILE, "w") ## Display message that scan is starting print("Starting a scan...") ## Scan each host in HOSTS list r = dict() for h in HOSTS: r = hostScan(h, PORTS, WAIT_TIME, CLEARNET) if OUTFILE == "empty_outfile": print("Results for: {}".format(h)) else: f.write("Results for: {}".format(h)) for i in r.keys(): if len(r[i]) != 0: if OUTFILE == "empty_outfile": print('{} open ports: {}'.format(i, str(r[i]))) else: f.write('{} open ports: {}\n'.format(i, str(r[i]))) ## Record time of program stopping and display the time running to the user endTime = time.time() totalTime = round(endTime - startTime, 2) if OUTFILE == "empty_outfile": print("Scan done in {} seconds".format(totalTime)) else: f.write("Scan done in {} seconds\n".format(totalTime)) f.close() print("Results written to {}".format(OUTFILE)) ## We are done here exit() if __name__ == "__main__": main()