Skip to content

Commit b504da7

Browse files
authored
Skip nested objects of Velociraptor records (#1480)
1 parent a266fc5 commit b504da7

File tree

2 files changed

+27
-7
lines changed

2 files changed

+27
-7
lines changed

dissect/target/plugins/apps/edr/velociraptor.py

Lines changed: 22 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88

99
from dissect.target.exceptions import UnsupportedPluginError
1010
from dissect.target.helpers.record import DynamicDescriptor, TargetRecordDescriptor
11-
from dissect.target.plugin import Plugin, export
11+
from dissect.target.plugin import Plugin, arg, export
1212

1313
if TYPE_CHECKING:
1414
from collections.abc import Iterator
@@ -22,9 +22,10 @@
2222

2323

2424
class VelociraptorRecordBuilder:
25-
def __init__(self, artifact_name: str):
25+
def __init__(self, artifact_name: str, extract_nested: bool):
2626
self._create_event_descriptor = lru_cache(4096)(self._create_event_descriptor)
2727
self.record_name = f"velociraptor/{artifact_name}"
28+
self.extract_nested = extract_nested
2829

2930
def build(self, object: dict, target: Target) -> TargetRecordDescriptor:
3031
"""Builds a Velociraptor record."""
@@ -52,8 +53,12 @@ def build(self, object: dict, target: Target) -> TargetRecordDescriptor:
5253
elif isinstance(value, str):
5354
record_type = "string"
5455
elif isinstance(value, dict):
55-
record_type = "record"
56-
value = self.build(value, target)
56+
if self.extract_nested:
57+
record_type = "record"
58+
value = self.build(value, target)
59+
else:
60+
# Skip nested objects that contain additional metadata
61+
continue
5762
else:
5863
record_type = "dynamic"
5964

@@ -82,9 +87,20 @@ def check_compatible(self) -> None:
8287
raise UnsupportedPluginError("No Velociraptor artifacts found")
8388

8489
@export(record=DynamicDescriptor(["datetime"]))
85-
def results(self) -> Iterator[Record]:
90+
@arg(
91+
"--extract-nested",
92+
action="store_true",
93+
help="extracts JSON objects from the artifacts",
94+
)
95+
def results(
96+
self,
97+
extract_nested: bool = False,
98+
) -> Iterator[Record]:
8699
"""Return Rapid7 Velociraptor artifacts.
87100
101+
By default JSON objects are not extracted from the artifacts,
102+
this can be done with the argument ``--extract-nested``.
103+
88104
References:
89105
- https://docs.velociraptor.app/docs/vql/artifacts/
90106
"""
@@ -93,7 +109,7 @@ def results(self) -> Iterator[Record]:
93109
artifact_name = (
94110
urllib.parse.unquote(artifact.name.removesuffix(".json")).split("/")[0].lower().replace(".", "_")
95111
)
96-
record_builder = VelociraptorRecordBuilder(artifact_name)
112+
record_builder = VelociraptorRecordBuilder(artifact_name, extract_nested=extract_nested)
97113

98114
for line in artifact.open("rt"):
99115
if not (line := line.strip()):

tests/plugins/apps/edr/test_velociraptor.py

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,8 +35,10 @@ def test_windows_velociraptor(mock_velociraptor_dir: Path, target_win: Target) -
3535
target_win.add_plugin(VelociraptorPlugin)
3636

3737
results = list(target_win.velociraptor())
38+
results_extract = list(target_win.velociraptor.results(extract_nested=True))
3839

3940
record = results[0]
41+
record_extract = results_extract[0]
4042

4143
assert record.name == "Microsoft.SharePoint.exe"
4244
assert record.pebbaseaddress == "0x295000"
@@ -48,4 +50,6 @@ def test_windows_velociraptor(mock_velociraptor_dir: Path, target_win: Target) -
4850
assert record.commandline == "/silentConfig"
4951
assert record.currentdirectory == "C:\\Windows\\system32\\"
5052
assert record._desc.name == "velociraptor/windows_memory_processinfo"
51-
assert record.env.allusersprofile == "C:\\ProgramData"
53+
assert not hasattr(record, "env")
54+
55+
assert record_extract.env.allusersprofile == "C:\\ProgramData"

0 commit comments

Comments
 (0)