Sonicwall Pipeline Rules

Sonicwall: TZ470 running SonicOS 7.01

Input type: Sylog/UDP

Rule summary:

Stage 0: Primary rule uses the “key_value” function to parse the delimited message using the default delimiter of space.

Stage 1: Four rules to split the port from the IP address, as Sonicwall sends as XXX.XXX.XXX.XXX:YYYY and sometimes as XXX.XXX.XXX.XXX:YYYY:INTERFACENAME

State 2: GeoIP Lookups on Source and Destination, as well as removing some of the redundant fields caused by Stage 1 rules.

Stage 3: Whois Lookups on Source and Destination.

Rules

Stage 0:

rule "Extract: Sonicwall Extraction"
when
    has_field("source") AND contains(to_string($message.source), "10.0.0.1", true)
then
    set_fields(
        fields:key_value(
            value:to_string($message.message),
            //remove double quotes from keys and values
            trim_value_chars:"\"", 
            trim_key_chars:"\""
            )
        );
end

Stage 1:

rule "Sonicwall Split IP Port Interface for dst"
when
    has_field("dst")
then
    set_fields(
        fields:
            grok(
                pattern:"%{IPV4:DstIP}:%{INT:DstPort}:%{GREEDYDATA:DstInterface}",
                value: to_string($message.dst)
            )
        );
end
rule "Sonicwall Split IP Port Interface for natDst"
when
    has_field("natDst")
then
    set_fields(
        fields:
            grok(
                pattern:"%{IPV4:NatDstIP}:%{INT:NatDstPort}",
                value: to_string($message.natDst)
            )
        );
end
rule "Sonicwall Split IP Port Interface for natSrc"
when
    has_field("natSrc")
then
    set_fields(
        fields:
            grok(
                pattern:"%{IPV4:NatSrcIP}:%{INT:NatSrcPort}",
                value: to_string($message.natSrc)
            )
        );
end
rule "Sonicwall Split IP Port Interface for src"
when
    has_field("src")
then
    set_fields(
        fields:
            grok(
                pattern:"%{IPV4:SrcIP}:%{INT:SrcPort}:%{GREEDYDATA:SrcInterface}",
                value: to_string($message.src)
            )
        );
end
rule "Sonicwall Split Protocol Service for proto"
when
    has_field("proto") and contains(to_string($message.proto),"/")
then
    set_fields(
        fields:
            grok(
                pattern:"%{DATA:Protocol}/%{GREEDYDATA:Service}",
                value: to_string($message.proto)
            )
        );
end
rule "Sonicwall Split Protocol Service for proto-icmp"
when
    has_field("proto") and contains(to_string($message.proto),"icmp")
then
    set_field("Protocol",to_string($message.proto));
end

Stage 2:

rule "GeoIP lookup: DstIP"
when
    has_field("DstIP")
then
    let geo = lookup("geoip", to_string($message.DstIP));
    set_field("DstIP_GeoLocation", geo["coordinates"]);
    set_field("DstIP_GeoCountryIsoCode", geo["country"].iso_code);
    set_field("DstIP_GeoCountryName", geo["country"].names.en);
    set_field("DstIP_GeoCityName", geo["city"].names.en);
end
rule "GeoIP lookup: SrcIP"
when
    has_field("SrcIP")
then
    let geo = lookup("geoip", to_string($message.SrcIP));
    set_field("SrcIP_GeoLocation", geo["coordinates"]);
    set_field("SrcIP_GeoCountryIsoCode", geo["country"].iso_code);
    set_field("SrcIP_GeoCountryName", geo["country"].names.en);
    set_field("SrcIP_GeoCityName", geo["city"].names.en);
end
rule "Remove Redundant Fields-Sonicwall"
when 
    has_field("id") and contains(to_string($message.id),"firewall")
then
    remove_field("dst",$message);
    remove_field("natDst",$message);
    remove_field("natSrc",$message);
    remove_field("proto",$message);
    remove_field("src",$message);
end

Stage 3:

rule "Whois lookup: DstIP"
when
    has_field("DstIP") && 
    is_ip(to_ip($message.DstIP)) &&
    //Above is set to diagnose why whois lookups sometimes fail for me.
    !in_private_net(to_string($message.DstIP)) &&
    //Above is set to avoid whois lookups on internal addresses
    !cidr_match("/24",to_ip("1.1.1.0")) &&
    //Above is set to avoid whois lookups on frequently used DNS service.
    to_string($message.DstPort) != "43"
    //Above is set to avoid whois lookups on whois lookup messages.
then
    let whoislookup = whois_lookup_ip(to_string($message.DstIP),"DstIP");
    set_fields(whoislookup);
    //set_field("Whois_DstIP_Lookup","True");
end
rule "Whois lookup: SrcIP"
when
        has_field("SrcIP") && 
        is_ip(to_ip($message.SrcIP)) &&
        !in_private_net(to_string($message.SrcIP)) &&
        !cidr_match("/24",to_ip("1.1.1.0")) &&
        to_string($message.SrcPort) != "43"
then
    let whoislookup = whois_lookup_ip(to_string($message.SrcIP),"SrcIP");
    set_fields(whoislookup);
    //set_field("Whois_SrcIP_Lookup","True");
end

END OF RULES

Happy to answer questions or accept feedback on any of this. Hope it helps someone!

2 Likes

@faen

Holy cow “Batman” :smiley: Thanks for sharing :+1:

1 Like

I had an error in my usage of cidr_match that I had hoped to correct with an edit, but it seems like I can only edit the post once, so here’s revised whois lookup rules with correct usage of cidr_match:

rule "Whois lookup: DstIP"
when
    has_field("DstIP") && 
    is_ip(to_ip($message.DstIP)) &&
    !in_private_net(to_string($message.DstIP)) &&
    !cidr_match("169.254.0.0/16",to_ip($message.DstIP)) &&
    //The private nets function does not include the 169.254.0.0 used by multicast
    !cidr_match("1.1.1.0/24",to_ip($message.DstIP)) &&
    to_string($message.DstPort) != "43"
then
    let whoislookup = whois_lookup_ip(to_string($message.DstIP),"DstIP");
    set_fields(whoislookup);
end
rule "Whois lookup: SrcIP"
when
    has_field("SrcIP") && 
    is_ip(to_ip($message.SrcIP)) &&
    !in_private_net(to_string($message.SrcIP)) &&
    !cidr_match("169.254.0.0/16",to_ip($message.SrcIP)) &&
    //The private nets function does not include the 169.254.0.0 used by multicast
    !cidr_match("1.1.1.0/24",to_ip($message.SrcIP)) &&
    to_string($message.SrcPort) != "43"
then
    let whoislookup = whois_lookup_ip(to_string($message.SrcIP),"SrcIP");
    set_fields(whoislookup);
end

I’ve also incorporated Alienvault OTX lookups into my pipeline rules for Sonicwall. Fiddling around with it still, but will post soon.

2 Likes

That’s some amazing work you got there Faen.

I’m not really sure how to pickup the Whois lookup part tho.

The stage seems to run and has a throughput but I don’t have a DNS resolution name in the fields.
Am I missing a subsequent step?

Thanks

Hi there, thanks for the compliment!

For the whois lookups, I first installed the “Whois - Threat Intel Plugin” and configured the lookup table options. Have you done that aleady?

Yeah! Of course… I’m not stup…

Ho thanks.

But seriously Thank you for your fast response|


Now I’d be interested in having a LOCAL HOST NAME field in the log messages… How would a genius like you tackle this?
I was on the track of doing a lookup table but now that you know my general knowledge… You wouldn’t be surprised I had to stop at some point.

Thank you

That’s a tough one. How big is your local network and how often do things change? In my case, I could do a static lookup table pretty easily, but that doesn’t take into account DHCP hosts. Perhaps you could find a way to query your DHCP server periodically and load that data as a lookup table programmatically?

Graylog has a DNS lookup Data Adapter that is built in that you can use against a local DNS server. The instructions on how are built into the page where you build it…

2 Likes

Beg your pardon If this is the first step to a process but the task at hand is to:

Get the host names out of ip adresses.

The Dns lookup Adapter there seems to resolve Hostnames to ip adresses.

Can you point me a direction for the next step?

Thank you

Use the reverse lookup (PTR) function of that lookup.

Edit to add the screen shot.

2 Likes

I noticed a weird thing:
Your pipeline seems fine if I test this part:
(SRC)
%{IPV4:SrcIP}:%{INT:SrcPort}:%{GREEDYDATA:SrcInterface}

{
  "well": "192.168.1.144:5060:X0",
  "SrcIP": "192.168.1.144",
  "SrcPort": "5060",
  "SrcInterface": "X0"
}

But the SrcIp, SrcPort and SrcInterface fields aren’t showing up in the messages.

the (dst) works fine tho…
image

I’m not sure on how to troubleshoot this. Is it working on your side?

Any idea?

Thank you

The Src messages are working for me - I would use the debug command, or also append text static fields at each step.

Could you post your entire pipeline rule?

1 Like

Sure… But You’ll find it very familiar… To my understanding, the only thing I had to change was the <>. But maybe be thats where the mistake resides.

rule "Extract: Sonicwall Extraction"
when
    has_field("source") AND contains(to_string($message.source), "172.23.0.1", true)
then
    set_fields(
        fields:key_value(
            value:to_string($message.message),
            //remove double quotes from keys and values
            trim_value_chars:"\"", 
            trim_key_chars:"\""
            )
        );
end
rule "Sonicwall Split IP Port Interface for dst"
when
    has_field("dst")
then
    set_fields(
        fields:
            grok(
                pattern:"%{IPV4:DstIP}:%{INT:DstPort}:%{GREEDYDATA:DstInterface}",
                value: to_string($message.dst)
            )
        );
end

rule "Sonicwall Split IP Port Interface for natDst"
when
    has_field("natDst")
then
    set_fields(
        fields:
            grok(
                pattern:"%{IPV4:NatDstIP}:%{INT:NatDstPort}",
                value: to_string($message.natDst)
            )
        );
end
rule "Sonicwall Split IP Port Interface for natSrc"
when
    has_field("natSrc")
then
    set_fields(
        fields:
            grok(
                pattern:"%{IPV4:NatSrcIP}:%{INT:NatSrcPort}",
                value: to_string($message.natSrc)
            )
        );
end
rule "Sonicwall Split IP Port Interface for src"
when
    has_field("src")
then
    set_fields(
        fields:
            grok(
                pattern:"%{IPV4:SrcIP}:%{INT:SrcPort}:%{GREEDYDATA:SrcInterface}",
                value: to_string($message.src)
            )
        );
end

rule "Sonicwall Split Protocol Service for proto"
when
    has_field("proto") and contains(to_string($message.proto),"/")
then
    set_fields(
        fields:
            grok(
                pattern:"%{DATA:Protocol}/%{GREEDYDATA:Service}",
                value: to_string($message.proto)
            )
        );
end
rule "Sonicwall Split Protocol Service for proto-icmp"
when
    has_field("proto") and contains(to_string($message.proto),"icmp")
then
    set_field("Protocol",to_string($message.proto));
end
rule "GeoIP lookup: DstIP"
when
    has_field("DstIP")
then
    let geo = lookup("geoip", to_string($message.DstIP));
    set_field("DstIP_GeoLocation", geo["coordinates"]);
    set_field("DstIP_GeoCountryIsoCode", geo["country"].iso_code);
    set_field("DstIP_GeoCountryName", geo["country"].names.en);
    set_field("DstIP_GeoCityName", geo["city"].names.en);
end
rule "GeoIP lookup: SrcIP"
when
    has_field("SrcIP")
then
    let geo = lookup("geoip", to_string($message.SrcIP));
    set_field("SrcIP_GeoLocation", geo["coordinates"]);
    set_field("SrcIP_GeoCountryIsoCode", geo["country"].iso_code);
    set_field("SrcIP_GeoCountryName", geo["country"].names.en);
    set_field("SrcIP_GeoCityName", geo["city"].names.en);
end
rule "Remove Redundant Fields-Sonicwall"
when 
    has_field("id") and contains(to_string($message.id),"firewall")
then
    remove_field("dst",$message);
    remove_field("natDst",$message);
    remove_field("natSrc",$message);
    remove_field("proto",$message);
    remove_field("src",$message);
end
rule "Whois lookup: DstIP"
when
    has_field("DstIP") && 
    is_ip(to_ip($message.DstIP)) &&
    !in_private_net(to_string($message.DstIP)) &&
    !cidr_match("169.254.0.0/16",to_ip($message.DstIP)) &&
    //The private nets function does not include the 169.254.0.0 used by multicast
    !cidr_match("1.1.1.0/24",to_ip($message.DstIP)) &&
    to_string($message.DstPort) != "43"
then
    let whoislookup = whois_lookup_ip(to_string($message.DstIP),"DstIP");
    set_fields(whoislookup);
end
rule "Whois lookup: SrcIP"
when
    has_field("SrcIP") && 
    is_ip(to_ip($message.SrcIP)) &&
    !in_private_net(to_string($message.SrcIP)) &&
    !cidr_match("169.254.0.0/16",to_ip($message.SrcIP)) &&
    //The private nets function does not include the 169.254.0.0 used by multicast
    !cidr_match("1.1.1.0/24",to_ip($message.SrcIP)) &&
    to_string($message.SrcPort) != "43"
then
    let whoislookup = whois_lookup_ip(to_string($message.SrcIP),"SrcIP");
    set_fields(whoislookup);
end

Where is the “well” field coming from in your previous post? I am not seeing it in any of the rules. The rules look OK to me, I would try adding some static fields in the “then” section to see if you can figure out where it is fouling up.

Sounded very familiar…

Well is the name of the grok pattern I didn’t even save after I tested the src field to validate my log messages were ‘‘supposed’’ to work the same as yours.

But yeah I checked again… it didn’t fix it self.

DstIP
192.168.1.9
DstInterface
X0
DstPort
53
app
2
c
1024
cdur
32000
dpi
0
dst
192.168.1.9:53:X0
dstMac
MAC
dstZone
LAN
facility
local0
facility_num
16
full_message
THEFULLMESSAGE
fw
199.168.220.154
fw_action
NA
gcat
6
id
gl2_source_input
level
6
m
537
message
THEMESSAGE
msg
Connection Closed
n
215116872
pri
6
proto
udp/dns
rcvd
135
rpkt
1
rule
Default Access Rule
sent
64
sess
sslvpnc
sn
REDACTED
source
172.23.0.1
spkt
1
src
192.168.101.8:59462:X1
srcZone
SSLVPN
time
2023-03-02 14:43:35
timestamp
2023-03-02 14:43:35.586
usr
ME!

Really looks like this part is just skipped

rule "Sonicwall Split IP Port Interface for src"
when
    has_field("src")
then
    set_fields(
        fields:
            grok(
                pattern:"%{IPV4:SrcIP}:%{INT:SrcPort}:%{GREEDYDATA:SrcInterface}",
                value: to_string($message.src)
            )
        );
end

Does this look right? Should I separate Dst From Src maybe? :expressionless:

EDIT:

Yep I split and it seems to work @faen Are you using GL5? I’m trying to find the cause.How did you set the rules compliance for each stages?
Now that I split the Src part to another stage. I get the srcip BUT
It won’t split the Proto part…How many rules pert stage can I set… I’m starting (again) to be comfused here.

Thanks

I have the Src rule as a separate rule within the stage. Here are my stage rules:

The reason I have all of these rules is because they take action on what was done subsequently. You might need to split it up a bit more to get what you are looking for.

1 Like

I am trying to implement these rules one at a time but it does not appear like anything is being done. I am showing 0 messages being processed:

rule "Extract: Sonicwall Extraction"
when
    has_field("source") AND contains(to_string($message.source), "192.168.1.1", true)
then
    set_fields(
        fields:key_value(
            value:to_string($message.message),
            //remove double quotes from keys and values
            trim_value_chars:"\"", 
            trim_key_chars:"\""
            )
        );
end

Screenshot does not show it but it’s tied to the correct stream.

Message from stream:

image