Azure Sentinel Kusto query language for Kemp Technologies ESP CEF logs

Posted on

Deploying Kemp technologies LoadMaster with the Kemp Edge Security Pack (ESP) enabled, simplifies the secure publishing of applications with pre-authentication of clients and Single Sign-On (SSO) to improve the user experience. ESP can be fully integrated into your current authentication and authorization directories, including Microsoft Active Directory. This means that the passwords used to gain access to Internet facing applications can be the ones set in your corporate directory, with all the password policy settings maintained in one place. In addition, single sign-on and group memberships can be used to provide granular access to applications, and secure two-factor authorization solutions, such as RADIUS or RSA SecurID, can be used to augment the directory-based passwords.

Edge Security Pack provides Common Event Format (CEF) logs that are easily integrated to Azure Sentinel, see video here. Azure Sentinel provides the Kusto query language to enable further parsing and deeper insight into the data provided.

In this blog, I will demonstrate Kusto query language code that can be used to parse the Kemp Technologies ESP CEF logs to provide enhanced visibility of the authentication requests that the LoadMaster is receiving and the outcome. I am assuming that you have some basic knowledge of Azure Sentinel, have a workspace created with LoadMaster integrated and launched the Log Analytics workspace Logs Query, as shown in Figure 1 and in the video below.

Figure 1: Log Analytics workspace Logs Query

Script 1 parses out all the fields for the ESP CEF logs and presents them to the user.

CommonSecurityLog
| where DeviceVendor == “Kemp”
| extend ExtFields = split(AdditionalExtensions, ‘;’)
| extend VSInfo = split(ExtFields[0], “=”)[1]
| extend VSIP = split(VSInfo, “:”)[0]
| extend VSPort = split(VSInfo, “:”)[1]
| extend Event = split(ExtFields[1], “=”)[1]
| extend SourceIP = case( DeviceEventClassID !in (6, 14, 15), split(ExtFields[2], “=”)[1], DeviceEventClassID == 6, split(ExtFields[3], “=”)[1], SourceIP)
| extend SourcePort = iff ( DeviceEventClassID in (1, 2, 3, 4, 5, 11, 12, 13, 16, 17), split(ExtFields[3], “=”)[1], “”)
| extend DestinationIP = iff ( DeviceEventClassID in (4, 5), split(ExtFields[4], “=”)[1], “”)
| extend DestinationPort = iff ( DeviceEventClassID in (4, 5), split(ExtFields[5], “=”)[1], “”)
| extend AwaitingRemoteAddress = iff ( DeviceEventClassID == 3, split(ExtFields[4], “=”)[1], “”)
| extend RequestMethod = iff( DeviceEventClassID in (11, 12), split(ExtFields[4], “=”)[1], “”)
| extend RequestURL = iff( DeviceEventClassID in (11, 12), split(ExtFields[5], “=”)[1], “”)
| extend user_ext = split(ExtFields[6], “=”)
| extend User = case ( DeviceEventClassID == 6, split(ExtFields[2], “=”)[1], DeviceEventClassID in (7, 8, 9, 10), split(ExtFields[3], “=”)[1], DeviceEventClassID in (11, 12), iff( user_ext[0] == “user”, user_ext[1], “”), “”)
| extend user_agent_ext = split(ExtFields[7], “=”)
| extend UserAgent = iff( DeviceEventClassID in (11, 12), case ( user_ext[0] == “useragent”, user_ext[1], user_agent_ext[0] == “useragent”, user_agent_ext[1], “”), “”)
| extend Resource = case ( DeviceEventClassID == 15, split(ExtFields[2], “=”)[1], DeviceEventClassID in (16, 17), split(ExtFields[4], “=”)[1], “”)
| extend DTCode = iff ( DeviceEventClassID == 13, split(ExtFields[4], “=”)[1], “”)
| project format_datetime(TimeGenerated, “yyyy-MM-dd HH:mm:ss”), DeviceVendor, DeviceProduct, DeviceEventClassID, LogSeverity, Message, Event, VSIP, VSPort, SourceIP, SourcePort, DestinationIP, DestinationPort, AwaitingRemoteAddress, User, UserAgent, Resource, RequestMethod, RequestURL, DTCode

A further example is shown in Figure 2 and Script 2 with all of the possible fields on display.

Figure 2: Parsed CEF logs

CommonSecurityLog
| where DeviceVendor == “Kemp”
| extend Event = split(split(AdditionalExtensions, “;”)[1], “=”)[1]
| summarize Count = count() by tostring(Event)
| render piechart
Figure 3: Pie Chart (CEF Event types)

We can parse and show the CEF Event types in pie bar charts as shown in Figure 3 and Figure 4 below

CommonSecurityLog
| where DeviceVendor == “Kemp”
| extend Event = split(split(AdditionalExtensions, “;”)[1], “=”)[1]
| summarize Count = count() by tostring(Event)
| sort by Count desc
| render columnchart
Figure 4: Bar Chart (CEF Event types)

CommonSecurityLog
| where DeviceVendor == “Kemp”
| extend ExtFields = split(AdditionalExtensions, ‘;’)
| extend VSInfo = split(ExtFields[0], “=”)[1]
| extend VSIP = split(VSInfo, “:”)[0]
| extend VSPort = split(VSInfo, “:”)[1]
| extend Event = split(ExtFields[1], “=”)[1]
| extend SourceIP = case( DeviceEventClassID !in (6, 14, 15), split(ExtFields[2], “=”)[1], DeviceEventClassID == 6, split(ExtFields[3], “=”)[1], SourceIP)
| extend SourcePort = iff ( DeviceEventClassID in (1, 2, 3, 4, 5, 11, 12, 13, 16, 17), split(ExtFields[3], “=”)[1], “”)
| extend DestinationIP = iff ( DeviceEventClassID in (4, 5), split(ExtFields[4], “=”)[1], “”)
| extend DestinationPort = iff ( DeviceEventClassID in (4, 5), split(ExtFields[5], “=”)[1], “”)
| extend AwaitingRemoteAddress = iff ( DeviceEventClassID == 3, split(ExtFields[4], “=”)[1], “”)
| extend RequestMethod = iff( DeviceEventClassID in (11, 12), split(ExtFields[4], “=”)[1], “”)
| extend RequestURL = iff( DeviceEventClassID in (11, 12), split(ExtFields[5], “=”)[1], “”)
| extend user_ext = split(ExtFields[6], “=”)
| extend User = case ( DeviceEventClassID == 6, split(ExtFields[2], “=”)[1], DeviceEventClassID in (7, 8, 9, 10), split(ExtFields[3], “=”)[1], DeviceEventClassID in (11, 12), iff( user_ext[0] == “user”, user_ext[1], “”), “”)
| extend user_agent_ext = split(ExtFields[7], “=”)
| extend UserAgent = iff( DeviceEventClassID in (11, 12), case ( user_ext[0] == “useragent”, user_ext[1], user_agent_ext[0] == “useragent”, user_agent_ext[1], “”), “”)
| extend Resource = case ( DeviceEventClassID == 15, split(ExtFields[2], “=”)[1], DeviceEventClassID in (16, 17), split(ExtFields[4], “=”)[1], “”)
| extend DTCode = iff ( DeviceEventClassID == 13, split(ExtFields[4], “=”)[1], “”)
| project format_datetime(TimeGenerated, “yyyy-MM-dd HH:mm:ss”), DeviceVendor, DeviceProduct, DeviceEventClassID, LogSeverity, Message, Event, VSIP, VSPort, SourceIP, SourcePort, DestinationIP, DestinationPort, AwaitingRemoteAddress, User, UserAgent, Resource, RequestMethod, RequestURL, DTCode
| summarize Count = count() by tostring(User)
| render piechart

We can parse and show what users connected as shown in Figure 5 below

Figure 5: Users

We can parse and show the top 5 users whom are unable to login through multiple IPs, as shown in.

CommonSecurityLog
| where DeviceVendor == “Kemp”
| extend ExtFields = split(AdditionalExtensions, ‘;’)
| extend Event = split(ExtFields[1], “=”)[1]
| extend SourceIP = case( DeviceEventClassID !in (6, 14, 15), split(ExtFields[2], “=”)[1], DeviceEventClassID == 6, split(ExtFields[3], “=”)[1], SourceIP)
| extend user_ext = split(ExtFields[6], “=”)
| extend User = case ( DeviceEventClassID == 6, split(ExtFields[2], “=”)[1], DeviceEventClassID in (7, 8, 9, 10), split(ExtFields[3], “=”)[1], DeviceEventClassID in (11, 12), iff( user_ext[0] == “user”, user_ext[1], “”), “”)
| where Event == “Access Denied”
| summarize UserCount = count() by User, tostring(Event), tostring(SourceIP)
| top 5 by UserCount
Figure 6: Top 5 users

The Kusto language combined with the detailed information in the Kemp Technologies LoadMaster Edge Security Pack Common Event Format logs enables deep insight and visibility into authentication and authorization attempts in your network.

References:

https://docs.microsoft.com/en-us/azure/data-explorer/kusto/query/

Posted on

David O'Connor

David O’Connor is a Product Manager in Kemp working in Limerick, Ireland. He holds a bachelor’s degree in Computer Engineering from University of Limerick. David has a telecoms background with previous roles in development, customer support and presales with a focus on product-market fit and creating tech products that customers love.