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.
- iotsploit_exploits-0.0.6/PKG-INFO +65 -0
- iotsploit_exploits-0.0.6/README.md +42 -0
- iotsploit_exploits-0.0.6/pyproject.toml +43 -0
- iotsploit_exploits-0.0.6/src/iotsploit_exploits/__init__.py +1 -0
- iotsploit_exploits-0.0.6/src/iotsploit_exploits/adb_check/__init__.py +0 -0
- iotsploit_exploits-0.0.6/src/iotsploit_exploits/adb_check/adb_check.py +493 -0
- iotsploit_exploits-0.0.6/src/iotsploit_exploits/demo/__init__.py +0 -0
- iotsploit_exploits-0.0.6/src/iotsploit_exploits/demo/async_sleep_attack.py +106 -0
- iotsploit_exploits-0.0.6/src/iotsploit_exploits/demo/stream_data_attack.py +184 -0
- iotsploit_exploits-0.0.6/src/iotsploit_exploits/flood_attack/__init__.py +0 -0
- iotsploit_exploits-0.0.6/src/iotsploit_exploits/flood_attack/flood_attack.py +129 -0
- iotsploit_exploits-0.0.6/src/iotsploit_exploits/flood_attack/syn_flood_attack.py +233 -0
- iotsploit_exploits-0.0.6/src/iotsploit_exploits/greatfet_echo.py +103 -0
- iotsploit_exploits-0.0.6/src/iotsploit_exploits/greatfet_rubber_duck.py +417 -0
- iotsploit_exploits-0.0.6/src/iotsploit_exploits/hydra_cracker/weak_pass.txt +471 -0
- iotsploit_exploits-0.0.6/src/iotsploit_exploits/hydra_cracker/weak_pass_simple.txt +5 -0
- iotsploit_exploits-0.0.6/src/iotsploit_exploits/hydra_ssh_attack.py +159 -0
- iotsploit_exploits-0.0.6/src/iotsploit_exploits/ip_scan/__init__.py +0 -0
- iotsploit_exploits-0.0.6/src/iotsploit_exploits/ip_scan/ip_scan.py +196 -0
- iotsploit_exploits-0.0.6/src/iotsploit_exploits/nmap_scan/__init__.py +0 -0
- iotsploit_exploits-0.0.6/src/iotsploit_exploits/nmap_scan/nmap_scan.py +207 -0
- iotsploit_exploits-0.0.6/src/iotsploit_exploits/plugin_ssh.py +146 -0
- iotsploit_exploits-0.0.6/src/iotsploit_exploits/rubber_duck_scripts/linux_infogather.txt +126 -0
- iotsploit_exploits-0.0.6/src/iotsploit_exploits/rubber_duck_scripts/windows_payload.txt +93 -0
- iotsploit_exploits-0.0.6/src/iotsploit_exploits/serial/__init__.py +0 -0
- iotsploit_exploits-0.0.6/src/iotsploit_exploits/serial/picocom_serial_reader.py +704 -0
- iotsploit_exploits-0.0.6/src/iotsploit_exploits/simple_rubber_duck.py +183 -0
- iotsploit_exploits-0.0.6/src/iotsploit_exploits/wifi_scan/__init__.py +0 -0
- 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."""
|
|
File without changes
|
|
@@ -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)}")
|
|
File without changes
|