dissect.target 3.20.dev4__py3-none-any.whl → 3.20.dev6__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.
- dissect/target/helpers/record.py +7 -2
- dissect/target/loader.py +0 -1
- dissect/target/loaders/mqtt.py +93 -11
- dissect/target/plugins/general/network.py +8 -0
- dissect/target/plugins/os/windows/_os.py +1 -22
- dissect/target/plugins/os/windows/network.py +363 -0
- dissect/target/tools/query.py +0 -3
- dissect/target/tools/utils.py +0 -4
- {dissect.target-3.20.dev4.dist-info → dissect.target-3.20.dev6.dist-info}/METADATA +1 -1
- {dissect.target-3.20.dev4.dist-info → dissect.target-3.20.dev6.dist-info}/RECORD +15 -16
- {dissect.target-3.20.dev4.dist-info → dissect.target-3.20.dev6.dist-info}/WHEEL +1 -1
- dissect/target/helpers/targetd.py +0 -58
- dissect/target/loaders/targetd.py +0 -223
- {dissect.target-3.20.dev4.dist-info → dissect.target-3.20.dev6.dist-info}/COPYRIGHT +0 -0
- {dissect.target-3.20.dev4.dist-info → dissect.target-3.20.dev6.dist-info}/LICENSE +0 -0
- {dissect.target-3.20.dev4.dist-info → dissect.target-3.20.dev6.dist-info}/entry_points.txt +0 -0
- {dissect.target-3.20.dev4.dist-info → dissect.target-3.20.dev6.dist-info}/top_level.txt +0 -0
dissect/target/helpers/record.py
CHANGED
@@ -144,6 +144,7 @@ EmptyRecord = RecordDescriptor(
|
|
144
144
|
)
|
145
145
|
|
146
146
|
COMMON_INTERFACE_ELEMENTS = [
|
147
|
+
("string", "source"),
|
147
148
|
("string", "name"),
|
148
149
|
("string", "type"),
|
149
150
|
("boolean", "enabled"),
|
@@ -151,7 +152,6 @@ COMMON_INTERFACE_ELEMENTS = [
|
|
151
152
|
("net.ipaddress[]", "dns"),
|
152
153
|
("net.ipaddress[]", "ip"),
|
153
154
|
("net.ipaddress[]", "gateway"),
|
154
|
-
("string", "source"),
|
155
155
|
]
|
156
156
|
|
157
157
|
|
@@ -165,8 +165,13 @@ WindowsInterfaceRecord = TargetRecordDescriptor(
|
|
165
165
|
[
|
166
166
|
*COMMON_INTERFACE_ELEMENTS,
|
167
167
|
("varint", "vlan"),
|
168
|
-
("
|
168
|
+
("net.ipnetwork[]", "network"),
|
169
|
+
("varint", "metric"),
|
170
|
+
("stringlist", "search_domain"),
|
171
|
+
("datetime", "first_connected"),
|
169
172
|
("datetime", "last_connected"),
|
173
|
+
("net.ipaddress[]", "subnetmask"),
|
174
|
+
("boolean", "dhcp"),
|
170
175
|
],
|
171
176
|
)
|
172
177
|
|
dissect/target/loader.py
CHANGED
@@ -177,7 +177,6 @@ def open(item: Union[str, Path], *args, **kwargs) -> Loader:
|
|
177
177
|
register("local", "LocalLoader")
|
178
178
|
register("remote", "RemoteLoader")
|
179
179
|
register("mqtt", "MQTTLoader")
|
180
|
-
register("targetd", "TargetdLoader")
|
181
180
|
register("asdf", "AsdfLoader")
|
182
181
|
register("tar", "TarLoader")
|
183
182
|
register("vmx", "VmxLoader")
|
dissect/target/loaders/mqtt.py
CHANGED
@@ -1,9 +1,11 @@
|
|
1
1
|
from __future__ import annotations
|
2
2
|
|
3
|
+
import argparse
|
3
4
|
import atexit
|
4
5
|
import logging
|
5
6
|
import math
|
6
7
|
import os
|
8
|
+
import re
|
7
9
|
import ssl
|
8
10
|
import sys
|
9
11
|
import time
|
@@ -280,7 +282,7 @@ class Broker:
|
|
280
282
|
factor = 1
|
281
283
|
|
282
284
|
def __init__(
|
283
|
-
self, broker:
|
285
|
+
self, broker: str, port: str, key: str, crt: str, ca: str, case: str, username: str, password: str, **kwargs
|
284
286
|
):
|
285
287
|
self.broker_host = broker
|
286
288
|
self.broker_port = int(port)
|
@@ -352,8 +354,7 @@ class Broker:
|
|
352
354
|
log.error(f"Failed to decode payload for hostname {hostname}: {e}")
|
353
355
|
return
|
354
356
|
|
355
|
-
|
356
|
-
print(f'"{hostname}",{decoded_payload}')
|
357
|
+
print(decoded_payload)
|
357
358
|
|
358
359
|
def _on_log(self, client: mqtt.Client, userdata: Any, log_level: int, message: str) -> None:
|
359
360
|
log.debug(message)
|
@@ -423,16 +424,97 @@ class Broker:
|
|
423
424
|
self.mqtt_client.loop_start()
|
424
425
|
|
425
426
|
|
426
|
-
|
427
|
-
|
428
|
-
|
429
|
-
|
430
|
-
|
431
|
-
|
432
|
-
|
427
|
+
def strictly_positive(value: str) -> int:
|
428
|
+
"""
|
429
|
+
Validates that the provided value is a strictly positive integer.
|
430
|
+
|
431
|
+
This function is intended to be used as a type for argparse arguments.
|
432
|
+
|
433
|
+
Args:
|
434
|
+
value (str): The value to validate.
|
435
|
+
|
436
|
+
Returns:
|
437
|
+
int: The validated integer value.
|
438
|
+
|
439
|
+
Raises:
|
440
|
+
argparse.ArgumentTypeError: If the value is not a strictly positive integer.
|
441
|
+
"""
|
442
|
+
try:
|
443
|
+
strictly_positive_value = int(value)
|
444
|
+
if strictly_positive_value < 1:
|
445
|
+
raise argparse.ArgumentTypeError("Value must be larger than or equal to 1.")
|
446
|
+
return strictly_positive_value
|
447
|
+
except ValueError:
|
448
|
+
raise argparse.ArgumentTypeError(f"Invalid integer value specified: '{value}'")
|
449
|
+
|
450
|
+
|
451
|
+
def port(value: str) -> int:
|
452
|
+
"""
|
453
|
+
Convert a string value to an integer representing a valid port number.
|
454
|
+
|
455
|
+
This function is intended to be used as a type for argparse arguments.
|
456
|
+
|
457
|
+
Args:
|
458
|
+
value (str): The string representation of the port number.
|
459
|
+
Returns:
|
460
|
+
int: The port number as an integer.
|
461
|
+
Raises:
|
462
|
+
argparse.ArgumentTypeError: If the port number is not an integer or out of the valid range (1-65535).
|
463
|
+
"""
|
464
|
+
|
465
|
+
try:
|
466
|
+
port = int(value)
|
467
|
+
if port < 1 or port > 65535:
|
468
|
+
raise argparse.ArgumentTypeError("Port number must be between 1 and 65535.")
|
469
|
+
return port
|
470
|
+
except ValueError:
|
471
|
+
raise argparse.ArgumentTypeError(f"Invalid port number specified: '{value}'")
|
472
|
+
|
473
|
+
|
474
|
+
def case(value: str) -> str:
|
475
|
+
"""
|
476
|
+
Validates that the given value is a valid case name consisting of
|
477
|
+
alphanumeric characters and underscores only.
|
478
|
+
|
479
|
+
This function is intended to be used as a type for argparse arguments.
|
480
|
+
|
481
|
+
Args:
|
482
|
+
value (str): The case name to validate.
|
483
|
+
|
484
|
+
Returns:
|
485
|
+
str: The validated case name if it matches the required pattern.
|
486
|
+
|
487
|
+
Raises:
|
488
|
+
argparse.ArgumentTypeError: If the case name does not match the required pattern.
|
489
|
+
"""
|
490
|
+
|
491
|
+
if re.match(r"^[a-zA-Z0-9_]+$", value):
|
492
|
+
return value
|
493
|
+
|
494
|
+
raise argparse.ArgumentTypeError(f"Invalid case name specified: '{value}'")
|
495
|
+
|
496
|
+
|
497
|
+
@arg(
|
498
|
+
"--mqtt-peers",
|
499
|
+
type=strictly_positive,
|
500
|
+
dest="peers",
|
501
|
+
default=1,
|
502
|
+
help="minimum number of peers to await for first alias",
|
503
|
+
)
|
504
|
+
@arg(
|
505
|
+
"--mqtt-case",
|
506
|
+
type=case,
|
507
|
+
dest="case",
|
508
|
+
help="case name (broker will determine if you are allowed to access this data)",
|
509
|
+
)
|
510
|
+
@arg("--mqtt-port", type=port, dest="port", default=443, help="broker connection port")
|
511
|
+
@arg("--mqtt-broker", default="localhost", dest="broker", help="broker ip-address")
|
512
|
+
@arg("--mqtt-key", type=Path, dest="key", required=True, help="private key file")
|
513
|
+
@arg("--mqtt-crt", type=Path, dest="crt", required=True, help="client certificate file")
|
514
|
+
@arg("--mqtt-ca", type=Path, dest="ca", required=True, help="certificate authority file")
|
433
515
|
@arg("--mqtt-command", dest="command", help="direct command to client(s)")
|
434
516
|
@arg("--mqtt-diag", action="store_true", dest="diag", help="show MQTT diagnostic information")
|
435
|
-
@arg("--mqtt-username", dest="username", help="Username for connection")
|
517
|
+
@arg("--mqtt-username", dest="username", default="mqtt-loader", help="Username for connection")
|
436
518
|
@arg("--mqtt-password", action="store_true", dest="password", help="Ask for password before connecting")
|
437
519
|
class MQTTLoader(Loader):
|
438
520
|
"""Load remote targets through a broker."""
|
@@ -3,6 +3,7 @@ from __future__ import annotations
|
|
3
3
|
from typing import Any, Iterator, Union
|
4
4
|
|
5
5
|
from flow.record.fieldtypes.net import IPAddress, IPNetwork
|
6
|
+
from flow.record.fieldtypes.net.ipv4 import Address, addr_long, addr_str, mask_to_bits
|
6
7
|
|
7
8
|
from dissect.target.helpers.record import (
|
8
9
|
MacInterfaceRecord,
|
@@ -80,3 +81,10 @@ class NetworkPlugin(Plugin):
|
|
80
81
|
for interface in self.interfaces():
|
81
82
|
if any(ip_addr in cidr for ip_addr in interface.ip):
|
82
83
|
yield interface
|
84
|
+
|
85
|
+
def calculate_network(self, ips: int | Address, subnets: int | Address) -> Iterator[str]:
|
86
|
+
for ip, subnet_mask in zip(ips, subnets):
|
87
|
+
subnet_mask_int = addr_long(subnet_mask)
|
88
|
+
cidr = mask_to_bits(subnet_mask_int)
|
89
|
+
network_address = addr_str(addr_long(ip) & subnet_mask_int)
|
90
|
+
yield f"{network_address}/{cidr}"
|
@@ -99,28 +99,7 @@ class WindowsPlugin(OSPlugin):
|
|
99
99
|
|
100
100
|
@export(property=True)
|
101
101
|
def ips(self) -> list[str]:
|
102
|
-
|
103
|
-
fields = ["IPAddress", "DhcpIPAddress"]
|
104
|
-
ips = set()
|
105
|
-
|
106
|
-
for r in self.target.registry.keys(key):
|
107
|
-
for s in r.subkeys():
|
108
|
-
for field in fields:
|
109
|
-
try:
|
110
|
-
ip = s.value(field).value
|
111
|
-
except RegistryValueNotFoundError:
|
112
|
-
continue
|
113
|
-
|
114
|
-
if isinstance(ip, str):
|
115
|
-
ip = [ip]
|
116
|
-
|
117
|
-
for i in ip:
|
118
|
-
if i == "0.0.0.0":
|
119
|
-
continue
|
120
|
-
|
121
|
-
ips.add(i)
|
122
|
-
|
123
|
-
return list(ips)
|
102
|
+
return self.target.network.ips()
|
124
103
|
|
125
104
|
def _get_version_reg_value(self, value_name: str) -> Any:
|
126
105
|
try:
|
@@ -0,0 +1,363 @@
|
|
1
|
+
from __future__ import annotations
|
2
|
+
|
3
|
+
from enum import IntEnum
|
4
|
+
from typing import Iterator
|
5
|
+
|
6
|
+
from dissect.util.ts import wintimestamp
|
7
|
+
|
8
|
+
from dissect.target.exceptions import (
|
9
|
+
RegistryKeyNotFoundError,
|
10
|
+
RegistryValueNotFoundError,
|
11
|
+
)
|
12
|
+
from dissect.target.helpers.record import WindowsInterfaceRecord
|
13
|
+
from dissect.target.helpers.regutil import RegistryKey
|
14
|
+
from dissect.target.plugins.general.network import NetworkPlugin
|
15
|
+
|
16
|
+
|
17
|
+
class IfTypes(IntEnum):
|
18
|
+
OTHER = 1
|
19
|
+
REGULAR_1822 = 2
|
20
|
+
HDH_1822 = 3
|
21
|
+
DDN_X25 = 4
|
22
|
+
RFC877_X25 = 5
|
23
|
+
ETHERNET_CSMACD = 6
|
24
|
+
IS088023_CSMACD = 7
|
25
|
+
ISO88024_TOKENBUS = 8
|
26
|
+
ISO88025_TOKENRING = 9
|
27
|
+
ISO88026_MAN = 10
|
28
|
+
STARLAN = 11
|
29
|
+
PROTEON_10MBIT = 12
|
30
|
+
PROTEON_80MBIT = 13
|
31
|
+
HYPERCHANNEL = 14
|
32
|
+
FDDI = 15
|
33
|
+
LAP_B = 16
|
34
|
+
SDLC = 17
|
35
|
+
DS1 = 18
|
36
|
+
E1 = 19
|
37
|
+
BASIC_ISDN = 20
|
38
|
+
PRIMARY_ISDN = 21
|
39
|
+
PROP_POINT2POINT_SERIAL = 22
|
40
|
+
PPP = 23
|
41
|
+
SOFTWARE_LOOPBACK = 24
|
42
|
+
EON = 25
|
43
|
+
ETHERNET_3MBIT = 26
|
44
|
+
NSIP = 27
|
45
|
+
SLIP = 28
|
46
|
+
ULTRA = 29
|
47
|
+
DS3 = 30
|
48
|
+
SIP = 31
|
49
|
+
FRAMERELAY = 32
|
50
|
+
RS232 = 33
|
51
|
+
PARA = 34
|
52
|
+
ARCNET = 35
|
53
|
+
ARCNET_PLUS = 36
|
54
|
+
ATM = 37
|
55
|
+
MIO_X25 = 38
|
56
|
+
SONET = 39
|
57
|
+
X25_PLE = 40
|
58
|
+
ISO88022_LLC = 41
|
59
|
+
LOCALTALK = 42
|
60
|
+
SMDS_DXI = 43
|
61
|
+
FRAMERELAY_SERVICE = 44
|
62
|
+
V35 = 45
|
63
|
+
HSSI = 46
|
64
|
+
HIPPI = 47
|
65
|
+
MODEM = 48
|
66
|
+
AAL5 = 49
|
67
|
+
SONET_PATH = 50
|
68
|
+
SONET_VT = 51
|
69
|
+
SMDS_ICIP = 52
|
70
|
+
PROP_VIRTUAL = 53
|
71
|
+
PROP_MULTIPLEXOR = 54
|
72
|
+
IEEE80212 = 55
|
73
|
+
FIBRECHANNEL = 56
|
74
|
+
HIPPIINTERFACE = 57
|
75
|
+
FRAMERELAY_INTERCONNECT = 58
|
76
|
+
AFLANE_8023 = 59
|
77
|
+
AFLANE_8025 = 60
|
78
|
+
CCTEMUL = 61
|
79
|
+
FASTETHER = 62
|
80
|
+
ISDN = 63
|
81
|
+
V11 = 64
|
82
|
+
V36 = 65
|
83
|
+
G703_64K = 66
|
84
|
+
G703_2MB = 67
|
85
|
+
QLLC = 68
|
86
|
+
FASTETHER_FX = 69
|
87
|
+
CHANNEL = 70
|
88
|
+
IEEE80211 = 71
|
89
|
+
IBM370PARCHAN = 72
|
90
|
+
ESCON = 73
|
91
|
+
DLSW = 74
|
92
|
+
ISDN_S = 75
|
93
|
+
ISDN_U = 76
|
94
|
+
LAP_D = 77
|
95
|
+
IPSWITCH = 78
|
96
|
+
RSRB = 79
|
97
|
+
ATM_LOGICAL = 80
|
98
|
+
DS0 = 81
|
99
|
+
DS0_BUNDLE = 82
|
100
|
+
BSC = 83
|
101
|
+
ASYNC = 84
|
102
|
+
CNR = 85
|
103
|
+
ISO88025R_DTR = 86
|
104
|
+
EPLRS = 87
|
105
|
+
ARAP = 88
|
106
|
+
PROP_CNLS = 89
|
107
|
+
HOSTPAD = 90
|
108
|
+
TERMPAD = 91
|
109
|
+
FRAMERELAY_MPI = 92
|
110
|
+
X213 = 93
|
111
|
+
ADSL = 94
|
112
|
+
RADSL = 95
|
113
|
+
SDSL = 96
|
114
|
+
VDSL = 97
|
115
|
+
ISO88025_CRFPRINT = 98
|
116
|
+
MYRINET = 99
|
117
|
+
VOICE_EM = 100
|
118
|
+
VOICE_FXO = 101
|
119
|
+
VOICE_FXS = 102
|
120
|
+
VOICE_ENCAP = 103
|
121
|
+
VOICE_OVERIP = 104
|
122
|
+
ATM_DXI = 105
|
123
|
+
ATM_FUNI = 106
|
124
|
+
ATM_IMA = 107
|
125
|
+
PPPMULTILINKBUNDLE = 108
|
126
|
+
IPOVER_CDLC = 109
|
127
|
+
IPOVER_CLAW = 110
|
128
|
+
STACKTOSTACK = 111
|
129
|
+
VIRTUALIPADDRESS = 112
|
130
|
+
MPC = 113
|
131
|
+
IPOVER_ATM = 114
|
132
|
+
ISO88025_FIBER = 115
|
133
|
+
TDLC = 116
|
134
|
+
GIGABITETHERNET = 117
|
135
|
+
HDLC = 118
|
136
|
+
LAP_F = 119
|
137
|
+
V37 = 120
|
138
|
+
X25_MLP = 121
|
139
|
+
X25_HUNTGROUP = 122
|
140
|
+
TRANSPHDLC = 123
|
141
|
+
INTERLEAVE = 124
|
142
|
+
FAST = 125
|
143
|
+
IP = 126
|
144
|
+
DOCSCABLE_MACLAYER = 127
|
145
|
+
DOCSCABLE_DOWNSTREAM = 128
|
146
|
+
DOCSCABLE_UPSTREAM = 129
|
147
|
+
A12MPPSWITCH = 130
|
148
|
+
TUNNEL = 131
|
149
|
+
COFFEE = 132
|
150
|
+
CES = 133
|
151
|
+
ATM_SUBINTERFACE = 134
|
152
|
+
L2_VLAN = 135
|
153
|
+
L3_IPVLAN = 136
|
154
|
+
L3_IPXVLAN = 137
|
155
|
+
DIGITALPOWERLINE = 138
|
156
|
+
MEDIAMAILOVERIP = 139
|
157
|
+
DTM = 140
|
158
|
+
DCN = 141
|
159
|
+
IPFORWARD = 142
|
160
|
+
MSDSL = 143
|
161
|
+
IEEE1394 = 144
|
162
|
+
IF_GSN = 145
|
163
|
+
DVBRCC_MACLAYER = 146
|
164
|
+
DVBRCC_DOWNSTREAM = 147
|
165
|
+
DVBRCC_UPSTREAM = 148
|
166
|
+
ATM_VIRTUAL = 149
|
167
|
+
MPLS_TUNNEL = 150
|
168
|
+
SRP = 151
|
169
|
+
VOICEOVERATM = 152
|
170
|
+
VOICEOVERFRAMERELAY = 153
|
171
|
+
IDSL = 154
|
172
|
+
COMPOSITELINK = 155
|
173
|
+
SS7_SIGLINK = 156
|
174
|
+
PROP_WIRELESS_P2P = 157
|
175
|
+
FR_FORWARD = 158
|
176
|
+
RFC1483 = 159
|
177
|
+
USB = 160
|
178
|
+
IEEE8023AD_LAG = 161
|
179
|
+
BGP_POLICY_ACCOUNTING = 162
|
180
|
+
FRF16_MFR_BUNDLE = 163
|
181
|
+
H323_GATEKEEPER = 164
|
182
|
+
H323_PROXY = 165
|
183
|
+
MPLS = 166
|
184
|
+
MF_SIGLINK = 167
|
185
|
+
HDSL2 = 168
|
186
|
+
SHDSL = 169
|
187
|
+
DS1_FDL = 170
|
188
|
+
POS = 171
|
189
|
+
DVB_ASI_IN = 172
|
190
|
+
DVB_ASI_OUT = 173
|
191
|
+
PLC = 174
|
192
|
+
NFAS = 175
|
193
|
+
TR008 = 176
|
194
|
+
GR303_RDT = 177
|
195
|
+
GR303_IDT = 178
|
196
|
+
ISUP = 179
|
197
|
+
PROP_DOCS_WIRELESS_MACLAYER = 180
|
198
|
+
PROP_DOCS_WIRELESS_DOWNSTREAM = 181
|
199
|
+
PROP_DOCS_WIRELESS_UPSTREAM = 182
|
200
|
+
HIPERLAN2 = 183
|
201
|
+
PROP_BWA_P2MP = 184
|
202
|
+
SONET_OVERHEAD_CHANNEL = 185
|
203
|
+
DIGITAL_WRAPPER_OVERHEAD_CHANNEL = 186
|
204
|
+
AAL2 = 187
|
205
|
+
RADIO_MAC = 188
|
206
|
+
ATM_RADIO = 189
|
207
|
+
IMT = 190
|
208
|
+
MVL = 191
|
209
|
+
REACH_DSL = 192
|
210
|
+
FR_DLCI_ENDPT = 193
|
211
|
+
ATM_VCI_ENDPT = 194
|
212
|
+
OPTICAL_CHANNEL = 195
|
213
|
+
OPTICAL_TRANSPORT = 196
|
214
|
+
WWANPP = 243
|
215
|
+
WWANPP2 = 244
|
216
|
+
|
217
|
+
|
218
|
+
def _try_value(subkey: RegistryKey, value: str) -> str | list | None:
|
219
|
+
try:
|
220
|
+
return subkey.value(value).value
|
221
|
+
except RegistryValueNotFoundError:
|
222
|
+
return None
|
223
|
+
|
224
|
+
|
225
|
+
class WindowsNetworkPlugin(NetworkPlugin):
|
226
|
+
def _interfaces(self) -> Iterator[WindowsInterfaceRecord]:
|
227
|
+
# Get all the network interfaces
|
228
|
+
for keys in self.target.registry.keys(
|
229
|
+
"HKLM\\SYSTEM\\CurrentControlSet\\Control\\Class\\{4d36e972-e325-11ce-bfc1-08002be10318}"
|
230
|
+
):
|
231
|
+
for subkey in keys.subkeys():
|
232
|
+
device_info = {}
|
233
|
+
|
234
|
+
if (net_cfg_instance_id := _try_value(subkey, "NetCfgInstanceId")) is None:
|
235
|
+
# if no NetCfgInstanceId is found, skip this network interface
|
236
|
+
continue
|
237
|
+
|
238
|
+
# Extract the network device configuration for given interface id
|
239
|
+
config = self._extract_network_device_config(net_cfg_instance_id)
|
240
|
+
if config is None or all(not conf for conf in config):
|
241
|
+
# if no configuration is found or all configurations are empty, skip this network interface
|
242
|
+
continue
|
243
|
+
|
244
|
+
# Extract the network device name for given interface id
|
245
|
+
name_key = self.target.registry.key(
|
246
|
+
f"HKLM\\SYSTEM\\CurrentControlSet\\Control\\Network\\"
|
247
|
+
f"{{4D36E972-E325-11CE-BFC1-08002BE10318}}\\{net_cfg_instance_id}\\Connection"
|
248
|
+
)
|
249
|
+
if value_name := _try_value(name_key, "Name"):
|
250
|
+
device_info["name"] = value_name
|
251
|
+
|
252
|
+
# Extract the metric value from the REGISTRY_KEY_INTERFACE key
|
253
|
+
interface_key = self.target.registry.key(
|
254
|
+
f"HKLM\\SYSTEM\\CurrentControlSet\\Services\\Tcpip\\Parameters\\Interfaces\\{net_cfg_instance_id}"
|
255
|
+
)
|
256
|
+
if value_metric := _try_value(interface_key, "InterfaceMetric"):
|
257
|
+
device_info["metric"] = value_metric
|
258
|
+
|
259
|
+
# Extract the rest of the device information
|
260
|
+
device_info["mac"] = _try_value(subkey, "NetworkAddress")
|
261
|
+
device_info["vlan"] = _try_value(subkey, "VlanID")
|
262
|
+
|
263
|
+
if timestamp := _try_value(subkey, "NetworkInterfaceInstallTimestamp"):
|
264
|
+
device_info["first_connected"] = wintimestamp(timestamp)
|
265
|
+
|
266
|
+
if type_device := _try_value(subkey, "*IfType"):
|
267
|
+
device_info["type"] = IfTypes(int(type_device)).name
|
268
|
+
|
269
|
+
# Yield a record for each non-empty configuration
|
270
|
+
for conf in config:
|
271
|
+
if conf:
|
272
|
+
# Create a copy of device_info to avoid overwriting
|
273
|
+
record_info = device_info.copy()
|
274
|
+
record_info.update(conf)
|
275
|
+
yield WindowsInterfaceRecord(
|
276
|
+
**record_info,
|
277
|
+
source=f"HKLM\\SYSTEM\\{subkey.path}",
|
278
|
+
_target=self.target,
|
279
|
+
)
|
280
|
+
|
281
|
+
def _extract_network_device_config(
|
282
|
+
self, interface_id: str
|
283
|
+
) -> list[dict[str, str | list], dict[str, str | list]] | None:
|
284
|
+
dhcp_config = {}
|
285
|
+
static_config = {}
|
286
|
+
|
287
|
+
# Get the registry keys for the given interface id
|
288
|
+
try:
|
289
|
+
keys = self.target.registry.key(
|
290
|
+
f"HKLM\\SYSTEM\\CurrentControlSet\\Services\\Tcpip\\Parameters\\Interfaces\\{interface_id}"
|
291
|
+
)
|
292
|
+
except RegistryKeyNotFoundError:
|
293
|
+
return None
|
294
|
+
|
295
|
+
if not len(keys):
|
296
|
+
return None
|
297
|
+
|
298
|
+
# Extract DHCP configuration from the registry
|
299
|
+
dhcp_gateway = _try_value(keys, "DhcpDefaultGateway")
|
300
|
+
if dhcp_gateway not in ["", "0.0.0.0", None, []]:
|
301
|
+
dhcp_config["gateway"] = dhcp_gateway
|
302
|
+
|
303
|
+
dhcp_ip = _try_value(keys, "DhcpIPAddress")
|
304
|
+
if dhcp_ip not in ["", "0.0.0.0", None]:
|
305
|
+
dhcp_config["ip"] = [dhcp_ip]
|
306
|
+
|
307
|
+
dhcp_dns = _try_value(keys, "DhcpNameServer")
|
308
|
+
if dhcp_dns not in ["", "0.0.0.0", None]:
|
309
|
+
dhcp_config["dns"] = dhcp_dns.split(" ")
|
310
|
+
|
311
|
+
dhcp_subnetmask = _try_value(keys, "DhcpSubnetMask")
|
312
|
+
if dhcp_subnetmask not in ["", "0.0.0.0", None]:
|
313
|
+
dhcp_config["subnetmask"] = [dhcp_subnetmask]
|
314
|
+
|
315
|
+
dhcp_domain = _try_value(keys, "DhcpDomain")
|
316
|
+
if dhcp_domain not in ["", None]:
|
317
|
+
dhcp_config["search_domain"] = [dhcp_domain]
|
318
|
+
|
319
|
+
if len(dhcp_config) > 0:
|
320
|
+
dhcp_enable = _try_value(keys, "EnableDHCP")
|
321
|
+
dhcp_config["enabled"] = dhcp_enable == 1
|
322
|
+
dhcp_config["dhcp"] = True
|
323
|
+
|
324
|
+
# Extract static configuration from the registry
|
325
|
+
static_gateway = _try_value(keys, "DefaultGateway")
|
326
|
+
if static_gateway not in ["", None, []]:
|
327
|
+
static_config["gateway"] = static_gateway
|
328
|
+
|
329
|
+
static_ip = _try_value(keys, "IPAddress")
|
330
|
+
if static_ip not in ["", "0.0.0.0", ["0.0.0.0"], None, []]:
|
331
|
+
static_config["ip"] = static_ip if isinstance(static_ip, list) else [static_ip]
|
332
|
+
|
333
|
+
static_dns = _try_value(keys, "NameServer")
|
334
|
+
if static_dns not in ["", "0.0.0.0", None]:
|
335
|
+
static_config["dns"] = static_dns.split(",")
|
336
|
+
|
337
|
+
static_subnetmask = _try_value(keys, "SubnetMask")
|
338
|
+
if static_subnetmask not in ["", "0.0.0.0", ["0.0.0.0"], None, []]:
|
339
|
+
static_config["subnetmask"] = (
|
340
|
+
static_subnetmask if isinstance(static_subnetmask, list) else [static_subnetmask]
|
341
|
+
)
|
342
|
+
|
343
|
+
static_domain = _try_value(keys, "Domain")
|
344
|
+
if static_domain not in ["", None]:
|
345
|
+
static_config["search_domain"] = [static_domain]
|
346
|
+
|
347
|
+
if len(static_config) > 0:
|
348
|
+
static_config["enabled"] = None
|
349
|
+
static_config["dhcp"] = False
|
350
|
+
|
351
|
+
# Combine ip and subnetmask for extraction
|
352
|
+
combined_configs = [
|
353
|
+
(dhcp_config, dhcp_config.get("ip", []), dhcp_config.get("subnetmask", [])),
|
354
|
+
(static_config, static_config.get("ip", []), static_config.get("subnetmask", [])),
|
355
|
+
]
|
356
|
+
|
357
|
+
# Iterate over combined ip/subnet lists
|
358
|
+
for config, ips, subnet_masks in combined_configs:
|
359
|
+
for network_address in self.calculate_network(ips, subnet_masks):
|
360
|
+
config.setdefault("network", []).append(network_address)
|
361
|
+
|
362
|
+
# Return both configurations
|
363
|
+
return [dhcp_config, static_config]
|
dissect/target/tools/query.py
CHANGED
@@ -18,7 +18,6 @@ from dissect.target.exceptions import (
|
|
18
18
|
UnsupportedPluginError,
|
19
19
|
)
|
20
20
|
from dissect.target.helpers import cache, record_modifier
|
21
|
-
from dissect.target.loaders.targetd import ProxyLoader
|
22
21
|
from dissect.target.plugin import PLUGINS, OSPlugin, Plugin, find_plugin_functions
|
23
22
|
from dissect.target.report import ExecutionReport
|
24
23
|
from dissect.target.tools.utils import (
|
@@ -174,8 +173,6 @@ def main():
|
|
174
173
|
|
175
174
|
if targets:
|
176
175
|
for plugin_target in Target.open_all(targets, args.children):
|
177
|
-
if isinstance(plugin_target._loader, ProxyLoader):
|
178
|
-
parser.error("can't list compatible plugins for remote targets.")
|
179
176
|
funcs, _ = find_plugin_functions(plugin_target, args.list, compatibility=True, show_hidden=True)
|
180
177
|
for func in funcs:
|
181
178
|
collected_plugins[func.path] = func.plugin_desc
|
dissect/target/tools/utils.py
CHANGED
@@ -16,7 +16,6 @@ from dissect.target import Target
|
|
16
16
|
from dissect.target.exceptions import UnsupportedPluginError
|
17
17
|
from dissect.target.helpers import docs, keychain
|
18
18
|
from dissect.target.helpers.docs import get_docstring
|
19
|
-
from dissect.target.helpers.targetd import CommandProxy
|
20
19
|
from dissect.target.loader import LOADERS_BY_SCHEME
|
21
20
|
from dissect.target.plugin import (
|
22
21
|
OSPlugin,
|
@@ -250,9 +249,6 @@ def plugin_function_with_argparser(
|
|
250
249
|
|
251
250
|
plugin_method = plugin_obj.get_all_records
|
252
251
|
parser = generate_argparse_for_plugin(plugin_obj)
|
253
|
-
elif isinstance(target_attr, CommandProxy):
|
254
|
-
plugin_method = target_attr.command()
|
255
|
-
parser = generate_argparse_for_bound_method(plugin_method)
|
256
252
|
elif callable(target_attr):
|
257
253
|
plugin_method = target_attr
|
258
254
|
parser = generate_argparse_for_bound_method(target_attr)
|
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.1
|
2
2
|
Name: dissect.target
|
3
|
-
Version: 3.20.
|
3
|
+
Version: 3.20.dev6
|
4
4
|
Summary: This module ties all other Dissect modules together, it provides a programming API and command line tools which allow easy access to various data sources inside disk images or file collections (a.k.a. targets)
|
5
5
|
Author-email: Dissect Team <dissect@fox-it.com>
|
6
6
|
License: Affero General Public License v3
|
@@ -2,7 +2,7 @@ dissect/target/__init__.py,sha256=Oc7ounTgq2hE4nR6YcNabetc7SQA40ldSa35VEdZcQU,63
|
|
2
2
|
dissect/target/container.py,sha256=0YcwcGmfJjhPXUB6DEcjWEoSuAtTDxMDpoTviMrLsxM,9353
|
3
3
|
dissect/target/exceptions.py,sha256=ULi7NXlqju_d8KENEL3aimmfKTFfbNssfeWhAnOB654,2972
|
4
4
|
dissect/target/filesystem.py,sha256=__p2p72B6mKgIiAOj85EC3ESdZ8gjgkm2pt1KKym3h0,60743
|
5
|
-
dissect/target/loader.py,sha256=
|
5
|
+
dissect/target/loader.py,sha256=11B3W9IpfdTU9GJiKONGffuGeICog7J9ypXZ6mffsBE,7337
|
6
6
|
dissect/target/plugin.py,sha256=xbvmjZMFNwJXZYMBE4KI93-nWL9Zhum8x39ydcO6s2o,50578
|
7
7
|
dissect/target/report.py,sha256=06uiP4MbNI8cWMVrC1SasNS-Yg6ptjVjckwj8Yhe0Js,7958
|
8
8
|
dissect/target/target.py,sha256=m4bAKgPLUJERKgxRZFevKvEBNaz77wIC5mVrDe6eI8o,32438
|
@@ -61,12 +61,11 @@ dissect/target/helpers/mui.py,sha256=i-7XoHbu4WO2fYapK9yGAMW04rFlgRispknc1KQIS5Q
|
|
61
61
|
dissect/target/helpers/network_managers.py,sha256=ByBSe2K3c8hgQC6dokcf-hHdmPcD8PmrOj0xs1C3yhs,25743
|
62
62
|
dissect/target/helpers/polypath.py,sha256=h8p7m_OCNiQljGwoZh5Aflr9H2ot6CZr6WKq1OSw58o,2175
|
63
63
|
dissect/target/helpers/protobuf.py,sha256=b4DsnqrRLrefcDjx7rQno-_LBcwtJXxuKf5RdOegzfE,1537
|
64
|
-
dissect/target/helpers/record.py,sha256=
|
64
|
+
dissect/target/helpers/record.py,sha256=euNDDZi29fo8ENN1gsPycB38OMn35clLM9_K-srZ5E0,5852
|
65
65
|
dissect/target/helpers/record_modifier.py,sha256=O_Jj7zOi891HIyAYjxxe6LFPYETHdMa5lNjo4NA_T_w,3969
|
66
66
|
dissect/target/helpers/regutil.py,sha256=kX-sSZbW8Qkg29Dn_9zYbaQrwLumrr4Y8zJ1EhHXIAM,27337
|
67
67
|
dissect/target/helpers/shell_application_ids.py,sha256=hYxrP-YtHK7ZM0ectJFHfoMB8QUXLbYNKmKXMWLZRlA,38132
|
68
68
|
dissect/target/helpers/shell_folder_ids.py,sha256=Behhb8oh0kMxrEk6YYKYigCDZe8Hw5QS6iK_d2hTs2Y,24978
|
69
|
-
dissect/target/helpers/targetd.py,sha256=ELhUulzQ4OgXgHsWhsLgM14vut8Wm6btr7qTynlwKaE,1812
|
70
69
|
dissect/target/helpers/utils.py,sha256=K3xVq9D0FwIhTBAuiWN8ph7Pq2GABgG3hOz-3AmKuEA,4244
|
71
70
|
dissect/target/helpers/compat/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
72
71
|
dissect/target/helpers/compat/path_310.py,sha256=PsLDIodlp3Hv5u-w7GDl6_LnTtchBYcRjz2MicX1egg,16982
|
@@ -88,7 +87,7 @@ dissect/target/loaders/kape.py,sha256=t5TfrGLqPeIpUUpXzIl6aHsqXMEGDqJ5YwDCs07DiB
|
|
88
87
|
dissect/target/loaders/libvirt.py,sha256=_3EFIytMGbiLMISHx4QXVrDebsRO6J6sMkE3TH68qsg,1374
|
89
88
|
dissect/target/loaders/local.py,sha256=Ul-LCd_fY7SyWOVR6nH-NqbkuNpxoZVmffwrkvQElU8,16453
|
90
89
|
dissect/target/loaders/log.py,sha256=cCkDIRS4aPlX3U-n_jUKaI2FPSV3BDpfqKceaU7rBbo,1507
|
91
|
-
dissect/target/loaders/mqtt.py,sha256=
|
90
|
+
dissect/target/loaders/mqtt.py,sha256=WVNXDCzEluyyUidXr25-H9Ic-c75dZIRdDdL5sa4yVQ,19375
|
92
91
|
dissect/target/loaders/multiraw.py,sha256=4a3ZST0NwjnfPDxHkcEfAcX2ddUlT_C-rcrMHNg1wp4,1046
|
93
92
|
dissect/target/loaders/ova.py,sha256=6h4O-7i87J394C6KgLsPkdXRAKNwtPubzLNS3vBGs7U,744
|
94
93
|
dissect/target/loaders/overlay.py,sha256=tj99HKvNG5_JbGfb1WCv4KNSbXXSnEcPQY5XT-JUxn8,992
|
@@ -104,7 +103,6 @@ dissect/target/loaders/smb.py,sha256=qP8m4Jq7hvAvUCF9jB4yr2Zut7p_R02_vxziNN3R1to
|
|
104
103
|
dissect/target/loaders/tanium.py,sha256=P9euiQzvVaQQtMQlEmNe0V25w1BkQFRZBuS-0-ksHpY,1585
|
105
104
|
dissect/target/loaders/tar.py,sha256=2uF9-mmFbSdLsCkySfT9AkzagQXsTQvDjrdL1UyjFuI,4170
|
106
105
|
dissect/target/loaders/target.py,sha256=MU_HUtg58YdhdZu6ga1sYG7fK61Dn7N0TBkWXDCWwyc,798
|
107
|
-
dissect/target/loaders/targetd.py,sha256=sfbn2_j3il2G-rPywAoNT5YPtD5KmKkmBv1zrPDRs6I,8250
|
108
106
|
dissect/target/loaders/utm.py,sha256=7oHYP_jmr5gcjoyOP1pnh9Rz-IqQirBI6bjSvGwiKao,1053
|
109
107
|
dissect/target/loaders/vb.py,sha256=CdimOMeoJEDq8xYDgtldGSiwhR-dY5uxac1L0sYwAEU,2078
|
110
108
|
dissect/target/loaders/vbox.py,sha256=8JD7D8iAY9JRvTHsrosp5ZMsZezuLhZ10Zt8sEL7KBI,732
|
@@ -184,7 +182,7 @@ dissect/target/plugins/general/config.py,sha256=Mdy9uhWn4OJ96zfXpLgjVifV5SrViqHn
|
|
184
182
|
dissect/target/plugins/general/default.py,sha256=8W_9JV3jKEeETlyTrB25sACoIIFmmO8wlVU5Zoi51W0,1425
|
185
183
|
dissect/target/plugins/general/example.py,sha256=6B_YOqajRBLNWBEOfIL_HnLaEANBF8KKoc0mweihiug,6034
|
186
184
|
dissect/target/plugins/general/loaders.py,sha256=6iUxhlSAgo7qSE8_XFxgiihK8sdMiP-s4k0W5Iv8m9k,879
|
187
|
-
dissect/target/plugins/general/network.py,sha256=
|
185
|
+
dissect/target/plugins/general/network.py,sha256=1dCWiVIaVPySquRs3YEsP7PxXXU5voa8CsxyIa7Vh54,2882
|
188
186
|
dissect/target/plugins/general/osinfo.py,sha256=RdK5mw3-H9H3sGXz8yP8U_p3wUG1Ww7_HBKZpFdsbTE,1358
|
189
187
|
dissect/target/plugins/general/plugins.py,sha256=4URjS6DN1Ey6Cqlbyx6NfFGgQZpWDrqxl8KLcZFODGE,4479
|
190
188
|
dissect/target/plugins/general/scrape.py,sha256=Fz7BNXflvuxlnVulyyDhLpyU8D_hJdH6vWVtER9vjTg,6651
|
@@ -262,7 +260,7 @@ dissect/target/plugins/os/unix/log/lastlog.py,sha256=Wq89wRSFZSBsoKVCxjDofnC4yw9
|
|
262
260
|
dissect/target/plugins/os/unix/log/messages.py,sha256=CXA-SkMPLaCgnTQg9nzII-7tO8Il_ENQmuYvDxo33rI,4698
|
263
261
|
dissect/target/plugins/os/unix/log/utmp.py,sha256=1nPHIaBUHt_9z6PDrvyqg4huKLihUaWLrMmgMsbaeIo,7755
|
264
262
|
dissect/target/plugins/os/windows/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
265
|
-
dissect/target/plugins/os/windows/_os.py,sha256
|
263
|
+
dissect/target/plugins/os/windows/_os.py,sha256=-x5TD5BvFw-7zEfqT6WG7n04YSeyr7wVLO07y6xkBP8,12476
|
266
264
|
dissect/target/plugins/os/windows/activitiescache.py,sha256=Q2aILnhJ2rp2AwEbWwyBuSLjMbGqaYJTsavSbfkcFKE,6741
|
267
265
|
dissect/target/plugins/os/windows/adpolicy.py,sha256=qjv0s-gAIGKCznWdVOARJbLXnCKYgvzoFNWoXnq3m1M,7102
|
268
266
|
dissect/target/plugins/os/windows/amcache.py,sha256=ZZNOs3bILTf0AGkDkhoatndl0j39DXkstN7oOyxJECU,27188
|
@@ -276,6 +274,7 @@ dissect/target/plugins/os/windows/generic.py,sha256=BSvDPfB9faU0uquMj0guw5tnR_97
|
|
276
274
|
dissect/target/plugins/os/windows/jumplist.py,sha256=3gZk6O1B3lKK2Jxe0B-HapOCEehk94CYNvCVDpQC9nQ,11773
|
277
275
|
dissect/target/plugins/os/windows/lnk.py,sha256=toEZV00CESLUsF7UmN65-ivWk0Ijg-ZPST0qyD-antY,7860
|
278
276
|
dissect/target/plugins/os/windows/locale.py,sha256=yXVdclpUqss9h8Nq7N4kg3OHwWGDfjdfiLiUZR3wqv8,2324
|
277
|
+
dissect/target/plugins/os/windows/network.py,sha256=nKNgCqVjzjPwkwyXIIgIIECO2UEYnzvM0PzqRVtCGls,10788
|
279
278
|
dissect/target/plugins/os/windows/notifications.py,sha256=T1CIvQgpW__qDR0Rq5zpeWmRWwjNDpvdMnvJJ_6tZXs,17378
|
280
279
|
dissect/target/plugins/os/windows/prefetch.py,sha256=v4OgSKMwcihz0SOuA0o0Ec8wsAKuiuEmJolqZmHFgJA,10491
|
281
280
|
dissect/target/plugins/os/windows/recyclebin.py,sha256=zx58hDCvcrD_eJl9nJmr_i80krSN03ya8nQzWFr2Tw0,4917
|
@@ -347,10 +346,10 @@ dissect/target/tools/fsutils.py,sha256=dyAdp2fzydcozaIZ1mFTpdUeVcibYNJCHN8AFw5Fo
|
|
347
346
|
dissect/target/tools/info.py,sha256=8nnbqFUYeo4NLPE7ORcTBcDL-TioGB2Nqc1TKcu5qdY,5715
|
348
347
|
dissect/target/tools/logging.py,sha256=5ZnumtMWLyslxfrUGZ4ntRyf3obOOhmn8SBjKfdLcEg,4174
|
349
348
|
dissect/target/tools/mount.py,sha256=8GRYnu4xEmFBHxuIZAYhOMyyTGX8fat1Ou07DNiUnW4,3945
|
350
|
-
dissect/target/tools/query.py,sha256=
|
349
|
+
dissect/target/tools/query.py,sha256=e-yAN9zdQjuOiTuoOQoo17mVEQGGcOgaA9YkF4GYpkM,15394
|
351
350
|
dissect/target/tools/reg.py,sha256=FDsiBBDxjWVUBTRj8xn82vZe-J_d9piM-TKS3PHZCcM,3193
|
352
351
|
dissect/target/tools/shell.py,sha256=dmshIriwdd_UwrdUcTfWkcYD8Z0mjzbDqwyZG-snDdM,50482
|
353
|
-
dissect/target/tools/utils.py,sha256=
|
352
|
+
dissect/target/tools/utils.py,sha256=cGk_DxEqVL0ofxlIC15GD4w3PV5RSE_IaKvVkAxEhR8,11974
|
354
353
|
dissect/target/tools/yara.py,sha256=70k-2VMulf1EdkX03nCACzejaOEcsFHOyX-4E40MdQU,2044
|
355
354
|
dissect/target/tools/dump/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
356
355
|
dissect/target/tools/dump/run.py,sha256=aD84peRS4zHqC78fH7Vd4ni3m1ZmVP70LyMwBRvoDGY,9463
|
@@ -364,10 +363,10 @@ dissect/target/volumes/luks.py,sha256=OmCMsw6rCUXG1_plnLVLTpsvE1n_6WtoRUGQbpmu1z
|
|
364
363
|
dissect/target/volumes/lvm.py,sha256=wwQVR9I3G9YzmY6UxFsH2Y4MXGBcKL9aayWGCDTiWMU,2269
|
365
364
|
dissect/target/volumes/md.py,sha256=7ShPtusuLGaIv27SvEETtgsuoQyAa4iAAeOR1NEaajI,1689
|
366
365
|
dissect/target/volumes/vmfs.py,sha256=-LoUbn9WNwTtLi_4K34uV_-wDw2W5hgaqxZNj4UmqAQ,1730
|
367
|
-
dissect.target-3.20.
|
368
|
-
dissect.target-3.20.
|
369
|
-
dissect.target-3.20.
|
370
|
-
dissect.target-3.20.
|
371
|
-
dissect.target-3.20.
|
372
|
-
dissect.target-3.20.
|
373
|
-
dissect.target-3.20.
|
366
|
+
dissect.target-3.20.dev6.dist-info/COPYRIGHT,sha256=m-9ih2RVhMiXHI2bf_oNSSgHgkeIvaYRVfKTwFbnJPA,301
|
367
|
+
dissect.target-3.20.dev6.dist-info/LICENSE,sha256=DZak_2itbUtvHzD3E7GNUYSRK6jdOJ-GqncQ2weavLA,34523
|
368
|
+
dissect.target-3.20.dev6.dist-info/METADATA,sha256=fpn-GHvIaUQ8kWFx0kTIwF9A0-q43fdFymwUWjX-AXQ,12896
|
369
|
+
dissect.target-3.20.dev6.dist-info/WHEEL,sha256=GV9aMThwP_4oNCtvEC2ec3qUYutgWeAzklro_0m4WJQ,91
|
370
|
+
dissect.target-3.20.dev6.dist-info/entry_points.txt,sha256=BWuxAb_6AvUAQpIQOQU0IMTlaF6TDht2AIZK8bHd-zE,492
|
371
|
+
dissect.target-3.20.dev6.dist-info/top_level.txt,sha256=Mn-CQzEYsAbkxrUI0TnplHuXnGVKzxpDw_po_sXpvv4,8
|
372
|
+
dissect.target-3.20.dev6.dist-info/RECORD,,
|
@@ -1,58 +0,0 @@
|
|
1
|
-
from __future__ import annotations
|
2
|
-
|
3
|
-
import functools
|
4
|
-
from typing import Any, Callable, Optional, Union
|
5
|
-
|
6
|
-
from dissect.util.stream import AlignedStream
|
7
|
-
|
8
|
-
from dissect.target.exceptions import FatalError
|
9
|
-
from dissect.target.loader import Loader
|
10
|
-
|
11
|
-
|
12
|
-
class TargetdStream(AlignedStream):
|
13
|
-
def _read(self, offset: int, length: int) -> bytes:
|
14
|
-
return b""
|
15
|
-
|
16
|
-
|
17
|
-
# Marker interface to indicate this loader loads targets from remote machines
|
18
|
-
class ProxyLoader(Loader):
|
19
|
-
pass
|
20
|
-
|
21
|
-
|
22
|
-
class TargetdInvalidStateError(FatalError):
|
23
|
-
pass
|
24
|
-
|
25
|
-
|
26
|
-
class CommandProxy:
|
27
|
-
def __init__(self, loader: Loader, func: Callable, namespace: Optional[str] = None):
|
28
|
-
self._loader = loader
|
29
|
-
self._func = func
|
30
|
-
self._namespace = namespace or func
|
31
|
-
|
32
|
-
def __getattr__(self, func: Union[Callable, str]) -> CommandProxy:
|
33
|
-
if func == "func":
|
34
|
-
return self._func.func
|
35
|
-
self._func = func
|
36
|
-
return self
|
37
|
-
|
38
|
-
def command(self) -> Callable:
|
39
|
-
namespace = None if self._func == self._namespace else self._namespace
|
40
|
-
return self._get(namespace, self._func)
|
41
|
-
|
42
|
-
def _get(self, namespace: Optional[str], plugin_func: Callable) -> Callable:
|
43
|
-
if namespace:
|
44
|
-
func = functools.update_wrapper(
|
45
|
-
functools.partial(self._loader.plugin_bridge, plugin_func=self._func, namespace=self._namespace),
|
46
|
-
self._loader.plugin_bridge,
|
47
|
-
)
|
48
|
-
else:
|
49
|
-
func = functools.update_wrapper(
|
50
|
-
functools.partial(self._loader.plugin_bridge, plugin_func=plugin_func), self._loader.plugin_bridge
|
51
|
-
)
|
52
|
-
return func
|
53
|
-
|
54
|
-
def __call__(self, *args, **kwargs) -> Any:
|
55
|
-
return self.command()(*args, **kwargs)
|
56
|
-
|
57
|
-
def __repr__(self) -> str:
|
58
|
-
return str(self._get(None, "get")(**{"property_name": self._func}))
|
@@ -1,223 +0,0 @@
|
|
1
|
-
from __future__ import annotations
|
2
|
-
|
3
|
-
import functools
|
4
|
-
import ssl
|
5
|
-
import time
|
6
|
-
import urllib
|
7
|
-
from pathlib import Path
|
8
|
-
from platform import os
|
9
|
-
from typing import Any, Callable, Optional, Union
|
10
|
-
|
11
|
-
from dissect.target.containers.raw import RawContainer
|
12
|
-
from dissect.target.exceptions import LoaderError
|
13
|
-
|
14
|
-
# to avoid loading issues during automatic loader detection
|
15
|
-
from dissect.target.helpers.targetd import (
|
16
|
-
CommandProxy,
|
17
|
-
ProxyLoader,
|
18
|
-
TargetdInvalidStateError,
|
19
|
-
TargetdStream,
|
20
|
-
)
|
21
|
-
from dissect.target.loader import Loader
|
22
|
-
from dissect.target.plugin import Plugin, export
|
23
|
-
from dissect.target.target import Target
|
24
|
-
|
25
|
-
TARGETD_AVAILABLE = False
|
26
|
-
try:
|
27
|
-
from flow import remoting
|
28
|
-
from targetd.clients import Client
|
29
|
-
|
30
|
-
TARGETD_AVAILABLE = True
|
31
|
-
except Exception:
|
32
|
-
pass
|
33
|
-
|
34
|
-
|
35
|
-
class TargetdLoader(ProxyLoader):
|
36
|
-
"""Load remote targets through a broker."""
|
37
|
-
|
38
|
-
instance = None
|
39
|
-
|
40
|
-
def __init__(self, path: Union[Path, str], **kwargs):
|
41
|
-
super().__init__(path)
|
42
|
-
self._plugin_func = None
|
43
|
-
self.client = None
|
44
|
-
self.output = None
|
45
|
-
self.peers = 1
|
46
|
-
self.cacert = None
|
47
|
-
self.adapter = "Flow.Remoting"
|
48
|
-
self.host = "localhost"
|
49
|
-
self.port = 1883
|
50
|
-
self.chunking = True
|
51
|
-
self.local_link = "unix:///tmp/targetd" if os.name == "posix" else "pipe:///tmp/targetd"
|
52
|
-
# @todo Add these options to loader help (when available)
|
53
|
-
self.configurables = [
|
54
|
-
["cacert", Path, "SSL: cacert file"],
|
55
|
-
["host", str, "IP-address of targetd broker"],
|
56
|
-
["port", int, "Port for connecting to targetd broker"],
|
57
|
-
["local_link", str, "Domain socket or named pipe"],
|
58
|
-
["adapter", str, "Adapter to use"],
|
59
|
-
["peers", int, "Minimum number of hosts to wait for before executing query"],
|
60
|
-
["chunking", int, "Chunk mode"],
|
61
|
-
]
|
62
|
-
uri = kwargs.get("parsed_path")
|
63
|
-
if uri is None:
|
64
|
-
raise LoaderError("No URI connection details have been passed.")
|
65
|
-
self.uri = uri.path
|
66
|
-
self.options = dict(urllib.parse.parse_qsl(uri.query, keep_blank_values=True))
|
67
|
-
|
68
|
-
def _process_options(self, target: Target) -> None:
|
69
|
-
for configurable_details in self.configurables:
|
70
|
-
configurable, value_type, description = configurable_details
|
71
|
-
configuration = self.options.get(configurable)
|
72
|
-
if not configuration:
|
73
|
-
default_value = getattr(self, configurable)
|
74
|
-
target.log.warning("%s not configured, using: %s=%s", description, configurable, default_value)
|
75
|
-
else:
|
76
|
-
setattr(self, configurable, value_type(configuration))
|
77
|
-
|
78
|
-
@export(output="record", cache=False)
|
79
|
-
def plugin_bridge(self, *args, **kwargs) -> list[Any]:
|
80
|
-
"""Command Execution Bridge Plugin for Targetd.
|
81
|
-
|
82
|
-
This is a generic plugin interceptor that becomes active only if using
|
83
|
-
the targetd loader. This plugin acts as a bridge to connect to the Targetd broker
|
84
|
-
and will translate the requested plugin-operation into Targetd-commands using the selected
|
85
|
-
adapter (i.e. Flow.Remoting).
|
86
|
-
"""
|
87
|
-
|
88
|
-
if not TARGETD_AVAILABLE:
|
89
|
-
raise ImportError("This loader requires the targetd package to be installed.")
|
90
|
-
|
91
|
-
self.output = None
|
92
|
-
self.has_output = False
|
93
|
-
|
94
|
-
plugin_func = kwargs.pop("plugin_func", None)
|
95
|
-
|
96
|
-
if self.client is None:
|
97
|
-
ssl_context = ssl.SSLContext(ssl.PROTOCOL_TLS_CLIENT)
|
98
|
-
ssl_context.check_hostname = False
|
99
|
-
if self.cacert and not self.cacert.exists():
|
100
|
-
raise LoaderError(f"file not found: {self.cacert}")
|
101
|
-
if self.cacert:
|
102
|
-
ssl_context.load_verify_locations(self.cacert)
|
103
|
-
else:
|
104
|
-
ssl_context.load_default_certs(purpose=ssl.Purpose.SERVER_AUTH)
|
105
|
-
self.client = Client(self.host, self.port, ssl_context, [self.uri], self.local_link, "targetd")
|
106
|
-
self.client.module_fullname = "dissect.target.loaders"
|
107
|
-
self.client.module_fromlist = ["command_runner"]
|
108
|
-
self.client.command = plugin_func
|
109
|
-
try:
|
110
|
-
self.client.start(*args, **kwargs)
|
111
|
-
except Exception:
|
112
|
-
# If something happens that prevents targetd from properly closing/resetting the
|
113
|
-
# connection, this exception is thrown during the next connection and the connection
|
114
|
-
# is closed properly after all so that the next time the loader will be able to
|
115
|
-
# use a new connection with a new session.
|
116
|
-
self.client.close()
|
117
|
-
raise TargetdInvalidStateError("Targetd connection is in invalid state, retry.")
|
118
|
-
else:
|
119
|
-
self.client.command = plugin_func
|
120
|
-
self.client.exec_command(*args, **kwargs)
|
121
|
-
|
122
|
-
while not self.has_output:
|
123
|
-
time.sleep(1)
|
124
|
-
return self.output
|
125
|
-
|
126
|
-
def _get_command(self, func: str, namespace: str = None) -> tuple[Loader, functools.partial]:
|
127
|
-
return (self, CommandProxy(self, func, namespace=namespace))
|
128
|
-
|
129
|
-
def each(self, func: Callable, target: Optional[Target] = None) -> Any:
|
130
|
-
"""Allows you to attach a scriptlet (function) to each of the remote hosts.
|
131
|
-
The results of the function are accumulated using +=. Exceptions are
|
132
|
-
catched and logged as warnings.
|
133
|
-
|
134
|
-
Usage:
|
135
|
-
|
136
|
-
def my_function(remote_target):
|
137
|
-
...do something interesting...
|
138
|
-
|
139
|
-
targets = Target.open( targetd://... )
|
140
|
-
targets.each(my_function)
|
141
|
-
|
142
|
-
"""
|
143
|
-
result = None
|
144
|
-
if not self.client:
|
145
|
-
target.init()
|
146
|
-
for peer in self.client.peers:
|
147
|
-
target.select(peer)
|
148
|
-
try:
|
149
|
-
if result is None:
|
150
|
-
result = func(target)
|
151
|
-
else:
|
152
|
-
result += func(target)
|
153
|
-
except Exception as failure:
|
154
|
-
target.log.warning("Exception while applying function to target: %s: %s", peer, failure)
|
155
|
-
return result
|
156
|
-
|
157
|
-
def _add_plugin(self, plugin: Plugin):
|
158
|
-
plugin.check_compatibe = lambda: True
|
159
|
-
|
160
|
-
def map(self, target: Target) -> None:
|
161
|
-
if TargetdLoader.instance:
|
162
|
-
raise Exception("You can only initiated 1 targetd control connection per session.")
|
163
|
-
self._process_options(target)
|
164
|
-
target.disks.add(RawContainer(TargetdStream()))
|
165
|
-
target.get_function = self._get_command
|
166
|
-
target.add_plugin = self._add_plugin
|
167
|
-
target.each = functools.update_wrapper(functools.partial(self.each, target=target), self.each)
|
168
|
-
TargetdLoader.instance = self
|
169
|
-
|
170
|
-
@staticmethod
|
171
|
-
def detect(path: Path) -> bool:
|
172
|
-
# You can only activate this loader by URI-scheme "targetd://"
|
173
|
-
return False
|
174
|
-
|
175
|
-
def __del__(self) -> None:
|
176
|
-
if self.client:
|
177
|
-
self.client.close()
|
178
|
-
|
179
|
-
|
180
|
-
if TARGETD_AVAILABLE:
|
181
|
-
# Loader has to provide the control script for targetd in this case
|
182
|
-
def command_runner(link: str, targetd: Client, *args, **kwargs) -> None:
|
183
|
-
caller = TargetdLoader.instance
|
184
|
-
if not targetd.rpcs:
|
185
|
-
targetd.easy_connect_remoting(remoting, link, caller.peers)
|
186
|
-
|
187
|
-
obj = targetd.rpcs
|
188
|
-
if namespace := kwargs.get("namespace", None):
|
189
|
-
obj = getattr(obj, namespace)
|
190
|
-
|
191
|
-
caller.has_output = True
|
192
|
-
if targetd.command == "init":
|
193
|
-
caller.output = True
|
194
|
-
return
|
195
|
-
|
196
|
-
if targetd.command == "select":
|
197
|
-
targetd.rpcs.select(*args)
|
198
|
-
caller.output = True
|
199
|
-
return
|
200
|
-
|
201
|
-
if targetd.command == "deselect":
|
202
|
-
targetd.rpcs.deselect()
|
203
|
-
caller.output = True
|
204
|
-
return
|
205
|
-
|
206
|
-
func = getattr(obj, targetd.command)
|
207
|
-
|
208
|
-
result = func(*args, **kwargs)
|
209
|
-
if result is not None:
|
210
|
-
if targetd.command == "get":
|
211
|
-
result = list(result)[0]
|
212
|
-
elif caller.chunking:
|
213
|
-
data = []
|
214
|
-
gen = func()
|
215
|
-
targetd.reset()
|
216
|
-
while True:
|
217
|
-
try:
|
218
|
-
data.append(next(gen))
|
219
|
-
except Exception:
|
220
|
-
break
|
221
|
-
result = data
|
222
|
-
caller.output = result
|
223
|
-
targetd.reset()
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|