(Print this page)

Reading Security events from the Event logs
Published date: Monday, October 30, 2017
On: Moer and Éric Moreau's web site

One of my clients now has to comply to many rules because he registered to the U.S. Securities and Exchange Commission (aka, the SEC).

The SEC is “proposing” many rules regarding the cybersecurity. One of the rule is to monitor Active Directory changes (user created, user deleted, group created, …). All those changes are logged into the Events Logs but my main problem is that those events are flooded between all other events I don’t really care about (like logon successfully). Another problem I have is that the log has a limited size (about 3 days in my case) so I would have to sit tight in front of the server! I know there are tools around but the one I found are quite expensive.

While working on that project, I found out that every one of you should probably have some interest for those events. With all the hacking happening today, it should be a good habit for many people to have a look at them.

Figure 1: One event of interest

This month’s downloadable code

This month solution contains both VB and C# projects. The solution was created using Visual Studio 2017 but the code should work as well in older version of the.Net Framework because there isn’t really nothing very fancy.

The one method that does almost everything

There is one method that does almost everything we need. This method is called ReadLogEntries and read as follow:

Public Shared Function ReadLogEntries(pAppLog As String) As String
    Dim strLogs As String = String.Empty

    Try
        ' logType can be an Application, Security, System, or any other Custom Log.
        Dim applicationlog As String = pAppLog
        Dim mymachine As String = "." ' local machine
        Dim myapplicationLog As New EventLog(applicationlog, mymachine)
        Dim entries As EventLogEntryCollection = myapplicationLog.Entries
        If entries.Count <= 0 Then
            strLogs += (Convert.ToString("No Event Logs in the Log:") & applicationlog) + Environment.NewLine
        End If

        strLogs += "Log Level,Log Event ID,Source,Date,Message" + Environment.NewLine
        Dim intItems As Integer = 0
        For Each entry As EventLogEntry In entries
            'skip
            If (entry.InstanceId = 4624 AndAlso entry.EntryType = EventLogEntryType.SuccessAudit) OrElse
                (entry.InstanceId = 4634 AndAlso entry.EntryType = EventLogEntryType.SuccessAudit) Then
                'Skip successfull logon/logoff
            Else
                strLogs += """" + entry.EntryType.ToString() + ""","
                strLogs += """" + entry.InstanceId.ToString() + ""","
                strLogs += """" + entry.Source + ""","
                strLogs += """" + entry.TimeGenerated.ToString() + ""","
                strLogs += """" + entry.Message.Replace(Environment.NewLine, " - --") + ""","
                strLogs += Environment.NewLine

                intItems += 1
                If intItems > 2000 Then
                    Exit For
                End If
            End If
        Next
    Catch e As Exception
        strLogs += "Exception: " + e.Message + Environment.NewLine
    End Try

    Return strLogs
End Function
public static string ReadLogEntries(string pAppLog)
{
    string strLogs = string.Empty;

    try
    {
        // logType can be an Application, Security, System, or any other Custom Log.
        string applicationlog = pAppLog;
        string mymachine = ".";   // local machine
        EventLog myapplicationLog = new EventLog(applicationlog, mymachine);
        EventLogEntryCollection entries = myapplicationLog.Entries;
        if (entries.Count <= 0)
        {
            strLogs += "No Event Logs in the Log :" + applicationlog + Environment.NewLine;
        }

        strLogs += "Log Level,Log Event ID,Source,Date,Message" + Environment.NewLine;
        int intItems = 0;
        foreach (EventLogEntry entry in entries)
        {
            if (
                (entry.InstanceId == 4624 && entry.EntryType == EventLogEntryType.SuccessAudit) ||
                (entry.InstanceId == 4634 && entry.EntryType == EventLogEntryType.SuccessAudit) 
            )
            {
                //Skip successfull logon/logoff
            }
            else
            {
                strLogs += @"""" + entry.EntryType + @""",";
                strLogs += @"""" + entry.InstanceId + @""",";
                strLogs += @"""" + entry.Source + @""",";
                strLogs += @"""" + entry.TimeGenerated + @""",";
                strLogs += @"""" + entry.Message.Replace(Environment.NewLine, " --- ") + @""",";
                strLogs += Environment.NewLine;

                intItems++;
                if (intItems > 2000)
                    break;
            }
        }
    }
    catch (Exception e)
    {
        strLogs += "Exception: " + e.Message + Environment.NewLine;
    }

    return strLogs;
}

This method receives a value in argument. The value of this argument is the name of the Event log you want to read. The values should be something like Application, Security, System, or any custom log you may have on your system.

Notice that for some logs, you will be required elevated privileges. You will find that you need these privileges if you see the “Exception: Requested registry access is not allowed.” Exception being thrown. Usually, the Security log is protected.

This method tries to read the event log on the local machine and spits out a long string of the events found in there. You might want to filter out that list because some logs are really verbose. As an example, I have filtered out events number 4624 and 4634 (successful log in and log out) because I do not need them. You will also find that I limit the number of items returned to 2000. The last thing I want to mention here is that not all the properties of an event are returned in that string (the properties are listed here - https://msdn.microsoft.com/en-us/library/system.diagnostics.eventlog_properties(v=vs.110).aspx). You definitely want to review all these limitations and adjust them for your specific requirements. One good addition would be to only process the events since the last time you ran the application (but that’s another topic!).

The values collected from these events are concatenated into a big string and returned to the caller.

In my demo application, I don’t do much with the string returned. It is displayed into a scrollable textbox and also saved into a CSV file (so you can easily open it in Excel):

Private Sub btnReadLogs_Click(sender As Object, e As EventArgs) Handles btnReadLogs.Click
    txtResults.Clear()

    Dim strText As String = ReadEventLog.ReadLogEntries(txtLogName.Text)

    txtResults.Text = strText

    Dim strFileName As String = $"Logs_{txtLogName.Text}_{DateTime.Now.ToString("yyyyMMdd_HHmmss")}.csv"

    System.IO.File.WriteAllText(strFileName, strText)
End Sub
private void btnReadLogs_Click(object sender, EventArgs e)
{
    txtResults.Clear();

    string strText = ReadEventLog.ReadLogEntries(txtLogName.Text);

    txtResults.Text = strText;

    string strFileName = $"Logs_{txtLogName.Text}_{DateTime.Now.ToString("yyyyMMdd_HHmmss")}.csv";
    System.IO.File.WriteAllText(strFileName, strText);
}

Figure 2: The demo application in action

In a real-world application, a database would most probably be your storage of choice to easily get historical data from it.

Resources

There are many resources around. I found an excellent one named “Randy Franklin Smith’s Ultimate Windows Security” offering many very useful resources about the events found in the logs. They offer many free resources including an encyclopedia.

Conclusion

Security is vast and a never-ending story. Very often tools exist to provide some guidance on that subject but probably none makes a full coverage of what you really need. With the help of some coding, you can get one step closer to your objectives.


(Print this page)