The organization you work for is considering deploying Suricata to enhance its traffic inspection capabilities. The IT Security manager provided you with some Suricata rules to get familiar with their structure. He also provided you with PCAP files containing malicious traffic so that you can experiment with writing Suricata detection rules.
A test instance of Suricata has already been set up and is waiting for you!
The learning objective of this lab is not only to get familiar with the detection capabilities of Suricata but also to learn effective rule writing of Suricata rules.
Specifically, you will learn how to use Suricata's capabilities in order to:
Rules are essentially instructions to the Suricata engine to look for specific markers in network traffic that we want to be notified about if they are seen.
Rules are not always oriented towards detecting malicious traffic. Rules can also be written to provide blue team members with actionable and/or contextual information regarding activity that is occurring on a network.
Rules can be as specific or as broad as we want them to be. Finding the perfect balance is important in order, for example, to be able to detect variations of a given malware but also avoid false positives. They can be seen as a big AND statement (multiple contents are specified, and the rule will trigger an alert if and only if all of these contents are seen in the passing traffic).
Threat intelligence and infosec communities usually offer critical information based on which rules are developed.
Each active rule consumes some of the host's CPU and memory. Specific guidelines exist to assist effective Suricata rule writing.
action protocol from_ip port -> to_ip port (msg:"we are under attack by X"; content:"something"; content:"something else"; sid:10000000; rev:1;)
Header (action - port part): This rule portion includes what action we want the rule to have along with the protocol Suricata should expect this rule to be found in. It also includes IPs and ports, as well as an arrow indicating directionality.
Rule message & Contents [(msg -else";) part]: The rule message is the message we want the analysts (or our self) to be presented with, whereas the contents are the portions of the traffic that we have deemed critical in order to detect activity that we want to be informed of.
Rule metadata [(sid - 1;) part]: This rule portion mainly helps to keep track of rule modifications. Sid is a unique rule identifier.
Let's dive into the Header.
action tells the Suricata engine what to do should the contents are matched. It can be:
Alert <- Generate alert and log matching packets, but let the traffic through
Log <- Log traffic (no alert)
Pass <- Ignore the packet and allow it through
Drop <- If in IPS mode, drop the packet
Reject <- IDS will send TCP RST packet(s)
protocol can be: tcp, udp, icmp, ip, http, tls, smb, dns
Then, we need a way to declare the directionality of the traffic; this can be done through:
Rule Host Variables. We have seen those variables when analyzing the suricata.yaml file. As a reminder, $HOME_NET refers to internal networks specified in the suricata.yaml file and $EXTERNAL_NET usually refers to whatever is not included in the $HOME_NET variable.
Rule Direction. Between the 2 IP-Port pairs an arrow exists. This arrow tells the Suricata engine the direction in which the traffic is flowing. Examples:
Outbound traffic $HOME_NET any -> $EXTERNAL_NET any
Inbound traffic $EXTENRAL_NET any -> $HOME_NET any
Bidirectional traffic $EXTENRAL_NET any <> $HOME_NET any
Rule Ports declare the port(s) in which traffic for this rule will be evaluated. Examples:
alert tcp $HOME_NET any -> $EXTERNAL_NET 4444
alert tcp $HOME_NET any -> $EXTERNAL_NET $FTP_PORTS
port variables configurable in suricata.yaml
alert tcp $HOME_NET any -> $EXTERNAL_NET !80
alert tcp $HOME_NET [1024:] -> $EXTERNAL_NET [80,800,6667:6680,!6672]
Now, let's suppose you were required to create a header based on the PCAP below.
The rule header should be alert dns $HOME_NET any -> any any
any was used instead of $EXTERNAL_NET because some networks include internal DNS resolvers and you don't want to rule out that traffic.
Let's now dive into Rule message & Contents.
Rule Message. Arbitrary text that appears when the rule is triggered. For better understanding, it would nice if the rule messages you create contain malware architecture, malware family and malware action.
Flow. Declares the originator and the responder. Remember while developing rules that you want to have the engine looking at "established" tcp sessions. Examples:
flow:
alert tcp $HOME_NET any -> $EXTERNAL_NET 4444
If the protocol is UDP
Dsize. Allows matching using the size of the packet payload (not http, only tcp, and udp). It is based on TCP segment length, NOT total packet length (Wireshark filter: tcp.len). Examples:
dsize:312;
dsize:300<>400;
Rule Content. Values that identify a specific network traffic or activity. Suricata matches this unique content in packets for detection. Note that for certain characters, the use of hex is required. Examples:
content:"some thing";
content:"some|20|thing";
content:"User-Agent|3a 20|";
Consider the below rule content.
content:"POST"; content:"/trigger.php"; content:"DetoxCrypto";
content:"publickey";
By using buffers it will be transformed to the below.
content:"POST"; http_method; content:"/trigger.php"; http_uri;
content:"DetoxCrypto"; http_user_agent; content:"publickey"; http_client_body;
nocase. Helps rules to not get bypassed through case changes. Example:
content:"DetoxCrypto"; nocase; http_user_agent;
offset. Informs the Suricata engine about the position inside the packet where is should start matching. This option is used in conjunction with "depth". Examples:
content:"whatever"; offset:5;
content:"|00 03|"; offset:3; depth:2;
distance. Informs the Suricata engine to look for the specified content n bytes relative to the previous match. This option is used in conjunction with "within". Examples:
content:"whatever"; content:"something"; distance:5;
content:"whatever"; content:"something"; distance:0;
content:"|00 03|"; offset:3; depth:2;
content:"whatever"; distance:0; nocase; within:30;
Here are some additional examples to better comprehend what we covered so far.
We are looking for the string "whatever" (with nocase enabled) in the first 9 bytes of the packet. An offset of 0 is implied. Then, we are looking for the string "some thing" occurring anywhere after the first match of "whatever".
alert tcp $HOME_NET any -> $EXTERNAL_NET any (msg:"Malicious"; flow:established,to_server; dsize:45; content:"suspicious"; offset:33;
Finally, let's dive into Rule metadata.
reference. This is the first rule metadata field we usually come across. It indicates where the initial information to develop this rule came from. Example:
sid. This is the signature ID number. It is a unique number given by the rule writer. Example:
revision. This field informs about the version of the rule. Example:
Note: To conclude covering Suricata rules, let's talk about PCRE (Pearl Compatible Regular Expression). PCRE is a very powerful ally while developing rules. One can use PCRE through the pcre statement (followed by a regular expression). Note that PCRE must be wrapped in leading and trailing forward slashes and any flags will go after the last slash. Examples:
pcre:"/something/flags";
pcre:"/^\/[a-z0-9]{6}\.php$/Ui";
Note that anchors go after and before wrapped slashes and certain characters need to be escaped with a backslash. Additionally, never write a PCRE-only rule.
Suricata rules can be downloaded from:
Other Sources
The best way to manage Suricata rules is by using: https://github.com/OISF/suricata-update (https://suricata.readthedocs.io/en/latest/rule-management/suricata-update.html)
You can study more on Suricata rules on the official documentation site https://suricata.readthedocs.io/en/latest/rules/index.html
Below, you can find solutions for every task of this lab. As a reminder, you can follow your own strategy, which may be different from the one explained in the following lab.
Let's start with Rule 1.
alert dns $HOME_NET any -> any any (msg:"TROJAN X Rogue DNS Query Observed" dns_query; content:"default27061330-a.akamaihd.net"; isdataat:!1,relative; reference:url,threatintelprovider.com/trojanx; classtype:trojan-activity; sid:1; rev:1;)
This rule specifies the DNS protocol rather than UDP or something else. dns is a Suricata protocol keyword that allows the engine to detect DNS traffic based on the protocol and not the port. This protocol keyword (dns) should always be used when inspecting DNS traffic.
The destination host is set to any. As mentioned previously, this is because some networks include internal DNS resolvers and we don't want to rule out that traffic.
The destination port is set to any as well (instead of port 53); this is because we are using the dns protocol keyword which will help inspect DNS traffic regardless of the port used.
Then, there is a typical message that will inform us about the detected activity.
The main content of the rule starts with the dns_query keyword. dns_query is essentially a sticky buffer used to inspect the value of a DNS request. Being a sticky buffer results in all content following being included in the buffer. So, for this rule, we first declare the dns_query keyword and then the content, which contains the normalized domain that was included in the request. The dns_query buffer is normalized, so, one can use a literal period as opposed to the hexadecimal representation in the raw packet. Note that the DNS buffer doesn't include the null byte after the end of the domain. This is where the isdataat keyword comes into play. !1,relative means that there should be no data 1 byte relative to the previous match, which is effectively the domain name. Basically, this is a way to "lock" the match and avoid matching anything past the .net part.
The remaining part can be easily comprehended.
Let's continue with Rule 2.
alert tls $EXTERNAL_NET any -> $HOME_NET any (msg:"TROJAN Z malicious SSL Cert"; flow:established,to_client; tls_cert_subject; content:"CN=uniquestring"; classtype:trojan-activity; sid:1; rev:1;)
- This rule specifies the TLS protocol. tls is a Suricata protocol keyword that allows the engine to detect TLS traffic based on the protocol and not the port. By looking further down the rule, we are obviously checking for the delivery of a TLS certificate from an external server, hence the $EXTERNAL_NET any -> $HOME_NET any directionality. Once again, any is set for both ports since the tls keyword is utilized.
Then, there is a typical message that will inform us about the detected activity. flow:established,to_client; established is used due to TCP and to_client due to the inbound traffic.
tls_cert_subject is once again a buffer, and the rule content is a unique string included in the TLS certificate's CN portion.
The remaining part can be easily comprehended
Let's start with Sofacy.pcap
To learn more about Sofacy, refer to the following link https://securelist.com/sofacy-apt-hits-high-profile-targets-with-updated-toolset/72924/. We'll develop a Suricata rule based on our analysis of the Sofacy.pcap file, considering that what we see inside the PCAP is malicious.
By opening Sofacy.pcap in Wireshark, the first thing we notice is two (2) DNS queries and the respective DNS responses.
Let's create a Suricata rule to detect those two (2) C2 domains.
alert dns $HOME_NET any -> any any (msg:"TROJAN Activity Detected DNS Query to Known Sofacy Domain 1"; dns_query; content:"drivres-update.info"; nocase; isdataat:!1,relative; sid:1; rev:1;)
alert dns $HOME_NET any -> any any (msg:"TROJAN Activity Detected DNS Query to Known Sofacy Domain 2"; dns_query; content:"softupdates.info"; nocase; isdataat:!1,relative; sid:2; rev:1;)
Put these two (2) rules inside the local.rules file residing in the /etc/suricata/rules directory. You can do so, by executing-
nano /etc/suricata/rules/local.rules
On the machine's Desktop, you will find a bash script (automate_suricata.sh), by OISF's J Williams, that will automate running Suricata with the local.rules file, logging in a different location (/tmp/suricata), echoing the contents of fast.log and also cleaning previous logs before each new Suricata execution occurs.
Now before running "automate_suricata.sh" script, make sure to check whether the following path: /etc/suricata/rules/local.rules
is added in suricata.yaml configuration file or not, as shown in the below image.
Now, it's time to test the rule above. You can do so, as follows.
cd Desktop
./automate_suricata.sh ./PCAPs/Sofacy.pcap
You should see the following.
Let's continue with the Citi.pcap file. If you are unfamiliar with Citi, Citi is a global bank.
By opening the Citi.pcap in Wireshark, the first thing we notice is a phishy-looking DNS query (note that online.citi.com is a legitimate URL).
Let's create a Suricata rule to detect such phishing-related DNS traffic against online.citi.com.
alert dns $HOME_NET any -> any any (msg:"Possible Citi Phishing Attempt Observed in DNS Query "; dns_query; content:"online.citi.com"; nocase; isdataat:1,relative; sid:3; rev:1;)
isdataat:1,relative will inform us if any data exist after online.citi.com. There shouldn't be any data after it. If data are identified after online.citi.com, then, we are dealing with a phishing URL, similar to the one you can see in the PCAP file above (inside the red rectangle)
It's time to test the rule above. You can do so, as follows.
cd Desktop
./automate_suricata.sh ./PCAPs/Citi.pcap
You should see the following.
Now, it's time to analyze the Qadars.pcap file.
To learn more about Qadars, refer to the following links:
https://securityintelligence.com/an-analysis-of-the-qadars-trojan/
https://sslbl.abuse.ch/ssl-certificates/sha1/1862c777babf298fe5a93406e4dc8456d718abcf/
We'll develop a Suricata rule based on our analysis of the Qadars.pcap file, considering that what we see inside the PCAP is malicious.
By opening Qadars.pcap in Wireshark, the first thing we notice is a DNS query and a DNS response regarding susana24.com. Then, we notice TLS traffic initiating.
Let's focus on the TLS certificate this time and try to create a rule based on the CN portion of it (susana24.com).
alert tls $EXTERNAL_NET any -> $HOME_NET any (msg:"TROJAN Activity Observed Malicious SSL Cert (Qadars CnC)"; flow:established,to_client; tls_cert_subject; content:"CN=susana24.com"; classtype:trojan-activity; sid:4; rev:1;)
It's time to test the rule above. You can do so, as follows.
cd Desktop
./automate_suricata.sh ./PCAPs/Qadars.pcap
You should see the following.
Now, it's time to analyze the 7ev3n.pcap
To learn more about the 7ev3n ransomware, refer to the following link https://www.vmray.com/cyber-security-blog/7ev3n-honet-ransomware-rest-us/.
We'll develop a Suricata rule based on our analysis of the 7ev3n.pcap file, considering that what we see inside the PCAP is malicious.
By opening 7ev3n.pcap in Wireshark and filtering so that we can see HTTP traffic only, the first thing we notice is this curious-looking request.
Let's follow the whole stream.
The requests above can provide us with a lot of clues on how to develop an effective Suricata rule.
Viable rules can be found below.
alert http $HOME_NET any -> $EXTERNAL_NET any (msg:" TROJAN 7ev3n Ransomware CnC Checkin"; flow:established,to_server; content:"GET"; http_method; content:".php?RIGHTS="; http_uri; content:"&WIN="; http_uri; distance:0; content:"&WALLET="; http_uri; distance:0; content:"&ID="; http_uri; distance:0; content:"&UI="; http_uri; distance:0; content:"Internet|20|Explorer"; http_user_agent; depth:17; isdataat:!1,relative; http_header_names; content:!"Referer"; content:!"Accept"; sid:5; rev:1;)
alert http $HOME_NET any -> $EXTERNAL_NET any (msg:" TROJAN 7ev3n Ransomware Encryption Activity"; flow:established,to_server; content:"GET"; http_method; content:".php?SSTART="; http_uri; content:"&CRYPTED_DATA="; http_uri; distance:0; content:"&ID="; http_uri; distance:0; content:"&FILES="; http_uri; distance:0; content:"&UI="; http_uri; distance:0; content:"Internet|20|Explorer"; http_user_agent; depth:17; isdataat:!1,relative; http_header_names; content:!"Referer"; content:!"Accept"; sid:6; rev:1;)
depth:17; isdataat:!1,relative; is looking to see if there are any data after the last "r" of the "Internet Explorer" string, ensuring that the User Agent field only contains "Internet Explorer". http_header_names; content:!"Referer"; content:!"Accept"; is leveraging the lack of usual headers for detection purposes.
It's time to test the rule above. You can do so, as follows.
cd Desktop
./automate_suricata.sh ./PCAPs/7ev3n.pcap
You should see the following.
Finally, let's analyze the Malicious_document.pcap
Typically, malicious Office documents rely on macro execution, embedded objects, or exploits to deliver a payload onto the victim machine. In such cases, the URI can be so characteristic as to be used as solid rule content. The same applies for the User-Agent.
By opening the Malicious_document.pcap in Wireshark, the first thing we notice is this curious-looking HTTP request.
The request above can provide us with a lot of clues on how to develop an effective Suricata rule. A viable rule can be found below.
alert http $HOME_NET any -> $EXTERNAL_NET any (msg:"Malicious Document Retrieving Payload"; flow:established,to_server; content:".exe?q="; fast_pattern; content:"Microsoft BITS/"; http_user_agent; depth:15; pcre:"/\.exe\?q=[0-9]{3,5}$/U"; http_header_names; content:!"Referer"; sid:7; rev:1;)
flow:established,to_server; is used since we are dealing with TCP and the directionality is towards the malicious server. We won't focus on the curious-looking HEAD HTTP method so that we can catch variations. fast_pattern; is used so that the Suricata engine "focuses more" on the .exe?q= content ('User-Agent:' will be a match very often, but .exe?q= appears less often in network traffic). pcre:"/\.exe\?q=[0-9]{3,5}$/U"; means the sensor should match every time it observes three to five numbers after q=. Finally, we are once again leveraging the lack of usual headers for detection purposes.
It's time to test the rule above. You can do so, as follows.
cd Desktop
./automate_suricata.sh ./PCAPs/Malicious_document.pcap
You should see the following.
Let's start with DDoSClient.pcap
We'll develop a Suricata rule based on our analysis of the DDoSClient.pcap file, considering that what we see inside the PCAP is malicious.
By opening DDoSClient.pcap in Wireshark, the first thing we notice is some curious-looking TCP traffic, containing information such as OS, CPU MHZ, and CPU Architecture.
The traffic above can provide us with enough clues on how to develop an effective Suricata rule. A viable rule can be found below.
alert tcp $HOME_NET any -> $EXTERNAL_NET any (msg:"ET TROJAN DDoS Client Information Checkin"; flow:established,to_server; content:"Windows"; nocase; depth:7; content:"MHZ|00 00 00 00 00 00|"; distance:0; nocase; content:"|00 00 00 00 00 00|Win"; distance:0; nocase; classtype:trojan-activity; sid:8; rev:1;)
Notice that we are using "Windows" and "MHZ" instead of "Windows 7" and "1795 MHZ", to detect variations. depth:7; is used because "Windows" is seven character's long. The first distance:0; is used because the nulls appear right after "Windows" (the number of nulls in the rule was chosen randomly).
It's time to test the rule above. You can do so, as follows.
cd Desktop
./automate_suricata.sh ./PCAPs/DDoSClient.pcap
You should see the following.
Now, it's time to analyze the Adobe.pcap
We'll develop a Suricata rule based on our analysis of the Adobe.pcap, considering that what we see inside the PCAP is malicious.
By opening Adobe.pcap in Wireshark and filtering so that we can see HTTP traffic only, the first thing we notice is this curious-looking POST request. Let's follow the whole stream.
The request above can provide us with a lot of clues on how to develop an effective Suricata rule. Try creating one on your own...
There was another curious-looking HTTP, the following one.
What is suspicious about the portion inside the red rectangle above, is the way in which it redirects the client elsewhere. Typically, you would see either <META HTTP-EQUIV="refresh" or redirection through JavaScript, not both at the same time.
This is quite uncommon, so let's make a rule out of it. A viable rule can be found below.
alert http $EXTERNAL_NET any -> $HOME_NET any (msg:"Adobe Phising Attempt"; flow:established,to_client; content:"200"; http_stat_code; http_content_type; content:"text/html"; nocase; file_data; content:"<META HTTP-EQUIV="; nocase; within:100; content:"refresh"; distance:1; nocase; within:7; content:"self.location.replace"; nocase; distance:0; content:"window.location"; nocase; distance:0; classtype:bad-unknown; sid:9; rev:1;)
file_data; is a buffer including what will be rendered in the browser. within:100 means we want to see the content within the first 100 bytes. The remaining part is easy to comprehend.
It's time to test the rule above. You can do so, as follows.
cd Desktop
./automate_suricata.sh ./PCAPs/Adobe.pcap
You should see the following.
Suricata Resources: