edf-plasma-cli 1.0.0__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.
- edf_plasma_cli/__init__.py +0 -0
- edf_plasma_cli/__version__.py +34 -0
- edf_plasma_cli/command/__init__.py +16 -0
- edf_plasma_cli/command/abc.py +45 -0
- edf_plasma_cli/command/dissect.py +156 -0
- edf_plasma_cli/command/list.py +27 -0
- edf_plasma_cli/main.py +70 -0
- edf_plasma_cli-1.0.0.dist-info/METADATA +236 -0
- edf_plasma_cli-1.0.0.dist-info/RECORD +13 -0
- edf_plasma_cli-1.0.0.dist-info/WHEEL +5 -0
- edf_plasma_cli-1.0.0.dist-info/entry_points.txt +2 -0
- edf_plasma_cli-1.0.0.dist-info/licenses/LICENSE +20 -0
- edf_plasma_cli-1.0.0.dist-info/top_level.txt +1 -0
|
File without changes
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
# file generated by setuptools-scm
|
|
2
|
+
# don't change, don't track in version control
|
|
3
|
+
|
|
4
|
+
__all__ = [
|
|
5
|
+
"__version__",
|
|
6
|
+
"__version_tuple__",
|
|
7
|
+
"version",
|
|
8
|
+
"version_tuple",
|
|
9
|
+
"__commit_id__",
|
|
10
|
+
"commit_id",
|
|
11
|
+
]
|
|
12
|
+
|
|
13
|
+
TYPE_CHECKING = False
|
|
14
|
+
if TYPE_CHECKING:
|
|
15
|
+
from typing import Tuple
|
|
16
|
+
from typing import Union
|
|
17
|
+
|
|
18
|
+
VERSION_TUPLE = Tuple[Union[int, str], ...]
|
|
19
|
+
COMMIT_ID = Union[str, None]
|
|
20
|
+
else:
|
|
21
|
+
VERSION_TUPLE = object
|
|
22
|
+
COMMIT_ID = object
|
|
23
|
+
|
|
24
|
+
version: str
|
|
25
|
+
__version__: str
|
|
26
|
+
__version_tuple__: VERSION_TUPLE
|
|
27
|
+
version_tuple: VERSION_TUPLE
|
|
28
|
+
commit_id: COMMIT_ID
|
|
29
|
+
__commit_id__: COMMIT_ID
|
|
30
|
+
|
|
31
|
+
__version__ = version = '1.0.0'
|
|
32
|
+
__version_tuple__ = version_tuple = (1, 0, 0)
|
|
33
|
+
|
|
34
|
+
__commit_id__ = commit_id = 'gac0edb2f0'
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
"""Command module"""
|
|
2
|
+
|
|
3
|
+
from .abc import Format
|
|
4
|
+
from .dissect import setup_command as setup_dissect_command
|
|
5
|
+
from .list import setup_command as setup_list_command
|
|
6
|
+
|
|
7
|
+
_COMMANDS = (
|
|
8
|
+
setup_dissect_command,
|
|
9
|
+
setup_list_command,
|
|
10
|
+
)
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
def setup_commands(cmd):
|
|
14
|
+
"""Setup commands"""
|
|
15
|
+
for setup_command in _COMMANDS:
|
|
16
|
+
setup_command(cmd)
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
"""Base for command implementation"""
|
|
2
|
+
|
|
3
|
+
from enum import Enum
|
|
4
|
+
from json import dumps
|
|
5
|
+
|
|
6
|
+
from rich.box import ROUNDED
|
|
7
|
+
from rich.console import Console
|
|
8
|
+
from rich.table import Column, Table
|
|
9
|
+
|
|
10
|
+
CONSOLE = Console()
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
class Format(Enum):
|
|
14
|
+
"""Output format"""
|
|
15
|
+
|
|
16
|
+
RICH = 'rich'
|
|
17
|
+
JSON = 'json'
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
class FileFormat(Enum):
|
|
21
|
+
"""File format"""
|
|
22
|
+
|
|
23
|
+
CSV = 'csv'
|
|
24
|
+
JSONL = 'jsonl'
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
def display_table(out_fmt: Format, headers, rows, **kwargs):
|
|
28
|
+
"""Build and print table"""
|
|
29
|
+
final_kwargs = {
|
|
30
|
+
'box': ROUNDED,
|
|
31
|
+
'row_styles': ['dim', ''],
|
|
32
|
+
}
|
|
33
|
+
final_kwargs.update(kwargs)
|
|
34
|
+
if out_fmt == Format.RICH:
|
|
35
|
+
final_headers = [
|
|
36
|
+
Column(header) if isinstance(header, str) else Column(**header)
|
|
37
|
+
for header in headers
|
|
38
|
+
]
|
|
39
|
+
table = Table(*final_headers, **final_kwargs)
|
|
40
|
+
for row in rows:
|
|
41
|
+
table.add_row(*row)
|
|
42
|
+
CONSOLE.print(table)
|
|
43
|
+
return
|
|
44
|
+
for row in rows:
|
|
45
|
+
print(dumps(dict(zip(headers, row))))
|
|
@@ -0,0 +1,156 @@
|
|
|
1
|
+
"""dissect command implementation"""
|
|
2
|
+
|
|
3
|
+
from pathlib import Path
|
|
4
|
+
from platform import node
|
|
5
|
+
|
|
6
|
+
from edf_plasma_core.helper.csv import write_csv_gz
|
|
7
|
+
from edf_plasma_core.helper.filtering import Filter
|
|
8
|
+
from edf_plasma_core.helper.json import write_jsonl_gz
|
|
9
|
+
from edf_plasma_core.helper.logging import get_logger
|
|
10
|
+
from edf_plasma_core.helper.matching import regexp
|
|
11
|
+
from edf_plasma_core.dissector import (
|
|
12
|
+
DissectionContext,
|
|
13
|
+
Dissector,
|
|
14
|
+
DissectorList,
|
|
15
|
+
get_dissectors,
|
|
16
|
+
)
|
|
17
|
+
|
|
18
|
+
from .abc import FileFormat, display_table
|
|
19
|
+
|
|
20
|
+
_LOGGER = get_logger('cli.command.dissect')
|
|
21
|
+
_HOSTNAME_REPL_PATTERN = regexp(r'[^\w]+')
|
|
22
|
+
_OUTPUT_FORMAT_STRATEGY = {
|
|
23
|
+
FileFormat.CSV: ('.csv.gz', write_csv_gz),
|
|
24
|
+
FileFormat.JSONL: (
|
|
25
|
+
'.jsonl.gz',
|
|
26
|
+
lambda filepath, _, records: write_jsonl_gz(filepath, records),
|
|
27
|
+
),
|
|
28
|
+
}
|
|
29
|
+
_GETATTR_STRATEGY = {
|
|
30
|
+
'slug': lambda dissector: {dissector.slug},
|
|
31
|
+
'tags': lambda dissector: {tag.value for tag in dissector.tags},
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
def _select(filter_spec: str, dissectors: DissectorList) -> DissectorList:
|
|
36
|
+
attribute, values = filter_spec.split(':', 1)
|
|
37
|
+
filter_ = Filter(include=set(values.split(',')))
|
|
38
|
+
getattr_ = _GETATTR_STRATEGY[attribute]
|
|
39
|
+
return [
|
|
40
|
+
dissector
|
|
41
|
+
for dissector in dissectors
|
|
42
|
+
if filter_.accept(getattr_(dissector))
|
|
43
|
+
]
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
def _run_dissector(
|
|
47
|
+
dissector: Dissector,
|
|
48
|
+
target: Path,
|
|
49
|
+
hostname: str,
|
|
50
|
+
file_format: FileFormat,
|
|
51
|
+
prefix: bool,
|
|
52
|
+
output_directory: Path,
|
|
53
|
+
) -> tuple[Path, Path]:
|
|
54
|
+
targets = [target]
|
|
55
|
+
if target.is_dir():
|
|
56
|
+
targets = list(dissector.select(target))
|
|
57
|
+
hostname = _HOSTNAME_REPL_PATTERN.sub('_', hostname).upper()
|
|
58
|
+
ctx_list = [
|
|
59
|
+
DissectionContext(
|
|
60
|
+
dissector=dissector.slug,
|
|
61
|
+
hostname=hostname,
|
|
62
|
+
source=str(target),
|
|
63
|
+
filepath=target,
|
|
64
|
+
)
|
|
65
|
+
for target in targets
|
|
66
|
+
]
|
|
67
|
+
prefix = f'{hostname}_' if prefix else ''
|
|
68
|
+
output_directory.mkdir(parents=True, exist_ok=True)
|
|
69
|
+
extension, write_records_to_file = _OUTPUT_FORMAT_STRATEGY[file_format]
|
|
70
|
+
out_filepath = output_directory / f'{prefix}{dissector.slug}{extension}'
|
|
71
|
+
err_filepath = (
|
|
72
|
+
output_directory / f'{prefix}{dissector.slug}_error{extension}'
|
|
73
|
+
)
|
|
74
|
+
write_records_to_file(
|
|
75
|
+
out_filepath,
|
|
76
|
+
dissector.table_schema.names,
|
|
77
|
+
dissector.dissect_many(ctx_list),
|
|
78
|
+
)
|
|
79
|
+
write_records_to_file(
|
|
80
|
+
err_filepath,
|
|
81
|
+
dissector.error_table_schema.names,
|
|
82
|
+
dissector.process_errors(ctx_list),
|
|
83
|
+
)
|
|
84
|
+
return out_filepath, err_filepath
|
|
85
|
+
|
|
86
|
+
|
|
87
|
+
def _dissect_cmd(args):
|
|
88
|
+
rows = []
|
|
89
|
+
dissectors = get_dissectors()
|
|
90
|
+
if args.filter:
|
|
91
|
+
try:
|
|
92
|
+
dissectors = _select(args.filter, dissectors)
|
|
93
|
+
except KeyError:
|
|
94
|
+
_LOGGER.error(
|
|
95
|
+
"invalid filter attribute, available attributes are %s",
|
|
96
|
+
list(_GETATTR_STRATEGY.keys()),
|
|
97
|
+
)
|
|
98
|
+
return
|
|
99
|
+
for dissector in dissectors:
|
|
100
|
+
file_format = FileFormat(args.file_format)
|
|
101
|
+
out_filepath, err_filepath = _run_dissector(
|
|
102
|
+
dissector,
|
|
103
|
+
args.target,
|
|
104
|
+
args.hostname,
|
|
105
|
+
file_format,
|
|
106
|
+
args.prefix,
|
|
107
|
+
args.output_directory,
|
|
108
|
+
)
|
|
109
|
+
rows.append(
|
|
110
|
+
[
|
|
111
|
+
dissector.slug,
|
|
112
|
+
str(out_filepath.resolve()),
|
|
113
|
+
str(err_filepath.resolve()),
|
|
114
|
+
]
|
|
115
|
+
)
|
|
116
|
+
display_table(
|
|
117
|
+
args.format,
|
|
118
|
+
[
|
|
119
|
+
'dissector',
|
|
120
|
+
{'header': 'out_filepath', 'overflow': 'fold'},
|
|
121
|
+
{'header': 'err_filepath', 'overflow': 'fold'},
|
|
122
|
+
],
|
|
123
|
+
rows,
|
|
124
|
+
show_header=False,
|
|
125
|
+
)
|
|
126
|
+
|
|
127
|
+
|
|
128
|
+
def setup_command(cmd):
|
|
129
|
+
"""Setup init command parser"""
|
|
130
|
+
dissect = cmd.add_parser('dissect', help="Run a single dissector")
|
|
131
|
+
dissect.add_argument(
|
|
132
|
+
'--file-format',
|
|
133
|
+
'--ff',
|
|
134
|
+
choices=[fmt.value for fmt in FileFormat],
|
|
135
|
+
default=FileFormat.CSV.value,
|
|
136
|
+
help="Output file format",
|
|
137
|
+
)
|
|
138
|
+
dissect.add_argument(
|
|
139
|
+
'--prefix',
|
|
140
|
+
action='store_true',
|
|
141
|
+
help="Prefix output file with hostname",
|
|
142
|
+
)
|
|
143
|
+
dissect.add_argument(
|
|
144
|
+
'--hostname', default=node(), help="Hostname for given artifact"
|
|
145
|
+
)
|
|
146
|
+
dissect.add_argument(
|
|
147
|
+
'--filter',
|
|
148
|
+
help="Dissector filter, e.g. 'tags:ios' or 'slug:microsoft_lnk,microsoft_mft'",
|
|
149
|
+
)
|
|
150
|
+
dissect.add_argument(
|
|
151
|
+
'target', type=Path, help="Filepath or directory to dissect"
|
|
152
|
+
)
|
|
153
|
+
dissect.add_argument(
|
|
154
|
+
'output_directory', type=Path, help="Dissector output directory"
|
|
155
|
+
)
|
|
156
|
+
dissect.set_defaults(func=_dissect_cmd)
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
"""list command implementation"""
|
|
2
|
+
|
|
3
|
+
from edf_plasma_core.dissector import get_dissectors
|
|
4
|
+
|
|
5
|
+
from .abc import display_table
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
def _list_cmd(args):
|
|
9
|
+
display_table(
|
|
10
|
+
args.format,
|
|
11
|
+
['slug', 'tags', 'description'],
|
|
12
|
+
(
|
|
13
|
+
[
|
|
14
|
+
dissector.slug,
|
|
15
|
+
','.join(sorted(tag.value for tag in dissector.tags)),
|
|
16
|
+
dissector.description,
|
|
17
|
+
]
|
|
18
|
+
for dissector in get_dissectors()
|
|
19
|
+
),
|
|
20
|
+
show_header=False,
|
|
21
|
+
)
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
def setup_command(cmd):
|
|
25
|
+
"""Setup init command parser"""
|
|
26
|
+
list_ = cmd.add_parser('list', aliases=['ls'], help="List dissectors")
|
|
27
|
+
list_.set_defaults(func=_list_cmd)
|
edf_plasma_cli/main.py
ADDED
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
"""Plasma main program"""
|
|
2
|
+
|
|
3
|
+
from argparse import ArgumentParser
|
|
4
|
+
from importlib.util import spec_from_file_location, module_from_spec
|
|
5
|
+
from pathlib import Path
|
|
6
|
+
from sys import modules, exit as sys_exit
|
|
7
|
+
|
|
8
|
+
from edf_plasma_core.helper.logging import get_logger
|
|
9
|
+
# load dissectors
|
|
10
|
+
import edf_plasma_dissectors as _
|
|
11
|
+
|
|
12
|
+
from .__version__ import version
|
|
13
|
+
from .command import Format, setup_commands
|
|
14
|
+
|
|
15
|
+
_LOGGER = get_logger('cli')
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
def _parse_args():
|
|
19
|
+
_LOGGER.info("Plasma v%s", version)
|
|
20
|
+
parser = ArgumentParser(description=f"Plasma v{version}")
|
|
21
|
+
parser.add_argument(
|
|
22
|
+
'--plugin-directory',
|
|
23
|
+
'-p',
|
|
24
|
+
type=Path,
|
|
25
|
+
help="Plugin directory to register extra dissectors",
|
|
26
|
+
)
|
|
27
|
+
parser.add_argument(
|
|
28
|
+
'--format',
|
|
29
|
+
'-f',
|
|
30
|
+
choices=[fmt.value for fmt in Format],
|
|
31
|
+
default=Format.RICH.value,
|
|
32
|
+
help="Output format",
|
|
33
|
+
)
|
|
34
|
+
cmd = parser.add_subparsers(dest='cmd')
|
|
35
|
+
cmd.required = True
|
|
36
|
+
setup_commands(cmd)
|
|
37
|
+
args = parser.parse_args()
|
|
38
|
+
args.format = Format(args.format)
|
|
39
|
+
return args
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
def _import_from_file(plugin: Path):
|
|
43
|
+
spec = spec_from_file_location(plugin.stem, plugin)
|
|
44
|
+
module = module_from_spec(spec)
|
|
45
|
+
modules[plugin.stem] = module
|
|
46
|
+
spec.loader.exec_module(module)
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
def _import_from_directory(plugin_directory: Path):
|
|
50
|
+
if not plugin_directory:
|
|
51
|
+
return
|
|
52
|
+
if not plugin_directory.is_dir():
|
|
53
|
+
return
|
|
54
|
+
for plugin in plugin_directory.glob('*.py'):
|
|
55
|
+
if not plugin.is_file():
|
|
56
|
+
continue
|
|
57
|
+
_import_from_file(plugin)
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
def app():
|
|
61
|
+
"""Application entrypoint"""
|
|
62
|
+
args = _parse_args()
|
|
63
|
+
_import_from_directory(args.plugin_directory)
|
|
64
|
+
exit_code = 0
|
|
65
|
+
try:
|
|
66
|
+
args.func(args)
|
|
67
|
+
except:
|
|
68
|
+
_LOGGER.exception("exception caught in main handler!")
|
|
69
|
+
exit_code = 1
|
|
70
|
+
sys_exit(exit_code)
|
|
@@ -0,0 +1,236 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: edf-plasma-cli
|
|
3
|
+
Version: 1.0.0
|
|
4
|
+
Summary: EDF Plasma CLI
|
|
5
|
+
Author-email: CERT-EDF <cert@edf.fr>
|
|
6
|
+
License-Expression: MIT
|
|
7
|
+
Project-URL: Homepage, https://github.com/cert-edf/plasma
|
|
8
|
+
Project-URL: Repository, https://github.com/cert-edf/plasma
|
|
9
|
+
Project-URL: Bug Tracker, https://github.com/cert-edf/plasma/issues
|
|
10
|
+
Keywords: edf,plasma,forensic,artifact,dissector,normalizer,cli
|
|
11
|
+
Classifier: Development Status :: 5 - Production/Stable
|
|
12
|
+
Classifier: Intended Audience :: End Users/Desktop
|
|
13
|
+
Classifier: Operating System :: POSIX :: Linux
|
|
14
|
+
Classifier: Programming Language :: Python :: 3
|
|
15
|
+
Classifier: Programming Language :: Python :: 3 :: Only
|
|
16
|
+
Classifier: Topic :: Utilities
|
|
17
|
+
Classifier: Topic :: Security
|
|
18
|
+
Requires-Python: >=3.12
|
|
19
|
+
Description-Content-Type: text/markdown
|
|
20
|
+
License-File: LICENSE
|
|
21
|
+
Requires-Dist: edf-plasma-dissectors~=1.0
|
|
22
|
+
Dynamic: license-file
|
|
23
|
+
|
|
24
|
+
# EDF Plasma CLI
|
|
25
|
+
|
|
26
|
+
## Introduction
|
|
27
|
+
|
|
28
|
+
This package implements a command line interface to perform dissection of forensics
|
|
29
|
+
artifacts from the command line using Plasma Framework's dissectors.
|
|
30
|
+
|
|
31
|
+
<br>
|
|
32
|
+
|
|
33
|
+
## Setup
|
|
34
|
+
|
|
35
|
+
```bash
|
|
36
|
+
# first, install edf-plasma-dissectors dependencies
|
|
37
|
+
apt install autoconf \
|
|
38
|
+
automake \
|
|
39
|
+
autopoint \
|
|
40
|
+
build-essential \
|
|
41
|
+
git \
|
|
42
|
+
libsystemd-dev \
|
|
43
|
+
libtool \
|
|
44
|
+
pkg-config \
|
|
45
|
+
python3-dev \
|
|
46
|
+
python3-venv
|
|
47
|
+
# create a virtual environment
|
|
48
|
+
python3 -m venv venv
|
|
49
|
+
# install edf-plasma-cli (will also install edf-plasma-core and edf-plasma-dissectors)
|
|
50
|
+
venv/bin/python -m pip install edf-plasma-cli
|
|
51
|
+
# start dissecting artifacts using dissect command
|
|
52
|
+
venv/bin/plasma -h
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
<br>
|
|
56
|
+
|
|
57
|
+
## Usage
|
|
58
|
+
|
|
59
|
+
> [!TIP]
|
|
60
|
+
> - Use `-f` to switch from `rich` output to `json` output to allow piping into `jq` for automation purpose
|
|
61
|
+
> - Use `-p` to load custom plugins from a directory
|
|
62
|
+
|
|
63
|
+
<br>
|
|
64
|
+
|
|
65
|
+
### List available dissectors
|
|
66
|
+
|
|
67
|
+
```bash
|
|
68
|
+
venv/bin/plasma list
|
|
69
|
+
```
|
|
70
|
+
```
|
|
71
|
+
[2025-09-17T09:52:49] INFO (plasma.cli): Plasma v1.0.0
|
|
72
|
+
╭────────────────────────────┬───────────────────────┬─────────────────────────────────────────────────╮
|
|
73
|
+
│ android_mvt_appops │ android,mvt │ Dissect MVT Android appops output │
|
|
74
|
+
│ android_mvt_files │ android,mvt │ Dissect MVT Android files output │
|
|
75
|
+
│ android_mvt_packages │ android,mvt │ Dissect MVT Android packages output │
|
|
76
|
+
│ android_mvt_packages_perms │ android,mvt │ Dissect MVT Android packages permissions output │
|
|
77
|
+
│ android_mvt_processes │ android,mvt │ Dissect MVT Android processes output │
|
|
78
|
+
│ android_mvt_sms │ android,mvt │ Dissect MVT Android sms output │
|
|
79
|
+
│ elf_ctor_dtor │ elf,linux │ Dissect ELF constructors and destructors │
|
|
80
|
+
│ elf_export │ elf,linux │ Dissect ELF binary exported symbols │
|
|
81
|
+
│ elf_import │ elf,linux │ Dissect ELF binary imported symbols │
|
|
82
|
+
│ elf_info │ elf,linux │ Dissect ELF information │
|
|
83
|
+
│ elf_library │ elf,linux │ Dissect ELF binary needed libraries │
|
|
84
|
+
│ elf_section │ elf,linux │ Dissect ELF binary sections │
|
|
85
|
+
│ elf_segment │ elf,linux │ Dissect ELF binary segments │
|
|
86
|
+
│ generic_chromium_history │ generic,linux,windows │ Dissect Chromium download and visit history │
|
|
87
|
+
│ generic_firefox_history │ generic,linux,windows │ Dissect Firefox download and visit history │
|
|
88
|
+
│ generic_ssh_pub_key │ generic,linux,windows │ Dissect SSH public key │
|
|
89
|
+
│ ios_mvt_analytics_ad_daily │ ios,mvt │ Dissect MVT iOS os analytics ad daily output │
|
|
90
|
+
│ ios_mvt_apps │ ios,mvt │ Dissect MVT iOS apps output │
|
|
91
|
+
│ ios_mvt_datausage │ ios,mvt │ Dissect MVT iOS datausage output │
|
|
92
|
+
│ ios_mvt_manifest │ ios,mvt │ Dissect MVT iOS manifest output │
|
|
93
|
+
│ ios_mvt_safari_history │ ios,mvt │ Dissect MVT iOS safari history output │
|
|
94
|
+
│ ios_mvt_safari_state │ ios,mvt │ Dissect MVT iOS safari state output │
|
|
95
|
+
│ ios_mvt_shortcuts │ ios,mvt │ Dissect MVT iOS shortcuts output │
|
|
96
|
+
│ ios_mvt_sms │ ios,mvt │ Dissect MVT iOS sms output │
|
|
97
|
+
│ ios_mvt_tcc │ ios,mvt │ Dissect MVT iOS tcc output │
|
|
98
|
+
│ ios_mvt_webkit_rsrc_load │ ios,mvt │ Dissect MVT iOS webkit resource load output │
|
|
99
|
+
│ ios_mvt_whatsapp │ ios,mvt │ Dissect MVT iOS whatsapp output │
|
|
100
|
+
│ ios_sysdiag_bluetooth │ ios,sysdiag │ Dissect iOS sysdiagnose bluetooth status output │
|
|
101
|
+
│ ios_sysdiag_disk │ ios,sysdiag │ Dissect iOS sysdiagnose disk output │
|
|
102
|
+
│ ios_sysdiag_mount │ ios,sysdiag │ Dissect iOS sysdiagnose mount output │
|
|
103
|
+
│ ios_sysdiag_ps │ ios,sysdiag │ Dissect iOS sysdiagnose ps output │
|
|
104
|
+
│ ios_sysdiag_remotectl │ ios,sysdiag │ Dissect iOS sysdiagnose remotectl output │
|
|
105
|
+
│ ios_sysdiag_shutdown │ ios,sysdiag │ Dissect iOS sysdiagnose shutdown output │
|
|
106
|
+
│ ios_sysdiag_wifi │ ios,sysdiag │ Dissect iOS sysdiagnose disk output │
|
|
107
|
+
│ linux_apt_history │ linux │ Dissect apt history log │
|
|
108
|
+
│ linux_apt_sources │ linux │ Dissect apt sources │
|
|
109
|
+
│ linux_at_acl │ linux │ Dissect at.allow and at.deny │
|
|
110
|
+
│ linux_at_jobs │ linux │ Dissect atjobs │
|
|
111
|
+
│ linux_auditd │ linux │ Dissect auditd log │
|
|
112
|
+
│ linux_authlog │ linux │ Dissect auth.log* and secure* journals │
|
|
113
|
+
│ linux_crontab │ linux │ Dissect crontabs │
|
|
114
|
+
│ linux_dpkg │ linux │ Dissect dpkg │
|
|
115
|
+
│ linux_fslist │ linux │ Dissect file list │
|
|
116
|
+
│ linux_fstab │ linux │ Dissect fstab │
|
|
117
|
+
│ linux_group │ linux │ Dissect group │
|
|
118
|
+
│ linux_history │ linux │ Dissect *_history files │
|
|
119
|
+
│ linux_hosts │ linux │ Dissect hosts │
|
|
120
|
+
│ linux_journal_auth │ linux │ Dissect auth events from systemd journal │
|
|
121
|
+
│ linux_journal_cron │ linux │ Dissect cron events from systemd journal │
|
|
122
|
+
│ linux_journal_ftp │ linux │ Dissect ftp events from systemd journal │
|
|
123
|
+
│ linux_logrotate │ linux │ Dissect logrotate │
|
|
124
|
+
│ linux_netstat │ linux │ Network connections │
|
|
125
|
+
│ linux_passwd │ linux │ Dissect passwd │
|
|
126
|
+
│ linux_resolv │ linux │ Dissect resolv │
|
|
127
|
+
│ linux_shadow │ linux │ Dissect shadow │
|
|
128
|
+
│ linux_systemd_service │ linux │ Dissect systemd service │
|
|
129
|
+
│ linux_systemd_timer │ linux │ Dissect systemd timer │
|
|
130
|
+
│ linux_udev_rules │ linux │ Dissect udev rules │
|
|
131
|
+
│ linux_usbguard_device │ linux │ Dissect device events from usbguard log │
|
|
132
|
+
│ linux_usbguard_policy │ linux │ Dissect policy events from usbguard log │
|
|
133
|
+
│ linux_wtmp_utmp │ linux │ Dissect utmp, wtmp and btmp binary logs │
|
|
134
|
+
│ linux_xdg_autostart │ linux │ Dissect xdg autostart │
|
|
135
|
+
│ linux_yum_history │ linux │ Dissect yum history log │
|
|
136
|
+
│ linux_yum_sources │ linux │ Dissect yum sources │
|
|
137
|
+
│ pcap_dns_answers │ pcap │ Dissect DNS answers from PCAP │
|
|
138
|
+
│ pcap_dns_queries │ pcap │ Dissect DNS queries from PCAP │
|
|
139
|
+
│ pcap_http_requests │ pcap │ Dissect DNS http requests from PCAP │
|
|
140
|
+
│ pcap_proto_stats │ pcap │ Dissect protocols from PCAP │
|
|
141
|
+
│ pcap_tcp_conv │ pcap │ Dissect TCP conversations from PCAP │
|
|
142
|
+
│ pcap_tls_cert │ pcap │ Dissect TLS certificates from PCAP │
|
|
143
|
+
│ pcap_tls_client_hello │ pcap │ Dissect TLS client hello from PCAP │
|
|
144
|
+
│ pcap_tls_server_hello │ pcap │ Dissect TLS server hello from PCAP │
|
|
145
|
+
│ pcap_udp_conv │ pcap │ Dissect UDP conversations from PCAP │
|
|
146
|
+
│ pe_ctor_dtor │ pe,windows │ Dissect PE constructors and destructors │
|
|
147
|
+
│ pe_export │ pe,windows │ Dissect PE exported symbols │
|
|
148
|
+
│ pe_import │ pe,windows │ Dissect PE imported symbols │
|
|
149
|
+
│ pe_info │ pe,windows │ Dissect PE information │
|
|
150
|
+
│ pe_resource │ pe,windows │ Dissect PE resources │
|
|
151
|
+
│ pe_rich │ pe,windows │ Dissect PE rich header │
|
|
152
|
+
│ pe_section │ pe,windows │ Dissect PE sections │
|
|
153
|
+
│ pe_signature │ pe,windows │ Dissect PE signatures │
|
|
154
|
+
│ windows_appx │ windows │ Dissect AppX manifest files │
|
|
155
|
+
│ windows_evtx │ windows │ Dissect events from EVTX files │
|
|
156
|
+
│ windows_iis │ windows │ Dissect IIS journal entries │
|
|
157
|
+
│ windows_lnk │ windows │ Dissect LNK │
|
|
158
|
+
│ windows_jumplist │ windows │ Dissect jumplist entries │
|
|
159
|
+
│ windows_mft │ windows │ Dissect entries from NTFS MFT │
|
|
160
|
+
│ windows_mssql │ windows │ Dissect MSSQL ERRORLOG entries │
|
|
161
|
+
│ windows_netstat │ windows │ Network connections │
|
|
162
|
+
│ windows_powershell │ windows │ Dissect powershell command line history │
|
|
163
|
+
│ windows_prefetch │ windows │ Dissect prefetch │
|
|
164
|
+
│ windows_registry │ windows │ Dissect registry hives │
|
|
165
|
+
│ windows_srudb │ windows │ Dissect SRUDB.dat │
|
|
166
|
+
│ windows_task │ windows │ Dissect scheduled tasks │
|
|
167
|
+
│ windows_usnj │ windows │ Dissect NTFS USN journal │
|
|
168
|
+
│ windows_webcache │ windows │ Dissect WebCacheV01.dat │
|
|
169
|
+
│ windows_wmi │ windows │ WMI event filter/consumer bindings │
|
|
170
|
+
│ windows_zone_identifier │ windows │ Dissect Zone.Identifier ADS │
|
|
171
|
+
╰────────────────────────────┴───────────────────────┴─────────────────────────────────────────────────╯
|
|
172
|
+
```
|
|
173
|
+
|
|
174
|
+
<br>
|
|
175
|
+
|
|
176
|
+
### List available dissectors
|
|
177
|
+
|
|
178
|
+
> [!TIP]
|
|
179
|
+
> Use `--fiter` to select dissectors by `tags` or by `slug`
|
|
180
|
+
|
|
181
|
+
```bash
|
|
182
|
+
plasma dissect --filter 'slug:linux_authlog,linux_wtmp_utmp' /tmp/demo/target /tmp/demo/output
|
|
183
|
+
```
|
|
184
|
+
```
|
|
185
|
+
[2025-09-17T10:06:13] INFO (plasma.cli): Plasma v1.0.0
|
|
186
|
+
INFO (plasma.dissectors.abc): file selected: linux_authlog (filepath=/tmp/demo/target/auth.log.4.gz)
|
|
187
|
+
INFO (plasma.dissectors.abc): file selected: linux_authlog (filepath=/tmp/demo/target/auth.log.3.gz)
|
|
188
|
+
INFO (plasma.dissectors.abc): file selected: linux_authlog (filepath=/tmp/demo/target/auth.log)
|
|
189
|
+
INFO (plasma.dissectors.abc): file selected: linux_authlog (filepath=/tmp/demo/target/auth.log.2.gz)
|
|
190
|
+
INFO (plasma.dissectors.abc): file selected: linux_authlog (filepath=/tmp/demo/target/auth.log.1)
|
|
191
|
+
INFO (plasma.dissectors.abc): dissect many start: linux_authlog (files=5)
|
|
192
|
+
INFO (plasma.dissectors.abc): dissection start: linux_authlog (filepath=/tmp/demo/target/auth.log.4.gz)
|
|
193
|
+
INFO (plasma.dissectors.abc): dissection complete: linux_authlog (records=2307, errors=0, time=0:00:00.012953)
|
|
194
|
+
INFO (plasma.dissectors.abc): dissection start: linux_authlog (filepath=/tmp/demo/target/auth.log.3.gz)
|
|
195
|
+
[2025-09-17T10:06:14] INFO (plasma.dissectors.abc): dissection complete: linux_authlog (records=2151, errors=0, time=0:00:00.012987)
|
|
196
|
+
INFO (plasma.dissectors.abc): dissection start: linux_authlog (filepath=/tmp/demo/target/auth.log)
|
|
197
|
+
INFO (plasma.dissectors.abc): dissection complete: linux_authlog (records=785, errors=0, time=0:00:00.004493)
|
|
198
|
+
INFO (plasma.dissectors.abc): dissection start: linux_authlog (filepath=/tmp/demo/target/auth.log.2.gz)
|
|
199
|
+
INFO (plasma.dissectors.abc): dissection complete: linux_authlog (records=1726, errors=0, time=0:00:00.010326)
|
|
200
|
+
INFO (plasma.dissectors.abc): dissection start: linux_authlog (filepath=/tmp/demo/target/auth.log.1)
|
|
201
|
+
INFO (plasma.dissectors.abc): dissection complete: linux_authlog (records=4580, errors=0, time=0:00:00.024124)
|
|
202
|
+
INFO (plasma.dissectors.abc): dissection many complete: linux_authlog (files=5, errors=0, time=0:00:00.069889)
|
|
203
|
+
INFO (plasma.dissectors.abc): file selected: linux_wtmp_utmp (filepath=/tmp/demo/target/wtmp)
|
|
204
|
+
INFO (plasma.dissectors.abc): dissect many start: linux_wtmp_utmp (files=1)
|
|
205
|
+
INFO (plasma.dissectors.abc): dissection start: linux_wtmp_utmp (filepath=/tmp/demo/target/wtmp)
|
|
206
|
+
INFO (plasma.dissectors.abc): dissection complete: linux_wtmp_utmp (records=1842, errors=0, time=0:00:00.106052)
|
|
207
|
+
INFO (plasma.dissectors.abc): dissection many complete: linux_wtmp_utmp (files=1, errors=0, time=0:00:00.107151)
|
|
208
|
+
╭─────────────────┬─────────────────────────────────────────┬───────────────────────────────────────────────╮
|
|
209
|
+
│ linux_authlog │ /tmp/demo/output/linux_authlog.csv.gz │ /tmp/demo/output/linux_authlog_error.csv.gz │
|
|
210
|
+
│ linux_wtmp_utmp │ /tmp/demo/output/linux_wtmp_utmp.csv.gz │ /tmp/demo/output/linux_wtmp_utmp_error.csv.gz │
|
|
211
|
+
╰─────────────────┴─────────────────────────────────────────┴───────────────────────────────────────────────╯
|
|
212
|
+
```
|
|
213
|
+
|
|
214
|
+
<br>
|
|
215
|
+
|
|
216
|
+
## License
|
|
217
|
+
|
|
218
|
+
Distributed under the [MIT License](LICENSE).
|
|
219
|
+
|
|
220
|
+
<br>
|
|
221
|
+
|
|
222
|
+
## Contributing
|
|
223
|
+
|
|
224
|
+
Contributions are welcome. See [CONTRIBUTING.md](https://github.com/CERT-EDF/plasma/blob/main/CONTRIBUTING.md).
|
|
225
|
+
|
|
226
|
+
## Past contributors (before open sourcing)
|
|
227
|
+
|
|
228
|
+
- [koromodako](https://github.com/koromodako)
|
|
229
|
+
- [SPToast](https://github.com/SPToast)
|
|
230
|
+
- [alex532h](https://github.com/alex532h)
|
|
231
|
+
|
|
232
|
+
<br>
|
|
233
|
+
|
|
234
|
+
## Security
|
|
235
|
+
|
|
236
|
+
To report a (suspected) security issue, see [SECURITY.md](https://github.com/CERT-EDF/plasma/blob/main/SECURITY.md).
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
edf_plasma_cli/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
2
|
+
edf_plasma_cli/__version__.py,sha256=9YOwLYSyRvoACgH0BeOxo1c69BZ4K5TV7anyKnWRL8U,712
|
|
3
|
+
edf_plasma_cli/main.py,sha256=DrF3WO0kEbA3DV8FuuhVA3GwBVqc5TJGrYqdm-gcngg,1828
|
|
4
|
+
edf_plasma_cli/command/__init__.py,sha256=AworCEwsfpGIYx3ErZ_b-3DV1qfWx1d4fWRJLl0D8Tg,343
|
|
5
|
+
edf_plasma_cli/command/abc.py,sha256=ng-8nwJ-_low08bXAHo7bDDRH_4bQslp9qZlhDD22tQ,985
|
|
6
|
+
edf_plasma_cli/command/dissect.py,sha256=goAIhAS4thkUDmm0PSyJC_38Vkg07QzO8FSxYA1BHYI,4593
|
|
7
|
+
edf_plasma_cli/command/list.py,sha256=HiHRmPEJIzcxQCoIlg2224EiehZZWbKFqJPmnuGiyJg,670
|
|
8
|
+
edf_plasma_cli-1.0.0.dist-info/licenses/LICENSE,sha256=u7ksjywIwisybgov0N66RBD_mV-gct9GTm52932s2QU,1064
|
|
9
|
+
edf_plasma_cli-1.0.0.dist-info/METADATA,sha256=HK-1I0GskFo67QMd_bcVwg29JZ38Kzzw350E0eJ6sEA,18156
|
|
10
|
+
edf_plasma_cli-1.0.0.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
|
11
|
+
edf_plasma_cli-1.0.0.dist-info/entry_points.txt,sha256=444GwzUer_wKIa0u3qTSu9ojCRQBVSGuOf3AOuiVSOQ,51
|
|
12
|
+
edf_plasma_cli-1.0.0.dist-info/top_level.txt,sha256=9NqR1kcilfFnRDLGSq9b8gJk-lIxmlea43S0dMGzLQQ,15
|
|
13
|
+
edf_plasma_cli-1.0.0.dist-info/RECORD,,
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2024 CERT-EDF
|
|
4
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
5
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
6
|
+
in the Software without restriction, including without limitation the rights
|
|
7
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
8
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
9
|
+
furnished to do so, subject to the following conditions:
|
|
10
|
+
|
|
11
|
+
The above copyright notice and this permission notice shall be included in all
|
|
12
|
+
copies or substantial portions of the Software.
|
|
13
|
+
|
|
14
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
15
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
16
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
17
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
18
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
19
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
20
|
+
SOFTWARE.
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
edf_plasma_cli
|