Added section 3.2 and 3.3
This commit is contained in:
parent
65e124d06c
commit
42c00d7ad0
|
|
@ -1,3 +1,7 @@
|
||||||
|
# Exclude locally built docs
|
||||||
|
docs/
|
||||||
|
.vscode/
|
||||||
|
|
||||||
# Byte-compiled / optimized / DLL files
|
# Byte-compiled / optimized / DLL files
|
||||||
__pycache__/
|
__pycache__/
|
||||||
*.py[cod]
|
*.py[cod]
|
||||||
|
|
|
||||||
|
|
@ -5,7 +5,7 @@ pages:
|
||||||
- apk --no-cache add make gcc musl-dev git
|
- apk --no-cache add make gcc musl-dev git
|
||||||
- pip install -r dev_requirements.txt
|
- pip install -r dev_requirements.txt
|
||||||
- pip install -r requirements.txt
|
- pip install -r requirements.txt
|
||||||
- cd docs
|
- cd docssrc
|
||||||
- make html
|
- make html
|
||||||
- mv _build/html ../public
|
- mv _build/html ../public
|
||||||
artifacts:
|
artifacts:
|
||||||
|
|
|
||||||
|
|
@ -9,6 +9,16 @@ Section 3.1 - Opening an Event Log
|
||||||
.. automodule:: sections.section_03.open_evtx
|
.. automodule:: sections.section_03.open_evtx
|
||||||
:members:
|
:members:
|
||||||
|
|
||||||
|
Section 3.2 - Iterate over events
|
||||||
|
---------------------------------
|
||||||
|
.. automodule:: sections.section_03.get_events
|
||||||
|
:members:
|
||||||
|
|
||||||
|
Section 3.3 - Filter event data
|
||||||
|
-------------------------------
|
||||||
|
.. automodule:: sections.section_03.filter_events_json
|
||||||
|
:members:
|
||||||
|
|
||||||
Indices and tables
|
Indices and tables
|
||||||
--------------------------------
|
--------------------------------
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,2 +1,3 @@
|
||||||
-e git+https://github.com/msuhanov/yarp@1.0.28#egg=yarp
|
-e git+https://github.com/msuhanov/yarp@1.0.28#egg=yarp
|
||||||
python-evtx==0.6.1
|
python-evtx==0.6.1
|
||||||
|
lxml==4.5.2
|
||||||
|
|
|
||||||
|
|
@ -61,12 +61,9 @@ Section 3 - Event Logs
|
||||||
|
|
||||||
* Using python-evtx
|
* Using python-evtx
|
||||||
- Opening evtx files
|
- Opening evtx files
|
||||||
|
- Iterating over events
|
||||||
* Parsing Logins (with types, levels, privs)
|
* Parsing Logins (with types, levels, privs)
|
||||||
- Parse out the commonly investigated 4624/4672 events
|
- Parse out the commonly investigated 4624/4672 events
|
||||||
* Parsing Logouts (durations)
|
|
||||||
- Parse 4624/4634 events to get information on user sessions
|
|
||||||
* Parsing Powershell decoding
|
|
||||||
- Reassemble PowerShell strings in events and decode commands
|
|
||||||
|
|
||||||
Section 4 - Text logs
|
Section 4 - Text logs
|
||||||
---------------------
|
---------------------
|
||||||
|
|
|
||||||
|
|
@ -20,17 +20,49 @@ Open Windows Event Logs (EVTX)
|
||||||
==============================
|
==============================
|
||||||
|
|
||||||
This function shows an example of opening an EVTX file and parsing out several
|
This function shows an example of opening an EVTX file and parsing out several
|
||||||
common parameters about the file.
|
header metadata parameters about the file.
|
||||||
|
|
||||||
.. literalinclude:: ../sections/section_03/open_evtx.py
|
.. literalinclude:: ../sections/section_03/open_evtx.py
|
||||||
:pyobject: open_evtx
|
:pyobject: open_evtx
|
||||||
|
|
||||||
|
Iterate over record XML data (EVTX)
|
||||||
|
===================================
|
||||||
|
|
||||||
|
In this function, we iterate over the records within an EVTX file and expose
|
||||||
|
the raw XML. This leverages a yield generator for
|
||||||
|
low impact on resources.
|
||||||
|
|
||||||
|
Additionally, if you would like to parse the XML, or interact with the child
|
||||||
|
elements, you can enable it by assigning the `parse_xml` parameter as True,
|
||||||
|
which will then call the ``.lxml()`` method on the individual event record.
|
||||||
|
This requires the installation of the lxml Library, as it returns a lxml.etree
|
||||||
|
object that you can interact with.
|
||||||
|
|
||||||
|
.. literalinclude:: ../sections/section_03/open_evtx.py
|
||||||
|
:pyobject: get_events
|
||||||
|
|
||||||
|
Filtering records within events logs
|
||||||
|
====================================
|
||||||
|
|
||||||
|
Now that we have :func:`get_events()`, we can begin to perform operations on
|
||||||
|
the newly accessible data. In this function, we extract information from the
|
||||||
|
LXML object, and use that to filter results based on Event ID and other fields
|
||||||
|
within the results. You can easily extend this to support other fields,
|
||||||
|
filters, and return values. Some examples include:
|
||||||
|
|
||||||
|
- extracting all login and logoff events, with their session identifiers,
|
||||||
|
then calculating the session durations
|
||||||
|
- Identify PowerShell events and expose arguments for further processing
|
||||||
|
(ie. Base64 decoding, shellcode analysis)
|
||||||
|
|
||||||
|
.. literalinclude:: ../sections/section_03/open_evtx.py
|
||||||
|
:pyobject: filter_events_json
|
||||||
|
|
||||||
Docstring References
|
Docstring References
|
||||||
====================
|
====================
|
||||||
"""
|
"""
|
||||||
|
import json
|
||||||
from collections import OrderedDict
|
from collections import OrderedDict
|
||||||
import os
|
|
||||||
import Evtx.Evtx as evtx
|
import Evtx.Evtx as evtx
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -72,6 +104,15 @@ def open_evtx(input_file):
|
||||||
|
|
||||||
Arguments:
|
Arguments:
|
||||||
input_file (str): Path to evtx file to open
|
input_file (str): Path to evtx file to open
|
||||||
|
|
||||||
|
Examples:
|
||||||
|
>>> open_evtx("System.evtx")
|
||||||
|
File version (major): 3
|
||||||
|
File version (minor): 1
|
||||||
|
File is ditry: True
|
||||||
|
File is full: False
|
||||||
|
Next record number: 10549
|
||||||
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
with evtx.Evtx(input_file) as open_log:
|
with evtx.Evtx(input_file) as open_log:
|
||||||
|
|
@ -87,6 +128,78 @@ def open_evtx(input_file):
|
||||||
for key, value in properties.items():
|
for key, value in properties.items():
|
||||||
print(f"{value}: {getattr(header, key)()}")
|
print(f"{value}: {getattr(header, key)()}")
|
||||||
|
|
||||||
|
|
||||||
|
def get_events(input_file, parse_xml=False):
|
||||||
|
"""Opens a Windows Event Log and returns XML information from
|
||||||
|
the event record.
|
||||||
|
|
||||||
|
Arguments:
|
||||||
|
input_file (str): Path to evtx file to open
|
||||||
|
parse_xml (bool): If True, return an lxml object, otherwise a string
|
||||||
|
|
||||||
|
Yields:
|
||||||
|
(generator): XML information in object or string format
|
||||||
|
|
||||||
|
Examples:
|
||||||
|
>>> for event_xml in enumerate(get_events("System.evtx")):
|
||||||
|
>>> print(event_xml)
|
||||||
|
|
||||||
|
"""
|
||||||
|
with evtx.Evtx(input_file) as event_log:
|
||||||
|
for record in event_log.records():
|
||||||
|
if parse_xml:
|
||||||
|
yield record.lxml()
|
||||||
|
else:
|
||||||
|
yield record.xml()
|
||||||
|
|
||||||
|
|
||||||
|
def filter_events_json(event_data, event_ids, fields=None):
|
||||||
|
"""Provide events where the event id is found within the provided list
|
||||||
|
of event ids. If found, it will return a JSON formatted object per event.
|
||||||
|
|
||||||
|
If a list of fields are provided, it will filter the resulting JSON event
|
||||||
|
object to contain only those fields.
|
||||||
|
|
||||||
|
Arguments:
|
||||||
|
event_data (genertor): Iterable containing event data as XML. Preferably
|
||||||
|
the result of the :func:`get_events()` method.
|
||||||
|
event_ids (list): A list of event identifiers. Each element should be a
|
||||||
|
string value, even though the identifier is an integer.
|
||||||
|
fields (list): Collection of fields from the XML data to include in the
|
||||||
|
JSON output. Only supports top-level fields.
|
||||||
|
|
||||||
|
Yields:
|
||||||
|
(dict): A dictionary containing the filtered record information
|
||||||
|
|
||||||
|
Example:
|
||||||
|
|
||||||
|
>>> filtered_logins = filter_events_json(
|
||||||
|
>>> get_events("System.evtx", parse_xml=True),
|
||||||
|
>>> event_ids=['4624', '4625'],
|
||||||
|
>>> fields=["SubjectUserName", "SubjectUserSid",
|
||||||
|
>>> "SubjectDomainName", "TargetUserName", "TargetUserSid",
|
||||||
|
>>> "TargetDomainName", "WorkstationName", "IpAddress",
|
||||||
|
>>> "IpPort", "ProcessName"]
|
||||||
|
>>> )
|
||||||
|
>>> for filtered_login in filtered_logins:
|
||||||
|
>>> print(json.dumps(filtered_login, indent=2))
|
||||||
|
|
||||||
|
"""
|
||||||
|
for evt in event_data:
|
||||||
|
system_tag = evt.find("System", evt.nsmap)
|
||||||
|
event_id = system_tag.find("EventID", evt.nsmap)
|
||||||
|
if event_id.text in event_ids:
|
||||||
|
event_data = evt.find("EventData", evt.nsmap)
|
||||||
|
json_data = {}
|
||||||
|
for data in event_data.getchildren():
|
||||||
|
if not fields or data.attrib['Name'] in fields:
|
||||||
|
# If we don't have a specified field filter list, print all
|
||||||
|
# Otherwise filter for only those fields within the list
|
||||||
|
json_data[data.attrib['Name']] = data.text
|
||||||
|
|
||||||
|
yield json_data
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
import argparse
|
import argparse
|
||||||
parser = argparse.ArgumentParser(
|
parser = argparse.ArgumentParser(
|
||||||
|
|
@ -97,4 +210,21 @@ if __name__ == "__main__":
|
||||||
parser.add_argument('EVTX_FILE', help="EVTX file to read")
|
parser.add_argument('EVTX_FILE', help="EVTX file to read")
|
||||||
args = parser.parse_args()
|
args = parser.parse_args()
|
||||||
|
|
||||||
|
print("EVTX File Header Information")
|
||||||
open_evtx(args.EVTX_FILE)
|
open_evtx(args.EVTX_FILE)
|
||||||
|
print("EVTX File records")
|
||||||
|
for count, event in enumerate(get_events(args.EVTX_FILE)):
|
||||||
|
if count >= 3:
|
||||||
|
break
|
||||||
|
print(event)
|
||||||
|
|
||||||
|
print("Filter for Login events")
|
||||||
|
logins = filter_events_json(
|
||||||
|
get_events(args.EVTX_FILE, parse_xml=True),
|
||||||
|
event_ids=['4624'],
|
||||||
|
fields=["SubjectUserName", "SubjectUserSid", "SubjectDomainName",
|
||||||
|
"TargetUserName", "TargetUserSid", "TargetDomainName",
|
||||||
|
"WorkstationName", "IpAddress", "IpPort", "ProcessName"]
|
||||||
|
)
|
||||||
|
for login in logins:
|
||||||
|
print(json.dumps(login, indent=2))
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue