The best way to understand these concepts is to actually see them in practice.
In this demo, we'll install python‑evtx using pip.
Then we will parse the Windows security log of a machine that was compromised.
To get the best experience,
we'll actually detect indicators of malicious activity.
First, by filtering for a specific EventID.
And then, we'll attempt to detect suspicious parent‑child process relationships.
We'll kick off this demo by highlighting the python‑evtx module.
This is a very important component that allows us to parse the Windows log file.
This module has been developed by William Ballenthin, and it's quite popular.
In this GitHub page, you'll find some more details on this module,
as well as the installation instructions.
Another important aspect is that this repository also
contains a folder with script examples,
which you can use directly to parse Windows event logs.
Now it's time to return to VS Code.
In the terminal, I will use pip to install this module.
But first, it's always good practice to upgrade pip,
and this can be achieved using pip install ‑‑upgrade pip.
After this is done, now I can install python‑evtx.
And we can see this has been done successfully.
There is another package that is required, which is lxml.
This is used to parse the XML log entries extracted from the event log.
This can also be installed using pip.
Now we can start implementing this in our script.
First,
I'll create a new function to open the Evtx file and
provide the path on disk as a parameter.
Next, up at the top,
I will import the Evtx module so I can have access to
the file‑parsing functionality.
Rather than using open, a python‑evtx function is used,
and the rest of the script now becomes similar to the
one opening regular text files, with two exceptions.
In the for loop, this will iterate through the log_file.records,
and the yield statement will return an lxml object,
which can be parsed rather than a plain string.
Moving forward,
we'll also need to build a function that will actually
parse the individual XML objects, and I will name it parse.Evtx,
which receives an lxml object as a parameter.
We need more information on the structure of each event,
and for this, we'll be using the Microsoft documentation.
Scrolling down, we can see the typical structure.
The System section will contain similar fields across multiple EventIDs,
whereas the EventData section will contain fields
specific to a type or a group of event IDs.
I will move the windows a bit,
and I suggest you do the same to ensure you have visibility
on the field names as you codify them.
In the first statement, I will identify the system tag.
Based on this, we'll attempt to identify the EventID field,
which is nested in the system tag,
and so is the timestamp in the field TimeCreated.
From the root element, we'll also extract the EventData tag.
Now we will instantiate the return dictionary similar to the other scripts,
and we'll assign the first element, which is the timestamp value.
The event ID is also an important field, so we want to save this as well.
It can be accessed by event_id.text.
Because event data might be different for the various event IDs,
we'll use a for loop to iterate through all of the nested tags
by calling getchildren on the event_data tag.
The key, in this case,
can be obtained by retrieving the Name attribute for each element and
assign the text value of the corresponding element.
And finally, return r so we can make use of this value.
Now it's a good time to test this out, so we'll jump into the terminal.
And after running Python,
I will import the openEvtx file and parseEvtx functions.
Similar to the other demos,
I'm using a variable for the result of the openEvtxFile function.
To get a single element from the file,
I'll create a temporary variable called event and assign it the value of the
first element by calling next and log_file as the parameter.
And it works.
We notice an XML element, but this is not something that we can use,
so I will continue and run the parseEvtx function and
use the event as the parameter.
And we can notice all the fields are extracted successfully.
Now that we have this capability, it's time for a more practical example.
Switching back to the Windows documentation.
The main reason I chose this event ID is because it's a very important one.
It can be configured on Windows systems to log process creation,
as well as the command line.
The scenario that I propose to detect is one based on a
technique documented in the MITRE framework,
where rundll32, a legitimate operating‑system utility,
is used for malicious purposes to execute malicious
code contained in a DLL library.
So how would we implement this?
Well, let's return to VS Code to find out.
I will create a new function called the detectRundll32.
This will receive the path to the Evtx log_file as the parameter.
I will call the openEvtxFile first using the path parameter.
Next,
I will iterate through all of the elements and create
a new variable called log_data,
which will receive the parsed log_entry from the dictionary, where we can
access the various fields using the field name as the key.
I will check if the event_id is 4688,
as well as if the CommandLine field is present,
meaning not none.
Because this can identify a lot of legitimate activity,
I will narrow down the events that I want to identify.
Namely, I want to check if rundll32 is the new process being created.
This can be done using the in operator to check if rundll32
is a substring of the NewProcessName path.
And the second requirement is a regular expression that attempts to identify if
the parent process is either command prompt or PowerShell.
If neither is identified, the re.search function will return none,
and therefore the commands of the if statement will not be executed.
Within the if statement, if both conditions are met,
then I want to print the CommandLine to verify the findings.
I'm using the try/except statements to avoid a lot of errors.
So let's run this in the terminal.
I will run Python again.
Import the new function and call it using the
Security.evtx log file within the logs directory.
This will take some time because there is a lot of data to parse through.
But in the end, we do manage to find the culprit,
and we see that rundll32 was used to execute a DLL named
agent.dll from within the AppData folder.
Nice.