mirror of git://git.acid.vegas/random.git
310 lines
10 KiB
Python
310 lines
10 KiB
Python
#!/usr/bin/env python
|
|
# Jupiter IRC Botnet - Developed by acidvegas in Python (https://acid.vegas/jupiter)
|
|
|
|
'''
|
|
Jupiter will create a botnet by connecting a defined number of clones to every EFMet server.
|
|
A single host could potentially create over 30 clones.
|
|
It is meant to monitor/jupe/hold nicks & be controlled to do just about anything.
|
|
The bot is designed to be very minimal, secure, & trustless by nature.
|
|
This means anyone can run a copy of your script on their server to help build your botnet.
|
|
|
|
Commands
|
|
id | Send bot identity
|
|
raw [-d] <data> | Send \<data> to server. optionally delayed with -d argument
|
|
monitor list | Return MONITOR list
|
|
monitor reset | Reset MONITOR list
|
|
monitor <+/-><nicks> | Add (+) or Remove (-) <nicks> from MONITOR list. (Can be a single nick or comma seperated list)
|
|
|
|
All commands must be prefixed with @all or the bots nick & will work in a channel or private message.
|
|
Raw data must be IRC RFC compliant data & any nicks in the MONITOR list will be juped as soon as they become available.
|
|
|
|
It is highly recommended that you use a random spoofing ident protocol daemon:
|
|
https://github.com/acidvegas/random/blob/master/irc/identd.py
|
|
'''
|
|
|
|
import random
|
|
import re
|
|
import socket
|
|
import ssl
|
|
import time
|
|
import threading
|
|
|
|
# Connection
|
|
servers = (
|
|
'efnet.deic.eu', # IPv6
|
|
'efnet.port80.se', # IPv6
|
|
'efnet.portlane.se', # IPv6
|
|
'irc.choopa.net', # IPv6
|
|
'irc.colosolutions.net',
|
|
'irc.du.se',
|
|
'irc.efnet.fr', # IPv6
|
|
'irc.efnet.nl', # IPv6 +6669
|
|
'irc.homelien.no', # IPv6
|
|
'irc.mzima.net', # IPv6 +6697
|
|
'irc.nordunet.se', # IPv6
|
|
'irc.prison.net',
|
|
'irc.underworld.no', # IPv6
|
|
'irc.servercentral.net' # +9999
|
|
)
|
|
ipv6 = False
|
|
vhosts = None # Use (line.rstrip() for line in open('vhosts.txt','r').readlines() if line) for reading from a file.
|
|
channel = '#jupiter'
|
|
key = None
|
|
|
|
# Settings
|
|
admin = 'nick!user@host' # Can use wildcards (Must be in nick!user@host format)
|
|
concurrency = 3 # Number of clones to load per server
|
|
id = 'TEST' # Unique ID so you can tell which bots belong what server
|
|
|
|
# Formatting Control Characters / Color Codes
|
|
bold = '\x02'
|
|
reset = '\x0f'
|
|
green = '03'
|
|
red = '04'
|
|
purple = '06'
|
|
orange = '07'
|
|
yellow = '08'
|
|
light_green = '09'
|
|
cyan = '10'
|
|
light_cyan = '11'
|
|
light_blue = '12'
|
|
pink = '13'
|
|
grey = '14'
|
|
|
|
# Globals
|
|
bots = list()
|
|
|
|
def botlist(nick):
|
|
global bots
|
|
if nick[:1] == '+':
|
|
bots.append(nick[1:])
|
|
elif nick[:1] == '-':
|
|
bots.remove(nick[1:])
|
|
|
|
def color(msg, foreground, background=None):
|
|
return f'\x03{foreground},{background}{msg}{reset}' if background else f'\x03{foreground}{msg}{reset}'
|
|
|
|
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 get_time():
|
|
return time.strftime('%I:%M:%S')
|
|
|
|
def is_admin(ident):
|
|
return re.compile(admin.replace('*','.*')).search(ident)
|
|
|
|
def random_nick():
|
|
prefix = random.choice(['st','sn','cr','pl','pr','qu','br','gr','sh','sk','kl','wr']+list('bcdfgklmnprstvwz'))
|
|
midfix = random.choice(('aeiou'))+random.choice(('aeiou'))+random.choice(('bcdfgklmnprstvwz'))
|
|
suffix = random.choice(['ed','est','er','le','ly','y','ies','iest','ian','ion','est','ing','led']+list('abcdfgklmnprstvwz'))
|
|
return prefix+midfix+suffix
|
|
|
|
class clone(threading.Thread):
|
|
def __init__(self, server, vhost):
|
|
self.monlist = list()
|
|
self.nickname = random_nick()
|
|
self.server = server
|
|
self.sock = None
|
|
self.vhost = vhost
|
|
threading.Thread.__init__(self)
|
|
|
|
def run(self):
|
|
time.sleep(random.randint(300,900))
|
|
self.connect()
|
|
|
|
def connect(self):
|
|
try:
|
|
self.create_socket()
|
|
self.sock.connect((server, 6667))
|
|
self.raw(f'USER {random_nick()} 0 * :{random_nick()}')
|
|
self.nick(self.nickname)
|
|
except socket.error as ex:
|
|
error('Failed to connect to IRC server.', ex)
|
|
self.event_disconnect()
|
|
else:
|
|
self.listen()
|
|
|
|
def create_socket(self):
|
|
ipv6_check = set([ip[4][0] for ip in socket.getaddrinfo(server,6667) if ':' in ip[4][0]])
|
|
self.sock = socket.socket(socket.AF_INET6) if ipv6 and ipv6_check else socket.socket()
|
|
if self.vhost:
|
|
self.sock.bind((self.vhost,0))
|
|
#self.sock = ssl.wrap_socket(self.sock)
|
|
|
|
def event_connect(self):
|
|
if self.nickname not in bots:
|
|
botlist('+'+self.nickname)
|
|
if self.monlist:
|
|
self.monitor('+', self.monlist)
|
|
self.join_channel(channel, key)
|
|
|
|
def event_ctcp(self, nick, target, msg):
|
|
if target == self.nickname:
|
|
self.sendmsg(channel, '[{0}] {1}{2}{3} {4}'.format(color('CTCP', green), color('<', grey), color(nick, yellow), color('>', grey), msg))
|
|
|
|
def event_disconnect(self):
|
|
if self.nickname in bots:
|
|
botlist('-'+self.nickname)
|
|
self.sock.close()
|
|
time.sleep(86400+random.randint(1800,3600))
|
|
self.connect()
|
|
|
|
def event_nick(self, nick, new_nick):
|
|
if nick == self.nickname:
|
|
self.nickname = new_nick
|
|
if new_nick in self.monlist:
|
|
self.monitor('C')
|
|
self.monlist = list()
|
|
elif nick in self.monlist:
|
|
self.nick(nick)
|
|
|
|
def event_nick_in_use(self, nick, target_nick):
|
|
if nick == '*':
|
|
self.nickname = random_nick()
|
|
self.nick(self.nickname)
|
|
|
|
def event_notice(self, nick, target, msg):
|
|
if target == self.nickname:
|
|
self.sendmsg(channel, '[{0}] {1}{2}{3} {4}'.format(color('NOTICE', purple), color('<', grey), color(nick, yellow), color('>', grey), msg))
|
|
|
|
def event_message(self, ident, nick, target, msg):
|
|
if is_admin(ident):
|
|
args = msg.split()
|
|
if args[0] in ('@all',self.nickname) and len(args) >= 2:
|
|
if len(args) == 2:
|
|
if args[1] == 'id':
|
|
self.sendmsg(target, id)
|
|
elif len(args) == 3 and args[1] == 'monitor':
|
|
if args[2] == 'list' and self.monlist:
|
|
self.sendmsg(target, '[{0}] {1}'.format(color('Monitor', light_blue), ', '.join(self.monlist)))
|
|
elif args[2] == 'reset' and self.monlist:
|
|
self.monitor('C')
|
|
self.monlist = list()
|
|
self.sendmsg(target, '{0} nick(s) have been {1} from the monitor list.'.format(color(str(len(self.monlist)), cyan), color('removed', red)))
|
|
elif args[2][:1] == '+':
|
|
nicks = [mon_nick for mon_nick in set(args[2][1:].split(',')) if mon_nick not in self.monlist]
|
|
if nicks:
|
|
self.monitor('+', nicks)
|
|
self.monlist += nicks
|
|
self.sendmsg(target, '{0} nick(s) have been {1} to the monitor list.'.format(color(str(len(nicks)), cyan), color('added', green)))
|
|
elif args[2][:1] == '-':
|
|
nicks = [mon_nick for mon_nick in set(args[2][1:].split(',')) if mon_nick in self.monlist]
|
|
if nicks:
|
|
self.monitor('-', nicks)
|
|
for mon_nick in nicks:
|
|
self.monlist.remove(mon_nick)
|
|
self.sendmsg(target, '{0} nick(s) have been {1} from the monitor list.'.format(color(str(len(nicks)), cyan), color('removed', red)))
|
|
elif len(args) >= 4 and args[1] == 'raw':
|
|
if args[2] == '-d':
|
|
data = ' '.join(args[3:])
|
|
threading.Thread(target=self.raw, args=(data,True)).start()
|
|
else:
|
|
data = ' '.join(args[2:])
|
|
self.raw(data)
|
|
elif target == self.nickname:
|
|
if msg.startswith('\x01ACTION'):
|
|
self.sendmsg(channel, '[{0}] {1}{2}{3} * {4}'.format(color('PM', red), color('<', grey), color(nick, yellow), color('>', grey), msg[8:][:-1]))
|
|
else:
|
|
self.sendmsg(channel, '[{0}] {1}{2}{3} {4}'.format(color('PM', red), color('<', grey), color(nick, yellow), color('>', grey), msg))
|
|
|
|
def event_mode(self, nick, chan, modes):
|
|
pass # Don't know what we are doing with this yet.
|
|
|
|
def event_quit(self, nick):
|
|
if nick in self.monlist:
|
|
self.nick(nick)
|
|
|
|
def handle_events(self, data):
|
|
args = data.split()
|
|
if data.startswith('ERROR :Closing Link:'):
|
|
raise Exception('Connection has closed.')
|
|
elif data.startswith('ERROR :Reconnecting too fast'):
|
|
raise Exception('Connection has closed. (throttled)')
|
|
elif args[0] == 'PING':
|
|
self.raw('PONG ' + args[1][1:])
|
|
elif args[1] == '001': # RPL_WELCOME
|
|
self.event_connect()
|
|
elif args[1] == '433' and len(args) >= 4: # ERR_NICKNAMEINUSE
|
|
nick = args[2]
|
|
target_nick = args[3]
|
|
self.event_nick_in_use(nick, target_nick)
|
|
elif args[1] == '731' and len(args) >= 4: # RPL_MONOFFLINE
|
|
nick = args[3][1:]
|
|
self.nick(nick)
|
|
elif args[1] == 'MODE' and len(args) >= 4:
|
|
nick = args[0].split('!')[0][1:]
|
|
chan = args[2]
|
|
modes = ' '.join(args[:3])
|
|
self.event_mode(nick, chan, modes)
|
|
elif args[1] == 'NICK' and len(args) == 3:
|
|
nick = args[0].split('!')[0][1:]
|
|
new_nick = args[2][1:]
|
|
self.event_nick(nick, new_nick)
|
|
elif args[1] == 'NOTICE':
|
|
nick = args[0].split('!')[0][1:]
|
|
target = args[2]
|
|
msg = ' '.join(args[3:])[1:]
|
|
self.event_notice(nick, target, msg)
|
|
elif args[1] == 'PRIVMSG' and len(args) >= 4:
|
|
ident = args[0][1:]
|
|
nick = args[0].split('!')[0][1:]
|
|
target = args[2]
|
|
msg = ' '.join(args[3:])[1:]
|
|
if msg[:1] == '\001':
|
|
msg = msg[1:]
|
|
self.event_ctcp(nick, target, msg)
|
|
else:
|
|
self.event_message(ident, nick, target, msg)
|
|
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):
|
|
debug(line)
|
|
self.handle_events(line)
|
|
except (UnicodeDecodeError,UnicodeEncodeError):
|
|
pass
|
|
except Exception as ex:
|
|
error('Unexpected error occured.', ex)
|
|
break
|
|
self.event_disconnect()
|
|
|
|
def mode(self, target, mode):
|
|
self.raw(f'MODE {target} {mode}')
|
|
|
|
def monitor(self, action, nicks=list()):
|
|
self.raw(f'MONITOR {action} ' + ','.join(nicks))
|
|
|
|
def nick(self, nick):
|
|
self.raw('NICK ' + nick)
|
|
|
|
def raw(self, data, delay=False):
|
|
if delay:
|
|
time.sleep(random.randint(300,900))
|
|
self.sock.send(bytes(data + '\r\n', 'utf-8'))
|
|
|
|
def sendmsg(self, target, msg):
|
|
self.raw(f'PRIVMSG {target} :{msg}')
|
|
|
|
# Main
|
|
if type(vhosts) == list:
|
|
for vhost in vhosts:
|
|
for i in range(concurrency):
|
|
for server in servers:
|
|
clone(server, vhost).start()
|
|
else:
|
|
for i in range(concurrency):
|
|
for server in servers:
|
|
clone(server, vhosts).start()
|
|
while True:input('')
|