dissect.target 3.14.dev28__py3-none-any.whl → 3.15__py3-none-any.whl
Sign up to get free protection for your applications and to get access to all the features.
- dissect/target/containers/ewf.py +1 -1
- dissect/target/containers/vhd.py +5 -2
- dissect/target/filesystem.py +36 -18
- dissect/target/filesystems/dir.py +10 -4
- dissect/target/filesystems/jffs.py +122 -0
- dissect/target/helpers/compat/path_310.py +506 -0
- dissect/target/helpers/compat/path_311.py +539 -0
- dissect/target/helpers/compat/path_312.py +443 -0
- dissect/target/helpers/compat/path_39.py +545 -0
- dissect/target/helpers/compat/path_common.py +223 -0
- dissect/target/helpers/cyber.py +512 -0
- dissect/target/helpers/fsutil.py +128 -666
- dissect/target/helpers/hashutil.py +17 -57
- dissect/target/helpers/keychain.py +9 -3
- dissect/target/helpers/loaderutil.py +1 -1
- dissect/target/helpers/mount.py +47 -4
- dissect/target/helpers/polypath.py +73 -0
- dissect/target/helpers/record_modifier.py +100 -0
- dissect/target/loader.py +2 -1
- dissect/target/loaders/asdf.py +2 -0
- dissect/target/loaders/cyber.py +37 -0
- dissect/target/loaders/log.py +14 -3
- dissect/target/loaders/raw.py +2 -0
- dissect/target/loaders/remote.py +12 -0
- dissect/target/loaders/tar.py +13 -0
- dissect/target/loaders/targetd.py +2 -0
- dissect/target/loaders/velociraptor.py +12 -3
- dissect/target/loaders/vmwarevm.py +2 -0
- dissect/target/plugin.py +272 -143
- dissect/target/plugins/apps/ssh/openssh.py +11 -54
- dissect/target/plugins/apps/ssh/opensshd.py +4 -3
- dissect/target/plugins/apps/ssh/putty.py +236 -0
- dissect/target/plugins/apps/ssh/ssh.py +58 -0
- dissect/target/plugins/apps/vpn/openvpn.py +6 -0
- dissect/target/plugins/apps/webserver/apache.py +309 -95
- dissect/target/plugins/apps/webserver/caddy.py +5 -2
- dissect/target/plugins/apps/webserver/citrix.py +82 -0
- dissect/target/plugins/apps/webserver/iis.py +9 -12
- dissect/target/plugins/apps/webserver/nginx.py +5 -2
- dissect/target/plugins/apps/webserver/webserver.py +25 -41
- dissect/target/plugins/child/wsl.py +1 -1
- dissect/target/plugins/filesystem/ntfs/mft.py +10 -0
- dissect/target/plugins/filesystem/ntfs/mft_timeline.py +10 -0
- dissect/target/plugins/filesystem/ntfs/usnjrnl.py +10 -0
- dissect/target/plugins/filesystem/ntfs/utils.py +28 -5
- dissect/target/plugins/filesystem/resolver.py +6 -4
- dissect/target/plugins/general/default.py +0 -2
- dissect/target/plugins/general/example.py +0 -1
- dissect/target/plugins/general/loaders.py +3 -5
- dissect/target/plugins/os/unix/_os.py +3 -3
- dissect/target/plugins/os/unix/bsd/citrix/_os.py +68 -28
- dissect/target/plugins/os/unix/bsd/citrix/history.py +130 -0
- dissect/target/plugins/os/unix/generic.py +17 -10
- dissect/target/plugins/os/unix/linux/fortios/__init__.py +0 -0
- dissect/target/plugins/os/unix/linux/fortios/_os.py +534 -0
- dissect/target/plugins/os/unix/linux/fortios/generic.py +30 -0
- dissect/target/plugins/os/unix/linux/fortios/locale.py +109 -0
- dissect/target/plugins/os/windows/log/evt.py +1 -1
- dissect/target/plugins/os/windows/log/schedlgu.py +155 -0
- dissect/target/plugins/os/windows/regf/firewall.py +1 -1
- dissect/target/plugins/os/windows/regf/shimcache.py +1 -1
- dissect/target/plugins/os/windows/regf/trusteddocs.py +1 -1
- dissect/target/plugins/os/windows/registry.py +1 -1
- dissect/target/plugins/os/windows/sam.py +3 -0
- dissect/target/plugins/os/windows/sru.py +41 -28
- dissect/target/plugins/os/windows/tasks.py +5 -2
- dissect/target/target.py +7 -3
- dissect/target/tools/dd.py +7 -1
- dissect/target/tools/fs.py +8 -1
- dissect/target/tools/info.py +22 -15
- dissect/target/tools/mount.py +28 -3
- dissect/target/tools/query.py +146 -117
- dissect/target/tools/reg.py +21 -16
- dissect/target/tools/shell.py +30 -6
- dissect/target/tools/utils.py +28 -0
- dissect/target/volumes/bde.py +14 -10
- dissect/target/volumes/luks.py +18 -10
- {dissect.target-3.14.dev28.dist-info → dissect.target-3.15.dist-info}/METADATA +4 -3
- {dissect.target-3.14.dev28.dist-info → dissect.target-3.15.dist-info}/RECORD +85 -67
- dissect/target/plugins/os/unix/linux/fortigate/_os.py +0 -175
- /dissect/target/{plugins/os/unix/linux/fortigate → helpers/compat}/__init__.py +0 -0
- {dissect.target-3.14.dev28.dist-info → dissect.target-3.15.dist-info}/COPYRIGHT +0 -0
- {dissect.target-3.14.dev28.dist-info → dissect.target-3.15.dist-info}/LICENSE +0 -0
- {dissect.target-3.14.dev28.dist-info → dissect.target-3.15.dist-info}/WHEEL +0 -0
- {dissect.target-3.14.dev28.dist-info → dissect.target-3.15.dist-info}/entry_points.txt +0 -0
- {dissect.target-3.14.dev28.dist-info → dissect.target-3.15.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,223 @@
|
|
1
|
+
from __future__ import annotations
|
2
|
+
|
3
|
+
import io
|
4
|
+
import posixpath
|
5
|
+
import stat
|
6
|
+
from typing import IO, TYPE_CHECKING, Iterator, Literal, Optional
|
7
|
+
|
8
|
+
if TYPE_CHECKING:
|
9
|
+
from dissect.target.helpers.fsutil import TargetPath
|
10
|
+
from dissect.target.filesystem import Filesystem, FilesystemEntry
|
11
|
+
|
12
|
+
from dissect.target.exceptions import FilesystemError, SymlinkRecursionError
|
13
|
+
from dissect.target.helpers.polypath import abspath, normalize
|
14
|
+
|
15
|
+
try:
|
16
|
+
# Up until CPython 3.12, pathlib._PathParents requires subclassing to inject the filesystem and flavour
|
17
|
+
# into each parent path component. Since CPython 3.12, this is no longer necessary.
|
18
|
+
# In CPython 3.13, _PathParents was moved to a different file, so this will result in an import error.
|
19
|
+
# Since we no longer need it in CPython 3.13, we can just ignore the error.
|
20
|
+
from pathlib import _PathParents
|
21
|
+
|
22
|
+
class _DissectPathParents(_PathParents):
|
23
|
+
__slots__ = ("_fs", "_flavour")
|
24
|
+
|
25
|
+
def __init__(self, path: TargetPath):
|
26
|
+
super().__init__(path)
|
27
|
+
self._fs = path._fs
|
28
|
+
self._flavour = path._flavour
|
29
|
+
|
30
|
+
def __getitem__(self, idx: int) -> TargetPath:
|
31
|
+
result = super().__getitem__(idx)
|
32
|
+
result._fs = self._fs
|
33
|
+
result._flavour = self._flavour
|
34
|
+
return result
|
35
|
+
|
36
|
+
except ImportError:
|
37
|
+
pass
|
38
|
+
|
39
|
+
|
40
|
+
class _DissectScandirIterator:
|
41
|
+
"""This class implements a ScandirIterator for dissect's scandir()
|
42
|
+
|
43
|
+
The _DissectScandirIterator provides a context manager, so scandir can be called as:
|
44
|
+
|
45
|
+
```
|
46
|
+
with scandir(path) as it:
|
47
|
+
for entry in it
|
48
|
+
print(entry.name)
|
49
|
+
```
|
50
|
+
|
51
|
+
similar to os.scandir() behaviour since Python 3.6.
|
52
|
+
"""
|
53
|
+
|
54
|
+
def __init__(self, iterator: Iterator[FilesystemEntry]):
|
55
|
+
self._iterator = iterator
|
56
|
+
|
57
|
+
def __del__(self) -> None:
|
58
|
+
self.close()
|
59
|
+
|
60
|
+
def __enter__(self) -> Iterator[FilesystemEntry]:
|
61
|
+
return self._iterator
|
62
|
+
|
63
|
+
def __exit__(self, *args, **kwargs) -> Literal[False]:
|
64
|
+
return False
|
65
|
+
|
66
|
+
def __iter__(self) -> Iterator[FilesystemEntry]:
|
67
|
+
return self._iterator
|
68
|
+
|
69
|
+
def __next__(self, *args) -> FilesystemEntry:
|
70
|
+
return next(self._iterator, *args)
|
71
|
+
|
72
|
+
def close(self):
|
73
|
+
# close() is not defined in the various filesystem implementations. The
|
74
|
+
# python ScandirIterator does define the interface however.
|
75
|
+
pass
|
76
|
+
|
77
|
+
|
78
|
+
def scandir(path: TargetPath) -> _DissectScandirIterator:
|
79
|
+
return _DissectScandirIterator(path.get().scandir())
|
80
|
+
|
81
|
+
|
82
|
+
def realpath(path: TargetPath, *, strict: bool = False) -> str:
|
83
|
+
"""Return the canonical path of the specified filename, eliminating any symbolic links encountered in the path."""
|
84
|
+
filename = str(path)
|
85
|
+
path, _ = _joinrealpath(path._fs, filename[:0], filename, strict, {})
|
86
|
+
return abspath(path)
|
87
|
+
|
88
|
+
|
89
|
+
def isjunction(path: TargetPath) -> bool:
|
90
|
+
"""Return True if the path is a junction."""
|
91
|
+
try:
|
92
|
+
from dissect.target.filesystems.ntfs import NtfsFilesystemEntry
|
93
|
+
except ImportError:
|
94
|
+
return False
|
95
|
+
|
96
|
+
entry = path.get()
|
97
|
+
# Python's ntpath isjunction() only checks for mount point reparse tags
|
98
|
+
return isinstance(entry, NtfsFilesystemEntry) and entry.dereference().is_mount_point()
|
99
|
+
|
100
|
+
|
101
|
+
# Join two paths, normalizing and eliminating any symbolic links
|
102
|
+
# encountered in the second path.
|
103
|
+
# NOTE: This is a copy of posixpath._joinrealpath with some small tweaks
|
104
|
+
def _joinrealpath(fs: Filesystem, path: str, rest: str, strict: bool, seen: dict[str, str]) -> tuple[str, bool]:
|
105
|
+
if posixpath.isabs(rest):
|
106
|
+
rest = rest[1:]
|
107
|
+
path = "/"
|
108
|
+
|
109
|
+
while rest:
|
110
|
+
name, _, rest = rest.partition("/")
|
111
|
+
if not name or name == ".":
|
112
|
+
# current dir
|
113
|
+
continue
|
114
|
+
if name == "..":
|
115
|
+
# parent dir
|
116
|
+
if path:
|
117
|
+
path, name = posixpath.split(path)
|
118
|
+
if name == "..":
|
119
|
+
path = posixpath.join(path, "..", "..")
|
120
|
+
else:
|
121
|
+
path = ".."
|
122
|
+
continue
|
123
|
+
newpath = posixpath.join(path, name)
|
124
|
+
try:
|
125
|
+
st = fs.get(newpath).lstat()
|
126
|
+
except FilesystemError:
|
127
|
+
if strict:
|
128
|
+
raise
|
129
|
+
is_link = False
|
130
|
+
else:
|
131
|
+
is_link = stat.S_ISLNK(st.st_mode)
|
132
|
+
if not is_link:
|
133
|
+
path = newpath
|
134
|
+
continue
|
135
|
+
# Resolve the symbolic link
|
136
|
+
if newpath in seen:
|
137
|
+
# Already seen this path
|
138
|
+
path = seen[newpath]
|
139
|
+
if path is not None:
|
140
|
+
# use cached value
|
141
|
+
continue
|
142
|
+
# The symlink is not resolved, so we must have a symlink loop.
|
143
|
+
if strict:
|
144
|
+
# Raise OSError(errno.ELOOP)
|
145
|
+
raise SymlinkRecursionError(newpath)
|
146
|
+
else:
|
147
|
+
# Return already resolved part + rest of the path unchanged.
|
148
|
+
return posixpath.join(newpath, rest), False
|
149
|
+
seen[newpath] = None # not resolved symlink
|
150
|
+
path, ok = _joinrealpath(fs, path, normalize(fs.readlink(newpath)), strict, seen)
|
151
|
+
if not ok:
|
152
|
+
return posixpath.join(path, rest), False
|
153
|
+
seen[newpath] = path # resolved symlink
|
154
|
+
|
155
|
+
return path, True
|
156
|
+
|
157
|
+
|
158
|
+
def io_open(
|
159
|
+
path: TargetPath,
|
160
|
+
mode: str = "rb",
|
161
|
+
buffering: int = 0,
|
162
|
+
encoding: Optional[str] = None,
|
163
|
+
errors: Optional[str] = None,
|
164
|
+
newline: Optional[str] = None,
|
165
|
+
) -> IO:
|
166
|
+
"""Open file and return a stream.
|
167
|
+
|
168
|
+
Supports a subset of features of the real pathlib.open/io.open.
|
169
|
+
|
170
|
+
Note: in contrast to regular Python, the mode is binary by default. Text mode
|
171
|
+
has to be explicitly specified. Buffering is also disabled by default.
|
172
|
+
"""
|
173
|
+
modes = set(mode)
|
174
|
+
if modes - set("rbt") or len(mode) > len(modes):
|
175
|
+
raise ValueError("invalid mode: %r" % mode)
|
176
|
+
|
177
|
+
reading = "r" in modes
|
178
|
+
binary = "b" in modes
|
179
|
+
text = "t" in modes or "b" not in modes
|
180
|
+
|
181
|
+
if "b" not in mode:
|
182
|
+
encoding = encoding or "UTF-8"
|
183
|
+
# CPython >= 3.10
|
184
|
+
if hasattr(io, "text_encoding"):
|
185
|
+
# Vermin linting needs to be skipped for this line as this is
|
186
|
+
# guarded by an explicit check for availability.
|
187
|
+
# novermin
|
188
|
+
encoding = io.text_encoding(encoding)
|
189
|
+
|
190
|
+
if not reading:
|
191
|
+
raise ValueError("must be reading mode")
|
192
|
+
if text and binary:
|
193
|
+
raise ValueError("can't have text and binary mode at once")
|
194
|
+
if binary and encoding is not None:
|
195
|
+
raise ValueError("binary mode doesn't take an encoding argument")
|
196
|
+
if binary and errors is not None:
|
197
|
+
raise ValueError("binary mode doesn't take an errors argument")
|
198
|
+
if binary and newline is not None:
|
199
|
+
raise ValueError("binary mode doesn't take a newline argument")
|
200
|
+
|
201
|
+
raw = path.get().open()
|
202
|
+
result = raw
|
203
|
+
|
204
|
+
line_buffering = False
|
205
|
+
if buffering == 1 or buffering < 0 and raw.isatty():
|
206
|
+
buffering = -1
|
207
|
+
line_buffering = True
|
208
|
+
if buffering < 0 or text and buffering == 0:
|
209
|
+
buffering = io.DEFAULT_BUFFER_SIZE
|
210
|
+
if buffering == 0:
|
211
|
+
if binary:
|
212
|
+
return result
|
213
|
+
raise ValueError("can't have unbuffered text I/O")
|
214
|
+
|
215
|
+
buffer = io.BufferedReader(raw, buffering)
|
216
|
+
result = buffer
|
217
|
+
if binary:
|
218
|
+
return result
|
219
|
+
|
220
|
+
result = io.TextIOWrapper(buffer, encoding, errors, newline, line_buffering)
|
221
|
+
result.mode = mode
|
222
|
+
|
223
|
+
return result
|