dissect.target 3.17.dev13__py3-none-any.whl → 3.17.dev17__py3-none-any.whl
Sign up to get free protection for your applications and to get access to all the features.
- 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
|