iotsploit-cli 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_cli/__init__.py +1 -0
- iotsploit_cli/commands/__init__.py +1 -0
- iotsploit_cli/commands/base_commands.py +5 -0
- iotsploit_cli/commands/device_commands.py +536 -0
- iotsploit_cli/commands/django_commands.py +254 -0
- iotsploit_cli/commands/firmware_commands.py +213 -0
- iotsploit_cli/commands/linux_commands.py +35 -0
- iotsploit_cli/commands/network_commands.py +36 -0
- iotsploit_cli/commands/plugin_commands.py +249 -0
- iotsploit_cli/commands/system_commands.py +77 -0
- iotsploit_cli/commands/target_commands.py +246 -0
- iotsploit_cli/console.py +493 -0
- iotsploit_cli-0.0.6.dist-info/METADATA +67 -0
- iotsploit_cli-0.0.6.dist-info/RECORD +16 -0
- iotsploit_cli-0.0.6.dist-info/WHEEL +4 -0
- iotsploit_cli-0.0.6.dist-info/entry_points.txt +3 -0
iotsploit_cli/console.py
ADDED
|
@@ -0,0 +1,493 @@
|
|
|
1
|
+
#!/usr/bin/env python
|
|
2
|
+
import os
|
|
3
|
+
import sys
|
|
4
|
+
import time
|
|
5
|
+
import importlib
|
|
6
|
+
import inspect
|
|
7
|
+
from typing import Dict
|
|
8
|
+
import argparse
|
|
9
|
+
|
|
10
|
+
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "iotsploit_django.settings.dev")
|
|
11
|
+
|
|
12
|
+
import django
|
|
13
|
+
django.setup()
|
|
14
|
+
|
|
15
|
+
def ensure_database_initialized():
|
|
16
|
+
"""Automatically initialize database tables if they don't exist"""
|
|
17
|
+
try:
|
|
18
|
+
from iotsploit_django.adapters.django.plugins.models import Plugin
|
|
19
|
+
Plugin.objects.exists()
|
|
20
|
+
|
|
21
|
+
from sqlalchemy.exc import OperationalError
|
|
22
|
+
from iotsploit_django.adapters.django.sqlalchemy_database import get_default_sqlalchemy_db
|
|
23
|
+
from iotsploit_django.adapters.django.device_models import DeviceDriverState
|
|
24
|
+
|
|
25
|
+
session = get_default_sqlalchemy_db().SessionLocal()
|
|
26
|
+
try:
|
|
27
|
+
session.query(DeviceDriverState).first()
|
|
28
|
+
session.close()
|
|
29
|
+
except OperationalError as e:
|
|
30
|
+
session.close()
|
|
31
|
+
if "no such table: device_driver_states" in str(e):
|
|
32
|
+
print("🔧 Setting up SQLAlchemy database tables...")
|
|
33
|
+
db = get_default_sqlalchemy_db()
|
|
34
|
+
db.Base.metadata.create_all(db.engine)
|
|
35
|
+
print("✅ SQLAlchemy tables created successfully!")
|
|
36
|
+
else:
|
|
37
|
+
raise e
|
|
38
|
+
|
|
39
|
+
except Exception as e:
|
|
40
|
+
if "no such table" in str(e) or "no such column" in str(e):
|
|
41
|
+
print("🔧 Database not initialized. Setting up database...")
|
|
42
|
+
try:
|
|
43
|
+
print("📋 Running Django migrations...")
|
|
44
|
+
from django.core.management import execute_from_command_line
|
|
45
|
+
execute_from_command_line(["manage.py", "migrate", "--noinput"])
|
|
46
|
+
print("✅ Django migrations completed!")
|
|
47
|
+
|
|
48
|
+
print("📋 Creating SQLAlchemy tables...")
|
|
49
|
+
from iotsploit_django.adapters.django.sqlalchemy_database import get_default_sqlalchemy_db
|
|
50
|
+
db = get_default_sqlalchemy_db()
|
|
51
|
+
db.Base.metadata.create_all(db.engine)
|
|
52
|
+
print("✅ SQLAlchemy tables created!")
|
|
53
|
+
|
|
54
|
+
print("🎉 Database initialization completed successfully!")
|
|
55
|
+
|
|
56
|
+
except Exception as init_error:
|
|
57
|
+
print(f"❌ Error initializing database: {init_error}")
|
|
58
|
+
print("💡 You may need to run: python manage.py migrate")
|
|
59
|
+
raise init_error
|
|
60
|
+
else:
|
|
61
|
+
raise e
|
|
62
|
+
|
|
63
|
+
# Initialize database before proceeding
|
|
64
|
+
ensure_database_initialized()
|
|
65
|
+
|
|
66
|
+
# Now it's safe to import Django and other modules
|
|
67
|
+
import cmd2
|
|
68
|
+
from cmd2 import ansi
|
|
69
|
+
from iotsploit_django.adapters.django.target_models import TargetManager
|
|
70
|
+
from iotsploit_core.domain.target import Vehicle
|
|
71
|
+
from iotsploit_django.composition_root.wiring import get_device_driver_manager, get_exploit_plugin_manager
|
|
72
|
+
from iotsploit_django.adapters.django.device_models import DeviceManager
|
|
73
|
+
from iotsploit_core.domain.device import DeviceType, SerialDevice, SocketCANDevice, USBDevice
|
|
74
|
+
from iotsploit_django.tools.env_mgr import Env_Mgr
|
|
75
|
+
from iotsploit_django.tools.report_mgr import Report_Mgr
|
|
76
|
+
from iotsploit_django.tools.input_mgr import Input_Mgr
|
|
77
|
+
from iotsploit_django.tools.xlogger import xlog as logger
|
|
78
|
+
from pwnlib import term
|
|
79
|
+
term.term_mode = True
|
|
80
|
+
|
|
81
|
+
def global_exception_handler(exctype, value, traceback):
|
|
82
|
+
logger.error("Unhandled exception", exc_info=(exctype, value, traceback))
|
|
83
|
+
|
|
84
|
+
sys.excepthook = global_exception_handler
|
|
85
|
+
|
|
86
|
+
def discover_command_modules():
|
|
87
|
+
"""Auto-discover and load all command modules"""
|
|
88
|
+
from iotsploit_cli.commands.base_commands import BaseCommands
|
|
89
|
+
|
|
90
|
+
command_classes = []
|
|
91
|
+
commands_dir = os.path.join(os.path.dirname(__file__), 'commands')
|
|
92
|
+
|
|
93
|
+
for filename in os.listdir(commands_dir):
|
|
94
|
+
if filename.endswith('.py') and not filename.startswith('__') and filename != 'base_commands.py':
|
|
95
|
+
module_name = filename[:-3]
|
|
96
|
+
|
|
97
|
+
try:
|
|
98
|
+
module = importlib.import_module(f'iotsploit_cli.commands.{module_name}')
|
|
99
|
+
|
|
100
|
+
for name, obj in inspect.getmembers(module):
|
|
101
|
+
if (inspect.isclass(obj) and
|
|
102
|
+
issubclass(obj, BaseCommands) and
|
|
103
|
+
obj != BaseCommands):
|
|
104
|
+
command_classes.append(obj)
|
|
105
|
+
logger.debug(f"Discovered command class: {obj.__name__} from {module_name}")
|
|
106
|
+
except ImportError as e:
|
|
107
|
+
logger.warning(f"Could not import {module_name}: {e}")
|
|
108
|
+
except Exception as e:
|
|
109
|
+
logger.error(f"Error loading {module_name}: {e}")
|
|
110
|
+
|
|
111
|
+
logger.info(f"Auto-discovered {len(command_classes)} command modules")
|
|
112
|
+
return command_classes
|
|
113
|
+
|
|
114
|
+
# Auto-discover command modules
|
|
115
|
+
try:
|
|
116
|
+
CommandMixins = discover_command_modules()
|
|
117
|
+
logger.info(f"Successfully loaded command modules: {[cls.__name__ for cls in CommandMixins]}")
|
|
118
|
+
except Exception as e:
|
|
119
|
+
logger.error(f"Failed to discover command modules: {e}")
|
|
120
|
+
CommandMixins = []
|
|
121
|
+
|
|
122
|
+
# Create dynamic inheritance
|
|
123
|
+
SAT_Shell_Base = type('SAT_Shell_Base', (cmd2.Cmd, *CommandMixins), {})
|
|
124
|
+
|
|
125
|
+
class SAT_Shell(SAT_Shell_Base):
|
|
126
|
+
intro = ansi.style('''
|
|
127
|
+
██╗ █████╗ ████████╗███████╗██████╗ ██╗ ██████╗ ██╗████████╗
|
|
128
|
+
██║██╔═══██╗╚══██╔══╝██╔════╝██╔══██╗██║ ██╔═══██╗██║╚══██╔══╝
|
|
129
|
+
██║██║ ██║ ██║ ███████╗██████╔╝██║ ██║ ██║██║ ██║
|
|
130
|
+
██║██║ ██║ ██║ ╚════██║██╔═══╝ ██║ ██║ ██║██║ ██║
|
|
131
|
+
██║╚██████╔╝ ██║ ███████║██║ ███████╗╚██████╔╝██║ ██║
|
|
132
|
+
╚═╝ ╚═════╝ ╚═╝ ╚══════╝╚═╝ ╚══════╝ ╚═════╝ ╚═╝ ╚═╝
|
|
133
|
+
''', fg=ansi.Fg.GREEN) + '\n' + ansi.style('Welcome to IoTSploit Shell. Type help or ? to list commands.\n', fg=ansi.Fg.YELLOW)
|
|
134
|
+
|
|
135
|
+
prompt = ansi.style('<IoX_SHELL> ', fg=ansi.Fg.BLUE)
|
|
136
|
+
|
|
137
|
+
def __init__(self):
|
|
138
|
+
# Initialize the command categories dictionary before calling super().__init__()
|
|
139
|
+
self._cmd_to_category = {}
|
|
140
|
+
|
|
141
|
+
# Now call the parent class initialization
|
|
142
|
+
super().__init__()
|
|
143
|
+
|
|
144
|
+
# Rest of your initialization code...
|
|
145
|
+
self.django_server_process = None
|
|
146
|
+
self.daphne_server_process = None
|
|
147
|
+
self.mcp_bridge_process = None
|
|
148
|
+
self.celery_worker_process = None
|
|
149
|
+
|
|
150
|
+
# Initialize device manager and connected devices
|
|
151
|
+
self.device_driver_manager = get_device_driver_manager()
|
|
152
|
+
# 初始化设备相关属性
|
|
153
|
+
self._current_plugin = None
|
|
154
|
+
self._current_device = None
|
|
155
|
+
self._current_driver = None
|
|
156
|
+
self.connected_devices = {}
|
|
157
|
+
|
|
158
|
+
|
|
159
|
+
# Initialize plugin manager
|
|
160
|
+
# Use Django composition root to wire ORM repos (+ optional Celery runner).
|
|
161
|
+
# Default to Celery-enabled behavior for parity with previous implementation.
|
|
162
|
+
use_celery = os.getenv("SAT_SHELL_USE_CELERY", "1").lower() not in ("0", "false", "no")
|
|
163
|
+
self.plugin_manager = get_exploit_plugin_manager(use_celery=use_celery)
|
|
164
|
+
self.plugin_manager.initialize()
|
|
165
|
+
|
|
166
|
+
# Initialize target manager
|
|
167
|
+
self.target_manager = TargetManager.get_instance()
|
|
168
|
+
self.target_manager.register_target("vehicle", Vehicle)
|
|
169
|
+
|
|
170
|
+
# Check if database has any targets, if not, optionally load from JSON as initial setup
|
|
171
|
+
existing_targets = self.target_manager.get_all_targets()
|
|
172
|
+
if not existing_targets:
|
|
173
|
+
logger.info("No targets found in database. You can:")
|
|
174
|
+
logger.info("1. Use Flutter UI to create targets")
|
|
175
|
+
logger.info("2. Use 'target_import' command to import from JSON file")
|
|
176
|
+
logger.info("3. Use target management commands to create targets manually")
|
|
177
|
+
else:
|
|
178
|
+
logger.info(f"Found {len(existing_targets)} targets in database")
|
|
179
|
+
|
|
180
|
+
# Note: Removed automatic JSON loading to prevent overwriting user changes
|
|
181
|
+
# Use 'target_import conf/target.json' command if you need to import from JSON
|
|
182
|
+
|
|
183
|
+
# Initialize device manager
|
|
184
|
+
self.device_manager = DeviceManager.get_instance()
|
|
185
|
+
self.device_manager.register_device(DeviceType.Serial, SerialDevice)
|
|
186
|
+
self.device_manager.register_device(DeviceType.USB, USBDevice)
|
|
187
|
+
self.device_manager.register_device(DeviceType.CAN, SocketCANDevice)
|
|
188
|
+
|
|
189
|
+
# Note: Removed automatic JSON loading to use database as single source of truth
|
|
190
|
+
# Devices are now discovered by drivers via scan() or managed through UI/CLI
|
|
191
|
+
# Use 'device_import <json_file>' command if you need to import from JSON
|
|
192
|
+
existing_devices = self.device_manager.get_all_devices()
|
|
193
|
+
if not existing_devices:
|
|
194
|
+
logger.info("No devices found in database. Devices will be auto-discovered by drivers.")
|
|
195
|
+
logger.info("You can also use 'device_import conf/devices.json' to import from JSON file.")
|
|
196
|
+
|
|
197
|
+
|
|
198
|
+
# Customize help display
|
|
199
|
+
self.help_category_header = ansi.style("\n{:-^80}\n", fg=ansi.Fg.BLUE)
|
|
200
|
+
self.help_category_footer = "\n"
|
|
201
|
+
|
|
202
|
+
# Group all commands under Shell Commands
|
|
203
|
+
self._cmd_to_category.update({
|
|
204
|
+
'alias': 'Shell Commands',
|
|
205
|
+
'connect_wifi': 'Shell Commands',
|
|
206
|
+
'device_info': 'Shell Commands',
|
|
207
|
+
'edit': 'Shell Commands',
|
|
208
|
+
'execute_plugin': 'Shell Commands',
|
|
209
|
+
'exec': 'Shell Commands',
|
|
210
|
+
'exit': 'Shell Commands',
|
|
211
|
+
'exploit': 'Shell Commands',
|
|
212
|
+
'help': 'Shell Commands',
|
|
213
|
+
'history': 'Shell Commands',
|
|
214
|
+
'list_device_drivers': 'Shell Commands',
|
|
215
|
+
'list_devices': 'Shell Commands',
|
|
216
|
+
'list_plugins': 'Shell Commands',
|
|
217
|
+
'list_targets': 'Shell Commands',
|
|
218
|
+
'ls': 'Shell Commands',
|
|
219
|
+
'lsdev': 'Shell Commands',
|
|
220
|
+
'lsdrv': 'Shell Commands',
|
|
221
|
+
'lsp': 'Shell Commands',
|
|
222
|
+
'lst': 'Shell Commands',
|
|
223
|
+
'lsusb': 'Shell Commands',
|
|
224
|
+
'macro': 'Shell Commands',
|
|
225
|
+
'quit': 'Shell Commands',
|
|
226
|
+
'run_pyscript': 'Shell Commands',
|
|
227
|
+
'run_script': 'Shell Commands',
|
|
228
|
+
'runserver': 'Shell Commands',
|
|
229
|
+
'set': 'Shell Commands',
|
|
230
|
+
'set_log_level': 'Shell Commands',
|
|
231
|
+
'shell': 'Shell Commands',
|
|
232
|
+
'shortcuts': 'Shell Commands',
|
|
233
|
+
'stop_server': 'Shell Commands',
|
|
234
|
+
})
|
|
235
|
+
|
|
236
|
+
def emptyline(self):
|
|
237
|
+
self.onecmd("help")
|
|
238
|
+
|
|
239
|
+
def do_help(self, arg):
|
|
240
|
+
'List available commands with "help" or detailed help with "help cmd".'
|
|
241
|
+
if arg:
|
|
242
|
+
# Show help for specific command
|
|
243
|
+
super().do_help(arg)
|
|
244
|
+
return
|
|
245
|
+
|
|
246
|
+
self.poutput(ansi.style("\nAvailable Commands:", fg=ansi.Fg.GREEN, bold=True))
|
|
247
|
+
self.poutput(ansi.style("Use 'help <command>' for detailed information about a command.\n", fg=ansi.Fg.YELLOW))
|
|
248
|
+
|
|
249
|
+
cmds_by_category = self.get_all_commands_by_category()
|
|
250
|
+
|
|
251
|
+
categories = sorted([cat for cat in cmds_by_category.keys() if cat != 'Shell Commands'])
|
|
252
|
+
if 'Shell Commands' in cmds_by_category:
|
|
253
|
+
categories.append('Shell Commands')
|
|
254
|
+
|
|
255
|
+
for category in categories:
|
|
256
|
+
if category == 'Uncategorized':
|
|
257
|
+
continue
|
|
258
|
+
|
|
259
|
+
self.poutput(self.help_category_header.format(f" {category} "))
|
|
260
|
+
cmd_list = sorted(cmds_by_category[category])
|
|
261
|
+
|
|
262
|
+
max_cmd_length = max(len(cmd) for cmd in cmd_list) + 2
|
|
263
|
+
|
|
264
|
+
for cmd in cmd_list:
|
|
265
|
+
doc = self.get_command_doc(cmd)
|
|
266
|
+
padded_cmd = f" {cmd:<{max_cmd_length}}"
|
|
267
|
+
self.poutput(ansi.style(padded_cmd, fg=ansi.Fg.CYAN) +
|
|
268
|
+
ansi.style(f"- {doc}", fg=ansi.Fg.WHITE))
|
|
269
|
+
self.poutput(self.help_category_footer)
|
|
270
|
+
|
|
271
|
+
total_commands = sum(len(cmds) for cat, cmds in cmds_by_category.items() if cat != 'Uncategorized')
|
|
272
|
+
self.poutput(ansi.style(f"\nTotal commands: {total_commands}", fg=ansi.Fg.GREEN))
|
|
273
|
+
|
|
274
|
+
def get_command_doc(self, cmd_name):
|
|
275
|
+
"""Get the first line of the command's docstring."""
|
|
276
|
+
cmd_func = getattr(self, 'do_' + cmd_name, None)
|
|
277
|
+
if cmd_func and cmd_func.__doc__:
|
|
278
|
+
return cmd_func.__doc__.split('\n')[0]
|
|
279
|
+
return ''
|
|
280
|
+
|
|
281
|
+
def get_all_commands_by_category(self):
|
|
282
|
+
"""Return a dict mapping category names to lists of command names."""
|
|
283
|
+
categories = {}
|
|
284
|
+
|
|
285
|
+
# Get all command names (methods starting with 'do_')
|
|
286
|
+
command_names = [attr[3:] for attr in dir(self) if attr.startswith('do_')]
|
|
287
|
+
|
|
288
|
+
for cmd_name in command_names:
|
|
289
|
+
# Get the command function
|
|
290
|
+
cmd_func = getattr(self, 'do_' + cmd_name)
|
|
291
|
+
|
|
292
|
+
# Get category from cmd2's category decorator or from our manual mapping
|
|
293
|
+
if hasattr(cmd_func, 'category'):
|
|
294
|
+
category = cmd_func.category
|
|
295
|
+
else:
|
|
296
|
+
# Check our manual mapping or default to 'Uncategorized'
|
|
297
|
+
category = self._cmd_to_category.get(cmd_name, 'Uncategorized')
|
|
298
|
+
|
|
299
|
+
# Add command to appropriate category list
|
|
300
|
+
if category not in categories:
|
|
301
|
+
categories[category] = []
|
|
302
|
+
categories[category].append(cmd_name)
|
|
303
|
+
|
|
304
|
+
return categories
|
|
305
|
+
|
|
306
|
+
def _select_device(self):
|
|
307
|
+
"""Helper method to handle device selection process"""
|
|
308
|
+
try:
|
|
309
|
+
available_plugins = [
|
|
310
|
+
driver_name for driver_name, device
|
|
311
|
+
in self.connected_devices.items()
|
|
312
|
+
]
|
|
313
|
+
|
|
314
|
+
if not available_plugins:
|
|
315
|
+
logger.error(ansi.style("No initialized devices available", fg=ansi.Fg.RED))
|
|
316
|
+
return False
|
|
317
|
+
|
|
318
|
+
selected_plugin = Input_Mgr.Instance().single_choice(
|
|
319
|
+
"Select device plugin",
|
|
320
|
+
available_plugins
|
|
321
|
+
)
|
|
322
|
+
|
|
323
|
+
device = self.connected_devices.get(selected_plugin)
|
|
324
|
+
if not device:
|
|
325
|
+
logger.error(ansi.style(f"Device not found for {selected_plugin}", fg=ansi.Fg.RED))
|
|
326
|
+
return False
|
|
327
|
+
|
|
328
|
+
self._current_device = device
|
|
329
|
+
self._current_driver = self.device_driver_manager.get_driver_instance(selected_plugin)
|
|
330
|
+
self._current_plugin = selected_plugin
|
|
331
|
+
|
|
332
|
+
logger.info(ansi.style(f"Selected device: {device.name}", fg=ansi.Fg.GREEN))
|
|
333
|
+
return True
|
|
334
|
+
|
|
335
|
+
except Exception as e:
|
|
336
|
+
logger.error(ansi.style(f"Error during device selection: {str(e)}", fg=ansi.Fg.RED))
|
|
337
|
+
logger.debug("Detailed error:", exc_info=True)
|
|
338
|
+
return False
|
|
339
|
+
|
|
340
|
+
def _display_devices(self, devices: Dict, sources: Dict):
|
|
341
|
+
"""Helper method to display device information"""
|
|
342
|
+
for device_id, device in devices.items():
|
|
343
|
+
source = sources.get(device_id, "unknown")
|
|
344
|
+
source_color = ansi.Fg.GREEN if source == "dynamic" else ansi.Fg.BLUE
|
|
345
|
+
|
|
346
|
+
logger.info(ansi.style(f"\n Device ID: {device_id}", fg=ansi.Fg.CYAN))
|
|
347
|
+
logger.info(ansi.style(f" Source: {source}", fg=source_color))
|
|
348
|
+
logger.info(f" Name: {device.name}")
|
|
349
|
+
logger.info(" " + "-" * 40)
|
|
350
|
+
|
|
351
|
+
def _auto_initialize_devices(self):
|
|
352
|
+
"""自动扫描并初始化所有可用设备"""
|
|
353
|
+
logger.info("Automatic device initialization started...")
|
|
354
|
+
|
|
355
|
+
available_drivers = list(self.device_driver_manager.drivers.keys())
|
|
356
|
+
if not available_drivers:
|
|
357
|
+
logger.warning("No device drivers available!")
|
|
358
|
+
return
|
|
359
|
+
|
|
360
|
+
logger.info(f"Found {len(available_drivers)} drivers: {', '.join(available_drivers)}")
|
|
361
|
+
|
|
362
|
+
for driver_name in available_drivers:
|
|
363
|
+
try:
|
|
364
|
+
logger.info(f"Initializing {driver_name}...")
|
|
365
|
+
|
|
366
|
+
scan_result = self.device_driver_manager.scan_devices(driver_name)
|
|
367
|
+
if scan_result['status'] != 'success':
|
|
368
|
+
logger.error(f"Failed to scan {driver_name}: {scan_result.get('message', 'Unknown error')}")
|
|
369
|
+
continue
|
|
370
|
+
|
|
371
|
+
devices = scan_result.get('devices', [])
|
|
372
|
+
if not devices:
|
|
373
|
+
logger.warning(f"No devices found for {driver_name}")
|
|
374
|
+
continue
|
|
375
|
+
|
|
376
|
+
logger.info(f"Found {len(devices)} device(s) for {driver_name}")
|
|
377
|
+
|
|
378
|
+
for device in devices:
|
|
379
|
+
try:
|
|
380
|
+
logger.info(f"Processing device: {device.name} (ID: {device.device_id})")
|
|
381
|
+
|
|
382
|
+
init_result = self.device_driver_manager.initialize_device(driver_name, device)
|
|
383
|
+
if init_result['status'] != 'success':
|
|
384
|
+
logger.error(f"Failed to initialize {device.name}: {init_result['message']}")
|
|
385
|
+
continue
|
|
386
|
+
|
|
387
|
+
connect_result = self.device_driver_manager.connect_device(driver_name, device)
|
|
388
|
+
if connect_result['status'] != 'success':
|
|
389
|
+
logger.error(f"Failed to connect {device.name}: {connect_result['message']}")
|
|
390
|
+
continue
|
|
391
|
+
|
|
392
|
+
self.connected_devices[driver_name] = device
|
|
393
|
+
logger.info(f"Successfully connected {device.name} using {driver_name}")
|
|
394
|
+
|
|
395
|
+
except Exception as e:
|
|
396
|
+
logger.error(f"Error processing device: {str(e)}")
|
|
397
|
+
|
|
398
|
+
except Exception as e:
|
|
399
|
+
logger.error(f"Error initializing {driver_name}: {str(e)}")
|
|
400
|
+
|
|
401
|
+
self._show_initialization_summary()
|
|
402
|
+
|
|
403
|
+
def _show_initialization_summary(self):
|
|
404
|
+
"""打印设备初始化摘要"""
|
|
405
|
+
logger.info("Device Initialization Summary:")
|
|
406
|
+
|
|
407
|
+
for driver_name, driver in self.device_driver_manager.drivers.items():
|
|
408
|
+
logger.info("")
|
|
409
|
+
logger.info(f"{driver_name}:")
|
|
410
|
+
|
|
411
|
+
current_device = self.connected_devices.get(driver_name)
|
|
412
|
+
|
|
413
|
+
if current_device:
|
|
414
|
+
state = self.device_driver_manager.get_device_state(
|
|
415
|
+
driver_name,
|
|
416
|
+
device_id=current_device.device_id
|
|
417
|
+
)
|
|
418
|
+
logger.info(f" Device: {current_device.name}")
|
|
419
|
+
logger.info(f" State: {state.value}")
|
|
420
|
+
else:
|
|
421
|
+
logger.info(f" Device: No device connected")
|
|
422
|
+
logger.info(f" State: unknown")
|
|
423
|
+
|
|
424
|
+
commands = self.device_driver_manager.get_supported_commands(driver_name)
|
|
425
|
+
if commands:
|
|
426
|
+
logger.info(f" Commands: {', '.join(commands.keys())}")
|
|
427
|
+
|
|
428
|
+
def _cleanup_devices(self):
|
|
429
|
+
"""清理所有设备连接"""
|
|
430
|
+
if not self.connected_devices:
|
|
431
|
+
return
|
|
432
|
+
|
|
433
|
+
logger.info("Cleaning up device connections...")
|
|
434
|
+
for driver_name, device in list(self.connected_devices.items()):
|
|
435
|
+
try:
|
|
436
|
+
result = self.device_driver_manager.close_device(driver_name, device)
|
|
437
|
+
if result['status'] == 'success':
|
|
438
|
+
logger.info(f"Successfully closed {device.name}")
|
|
439
|
+
del self.connected_devices[driver_name]
|
|
440
|
+
else:
|
|
441
|
+
logger.error(f"Failed to close {device.name}: {result['message']}")
|
|
442
|
+
except Exception as e:
|
|
443
|
+
logger.error(f"Error closing {device.name}: {str(e)}")
|
|
444
|
+
|
|
445
|
+
def main():
|
|
446
|
+
import signal
|
|
447
|
+
import atexit
|
|
448
|
+
|
|
449
|
+
parser = argparse.ArgumentParser(description='SAT Shell entrypoint')
|
|
450
|
+
parser.add_argument('--runserver', action='store_true', help='Start servers directly and keep running until Ctrl+C; on Ctrl+C, stop servers')
|
|
451
|
+
args = parser.parse_args()
|
|
452
|
+
|
|
453
|
+
shell = SAT_Shell()
|
|
454
|
+
Report_Mgr.Instance().log_init()
|
|
455
|
+
Env_Mgr.Instance().set("SAT_RUN_IN_SHELL", True)
|
|
456
|
+
|
|
457
|
+
# Register OS signal + atexit handlers in the entrypoint so that unexpected exits
|
|
458
|
+
# (terminal closed / SIGTERM) also trigger the same cleanup path.
|
|
459
|
+
def _shutdown():
|
|
460
|
+
try:
|
|
461
|
+
shell.do_stop_server("")
|
|
462
|
+
finally:
|
|
463
|
+
try:
|
|
464
|
+
shell._cleanup_devices()
|
|
465
|
+
except Exception:
|
|
466
|
+
pass
|
|
467
|
+
|
|
468
|
+
def _signal_handler(signum, frame): # noqa: ARG001
|
|
469
|
+
_shutdown()
|
|
470
|
+
raise SystemExit(0)
|
|
471
|
+
|
|
472
|
+
def _atexit_handler():
|
|
473
|
+
# Do not raise here; just best-effort cleanup.
|
|
474
|
+
_shutdown()
|
|
475
|
+
|
|
476
|
+
signal.signal(signal.SIGTERM, _signal_handler)
|
|
477
|
+
signal.signal(signal.SIGINT, _signal_handler)
|
|
478
|
+
signal.signal(signal.SIGHUP, _signal_handler)
|
|
479
|
+
atexit.register(_atexit_handler)
|
|
480
|
+
|
|
481
|
+
if args.runserver:
|
|
482
|
+
ok = shell.do_runserver("")
|
|
483
|
+
if ok is False:
|
|
484
|
+
sys.exit(1)
|
|
485
|
+
while True:
|
|
486
|
+
time.sleep(1)
|
|
487
|
+
else:
|
|
488
|
+
shell.cmdloop()
|
|
489
|
+
|
|
490
|
+
|
|
491
|
+
if __name__ == '__main__':
|
|
492
|
+
main()
|
|
493
|
+
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
Metadata-Version: 2.3
|
|
2
|
+
Name: iotsploit-cli
|
|
3
|
+
Version: 0.0.6
|
|
4
|
+
Summary: IoTSploit CLI shell (console + command modules) - IoT security testing interactive interface
|
|
5
|
+
License: GPL-3.0-or-later
|
|
6
|
+
Keywords: iot,security,testing,pentest,cli,shell
|
|
7
|
+
Author: IoTSploit Team
|
|
8
|
+
Author-email: support@iotsploit.org
|
|
9
|
+
Requires-Python: >=3.10,<4.0
|
|
10
|
+
Classifier: Development Status :: 3 - Alpha
|
|
11
|
+
Classifier: Environment :: Console
|
|
12
|
+
Classifier: Intended Audience :: Developers
|
|
13
|
+
Classifier: Intended Audience :: Information Technology
|
|
14
|
+
Classifier: License :: OSI Approved :: GNU General Public License v3 or later (GPLv3+)
|
|
15
|
+
Classifier: Programming Language :: Python :: 3
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
19
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
20
|
+
Classifier: Topic :: Security
|
|
21
|
+
Classifier: Topic :: System :: Hardware
|
|
22
|
+
Requires-Dist: cmd2 (>=2.4,<3.0)
|
|
23
|
+
Requires-Dist: iotsploit-core (>=0.0.6,<0.0.7)
|
|
24
|
+
Requires-Dist: iotsploit-django (>=0.0.6,<0.0.7)
|
|
25
|
+
Requires-Dist: iotsploit-drivers (>=0.0.6,<0.0.7)
|
|
26
|
+
Requires-Dist: iotsploit-exploits (>=0.0.6,<0.0.7)
|
|
27
|
+
Requires-Dist: iotsploit-mcp (>=0.0.6,<0.0.7)
|
|
28
|
+
Requires-Dist: pwntools (>=4.12,<5.0)
|
|
29
|
+
Project-URL: Documentation, https://www.iotsploit.org/
|
|
30
|
+
Project-URL: Homepage, https://www.iotsploit.org/
|
|
31
|
+
Project-URL: Repository, https://github.com/TKXB/iotsploit
|
|
32
|
+
Description-Content-Type: text/markdown
|
|
33
|
+
|
|
34
|
+
# iotsploit-cli
|
|
35
|
+
|
|
36
|
+
IoTSploit interactive CLI shell for IoT security testing.
|
|
37
|
+
|
|
38
|
+
## Overview
|
|
39
|
+
|
|
40
|
+
This package provides the `iotsploit` command-line shell built on top of `cmd2`.
|
|
41
|
+
It bundles the core console loop (`console.py`) and all command modules
|
|
42
|
+
(`commands/`) that implement device management, plugin execution, target
|
|
43
|
+
management, network operations, and more.
|
|
44
|
+
|
|
45
|
+
## Installation
|
|
46
|
+
|
|
47
|
+
```bash
|
|
48
|
+
pip install iotsploit-cli
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
## Usage
|
|
52
|
+
|
|
53
|
+
```bash
|
|
54
|
+
iotsploit
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
Or with the Django server started immediately:
|
|
58
|
+
|
|
59
|
+
```bash
|
|
60
|
+
iotsploit --runserver
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
## License
|
|
64
|
+
|
|
65
|
+
GPL-3.0-or-later. See [LICENSE](../LICENSE) for details.
|
|
66
|
+
For commercial use, contact wang3919379@gmail.com.
|
|
67
|
+
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
iotsploit_cli/__init__.py,sha256=QpQDB0C0TMW2nV9L602di-QvV29L1HZkbRcSwD7cXOI,29
|
|
2
|
+
iotsploit_cli/commands/__init__.py,sha256=LXBeYtLCrCoVV2REWHEsBXxiDypZPVdLR9vjVg2nE5g,33
|
|
3
|
+
iotsploit_cli/commands/base_commands.py,sha256=I186hrgquBYY0HP_NPWzUsGmWi2LIwjjUckmMJSw3ws,97
|
|
4
|
+
iotsploit_cli/commands/device_commands.py,sha256=JPfAVgQg_BLrsX25vpwu9KN-cbKWDpuXa6C1vh1KJ9g,23466
|
|
5
|
+
iotsploit_cli/commands/django_commands.py,sha256=kwEr1bjnHukT0wgiIEfGyFWBZ-gGudo41Db6UbmcZYw,10828
|
|
6
|
+
iotsploit_cli/commands/firmware_commands.py,sha256=Y4YWxtSnWhS3gXvUbbZYsrjgPo1Q2JzW1lt02RCi-TQ,9142
|
|
7
|
+
iotsploit_cli/commands/linux_commands.py,sha256=XrkjhivUQF3n0k_4TEr7atZhWcqudx5lVCK46VrwukM,1110
|
|
8
|
+
iotsploit_cli/commands/network_commands.py,sha256=xCstdzff-h9kUTHM8sXnZCDSs7RRAOlYglG17f-YFko,1181
|
|
9
|
+
iotsploit_cli/commands/plugin_commands.py,sha256=pQQt85-Z8YoDZaTe46Xr0T4p2emWCiD808gXx_6yEIA,11774
|
|
10
|
+
iotsploit_cli/commands/system_commands.py,sha256=V9E-TQzElbNXYfU2uIsFccrROewHjyIIIjG9my7i9ZY,3187
|
|
11
|
+
iotsploit_cli/commands/target_commands.py,sha256=xG8l9rCPRw4CskTXOjSl2fGEmeRwYxOX1P5FcLYbB-s,10475
|
|
12
|
+
iotsploit_cli/console.py,sha256=S5xBMkstUZd3v6mDj9EXYTeEzuqOOIN2RFKgChLOv7Q,21378
|
|
13
|
+
iotsploit_cli-0.0.6.dist-info/METADATA,sha256=hHe3sgcoGXxq7MPG0DLPHlEqF4i0YdB4IkPB5g1Aev8,2080
|
|
14
|
+
iotsploit_cli-0.0.6.dist-info/WHEEL,sha256=IYZQI976HJqqOpQU6PHkJ8fb3tMNBFjg-Cn-pwAbaFM,88
|
|
15
|
+
iotsploit_cli-0.0.6.dist-info/entry_points.txt,sha256=c7_NpNNSkaSi0_gYVGX6LeZYekfNx5RhjzReN1cFZ30,56
|
|
16
|
+
iotsploit_cli-0.0.6.dist-info/RECORD,,
|