{"id":6434,"date":"2020-04-30T17:12:55","date_gmt":"2020-04-30T14:12:55","guid":{"rendered":"https:\/\/kifarunix.com\/?p=6434"},"modified":"2024-03-14T20:02:26","modified_gmt":"2024-03-14T17:02:26","slug":"process-and-visualize-modsecurity-logs-on-elk-stack","status":"publish","type":"post","link":"https:\/\/kifarunix.com\/process-and-visualize-modsecurity-logs-on-elk-stack\/","title":{"rendered":"Process and Visualize ModSecurity Logs on ELK Stack"},"content":{"rendered":"\n<p>In this tutorial, you will learn how to process and visualize ModSecurity Logs on ELK Stack. <a aria-label=\"undefined (opens in a new tab)\" href=\"https:\/\/modsecurity.org\/\" target=\"_blank\" rel=\"noreferrer noopener\">ModSecurity<\/a> is an open source, cross-platform web application firewall (WAF) module&nbsp;developed by Trustwave\u2019s SpiderLabs.&nbsp;Known as the \u201cSwiss Army Knife\u201d of WAFs, it enables web application defenders to gain visibility into HTTP(S) traffic and provides a power rules language and API to implement advanced protections.<\/p>\n\n\n\n<p>ModSecurity utilizes the OWASP Core Rule Set which can be used to protect web applications against various attacks including the top 10 OWASP such as SQL Injection, Cross Site Scripting, Local File Inclusion, etc.<\/p>\n\n\n\n<p>OWASP, has recently, in their report, <a aria-label=\"undefined (opens in a new tab)\" href=\"https:\/\/www.owasp.org\/index.php\/Category:OWASP_Top_Ten_Project\" target=\"_blank\" rel=\"noreferrer noopener\">OWASP Top 10 2017<\/a>, included <a href=\"https:\/\/owasp.org\/www-project-top-ten\/OWASP_Top_Ten_2017\/Top_10-2017_A10-Insufficient_Logging%252526Monitoring\" target=\"_blank\" aria-label=\"undefined (opens in a new tab)\" rel=\"noreferrer noopener\">Insufficient Logging and Monitoring<\/a> as one of the top 10 web application security risks. <\/p>\n\n\n\n<p>&#8220;<em>Insufficient logging and monitoring, coupled with missing or ineffective integration with incident response, allows attackers to further attack systems, maintain persistence, pivot to more systems, and tamper, extract, or destroy data. Most breach studies show time to detect a breach is over 200 days, typically detected by external parties rather than internal processes or monitoring<\/em>&#8220;.<\/p>\n\n\n\n<p>ELK\/Elastic Stack on the other hand is powerful platform that collects and processes data from multiple data sources, stores that data in a search and analytic engine that enables users to visualize using charts, tables, graphs.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Process ModSecurity Logs for Visualizing on ELK Stack<\/h2>\n\n\n\n<p>Therefore, as a way of ensuring logging and monitoring of web application logs, we are going to learn how you can process and visualize ModSecurity WAF logs on ELK\/Elastic Stack.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Prerequisites<\/h2>\n\n\n\n<h3 class=\"wp-block-heading\">Install and Setup ELK\/Elastic Stack<\/h3>\n\n\n\n<p>Ensure that you have an ELK stack setup before you can proceed. You can follow the links below to install and setup ELK\/Elastic stack on Linux;<\/p>\n\n\n\n<p><a aria-label=\"undefined (opens in a new tab)\" href=\"https:\/\/kifarunix.com\/install-elastic-elk-stack-on-ubuntu-20-04\/\" target=\"_blank\" rel=\"noreferrer noopener\">Install ELK Stack on Ubuntu 20.04<\/a><\/p>\n\n\n\n<p><a href=\"https:\/\/kifarunix.com\/installing-elk-stack-on-centos-8\/\" target=\"_blank\" aria-label=\"undefined (opens in a new tab)\" rel=\"noreferrer noopener\">Installing ELK Stack on CentOS 8<\/a><\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Install and Setup ModSecurity on CentOS\/Ubuntu<\/h3>\n\n\n\n<p>Install and enable Web application protection with ModSecurity. You can check the guides below to install ModSecurity 3 (libModSecurity);<\/p>\n\n\n\n<p><a aria-label=\"undefined (opens in a new tab)\" href=\"https:\/\/kifarunix.com\/configure-libmodsecurity-with-apache-on-centos-8\/\" target=\"_blank\" rel=\"noreferrer noopener\">Configure LibModsecurity with Apache on CentOS 8<\/a><\/p>\n\n\n\n<p><a aria-label=\"undefined (opens in a new tab)\" href=\"https:\/\/kifarunix.com\/configure-libmodsecurity-with-nginx-on-centos-8\/\" target=\"_blank\" rel=\"noreferrer noopener\">Configure LibModsecurity with Nginx on CentOS 8<\/a><\/p>\n\n\n\n<p><a href=\"https:\/\/kifarunix.com\/install-libmodsecurity-with-apache-on-ubuntu-18-04\/\" target=\"_blank\" aria-label=\"undefined (opens in a new tab)\" rel=\"noreferrer noopener\">Install LibModsecurity with Apache on Ubuntu 18.04<\/a><\/p>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"process-modsecurity-logs-with-logstash\"><a href=\"#process-modsecurity-logs-with-logstash\">Configure Logstash to Process ModSecurity Logs<\/a><\/h3>\n\n\n\n<p><a href=\"https:\/\/www.elastic.co\/logstash\" target=\"_blank\" aria-label=\"undefined (opens in a new tab)\" rel=\"noreferrer noopener\">Logstash<\/a> is a server-side data processing pipeline that ingests data from a multiple sources, transforms it, and then stashes it onto search and data analytics engines such as Elasticsearch.<\/p>\n\n\n\n<p>Logstash pipeline is made up of three sections;<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>the&nbsp;<strong>input<\/strong>: collect data from different sources<\/li>\n\n\n\n<li>the&nbsp;<strong>filter<\/strong>: (<em>Optional<\/em>) performs further processing on data.<\/li>\n\n\n\n<li>the<strong>&nbsp;output<\/strong>: stashes received data into a destination datastore such as Elasticsearch.<\/li>\n<\/ul>\n\n\n\n<h4 class=\"wp-block-heading\" id=\"configure-logstash-filebeat-input\"><a href=\"#configure-logstash-filebeat-input\">Configure Logstash Input plugin<\/a><\/h4>\n\n\n\n<p>Logstash supports various <a aria-label=\"undefined (opens in a new tab)\" href=\"https:\/\/www.elastic.co\/guide\/en\/logstash\/master\/input-plugins.html\" target=\"_blank\" rel=\"noreferrer noopener\">input plugins<\/a> that enables it to read event data from various sources.<\/p>\n\n\n\n<p>In this tutorial, we will be using Filebeat to collect and push ModSecurity logs to Logstash. Hence, we will configure Logstash to receive events from the&nbsp;Elastic Beats&nbsp;framework, which in this case is Filebeat.<\/p>\n\n\n\n<p>To configure Logstash to collect events from Filebeat or any Elastic beat, create an input plugin configuration file.<\/p>\n\n\n\n<pre class=\"wp-block-preformatted\"><code>vim \/etc\/logstash\/conf.d\/modsecurity-filter.conf<\/code><\/pre>\n\n\n\n<pre class=\"wp-block-preformatted\"><code>input {\n  beats {\n    port =&gt; 5044\n  }\n}<\/code><\/pre>\n\n\n\n<h3 class=\"wp-block-heading\"><a href=\"#logstash-grok-filter-plugin\">Configure Logstash Filter plugin<\/a><\/h3>\n\n\n\n<p>We will be utilizing <a aria-label=\"undefined (opens in a new tab)\" href=\"https:\/\/www.elastic.co\/guide\/en\/logstash\/master\/plugins-filters-grok.html\" target=\"_blank\" rel=\"noreferrer noopener\">Logstash Grok filter<\/a> to process our ModSecurity logs.<\/p>\n\n\n\n<p>Below is a sample ModSecurity audit log line. An audit log line provides details about a transaction that\u2019s blocked and why it was blocked.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>&#91;Sun Jul 12 21:22:47.978339 2020] &#91;:error] &#91;pid 78188:tid 140587634259712] &#91;client 192.168.56.100:54556] ModSecurity: Warning. Matched \"Operator `PmFromFile' with parameter `scanners-user-agents.data' against variable `REQUEST_HEADERS:User-Agent' (Value: `Mozilla\/5.0 zgrab\/0.x' ) &#91;file \"\/etc\/httpd\/conf.d\/modsecurity.d\/owasp-crs\/rules\/REQUEST-913-SCANNER-DETECTION.conf\"] &#91;line \"33\"] &#91;id \"913100\"] &#91;rev \"\"] &#91;msg \"Found User-Agent associated with security scanner\"] &#91;data \"Matched Data: zgrab found within REQUEST_HEADERS:User-Agent: mozilla\/5.0 zgrab\/0.x\"] &#91;severity \"2\"] &#91;ver \"OWASP_CRS\/3.2.0\"] &#91;maturity \"0\"] &#91;accuracy \"0\"] &#91;tag \"application-multi\"] &#91;tag \"language-multi\"] &#91;tag \"platform-multi\"] &#91;tag \"attack-reputation-scanner\"] &#91;tag \"paranoia-level\/1\"] &#91;tag \"OWASP_CRS\"] &#91;tag \"OWASP_CRS\/AUTOMATION\/SECURITY_SCANNER\"] &#91;tag \"WASCTC\/WASC-21\"] &#91;tag \"OWASP_TOP_10\/A7\"] &#91;tag \"PCI\/6.5.10\"] &#91;hostname \"kifarunix-demo.com\"] &#91;uri \"\/\"] &#91;unique_id \"15945817674.229523\"] &#91;ref \"o12,5v48,21t:lowercase\"]<\/code><\/pre>\n\n\n\n<p>Every ModSecurity alert conforms to the following format when it appears in the Apache error log:<\/p>\n\n\n\n<pre class=\"wp-block-preformatted\"><code>[Sun Jun 24 10:19:58 2007] [error] [client IP-Address] ModSecurity: ALERT_MESSAGE<\/code><\/pre>\n\n\n\n<p>In order to make a good visualization out of such unstructured log, we need to create filters to extract only specific fields that make sense and that gives information about an attack.<\/p>\n\n\n\n<p>You can utilize the readily available <a aria-label=\"undefined (opens in a new tab)\" href=\"https:\/\/github.com\/logstash-plugins\/logstash-patterns-core\/blob\/master\/patterns\/grok-patterns\" target=\"_blank\" rel=\"noreferrer noopener\">Logstash grok patterns<\/a> or utilize regular expressions if there is no grok filter for the section of the log you need to extract.<\/p>\n\n\n\n<p>You can use the Grok Debugger on Kibana to test your Grok pattern\/Regular expressions,&nbsp;<strong>Dev-tools &gt; Grok Debugger<\/strong>, or <a aria-label=\"undefined (opens in a new tab)\" href=\"http:\/\/grokdebug.herokuapp.com\/\" target=\"_blank\" rel=\"noreferrer noopener\">http:\/\/grokdebug.herokuapp.com<\/a>&nbsp;and&nbsp;<a aria-label=\"undefined (opens in a new tab)\" href=\"http:\/\/grokconstructor.appspot.com\/\" target=\"_blank\" rel=\"noreferrer noopener\">http:\/\/grokconstructor.appspot.com\/<\/a>&nbsp;apps.<\/p>\n\n\n\n<p>We create Logstash Grok patterns to process the ModSecurity audit log and extract the following fields;<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Event time (<strong><code>event_time<\/code><\/strong>)<br><strong><code>Sun Jul 12 21:22:47.978339 2020<\/code><\/strong><\/li>\n\n\n\n<li>Log Severity Level (<strong><code>log_level<\/code><\/strong>)<br><code><strong>error<\/strong><\/code><\/li>\n\n\n\n<li>Client IP Address (Source of Attack, <code><strong>src_ip<\/strong><\/code>)<br><code><strong>192.168.56.100<\/strong><\/code><\/li>\n\n\n\n<li>Alert Message (<code><strong>alert_message<\/strong><\/code>)<\/li>\n<\/ul>\n\n\n\n<pre class=\"wp-block-code\"><code>ModSecurity: Warning. Matched \"Operator PmFromFile' with parameter `scanners-user-agents.data`' against variable REQUEST_HEADERS:User-Agent' (Value:Mozilla\/5.0 zgrab\/0.x' ) &#91;file \"\/etc\/httpd\/conf.d\/modsecurity.d\/owasp-crs\/rules\/REQUEST-913-SCANNER-DETECTION.conf\"] &#91;line \"33\"] &#91;id \"913100\"] &#91;rev \"\"] &#91;msg \"Found User-Agent associated with security scanner\"] &#91;data \"Matched Data: zgrab found within REQUEST_HEADERS:User-Agent: mozilla\/5.0 zgrab\/0.x\"] &#91;severity \"2\"] &#91;ver \"OWASP_CRS\/3.2.0\"] &#91;maturity \"0\"] &#91;accuracy \"0\"] &#91;tag \"application-multi\"] &#91;tag \"language-multi\"] &#91;tag \"platform-multi\"] &#91;tag \"attack-reputation-scanner\"] &#91;tag \"paranoia-level\/1\"] &#91;tag \"OWASP_CRS\"] &#91;tag \"OWASP_CRS\/AUTOMATION\/SECURITY_SCANNER\"] &#91;tag \"WASCTC\/WASC-21\"] &#91;tag \"OWASP_TOP_10\/A7\"] &#91;tag \"PCI\/6.5.10\"] &#91;hostname \"kifarunix-demo.com\"] &#91;uri \"\/\"] &#91;unique_id \"15945817674.229523\"] &#91;ref \"o12,5v48,21t:lowercase\"]<\/code><\/pre>\n\n\n\n<p>From the Alert Message (<strong>alert_message<\/strong>), we will further extract the following fields;<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Rules file (<strong><code>rules_file<\/code><\/strong>):  <strong><code>\/etc\/httpd\/conf.d\/modsecurity.d\/owasp-crs\/rules\/REQUEST-913-SCANNER-DETECTION.conf<\/code><\/strong><\/li>\n\n\n\n<li>Attack type based on rules file (<strong><code>attack_type<\/code><\/strong>): <strong><code>SCANNER-DETECTION<\/code><\/strong><\/li>\n\n\n\n<li>Rule ID (<strong><code>rule_id<\/code><\/strong>): <strong><code>913100<\/code><\/strong><\/li>\n\n\n\n<li>Attack Message (<strong><code>alert_msg<\/code><\/strong>):  <strong><code>Found User-Agent associated with security scanner<\/code><\/strong><\/li>\n\n\n\n<li>Matched Data (matched_data): <strong><code>Matched Data: zgrab found within REQUEST_HEADERS:User-Agent: mozilla\/5.0 zgrab\/0.x<\/code><\/strong><\/li>\n\n\n\n<li>User Agent (<strong><code>user_agent<\/code><\/strong>): <strong><code>mozilla\/5.0 zgrab\/0.x<\/code><\/strong><\/li>\n\n\n\n<li>Hostname (<strong><code>dst_host<\/code><\/strong>): <strong><code>kifarunix-demo.com<\/code><\/strong><\/li>\n\n\n\n<li>Request URI (<code><strong>request_uri<\/strong><\/code>): <strong><code>\/<\/code><\/strong><\/li>\n\n\n\n<li>Referer, if any. (<strong><code>referer<\/code><\/strong>)<\/li>\n<\/ul>\n\n\n\n<p>In order to extract these sections, below is our Logstash Grok filter.<\/p>\n\n\n\n<pre class=\"scroll-box\"><code>filter {\n    # Extract event time, log severity level, source of attack (client), and the alert message.\n    grok {\n      match =&gt; { \"message\" =&gt; \"(?&lt;event_time&gt;%{MONTH}\\s%{MONTHDAY}\\s%{TIME}\\s%{YEAR})\\] \\[\\:%{LOGLEVEL:log_level}.*client\\s%{IPORHOST:src_ip}:\\d+]\\s(?&lt;alert_message&gt;.*)\" }\n    }\n    # Extract Rules File from Alert Message\n    grok {\n      match =&gt; { \"alert_message\" =&gt; \"(?&lt;rulesfile&gt;\\[file \\\"(\/.+.conf)\\\"\\])\" }\n    }\t\n    grok {\n      match =&gt; { \"rulesfile\" =&gt; \"(?&lt;rules_file&gt;\/.+.conf)\" }\n    }\t\n    # Extract Attack Type from Rules File\n    grok {\n      match =&gt; { \"rulesfile\" =&gt; \"(?&lt;attack_type&gt;[A-Z]+-[A-Z][^.]+)\" }\n    }\t\n    # Extract Rule ID from Alert Message\n    grok {\n      match =&gt; { \"alert_message\" =&gt; \"(?&lt;ruleid&gt;\\[id \\\"(\\d+)\\\"\\])\" }\n    }\t\n    grok {\n      match =&gt; { \"ruleid\" =&gt; \"(?&lt;rule_id&gt;\\d+)\" }\n    }\n    # Extract Attack Message (msg) from Alert Message \t\n    grok {\n      match =&gt; { \"alert_message\" =&gt; \"(?&lt;msg&gt;\\[msg \\S(.*?)\\\"\\])\" }\n    }\t\n    grok {\n      match =&gt; { \"msg\" =&gt; \"(?&lt;alert_msg&gt;\\\"(.*?)\\\")\" }\n    }\n    # Extract the User\/Scanner Agent from Alert Message\t\n    grok {\n      match =&gt; { \"alert_message\" =&gt; \"(?&lt;scanner&gt;User-Agent' \\SValue: `(.*?)')\" }\n    }\t\n    grok {\n      match =&gt; { \"scanner\" =&gt; \"(?&lt;user_agent&gt;:(.*?)\\')\" }\n    }\n    grok {\n      match =&gt; { \"alert_message\" =&gt; \"(?&lt;matched_data&gt;(Matched Data:+.+))\\\"\\]\\s\\[severity\" }\n    }\n    grok {\n      match =&gt; { \"alert_message\" =&gt; \"(?&lt;agent&gt;User-Agent: (.*?)\\')\" }\n    }\t\n    grok {\n      match =&gt; { \"agent\" =&gt; \"(?&lt;user_agent&gt;: (.*?)\\')\" }\n    }\t\n    # Extract the Target Host\n    grok {\n      match =&gt; { \"alert_message\" =&gt; \"(hostname \\\"%{IPORHOST:dst_host})\" }\n    }\t\n    # Extract the Request URI\n    grok {\n      match =&gt; { \"alert_message\" =&gt; \"(uri \\\"%{URIPATH:request_uri})\" }\n    }\n    grok {\n      match =&gt; { \"alert_message\" =&gt; \"(?&lt;ref&gt;referer: (.*))\" }\n    }\t\n    grok {\n      match =&gt; { \"ref\" =&gt; \"(?&lt;referer&gt; (.*))\" }\n    }\n    mutate {\n      # Remove unnecessary characters from the fields.\n      gsub =&gt; [\n        \"alert_msg\", \"[\\\"]\", \"\",\n        \"user_agent\", \"[:\\\"'`]\", \"\",\n        \"user_agent\", \"^\\s*\", \"\",\n        \"referer\", \"^\\s*\", \"\"\n      ]\n      # Remove the Unnecessary fields so we can only remain with\n      # General message, rules_file, attack_type, rule_id, alert_msg, user_agent, hostname (being attacked), Request URI and Referer. \n      remove_field =&gt; [ \"alert_message\", \"rulesfile\", \"ruleid\", \"msg\", \"scanner\", \"agent\", \"ref\" ]\n    }\t\n}\n<\/code><\/pre>\n\n\n\n<p>So how does this filter works?<\/p>\n\n\n\n<p>In the first grok filter;<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>    grok {\n      match =&gt; { \"message\" =&gt; \"(?&lt;event_time&gt;%{MONTH}\\s%{MONTHDAY}\\s%{TIME}\\s%{YEAR})\\] \\&#91;\\:%{LOGLEVEL:log_level}.*client\\s%{IPORHOST:src_ip}:\\d+]\\s(?&lt;alert_message&gt;.*)\" }\n    }<\/code><\/pre>\n\n\n\n<p>we extract event time, log severity level, source of attack (client), and the alert message.<\/p>\n\n\n\n<pre class=\"wp-block-preformatted\"><code>{\n  <strong>\"src_ip\": \"192.168.56.100\",<\/strong>\n  \"<strong>alert_message<\/strong>\": \"ModSecurity: Warning. Matched \"Operator `PmFromFile' with parameter `scanners-user-agents.data' against variable `REQUEST_HEADERS:User-Agent' (Value: `Mozilla\/5.0 zgrab\/0.x' ) [file \"\/etc\/httpd\/conf.d\/modsecurity.d\/owasp-crs\/rules\/REQUEST-913-SCANNER-DETECTION.conf\"] [line \"33\"] [id \"913100\"] [rev \"\"] [msg \"Found User-Agent associated with security scanner\"] [data \"Matched Data: zgrab found within REQUEST_HEADERS:User-Agent: mozilla\/5.0 zgrab\/0.x\"] [severity \"2\"] [ver \"OWASP_CRS\/3.2.0\"] [maturity \"0\"] [accuracy \"0\"] [tag \"application-multi\"] [tag \"language-multi\"] [tag \"platform-multi\"] [tag \"attack-reputation-scanner\"] [tag \"paranoia-level\/1\"] [tag \"OWASP_CRS\"] [tag \"OWASP_CRS\/AUTOMATION\/SECURITY_SCANNER\"] [tag \"WASCTC\/WASC-21\"] [tag \"OWASP_TOP_10\/A7\"] [tag \"PCI\/6.5.10\"] [hostname \"kifarunix-demo.com\"] [uri \"\/\"] [unique_id \"15945817674.229523\"] [ref \"o12,5v48,21t:lowercase\"]\",\n<strong>  \"log_level\": \"error\",\n  \"event_time\": \"Jul 12 21:22:47.978339 2020\"<\/strong>\n}<\/code><\/pre>\n\n\n\n<p>From the <code><strong>alert_message<\/strong><\/code>, we then proceed to extract other fields.<\/p>\n\n\n\n<p>For example, we begin with extracting the rule file using the pattern;<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>(?&lt;rulesfile&gt;\\&#91;file \\\"(\/.+.conf)\\\"\\])<\/code><\/pre>\n\n\n\n<pre class=\"wp-block-preformatted\"><code>{\n  \"rulesfile\": \"[file \\\"\/etc\/httpd\/conf.d\/modsecurity.d\/owasp-crs\/rules\/REQUEST-913-SCANNER-DETECTION.conf\\\"]\"\n}<\/code><\/pre>\n\n\n\n<p>This gets us, <code><strong>[file \\\"\/etc\/httpd\/conf.d\/modsecurity.d\/owasp-crs\/rules\/REQUEST-913-SCANNER-DETECTION.conf\\\"]<\/strong><\/code>.<\/p>\n\n\n\n<p>We need to obtain just the file, <strong><code>\/etc\/httpd\/conf.d\/modsecurity.d\/owasp-crs\/rules\/REQUEST-913-SCANNER-DETECTION.conf<\/code><\/strong> and hence, we further use the pattern below to extract it renaming the new field from <strong><code>rulesfile<\/code><\/strong> to <code><strong>rules_file<\/strong><\/code>.<\/p>\n\n\n\n<pre class=\"wp-block-preformatted\"><code>(?&lt;rules_file_path&gt;\/.+.conf)<\/code><\/pre>\n\n\n\n<pre class=\"wp-block-preformatted\"><code>{\n  \"rules_file_path\": \"<strong>\/etc\/httpd\/conf.d\/modsecurity.d\/owasp-crs\/rules\/REQUEST-913-SCANNER-DETECTION.conf<\/strong>\"\n}<\/code><\/pre>\n\n\n\n<p>The same applies to other fields.<\/p>\n\n\n\n<p>We also <strong><code>mutated<\/code><\/strong> other fields and remove unnecessary characters including the <strong>trailing spaces<\/strong>, <strong>double quotes<\/strong> etc using the <strong><code><a aria-label=\"undefined (opens in a new tab)\" href=\"https:\/\/www.elastic.co\/guide\/en\/logstash\/current\/plugins-filters-mutate.html#plugins-filters-mutate-gsub\" target=\"_blank\" rel=\"noreferrer noopener\">gsub<\/a><\/code><\/strong> option. For example, on the alert_msg field, we removed the double quotes, on user_Agent field, we removed trailing spaces etc.<\/p>\n\n\n\n<pre class=\"wp-block-preformatted\"><code>    mutate {\n      # Remove unnecessary characters from the fields.\n<strong>      gsub =&gt; [\n        \"alert_msg\", \"[\\\"]\", \"\",\n        \"user_agent\", \"[:\\\"'`]\", \"\",\n        \"user_agent\", \"^\\s*\", \"\",\n        \"referer\", \"^\\s*\", \"\"<\/strong>\n      ]<\/code><\/pre>\n\n\n\n<p>We also removed unnecessary fields using the <code><strong>remove_field<\/strong><\/code> mutate option.<\/p>\n\n\n\n<pre class=\"wp-block-preformatted\"><code>      # Remove the Unnecessary fields so we can only remain with\n      # General message, rules_file, attack_type, rule_id, alert_msg, user_agent, hostname (being attacked), Request URI and Referer. \n      <strong>remove_field =&gt; [ \"alert_message\", \"rulesfile\", \"ruleid\", \"msg\", \"scanner\", \"agent\", \"ref\" ]<\/strong>\n    }<\/code><\/pre>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"logstash-elasticsearch-output\"><a href=\"#logstash-elasticsearch-output\">Configure Logstash Output Plugin<\/a><\/h3>\n\n\n\n<p>In this setup, we will forward Logstash processed data to the ES search and analytics engine.<\/p>\n\n\n\n<p>Thus, our output plugin is configured as;<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>output {\n   elasticsearch {\n     hosts =&gt; &#91;\"192.168.56.119:9200\"]\n     manage_template =&gt; false\n     index =&gt; \"modsec-%{+YYYY.MM}\"\n   }\n}<\/code><\/pre>\n\n\n\n<p>Therefore, in general, our Logstash ModSecurity processing configuration file looks like;<\/p>\n\n\n\n<pre class=\"wp-block-preformatted\"><code>cat \/etc\/logstash\/conf.d\/modsecurity-filter.conf<\/code><\/pre>\n\n\n\n<pre class=\"scroll-box\"><code>input {\n  beats {\n    port =&gt; 5044\n  }\n}\nfilter {\n    # Extract event time, log severity level, source of attack (client), and the alert message.\n    grok {\n      match =&gt; { \"message\" =&gt; \"(?&lt;event_time&gt;%{MONTH}\\s%{MONTHDAY}\\s%{TIME}\\s%{YEAR})\\] \\[\\:%{LOGLEVEL:log_level}.*client\\s%{IPORHOST:src_ip}:\\d+]\\s(?&lt;alert_message&gt;.*)\" }\n    }\n    # Extract Rules File from Alert Message\n    grok {\n      match =&gt; { \"alert_message\" =&gt; \"(?&lt;rulesfile&gt;\\[file \\\"(\/.+.conf)\\\"\\])\" }\n    }\t\n    grok {\n      match =&gt; { \"rulesfile\" =&gt; \"(?&lt;rules_file&gt;\/.+.conf)\" }\n    }\t\n    # Extract Attack Type from Rules File\n    grok {\n      match =&gt; { \"rulesfile\" =&gt; \"(?&lt;attack_type&gt;[A-Z]+-[A-Z][^.]+)\" }\n    }\t\n    # Extract Rule ID from Alert Message\n    grok {\n      match =&gt; { \"alert_message\" =&gt; \"(?&lt;ruleid&gt;\\[id \\\"(\\d+)\\\"\\])\" }\n    }\t\n    grok {\n      match =&gt; { \"ruleid\" =&gt; \"(?&lt;rule_id&gt;\\d+)\" }\n    }\n    # Extract Attack Message (msg) from Alert Message \t\n    grok {\n      match =&gt; { \"alert_message\" =&gt; \"(?&lt;msg&gt;\\[msg \\S(.*?)\\\"\\])\" }\n    }\t\n    grok {\n      match =&gt; { \"msg\" =&gt; \"(?&lt;alert_msg&gt;\\\"(.*?)\\\")\" }\n    }\n    # Extract the User\/Scanner Agent from Alert Message\t\n    grok {\n      match =&gt; { \"alert_message\" =&gt; \"(?&lt;scanner&gt;User-Agent' \\SValue: `(.*?)')\" }\n    }\t\n    grok {\n      match =&gt; { \"scanner\" =&gt; \"(?&lt;user_agent&gt;:(.*?)\\')\" }\n    }\t\n    grok {\n      match =&gt; { \"alert_message\" =&gt; \"(?&lt;agent&gt;User-Agent: (.*?)\\')\" }\n    }\t\n    grok {\n      match =&gt; { \"agent\" =&gt; \"(?&lt;user_agent&gt;: (.*?)\\')\" }\n    }\t\n    # Extract the Target Host\n    grok {\n      match =&gt; { \"alert_message\" =&gt; \"(hostname \\\"%{IPORHOST:dst_host})\" }\n    }\t\n    # Extract the Request URI\n    grok {\n      match =&gt; { \"alert_message\" =&gt; \"(uri \\\"%{URIPATH:request_uri})\" }\n    }\n    grok {\n      match =&gt; { \"alert_message\" =&gt; \"(?&lt;ref&gt;referer: (.*))\" }\n    }\t\n    grok {\n      match =&gt; { \"ref\" =&gt; \"(?&lt;referer&gt; (.*))\" }\n    }\n    mutate {\n      # Remove unnecessary characters from the fields.\n      gsub =&gt; [\n        \"alert_msg\", \"[\\\"]\", \"\",\n        \"user_agent\", \"[:\\\"'`]\", \"\",\n        \"user_agent\", \"^\\s*\", \"\",\n        \"referer\", \"^\\s*\", \"\"\n      ]\n      # Remove the Unnecessary fields so we can only remain with\n      # General message, rules_file, attack_type, rule_id, alert_msg, user_agent, hostname (being attacked), Request URI and Referer. \n      remove_field =&gt; [ \"alert_message\", \"rulesfile\", \"ruleid\", \"msg\", \"scanner\", \"agent\", \"ref\" ]\n    }\t\n}\noutput {\n   elasticsearch {\n     hosts =&gt; [\"192.168.56.119:9200\"]\n     manage_template =&gt; false\n     index =&gt; \"modsec-%{+YYYY.MM}\"\n   }\n}\n<\/code><\/pre>\n\n\n\n<p><strong>Please note that the extracted fields are not exhaustive. Feel free to further process your logs and extract the fields of your interest.<\/strong><\/p>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"forward-modsecurity-logs-to-elk\"><a href=\"#forward-modsecurity-logs-to-elk\">Forward ModSecurity Logs to Elastic Stack<\/a><\/h3>\n\n\n\n<p>Once you have installed and setup libModSecurity on your web server, forward the logs to Elastic Stack, in this case, to Logstash data processing pipeline.<\/p>\n\n\n\n<p>In this setup, we are using Filebeat to collect and push logs to Elastic Stack. Follow the links below to install and setup Filebeat.<\/p>\n\n\n\n<p><a href=\"https:\/\/kifarunix.com\/install-and-configure-filebeat-on-centos-8\/\" target=\"_blank\" aria-label=\"undefined (opens in a new tab)\" rel=\"noreferrer noopener\">Install and Configure Filebeat on CentOS 8<\/a><\/p>\n\n\n\n<p><a href=\"https:\/\/kifarunix.com\/install-filebeat-on-fedora-30-fedora-29-centos-7\/\" target=\"_blank\" aria-label=\"undefined (opens in a new tab)\" rel=\"noreferrer noopener\">Install Filebeat on Fedora 30\/Fedora 29\/CentOS 7<\/a><\/p>\n\n\n\n<p><a href=\"https:\/\/kifarunix.com\/install-and-configure-filebeat-7-on-ubuntu-18-04-debian-9-8\/\" target=\"_blank\" aria-label=\"undefined (opens in a new tab)\" rel=\"noreferrer noopener\">Install and Configure Filebeat 7 on Ubuntu 18.04\/Debian 9.8<\/a><\/p>\n\n\n\n<p>Be sure to configure the right path where ModSecurity logs are being written to.<\/p>\n\n\n\n<h4 class=\"wp-block-heading\">Debugging Grok Filter<\/h4>\n\n\n\n<p>If you need to debug your filter, replace the output section of the Logstash configuration file as shown below and sent the processing to standard output;<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>output {\n   #elasticsearch {\n   #  hosts =&gt; &#91;\"192.168.56.119:9200\"]\n   #  manage_template =&gt; false\n   #  index =&gt; \"modsec-%{+YYYY.MM}\"\n   #}\n  stdout { codec =&gt; rubydebug }\n\n}<\/code><\/pre>\n\n\n\n<p>Stop Logstash;<\/p>\n\n\n\n<pre class=\"wp-block-preformatted\"><code>systemctl stop logstash<\/code><\/pre>\n\n\n\n<p>The run Logstash against your filter configuration file;<\/p>\n\n\n\n<pre class=\"wp-block-preformatted\"><code>\/usr\/share\/logstash\/bin\/logstash -f \/etc\/logstash\/conf.d\/modsecurity-filter.conf --path.settings \/etc\/logstash\/<\/code><\/pre>\n\n\n\n<p><strong>While this run, ensure that the ModSecurity logs are streaming in so they can be processed as you see what happens.<\/strong><\/p>\n\n\n\n<p>Here is a sample debugging output;<\/p>\n\n\n\n<pre class=\"scroll-box\"><code>{\n            \"ecs\" =&gt; {\n        \"version\" =&gt; \"1.5.0\"\n    },\n         <strong>\"src_ip\" =&gt; \"253.63.240.101\",<\/strong>\n       <strong>\"dst_host\" =&gt; \"kifarunix-demo.com\",<\/strong>\n     \"@timestamp\" =&gt; 2020-07-31T08:47:36.940Z,\n           \"tags\" =&gt; [\n        [0] \"beats_input_codec_plain_applied\",\n        [1] \"_grokparsefailure\"\n    ],\n          \"input\" =&gt; {\n        \"type\" =&gt; \"log\"\n    },\n...\n...\n    },\n     <strong>\"rules_file\" =&gt; \"\/etc\/httpd\/conf.d\/modsecurity.d\/owasp-crs\/rules\/REQUEST-913-SCANNER-DETECTION.conf\"<\/strong>,\n            \"log\" =&gt; {\n        \"offset\" =&gt; 10787,\n          \"file\" =&gt; {\n            \"path\" =&gt; \"\/var\/log\/httpd\/modsec_audit.log\"\n        }\n    },\n<strong>        \"rule_id\" =&gt; \"913100\",\n      \"alert_msg\" =&gt; \"Found User-Agent associated with security scanner\",\n     \"user_agent\" =&gt; \"sqlmap\/1.2.4#stable (http\/\/sqlmap.org)\",<\/strong>\n        \"message\" =&gt; \"[Tue Jul 14 16:39:28.086871 2020] [:error] [pid 83149:tid 139784827672320] [client 253.63.240.101:50102] ModSecurity: Warning. Matched \\\"Operator `PmFromFile' with parameter `scanners-user-agents.data' against variable `REQUEST_HEADERS:User-Agent' (Value: `sqlmap\/1.2.4#stable (http:\/\/sqlmap.org)' ) [file \\\"\/etc\/httpd\/conf.d\/modsecurity.d\/owasp-crs\/rules\/REQUEST-913-SCANNER-DETECTION.conf\\\"] [line \\\"33\\\"] [id \\\"913100\\\"] [rev \\\"\\\"] [msg \\\"Found User-Agent associated with security scanner\\\"] [data \\\"Matched Data: sqlmap found within REQUEST_HEADERS:User-Agent: sqlmap\/1.2.4#stable (http:\/\/sqlmap.org)\\\"] [severity \\\"2\\\"] [ver \\\"OWASP_CRS\/3.2.0\\\"] [maturity \\\"0\\\"] [accuracy \\\"0\\\"] [tag \\\"application-multi\\\"] [tag \\\"language-multi\\\"] [tag \\\"platform-multi\\\"] [tag \\\"attack-reputation-scanner\\\"] [tag \\\"paranoia-level\/1\\\"] [tag \\\"OWASP_CRS\\\"] [tag \\\"OWASP_CRS\/AUTOMATION\/SECURITY_SCANNER\\\"] [tag \\\"WASCTC\/WASC-21\\\"] [tag \\\"OWASP_TOP_10\/A7\\\"] [tag \\\"PCI\/6.5.10\\\"] [hostname \\\"kifarunix-demo.com\\\"] [uri \\\"\/\\\"] [unique_id \\\"159473756862.024448\\\"] [ref \\\"o0,6v179,39t:lowercase\\\"], referer: http:\/\/kifarunix-demo.com:80\/?p=1006\",\n<strong>    \"attack_type\" =&gt; \"SCANNER-DETECTION\",\n    \"request_uri\" =&gt; \"\/\",\n     \"event_time\" =&gt; \"Jul 14 16:39:28.086871 2020\",\n        \"referer\" =&gt; \"http:\/\/kifarunix-demo.com:80\/?p=1006\",\n      \"log_level\" =&gt; \"error\",<\/strong>\n       \"@version\" =&gt; \"1\"\n}\n<\/code><\/pre>\n\n\n\n<p>Once done debugging, you can then re-enable Elasticsearch output.<\/p>\n\n\n\n<p>Check that your Elasticsearch Index has been created;<\/p>\n\n\n\n<pre class=\"wp-block-preformatted\"><code>curl -XGET 192.168.56.119:9200\/_cat\/indices\/modsec-*?<\/code><\/pre>\n\n\n\n<pre class=\"wp-block-preformatted\"><code>yellow open modsec-2020.07 bKLGOrJ8SFCAnewz89XvUA 1 1 46 0 104.4kb 104.4kb<\/code><\/pre>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"visualize-modsecurity-logs-on-kibana\"><a href=\"#visualize-modsecurity-logs-on-kibana\">Visualize ModSecurity Logs on Kibana<\/a><\/h3>\n\n\n\n<h4 class=\"wp-block-heading\">Create Kibana Index<\/h4>\n\n\n\n<p>On Kibana Dashboard, Navigate to&nbsp;<strong>Management &gt; Kibana &gt; Index Patterns &gt; Create index pattern<\/strong>. An index pattern can match the name of a single index, or include a wildcard (*) to match multiple indices.<\/p>\n\n\n\n<p>On the next page, select&nbsp;<strong>@timestamp<\/strong>&nbsp;as the time filter and click&nbsp;<strong>Create index pattern<\/strong>.<\/p>\n\n\n\n<p>After that, click&nbsp;<strong>Discover tab<\/strong>&nbsp;on the left pane to view the data. Select your Index pattern, in this case, <strong>modsec-*<\/strong>.<\/p>\n\n\n\n<p>The view below shows our fields selected as per our Logstash filters.<\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><img loading=\"lazy\" decoding=\"async\" width=\"1901\" height=\"960\" src=\"https:\/\/kifarunix.com\/wp-content\/uploads\/2020\/07\/modsecurity-elk-stack.png\" alt=\"Process and Visualize ModSecurity Logs on ELK Stack\" class=\"wp-image-6565\" title=\"\" srcset=\"https:\/\/kifarunix.com\/wp-content\/uploads\/2020\/07\/modsecurity-elk-stack.png?v=1596203286 1901w, https:\/\/kifarunix.com\/wp-content\/uploads\/2020\/07\/modsecurity-elk-stack-768x388.png?v=1596203286 768w, https:\/\/kifarunix.com\/wp-content\/uploads\/2020\/07\/modsecurity-elk-stack-1536x776.png?v=1596203286 1536w\" sizes=\"(max-width: 1901px) 100vw, 1901px\" \/><\/figure>\n\n\n\n<p>And there you go. You have your ModSecurity log fields populated on Kibana.<\/p>\n\n\n\n<p>To complete this tutorial on how to process ModSecurity Logs and visualize on ELK Stack, you can now proceed to create ModSecurity Kibana visualization dashboards.<\/p>\n\n\n\n<p><a href=\"https:\/\/kifarunix.com\/create-kibana-visualization-dashboards-for-modsecurity-logs\/\" target=\"_blank\" aria-label=\"undefined (opens in a new tab)\" rel=\"noreferrer noopener\">Create Kibana Visualization Dashboards for ModSecurity Logs<\/a><\/p>\n\n\n\n<p>Follow the link below to create ModSecurity Kibana visualization dashboards.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Further Reading<\/h3>\n\n\n\n<p><a aria-label=\"undefined (opens in a new tab)\" href=\"https:\/\/www.nginx.com\/blog\/modsecurity-logging-and-debugging\/\" target=\"_blank\" rel=\"noreferrer noopener\">ModSecurity Logging and Debugging<\/a><\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Related Tutorials<\/h3>\n\n\n\n<p><a rel=\"noreferrer noopener\" href=\"https:\/\/kifarunix.com\/how-to-debug-logstash-grok-filters\/\" target=\"_blank\">How to Debug Logstash Grok Filters<\/a><\/p>\n\n\n\n<p><a rel=\"noreferrer noopener\" href=\"https:\/\/kifarunix.com\/install-logstash-7-on-fedora-30-fedora-29-centos-7\/\" target=\"_blank\">Install Logstash 7 on Fedora 30\/Fedora 29\/CentOS 7<\/a><\/p>\n\n\n\n<p><a aria-label=\"undefined (opens in a new tab)\" href=\"https:\/\/kifarunix.com\/send-windows-logs-to-elastic-stack-using-winlogbeat-and-sysmon\/\" target=\"_blank\" rel=\"noreferrer noopener\">Send Windows logs to Elastic Stack using Winlogbeat and Sysmon<\/a><\/p>\n\n\n\n<p><a aria-label=\"undefined (opens in a new tab)\" href=\"https:\/\/kifarunix.com\/install-and-configure-elastic-auditbeat-on-ubuntu-18-04\/\" target=\"_blank\" rel=\"noreferrer noopener\">Install and Configure Elastic Auditbeat on Ubuntu 18.04<\/a><\/p>\n","protected":false},"excerpt":{"rendered":"<p>In this tutorial, you will learn how to process and visualize ModSecurity Logs on ELK Stack. ModSecurity is an open source, cross-platform web application firewall<\/p>\n","protected":false},"author":3,"featured_media":14353,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"rank_math_lock_modified_date":false,"footnotes":""},"categories":[121,910,1207,72,34],"tags":[1850,1853,1852,920,1141,1851,1849],"class_list":["post-6434","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-howtos","category-elastic-stack","category-modsecurity","category-monitoring","category-security","tag-create-modsecurity-logstash-filters","tag-elastic","tag-elk-stack","tag-kibana","tag-modsecurity-3","tag-modsecurity-kibana-dashboards","tag-visualize-modsecurity-logs-on-kibana","generate-columns","tablet-grid-50","mobile-grid-100","grid-parent","grid-50","resize-featured-image"],"_links":{"self":[{"href":"https:\/\/kifarunix.com\/wp-json\/wp\/v2\/posts\/6434"}],"collection":[{"href":"https:\/\/kifarunix.com\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/kifarunix.com\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/kifarunix.com\/wp-json\/wp\/v2\/users\/3"}],"replies":[{"embeddable":true,"href":"https:\/\/kifarunix.com\/wp-json\/wp\/v2\/comments?post=6434"}],"version-history":[{"count":20,"href":"https:\/\/kifarunix.com\/wp-json\/wp\/v2\/posts\/6434\/revisions"}],"predecessor-version":[{"id":21346,"href":"https:\/\/kifarunix.com\/wp-json\/wp\/v2\/posts\/6434\/revisions\/21346"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/kifarunix.com\/wp-json\/wp\/v2\/media\/14353"}],"wp:attachment":[{"href":"https:\/\/kifarunix.com\/wp-json\/wp\/v2\/media?parent=6434"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/kifarunix.com\/wp-json\/wp\/v2\/categories?post=6434"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/kifarunix.com\/wp-json\/wp\/v2\/tags?post=6434"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}