iotsploit-exploits 0.0.6__tar.gz

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.
Files changed (29) hide show
  1. iotsploit_exploits-0.0.6/PKG-INFO +65 -0
  2. iotsploit_exploits-0.0.6/README.md +42 -0
  3. iotsploit_exploits-0.0.6/pyproject.toml +43 -0
  4. iotsploit_exploits-0.0.6/src/iotsploit_exploits/__init__.py +1 -0
  5. iotsploit_exploits-0.0.6/src/iotsploit_exploits/adb_check/__init__.py +0 -0
  6. iotsploit_exploits-0.0.6/src/iotsploit_exploits/adb_check/adb_check.py +493 -0
  7. iotsploit_exploits-0.0.6/src/iotsploit_exploits/demo/__init__.py +0 -0
  8. iotsploit_exploits-0.0.6/src/iotsploit_exploits/demo/async_sleep_attack.py +106 -0
  9. iotsploit_exploits-0.0.6/src/iotsploit_exploits/demo/stream_data_attack.py +184 -0
  10. iotsploit_exploits-0.0.6/src/iotsploit_exploits/flood_attack/__init__.py +0 -0
  11. iotsploit_exploits-0.0.6/src/iotsploit_exploits/flood_attack/flood_attack.py +129 -0
  12. iotsploit_exploits-0.0.6/src/iotsploit_exploits/flood_attack/syn_flood_attack.py +233 -0
  13. iotsploit_exploits-0.0.6/src/iotsploit_exploits/greatfet_echo.py +103 -0
  14. iotsploit_exploits-0.0.6/src/iotsploit_exploits/greatfet_rubber_duck.py +417 -0
  15. iotsploit_exploits-0.0.6/src/iotsploit_exploits/hydra_cracker/weak_pass.txt +471 -0
  16. iotsploit_exploits-0.0.6/src/iotsploit_exploits/hydra_cracker/weak_pass_simple.txt +5 -0
  17. iotsploit_exploits-0.0.6/src/iotsploit_exploits/hydra_ssh_attack.py +159 -0
  18. iotsploit_exploits-0.0.6/src/iotsploit_exploits/ip_scan/__init__.py +0 -0
  19. iotsploit_exploits-0.0.6/src/iotsploit_exploits/ip_scan/ip_scan.py +196 -0
  20. iotsploit_exploits-0.0.6/src/iotsploit_exploits/nmap_scan/__init__.py +0 -0
  21. iotsploit_exploits-0.0.6/src/iotsploit_exploits/nmap_scan/nmap_scan.py +207 -0
  22. iotsploit_exploits-0.0.6/src/iotsploit_exploits/plugin_ssh.py +146 -0
  23. iotsploit_exploits-0.0.6/src/iotsploit_exploits/rubber_duck_scripts/linux_infogather.txt +126 -0
  24. iotsploit_exploits-0.0.6/src/iotsploit_exploits/rubber_duck_scripts/windows_payload.txt +93 -0
  25. iotsploit_exploits-0.0.6/src/iotsploit_exploits/serial/__init__.py +0 -0
  26. iotsploit_exploits-0.0.6/src/iotsploit_exploits/serial/picocom_serial_reader.py +704 -0
  27. iotsploit_exploits-0.0.6/src/iotsploit_exploits/simple_rubber_duck.py +183 -0
  28. iotsploit_exploits-0.0.6/src/iotsploit_exploits/wifi_scan/__init__.py +0 -0
  29. iotsploit_exploits-0.0.6/src/iotsploit_exploits/wifi_scan/wifi_scan.py +242 -0
@@ -0,0 +1,65 @@
1
+ Metadata-Version: 2.3
2
+ Name: iotsploit-exploits
3
+ Version: 0.0.6
4
+ Summary: Official IoTSploit exploit plugin package
5
+ License: GPL-3.0-or-later
6
+ Author: IoTSploit Team
7
+ Author-email: support@iotsploit.org
8
+ Requires-Python: >=3.10,<4.0
9
+ Classifier: License :: OSI Approved :: GNU General Public License v3 or later (GPLv3+)
10
+ Classifier: Programming Language :: Python :: 3
11
+ Classifier: Programming Language :: Python :: 3.10
12
+ Classifier: Programming Language :: Python :: 3.11
13
+ Classifier: Programming Language :: Python :: 3.12
14
+ Classifier: Programming Language :: Python :: 3.13
15
+ Requires-Dist: facedancer (>=3.0.6,<4.0)
16
+ Requires-Dist: iotsploit-core (>=0.0.6,<0.0.7)
17
+ Requires-Dist: iotsploit-django (>=0.0.6,<0.0.7)
18
+ Requires-Dist: pwntools (>=4.12)
19
+ Requires-Dist: pyserial (>=3.5)
20
+ Requires-Dist: scapy (>=2.5)
21
+ Description-Content-Type: text/markdown
22
+
23
+ # iotsploit-exploits
24
+
25
+ Official IoTSploit exploit plugin package. Contains all built-in exploit/security testing plugins.
26
+
27
+ ## Included Plugins
28
+
29
+ | Plugin | Module | Description |
30
+ |--------|--------|-------------|
31
+ | Flood Attack | `iotsploit_exploits.flood_attack` | Network flood attack |
32
+ | SYN Flood | `iotsploit_exploits.flood_attack` | TCP SYN flood attack |
33
+ | WiFi Scan | `iotsploit_exploits.wifi_scan` | WiFi network scanner |
34
+ | IP Scan | `iotsploit_exploits.ip_scan` | IP network scanner |
35
+ | Nmap Scan | `iotsploit_exploits.nmap_scan` | Nmap-based scanner |
36
+ | ADB Check | `iotsploit_exploits.adb_check` | Android Debug Bridge security check |
37
+ | Hydra SSH | `iotsploit_exploits.hydra_ssh_attack` | Hydra SSH attack |
38
+ | SSH Plugin | `iotsploit_exploits.plugin_ssh` | SSH connection plugin |
39
+ | Serial Reader | `iotsploit_exploits.serial` | Picocom serial reader |
40
+ | GreatFET Echo | `iotsploit_exploits.greatfet_echo` | FTDI echo emulation |
41
+ | Rubber Duck | `iotsploit_exploits.greatfet_rubber_duck` | USB rubber duck attack |
42
+ | Simple Rubber Duck | `iotsploit_exploits.simple_rubber_duck` | Simplified rubber duck |
43
+ | Async Sleep | `iotsploit_exploits.demo` | Demo async plugin |
44
+ | Stream Data | `iotsploit_exploits.demo` | Demo streaming plugin |
45
+
46
+ ## System Tool Dependencies
47
+
48
+ Some plugins require system-level tools (not installable via pip):
49
+
50
+ - **nmap_scan**: Requires `nmap` (`apt install nmap`)
51
+ - **hydra_ssh_attack**: Requires `hydra` (`apt install hydra`)
52
+ - **adb_check**: Requires `adb` (`apt install android-tools-adb`)
53
+
54
+ ## Installation
55
+
56
+ ```bash
57
+ pip install iotsploit-exploits
58
+ ```
59
+
60
+ For development:
61
+
62
+ ```bash
63
+ pip install -e ./iotsploit-exploits
64
+ ```
65
+
@@ -0,0 +1,42 @@
1
+ # iotsploit-exploits
2
+
3
+ Official IoTSploit exploit plugin package. Contains all built-in exploit/security testing plugins.
4
+
5
+ ## Included Plugins
6
+
7
+ | Plugin | Module | Description |
8
+ |--------|--------|-------------|
9
+ | Flood Attack | `iotsploit_exploits.flood_attack` | Network flood attack |
10
+ | SYN Flood | `iotsploit_exploits.flood_attack` | TCP SYN flood attack |
11
+ | WiFi Scan | `iotsploit_exploits.wifi_scan` | WiFi network scanner |
12
+ | IP Scan | `iotsploit_exploits.ip_scan` | IP network scanner |
13
+ | Nmap Scan | `iotsploit_exploits.nmap_scan` | Nmap-based scanner |
14
+ | ADB Check | `iotsploit_exploits.adb_check` | Android Debug Bridge security check |
15
+ | Hydra SSH | `iotsploit_exploits.hydra_ssh_attack` | Hydra SSH attack |
16
+ | SSH Plugin | `iotsploit_exploits.plugin_ssh` | SSH connection plugin |
17
+ | Serial Reader | `iotsploit_exploits.serial` | Picocom serial reader |
18
+ | GreatFET Echo | `iotsploit_exploits.greatfet_echo` | FTDI echo emulation |
19
+ | Rubber Duck | `iotsploit_exploits.greatfet_rubber_duck` | USB rubber duck attack |
20
+ | Simple Rubber Duck | `iotsploit_exploits.simple_rubber_duck` | Simplified rubber duck |
21
+ | Async Sleep | `iotsploit_exploits.demo` | Demo async plugin |
22
+ | Stream Data | `iotsploit_exploits.demo` | Demo streaming plugin |
23
+
24
+ ## System Tool Dependencies
25
+
26
+ Some plugins require system-level tools (not installable via pip):
27
+
28
+ - **nmap_scan**: Requires `nmap` (`apt install nmap`)
29
+ - **hydra_ssh_attack**: Requires `hydra` (`apt install hydra`)
30
+ - **adb_check**: Requires `adb` (`apt install android-tools-adb`)
31
+
32
+ ## Installation
33
+
34
+ ```bash
35
+ pip install iotsploit-exploits
36
+ ```
37
+
38
+ For development:
39
+
40
+ ```bash
41
+ pip install -e ./iotsploit-exploits
42
+ ```
@@ -0,0 +1,43 @@
1
+ [tool.poetry]
2
+ name = "iotsploit-exploits"
3
+ version = "0.0.6"
4
+ description = "Official IoTSploit exploit plugin package"
5
+ authors = ["IoTSploit Team <support@iotsploit.org>"]
6
+ license = "GPL-3.0-or-later"
7
+ readme = "README.md"
8
+ packages = [{ include = "iotsploit_exploits", from = "src" }]
9
+ include = [
10
+ { path = "src/iotsploit_exploits/rubber_duck_scripts/*.txt", format = ["sdist", "wheel"] },
11
+ { path = "src/iotsploit_exploits/hydra_cracker/*.txt", format = ["sdist", "wheel"] },
12
+ ]
13
+
14
+ [tool.poetry.dependencies]
15
+ python = ">=3.10,<4.0"
16
+ iotsploit-core = "^0.0.6"
17
+ iotsploit-django = "^0.0.6"
18
+ scapy = ">=2.5"
19
+ pwntools = ">=4.12"
20
+ pyserial = ">=3.5"
21
+ # Bare ">=3.0" also matches legacy FaceDancer 2019.x releases under PEP 440.
22
+ # Keep this on the 3.x line so the exploit imports resolve to the expected API.
23
+ facedancer = ">=3.0.6,<4.0"
24
+
25
+ [tool.poetry.plugins."iotsploit.exploit_plugins"]
26
+ flood_attack = "iotsploit_exploits.flood_attack.flood_attack:FloodAttackPlugin"
27
+ syn_flood_attack = "iotsploit_exploits.flood_attack.syn_flood_attack:SynFloodAttackPlugin"
28
+ wifi_scan = "iotsploit_exploits.wifi_scan.wifi_scan:WifiScanPlugin"
29
+ ip_scan = "iotsploit_exploits.ip_scan.ip_scan:IPScanPlugin"
30
+ nmap_scan = "iotsploit_exploits.nmap_scan.nmap_scan:NmapScanPlugin"
31
+ adb_check = "iotsploit_exploits.adb_check.adb_check:AdbSecurityCheckPlugin"
32
+ hydra_ssh_attack = "iotsploit_exploits.hydra_ssh_attack:HydraSSHAttackPlugin"
33
+ plugin_ssh = "iotsploit_exploits.plugin_ssh:SSHPlugin"
34
+ picocom_serial_reader = "iotsploit_exploits.serial.picocom_serial_reader:PicocomSerialReaderPlugin"
35
+ greatfet_echo = "iotsploit_exploits.greatfet_echo:FTDIEchoPlugin"
36
+ greatfet_rubber_duck = "iotsploit_exploits.greatfet_rubber_duck:RubberDuckPlugin"
37
+ simple_rubber_duck = "iotsploit_exploits.simple_rubber_duck:SimpleRubberDuckPlugin"
38
+ async_sleep_attack = "iotsploit_exploits.demo.async_sleep_attack:AsyncSleepAttackPlugin"
39
+ stream_data_attack = "iotsploit_exploits.demo.stream_data_attack:StreamDataAttackPlugin"
40
+
41
+ [build-system]
42
+ requires = ["poetry-core>=1.8.0"]
43
+ build-backend = "poetry.core.masonry.api"
@@ -0,0 +1 @@
1
+ """Official IoTSploit exploit plugin package."""
@@ -0,0 +1,493 @@
1
+ #!/usr/bin/python3
2
+
3
+ import os
4
+ # Disable pwntools terminal mode before importing to prevent fileno() errors in non-TTY environments
5
+ os.environ.setdefault("PWNLIB_NOTERM", "1")
6
+
7
+ import logging
8
+ import traceback
9
+ import pluggy
10
+ from iotsploit_django.tools.adb_mgr import ADB_Mgr
11
+ from iotsploit_django.tools.doip_mgr import DoIP_Mgr
12
+ from iotsploit_core.core.exploit_spec import ExploitResult
13
+ from iotsploit_core.core.base_plugin import BasePlugin
14
+ from iotsploit_core.utils import iots_logger
15
+ from typing import Optional, List, Dict, Any
16
+
17
+ logger = iots_logger.get_logger("adb_check")
18
+ hookimpl = pluggy.HookimplMarker("exploit_mgr")
19
+
20
+ class AdbSecurityCheckPlugin(BasePlugin):
21
+ def __init__(self):
22
+ super().__init__({
23
+ 'Name': 'ADB Security Check',
24
+ 'Description': 'Performs security checks on an Android device via ADB',
25
+ 'License': 'GPL',
26
+ 'Author': ['iotsploit'],
27
+ 'Parameters': {
28
+ 'device_serial': {
29
+ 'type': 'string',
30
+ 'required': False,
31
+ 'description': 'ADB device serial number (if not using a target)',
32
+ 'default': '2fd1f89'
33
+ },
34
+ 'device_name': {
35
+ 'type': 'string',
36
+ 'required': False,
37
+ 'description': 'Device name from target (e.g., "DHU", "TCAM")',
38
+ 'default': 'Android Debug Bridge'
39
+ },
40
+ 'try_root': {
41
+ 'type': 'bool',
42
+ 'required': False,
43
+ 'description': 'Attempt to gain root access (may fail on production builds)',
44
+ 'default': False
45
+ },
46
+ 'check_suid': {
47
+ 'type': 'bool',
48
+ 'required': False,
49
+ 'description': 'Check for SUID binaries',
50
+ 'default': True
51
+ },
52
+ 'check_writable': {
53
+ 'type': 'bool',
54
+ 'required': False,
55
+ 'description': 'Check for writable directories',
56
+ 'default': True
57
+ },
58
+ 'skip_interactive': {
59
+ 'type': 'bool',
60
+ 'required': False,
61
+ 'description': 'Skip interactive prompts',
62
+ 'default': True
63
+ }
64
+ }
65
+ })
66
+ self._has_root = None
67
+ self._device_connected = False
68
+
69
+ @hookimpl
70
+ def initialize(self, device_plugin=None):
71
+ logger.info("Initializing ADB Security Check Plugin")
72
+
73
+ @hookimpl
74
+ def execute(self, target: Optional[dict] = None, parameters: Optional[dict] = None) -> ExploitResult:
75
+ """Main execution method"""
76
+ logger.info("Executing ADB Security Check Plugin")
77
+
78
+ # Log target information if provided
79
+ if target:
80
+ self._log_target_info(target)
81
+ else:
82
+ logger.info("No target provided, using default parameters")
83
+
84
+ # Get parameters or use defaults
85
+ params = parameters if parameters else {}
86
+ device_serial = params.get('device_serial', '2fd1f89')
87
+ device_name = params.get('device_name', 'Android Debug Bridge')
88
+ try_root = params.get('try_root', False)
89
+ check_suid = params.get('check_suid', True)
90
+ check_writable = params.get('check_writable', True)
91
+ skip_interactive = params.get('skip_interactive', True)
92
+
93
+ # If target is provided, try to get the device from it
94
+ if target:
95
+ target_device_serial = self.get_device_serial_from_target(target, device_name)
96
+ if target_device_serial:
97
+ device_serial = target_device_serial
98
+ logger.info(f"Using device serial '{device_serial}' from target")
99
+ else:
100
+ logger.warning(f"Could not find any ADB device in target, falling back to parameter device_serial: {device_serial}")
101
+ # Don't return error, just use the provided device_serial parameter
102
+
103
+ try:
104
+ # First check if device is available
105
+ is_connected = self.check_device_available(device_serial)
106
+ if not is_connected:
107
+ return ExploitResult(
108
+ False,
109
+ f"ADB device {device_serial} not available",
110
+ {"adb_connection": {"connected": False, "message": "Device not connected"}}
111
+ )
112
+
113
+ self._device_connected = True
114
+
115
+ # Try to determine root status once
116
+ if try_root:
117
+ self._has_root = self.try_get_root_access(device_serial)
118
+ else:
119
+ self._has_root = False
120
+ logger.info("Root access attempt disabled by parameters")
121
+
122
+ # Collect results
123
+ results = {
124
+ "adb_connection": {"connected": True, "message": "ADB device available"},
125
+ "adb_root": {
126
+ "root_access": self._has_root,
127
+ "message": "ADB has root access" if self._has_root else "ADB does not have root access (not attempted or failed)"
128
+ }
129
+ }
130
+
131
+ # Execute checks that don't require interaction
132
+ try:
133
+ results["selinux_status"] = self.check_selinux(device_serial)
134
+ except Exception as e:
135
+ logger.error(f"Error checking SELinux: {str(e)}")
136
+ results["selinux_status"] = {"error": str(e), "message": "Failed to check SELinux status"}
137
+
138
+ # Only try these checks if we have root access or they're specifically requested
139
+ if check_suid:
140
+ if not self._has_root and try_root:
141
+ results["suid_binaries"] = {"error": "No root access", "message": "SUID binaries check requires root access"}
142
+ else:
143
+ try:
144
+ results["suid_binaries"] = self.check_suid_binaries(device_serial)
145
+ except Exception as e:
146
+ logger.error(f"Error checking SUID binaries: {str(e)}")
147
+ results["suid_binaries"] = {"error": str(e), "message": "Failed to check SUID binaries"}
148
+
149
+ if check_writable:
150
+ if not self._has_root and try_root:
151
+ results["writable_dirs"] = {"error": "No root access", "message": "Writable directories check requires root access"}
152
+ else:
153
+ try:
154
+ results["writable_dirs"] = self.check_writable_dirs(device_serial)
155
+ except Exception as e:
156
+ logger.error(f"Error checking writable directories: {str(e)}")
157
+ results["writable_dirs"] = {"error": str(e), "message": "Failed to check writable directories"}
158
+
159
+ return ExploitResult(True, "ADB security checks completed", results)
160
+ except Exception as e:
161
+ logger.error(f"Error during ADB security checks: {str(e)}")
162
+ logger.debug(traceback.format_exc())
163
+ return ExploitResult(False, f"ADB security checks failed: {str(e)}", {})
164
+
165
+ def _log_target_info(self, target: Optional[dict]) -> None:
166
+ """Log comprehensive target information (expects dict from exploit_manager normalization)"""
167
+ if not isinstance(target, dict):
168
+ logger.warning(f"Target is not a dict: {type(target).__name__}")
169
+ return
170
+
171
+ try:
172
+ logger.info("=" * 50)
173
+ logger.info("TARGET INFORMATION")
174
+ logger.info("=" * 50)
175
+
176
+ logger.info(f"Target Type: {target.get('type', 'Unknown')}")
177
+ logger.info(f"Target ID: {target.get('target_id', 'Unknown')}")
178
+ logger.info(f"Target Name: {target.get('name', 'Unknown')}")
179
+ logger.info(f"Target Status: {target.get('status', 'Unknown')}")
180
+
181
+ if target.get('ip_address'):
182
+ logger.info(f"IP Address: {target['ip_address']}")
183
+ if target.get('location'):
184
+ logger.info(f"Location: {target['location']}")
185
+
186
+ # Log properties
187
+ if target.get('properties'):
188
+ logger.info("Target Properties:")
189
+ for key, value in target['properties'].items():
190
+ logger.info(f" {key}: {value}")
191
+
192
+ # Log components from dictionary
193
+ if target.get('components'):
194
+ logger.info(f"Components ({len(target['components'])}):")
195
+ for i, comp in enumerate(target['components']):
196
+ logger.info(f" [{i+1}] {comp.get('name', 'Unknown')} ({comp.get('type', 'Unknown')})")
197
+ logger.info(f" ID: {comp.get('component_id', 'Unknown')}")
198
+ logger.info(f" Status: {comp.get('status', 'Unknown')}")
199
+ if comp.get('adb_serial_id'):
200
+ logger.info(f" ADB Serial: {comp['adb_serial_id']}")
201
+ if comp.get('usb_vendor_id'):
202
+ logger.info(f" USB Vendor ID: {comp['usb_vendor_id']}")
203
+ if comp.get('usb_product_id'):
204
+ logger.info(f" USB Product ID: {comp['usb_product_id']}")
205
+
206
+ # Log interfaces
207
+ if target.get('interfaces'):
208
+ logger.info(f"Interfaces ({len(target['interfaces'])}):")
209
+ for i, intf in enumerate(target['interfaces']):
210
+ logger.info(f" [{i+1}] {intf.get('name', 'Unknown')} ({intf.get('type', 'Unknown')})")
211
+ logger.info(f" ID: {intf.get('interface_id', 'Unknown')}")
212
+ logger.info(f" Status: {intf.get('status', 'Unknown')}")
213
+
214
+ logger.info("=" * 50)
215
+
216
+ except Exception as e:
217
+ logger.error(f"Error logging target info: {str(e)}")
218
+ logger.debug(f"Target object: {target}")
219
+
220
+ def get_device_serial_from_target(self, target: Optional[dict], device_name: str) -> Optional[str]:
221
+ """Extract the ADB device serial from the target dict (normalized by exploit_manager)"""
222
+ if not isinstance(target, dict):
223
+ return None
224
+
225
+ try:
226
+ found_adb_devices: List[Dict[str, Any]] = []
227
+
228
+ # If target has the components key, check them
229
+ if 'components' in target:
230
+ for component in target['components']:
231
+ if component.get('type') == 'adb_device':
232
+ found_adb_devices.append(component)
233
+ # First try exact name match
234
+ if component.get('name') == device_name:
235
+ logger.info(f"Found ADB device '{device_name}' in target with serial: {component.get('adb_serial_id')}")
236
+ return component.get('adb_serial_id')
237
+
238
+ # If no exact match found but we have ADB devices, use the first one
239
+ if found_adb_devices:
240
+ first_device = found_adb_devices[0]
241
+ logger.info(f"Device '{device_name}' not found, using first available ADB device '{first_device.get('name')}' with serial: {first_device.get('adb_serial_id')}")
242
+ return first_device.get('adb_serial_id')
243
+
244
+ # If target itself is an ADB device
245
+ if target.get('type') == 'adb_device' and target.get('name') == device_name:
246
+ logger.info(f"Target itself is the ADB device '{device_name}' with serial: {target.get('adb_serial_id')}")
247
+ return target.get('adb_serial_id')
248
+
249
+ return None
250
+ except Exception as e:
251
+ logger.error(f"Error getting device serial from target: {str(e)}")
252
+ return None
253
+
254
+ def check_device_available(self, device_serial):
255
+ """Simple check if device is in ADB device list"""
256
+ try:
257
+ adb_devices = ADB_Mgr.Instance().list_devices()
258
+ for dev in adb_devices:
259
+ if dev.serial == device_serial:
260
+ logger.info(f"Device {device_serial} found in ADB device list")
261
+ return True
262
+ logger.info(f"Device {device_serial} not found in ADB device list")
263
+ return False
264
+ except Exception as e:
265
+ logger.error(f"Error checking device availability: {str(e)}")
266
+ return False
267
+
268
+ def try_get_root_access(self, device_serial):
269
+ """Try to get root access once and remember the result"""
270
+ try:
271
+ # Try to execute a simple command with root
272
+ logger.info("Checking if device has root access")
273
+ cmd_result = self.execute_shell_command(device_serial, "id", require_root=True)
274
+
275
+ if cmd_result and "uid=0" in cmd_result:
276
+ logger.info("Device has root access")
277
+ return True
278
+ else:
279
+ logger.info("Device does not have root access")
280
+ return False
281
+ except Exception as e:
282
+ logger.error(f"Error checking root access: {str(e)}")
283
+ return False
284
+
285
+ def execute_shell_command(self, device_serial, command, require_root=False):
286
+ """Execute a shell command with better error handling"""
287
+ try:
288
+ # First check if the device is available
289
+ if not self._device_connected:
290
+ is_connected = self.check_device_available(device_serial)
291
+ if not is_connected:
292
+ logger.error(f"Device {device_serial} not available")
293
+ return None
294
+ self._device_connected = True
295
+
296
+ # If root is required but we know we don't have it, don't even try
297
+ if require_root and self._has_root is False:
298
+ logger.debug(f"Root access required for command but device is not rooted")
299
+ return None
300
+
301
+ # Execute the command
302
+ result = ADB_Mgr.Instance().shell_cmd(device_serial, command, require_root)
303
+ return result
304
+ except Exception as e:
305
+ logger.error(f"Error executing shell command '{command}': {str(e)}")
306
+ # If root failed, remember that for future attempts
307
+ if require_root and "Cannot run as root" in str(e):
308
+ self._has_root = False
309
+ return None
310
+
311
+ def check_adb_connection(self, device_serial, should_close=False):
312
+ """Check if ADB is connected and handle enable/disable"""
313
+ isOpenADB = False
314
+ try:
315
+ isOpenADB = ADB_Mgr.Instance().check_connect_status(device_serial)
316
+ except Exception as e:
317
+ logger.error(f"ADB connection check failed: {str(e)}")
318
+ return {"connected": False, "message": f"ADB connection check failed: {str(e)}"}
319
+
320
+ if should_close:
321
+ if isOpenADB:
322
+ try:
323
+ DoIP_Mgr.Instance().closedebug("dhu")
324
+ try:
325
+ isOpenADB = ADB_Mgr.Instance().check_connect_status(device_serial)
326
+ except Exception:
327
+ isOpenADB = False
328
+ return {"connected": not isOpenADB, "message": "ADB connection closed successfully"}
329
+ except Exception as e:
330
+ logger.error(f"Error closing debug mode: {str(e)}")
331
+ return {"connected": isOpenADB, "message": f"Failed to close debug mode: {str(e)}"}
332
+ return {"connected": True, "message": "ADB already disconnected"}
333
+ else:
334
+ return {"connected": isOpenADB, "message": "ADB connection established" if isOpenADB else "Failed to connect to ADB"}
335
+
336
+ def check_selinux(self, device_serial):
337
+ """Check SELinux status"""
338
+ try:
339
+ status = ADB_Mgr.Instance().query_android_selinux_status(device_serial)
340
+ if status is None:
341
+ return {
342
+ "enforcing": False,
343
+ "message": "Unable to determine SELinux status",
344
+ "security_level": "unknown"
345
+ }
346
+ return {
347
+ "enforcing": status,
348
+ "message": "SELinux is enforcing" if status else "SELinux is permissive or disabled",
349
+ "security_level": "high" if status else "low"
350
+ }
351
+ except Exception as e:
352
+ logger.error(f"Error checking SELinux: {str(e)}")
353
+ raise
354
+
355
+ def check_suid_binaries(self, device_serial):
356
+ """Check for SUID binaries"""
357
+ try:
358
+ # Use our execute_shell_command to avoid root attempts if we know they'll fail
359
+ if self._has_root is False:
360
+ # Try without requiring root
361
+ cmd_result = self.execute_shell_command(
362
+ device_serial,
363
+ 'find / -perm -4000 -type f -print 2>/dev/null | wc -l',
364
+ require_root=False
365
+ )
366
+ if cmd_result:
367
+ count = int(cmd_result.strip()) if cmd_result.strip().isdigit() else 0
368
+ return {
369
+ "secure": count == 0,
370
+ "count": count,
371
+ "files": [],
372
+ "message": f"Found {count} SUID binary files (no details available without root)"
373
+ }
374
+
375
+ return {
376
+ "secure": False,
377
+ "count": 0,
378
+ "files": [],
379
+ "message": "Cannot check SUID binaries without root access"
380
+ }
381
+
382
+ # Use the standard method if we have root
383
+ result = ADB_Mgr.Instance().query_files_permission_suid(device_serial)
384
+
385
+ # Handle None result
386
+ if result is None:
387
+ return {
388
+ "secure": False,
389
+ "count": 0,
390
+ "files": [],
391
+ "message": "Failed to check SUID binaries"
392
+ }
393
+
394
+ # Process results for better reporting
395
+ suid_files = []
396
+ if result and isinstance(result, list):
397
+ for item in result:
398
+ suid_files.append({
399
+ "path": item.get("filepath", ""),
400
+ "permissions": item.get("rwx", ""),
401
+ "owner": item.get("owner", ""),
402
+ "group": item.get("group", ""),
403
+ "selinux": item.get("sid", "")
404
+ })
405
+
406
+ return {
407
+ "secure": len(suid_files) == 0,
408
+ "count": len(suid_files),
409
+ "files": suid_files,
410
+ "message": f"Found {len(suid_files)} SUID binary files"
411
+ }
412
+ except Exception as e:
413
+ logger.error(f"Error checking SUID binaries: {str(e)}")
414
+ raise
415
+
416
+ def check_writable_dirs(self, device_serial):
417
+ """Check for world-writable directories"""
418
+ try:
419
+ # Use our execute_shell_command to avoid root attempts if we know they'll fail
420
+ if self._has_root is False:
421
+ # Try without requiring root
422
+ cmd_result = self.execute_shell_command(
423
+ device_serial,
424
+ 'find / -perm -2 -type d -print 2>/dev/null | grep -v "^/proc\|^/sys\|^/dev" | wc -l',
425
+ require_root=False
426
+ )
427
+ if cmd_result:
428
+ count = int(cmd_result.strip()) if cmd_result.strip().isdigit() else 0
429
+ return {
430
+ "secure": count == 0,
431
+ "count": count,
432
+ "directories": [],
433
+ "message": f"Found {count} world-writable directories (no details available without root)"
434
+ }
435
+
436
+ return {
437
+ "secure": False,
438
+ "count": 0,
439
+ "directories": [],
440
+ "message": "Cannot check writable directories without root access"
441
+ }
442
+
443
+ # Use the standard method if we have root
444
+ passselinuxs = ["unlabeled", "vendor_data_file", "vendor_secure_element_vendor_data_file", "device_data_file"]
445
+ dirs = ADB_Mgr.Instance().query_dirs_permission_writable_by_any_user(
446
+ device_serial,
447
+ "/",
448
+ "",
449
+ ["/dev/", "/sys/", "/proc/"],
450
+ passselinuxs
451
+ )
452
+
453
+ # Handle None result
454
+ if dirs is None:
455
+ return {
456
+ "secure": False,
457
+ "count": 0,
458
+ "directories": [],
459
+ "message": "Failed to check writable directories"
460
+ }
461
+
462
+ # Process results for better reporting
463
+ writable_dirs = []
464
+ if dirs and isinstance(dirs, list):
465
+ for item in dirs:
466
+ writable_dirs.append({
467
+ "path": item.get("dirpath", ""),
468
+ "permissions": item.get("rwx", ""),
469
+ "owner": item.get("owner", ""),
470
+ "group": item.get("group", ""),
471
+ "selinux": item.get("sid", "")
472
+ })
473
+
474
+ return {
475
+ "secure": len(writable_dirs) == 0,
476
+ "count": len(writable_dirs),
477
+ "directories": writable_dirs,
478
+ "message": f"Found {len(writable_dirs)} world-writable directories"
479
+ }
480
+ except Exception as e:
481
+ logger.error(f"Error checking writable directories: {str(e)}")
482
+ raise
483
+
484
+ @hookimpl
485
+ def cleanup(self):
486
+ """Cleanup resources"""
487
+ logger.info("Cleaning up ADB Security Check Plugin")
488
+ try:
489
+ # Reset state variables
490
+ self._has_root = None
491
+ self._device_connected = False
492
+ except Exception as e:
493
+ logger.error(f"Error during cleanup: {str(e)}")