From 7585db4f62ae32312805790454da27b85b8c0ac5 Mon Sep 17 00:00:00 2001 From: adator <85586985+adator85@users.noreply.github.com> Date: Sun, 15 Sep 2024 22:39:34 +0200 Subject: [PATCH 01/14] V5.2.2 --- core/Model.py | 4 + core/connection.py | 17 ++- core/irc.py | 18 ++- mods/mod_clone.py | 288 +++++++++++++++++++++++-------------------- mods/mod_defender.py | 30 +++-- version.json | 2 +- 6 files changed, 205 insertions(+), 154 deletions(-) diff --git a/core/Model.py b/core/Model.py index 946435c..f7b2dca 100644 --- a/core/Model.py +++ b/core/Model.py @@ -10,10 +10,12 @@ class User: uid: str nickname: str username: str + realname: str hostname: str umodes: str vhost: str isWebirc: bool + isWebsocket: bool remote_ip: str score_connexion: int connexion_datetime: datetime = field(default=datetime.now()) @@ -410,6 +412,8 @@ class Clones: alive: bool nickname: str username: str + realname: str + connected: bool = False UID_CLONE_DB: list[CloneModel] = [] diff --git a/core/connection.py b/core/connection.py index 0b403d4..23de602 100644 --- a/core/connection.py +++ b/core/connection.py @@ -7,13 +7,15 @@ from typing import Union class Connection: - def __init__(self, server_port: int, nickname: str, username: str, channels:list[str], CloneObject: Clones, ssl:bool = False) -> None: + def __init__(self, server_port: int, nickname: str, username: str, realname: str, channels:list[str], CloneObject: Clones, ssl:bool = False) -> None: self.Config = Config().ConfigObject self.Base = Base(self.Config) self.IrcSocket: Union[socket.socket, SSLSocket] = None self.nickname = nickname self.username = username + self.realname = realname + self.chanlog = '#clones' self.channels:list[str] = channels self.CHARSET = ['utf-8', 'iso-8859-1'] self.Clones = CloneObject @@ -97,10 +99,11 @@ class Connection: try: nickname = self.nickname username = self.username + realname = self.realname # Envoyer un message d'identification writer.send(f"USER {nickname} {username} {username} {nickname} {username} :{username}\r\n".encode('utf-8')) - writer.send(f"USER {username} {username} {username} :{username}\r\n".encode('utf-8')) + writer.send(f"USER {username} {username} {username} :{realname}\r\n".encode('utf-8')) writer.send(f"NICK {nickname}\r\n".encode('utf-8')) self.Base.logs.debug('Link information sent to the server') @@ -162,6 +165,7 @@ class Connection: for data in cmd: response = data.decode(self.CHARSET[0]).split() self.signal = self.currentCloneObject.alive + current_clone_nickname = self.currentCloneObject.nickname # print(response) match response[0]: @@ -176,6 +180,7 @@ class Connection: match response[1]: case '376': + self.currentCloneObject.connected = True for channel in self.channels: self.send2socket(f"JOIN {channel}") return None @@ -184,6 +189,14 @@ class Connection: self.Base.logs.debug(f'{self.currentCloneObject.nickname} - {self.currentCloneObject.alive}') fullname = str(response[0]).replace(':', '') nickname = fullname.split('!')[0].replace(':','') + + if response[2] == current_clone_nickname: + message = [] + for i in range(3, len(response)): + message.append(response[i]) + final_message = ' '.join(message) + self.send2socket(f"PRIVMSG {self.chanlog} :{fullname} => {final_message[1:]}") + if nickname == self.Config.SERVICE_NICKNAME: command = str(response[3]).replace(':','') diff --git a/core/irc.py b/core/irc.py index 2e7e80e..3703010 100644 --- a/core/irc.py +++ b/core/irc.py @@ -816,10 +816,9 @@ class Irc: # ['@s2s-md/geoip=cc=GB|cd=United\\sKingdom|asn=16276|asname=OVH\\sSAS;s2s-md/tls_cipher=TLSv1.3-TLS_CHACHA20_POLY1305_SHA256;s2s-md/creationtime=1721564601', # ':001', 'UID', 'albatros', '0', '1721564597', 'albatros', 'vps-91b2f28b.vps.ovh.net', # '001HB8G04', '0', '+iwxz', 'Clk-A62F1D18.vps.ovh.net', 'Clk-A62F1D18.vps.ovh.net', 'MyZBwg==', ':...'] - if 'webirc' in original_response[0]: - isWebirc = True - else: - isWebirc = False + + isWebirc = True if 'webirc' in original_response[0] else False + isWebsocket = True if 'websocket' in original_response[0] else False uid = str(original_response[8]) nickname = str(original_response[3]) @@ -832,6 +831,13 @@ class Irc: else: remote_ip = '127.0.0.1' + # extract realname + realname_list = [] + for i in range(14, len(original_response)): + realname_list.append(original_response[i]) + + realname = ' '.join(realname_list)[1:] + score_connexion = self.first_score self.User.insert( @@ -839,10 +845,12 @@ class Irc: uid=uid, nickname=nickname, username=username, + realname=realname, hostname=hostname, umodes=umodes, vhost=vhost, isWebirc=isWebirc, + isWebsocket=isWebsocket, remote_ip=remote_ip, score_connexion=score_connexion, connexion_datetime=datetime.now() @@ -1342,7 +1350,7 @@ class Irc: case 'show_users': for db_user in self.User.UID_DB: - self.send2socket(f":{dnickname} NOTICE {fromuser} :UID : {db_user.uid} - isWebirc: {db_user.isWebirc} - Nickname: {db_user.nickname} - Connection: {db_user.connexion_datetime}") + self.send2socket(f":{dnickname} NOTICE {fromuser} :UID : {db_user.uid} - isWebirc: {db_user.isWebirc} - isWebSocket: {db_user.isWebsocket} - Nickname: {db_user.nickname} - Connection: {db_user.connexion_datetime}") case 'show_admins': for db_admin in self.Admin.UID_ADMIN_DB: diff --git a/mods/mod_clone.py b/mods/mod_clone.py index 9da948f..8d265b8 100644 --- a/mods/mod_clone.py +++ b/mods/mod_clone.py @@ -122,9 +122,25 @@ class Clone(): return None - def thread_create_clones(self, nickname: str, username: str, channels: list, server_port: int, ssl: bool) -> None: + def thread_change_hostname(self): - Connection(server_port=server_port, nickname=nickname, username=username, channels=channels, CloneObject=self.Clone, ssl=ssl) + fake = faker.Faker('en_GB') + for clone in self.Clone.UID_CLONE_DB: + rand_1 = fake.random_elements(['A','B','C','D','E','F','0','1','2','3','4','5','6','7','8','9'], unique=True, length=8) + rand_2 = fake.random_elements(['A','B','C','D','E','F','0','1','2','3','4','5','6','7','8','9'], unique=True, length=8) + rand_3 = fake.random_elements(['A','B','C','D','E','F','0','1','2','3','4','5','6','7','8','9'], unique=True, length=8) + + rand_ip = ''.join(rand_1) + '.' + ''.join(rand_2) + '.' + ''.join(rand_3) + '.IP' + if clone.connected: + self.Irc.send2socket(f':{self.Config.SERVICE_NICKNAME} CHGHOST {clone.nickname} {rand_ip}') + + while not clone.connected: + if clone.connected: + self.Irc.send2socket(f':{self.Config.SERVICE_NICKNAME} CHGHOST {clone.nickname} {rand_ip}') + + def thread_create_clones(self, nickname: str, username: str, realname: str, channels: list, server_port: int, ssl: bool) -> None: + + Connection(server_port=server_port, nickname=nickname, username=username, realname=realname, channels=channels, CloneObject=self.Clone, ssl=ssl) return None @@ -144,30 +160,34 @@ class Clone(): try: fake = faker.Faker('en_GB') nickname = fake.first_name() - username = fake.last_name() - hostname = fake.domain_name(3) + # username = fake.last_name() + + # Generate Username + chaine = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789' + new_username = fake.random_sample(chaine, 9) + username = ''.join(new_username) + + # Create realname XX F|M Department + gender = fake.random_choices(['F','M'], 1) + gender = ''.join(gender) + age = random.randint(20, 60) + fake_fr = faker.Faker(['fr_FR', 'en_GB']) + department = fake_fr.department_name() + realname = f'{age} {gender} {department}' if self.Clone.exists(nickname=nickname): caracteres = '0123456789' randomize = ''.join(random.choice(caracteres) for _ in range(2)) nickname = nickname + str(randomize) self.Clone.insert( - self.Clone.CloneModel(alive=True, nickname=nickname, username=username) + self.Clone.CloneModel(alive=True, nickname=nickname, username=username, realname=realname) ) else: self.Clone.insert( - self.Clone.CloneModel(alive=True, nickname=nickname, username=username) + self.Clone.CloneModel(alive=True, nickname=nickname, username=username, realname=realname) ) - # if not nickname in self.ModConfig.clone_nicknames: - # self.ModConfig.clone_nicknames.append(nickname) - # else: - # caracteres = '0123456789' - # randomize = ''.join(random.choice(caracteres) for _ in range(2)) - # nickname = nickname + str(randomize) - # self.ModConfig.clone_nicknames.append(nickname) - - return (nickname, username, hostname) + return (nickname, username, realname) except AttributeError as ae: self.Logs.error(f'Attribute Error : {ae}') @@ -189,131 +209,129 @@ class Clone(): def _hcmds(self, user:str, channel: any, cmd: list, fullcmd: list = []) -> None: - command = str(cmd[0]).lower() - fromuser = user + try: + command = str(cmd[0]).lower() + fromuser = user - dnickname = self.Config.SERVICE_NICKNAME # Defender nickname + dnickname = self.Config.SERVICE_NICKNAME # Defender nickname - match command: + match command: - case 'clone': - option = str(cmd[1]).lower() + case 'clone': - if len(command) == 1: - self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} :/msg {dnickname} clone connect 6') - self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} :/msg {dnickname} clone kill [all | nickname]') - self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} :/msg {dnickname} clone join [all | nickname] #channel') - self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} :/msg {dnickname} clone list') - self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} :/msg {dnickname} clone shadow') - - match option: - - case 'shadow': - try: - fake = faker.Faker('en_GB') - for clone in self.Clone.UID_CLONE_DB: - hostname = fake.domain_name(3) - self.Irc.send2socket(f':{dnickname} CHGHOST {clone.nickname} {hostname}') - - except Exception as err: - self.Logs.error(f'{err}') - self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} :/msg {dnickname} clone shadow') - - case 'connect': - try: - number_of_clones = int(cmd[2]) - for i in range(number_of_clones): - nickname, username, hostname = self.generate_names() - self.Base.create_thread( - self.thread_create_clones, - (nickname, username, [], 6697, True) - ) - - self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} :{str(number_of_clones)} clones joined the network') - - except Exception as err: - self.Logs.error(f'{err}') - self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} :/msg {dnickname} clone connect [number of clone you want to connect]') - self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} :Exemple /msg {dnickname} clone connect 6') - - case 'kill': - try: - # clone kill [all | nickname] - clone_name = str(cmd[2]) - clone_to_kill: list[str] = [] - - if clone_name.lower() == 'all': - for clone in self.Clone.UID_CLONE_DB: - self.Irc.send2socket(f':{dnickname} PRIVMSG {clone.nickname} :KILL') - clone_to_kill.append(clone.nickname) - clone.alive = False - - for clone_nickname in clone_to_kill: - self.Clone.delete(clone_nickname) - - del clone_to_kill - - else: - if self.Clone.exists(clone_name): - self.Irc.send2socket(f':{dnickname} PRIVMSG {clone_name} :KILL') - self.Clone.kill(clone_name) - self.Clone.delete(clone_name) - - except Exception as err: - self.Logs.error(f'{err}') - self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} :/msg {dnickname} clone kill all') - self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} :/msg {dnickname} clone kill clone_nickname') - - case 'join': - try: - # clone join [all | nickname] #channel - clone_name = str(cmd[2]) - clone_channel_to_join = str(cmd[3]) - - if clone_name.lower() == 'all': - self.Base.create_thread(self.thread_join_channels, (clone_channel_to_join, 2)) - else: - self.Base.create_thread(self.thread_join_channels, (clone_channel_to_join, 2, clone_name)) - - except Exception as err: - self.Logs.error(f'{err}') - self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} :/msg {dnickname} clone join all #channel') - self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} :/msg {dnickname} clone join clone_nickname #channel') - - case 'list': - try: - for clone_name in self.Clone.UID_CLONE_DB: - self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} :>> {clone_name.nickname} | {clone_name.username}') - pass - except Exception as err: - self.Logs.error(f'{err}') - - case 'say': - try: - # clone say clone_nickname #channel message - clone_name = str(cmd[2]) - clone_channel = str(cmd[3]) if self.Base.Is_Channel(str(cmd[3])) else None - - message = [] - for i in range(4, len(cmd)): - message.append(cmd[i]) - final_message = ' '.join(message) - - if clone_channel is None or not self.Clone.exists(clone_name): - self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} :/msg {dnickname} clone say [clone_nickname] #channel message') - return None - - if self.Clone.exists(clone_name): - self.Irc.send2socket(f':{dnickname} PRIVMSG {clone_name} :SAY {clone_channel} {final_message}') - - except Exception as err: - self.Logs.error(f'{err}') - self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} :/msg {dnickname} clone say [clone_nickname] #channel message') - - case _: + if len(cmd) == 1: self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} :/msg {dnickname} clone connect 6') self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} :/msg {dnickname} clone kill [all | nickname]') self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} :/msg {dnickname} clone join [all | nickname] #channel') - self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} :/msg {dnickname} clone say [clone_nickname] #channel [message]') self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} :/msg {dnickname} clone list') - self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} :/msg {dnickname} clone shadow') + + option = str(cmd[1]).lower() + + match option: + + case 'connect': + try: + number_of_clones = int(cmd[2]) + for i in range(number_of_clones): + nickname, username, realname = self.generate_names() + self.Base.create_thread( + self.thread_create_clones, + (nickname, username, realname, ['#clones'], 6697, True) + ) + + self.Base.create_thread( + self.thread_change_hostname, + run_once=True + ) + + self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} :{str(number_of_clones)} clones joined the network') + + except Exception as err: + self.Logs.error(f'{err}') + self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} :/msg {dnickname} clone connect [number of clone you want to connect]') + self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} :Exemple /msg {dnickname} clone connect 6') + + case 'kill': + try: + # clone kill [all | nickname] + clone_name = str(cmd[2]) + clone_to_kill: list[str] = [] + + if clone_name.lower() == 'all': + for clone in self.Clone.UID_CLONE_DB: + self.Irc.send2socket(f':{dnickname} PRIVMSG {clone.nickname} :KILL') + clone_to_kill.append(clone.nickname) + clone.alive = False + + for clone_nickname in clone_to_kill: + self.Clone.delete(clone_nickname) + + del clone_to_kill + + else: + if self.Clone.exists(clone_name): + self.Irc.send2socket(f':{dnickname} PRIVMSG {clone_name} :KILL') + self.Clone.kill(clone_name) + self.Clone.delete(clone_name) + + except Exception as err: + self.Logs.error(f'{err}') + self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} :/msg {dnickname} clone kill all') + self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} :/msg {dnickname} clone kill clone_nickname') + + case 'join': + try: + # clone join [all | nickname] #channel + clone_name = str(cmd[2]) + clone_channel_to_join = str(cmd[3]) + + if clone_name.lower() == 'all': + self.Base.create_thread(self.thread_join_channels, (clone_channel_to_join, 2)) + else: + self.Base.create_thread(self.thread_join_channels, (clone_channel_to_join, 2, clone_name)) + + except Exception as err: + self.Logs.error(f'{err}') + self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} :/msg {dnickname} clone join all #channel') + self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} :/msg {dnickname} clone join clone_nickname #channel') + + case 'list': + try: + for clone_name in self.Clone.UID_CLONE_DB: + self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} :>> {clone_name.nickname} | {clone_name.username}') + pass + except Exception as err: + self.Logs.error(f'{err}') + + case 'say': + try: + # clone say clone_nickname #channel message + clone_name = str(cmd[2]) + clone_channel = str(cmd[3]) if self.Base.Is_Channel(str(cmd[3])) else None + + message = [] + for i in range(4, len(cmd)): + message.append(cmd[i]) + final_message = ' '.join(message) + + if clone_channel is None or not self.Clone.exists(clone_name): + self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} :/msg {dnickname} clone say [clone_nickname] #channel message') + return None + + if self.Clone.exists(clone_name): + self.Irc.send2socket(f':{dnickname} PRIVMSG {clone_name} :SAY {clone_channel} {final_message}') + + except Exception as err: + self.Logs.error(f'{err}') + self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} :/msg {dnickname} clone say [clone_nickname] #channel message') + + case _: + self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} :/msg {dnickname} clone connect 6') + self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} :/msg {dnickname} clone kill [all | nickname]') + self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} :/msg {dnickname} clone join [all | nickname] #channel') + self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} :/msg {dnickname} clone say [clone_nickname] #channel [message]') + self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} :/msg {dnickname} clone list') + except IndexError as ie: + self.Logs.error(f'Index Error: {ie}') + except Exception as err: + self.Logs.error(f'Index Error: {err}') diff --git a/mods/mod_defender.py b/mods/mod_defender.py index 06e5400..7abf92c 100644 --- a/mods/mod_defender.py +++ b/mods/mod_defender.py @@ -44,11 +44,13 @@ class Defender(): nickname: str username: str hostname: str + realname: str umodes: str vhost: str ip: str score: int isWebirc: bool + isWebsocket: bool secret_code: str connected_datetime: str updated_datetime: str @@ -611,13 +613,14 @@ class Defender(): """local_scan Args: - remote_ip (str): _description_ + userModel (UserModel): _description_ """ User = userModel remote_ip = User.remote_ip username = User.username hostname = User.hostname nickname = User.nickname + fullname = f'{nickname}!{username}@{hostname}' if remote_ip in self.Config.WHITELISTED_IP: return None @@ -631,14 +634,13 @@ class Defender(): connection = (remote_ip, self.Base.int_if_possible(port)) newSocket.connect(connection) - fullname = f'{nickname}!{username}@{hostname}' - self.Irc.send2socket(f":{self.Config.SERVICE_NICKNAME} PRIVMSG {self.Config.SERVICE_CHANLOG} :[ {self.Config.CONFIG_COLOR['rouge']}PROXY_SCAN{self.Config.CONFIG_COLOR['noire']} ] {fullname} ({remote_ip}) : Port [{str(port)}] ouvert sur l'adresse ip [{remote_ip}]") # print(f"=======> Le port {str(port)} est ouvert !!") self.Base.running_sockets.append(newSocket) # print(newSocket) newSocket.shutdown(socket.SHUT_RDWR) newSocket.close() + except (socket.timeout, ConnectionRefusedError): self.Logs.info(f"Le port {remote_ip}:{str(port)} est fermé") except AttributeError as ae: @@ -670,10 +672,10 @@ class Defender(): self.Logs.warning(f"thread_local_scan Error : {ve}") def get_ports_connexion(self, userModel: User.UserModel) -> list[int]: - """psutil_scan for Linux + """psutil_scan for Linux (should be run on the same location as the unrealircd server) Args: - remote_ip (str): The remote ip address + userModel (UserModel): The User Model Object Returns: list[int]: list of ports @@ -694,6 +696,9 @@ class Defender(): matching_ports = [conn.raddr.port for conn in connections if conn.raddr and conn.raddr.ip == remote_ip] self.Logs.info(f"Connexion of {fullname} ({remote_ip}) using ports : {str(matching_ports)}") + if matching_ports: + self.Irc.send2socket(f":{self.Config.SERVICE_NICKNAME} PRIVMSG {self.Config.SERVICE_CHANLOG} :[ {self.Config.CONFIG_COLOR['rouge']}PROXY_SCAN{self.Config.CONFIG_COLOR['noire']} ] {fullname} ({remote_ip}) : is using ports {matching_ports}") + return matching_ports except psutil.AccessDenied as ad: @@ -1061,9 +1066,9 @@ class Defender(): currentDateTime = self.Base.get_datetime() self.reputation_insert( self.ReputationModel( - uid=_User.uid, nickname=_User.nickname, username=_User.username, hostname=_User.hostname, - umodes=_User.umodes, vhost=_User.vhost, ip=_User.remote_ip, score=_User.score_connexion, - secret_code=self.Base.get_random(8), isWebirc=_User.isWebirc, connected_datetime=currentDateTime, + uid=_User.uid, nickname=_User.nickname, username=_User.username, realname=_User.realname, + hostname=_User.hostname, umodes=_User.umodes, vhost=_User.vhost, ip=_User.remote_ip, score=_User.score_connexion, + secret_code=self.Base.get_random(8), isWebirc=_User.isWebirc, isWebsocket=_User.isWebsocket, connected_datetime=currentDateTime, updated_datetime=currentDateTime ) ) @@ -1086,6 +1091,9 @@ class Defender(): get_reputation = self.reputation_get_Reputation(parsed_UID) + self.Irc.send2socket(f":{service_id} MODE {parsed_chan} +b ~security-group:unknown-users") + self.Irc.send2socket(f":{service_id} MODE {parsed_chan} +eee ~security-group:webirc-users ~security-group:known-users ~security-group:websocket-users") + if not get_reputation is None: isWebirc = get_reputation.isWebirc @@ -1285,9 +1293,7 @@ class Defender(): for chan in self.Channel.UID_CHANNEL_DB: if chan.name != jail_chan: self.Irc.send2socket(f":{service_id} MODE {chan.name} +b ~security-group:unknown-users") - self.Irc.send2socket(f":{service_id} MODE {chan.name} +e ~security-group:webirc-users") - self.Irc.send2socket(f":{service_id} MODE {chan.name} +e ~security-group:known-users") - self.Irc.send2socket(f":{service_id} MODE {chan.name} +e ~security-group:websocket-users") + self.Irc.send2socket(f":{service_id} MODE {chan.name} +eee ~security-group:webirc-users ~security-group:known-users ~security-group:websocket-users") self.Base.db_query_channel('add', self.module_name, jail_chan) @@ -1623,10 +1629,12 @@ class Defender(): self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} : UID : {UserObject.uid}') self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} : NICKNAME : {UserObject.nickname}') self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} : USERNAME : {UserObject.username}') + self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} : REALNAME : {UserObject.realname}') self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} : HOSTNAME : {UserObject.hostname}') self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} : VHOST : {UserObject.vhost}') self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} : IP : {UserObject.remote_ip}') self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} : WebIrc : {UserObject.isWebirc}') + self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} : WebWebsocket : {UserObject.isWebsocket}') self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} : REPUTATION : {UserObject.score_connexion}') self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} : MODES : {UserObject.umodes}') self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} : CONNECTION TIME : {UserObject.connexion_datetime}') diff --git a/version.json b/version.json index f4ad94a..dd07759 100644 --- a/version.json +++ b/version.json @@ -1,3 +1,3 @@ { - "version": "5.2.1" + "version": "5.2.2" } \ No newline at end of file From 2f681db2d78babf23409d2b36b96525b91729019 Mon Sep 17 00:00:00 2001 From: adator <85586985+adator85@users.noreply.github.com> Date: Sun, 15 Sep 2024 23:29:32 +0200 Subject: [PATCH 02/14] Adding Geoip to the UserModel --- core/Model.py | 1 + core/irc.py | 93 +++++++++++++++++++++++++------------------- mods/mod_clone.py | 3 +- mods/mod_defender.py | 1 + 4 files changed, 57 insertions(+), 41 deletions(-) diff --git a/core/Model.py b/core/Model.py index f7b2dca..e9b5671 100644 --- a/core/Model.py +++ b/core/Model.py @@ -18,6 +18,7 @@ class User: isWebsocket: bool remote_ip: str score_connexion: int + geoip: str = None connexion_datetime: datetime = field(default=datetime.now()) UID_DB: list[UserModel] = [] diff --git a/core/irc.py b/core/irc.py index 3703010..e118d02 100644 --- a/core/irc.py +++ b/core/irc.py @@ -591,7 +591,7 @@ class Irc: self.send2socket(f':{dnickname} NOTICE {fromuser} : Please run (git pull origin main) in the current folder') else: self.send2socket(f':{dnickname} NOTICE {fromuser} : You have the latest version of defender') - + return None def cmd(self, data: list[str]) -> None: @@ -813,52 +813,67 @@ class Irc: self.Base.logs.error(f'Index Error: {ie}') case 'UID': - # ['@s2s-md/geoip=cc=GB|cd=United\\sKingdom|asn=16276|asname=OVH\\sSAS;s2s-md/tls_cipher=TLSv1.3-TLS_CHACHA20_POLY1305_SHA256;s2s-md/creationtime=1721564601', - # ':001', 'UID', 'albatros', '0', '1721564597', 'albatros', 'vps-91b2f28b.vps.ovh.net', - # '001HB8G04', '0', '+iwxz', 'Clk-A62F1D18.vps.ovh.net', 'Clk-A62F1D18.vps.ovh.net', 'MyZBwg==', ':...'] + try: + # ['@s2s-md/geoip=cc=GB|cd=United\\sKingdom|asn=16276|asname=OVH\\sSAS;s2s-md/tls_cipher=TLSv1.3-TLS_CHACHA20_POLY1305_SHA256;s2s-md/creationtime=1721564601', + # ':001', 'UID', 'albatros', '0', '1721564597', 'albatros', 'vps-91b2f28b.vps.ovh.net', + # '001HB8G04', '0', '+iwxz', 'Clk-A62F1D18.vps.ovh.net', 'Clk-A62F1D18.vps.ovh.net', 'MyZBwg==', ':...'] - isWebirc = True if 'webirc' in original_response[0] else False - isWebsocket = True if 'websocket' in original_response[0] else False + isWebirc = True if 'webirc' in original_response[0] else False + isWebsocket = True if 'websocket' in original_response[0] else False - uid = str(original_response[8]) - nickname = str(original_response[3]) - username = str(original_response[6]) - hostname = str(original_response[7]) - umodes = str(original_response[10]) - vhost = str(original_response[11]) - if not 'S' in umodes: - remote_ip = self.Base.decode_ip(str(original_response[13])) - else: - remote_ip = '127.0.0.1' + uid = str(original_response[8]) + nickname = str(original_response[3]) + username = str(original_response[6]) + hostname = str(original_response[7]) + umodes = str(original_response[10]) + vhost = str(original_response[11]) - # extract realname - realname_list = [] - for i in range(14, len(original_response)): - realname_list.append(original_response[i]) + if not 'S' in umodes: + remote_ip = self.Base.decode_ip(str(original_response[13])) + else: + remote_ip = '127.0.0.1' - realname = ' '.join(realname_list)[1:] + # extract realname + realname_list = [] + for i in range(14, len(original_response)): + realname_list.append(original_response[i]) - score_connexion = self.first_score + realname = ' '.join(realname_list)[1:] - self.User.insert( - self.User.UserModel( - uid=uid, - nickname=nickname, - username=username, - realname=realname, - hostname=hostname, - umodes=umodes, - vhost=vhost, - isWebirc=isWebirc, - isWebsocket=isWebsocket, - remote_ip=remote_ip, - score_connexion=score_connexion, - connexion_datetime=datetime.now() + # Extract Geoip information + pattern = r'^.*geoip=cc=(\S{2}).*$' + geoip_match = re.match(pattern, original_response[0]) + + if geoip_match: + geoip = geoip_match.group(1) + else: + geoip = None + + score_connexion = self.first_score + + self.User.insert( + self.User.UserModel( + uid=uid, + nickname=nickname, + username=username, + realname=realname, + hostname=hostname, + umodes=umodes, + vhost=vhost, + isWebirc=isWebirc, + isWebsocket=isWebsocket, + remote_ip=remote_ip, + geoip=geoip, + score_connexion=score_connexion, + connexion_datetime=datetime.now() + ) ) - ) - for classe_name, classe_object in self.loaded_classes.items(): - classe_object.cmd(original_response) + for classe_name, classe_object in self.loaded_classes.items(): + classe_object.cmd(original_response) + + except Exception as err: + self.Base.logs.error(f'General Error: {err}') case 'PRIVMSG': try: diff --git a/mods/mod_clone.py b/mods/mod_clone.py index 8d265b8..9050394 100644 --- a/mods/mod_clone.py +++ b/mods/mod_clone.py @@ -298,8 +298,7 @@ class Clone(): case 'list': try: for clone_name in self.Clone.UID_CLONE_DB: - self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} :>> {clone_name.nickname} | {clone_name.username}') - pass + self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} :>> Nickname: {clone_name.nickname} | Username: {clone_name.username}') except Exception as err: self.Logs.error(f'{err}') diff --git a/mods/mod_defender.py b/mods/mod_defender.py index 7abf92c..b103d2e 100644 --- a/mods/mod_defender.py +++ b/mods/mod_defender.py @@ -1633,6 +1633,7 @@ class Defender(): self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} : HOSTNAME : {UserObject.hostname}') self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} : VHOST : {UserObject.vhost}') self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} : IP : {UserObject.remote_ip}') + self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} : Country : {UserObject.geoip}') self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} : WebIrc : {UserObject.isWebirc}') self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} : WebWebsocket : {UserObject.isWebsocket}') self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} : REPUTATION : {UserObject.score_connexion}') From 5d3a2b0e6471395d6e87c0001e9f6b66ea294678 Mon Sep 17 00:00:00 2001 From: adator <85586985+adator85@users.noreply.github.com> Date: Sun, 15 Sep 2024 23:32:44 +0200 Subject: [PATCH 03/14] Changing Proxy_scan to PSUTIL_scan --- mods/mod_defender.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mods/mod_defender.py b/mods/mod_defender.py index b103d2e..ecfab82 100644 --- a/mods/mod_defender.py +++ b/mods/mod_defender.py @@ -697,7 +697,7 @@ class Defender(): self.Logs.info(f"Connexion of {fullname} ({remote_ip}) using ports : {str(matching_ports)}") if matching_ports: - self.Irc.send2socket(f":{self.Config.SERVICE_NICKNAME} PRIVMSG {self.Config.SERVICE_CHANLOG} :[ {self.Config.CONFIG_COLOR['rouge']}PROXY_SCAN{self.Config.CONFIG_COLOR['noire']} ] {fullname} ({remote_ip}) : is using ports {matching_ports}") + self.Irc.send2socket(f":{self.Config.SERVICE_NICKNAME} PRIVMSG {self.Config.SERVICE_CHANLOG} :[ {self.Config.CONFIG_COLOR['rouge']}PSUTIL_SCAN{self.Config.CONFIG_COLOR['noire']} ] {fullname} ({remote_ip}) : is using ports {matching_ports}") return matching_ports From d459fd662fa43973e5515cd1d023374cf5db746e Mon Sep 17 00:00:00 2001 From: adator <85586985+adator85@users.noreply.github.com> Date: Mon, 16 Sep 2024 00:41:43 +0200 Subject: [PATCH 04/14] Changing hostname modification --- mods/mod_clone.py | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/mods/mod_clone.py b/mods/mod_clone.py index 9050394..e65729a 100644 --- a/mods/mod_clone.py +++ b/mods/mod_clone.py @@ -131,12 +131,17 @@ class Clone(): rand_3 = fake.random_elements(['A','B','C','D','E','F','0','1','2','3','4','5','6','7','8','9'], unique=True, length=8) rand_ip = ''.join(rand_1) + '.' + ''.join(rand_2) + '.' + ''.join(rand_3) + '.IP' - if clone.connected: - self.Irc.send2socket(f':{self.Config.SERVICE_NICKNAME} CHGHOST {clone.nickname} {rand_ip}') + found = False - while not clone.connected: + while not found: if clone.connected: self.Irc.send2socket(f':{self.Config.SERVICE_NICKNAME} CHGHOST {clone.nickname} {rand_ip}') + found = True + break + + # while not clone.connected: + # if clone.connected: + # self.Irc.send2socket(f':{self.Config.SERVICE_NICKNAME} CHGHOST {clone.nickname} {rand_ip}') def thread_create_clones(self, nickname: str, username: str, realname: str, channels: list, server_port: int, ssl: bool) -> None: From 66ea4925934050a5fe783da15d1105567806f954 Mon Sep 17 00:00:00 2001 From: adator <85586985+adator85@users.noreply.github.com> Date: Mon, 16 Sep 2024 00:57:27 +0200 Subject: [PATCH 05/14] removing old code --- mods/mod_clone.py | 4 ---- 1 file changed, 4 deletions(-) diff --git a/mods/mod_clone.py b/mods/mod_clone.py index e65729a..2cd75ad 100644 --- a/mods/mod_clone.py +++ b/mods/mod_clone.py @@ -139,10 +139,6 @@ class Clone(): found = True break - # while not clone.connected: - # if clone.connected: - # self.Irc.send2socket(f':{self.Config.SERVICE_NICKNAME} CHGHOST {clone.nickname} {rand_ip}') - def thread_create_clones(self, nickname: str, username: str, realname: str, channels: list, server_port: int, ssl: bool) -> None: Connection(server_port=server_port, nickname=nickname, username=username, realname=realname, channels=channels, CloneObject=self.Clone, ssl=ssl) From c1c0b480ce55266ffd6c0eb8855987f43c0acc84 Mon Sep 17 00:00:00 2001 From: adator <85586985+adator85@users.noreply.github.com> Date: Mon, 16 Sep 2024 21:14:04 +0200 Subject: [PATCH 06/14] V5.2.3 If missing MOTD --- core/connection.py | 8 ++++++++ version.json | 2 +- 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/core/connection.py b/core/connection.py index 23de602..94a71d9 100644 --- a/core/connection.py +++ b/core/connection.py @@ -180,10 +180,18 @@ class Connection: match response[1]: case '376': + # End of MOTD self.currentCloneObject.connected = True for channel in self.channels: self.send2socket(f"JOIN {channel}") return None + case '422': + # Missing MOTD + self.currentCloneObject.connected = True + for channel in self.channels: + self.send2socket(f"JOIN {channel}") + return None + case 'PRIVMSG': self.Base.logs.debug(response) self.Base.logs.debug(f'{self.currentCloneObject.nickname} - {self.currentCloneObject.alive}') diff --git a/version.json b/version.json index dd07759..1137156 100644 --- a/version.json +++ b/version.json @@ -1,3 +1,3 @@ { - "version": "5.2.2" + "version": "5.2.3" } \ No newline at end of file From a7de16f7ad765f6e008d535797bf8922df085dbc Mon Sep 17 00:00:00 2001 From: adator <85586985+adator85@users.noreply.github.com> Date: Tue, 17 Sep 2024 00:14:53 +0200 Subject: [PATCH 07/14] V5.2.4 multiple changes related to clones --- core/Model.py | 1 + core/base.py | 4 ++-- core/connection.py | 37 +++++++++++++++++++++---------------- core/loadConf.py | 4 ++++ mods/mod_clone.py | 17 ++++++++++------- version.json | 2 +- 6 files changed, 39 insertions(+), 26 deletions(-) diff --git a/core/Model.py b/core/Model.py index e9b5671..18016fc 100644 --- a/core/Model.py +++ b/core/Model.py @@ -414,6 +414,7 @@ class Clones: nickname: str username: str realname: str + vhost: str = None connected: bool = False UID_CLONE_DB: list[CloneModel] = [] diff --git a/core/base.py b/core/base.py index 808e9b6..922ab74 100644 --- a/core/base.py +++ b/core/base.py @@ -429,7 +429,7 @@ class Base: except AssertionError as ae: self.logs.error(f'Assertion Error -> {ae}') - def create_thread(self, func:object, func_args: tuple = (), run_once:bool = False) -> None: + def create_thread(self, func:object, func_args: tuple = (), run_once:bool = False, daemon: bool = True) -> None: """Create a new thread and store it into running_threads variable Args: @@ -445,7 +445,7 @@ class Base: if thread.getName() == func_name: return None - th = threading.Thread(target=func, args=func_args, name=str(func_name), daemon=True) + th = threading.Thread(target=func, args=func_args, name=str(func_name), daemon=daemon) th.start() self.running_threads.append(th) diff --git a/core/connection.py b/core/connection.py index 94a71d9..6fbd559 100644 --- a/core/connection.py +++ b/core/connection.py @@ -15,7 +15,7 @@ class Connection: self.nickname = nickname self.username = username self.realname = realname - self.chanlog = '#clones' + self.clone_chanlog = self.Config.SALON_CLONES self.channels:list[str] = channels self.CHARSET = ['utf-8', 'iso-8859-1'] self.Clones = CloneObject @@ -62,7 +62,7 @@ class Connection: self.Base.logs.critical(f"AttributeError __create_socket: {ae} - {soc.fileno()}") return False - def send2socket(self, send_message:str) -> None: + def send2socket(self, send_message:str, disconnect: bool = False) -> None: """Envoit les commandes à envoyer au serveur. Args: @@ -70,9 +70,8 @@ class Connection: """ try: with self.Base.lock: - # print(f">{str(send_message)}") self.IrcSocket.send(f"{send_message}\r\n".encode(self.CHARSET[0])) - self.Base.logs.debug(f'{send_message}') + self.Base.logs.debug(f'<<{self.currentCloneObject.nickname}>>: {send_message}') except UnicodeDecodeError: self.Base.logs.error(f'Decode Error try iso-8859-1 - message: {send_message}') @@ -114,7 +113,6 @@ class Connection: def connect(self): try: - while self.signal: try: # 4072 max what the socket can grab @@ -132,6 +130,7 @@ class Connection: data = data_in_bytes.splitlines(True) if not data: + # If no data then quit the loop break self.parser(data) @@ -145,10 +144,6 @@ class Connection: self.Base.logs.error(f"OSError __connect_to_irc: {oe} - {data}") self.signal = False - self.IrcSocket.shutdown(socket.SHUT_RDWR) - self.IrcSocket.close() - self.Base.logs.info("--> Clone Disconnected ...") - except AssertionError as ae: self.Base.logs.error(f'Assertion error : {ae}') except ValueError as ve: @@ -159,12 +154,17 @@ class Connection: self.Base.logs.critical(f"{atte}") except Exception as e: self.Base.logs.error(f"Exception: {e}") + finally: + self.IrcSocket.shutdown(socket.SHUT_WR) + self.IrcSocket.shutdown(socket.SHUT_RD) + self.Base.logs.info(f"<<{self.currentCloneObject.nickname}>> Clone Disconnected ...") + # self.IrcSocket.close() def parser(self, cmd:list[bytes]): try: + for data in cmd: response = data.decode(self.CHARSET[0]).split() - self.signal = self.currentCloneObject.alive current_clone_nickname = self.currentCloneObject.nickname # print(response) @@ -176,7 +176,8 @@ class Connection: case 'ERROR': error_value = str(response[1]).replace(':','') if error_value == 'Closing': - self.signal = False + self.Base.logs.info(f"<<{self.currentCloneObject.nickname}>> {response} ...") + # self.signal = False match response[1]: case '376': @@ -184,17 +185,22 @@ class Connection: self.currentCloneObject.connected = True for channel in self.channels: self.send2socket(f"JOIN {channel}") + + self.send2socket(f"JOIN {self.clone_chanlog}") + return None case '422': # Missing MOTD self.currentCloneObject.connected = True for channel in self.channels: self.send2socket(f"JOIN {channel}") + + self.send2socket(f"JOIN {self.clone_chanlog}") return None case 'PRIVMSG': - self.Base.logs.debug(response) - self.Base.logs.debug(f'{self.currentCloneObject.nickname} - {self.currentCloneObject.alive}') + self.Base.logs.debug(f'<<{self.currentCloneObject.nickname}>> Response: {response}') + self.Base.logs.debug(f'<<{self.currentCloneObject.nickname}>> Alive: {self.currentCloneObject.alive}') fullname = str(response[0]).replace(':', '') nickname = fullname.split('!')[0].replace(':','') @@ -203,14 +209,13 @@ class Connection: for i in range(3, len(response)): message.append(response[i]) final_message = ' '.join(message) - self.send2socket(f"PRIVMSG {self.chanlog} :{fullname} => {final_message[1:]}") + self.send2socket(f"PRIVMSG {self.clone_chanlog} :{fullname} => {final_message[1:]}") if nickname == self.Config.SERVICE_NICKNAME: command = str(response[3]).replace(':','') if command == 'KILL': - self.send2socket(f'QUIT :Thanks and goodbye') - self.signal = self.currentCloneObject.alive + self.send2socket(f'QUIT :Thanks and goodbye', disconnect=True) if command == 'JOIN': channel_to_join = str(response[4]) diff --git a/core/loadConf.py b/core/loadConf.py index e6b676d..d43d1b2 100644 --- a/core/loadConf.py +++ b/core/loadConf.py @@ -82,6 +82,9 @@ class ConfigDataModel: SALON_LIBERER: str """Channel where the nickname will be released""" + SALON_CLONES: str + """Channel to host clones""" + API_TIMEOUT: int """Default api timeout in second""" @@ -184,6 +187,7 @@ class Config: SALON_JAIL=import_config["SALON_JAIL"], SALON_JAIL_MODES=import_config["SALON_JAIL_MODES"], SALON_LIBERER=import_config["SALON_LIBERER"], + SALON_CLONES=import_config["SALON_CLONES"], API_TIMEOUT=import_config["API_TIMEOUT"], PORTS_TO_SCAN=import_config["PORTS_TO_SCAN"], WHITELISTED_IP=import_config["WHITELISTED_IP"], diff --git a/mods/mod_clone.py b/mods/mod_clone.py index 2cd75ad..410eb11 100644 --- a/mods/mod_clone.py +++ b/mods/mod_clone.py @@ -126,6 +126,9 @@ class Clone(): fake = faker.Faker('en_GB') for clone in self.Clone.UID_CLONE_DB: + if not clone.vhost is None: + continue + rand_1 = fake.random_elements(['A','B','C','D','E','F','0','1','2','3','4','5','6','7','8','9'], unique=True, length=8) rand_2 = fake.random_elements(['A','B','C','D','E','F','0','1','2','3','4','5','6','7','8','9'], unique=True, length=8) rand_3 = fake.random_elements(['A','B','C','D','E','F','0','1','2','3','4','5','6','7','8','9'], unique=True, length=8) @@ -137,6 +140,7 @@ class Clone(): if clone.connected: self.Irc.send2socket(f':{self.Config.SERVICE_NICKNAME} CHGHOST {clone.nickname} {rand_ip}') found = True + clone.vhost = rand_ip break def thread_create_clones(self, nickname: str, username: str, realname: str, channels: list, server_port: int, ssl: bool) -> None: @@ -146,16 +150,16 @@ class Clone(): return None def thread_join_channels(self, channel_name: str, wait: float, clone_name:str = None): - + self.Irc.send2socket(f':{self.Config.SERVICE_NICKNAME} PRIVMSG {self.Config.SERVICE_CHANLOG} :Clones start to join {channel_name} with {wait} secondes frequency') if clone_name is None: for clone in self.Clone.UID_CLONE_DB: - self.Irc.send2socket(f':{self.Config.SERVICE_NICKNAME} PRIVMSG {clone.nickname} :JOIN {channel_name}') time.sleep(wait) + self.Irc.send2socket(f':{self.Config.SERVICE_NICKNAME} PRIVMSG {clone.nickname} :JOIN {channel_name}') else: for clone in self.Clone.UID_CLONE_DB: if clone_name == clone.nickname: - self.Irc.send2socket(f':{self.Config.SERVICE_NICKNAME} PRIVMSG {clone.nickname} :JOIN {channel_name}') time.sleep(wait) + self.Irc.send2socket(f':{self.Config.SERVICE_NICKNAME} PRIVMSG {clone.nickname} :JOIN {channel_name}') def generate_names(self) -> tuple[str, str, str]: try: @@ -237,14 +241,13 @@ class Clone(): nickname, username, realname = self.generate_names() self.Base.create_thread( self.thread_create_clones, - (nickname, username, realname, ['#clones'], 6697, True) + (nickname, username, realname, [], 6697, True) ) self.Base.create_thread( - self.thread_change_hostname, - run_once=True + self.thread_change_hostname ) - + self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} :{str(number_of_clones)} clones joined the network') except Exception as err: diff --git a/version.json b/version.json index 1137156..cc238b3 100644 --- a/version.json +++ b/version.json @@ -1,3 +1,3 @@ { - "version": "5.2.3" + "version": "5.2.4" } \ No newline at end of file From 487f9a276299cdda24b5b07bd0188d565c7bce68 Mon Sep 17 00:00:00 2001 From: adator <85586985+adator85@users.noreply.github.com> Date: Tue, 17 Sep 2024 01:43:24 +0200 Subject: [PATCH 08/14] V5.2.5 --- README.md | 148 ++++++++++++++++++++++++++++++++++--------- core/Model.py | 1 + core/connection.py | 2 +- core/installation.py | 5 ++ core/loadConf.py | 58 ++++++++--------- mods/mod_clone.py | 26 ++++++-- version.json | 2 +- 7 files changed, 174 insertions(+), 68 deletions(-) diff --git a/README.md b/README.md index 6c43f88..04b467a 100644 --- a/README.md +++ b/README.md @@ -57,58 +57,146 @@ Pour Les prochains lancement de defender vous devez utiliser la commande suivant $ sudo systemctl --user start defender # Configuration - +``` SERVEUR (Serveur) - SERVEUR_IP: Adresse IP du serveur IRC à rejoindre. - SERVEUR_HOSTNAME: Nom d'hôte du serveur IRC à rejoindre (optionnel). - SERVEUR_LINK: Lien vers le serveur IRC (optionnel). - SERVEUR_PORT: Port de connexion au serveur IRC. - SERVEUR_PASSWORD: Mot de passe d'enregistrement du service sur le serveur IRC. - SERVEUR_ID: Identifiant unique du service. - SERVEUR_SSL: Active la connexion SSL sécurisée au serveur IRC (true/false). + * SERVEUR_IP: Adresse IP du serveur IRC à rejoindre. (default : 127.0.0.1) + * SERVEUR_HOSTNAME: Nom d'hôte du serveur IRC à rejoindre (optionnel). + * SERVEUR_LINK: Lien vers le serveur IRC (optionnel). + * SERVEUR_PORT: Port de connexion au serveur IRC. + * SERVEUR_PASSWORD: Mot de passe d'enregistrement du service sur le serveur IRC. + SERVEUR_ID: Identifiant unique du service. (default : 19Z) + SERVEUR_SSL: Active la connexion SSL sécurisée au serveur IRC (true/false) (default : false). SERVICE (Service) - SERVICE_NAME: Nom du service IRC. - SERVICE_NICKNAME: Surnom utilisé par le service sur le serveur IRC. - SERVICE_REALNAME: Nom réel du service affiché sur le serveur IRC. - SERVICE_USERNAME: Nom d'utilisateur utilisé par le service pour se connecter au serveur IRC. - SERVICE_HOST: Nom d'hôte du service affiché sur le serveur IRC (optionnel). - SERVICE_INFO: Description du service. - SERVICE_CHANLOG: Canal utilisé pour la journalisation des actions du service. - SERVICE_SMODES: Modes serveur appliqués aux canaux rejoints par le service. - SERVICE_CMODES: Modes de canal appliqués aux canaux rejoints par le service. - SERVICE_UMODES: Modes utilisateur appliqués au service. - SERVICE_PREFIX: Caractère utilisé comme préfixe des commandes du service. + SERVICE_NAME: Nom du service IRC. (default : Defender) + SERVICE_NICKNAME: Surnom utilisé par le service sur le serveur IRC. (default : Defender) + SERVICE_REALNAME: Nom réel du service affiché sur le serveur IRC. (default : Defender Security) + SERVICE_USERNAME: Nom d'utilisateur utilisé par le service pour se connecter au serveur IRC. (default : IRCSecurity) + SERVICE_HOST: Nom d'hôte du service affiché sur le serveur IRC (optionnel). (default : defender.local.network) + SERVICE_INFO: Description du service. (default : Defender Network IRC Service) + SERVICE_CHANLOG: Canal utilisé pour la journalisation des actions du service. (default : #services) + SERVICE_SMODES: Modes serveur appliqués aux canaux rejoints par le service. (default : +ioqBS) + SERVICE_CMODES: Modes de canal appliqués aux canaux rejoints par le service. (default : ntsOP) + SERVICE_UMODES: Modes utilisateur appliqués au service. (default : o) + SERVICE_PREFIX: Caractère utilisé comme préfixe des commandes du service. (default : !) COMPTE (Compte) - OWNER: Nom d'utilisateur possédant les droits d'administration du service. - PASSWORD: Mot de passe de l'administrateur du service. + OWNER: Nom d'utilisateur possédant les droits d'administration du service. (default : admin) + PASSWORD: Mot de passe de l'administrateur du service. (default : admin) CANAUX (Canaux) - SALON_JAIL: Canal utilisé comme prison pour les utilisateurs sanctionnés. - SALON_JAIL_MODES: Modes appliqués au canal de prison. - SALON_LIBERER: Canal utilisé pour la libération des utilisateurs sanctionnés. + SALON_JAIL: Canal utilisé comme prison pour les utilisateurs sanctionnés. (default : #jail) + SALON_JAIL_MODES: Modes appliqués au canal de prison. (default : sS) + SALON_LIBERER: Canal utilisé pour la libération des utilisateurs sanctionnés. (default : #welcome) API (API) - API_TIMEOUT: Durée maximale d'attente d'une réponse de l'API en secondes. + API_TIMEOUT: Durée maximale d'attente d'une réponse de l'API en secondes. (default : 2) SCANNER (Scanner) - PORTS_TO_SCAN: Liste des ports à scanner pour détecter des serveurs potentiellement malveillants. + PORTS_TO_SCAN: Liste des ports à scanner pour détecter des serveurs potentiellement malveillants. (default : []) SÉCURITÉ (Sécurité) - WHITELISTED_IP: Liste d'adresses IP autorisées à contourner certaines restrictions. - GLINE_DURATION: Durée de bannissement temporaire d'un utilisateur en minutes. + WHITELISTED_IP: Liste d'adresses IP autorisées à contourner certaines restrictions. (default : ['127.0.0.1']) + GLINE_DURATION: Durée de bannissement temporaire d'un utilisateur en minutes. (default : "30") DEBUG (Debug) - DEBUG_LEVEL: Niveau de verbosité des messages de debug (plus grand est le nombre, plus il y a d'informations). + DEBUG_LEVEL: Niveau de verbosité des messages de debug (plus grand est le nombre, plus il y a d'informations). (default : 20) Pour une production COULEURS (Couleurs) CONFIG_COLOR: Dictionnaire contenant des codes de couleurs IRC pour un meilleur affichage des messages. - +``` Modification de la configuration Vous devez modifier le fichier configuration.json en remplaçant les valeurs par défaut avec vos propres informations. Assurez-vous de bien lire la description de chaque paramètre pour une configuration optimale du service. +## Exemple de configuration de base +```json +{ + "SERVEUR_IP": "IP.DE.TON.SERVER", + "SERVEUR_HOSTNAME": "HOST.DE.TON.SERVER", + "SERVEUR_LINK": "LINK.DE.TON.SERVER", + "SERVEUR_PORT": 6901, + "SERVEUR_PASSWORD": "MOT_DE_PASS_DE_TON_LINK", + "SERVEUR_ID": "10Z", + "SERVEUR_SSL": true, + + "SERVICE_NAME": "defender", + "SERVICE_NICKNAME": "Dev-PyDefender", + "SERVICE_REALNAME": "Python Defender Security", + "SERVICE_USERNAME": "Dev-PyDefender", + "SERVICE_HOST": "HOST.DE.TON.DEFENDER", + + "OWNER": "TON_NICK_NAME", + "PASSWORD": "admin", + + "CONFIG_COLOR": { + "blanche": "\\u0003\\u0030", + "noire": "\\u0003\\u0031", + "bleue": "\\u0003\\u0020", + "verte": "\\u0003\\u0033", + "rouge": "\\u0003\\u0034", + "jaune": "\\u0003\\u0036", + "gras": "\\u0002", + "nogc": "\\u0002\\u0003" + } +} + +``` + +## Exemple complet de configuration +```json +{ + "SERVEUR_IP": "YOUR.SERVER.IP", + "SERVEUR_HOSTNAME": "irc.deb.biz.st", + "SERVEUR_LINK": "defenderdev.deb.biz.st", + "SERVEUR_PORT": 6901, + "SERVEUR_PASSWORD": "yourpassword", + "SERVEUR_ID": "10Z", + "SERVEUR_SSL": true, + + "SERVICE_NAME": "defender", + "SERVICE_NICKNAME": "Dev-PyDefender", + "SERVICE_REALNAME": "Python Defender Security", + "SERVICE_USERNAME": "Dev-PyDefender", + "SERVICE_HOST": "HOST.DE.TON.DEFENDER", + "SERVICE_INFO": "Network IRC Service", + "SERVICE_CHANLOG": "#services", + "SERVICE_SMODES": "+ioqBS", + "SERVICE_CMODES": "ntsOP", + "SERVICE_UMODES": "o", + "SERVICE_PREFIX": "!", + + "OWNER": "TON_NICK_NAME", + "PASSWORD": "admin", + + "SALON_JAIL": "#jail", + "SALON_JAIL_MODES": "sS", + "SALON_LIBERER": "#welcome", + + "SALON_CLONES": "#clones", + + "API_TIMEOUT": 2, + + "PORTS_TO_SCAN": [3028, 8080, 1080, 1085, 4145, 9050], + "WHITELISTED_IP": ["127.0.0.1"], + "GLINE_DURATION": "30", + + "DEBUG_LEVEL": 10, + + "CONFIG_COLOR": { + "blanche": "\\u0003\\u0030", + "noire": "\\u0003\\u0031", + "bleue": "\\u0003\\u0020", + "verte": "\\u0003\\u0033", + "rouge": "\\u0003\\u0034", + "jaune": "\\u0003\\u0036", + "gras": "\\u0002", + "nogc": "\\u0002\\u0003" + } + +} +``` + # \\!/ Attention \\!/ Le mot de passe de l'administrateur et le mot de passe du service doivent être modifiés pour des raisons de sécurité. Ne partagez pas vos informations de connexion au serveur IRC avec des tiers. diff --git a/core/Model.py b/core/Model.py index 18016fc..7664de3 100644 --- a/core/Model.py +++ b/core/Model.py @@ -414,6 +414,7 @@ class Clones: nickname: str username: str realname: str + channels: list vhost: str = None connected: bool = False diff --git a/core/connection.py b/core/connection.py index 6fbd559..b931388 100644 --- a/core/connection.py +++ b/core/connection.py @@ -204,7 +204,7 @@ class Connection: fullname = str(response[0]).replace(':', '') nickname = fullname.split('!')[0].replace(':','') - if response[2] == current_clone_nickname: + if response[2] == current_clone_nickname and nickname != self.Config.SERVICE_NICKNAME: message = [] for i in range(3, len(response)): message.append(response[i]) diff --git a/core/installation.py b/core/installation.py index a29ffd7..9245968 100644 --- a/core/installation.py +++ b/core/installation.py @@ -80,6 +80,11 @@ class Install: self.skip_install = True else: if self.is_root(): + print(f'/!\\ I fully not recommend running Defender as root /!\\') + self.skip_install = True + # Check if configuration.json exist + if not os.path.exists({os.path.join(self.config.defender_install_folder, 'core', 'configuration.json')}): + print(f'/!\\ configuration.json is not available, please create it first /!\\') self.skip_install = True def is_root(self) -> bool: diff --git a/core/loadConf.py b/core/loadConf.py index d43d1b2..80dc404 100644 --- a/core/loadConf.py +++ b/core/loadConf.py @@ -164,35 +164,35 @@ class Config: import_config = self.__load_json_service_configuration() ConfigObject: ConfigDataModel = ConfigDataModel( - SERVEUR_IP=import_config["SERVEUR_IP"], - SERVEUR_HOSTNAME=import_config["SERVEUR_HOSTNAME"], - SERVEUR_LINK=import_config["SERVEUR_LINK"], - SERVEUR_PORT=import_config["SERVEUR_PORT"], - SERVEUR_PASSWORD=import_config["SERVEUR_PASSWORD"], - SERVEUR_ID=import_config["SERVEUR_ID"], - SERVEUR_SSL=import_config["SERVEUR_SSL"], - SERVICE_NAME=import_config["SERVICE_NAME"], - SERVICE_NICKNAME=import_config["SERVICE_NICKNAME"], - SERVICE_REALNAME=import_config["SERVICE_REALNAME"], - SERVICE_USERNAME=import_config["SERVICE_USERNAME"], - SERVICE_HOST=import_config["SERVICE_HOST"], - SERVICE_INFO=import_config["SERVICE_INFO"], - SERVICE_CHANLOG=import_config["SERVICE_CHANLOG"], - SERVICE_SMODES=import_config["SERVICE_SMODES"], - SERVICE_CMODES=import_config["SERVICE_CMODES"], - SERVICE_UMODES=import_config["SERVICE_UMODES"], - SERVICE_PREFIX=import_config["SERVICE_PREFIX"], - OWNER=import_config["OWNER"], - PASSWORD=import_config["PASSWORD"], - SALON_JAIL=import_config["SALON_JAIL"], - SALON_JAIL_MODES=import_config["SALON_JAIL_MODES"], - SALON_LIBERER=import_config["SALON_LIBERER"], - SALON_CLONES=import_config["SALON_CLONES"], - API_TIMEOUT=import_config["API_TIMEOUT"], - PORTS_TO_SCAN=import_config["PORTS_TO_SCAN"], - WHITELISTED_IP=import_config["WHITELISTED_IP"], - GLINE_DURATION=import_config["GLINE_DURATION"], - DEBUG_LEVEL=import_config["DEBUG_LEVEL"], + SERVEUR_IP=import_config["SERVEUR_IP"] if "SERVEUR_IP" in import_config else '127.0.0.1', + SERVEUR_HOSTNAME=import_config["SERVEUR_HOSTNAME"] if "SERVEUR_HOSTNAME" in import_config else None, + SERVEUR_LINK=import_config["SERVEUR_LINK"] if "SERVEUR_LINK" in import_config else None, + SERVEUR_PORT=import_config["SERVEUR_PORT"] if "SERVEUR_PORT" in import_config else 6667, + SERVEUR_PASSWORD=import_config["SERVEUR_PASSWORD"] if "SERVEUR_PASSWORD" in import_config else None, + SERVEUR_ID=import_config["SERVEUR_ID"] if "SERVEUR_ID" in import_config else '19Z', + SERVEUR_SSL=import_config["SERVEUR_SSL"] if "SERVEUR_SSL" in import_config else False, + SERVICE_NAME=import_config["SERVICE_NAME"] if "SERVICE_NAME" in import_config else 'Defender', + SERVICE_NICKNAME=import_config["SERVICE_NICKNAME"] if "SERVICE_NICKNAME" in import_config else 'Defender', + SERVICE_REALNAME=import_config["SERVICE_REALNAME"] if "SERVICE_REALNAME" in import_config else 'Defender Security', + SERVICE_USERNAME=import_config["SERVICE_USERNAME"] if "SERVICE_USERNAME" in import_config else 'IRCSecurity', + SERVICE_HOST=import_config["SERVICE_HOST"] if "SERVICE_HOST" in import_config else 'defender.local.network', + SERVICE_INFO=import_config["SERVICE_INFO"] if "SERVICE_INFO" in import_config else 'Defender Network IRC Service', + SERVICE_CHANLOG=import_config["SERVICE_CHANLOG"] if "SERVICE_CHANLOG" in import_config else '#services', + SERVICE_SMODES=import_config["SERVICE_SMODES"] if "SERVICE_SMODES" in import_config else '+ioqBS', + SERVICE_CMODES=import_config["SERVICE_CMODES"] if "SERVICE_CMODES" in import_config else 'ntsOP', + SERVICE_UMODES=import_config["SERVICE_UMODES"] if "SERVICE_UMODES" in import_config else 'o', + SERVICE_PREFIX=import_config["SERVICE_PREFIX"] if "SERVICE_PREFIX" in import_config else '!', + OWNER=import_config["OWNER"] if "OWNER" in import_config else 'admin', + PASSWORD=import_config["PASSWORD"] if "PASSWORD" in import_config else 'admin', + SALON_JAIL=import_config["SALON_JAIL"] if "SALON_JAIL" in import_config else '#jail', + SALON_JAIL_MODES=import_config["SALON_JAIL_MODES"] if "SALON_JAIL_MODES" in import_config else 'sS', + SALON_LIBERER=import_config["SALON_LIBERER"] if "SALON_LIBERER" in import_config else '#welcome', + SALON_CLONES=import_config["SALON_CLONES"] if "SALON_CLONES" in import_config else '#clones', + API_TIMEOUT=import_config["API_TIMEOUT"] if "API_TIMEOUT" in import_config else 2, + PORTS_TO_SCAN=import_config["PORTS_TO_SCAN"] if "PORTS_TO_SCAN" in import_config else [], + WHITELISTED_IP=import_config["WHITELISTED_IP"] if "WHITELISTED_IP" in import_config else ['127.0.0.1'], + GLINE_DURATION=import_config["GLINE_DURATION"] if "GLINE_DURATION" in import_config else '30', + DEBUG_LEVEL=import_config["DEBUG_LEVEL"] if "DEBUG_LEVEL" in import_config else 20, CONFIG_COLOR=import_config["CONFIG_COLOR"], table_admin='core_admin', table_commande='core_command', diff --git a/mods/mod_clone.py b/mods/mod_clone.py index 410eb11..52e71e5 100644 --- a/mods/mod_clone.py +++ b/mods/mod_clone.py @@ -153,18 +153,22 @@ class Clone(): self.Irc.send2socket(f':{self.Config.SERVICE_NICKNAME} PRIVMSG {self.Config.SERVICE_CHANLOG} :Clones start to join {channel_name} with {wait} secondes frequency') if clone_name is None: for clone in self.Clone.UID_CLONE_DB: - time.sleep(wait) - self.Irc.send2socket(f':{self.Config.SERVICE_NICKNAME} PRIVMSG {clone.nickname} :JOIN {channel_name}') + if not channel_name in clone.channels: + time.sleep(wait) + self.Irc.send2socket(f':{self.Config.SERVICE_NICKNAME} PRIVMSG {clone.nickname} :JOIN {channel_name}') + clone.channels.append(channel_name) else: for clone in self.Clone.UID_CLONE_DB: if clone_name == clone.nickname: - time.sleep(wait) - self.Irc.send2socket(f':{self.Config.SERVICE_NICKNAME} PRIVMSG {clone.nickname} :JOIN {channel_name}') + if not channel_name in clone.channels: + time.sleep(wait) + self.Irc.send2socket(f':{self.Config.SERVICE_NICKNAME} PRIVMSG {clone.nickname} :JOIN {channel_name}') + clone.channels.append(channel_name) def generate_names(self) -> tuple[str, str, str]: try: fake = faker.Faker('en_GB') - nickname = fake.first_name() + # nickname = fake.first_name() # username = fake.last_name() # Generate Username @@ -175,6 +179,14 @@ class Clone(): # Create realname XX F|M Department gender = fake.random_choices(['F','M'], 1) gender = ''.join(gender) + + if gender == 'F': + nickname = fake.first_name_female() + elif gender == 'M': + nickname = fake.first_name_male() + else: + nickname = fake.first_name() + age = random.randint(20, 60) fake_fr = faker.Faker(['fr_FR', 'en_GB']) department = fake_fr.department_name() @@ -185,11 +197,11 @@ class Clone(): randomize = ''.join(random.choice(caracteres) for _ in range(2)) nickname = nickname + str(randomize) self.Clone.insert( - self.Clone.CloneModel(alive=True, nickname=nickname, username=username, realname=realname) + self.Clone.CloneModel(alive=True, nickname=nickname, username=username, realname=realname, channels=[]) ) else: self.Clone.insert( - self.Clone.CloneModel(alive=True, nickname=nickname, username=username, realname=realname) + self.Clone.CloneModel(alive=True, nickname=nickname, username=username, realname=realname, channels=[]) ) return (nickname, username, realname) diff --git a/version.json b/version.json index cc238b3..f3e4803 100644 --- a/version.json +++ b/version.json @@ -1,3 +1,3 @@ { - "version": "5.2.4" + "version": "5.2.5" } \ No newline at end of file From 9c78ad0860765f3446f47f00f602642c21befcbc Mon Sep 17 00:00:00 2001 From: adator <85586985+adator85@users.noreply.github.com> Date: Tue, 17 Sep 2024 01:46:09 +0200 Subject: [PATCH 09/14] Same version, copyright changed --- core/irc.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/irc.py b/core/irc.py index e118d02..80c7ca8 100644 --- a/core/irc.py +++ b/core/irc.py @@ -1376,7 +1376,7 @@ class Irc: self.send2socket(f':{dnickname} NOTICE {fromuser} : {uptime}') case 'copyright': - self.send2socket(f':{dnickname} NOTICE {fromuser} : # Defender V.{self.Config.current_version} Developped by adator® and dktmb® #') + self.send2socket(f':{dnickname} NOTICE {fromuser} : # Defender V.{self.Config.current_version} Developped by adator® #') case 'checkversion': From 3cd2077f63b53c1152743b9611e4eebee46e21a4 Mon Sep 17 00:00:00 2001 From: adator <85586985+adator85@users.noreply.github.com> Date: Wed, 18 Sep 2024 19:08:08 +0200 Subject: [PATCH 10/14] update installation module --- core/installation.py | 43 ++++++++++++++++++++++--------------------- core/loadConf.py | 29 +++++++++++++++++++++++++++-- 2 files changed, 49 insertions(+), 23 deletions(-) diff --git a/core/installation.py b/core/installation.py index 9245968..2cdd5c4 100644 --- a/core/installation.py +++ b/core/installation.py @@ -28,22 +28,17 @@ class Install: self.set_configuration() - if not self.check_python_version(): - # Tester si c'est la bonne version de python - exit("Python Version Error") - else: + if self.skip_install: + return None - if self.skip_install: - return None + # Sinon tester les dependances python et les installer avec pip + if self.do_install(): - # Sinon tester les dependances python et les installer avec pip - if self.do_install(): + self.install_dependencies() - self.install_dependencies() + self.create_service_file() - self.create_service_file() - - self.print_final_message() + self.print_final_message() return None @@ -74,18 +69,24 @@ class Install: venv_python_executable=f'{os.path.join(defender_install_folder, venv_folder, "bin")}{os.sep}python' ) - # Exclude Windows OS + if self.check_python_version(): + # If the Python version is not good then Exit + exit("/!\\ Python version error /!\\") + + if not os.path.exists(os.path.join(self.config.defender_install_folder, 'core', 'configuration.json')): + # If configuration file do not exist + exit("/!\\ Configuration file (configuration.json) doesn't exist /!\\") + + # Exclude Windows OS from the installation if os.name == 'nt': #print('/!\\ Skip installation /!\\') self.skip_install = True - else: - if self.is_root(): - print(f'/!\\ I fully not recommend running Defender as root /!\\') - self.skip_install = True - # Check if configuration.json exist - if not os.path.exists({os.path.join(self.config.defender_install_folder, 'core', 'configuration.json')}): - print(f'/!\\ configuration.json is not available, please create it first /!\\') - self.skip_install = True + return False + + if self.is_root(): + exit(f'/!\\ I highly not recommend running Defender as root /!\\') + self.skip_install = True + return False def is_root(self) -> bool: diff --git a/core/loadConf.py b/core/loadConf.py index 80dc404..8735ddc 100644 --- a/core/loadConf.py +++ b/core/loadConf.py @@ -150,8 +150,33 @@ class Config: with open(conf_filename, 'r') as configuration_data: configuration:dict[str, Union[str, int, list, dict]] = json.load(configuration_data) - for key, value in configuration['CONFIG_COLOR'].items(): - configuration['CONFIG_COLOR'][key] = str(value).encode('utf-8').decode('unicode_escape') + config_dict = {"CONFIG_COLOR" : { + "blanche": "\\u0003\\u0030", + "noire": "\\u0003\\u0031", + "bleue": "\\u0003\\u0020", + "verte": "\\u0003\\u0033", + "rouge": "\\u0003\\u0034", + "jaune": "\\u0003\\u0036", + "gras": "\\u0002", + "nogc": "\\u0002\\u0003" + } + } + + missing_color = False + + if not "CONFIG_COLOR" in configuration: + missing_color = True + configuration_color = config_dict + else: + configuration_color = configuration["CONFIG_COLOR"] + + if missing_color: + for key, value in configuration_color.items(): + configuration_color['CONFIG_COLOR'][key] = str(value).encode('utf-8').decode('unicode_escape') + configuration['CONFIG_COLOR'] = configuration_color['CONFIG_COLOR'] + else: + for key, value in configuration['CONFIG_COLOR'].items(): + configuration['CONFIG_COLOR'][key] = str(value).encode('utf-8').decode('unicode_escape') return configuration From 0f31e67be6ec62433db896a6576e51825f268011 Mon Sep 17 00:00:00 2001 From: adator <85586985+adator85@users.noreply.github.com> Date: Wed, 18 Sep 2024 19:10:15 +0200 Subject: [PATCH 11/14] check python version fix --- core/installation.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/installation.py b/core/installation.py index 2cdd5c4..c424e01 100644 --- a/core/installation.py +++ b/core/installation.py @@ -69,7 +69,7 @@ class Install: venv_python_executable=f'{os.path.join(defender_install_folder, venv_folder, "bin")}{os.sep}python' ) - if self.check_python_version(): + if not self.check_python_version(): # If the Python version is not good then Exit exit("/!\\ Python version error /!\\") From c59dd16e878f990cb45b05e20a158c579ee8b6de Mon Sep 17 00:00:00 2001 From: adator <85586985+adator85@users.noreply.github.com> Date: Wed, 18 Sep 2024 20:21:44 +0200 Subject: [PATCH 12/14] V5.2.6 --- README.md | 12 +----------- core/loadConf.py | 3 +++ version.json | 2 +- 3 files changed, 5 insertions(+), 12 deletions(-) diff --git a/README.md b/README.md index 04b467a..247798d 100644 --- a/README.md +++ b/README.md @@ -127,18 +127,8 @@ Pour Les prochains lancement de defender vous devez utiliser la commande suivant "SERVICE_HOST": "HOST.DE.TON.DEFENDER", "OWNER": "TON_NICK_NAME", - "PASSWORD": "admin", + "PASSWORD": "admin" - "CONFIG_COLOR": { - "blanche": "\\u0003\\u0030", - "noire": "\\u0003\\u0031", - "bleue": "\\u0003\\u0020", - "verte": "\\u0003\\u0033", - "rouge": "\\u0003\\u0034", - "jaune": "\\u0003\\u0036", - "gras": "\\u0002", - "nogc": "\\u0002\\u0003" - } } ``` diff --git a/core/loadConf.py b/core/loadConf.py index 8735ddc..5224b3a 100644 --- a/core/loadConf.py +++ b/core/loadConf.py @@ -184,6 +184,9 @@ class Config: print(f'FileNotFound: {fe}') print('Configuration file not found please create core/configuration.json') sys.exit(0) + except KeyError as ke: + print(f'Key Error: {ke}') + print('The key must be defined in core/configuration.json') def __load_service_configuration(self) -> ConfigDataModel: import_config = self.__load_json_service_configuration() diff --git a/version.json b/version.json index f3e4803..473fc25 100644 --- a/version.json +++ b/version.json @@ -1,3 +1,3 @@ { - "version": "5.2.5" + "version": "5.2.6" } \ No newline at end of file From 8f08a1e77fcbb24e19c4b1622df79a53c142a7e9 Mon Sep 17 00:00:00 2001 From: adator <85586985+adator85@users.noreply.github.com> Date: Wed, 18 Sep 2024 20:29:33 +0200 Subject: [PATCH 13/14] update readme.md --- README.md | 48 ++++++++++++++++++++++++++++++++++++++++++------ 1 file changed, 42 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index 247798d..ec8e033 100644 --- a/README.md +++ b/README.md @@ -137,10 +137,10 @@ Pour Les prochains lancement de defender vous devez utiliser la commande suivant ```json { "SERVEUR_IP": "YOUR.SERVER.IP", - "SERVEUR_HOSTNAME": "irc.deb.biz.st", - "SERVEUR_LINK": "defenderdev.deb.biz.st", + "SERVEUR_HOSTNAME": "YOUR.SERVER.HOST", + "SERVEUR_LINK": "LINK.DE.TON.SERVER", "SERVEUR_PORT": 6901, - "SERVEUR_PASSWORD": "yourpassword", + "SERVEUR_PASSWORD": "YOUR_LINK_PASSWORD", "SERVEUR_ID": "10Z", "SERVEUR_SSL": true, @@ -191,10 +191,46 @@ Pour Les prochains lancement de defender vous devez utiliser la commande suivant Le mot de passe de l'administrateur et le mot de passe du service doivent être modifiés pour des raisons de sécurité. Ne partagez pas vos informations de connexion au serveur IRC avec des tiers. a votre premiere connexion vous devez tapez +``` + /msg [NomDuService] auth [nickname] [password] + -- Une fois identifié tapez la commande suivante + /msg [NomDuService] editaccess [nickname] [Nouveau-Password] 5 +``` +# Unrealircd configuration +``` +listen { + ip *; + port 6901; + options { tls; serversonly; } +} - /msg [NomDuService] auth [nickname] [password] - -- Une fois identifié tapez la commande suivante - /msg [NomDuService] editaccess [nickname] [Nouveau-Password] 5 +link LINK.DE.TON.SERVER +{ + + incoming { + mask *; + bind-ip *; + port 6901; + //options { tls; }; + } + + outgoing { + bind-ip *; /* ou une IP précise */ + hostname LINK.DE.TON.SERVER; + port 6901; + //options { tls; } + } + + password "YOUR_LINK_PASSWORD"; + + class servers; + +} + +ulines { + LINK.DE.TON.SERVER; +} +``` # Extension: Le code est modulaire et conçu pour être facilement étendu. Vous pouvez ajouter de nouvelles commandes, de nouvelles fonctionnalités (mods/mod_test.py est un exemple pour bien demarrer la création de son module). From ee039322d4aa5273199e219c22edc4e14864c007 Mon Sep 17 00:00:00 2001 From: adator <85586985+adator85@users.noreply.github.com> Date: Thu, 19 Sep 2024 21:06:07 +0200 Subject: [PATCH 14/14] V5.2.7 --- core/connection.py | 10 +++++----- core/irc.py | 6 +++--- core/loadConf.py | 16 ++++++++-------- mods/mod_clone.py | 24 ++++++++++++++++++++++++ version.json | 2 +- 5 files changed, 41 insertions(+), 17 deletions(-) diff --git a/core/connection.py b/core/connection.py index b931388..98af05c 100644 --- a/core/connection.py +++ b/core/connection.py @@ -144,6 +144,10 @@ class Connection: self.Base.logs.error(f"OSError __connect_to_irc: {oe} - {data}") self.signal = False + self.IrcSocket.shutdown(socket.SHUT_WR) + self.IrcSocket.shutdown(socket.SHUT_RD) + self.Base.logs.info(f"<<{self.currentCloneObject.nickname}>> Clone Disconnected ...") + except AssertionError as ae: self.Base.logs.error(f'Assertion error : {ae}') except ValueError as ve: @@ -154,11 +158,6 @@ class Connection: self.Base.logs.critical(f"{atte}") except Exception as e: self.Base.logs.error(f"Exception: {e}") - finally: - self.IrcSocket.shutdown(socket.SHUT_WR) - self.IrcSocket.shutdown(socket.SHUT_RD) - self.Base.logs.info(f"<<{self.currentCloneObject.nickname}>> Clone Disconnected ...") - # self.IrcSocket.close() def parser(self, cmd:list[bytes]): try: @@ -177,6 +176,7 @@ class Connection: error_value = str(response[1]).replace(':','') if error_value == 'Closing': self.Base.logs.info(f"<<{self.currentCloneObject.nickname}>> {response} ...") + self.currentCloneObject.connected = False # self.signal = False match response[1]: diff --git a/core/irc.py b/core/irc.py index 80c7ca8..a8f1f8e 100644 --- a/core/irc.py +++ b/core/irc.py @@ -1017,7 +1017,7 @@ class Irc: current_command = cmd[0] uid_to_deauth = self.User.get_uid(fromuser) self.delete_db_admin(uid_to_deauth) - self.send2socket(f":{dnickname} PRIVMSG {dchanlog} :[ {self.Config.CONFIG_COLOR['rouge']}{current_command}{self.Config.CONFIG_COLOR['noire']} ] - {self.User.get_nickname(fromuser)} est désormais déconnecter de {dnickname}") + self.send2socket(f":{dnickname} PRIVMSG {dchanlog} :[ {self.Config.CONFIG_COLOR['rouge']}{str(current_command).upper()} ]{self.Config.CONFIG_COLOR['noire']} - {self.User.get_nickname(fromuser)} est désormais déconnecter de {dnickname}") case 'auth': # ['auth', 'adator', 'password'] @@ -1039,10 +1039,10 @@ class Irc: if not user_from_db is None: uid_user = self.User.get_uid(user_to_log) self.insert_db_admin(uid_user, user_from_db[1]) - self.send2socket(f":{dnickname} PRIVMSG {dchanlog} :[ {self.Config.CONFIG_COLOR['verte']}{current_command}{self.Config.CONFIG_COLOR['noire']} ] - {self.User.get_nickname(fromuser)} est désormais connecté a {dnickname}") + self.send2socket(f":{dnickname} PRIVMSG {dchanlog} :[ {self.Config.CONFIG_COLOR['verte']}{str(current_command).upper()} ]{self.Config.CONFIG_COLOR['noire']} - {self.User.get_nickname(fromuser)} est désormais connecté a {dnickname}") self.send2socket(f":{self.Config.SERVICE_NICKNAME} NOTICE {fromuser} :Connexion a {dnickname} réussie!") else: - self.send2socket(f":{dnickname} PRIVMSG {dchanlog} :[ {self.Config.CONFIG_COLOR['rouge']}{current_command}{self.Config.CONFIG_COLOR['noire']} ] - {self.User.get_nickname(fromuser)} a tapé un mauvais mot de pass") + self.send2socket(f":{dnickname} PRIVMSG {dchanlog} :[ {self.Config.CONFIG_COLOR['rouge']}{str(current_command).upper()} ]{self.Config.CONFIG_COLOR['noire']} - {self.User.get_nickname(fromuser)} a tapé un mauvais mot de pass") self.send2socket(f":{self.Config.SERVICE_NICKNAME} NOTICE {fromuser} :Mot de passe incorrecte") else: diff --git a/core/loadConf.py b/core/loadConf.py index 5224b3a..a7b4a2d 100644 --- a/core/loadConf.py +++ b/core/loadConf.py @@ -151,14 +151,14 @@ class Config: configuration:dict[str, Union[str, int, list, dict]] = json.load(configuration_data) config_dict = {"CONFIG_COLOR" : { - "blanche": "\\u0003\\u0030", - "noire": "\\u0003\\u0031", - "bleue": "\\u0003\\u0020", - "verte": "\\u0003\\u0033", - "rouge": "\\u0003\\u0034", - "jaune": "\\u0003\\u0036", - "gras": "\\u0002", - "nogc": "\\u0002\\u0003" + "blanche": "\x0300", + "noire": "\x0301", + "bleue": "\x0302", + "verte": "\x0303", + "rouge": "\x0304", + "jaune": "\x0306", + "gras": "\x02", + "nogc": "\x02\x03" } } diff --git a/mods/mod_clone.py b/mods/mod_clone.py index 52e71e5..bcb9fcf 100644 --- a/mods/mod_clone.py +++ b/mods/mod_clone.py @@ -122,6 +122,25 @@ class Clone(): return None + def thread_clone_clean_up(self, wait: float): + + activated = True + + while activated: + clone_to_kill: list[str] = [] + + for clone in self.Clone.UID_CLONE_DB: + if not clone.connected and clone.alive: + clone_to_kill.append(clone.nickname) + clone.alive = False + + for clone_nickname in clone_to_kill: + if self.Clone.delete(clone_nickname): + self.Logs.debug(f'<<{clone_nickname}>> object has been deleted') + + del clone_to_kill + time.sleep(wait) + def thread_change_hostname(self): fake = faker.Faker('en_GB') @@ -142,6 +161,9 @@ class Clone(): found = True clone.vhost = rand_ip break + if not clone in self.Clone.UID_CLONE_DB: + found = True + break def thread_create_clones(self, nickname: str, username: str, realname: str, channels: list, server_port: int, ssl: bool) -> None: @@ -262,6 +284,8 @@ class Clone(): self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} :{str(number_of_clones)} clones joined the network') + self.Base.create_thread(self.thread_clone_clean_up, (5, ), run_once=True) + except Exception as err: self.Logs.error(f'{err}') self.Irc.send2socket(f':{dnickname} NOTICE {fromuser} :/msg {dnickname} clone connect [number of clone you want to connect]') diff --git a/version.json b/version.json index 473fc25..842020b 100644 --- a/version.json +++ b/version.json @@ -1,3 +1,3 @@ { - "version": "5.2.6" + "version": "5.2.7" } \ No newline at end of file