├── README.md
└── cipscan.py
/README.md:
--------------------------------------------------------------------------------
1 | # SCADA-CIP-Discovery
2 | Common Industrial Protocol based device scanner over the internet
3 | This program needs more refinement. The response packets are not displayed as it should in a refined manner.
4 | Use wireshark when running this script with the filter set to enip to view the response data for analysis
5 | Run using "python cipscan.py 127.0.0.0/24"
6 | A usual response packet will contain information like this
7 |
8 | Vendor ID: Rockwell Automation/Allen-Bradley (0x0001)
9 | Device Type: Programmable Logic Controller (14)
10 | Product Code: XX
11 | Revision: 2.11
12 | Status: 0x0004
13 | Serial Number: 0xXXXXXXdX
14 | Product Name Length: XX
15 | Product Name: XXXX-LXXBXB B/XX.XX
16 | State: 0x00
17 |
18 | In addition to this the private IP addresses of the system will also be included like 192.168.0.17
19 |
--------------------------------------------------------------------------------
/cipscan.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 | """
3 | File: cipscan.py
4 | Desc: Common Industrial Protocol Scanner UDP
5 | Version: 1.0
6 | Copyright (c) 2016 Ayushman Dutta
7 | This program is free software: you can redistribute it and/or modify
8 | it under the terms of the GNU General Public License as published by
9 | the Free Software Foundation version either version 3 of the License,
10 | or (at your option) any later version.
11 | This program is distributed in the hope that it will be useful,
12 | but WITHOUT ANY WARRANTY; without even the implied warranty of
13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 | GNU General Public License for more details.
15 | You should have received a copy of the GNU General Public License
16 | along with this program. If not, see .
17 | """
18 |
19 | import socket
20 | import struct
21 | import optparse
22 | from IPy import IP
23 | import sys
24 | from multiprocessing import Process,Queue
25 | class CipScan(Process):
26 |
27 | def __init__(self,iprange,options):
28 | Process.__init__(self)
29 | self.iprange=iprange
30 | self.options=options
31 | def run(self):
32 | for ip in self.iprange:
33 | try:
34 | s=socket.socket(socket.AF_INET,socket.SOCK_DGRAM)
35 | #s=socket.socket(socket.AF_INET,socket.SOCK_STREAM) # For TCP based queries
36 | s.settimeout(float(self.options.timeout)/float(100))
37 | msg = str(ip)+":"+str(self.options.port)
38 | print("Scanning"+" "+msg+"\n")
39 | conn=s.connect((str(ip),self.options.port))
40 | packet=struct.pack('24B',0x63, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00)
41 | #packet=struct.pack('73B',0x70, 0x00, 0x31, 0x00, 0xc9, 0x74, 0xb8, 0x96, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0xa1, 0x00, 0x04, 0x00, 0x0d, 0x00, 0xfe, 0x80, 0xb1, 0x00, 0x1d, 0x00, 0xf9, 0x39, 0xcb, 0x00, 0x00, 0x00, 0x07, 0x4d, 0x00, 0x04, 0x02, 0x5c, 0x0b, 0x4f, 0x00, 0xce, 0xf0, 0x00, 0x07, 0x00, 0x00, 0x3f, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff) #PCCC communication TCP based
42 | except socket.error:
43 | msg="Failed to Connect\n"
44 | print(msg+"\n")
45 | s.close()
46 | break
47 | try:
48 | s.send(packet)
49 | print('Sent'+' '+packet)
50 | except socket.error:
51 | msg="Failed to Send\n"
52 | print(msg)
53 | s.close()
54 | break
55 | try:
56 | recv=s.recvfrom(1024)
57 | print(recv)
58 | except socket.error:
59 | msg="Failed to Receive\n"
60 | print(msg+"\n")
61 | s.close()
62 | #break
63 | s.close()
64 | print("Scan has completed"+"\n")
65 |
66 |
67 |
68 | def main():
69 | p = optparse.OptionParser( description=' Finds CIP devices in IP range and determines Vendor Specific Information along with Internal private IP.\nOutputs in ip:port sid format.',
70 | prog='CipScan',
71 | version='CIP Scan 1.0',
72 | usage = "usage: %prog [options] IPRange")
73 | p.add_option('--port', '-p', type='int', dest="port", default=44818, help='CIP port DEFAULT:44818')
74 | p.add_option('--timeout', '-t', type='int', dest="timeout", default=500, help='socket timeout (mills) DEFAULT:500')
75 | options, arguments = p.parse_args()
76 | if len(arguments) == 1:
77 | print("Starting Common Industrial Protocol Scan"+"\n")
78 | i=""
79 | i=arguments[0]
80 | iprange=IP(i)
81 | q = Queue()
82 | for ip in iprange:
83 | print("Starting Multithreading"+"\n")
84 | p = CipScan(ip,options).start()
85 | q.put(p,False)
86 | else:
87 | p.print_help()
88 | if __name__ == '__main__':
89 | try:
90 | main()
91 | except KeyboardInterrupt:
92 | print "Scan canceled by user."
93 | print "Thank you for using CIP Scan"
94 | except :
95 | sys.exit()
96 |
--------------------------------------------------------------------------------