So I decided to write a simple python port scanner but I wanted it to support to TCP, UDP, port ranges (22,23,135-139,443,445,3389 etc) and IP ranges(192.168.0.1/24, 192.168.10-20, example.com)

I also wanted to limit the libraries I used for use on locked down systems, thus, we only use socket, sys and argparse.

The first thing to do was get the code working for a single host and then slowly add bits to it in order to get it working. This is alpha, so your mileage may vary but here it is. Credit to Brandon Sterne as I ripped apart his code for a python CIDR converter.

1
2
3
4
5
6
7
8
9
10
11
12
13
usage: nmap.py [-h] [-v] [-sS] [-sU] [-p PORTS] [-t TARGETS]

nmap.py - Replicates limited nmap functionality in python

optional arguments:
  -h, --help            show this help message and exit
  -v, --verbose         Enable this for full output
  -sS, --tcpscan        Enable this for TCP scans
  -sU, --udpscan        Enable this for UDP scans
  -p PORTS, --ports PORTS
                        The ports you want to scan (21,22,80,135-139,443,445)
  -t TARGETS, --targets TARGETS
                        The target(s) you want to scan (192.168.0.1)

And then the code: (but better to download from github)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
#-------------------------------------------------------------------------------
# Name:         nmap.py
# Purpose:      Replicates limited nmap functionality using python
# Author:       phillipsme
# Created:      12/08/2014
# Copyright:    (c) phillipsme 2014
# Licence:      Free to use, free to have fun!
# Version:      alpha!!! (0.1)
# ToDo:         verbose,dont do reverse lookup
#-------------------------------------------------------------------------------
import socket
import argparse
import sys

def main():
    parser = argparse.ArgumentParser(description='nmap.py - Replicates limited nmap functionality in python')
    parser.add_argument('-v', '--verbose', action='store_true', help='Enable this for full output')
    parser.add_argument('-sS', '--tcpscan', action='store_true', help='Enable this for TCP scans')
    parser.add_argument('-sU', '--udpscan', action='store_true', help='Enable this for UDP scans')
    parser.add_argument('-p', '--ports', default='1-1024', help='The ports you want to scan (21,22,80,135-139,443,445)')
    parser.add_argument('-t', '--targets', help='The target(s) you want to scan (192.168.0.1)')
    if len(sys.argv)==1: parser.print_help(); sys.exit(0)
    args = parser.parse_args()

    # Set target (and convert for FQDN)
    targets=[]
    if args.targets:
        if '/' in args.targets: #found cidr target
            targets = returnCIDR(args.targets)
        elif '-' in args.targets:
            targets = iprange(args.targets)
        else:
            try: targets.append(socket.gethostbyname(args.targets)) # get IP from FQDN
            except: errormsg("Failed to translate hostname to IP address")
    else: parser.print_help(); errormsg("You need to set a hostname")

    # Set ports
    if args.ports == '-': args.ports = '1-65535'
    ranges = (x.split("-") for x in args.ports.split(","))
    ports = [i for r in ranges for i in range(int(r[0]), int(r[-1]) + 1)]

    # Output command line args to screen
    if args.verbose: printmsg("Arguments used:"); print args ;

    # Start Scanning
    for target in targets:
        tcpports, udpports = portscan(target,ports,args.tcpscan,args.udpscan,args.verbose)

def portscan(target,ports,tcp,udp,verbose):
    #target=IPaddr,ports=list of ports,tcp=true/false,udp=true/false,verbose=true/false
    printmsg(("Now scanning %s" % (target)))
    tcpports=[]
    udpports=[]
    if tcp:
        for portnum in ports:
            try:
                s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
                s.settimeout(0.01)
                s.connect((target, portnum))
            except Exception:
                failvar = 0
                if verbose: print "%d/tcp \tclosed" % (portnum)
            else:
                print "%d/tcp \topen"% (portnum)
                tcpports.append(portnum)
            s.close()
    if udp:
        for portnum in ports:
            try:
                s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
                s.settimeout(0.1)
                s.sendto("--TEST LINE--", (target, portnum))
                recv, svr = s.recvfrom(255)
            except Exception, e:
                try: errno, errtxt = e
                except ValueError:
                    print "%d/udp \topen"% (portnum)
                    udpports.append(portnum)
                else:
                    if verbose: print "%d/udp \tclosed" % (portnum)
            s.close()
    printmsg(("%i open TCP ports, %i open UDP ports of %i ports scanned" % (len(tcpports),len(udpports),len(ports))))
    return tcpports, udpports

def errormsg(msg): print "[!] Error: %s" % (msg) ; sys.exit(1)
def printmsg(msg): print "[+] nmap.py: %s" % (msg)

def iprange(addressrange): # converts a ip range into a list
    list=[]
    first3octets = '.'.join(addressrange.split('-')[0].split('.')[:3]) + '.'
    for i in range(int(addressrange.split('-')[0].split('.')[3]),int(addressrange.split('-')[1])+1):
        list.append(first3octets+str(i))
    return list

def ip2bin(ip):
    b = ""
    inQuads = ip.split(".")
    outQuads = 4
    for q in inQuads:
        if q != "": b += dec2bin(int(q),8); outQuads -= 1
    while outQuads > 0: b += "00000000"; outQuads -= 1
    return b

def dec2bin(n,d=None):
    s = ""
    while n>0:
        if n&1: s = "1"+s
        else: s = "0"+s
        n >>= 1
    if d is not None:
        while len(s)<d: s = "0"+s
    if s == "": s = "0"
    return s

def bin2ip(b):
    ip = ""
    for i in range(0,len(b),8):
        ip += str(int(b[i:i+8],2))+"."
    return ip[:-1]

def returnCIDR(c):
    parts = c.split("/")
    baseIP = ip2bin(parts[0])
    subnet = int(parts[1])
    ips=[]
    if subnet == 32: return bin2ip(baseIP)
    else:
        ipPrefix = baseIP[:-(32-subnet)]
        for i in range(2**(32-subnet)): ips.append(bin2ip(ipPrefix+dec2bin(i, (32-subnet))))
        return ips

if __name__ == '__main__':
    main()

And for the sample output:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
C:\Users\phillipsme\Documents>python nmap.py -sS -sU -p 22,23,80,123,135-139,427,443,445,3389,8081-8082 -t 127.0.0.1/30
[+] nmap.py: Now scanning 127.0.0.0
22/udp  open
23/udp  open
80/udp  open
123/udp         open
135/udp         open
136/udp         open
137/udp         open
138/udp         open
139/udp         open
427/udp         open
443/udp         open
445/udp         open
3389/udp        open
8081/udp        open
8082/udp        open
[+] nmap.py: 0 open TCP ports, 15 open UDP ports of 15 ports scanned
[+] nmap.py: Now scanning 127.0.0.1
135/tcp         open
445/tcp         open
3389/tcp        open
8081/tcp        open
123/udp         open
427/udp         open
3389/udp        open
8081/udp        open
8082/udp        open
[+] nmap.py: 4 open TCP ports, 5 open UDP ports of 15 ports scanned
[+] nmap.py: Now scanning 127.0.0.2
135/tcp         open
445/tcp         open
3389/tcp        open
8081/tcp        open
123/udp         open
427/udp         open
3389/udp        open
8081/udp        open
8082/udp        open
[+] nmap.py: 4 open TCP ports, 5 open UDP ports of 15 ports scanned
[+] nmap.py: Now scanning 127.0.0.3
135/tcp         open
445/tcp         open
3389/tcp        open
8081/tcp        open
123/udp         open
427/udp         open
3389/udp        open
8081/udp        open
8082/udp        open
[+] nmap.py: 4 open TCP ports, 5 open UDP ports of 15 ports scanned

C:\Users\phillipsme\Documents>

Leave a Reply