I have been working on getting some detailed logging from Snort logs
generated through PFSense and thought I would share them. This can also
be modified to work with a Snort setup not running on PFSense as well.
Can also modify for Suricata if needed.
In order to send your Snort logs you will need an instance of logstash
running on your Snort node with the following added to your
logstash.conf file. Modify to suit your specific log location.
input {
file {
path => "/var/log/snort/alert"
type => "snort"
sincedb_path => "/var/log/.snortsincedb"
}
}
# Setting up PFsense Firewall parsing
filter {
if "PFSense" in [tags] {
mutate {
add_tag => [ "firewall" ]
}
grok {
match => [
"message", "<%{POSINT:syslog_pri}>%{SYSLOGBASE} %{NOTSPACE} %{GREEDYDATA:pfsense_message}",
"message", "<%{POSINT:syslog_pri}>%{SYSLOGBASE} %{GREEDYDATA:pfsense_message}"
]
}
mutate {
add_field => [ "orig_message", "%{message}" ]
}
syslog_pri { }
date {
match => [ "timestamp", "MMM d HH:mm:ss", "MMM dd HH:mm:ss", "MMM d HH:mm:ss,SSS", "MMM dd HH:mm:ss,SSS", "ISO8601" ]
timezone => "America/New_York"
}
mutate {
replace => [ "message", "%{pfsense_message}" ]
}
if [program] =~ /^pf$/ {
mutate {
add_tag => [ "packetfilter" ]
}
multiline {
pattern => "^\s+|^\t\s+"
what => "previous"
}
mutate {
gsub => ['message', "\n", " "]
}
grok {
match => [
"message", "rule %{NOTSPACE:rule}\(.*\): %{WORD:action} .* on %{NOTSPACE:iface}: .* proto %{WORD:proto}%{GREEDYDATA}%{IP:src_ip}\.%{INT:src_port} [<|>] %{IP:dst_ip}\.%{INT:dst_port}: %{GREEDYDATA}",
"message", "%{GREEDYDATA}"
]
}
mutate {
remove_field => [ "pfsense_message" ]
}
}
if [program] =~ /^dhcpd$/ {
if [message] =~ /^DHCPACK|^DHCPREQUEST|^DHCPOFFER/ {
grok {
match => [ "message", "(?<action>.*) (on|for|to) (?<src_ip>[0-2]?[0-9]?[0-9]\.[0-2]?[0-9]?[0-9]\.[0-2]?[0-9]?[0-9]\.[0-2]?[0-9]?[0-9]) .*(?<mac_address>[0-9a-fA-F][0-9a-fA-F]:[0-9a-fA-F][0-9a-fA-F]:[0-9a-fA-F][0-9a-fA-F]:[0-9a-fA-F][0-9a-fA-F]:[0-9a-fA-F][0-9a-fA-F]:[0-9a-fA-F][0-9a-fA-F]).* via (?<iface>.*)" ]
}
}
if [message] =~ /^DHCPDISCOVER/ {
grok {
match => [ "message", "(?<action>.*) from (?<mac_address>[0-9a-fA-F][0-9a-fA-F]:[0-9a-fA-F][0-9a-fA-F]:[0-9a-fA-F][0-9a-fA-F]:[0-9a-fA-F][0-9a-fA-F]:[0-9a-fA-F][0-9a-fA-F]:[0-9a-fA-F][0-9a-fA-F]).* via (?<iface>.*)" ]
}
}
if [message] =~ /^DHCPINFORM/ {
grok {
match => [ "message", "(?<action>.*) from (?<src_ip>.*).* via (?<iface>.*)" ]
}
}
}
if [program] == "suricata" {
mutate {
add_tag => [ "suricata" ]
}
}
if [program] == "snort" {
mutate {
add_tag => [ "snort" ]
}
}
mutate {
replace => [ "host", "%{logsource}" ]
add_tag => [ "pre-processed" ]
remove_field => [ "logsource", "pfsense_message", "syslog_host_id" ]
}
}
}
# Snort parsing
filter {
if "snort" in [tags] {
if "PFSense" in [tags] {
grok {
match => [
"orig_message", "snort\[%{INT:snort_pid}\]\:.*\[%{INT:ids_gid}\:%{INT:ids_sid}\:%{INT:ids_rev}\].%{GREEDYDATA:ids_alert}.\[Classification\: %{DATA:ids_classification}\].*\[Priority\: %{INT:ids
_priority}].*{%{WORD:ids_proto}}.*%{IP:src_ip}:%{INT:src_port} \-\>.*%{IP:dst_ip}:%{INT:dst_port}",
"orig_message", "snort\[%{INT:snort_pid}\]\:.*\[%{INT:ids_gid}\:%{INT:ids_sid}\:%{INT:ids_rev}\].%{GREEDYDATA:ids_alert}.\[Classification\: %{DATA:ids_classification}\].*\[Priority\: %{INT:ids
_priority}].*\{PROTO:%{WORD:ids_proto}.*%{IP:src_ip} \-\>.*%{IP:dst_ip}"
]
}
grok {
match => [
"orig_message", "snort\[%{INT}\]\:.\[%{DATA:ids_signature}:%{INT}].%{GREEDYDATA}"
]
}
}
if "PFsense" not in [tags] and [type] == "snort" {
grok {
match => [
"message", "%{DATESTAMP:timestamp}.*\[%{INT:ids_gid}\:%{INT:ids_sid}\:%{INT:ids_rev}\].%{GREEDYDATA:ids_alert}.\[Classification\: %{DATA:ids_classification}\].*\[Priority\: %{INT:ids_priority}
].*{%{WORD:ids_proto}}.*%{IP:src_ip}:%{INT:src_port} \-\>.*%{IP:dst_ip}:%{INT:dst_port}",
"message", "%{DATESTAMP:timestamp}.*\[%{INT:ids_gid}\:%{INT:ids_sid}\:%{INT:ids_rev}\].%{GREEDYDATA:ids_alert}.\[Classification\: %{DATA:ids_classification}\].*\[Priority\: %{INT:ids_priority}
].*\{PROTO:%{WORD:ids_proto}.*%{IP:src_ip} \-\>.*%{IP:dst_ip}"
]
}
grok {
match => [
"message", "%{DATESTAMP}.*\[%{DATA:ids_signature}:%{INT}].%{GREEDYDATA}"
]
}
}
translate {
field => "ids_priority"
destination => "ids_priority_full"
dictionary => [
"1", "High",
"2", "Medium",
"3", "Low"
]
}
geoip {
source => "src_ip"
target => "geoip"
add_field => [ "[geoip][coordinates]", "%{[geoip][longitude]}" ]
add_field => [ "[geoip][coordinates]", "%{[geoip][latitude]}" ]
}
mutate {
convert => [ "[geoip][coordinates]", "float" ]
}
if [ids_signature] {
if [ids_alert] =~ /^GPL/ {
mutate {
add_tag => [ "Snort-ET-sig" ]
add_field => [ "ids_rule_type", "Emerging Threats" ]
}
}
if [ids_alert] =~ /^ET/ {
mutate {
add_tag => [ "Snort-ET-sig" ]
add_field => [ "ids_rule_type", "Emerging Threats" ]
}
}
if "Snort-ET-sig" not in [tags] {
mutate {
add_tag => [ "Snort-sig" ]
add_field => [ "ids_rule_type", "Snort" ]
}
}
}
if "Snort-sig" in [tags] {
if [ids_gid] == "1" {
mutate {
add_field => [ "Signature_Info", "http://rootedyour.com/snortsid?sid=%{ids_sid}" ]
}
}
if [ids_gid] != "1" {
mutate {
add_field => [ "Signature_Info", "http://rootedyour.com/snortsid?sid=%{ids_gid}-%{ids_sid}" ]
}
}
}
if "Snort-ET-sig" in [tags] {
mutate {
add_field => [ "Signature_Info", "http://doc.emergingthreats.net/bin/view/Main/%{ids_sid}" ]
}
}
}
}
Here are some sample Kibana dashboards.
Enjoy!