dissect.target 3.17.dev13__py3-none-any.whl → 3.17.dev17__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/container.py +1 -0
- dissect/target/containers/fortifw.py +190 -0
- dissect/target/loader.py +1 -1
- dissect/target/loaders/mqtt.py +15 -31
- dissect/target/plugins/os/unix/linux/sockets.py +2 -2
- dissect/target/target.py +1 -1
- {dissect.target-3.17.dev13.dist-info → dissect.target-3.17.dev17.dist-info}/METADATA +1 -1
- {dissect.target-3.17.dev13.dist-info → dissect.target-3.17.dev17.dist-info}/RECORD +13 -13
- dissect/target/plugins/child/mqtt.py +0 -35
- {dissect.target-3.17.dev13.dist-info → dissect.target-3.17.dev17.dist-info}/COPYRIGHT +0 -0
- {dissect.target-3.17.dev13.dist-info → dissect.target-3.17.dev17.dist-info}/LICENSE +0 -0
- {dissect.target-3.17.dev13.dist-info → dissect.target-3.17.dev17.dist-info}/WHEEL +0 -0
- {dissect.target-3.17.dev13.dist-info → dissect.target-3.17.dev17.dist-info}/entry_points.txt +0 -0
- {dissect.target-3.17.dev13.dist-info → dissect.target-3.17.dev17.dist-info}/top_level.txt +0 -0
dissect/target/container.py
CHANGED
@@ -0,0 +1,190 @@
|
|
1
|
+
from __future__ import annotations
|
2
|
+
|
3
|
+
import gzip
|
4
|
+
import io
|
5
|
+
import logging
|
6
|
+
import zlib
|
7
|
+
from itertools import cycle, islice
|
8
|
+
from pathlib import Path
|
9
|
+
from typing import BinaryIO
|
10
|
+
|
11
|
+
from dissect.util.stream import AlignedStream, RangeStream, RelativeStream
|
12
|
+
|
13
|
+
from dissect.target.container import Container
|
14
|
+
from dissect.target.tools.utils import catch_sigpipe
|
15
|
+
|
16
|
+
log = logging.getLogger(__name__)
|
17
|
+
|
18
|
+
|
19
|
+
def find_xor_key(fh: BinaryIO) -> bytes:
|
20
|
+
"""Find the XOR key for the firmware file by using known plaintext of zeros.
|
21
|
+
|
22
|
+
File-like object ``fh`` must be seeked to the correct offset where it should decode to all zeroes (0x00).
|
23
|
+
|
24
|
+
Arguments:
|
25
|
+
fh: File-like object to read from.
|
26
|
+
|
27
|
+
Returns:
|
28
|
+
bytes: XOR key, note that the XOR key is not validated and may be incorrect.
|
29
|
+
"""
|
30
|
+
key = bytearray()
|
31
|
+
|
32
|
+
pos = fh.tell()
|
33
|
+
buf = fh.read(32)
|
34
|
+
fh.seek(pos)
|
35
|
+
|
36
|
+
if pos % 512 == 0:
|
37
|
+
xor_char = 0xFF
|
38
|
+
else:
|
39
|
+
fh.seek(pos - 1)
|
40
|
+
xor_char = ord(fh.read(1))
|
41
|
+
|
42
|
+
for i, k_char in enumerate(buf):
|
43
|
+
idx = (i + pos) & 0x1F
|
44
|
+
key.append((xor_char ^ k_char ^ idx) & 0xFF)
|
45
|
+
xor_char = k_char
|
46
|
+
|
47
|
+
# align xor key
|
48
|
+
koffset = 32 - (pos & 0x1F)
|
49
|
+
key = islice(cycle(key), koffset, koffset + 32)
|
50
|
+
return bytes(key)
|
51
|
+
|
52
|
+
|
53
|
+
class FortiFirmwareFile(AlignedStream):
|
54
|
+
"""Fortinet firmware file, handles transparant decompression and deobfuscation of the firmware file."""
|
55
|
+
|
56
|
+
def __init__(self, fh: BinaryIO):
|
57
|
+
self.fh = fh
|
58
|
+
self.trailer_offset = None
|
59
|
+
self.trailer_data = None
|
60
|
+
self.xor_key = None
|
61
|
+
self.is_gzipped = False
|
62
|
+
|
63
|
+
size = None
|
64
|
+
|
65
|
+
# Check if the file is gzipped
|
66
|
+
self.fh.seek(0)
|
67
|
+
header = self.fh.read(4)
|
68
|
+
if header.startswith(b"\x1f\x8b"):
|
69
|
+
self.is_gzipped = True
|
70
|
+
|
71
|
+
# Find the extra metadata behind the gzip compressed data
|
72
|
+
# as a bonus we can also calculate the size of the firmware here
|
73
|
+
dec = zlib.decompressobj(wbits=16 + zlib.MAX_WBITS)
|
74
|
+
self.fh.seek(0)
|
75
|
+
size = 0
|
76
|
+
while True:
|
77
|
+
data = self.fh.read(io.DEFAULT_BUFFER_SIZE)
|
78
|
+
if not data:
|
79
|
+
break
|
80
|
+
d = dec.decompress(dec.unconsumed_tail + data)
|
81
|
+
size += len(d)
|
82
|
+
|
83
|
+
# Ignore the trailer data of the gzip file if we have any
|
84
|
+
if dec.unused_data:
|
85
|
+
self.trailer_offset = self.fh.seek(-len(dec.unused_data), io.SEEK_END)
|
86
|
+
self.trailer_data = self.fh.read()
|
87
|
+
log.info("Found trailer offset: %d, data: %r", self.trailer_offset, self.trailer_data)
|
88
|
+
self.fh = RangeStream(self.fh, 0, self.trailer_offset)
|
89
|
+
|
90
|
+
self.fh.seek(0)
|
91
|
+
self.fh = gzip.GzipFile(fileobj=self.fh)
|
92
|
+
|
93
|
+
# Find the xor key based on known offsets where the firmware should decode to zero bytes
|
94
|
+
for zero_offset in (0x30, 0x40, 0x400):
|
95
|
+
self.fh.seek(zero_offset)
|
96
|
+
xor_key = find_xor_key(self.fh)
|
97
|
+
if xor_key.isalnum():
|
98
|
+
self.xor_key = xor_key
|
99
|
+
log.info("Found key %r @ offset %s", self.xor_key, zero_offset)
|
100
|
+
break
|
101
|
+
else:
|
102
|
+
log.info("No xor key found")
|
103
|
+
|
104
|
+
# Determine the size of the firmware file if we didn't calculate it yet
|
105
|
+
if size is None:
|
106
|
+
size = self.fh.seek(0, io.SEEK_END)
|
107
|
+
|
108
|
+
log.info("firmware size: %s", size)
|
109
|
+
log.info("xor key: %r", self.xor_key)
|
110
|
+
log.info("gzipped: %s", self.is_gzipped)
|
111
|
+
self.fh.seek(0)
|
112
|
+
|
113
|
+
# Align the stream to 512 bytes which simplifies the XOR deobfuscation code
|
114
|
+
super().__init__(size=size, align=512)
|
115
|
+
|
116
|
+
def _read(self, offset: int, length: int) -> bytes:
|
117
|
+
self.fh.seek(offset)
|
118
|
+
buf = self.fh.read(length)
|
119
|
+
|
120
|
+
if not self.xor_key:
|
121
|
+
return buf
|
122
|
+
|
123
|
+
buf = bytearray(buf)
|
124
|
+
xor_char = 0xFF
|
125
|
+
for i, cur_char in enumerate(buf):
|
126
|
+
if (i + offset) % 512 == 0:
|
127
|
+
xor_char = 0xFF
|
128
|
+
idx = (i + offset) & 0x1F
|
129
|
+
buf[i] = ((self.xor_key[idx] ^ cur_char ^ xor_char) - idx) & 0xFF
|
130
|
+
xor_char = cur_char
|
131
|
+
|
132
|
+
return bytes(buf)
|
133
|
+
|
134
|
+
|
135
|
+
class FortiFirmwareContainer(Container):
|
136
|
+
__type__ = "fortifw"
|
137
|
+
|
138
|
+
def __init__(self, fh: BinaryIO | Path, *args, **kwargs):
|
139
|
+
if not hasattr(fh, "read"):
|
140
|
+
fh = fh.open("rb")
|
141
|
+
|
142
|
+
# Open the firmware file
|
143
|
+
self.ff = FortiFirmwareFile(fh)
|
144
|
+
|
145
|
+
# seek to MBR
|
146
|
+
self.fw = RelativeStream(self.ff, 0x200)
|
147
|
+
super().__init__(self.fw, self.ff.size, *args, **kwargs)
|
148
|
+
|
149
|
+
@staticmethod
|
150
|
+
def detect_fh(fh: BinaryIO, original: list | BinaryIO) -> bool:
|
151
|
+
return False
|
152
|
+
|
153
|
+
@staticmethod
|
154
|
+
def detect_path(path: Path, original: list | BinaryIO) -> bool:
|
155
|
+
# all Fortinet firmware files end with `-FORTINET.out`
|
156
|
+
return str(path).lower().endswith("-fortinet.out")
|
157
|
+
|
158
|
+
def read(self, length: int) -> bytes:
|
159
|
+
return self.fw.read(length)
|
160
|
+
|
161
|
+
def seek(self, offset: int, whence: int = io.SEEK_SET) -> int:
|
162
|
+
return self.fw.seek(offset, whence)
|
163
|
+
|
164
|
+
def tell(self) -> int:
|
165
|
+
return self.fw.tell()
|
166
|
+
|
167
|
+
def close(self) -> None:
|
168
|
+
pass
|
169
|
+
|
170
|
+
|
171
|
+
@catch_sigpipe
|
172
|
+
def main(argv: list[str] | None = None) -> None:
|
173
|
+
import argparse
|
174
|
+
import shutil
|
175
|
+
import sys
|
176
|
+
|
177
|
+
parser = argparse.ArgumentParser(description="Decompress and deobfuscate Fortinet firmware file to stdout.")
|
178
|
+
parser.add_argument("file", type=argparse.FileType("rb"), help="Fortinet firmware file")
|
179
|
+
parser.add_argument("--verbose", "-v", action="store_true", help="verbose output")
|
180
|
+
args = parser.parse_args(argv)
|
181
|
+
|
182
|
+
if args.verbose:
|
183
|
+
logging.basicConfig(level=logging.INFO)
|
184
|
+
|
185
|
+
ff = FortiFirmwareFile(args.file)
|
186
|
+
shutil.copyfileobj(ff, sys.stdout.buffer)
|
187
|
+
|
188
|
+
|
189
|
+
if __name__ == "__main__":
|
190
|
+
main()
|
dissect/target/loader.py
CHANGED
@@ -77,7 +77,7 @@ class Loader:
|
|
77
77
|
raise NotImplementedError()
|
78
78
|
|
79
79
|
@staticmethod
|
80
|
-
def find_all(path: Path) -> Iterator[Path]:
|
80
|
+
def find_all(path: Path, **kwargs) -> Iterator[Path]:
|
81
81
|
"""Finds all targets to load from ``path``.
|
82
82
|
|
83
83
|
This can be used to open multiple targets from a target path that doesn't necessarily map to files on a disk.
|
dissect/target/loaders/mqtt.py
CHANGED
@@ -6,17 +6,15 @@ import time
|
|
6
6
|
import urllib
|
7
7
|
from dataclasses import dataclass
|
8
8
|
from functools import lru_cache
|
9
|
-
from io import BytesIO
|
10
9
|
from pathlib import Path
|
11
10
|
from struct import pack, unpack_from
|
12
|
-
from typing import Any, Callable, Optional, Union
|
11
|
+
from typing import Any, Callable, Iterator, Optional, Union
|
13
12
|
|
14
13
|
import paho.mqtt.client as mqtt
|
15
14
|
from dissect.util.stream import AlignedStream
|
16
15
|
|
17
16
|
from dissect.target.containers.raw import RawContainer
|
18
17
|
from dissect.target.exceptions import LoaderError
|
19
|
-
from dissect.target.filesystem import VirtualFilesystem
|
20
18
|
from dissect.target.loader import Loader
|
21
19
|
from dissect.target.plugin import arg
|
22
20
|
from dissect.target.target import Target
|
@@ -74,7 +72,7 @@ class MQTTConnection:
|
|
74
72
|
self.info = lru_cache(128)(self.info)
|
75
73
|
self.read = lru_cache(128)(self.read)
|
76
74
|
|
77
|
-
def topo(self, peers: int):
|
75
|
+
def topo(self, peers: int) -> list[str]:
|
78
76
|
self.broker.topology(self.host)
|
79
77
|
|
80
78
|
while len(self.broker.peers(self.host)) < peers:
|
@@ -148,7 +146,7 @@ class Broker:
|
|
148
146
|
def disk(self, host: str) -> DiskMessage:
|
149
147
|
return self.diskinfo[host]
|
150
148
|
|
151
|
-
def peers(self, host: str) ->
|
149
|
+
def peers(self, host: str) -> list[str]:
|
152
150
|
return self.topo[host]
|
153
151
|
|
154
152
|
def _on_disk(self, hostname: str, payload: bytes) -> None:
|
@@ -252,9 +250,6 @@ class Broker:
|
|
252
250
|
class MQTTLoader(Loader):
|
253
251
|
"""Load remote targets through a broker."""
|
254
252
|
|
255
|
-
PATH = "/remote/data/hosts.txt"
|
256
|
-
FOLDER = "/remote/hosts"
|
257
|
-
|
258
253
|
connection = None
|
259
254
|
broker = None
|
260
255
|
peers = []
|
@@ -262,10 +257,15 @@ class MQTTLoader(Loader):
|
|
262
257
|
def __init__(self, path: Union[Path, str], **kwargs):
|
263
258
|
super().__init__(path)
|
264
259
|
cls = MQTTLoader
|
260
|
+
self.broker = cls.broker
|
261
|
+
self.connection = MQTTConnection(self.broker, path)
|
265
262
|
|
266
|
-
|
267
|
-
|
263
|
+
@staticmethod
|
264
|
+
def detect(path: Path) -> bool:
|
265
|
+
return False
|
268
266
|
|
267
|
+
def find_all(path: Path, **kwargs) -> Iterator[str]:
|
268
|
+
cls = MQTTLoader
|
269
269
|
num_peers = 1
|
270
270
|
if cls.broker is None:
|
271
271
|
if (uri := kwargs.get("parsed_path")) is None:
|
@@ -275,26 +275,10 @@ class MQTTLoader(Loader):
|
|
275
275
|
cls.broker.connect()
|
276
276
|
num_peers = int(options.get("peers", 1))
|
277
277
|
|
278
|
-
|
279
|
-
|
280
|
-
|
278
|
+
cls.connection = MQTTConnection(cls.broker, path)
|
279
|
+
cls.peers = cls.connection.topo(num_peers)
|
280
|
+
yield from cls.peers
|
281
281
|
|
282
282
|
def map(self, target: Target) -> None:
|
283
|
-
|
284
|
-
target.
|
285
|
-
for disk in self.connection.info():
|
286
|
-
target.disks.add(RawContainer(disk))
|
287
|
-
else:
|
288
|
-
target.props["mqtt"] = True
|
289
|
-
|
290
|
-
vfs = VirtualFilesystem()
|
291
|
-
vfs.map_file_fh(self.PATH, BytesIO("\n".join(self.peers).encode("utf-8")))
|
292
|
-
for index, peer in enumerate(self.peers):
|
293
|
-
vfs.map_file_fh(f"{self.FOLDER}/host{index}-{peer}", BytesIO(peer.encode("utf-8")))
|
294
|
-
|
295
|
-
target.fs.mount("/data", vfs)
|
296
|
-
target.filesystems.add(vfs)
|
297
|
-
|
298
|
-
@staticmethod
|
299
|
-
def detect(path: Path) -> bool:
|
300
|
-
return str(path).startswith("/remote/hosts/host")
|
283
|
+
for disk in self.connection.info():
|
284
|
+
target.disks.add(RawContainer(disk))
|
@@ -17,9 +17,9 @@ NetSocketRecord = TargetRecordDescriptor(
|
|
17
17
|
("uint32", "rx_queue"),
|
18
18
|
("uint32", "tx_queue"),
|
19
19
|
("string", "local_ip"),
|
20
|
-
("
|
20
|
+
("uint16", "local_port"),
|
21
21
|
("string", "remote_ip"),
|
22
|
-
("
|
22
|
+
("uint16", "remote_port"),
|
23
23
|
("string", "state"),
|
24
24
|
("string", "owner"),
|
25
25
|
("uint32", "inode"),
|
dissect/target/target.py
CHANGED
@@ -280,7 +280,7 @@ class Target:
|
|
280
280
|
continue
|
281
281
|
|
282
282
|
getlogger(entry).debug("Attempting to use loader: %s", loader_cls)
|
283
|
-
for sub_entry in loader_cls.find_all(entry):
|
283
|
+
for sub_entry in loader_cls.find_all(entry, parsed_path=parsed_path):
|
284
284
|
try:
|
285
285
|
ldr = loader_cls(sub_entry, parsed_path=parsed_path)
|
286
286
|
except Exception as e:
|
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.1
|
2
2
|
Name: dissect.target
|
3
|
-
Version: 3.17.
|
3
|
+
Version: 3.17.dev17
|
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
|
@@ -1,15 +1,16 @@
|
|
1
1
|
dissect/target/__init__.py,sha256=Oc7ounTgq2hE4nR6YcNabetc7SQA40ldSa35VEdZcQU,63
|
2
|
-
dissect/target/container.py,sha256=
|
2
|
+
dissect/target/container.py,sha256=0YcwcGmfJjhPXUB6DEcjWEoSuAtTDxMDpoTviMrLsxM,9353
|
3
3
|
dissect/target/exceptions.py,sha256=VVW_Rq_vQinapz-2mbJ3UkxBEZpb2pE_7JlhMukdtrY,2877
|
4
4
|
dissect/target/filesystem.py,sha256=VD1BA6hLqH_FPWFZ-wliEuCxnFrUK61S9VbGK7CtA5w,55597
|
5
|
-
dissect/target/loader.py,sha256=
|
5
|
+
dissect/target/loader.py,sha256=_mrMOzKdpb7nlZJpLENOLuU4Ty92PzJem9GFDuo0PK4,7298
|
6
6
|
dissect/target/plugin.py,sha256=HAN8maaDt-Rlqt8Rr1IW7gXQpzNQZjCVz-i4aSPphSw,48677
|
7
7
|
dissect/target/report.py,sha256=06uiP4MbNI8cWMVrC1SasNS-Yg6ptjVjckwj8Yhe0Js,7958
|
8
|
-
dissect/target/target.py,sha256=
|
8
|
+
dissect/target/target.py,sha256=jq0Ii8073GOfwfqRj7UMuJT5jTVvQ_FD9Vrl9TMGpVc,32180
|
9
9
|
dissect/target/volume.py,sha256=aQZAJiny8jjwkc9UtwIRwy7nINXjCxwpO-_UDfh6-BA,15801
|
10
10
|
dissect/target/containers/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
11
11
|
dissect/target/containers/asdf.py,sha256=DJp0QEFwUjy2MFwKYcYqIR_BS1fQT1Yi9Kcmqt0aChM,1366
|
12
12
|
dissect/target/containers/ewf.py,sha256=FTEPZpogDzymrbAeSnLuHNNStifLzNVhUvtbEMOyo0E,1342
|
13
|
+
dissect/target/containers/fortifw.py,sha256=2Ga89c0qPguHAPigcte8wptgF2aM9qfPTZHddkfQ8J4,5874
|
13
14
|
dissect/target/containers/hdd.py,sha256=Y1qYpk3GePCpq2HZIyqyoGch7nzN8aeI3zWG3UGhf5o,1069
|
14
15
|
dissect/target/containers/hds.py,sha256=xijSUSRM392Ckc9QsOsvjx7PMyeoR4qOlWGG0w4nqUU,1145
|
15
16
|
dissect/target/containers/qcow2.py,sha256=FtXLZA-Xkegbv--dStusQntUiDqM1idSFWMtJRiL7eM,1128
|
@@ -84,7 +85,7 @@ dissect/target/loaders/itunes.py,sha256=69aMTQiiGYpmD_EYSmf9mO1re8C3jAZIEStmwlMx
|
|
84
85
|
dissect/target/loaders/kape.py,sha256=t5TfrGLqPeIpUUpXzIl6aHsqXMEGDqJ5YwDCs07DiBA,1237
|
85
86
|
dissect/target/loaders/local.py,sha256=Ul-LCd_fY7SyWOVR6nH-NqbkuNpxoZVmffwrkvQElU8,16453
|
86
87
|
dissect/target/loaders/log.py,sha256=cCkDIRS4aPlX3U-n_jUKaI2FPSV3BDpfqKceaU7rBbo,1507
|
87
|
-
dissect/target/loaders/mqtt.py,sha256=
|
88
|
+
dissect/target/loaders/mqtt.py,sha256=b0VrQ75_tmc4POkcfnUwKJoj1qmcjm1OKsVBQ9MjgqI,9552
|
88
89
|
dissect/target/loaders/multiraw.py,sha256=4a3ZST0NwjnfPDxHkcEfAcX2ddUlT_C-rcrMHNg1wp4,1046
|
89
90
|
dissect/target/loaders/ova.py,sha256=6h4O-7i87J394C6KgLsPkdXRAKNwtPubzLNS3vBGs7U,744
|
90
91
|
dissect/target/loaders/ovf.py,sha256=ELMq6J2y6cPKbp7pjWAqMMnFYefWxXNqzIiAQdvGGXQ,1061
|
@@ -151,7 +152,6 @@ dissect/target/plugins/apps/webserver/webserver.py,sha256=a7a2lLrhsa9c1AXnwiLP-t
|
|
151
152
|
dissect/target/plugins/child/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
152
153
|
dissect/target/plugins/child/esxi.py,sha256=GfgQzxntcHcyxAE2QjMJ-TrFhklweSXLbYh0uuv-klg,693
|
153
154
|
dissect/target/plugins/child/hyperv.py,sha256=R2qVeu4p_9V53jO-65znN0LwX9v3FVA-9jbbtOQcEz8,2236
|
154
|
-
dissect/target/plugins/child/mqtt.py,sha256=8nLCm71FhQarqPMQ8JL-I-jjxSB9ct2pZGbq4FJO7ao,1189
|
155
155
|
dissect/target/plugins/child/virtuozzo.py,sha256=Mx4ZxEl21g7IYkzraw4FBZup5EfrkFDv4WuTE3hxguw,1206
|
156
156
|
dissect/target/plugins/child/vmware_workstation.py,sha256=8wkA_tSufvBUyp4XQHzRzFETf5ROlyyO_MVS3TExyfw,1570
|
157
157
|
dissect/target/plugins/child/wsl.py,sha256=IssQgYET1T-XR5ZX2lGlNFJ_u_3QECpMF_7kXu09HTE,2469
|
@@ -216,7 +216,7 @@ dissect/target/plugins/os/unix/linux/netstat.py,sha256=MAC4ZdeNqcKpxT2ZMh1-7rjt4
|
|
216
216
|
dissect/target/plugins/os/unix/linux/proc.py,sha256=jm35fAasnNbObN2tpflwQuCfVYLDkTP2EDrzYG42ZSk,23354
|
217
217
|
dissect/target/plugins/os/unix/linux/processes.py,sha256=sTQqZYPW-_gs7Z3f0wwsV6clUX4NK44GGyMiZToBIrg,1936
|
218
218
|
dissect/target/plugins/os/unix/linux/services.py,sha256=-d2y073mOXUM3XCzRgDVCRFR9eTLoVuN8FsZVewHzRg,4075
|
219
|
-
dissect/target/plugins/os/unix/linux/sockets.py,sha256=
|
219
|
+
dissect/target/plugins/os/unix/linux/sockets.py,sha256=11de73KiF2D2s1eyPBA4EWDpNsEzOunbj3YqSlMYZ2Y,9765
|
220
220
|
dissect/target/plugins/os/unix/linux/android/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
221
221
|
dissect/target/plugins/os/unix/linux/android/_os.py,sha256=trmESlpHdwVu7wV18RevEhh_TsVyfKPFCd5Usb5-fSU,2056
|
222
222
|
dissect/target/plugins/os/unix/linux/debian/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
@@ -336,10 +336,10 @@ dissect/target/volumes/luks.py,sha256=OmCMsw6rCUXG1_plnLVLTpsvE1n_6WtoRUGQbpmu1z
|
|
336
336
|
dissect/target/volumes/lvm.py,sha256=wwQVR9I3G9YzmY6UxFsH2Y4MXGBcKL9aayWGCDTiWMU,2269
|
337
337
|
dissect/target/volumes/md.py,sha256=j1K1iKmspl0C_OJFc7-Q1BMWN2OCC5EVANIgVlJ_fIE,1673
|
338
338
|
dissect/target/volumes/vmfs.py,sha256=-LoUbn9WNwTtLi_4K34uV_-wDw2W5hgaqxZNj4UmqAQ,1730
|
339
|
-
dissect.target-3.17.
|
340
|
-
dissect.target-3.17.
|
341
|
-
dissect.target-3.17.
|
342
|
-
dissect.target-3.17.
|
343
|
-
dissect.target-3.17.
|
344
|
-
dissect.target-3.17.
|
345
|
-
dissect.target-3.17.
|
339
|
+
dissect.target-3.17.dev17.dist-info/COPYRIGHT,sha256=m-9ih2RVhMiXHI2bf_oNSSgHgkeIvaYRVfKTwFbnJPA,301
|
340
|
+
dissect.target-3.17.dev17.dist-info/LICENSE,sha256=DZak_2itbUtvHzD3E7GNUYSRK6jdOJ-GqncQ2weavLA,34523
|
341
|
+
dissect.target-3.17.dev17.dist-info/METADATA,sha256=BAALMIovnkuqmnNGuxrQv3CM7ptX_oBXUyLi_LRWAPs,11300
|
342
|
+
dissect.target-3.17.dev17.dist-info/WHEEL,sha256=GJ7t_kWBFywbagK5eo9IoUwLW6oyOeTKmQ-9iHFVNxQ,92
|
343
|
+
dissect.target-3.17.dev17.dist-info/entry_points.txt,sha256=tvFPa-Ap-gakjaPwRc6Fl6mxHzxEZ_arAVU-IUYeo_s,447
|
344
|
+
dissect.target-3.17.dev17.dist-info/top_level.txt,sha256=Mn-CQzEYsAbkxrUI0TnplHuXnGVKzxpDw_po_sXpvv4,8
|
345
|
+
dissect.target-3.17.dev17.dist-info/RECORD,,
|
@@ -1,35 +0,0 @@
|
|
1
|
-
from __future__ import annotations
|
2
|
-
|
3
|
-
from typing import TYPE_CHECKING, Iterator
|
4
|
-
|
5
|
-
from flow.record.fieldtypes import posix_path
|
6
|
-
|
7
|
-
from dissect.target.exceptions import UnsupportedPluginError
|
8
|
-
from dissect.target.helpers.record import ChildTargetRecord
|
9
|
-
from dissect.target.plugin import ChildTargetPlugin
|
10
|
-
|
11
|
-
if TYPE_CHECKING:
|
12
|
-
from dissect.target.target import Target
|
13
|
-
|
14
|
-
|
15
|
-
class MQTT(ChildTargetPlugin):
|
16
|
-
"""Child target plugin that yields from remote broker."""
|
17
|
-
|
18
|
-
__type__ = "mqtt"
|
19
|
-
|
20
|
-
PATH = "/remote/data/hosts.txt"
|
21
|
-
FOLDER = "/remote/hosts"
|
22
|
-
|
23
|
-
def __init__(self, target: Target):
|
24
|
-
super().__init__(target)
|
25
|
-
|
26
|
-
def check_compatible(self) -> None:
|
27
|
-
if not self.target.props.get("mqtt") or not self.target.fs.path(self.PATH).exists():
|
28
|
-
raise UnsupportedPluginError("No remote children.txt file found.")
|
29
|
-
|
30
|
-
def list_children(self) -> Iterator[ChildTargetRecord]:
|
31
|
-
hosts = self.target.fs.path(self.PATH).read_text(encoding="utf-8").split("\n")
|
32
|
-
for index, host in enumerate(hosts):
|
33
|
-
yield ChildTargetRecord(
|
34
|
-
type=self.__type__, path=posix_path(f"{self.FOLDER}/host{index}-{host}"), _target=self.target
|
35
|
-
)
|
File without changes
|
File without changes
|
File without changes
|
{dissect.target-3.17.dev13.dist-info → dissect.target-3.17.dev17.dist-info}/entry_points.txt
RENAMED
File without changes
|
File without changes
|