(g)ULP!
Loading...
Searching...
No Matches
Plugin Class Reference
Inheritance diagram for Plugin:
Collaboration diagram for Plugin:

Public Member Functions

str desc (self)
 
GulpRequestStatus ingest (self, str index, str req_id, int client_id, int operation_id, str context, str|list[dict] source, str ws_id, GulpPluginParams plugin_params=None, GulpIngestionFilter flt=None, **kwargs)
 
str name (self)
 
list[GulpDocument] record_to_gulp_document (self, int operation_id, int client_id, str context, str source, TmpIngestStats fs, any record, int record_idx, GulpMapping custom_mapping=None, dict index_type_mapping=None, str plugin=None, GulpPluginParams plugin_params=None, **kwargs)
 
GulpPluginType type (self)
 
str version (self)
 
- Public Member Functions inherited from PluginBase
None __init__ (self, str path, AsyncEngine collab=None, AsyncElasticsearch elastic=None, **kwargs)
 
None cleanup (self)
 
list[str] depends_on (self)
 
str event_type_field (self)
 
str get_unmapped_field_name (self, str field)
 
tuple[dict, GulpMapping] ingest_plugin_initialize (self, str index, str|dict source, bool skip_mapping=False, ProcessingPipeline pipeline=None, str mapping_file=None, str mapping_id=None, GulpPluginParams plugin_params=None)
 
bool internal (self)
 
logging.Logger logger (cls)
 
list[GulpPluginOptionoptions (self)
 
ProcessingPipeline pipeline (self, GulpPluginParams plugin_params=None, **kwargs)
 
dict run_command (self, GulpPluginParams p)
 
ProcessingPipeline sigma_plugin_initialize (self, ProcessingPipeline pipeline=None, str mapping_file=None, str mapping_id=None, str product=None, GulpPluginParams plugin_params=None)
 
list[str] tags (self)
 

Protected Member Functions

dict _map_evt_code (self, str ev_code)
 
GulpLogLevel _normalize_loglevel (self, int|str l)
 
- Protected Member Functions inherited from PluginBase
list[GulpDocument] _build_gulpdocuments (self, list[FieldMappingEntry] fme, int idx, int operation_id, str context, str plugin, int client_id, str raw_event, str original_id, str src_file, int timestamp=None, int timestamp_nsec=None, str event_code=None, list[str] cat=None, int duration_nsec=0, GulpLogLevel gulp_log_level=None, str original_log_level=None, bool remove_raw_event=False, **kwargs)
 
list[dict] _build_ingestion_chunk_for_ws (self, list[dict] docs, GulpIngestionFilter flt=None)
 
list[GulpDocument] _call_record_to_gulp_document_funcs (self, int operation_id, int client_id, str context, str source, TmpIngestStats fs, any record, int record_idx, GulpMapping custom_mapping=None, dict index_type_mapping=None, str plugin=None, GulpPluginParams plugin_params=None, Callable record_to_gulp_document_fun=None, **kwargs)
 
GulpRequestStatus _finish_ingestion (self, str index, str|dict source, str req_id, int client_id, str ws_id, TmpIngestStats fs, GulpIngestionFilter flt=None)
 
TmpIngestStats _flush_buffer (self, str index, TmpIngestStats fs, str ws_id, str req_id, GulpIngestionFilter flt=None, bool wait_for_refresh=False)
 
TmpIngestStats _ingest_record (self, str index, GulpDocument|dict doc, TmpIngestStats fs, str ws_id, str req_id, GulpIngestionFilter flt=None, bool flush_enabled=True, **kwargs)
 
list[FieldMappingEntry] _map_source_key (self, GulpPluginParams plugin_params, GulpMapping custom_mapping, str source_key, Any v, dict index_type_mapping=None, bool ignore_custom_mapping=False, **kwargs)
 
TmpIngestStats _parser_failed (self, TmpIngestStats fs, str|dict source, Exception|str ex)
 
tuple[GulpMapping, GulpPluginParams_process_plugin_params (self, GulpMapping custom_mapping, GulpPluginParams plugin_params=None)
 
tuple[TmpIngestStats, bool] _process_record (self, str index, any record, int record_idx, Callable my_record_to_gulp_document_fun, str ws_id, str req_id, int operation_id, int client_id, str context, str source, TmpIngestStats fs, GulpMapping custom_mapping=None, dict index_type_mapping=None, str plugin=None, GulpPluginParams plugin_params=None, GulpIngestionFilter flt=None, **kwargs)
 
TmpIngestStats _record_failed (self, TmpIngestStats fs, any entry, str|dict source, Exception|str ex)
 
any _type_checks (self, any v, str k, dict index_type_mapping)
 

Additional Inherited Members

- Public Attributes inherited from PluginBase
list buffer = []
 
str client_id = None
 
 collab = collab
 
tuple context = (None,)
 
 elastic = elastic
 
str index = None
 
str operation_id = None
 
 path = path
 
str req_id = None
 
str ws_id = None
 

Detailed Description

windows evtx log file processor.

Definition at line 23 of file win_evtx.py.

Member Function Documentation

◆ _map_evt_code()

dict _map_evt_code ( self,
str ev_code )
protected
better map an event code to fields

Args:
    ev_code (str): The event code to be converted.
Returns:
    dict: A dictionary with 'event.category' and 'event.type'

Definition at line 64 of file win_evtx.py.

64 def _map_evt_code(self, ev_code: str) -> dict:
65 """
66 better map an event code to fields
67
68 Args:
69 ev_code (str): The event code to be converted.
70 Returns:
71 dict: A dictionary with 'event.category' and 'event.type'
72 """
73 codes = {
74 "100": {"event.category": ["package"], "event.type": ["start"]},
75 "106": {"event.category": ["package"], "event.type": ["install"]},
76 "140": {"event.category": ["package"], "event.type": ["change"]},
77 "141": {"event.category": ["package"], "event.type": ["delete"]},
78 "1006": {"event.category": ["host"], "event.type": ["change"]},
79 "4624": { # eventid
80 "event.category": ["authentication"],
81 "event.type": ["start"],
82 },
83 "4672": {
84 "event.category": ["authentication"],
85 },
86 "4648": {
87 "event.category": ["authentication"],
88 },
89 "4798": {"event.category": ["iam"]},
90 "4799": {"event.category": ["iam"]},
91 "5379": {"event.category": ["iam"], "event.type": ["access"]},
92 "5857": {"event.category": ["process"], "event.type": ["access"]},
93 "5858": {"event.category": ["process"], "event.type": ["error"]},
94 "5859": {"event.category": ["process"], "event.type": ["change"]},
95 "5860": {"event.category": ["process"], "event.type": ["change"]},
96 "5861": {"event.category": ["process"], "event.type": ["change"]},
97 "7036": {
98 "event.category": ["package"],
99 "event.type": ["change"],
100 },
101 "7040": {
102 "event.category": ["package"],
103 "event.type": ["change"],
104 },
105 "7045": {
106 "event.category": ["package"],
107 "event.type": ["install"],
108 },
109 "13002": {"event.type": ["change"]},
110 }
111 if ev_code in codes:
112 return codes[ev_code]
113
114 return None
115
Here is the caller graph for this function:

◆ _normalize_loglevel()

GulpLogLevel _normalize_loglevel ( self,
int | str l )
protected

Definition at line 28 of file win_evtx.py.

28 def _normalize_loglevel(self, l: int | str) -> GulpLogLevel:
29 ll = int(l)
30 if ll == 0:
31 return GulpLogLevel.ALWAYS
32 if ll == 1:
33 return GulpLogLevel.CRITICAL
34 if ll == 2:
35 return GulpLogLevel.ERROR
36 if ll == 3:
37 return GulpLogLevel.WARNING
38 if ll == 4:
39 return GulpLogLevel.INFO
40 if ll == 5:
41 return GulpLogLevel.VERBOSE
42 if ll > 5:
43 # maps directly to GulpLogLevel.CUSTOM_n
44 try:
45 return GulpLogLevel(ll)
46 except:
47 return GulpLogLevel.UNEXPECTED
48
49 # wtf ?!
50 return GulpLogLevel.VERBOSE
51
Here is the caller graph for this function:

◆ desc()

str desc ( self)
Returns a description of the plugin.

Reimplemented from PluginBase.

Definition at line 55 of file win_evtx.py.

55 def desc(self) -> str:
56 return "Windows EVTX log file processor."
57
Here is the caller graph for this function:

◆ ingest()

GulpRequestStatus ingest ( self,
str index,
str req_id,
int client_id,
int operation_id,
str context,
str | list[dict] source,
str ws_id,
GulpPluginParams plugin_params = None,
GulpIngestionFilter flt = None,
** kwargs )
Ingests a file using the plugin.

NOTE: implementers should call super().ingest() in their implementation.
NOTE: this function *SHOULD NOT* raise exceptions

Args:
    index (str): name of the elasticsearch index to ingest the document into.
    req_id (str): The request ID related to this ingestion (must exist on the collab db).
    client_id (int): The client ID performing the ingestion.
    operation_id (int): The operation ID related to this ingestion.
    context (str): Context related to this ingestion.
    source (str|list[dict]): The path to the file to ingest, or a list of events dicts.
    ws_id (str): The websocket ID
    plugin_params (GulpPluginParams): additional parameters to pass to the ingestion function. Defaults to None.
    flt (GulpIngestionFilter, optional): filter to apply to this ingestion, if any. Defaults to None.
    kwargs: additional arguments if any

Reimplemented from PluginBase.

Definition at line 288 of file win_evtx.py.

300 ) -> GulpRequestStatus:
301
302 await super().ingest(
303 index=index,
304 req_id=req_id,
305 client_id=client_id,
306 operation_id=operation_id,
307 context=context,
308 source=source,
309 ws_id=ws_id,
310 plugin_params=plugin_params,
311 flt=flt,
312 **kwargs,
313 )
314
315 parser = None
316
317 fs = TmpIngestStats(source)
318
319 # initialize mapping
320 try:
321 index_type_mapping, custom_mapping = await self.ingest_plugin_initialize(
322 index,
323 source=source,
324 pipeline=ecs_windows(),
325 mapping_file="windows.json",
326 plugin_params=plugin_params,
327 )
328 # Plugin.logger().debug("win_mappings: %s" % (win_mapping))
329 except Exception as ex:
330 fs=self._parser_failed(fs, source, ex)
331 return await self._finish_ingestion(index, source, req_id, client_id, ws_id, fs=fs, flt=flt)
332
333 # init parser
334 try:
335 parser = PyEvtxParser(source)
336 except Exception as ex:
337 # cannot parse this file at all
338 fs=self._parser_failed(fs, source, ex)
339 return await self._finish_ingestion(index, source, req_id, client_id, ws_id, fs=fs, flt=flt)
340
341 ev_idx = 0
342
343 try:
344 for rr in parser.records():
345 # process (ingest + update stats)
346 try:
347 fs, must_break = await self._process_record(
348 index,
349 rr,
350 ev_idx,
351 self.record_to_gulp_document,
352 ws_id,
353 req_id,
354 operation_id,
355 client_id,
356 context,
357 source,
358 fs,
359 custom_mapping=custom_mapping,
360 index_type_mapping=index_type_mapping,
361 plugin_params=plugin_params,
362 flt=flt,
363 **kwargs,
364 )
365
366 ev_idx += 1
367 if must_break:
368 break
369
370 except Exception as ex:
371 fs=self._record_failed(fs, rr, source, ex)
372
373 except Exception as ex:
374 fs=self._parser_failed(fs, source, ex)
375
376 # done
377 return await self._finish_ingestion(index, source, req_id, client_id, ws_id, fs=fs, flt=flt)
Here is the call graph for this function:
Here is the caller graph for this function:

◆ name()

str name ( self)
Returns the name of the plugin.

Reimplemented from PluginBase.

Definition at line 58 of file win_evtx.py.

58 def name(self) -> str:
59 return "win_evtx"
60
Here is the caller graph for this function:

◆ record_to_gulp_document()

list[GulpDocument] record_to_gulp_document ( self,
int operation_id,
int client_id,
str context,
str source,
TmpIngestStats fs,
any record,
int record_idx,
GulpMapping custom_mapping = None,
dict index_type_mapping = None,
str plugin = None,
GulpPluginParams plugin_params = None,
** kwargs )
Converts a record to one or more GulpDocument objects based on the provided index mappings.

Args:
    operation_id (int): The operation ID associated with the record.
    client_id (int): The client ID associated with the record.
    context (str): The context associated with the record.
    source (str): The source of the record (source file name or path, usually).
    fs (TmpIngestStats): The temporary ingestion statistics (may be updated on return).
    record (any): record to convert, plugin dependent format: note that here stacked plugins receives a list of GulpDocument objects instead (since the original record may generate one or more documents).
    record_idx (int): The index of the record in source.
    custom_mapping (GulpMapping, optional): The custom mapping to use for the conversion. Defaults to None.
    index_type_mapping (dict, optional): elastic search index type mappings { "ecs_field": "type", ... }. Defaults to None.
    plugin (str, optional): "agent.type" to be set in the GulpDocument. Defaults to None.
    plugin_params (GulpPluginParams, optional): The plugin parameters to use, if any. Defaults to None.
    extra (dict, optional): Additional fields to add to the GulpDocument (after applying mapping). Defaults to None.
    **kwargs: Additional keyword arguments:

Returns:
    list[GulDocument]: The converted GulpDocument objects or None if an exception occurred (fs is updated then).

Raises:
    NotImplementedError: This method is not implemented yet.

Reimplemented from PluginBase.

Definition at line 116 of file win_evtx.py.

130 ) -> list[GulpDocument]:
131 # process record
132 # self.logger().debug(record)
133 evt_str: str = record["data"].encode()
134
135 data_elem = None
136 data_elem = etree.fromstring(evt_str)
137
138 # check if we should ignore mapping
139 ignore_mapping = False
140 if plugin_params is not None and plugin_params.ignore_mapping_ingest:
141 ignore_mapping = True
142
143 cat_tree = data_elem[0]
144
145 # extra mapping from event code
146 fme: list[FieldMappingEntry] = []
147 entry = FieldMappingEntry(result={})
148
149 evt_code = str(muty.xml.child_node_text(cat_tree, "EventID"))
150 try:
151 evt_channel = str(muty.xml.child_node_text(cat_tree, "Channel"))
152 except:
153 evt_channel = str(muty.xml.strip_namespace(cat_tree.tag))
154
155 entry.result["winlog.channel"] = evt_channel
156 try:
157 evt_provider = str(muty.xml.child_attrib(cat_tree, "Provider", "Name"))
158 entry.result["event.provider"] = evt_provider
159 except:
160 evt_provider = None
161
162 d = self._map_evt_code(evt_code)
163 if d is not None:
164 entry.result.update(d)
165
166 evt_id = muty.xml.child_node_text(cat_tree, "EventRecordID")
167 original_log_level: str = muty.xml.child_node_text(cat_tree, "Level")
168 gulp_log_level = self._normalize_loglevel(
169 muty.xml.child_node_text(cat_tree, "Level")
170 )
171
172 # evtx timestamp is always UTC string, i.e.: '2021-11-19 16:53:03.836551 UTC'
173 time_str = record["timestamp"]
174 time_nanosec = muty.time.string_to_epoch_nsec(time_str)
175 time_msec = muty.time.nanos_to_millis(time_nanosec)
176 # Plugin.logger().debug('%s - %d' % (time_str, time_msec))
177
178 # raw event as text
179 raw_text = str(record["data"])
180
181 if ignore_mapping:
182 # persist these into the event
183 entry.result["EventID"] = evt_code
184 entry.result["Level"] = original_log_level
185 entry.result["Provider"] = evt_provider
186 entry.result["EventRecordID"] = evt_id
187 entry.result["Channel"] = evt_channel
188
189 # append this entry
190 fme.append(entry)
191
192 e_tree: etree.ElementTree = etree.ElementTree(data_elem)
193
194 # self.logger().debug("e_tree: %s" % (e_tree))
195
196 for e in e_tree.iter():
197 e.tag = muty.xml.strip_namespace(e.tag)
198
199 # these are already processed
200 if e.tag in ["EventID", "EventRecordID", "Level", "Provider"]:
201 continue
202
203 # self.logger().debug(
204 # "found e_tag=%s, value=%s" % (e.tag, e.text)
205 # )
206
207 # Check XML tag name and set extra mapping
208 entries = self._map_source_key(
209 plugin_params,
210 custom_mapping,
211 e.tag,
212 e.text,
213 index_type_mapping=index_type_mapping,
214 **kwargs,
215 )
216 for entry in entries:
217 fme.append(entry)
218
219 # add attributes as more extra data
220 for attr_k, attr_v in e.attrib.items():
221 kk = "gulp.winevtx.xml.attr.%s" % (attr_k)
222 inner_entries = self._map_source_key(
223 plugin_params,
224 custom_mapping,
225 kk,
226 attr_v,
227 index_type_mapping=index_type_mapping,
228 **kwargs,
229 )
230 for entry in inner_entries:
231 fme.append(entry)
232
233 # Check attributes and set extra mapping
234 for attr_k, attr_v in e.attrib.items():
235 # Plugin.logger().info("name: %s value: %s" % (attr_k, attr_v))
236 # self.logger().debug("processing attr_v=%s, value=%s" % (attr_v, e.text))
237 entries = self._map_source_key(
238 plugin_params,
239 custom_mapping,
240 attr_v,
241 e.text,
242 index_type_mapping=index_type_mapping,
243 **kwargs,
244 )
245 for entry in entries:
246 fme.append(entry)
247
248 if e.text is None:
249 # no more processing
250 continue
251
252 value = e.text.strip()
253 if attr_v is not None:
254 # prefer attr_v
255 value = attr_v.strip()
256
257 entries = self._map_source_key(
258 plugin_params,
259 custom_mapping,
260 attr_k,
261 value,
262 index_type_mapping=index_type_mapping,
263 **kwargs,
264 )
265 for entry in entries:
266 fme.append(entry)
267
268
269 # finally create documents
270 docs = self._build_gulpdocuments(
271 fme,
272 idx=record_idx,
273 operation_id=operation_id,
274 context=context,
275 plugin=self.name(),
276 client_id=client_id,
277 raw_event=raw_text,
278 original_id=evt_id,
279 src_file=os.path.basename(source),
280 timestamp=time_msec,
281 timestamp_nsec=time_nanosec,
282 event_code=evt_code,
283 gulp_log_level=gulp_log_level,
284 original_log_level=original_log_level
285 )
286 return docs
287
Here is the call graph for this function:
Here is the caller graph for this function:

◆ type()

GulpPluginType type ( self)
Returns the plugin type.

Reimplemented from PluginBase.

Definition at line 52 of file win_evtx.py.

52 def type(self) -> GulpPluginType:
53 return GulpPluginType.INGESTION
54

◆ version()

str version ( self)
Returns plugin version.

Reimplemented from PluginBase.

Definition at line 61 of file win_evtx.py.

61 def version(self) -> str:
62 return "1.0"
63

The documentation for this class was generated from the following file: