Blog

  • IDS/IPS Evasion

    Exercise 1

    Description:
    Examine the TCP session between hosts 192.168.1.103 and 192.168.1.104. There is something that is nonstandard about this session. What is it, and why might it cause an IDS evasion?

    1. Does the session get established?

    In Packet 64, the client at 192.168.1.104 tried to establish a connection with the server at 192.168.1.103. Instead of acknowledging the connection, host 192.168.1.103 sent a TCP packet with an SYN flag to the host at 192.168.1.104. In Packet 66, the client responded with a Syn Ack packet. This packet is flagged as a retransmission as there was a time lapse of 30 seconds between pack 65 and 66. What seems to have happened is that since he did not receive a response to his Syn packet, the client retransmitted it while at the same time acknowledging the Syn packet sent by host 192.168.1.103. The server sent an Ack package in packet 67 to complete the handshake and the session was established. This is what is called a “four-way handshake” and it might lead to IDS/IPS evasion as this session will not be tracked since it is not a conventional three-way handshake.

    2. Can Snort find the malicious content?

    One of the connections that is present in the evade.pcap file has content that looks like this:

    21:56:47.400000 IP 184.168.221.65.52342 > 10.1.15.80: Flags [P.], seq 143:463, ack 1, win 8192, length 421
    HTTP: GET /EVILSTUFF HTTP/1.1..Host: example.com..User-Agent: curl/7.35.0..Accept: /….

    We can clearly see GET /EVILSTUFF HTTP/1.1 in the packet. Let’s see if we can alert on that using this alert:

    alert http (msg: “Evil 1 in URI”; content: “EVIL”; sid:10000005; rev:1;)

    Let’s run Snort and see how it does.

    The alert was not triggered.

    3. Can Zeek find it?

    Let’s create a Zeek signature specifically designed to find that EVIL URL request. Please create a file named evil.sig that contains:

    signature Evil {
    ip-proto == tcp
    dst-port == 80
    payload /EVIL/
    event “EVIL URL!”
    }

    Let’s run Zeek against the evade.pcap file and see if Zeek finds the known signature:

    Zeek also fails to find the malicious content.

    Exercise 2

    Description: Look at the HTTP traffic between hosts 10.246.50.2 and 10.246.50.6.

    Examine the HTTP headers on the GET request. What type of attack is this, and what does the code instruct the HTTP server to do? Was the attack successful? How do you know?

    The User Agent looks abnormal. Normally it indicates the browser version used by the client, but in this case, it looks like an empty function followed by a ping command:

    User-Agent: () { :;}; /bin/ping -c1 10.246.50.2

    Searching for this kind of exploit online, I found out that this is a Shellshock vulnerability that is delivered via the User-Agent HTTP header value because the User-Agent is an environment variable.

    If the attack was successful, I should be able to find a ping request sent from the server (10.246.50.6) to the client (10.246.50.2):

    The attack was successful.

    Exercise 3:

    Description:
    Look at the traffic between hosts 192.168.1.105 and 192.168.1.103. The fourth record in the exchange between the hosts is a RST from the client 192.168.1.105 to the server 192.168.1.103. However, as you can observe, 192.168.1.105 continues to send traffic and 192.168.1.103 acknowledges it. Explain the reason why traffic is sent and acknowledged after the RST and why it might cause an IDS evasion.

    The fourth packet has a bad TCP checksum, meaning that receiving host 192.168.1.103 will drop it, permitting the subsequent sent and acknowledged packets. Some IDS/IPS systems do not validate the TCP checksum and therefore may stop tracking the session because it sees the RST. This would cause an evasion because the session continues and the destination host receives the malicious traffic without the IDS/IPS being aware of it.

  • Zeek Script Part 3

    In this lab, I will be developing another useful script that is a little bit more advanced than the ones created in part 1 and 2.

    Exercise – HTTP Exfiltration?

    Description:
    In this exercise, we will create a script that locates anomalous outbound data transfers based on the idea that, generally, we would expect to find that web connections have more data coming from the server to the client. This will potentially allow us to identify data exfiltrations.

    Using the Zeek documentation, write a Zeek script that prints a message any time a connection involving TCP port 80 ends and the amount of data sent by the client was greater than that sent by the server.

    The first problem is determining which event to subscribe to. Again, I need to review the Zeek documentation, to be able to find corresponding events of interest for the problem I am trying to solve. I am looking for an event that would correspond to a connection ending. There is an event that corresponds to event new_connection(). This is the event connection_finished(c:connection) event.

    I am interested in HTTP connections, and I want to limit my view to only connections involving TCP port 80. I first tried implementing the following condition in my script :

    if(c$id$resp_p != 80) { return; }

    but I received a ‘type clash‘ error when I tried running a script using this condition. Zeek exposes the idea of a port as its own data type. This data type requires a number and a protocol name, separated by a slash. Typical HTTP would be 80/tcp. My condition should then be modified to if(c$id$resp_p != 80/tcp) { return; }

    I also need to look at the number of byts sent by the server and the client. The c$orig and c$resp sections of the connection_finished event handler have useful data in them. There are fields that will give me the total number of IP bytes, but there are also size attributes. This field informs you of the total number of bytes of payload sent by either side of the connection.

    Using these fields, I would then need to add some logic that will compare the number of bytes sent by the originator to the number of bytes sent by the respondent. If the originator (client) sent more than the respondent (server), I want to print a message.

    Let’s test this script out:

  • Zeek Script Part 2

    Objectives

    Our main objectives in this section are to both learn more about how to write Zeek scripts by experimenting with the language and to create a script that will be useful in the real world.

    Exercise

    Description: In this exercise, we will create a script that reports outbound connections for which no previous DNS resolutions were observed.

    First, for what reasons would a host ever attempt an outbound connection without first performing a DNS resolution? Here are some possibilities:

    • Hard-coded DNS server addresses
    • Other hard-coded configuration of addresses
    • Malware phoning home to a set of known addresses
    • Resolution occurred over another path
    • Outbound network scanners
    • Malware attempting to spread out of our border
    • Use of DNS over HTTPS or DNS over TLS

    There are surely other possibilities. However, the list above includes most of the more common explanations. Let’s discuss and explain these.

    We do expect outbound connections to the upstream DNS servers with no corresponding name queries. Why? That’s just how it works! We statically configure our DNS server with a set of “root hints” or configure it to relay all requests upstream to a specific name server. We will never see queries related to finding these addresses.

    We may have systems or services that have hard-coded IP addresses in them. These are not necessarily nefarious, but such configurations are very fragile! It is much better to configure systems to connect to names that can be resolved via DNS. This allows us to relocate systems or services without breaking anything.

    The other reasons are not so good. This behavior could be an indication of a malware infection. Perhaps it is attempting to establish a command-and-control link to a static address or addresses. This is especially common when we are early on in a targeted compromise. Perhaps it’s generic malware that’s attempting to spread beyond the borders of our network. Since it doesn’t know exactly who it wants to infect, malware will frequently scan random addresses (without DNS lookups, of course!).

    As far as DNS resolutions occurring over some other path, consider both why and how this might be done. Certainly, someone can argue that it would be done for privacy, but within an enterprise network, how much “private” activity should there be? It is more likely that, in this case, it is being done to bypass content filtering controls, though it could also be inadvertent.

    A prime way that this could happen is through the use of DNS over TLS (port 853) or DNS over HTTPS (port 443). As you are likely aware, a number of browsers optionally support DNS over HTTPS today. While the script that we are creating will not allow us to see what the DNS resolutions were, we can certainly detect that a system seems to be circumventing our normal resolution infrastructure and take appropriate action to remediate the system or activity.

    We can see that this is all interesting behavior. How can we find it?

    1. Using any editor of your choice that is installed on the VM, please create a script named anomalousOutbound.zeek that will print the address resolved every time a DNS A or DNS AAAA record is seen.

    To begin, I should define a module name for my script. I am going to call my module a module NoDNS. This defines a namespace and moves everything contained in my script into that namespace.

    Next, I need to determine which events I need to subscribe to using the Zeek Scripting protocol analyzers documentation for DNS. Looking at this documentation, there are two events that I am interested in:

    event dns_A_reply(c: connection, msg: dns_msg, ans: dns_answer, a: addr)

    and the IPv6 version:

    event dns_AAAA_reply(c: connection, msg: dns_msg, ans: dns_answer, a: addr)

    I need to create event handlers for these two events in my script and add a code block to each that will print the resolved address.

    I am interested in printing the address resolved every time an A record or AAAA record is seen. The parameter ‘a: addr’ holds the resolved IP address from the DNS response and is passed to the event handler when the event is triggered during Zeek’s analysis of network traffic. This is exactly what I need to print. Let’s modify my script to account for this.

    Let’s make sure that the output of this script is correct:

    The next step is to add some sort of global variable that will keep track of the DNS addresses that have been resolved. I can add a global variable for this purpose and add all of the addresses to it. I am going to use the zeek_done() event to print the content of this variable when the script completes to verify that it is working.

    The output from this script doesn’t look much different, but it runs much differently. Rather than printing addresses as they are seen, the full list is printed at the very end. The entire list is contained within curly braces ({}), indicating that it is a set:

    The final step is to remove the zeek_done() call and to cause our script to trigger every time a connection is seen to a responding host that was not seen in our set. I need to identify an event that will be generated every time a new connection or session is seen. Looking at the zeek documentation, there is the event new_connection(c: connection) that is perfect for my purposes. It is generated whenever a new stream is identified involving TCP, UDP, or ICMP.

    Now I need to add some output to the event, but only if the responding host isn’t found in the set of resolved addresses. Both vectors and sets allow for the use of the ‘in’ keyword to check for membership.

    Let’s run this new script:

    This script still has a few problems. I am seeing external to internal and internal to internal. I need to add a condition to the new_connection() event handler to ignore external to internal and internal to internal connections.


    The very last thing that I will add to this script is an exception for certain approved external DNS servers (such as 8.8.8.8, which is Google’s DNS server). This is necessary because internal DNS resolvers often connect directly to external DNS servers without first performing a DNS lookup for their addresses. These addresses are typically configured in advance, so no DNS resolution happens beforehand. To avoid false positives, the script should exclude these approved servers from being flagged.


    Let’s do a last test run :








  • Zeek Script Part 1

    Objectives
    This exercise involves creating and running a very basic Zeek script.

    Exercise 1
    Description: In this first exercise, we will create the traditional “Hello, World!” that is typical of a first attempt at programming in a new language.

    Using any editor of your choice that is installed on the VM, please create a script that will generate the string “Hello, World!” at the console when it is executed with Zeek.

    Since Zeek is a C-like language, it shares many very familiar syntactical and grammatical similarities. A list of a few similarities is:

    • Functions and events are defined using a type, a name, and a parameter list.
    • Parameter lists are contained within parentheses.
    • The types of all variables passed as parameters must be defined.
    • The end of a statement is delimited with a semicolon ;.
    • Sections of code are all contained within curly-braces {}.

    The first step is to create a file to put my script into. I am going to name my script first.zeek.

    The next step is to determine which Zeek events will serve my needs. I want an event that will fire every time Zeek starts up.

    After searching through the Zeek documentation, I found an event called “zeek_init()” that seems to be the correct event. It is triggered every time Zeek starts.

    Regarding Module Name, my SEC 503 very strongly recommended that I develop the habit of prefixing all of my scripts with the module keyword and a name (example: module MyFirstScript;)

    This statement defines a namespace within which all of my global variables, functions, and events exist. When I do this, other scripts can still access my global variables, functions, and events by prefixing them with the MyFirstScript:: namespace operator, and it protects me from inadvertently overwriting existing variables, events, and functions in the global namespace.

    With the event information, I simply need to define my event and instruct Zeek as to what it should do when the event fires. In my case I want Zeek to print out “Hello, world!” when Zeek runs.

    Putting it all together:

    Let’s test this script and see if it runs properly:

    It worked!!!

  • Zeek Signature

    Given the time spent mastering signature-based detection, signatures are a very familiar starting point, and signatures can certainly play a role in Zeek scripts and logs. Our objective is to create a simple signature and configure Zeek to use this signature to detect content.

    Exercise 1
    Description: Create a signature to find the dnscat proof-of-concept covert channel. This form of dnscat can be easily identified by creating a signature that looks for the string dnscat in UDP DNS packets.

    Zeek signatures are typically stored in files using the .sig extension. Please create a signature to log every time the word dnscat is seen in UDP DNS packets! Use the filename dnscat.sig for your signature. Run Zeek with this signature file and verify that it successfully logs events. Activity from this covert channel can be found in the signature.pcap file in the /sec503/Exercises/Day4/zeek/zeek-sig directory.

    The general form of a zeek signature will be:

    signature signatureName {
    ip-proto == XXX # Fill in the correct IP protocol
    dst-port == XXX # Fill in the correct DNS server port number
    # Any other required IP, TCP, UDP, or other protocol headers go here.
    payload /RegularExpression/ # Create a regular expression to find the content
    event “Longer message” # An arbitrary message string to add to the log
    }

    To tell Zeek to load a specific signature file, you must use the -s option: zeek -r capturefile.pcap -s signaturefile.sig.

    Putting it all together the signature, the signature could look like this:

    signature dnscat{

    ip-proto==udp

    dst-port==53

    payload/.*dsncat.*/

    event “udp dnscat tunnel”

    }

    Let’s load this signature file

  • Running Zeek and Zeek Output

    Lab Overview

    In this lab, I explored Zeek’s readback mode using the http.pcap file. The goal was to gain familiarity with the various logs that Zeek produces when analyzing captured network traffic. This lab builds on the foundation of working with Zeek logs to understand how different IDs such as conn_uids and fuid are generated and how they relate across files.

    Understanding Zeek IDs

    One of the key takeaways from this lab is the distinction between static content in the logs and dynamically generated IDs:

    • conn_uids: Unique identifiers for a connection.
    • fuid: The file unique identifier that ties files in files.log back to specific connections.

    Exercise 1

    Run Zeek in readback mode using http.pcap to become familiar with the logs it generates. Begin by instructing Zeek to read the http.pcap file and produce logs:

    This generates several log files:

    We can see that Zeek has created five log files as a result of parsing the packet capture file. Let’s examine these.

    Look at the contents of conn.log, which records the different connections observed:

    You see a couple of lines of comments, including the field names followed by the field types. These lines define the structure of Zeek logs. Every log file that Zeek produces will be similarly headed, which defines the field names and types for that file’s contents in this instance.

    It is difficult to tell the wrapped text, so we will use the zeek-cut command to present the output in a more coherent format. This command reads and formats log file output into a tabular format and should output Zeek-cut using a pipeline.

    Let’s first look at the help for zeek-cut:

    It’s time to put this tool to use. When we use it in our first run, we will supply several arguments to zeek-cut to modify the output:

    -u option to print the time as a UTC timestamp. The raw output of time is in seconds and fractions of seconds since the UNIX epoch isn’t very human-friendly.

    The ts argument indicates that we wish to see the ts field, which contains the time value.

    The uid argument indicates that we wish to see the uniquely generated ID for this connection.

    The id.orig_h argument indicates that we wish to see the IP address of the host that originated the connection.

    The id.orig_p argument indicates that we wish to see the port number used by the host that originated the connection.

    The id.resp_h argument indicates that we wish to see the IP address of the responding host in the connection.

    The id.resp_p argument indicates that we wish to see the port number used by the host that responded in the connection.

    The orig_bytes argument indicates that we wish to see the number of bytes sent by the originating host.

    The resp_bytes argument indicates that we wish to see the number of bytes sent by the responding host.

    Let’s put this all together to look at the connection log again:

    This provides a much easier to read output than simply using the cat tool on the log file!

    Next, see if you can figure out how to use the zeek-cut command to display the following fields:

    • Number of packets that were sent by the originator
    • Number of packets that were sent by the respondent
    • The source port used by the originator

    How many packets were sent and received in the connection where the source port is 36499?

    35 packets were sent and 34 packets were received in the connection where the source port is 36499.

    Next, try to use zeek-cut to display the source IP, source port, destination IP, destination port, and the name of the server in the only connection in ssl.log. What is the name of the server used?

    The name of the server was www.google.com

    The files.log contains data about any file Zeek finds in any connection for which it has an appropriate protocol analyzer that exposes files. This means that it doesn’t matter if the file was sent over HTTP, FTP, TFTP, or any other supported protocol that has the notion of a file; a record of that file transfer will appear in the files.log.

    Use zeek-cut to display the file id (fuid), the source host, the destination host, and the connection id. Unlike the uid value, the fuid value remains the same for every run.

    There is a single files.log record. You can discover the connections found in other logs with related protocol information (where that protocol understands files) by issuing a grep command for the unique fuid value

    In what other Zeek log was the same file seen?

    It was seen in the http.log file.

    Now search for the connection id found in files.log to discover all the logs that contain information about this connection.

    In which logs, other than files.log, was the same connection observed?

    The connection ID was found in:

    conn.log and http.log

    This is helpful when you would like to find related activity for a given connection.

    Extracting the file contents of the files referenced in files.log is very easy in Zeek. This can be very useful if you suspect some malware has been downloaded and you want to analyze the suspect file.

    A Zeek script has been provided that will extract all of the files in a packet capture automatically. This file is in /sec503/Exercises/Day4/zeek/zeek-run/file-extract.zeek.

    Use the Zeek script file-extract.zeek to extract the file:

    A directory named extract_files is created in your current working directory. Change into that directory. You will find a single file within. When using this script, each extracted file has a name that begins with ‘extract’, followed by the raw timestamp:

    Examine the contents of the file. What standard web server return code message does the www.google.com server return to the sender?

    The message returned by www.google.com indicates that the requested document has moved. It is an HTTP 302 message.

    Exercise 2

    Description: Run Zeek in sniffing mode to examine traffic. Zeek is configured to sniff from the loopback interface on the VM. You will use the tcpreplay tool that can play back previously captured traffic. You will direct the traffic to the loopback interface where Zeek will be sniffing traffic.

    I must be root to accomplish the various commands in this exercise. I used the sudo su command to become root:

    Now you want to start Zeek Control with the zeekctl command:

    Next, load all Zeek scripts with the install command:

    Finally, tell Zeek to begin monitoring and logging with the start command:

    Check to make sure Zeek is running with the status command:

    we have provided a simple signature found in /sec503/Exercises/Day4/outbound.sig. We have also configured the /usr/local/zeek/share/zeek/site/local.zeek so that it is loaded when we start Zeek in live mode. local.zeek is effectively the master configuration for your Zeek cluster.

    The signature provided looks for HTTP traffic with a source IP in the 192.168.0.0/16 range going to a destination IP not in that range. An HTTP request header that begins with “User-Agent” and is followed by any other content will generate a message of “Outbound HTTP traffic” in signatures.log.

    Open another ssh connection to the VM. This is a second connection to the VM (use whichever ssh tool you’ve been using, just another connection). Elevate your permissions sudo su to root again. You must be root to execute these commands.

    In this new ssh connection, change directories to /sec503/Exercises/Day4/zeek/zeek-run, where the http.pcap is located.

    Run tcpreplay using an interface (−i) value of “lo” (loopback) and specify the http.pcap file as follows. You will see a bunch of messages and warnings; however, it should run successfully:

    With tcpreplay complete, in this same ssh window as root, examine the file signatures.log in /usr/local/zeek/logs/current. This is the directory where the log files are created when running in live mode. Make sure you see the signature message of “Outbound HTTP traffic” in the variable event_msg in signatures.log.

    Congratulations! You have successfully started Zeek in live sniffing mode with a customized signature that has detected some noteworthy traffic simulated by running tcpreplay. Zeek has generated files that allow you to examine the specific connection that triggered the signature.

    Return to the terminal where you started Zeek in live sniffing mode. Enter stop to stop Zeek and then exit to exit zeekctl.

  • Decrypting TLS with Wireshark

    Background

    Some of the hosts on the monitored network are forced to pass through a transparent tls proxy. Our general practice is that we prefer not to decrypt tls packets unless something motivates us to do so, so we generally store the packets as is (encrypted). We also store the accompanying tls premaster secrets (the keys to the sessions) for as long as we retain the packets. This allows us to inspect specific sessions as needed without overly worrying about privacy issues since we maintain the premaster secrets in a separate, secure location. Not all of the sensors in the packet repository are subject to this.

    Exercise 1

    Use the analyze tool to extract the https packets (tcp port 443) from the perimeter repository for the time range 2019/05/03 13:30:00 to 2019/05/03 14:00:00 and store them in a file named tls.pcap.

    Exercise 2

    Download and open the tls.pcap file in Wireshark. Verify that the TLS sessions are encrypted using the Follow TCP Stream and Follow TLS Stream options.

    First, I need to locate the first TLS connection where the handshake is seen. In Wireshark, I can use the tls.handshake filter to find it pretty quickly:

    The first TLS handshake that is visible is located in packet 561. Using the Follow TCP Stream option, I can now follow the stream:

    This data has some readable parts. Is it encrypted? Let’s use the Follow TLS Stream option on this same session.

    I see a blank window which ensures that the data is indeed encrypted and that I do not have the correct key to decrypt it.

    Exercise 3

    Using the tls.pcap file and the sslkeylogfile.txt in the /sec503/data directory, decrypt the first session in the capture file where the tls handshake was captured. What kind of data is found in this session?

    I have to first configure Wireshark protocol preferences for TLS to use this txt file to decrypt the TLS sessions in the tls.pcap capture:

    The Pre-master secret is now configured and I can go back to the first TLS handshake and Follow the TLS Stream:

    The window is not empty anymore and I see that the protocol used inside the TLS session is HTTP 2.0.

    Exercise 4

    Locate the first GET request in this traffic. What is the URL that is requested ?

    Knowing that the protocol used is HTTP 2.0, I can apply the following display filter to the tls stream that I have already Identified:

    Expanding the HTP2 portion, I can find the URL being requested:

  • DNS

    Exercise 1:

    Description: There is a high volume of activity between 192.168.11.162 and 192.168.11.101 using UDP port 53. Can you explain what this is? Is the attack successful?

    The first packet is a DNS query for the A record (or IPv4 address) of the domain www.evilname.com.

    This query is followed by several DNS responses which is very suspicious as normally one query is followed by one dns response packet. The response should match the query ID so that the client can match it to their request.

    We see that the transaction ID for our DNS query is 0x2870

    The DNS response matching this ID is packet 118:

    The question is: what are these other 100 DNS response packets? DNS response packet number 118 was sent from the same MAC address that was listed as the receiver in the original DNS query. In contrast, the other 100 response packets came from a different MAC address. This looks like a potential DNS cache poisoning attack as multiple spoofed DNS responses begin arriving from 192.168.11.101 with a different mac address: 5a:92:eb:81:00:00. These packets also appear to respond to the www.evilname.com query, but their transaction ids do not match the original query. They start at 0x0001 and increase incrementally.

    This behavior indicates that the attacker was attempting to guess the correct transaction id before the legitimate dns response arrived. This type of brute-force guessing is typical in dns cache poisoning attacks. In this case, the attack failed because the real server responded with the correct transaction id before the spoofed responses could match it.

    Exercise 2:

    Investigate dns packet behavior by identifying anomalies or unexpected response codes in the packet capture file dns.pcap.

    Filter 1: identify dns queries that contain more than one question, which is considered abnormal. Craft a filter to detect packets with multiple dns queries in a single request.

    I used tcpdump with berkeley packet filters to inspect specific fields in the dns header, particularly those found at udp offsets 10 and 11, where dns flags and return codes are located. I created a filter that looks for udp traffic on port 53 (dns), verifies the packet is a query (not a response), and checks that the number of questions is greater than one:

    We find one packet with two questions contained in a DNS query.

    Filter 2: detect dns responses with non-zero return codes by using both tcpdump and tshark. a non-zero return code in a dns response can indicate issues like a name resolution failure (e.g., nxdomain) or other errors, and is often worth investigating.

    Exercise 3

    Write a custom snort rule to detect dns queries targeting the domain “amazon.com”. The goal is to identify these queries by using both the content keyword to match the domain name and the byte_test operator to check for a valid query structure.

    To get started with this custom rule, we know that Snort 3 has a dns service header already defined. Therefore, we can start with something like “alert dns ()”

    Now, we can think on how to properly add a content option to detect dns queries for the domain “amazon.com”. Instead of searching for the plain-text domain name, i had to account for how dns encodes names—using length markers and sometimes compression techniques to make the query as small as possible. I began with a simple content match and gradually refined it to target the specific portion of the dns payload. I also used the pipe symbols to include raw byte values, ensuring my content match aligned with the encoded format of the dns query.

    Let’s test this rule:

    The rules triggers but we see that some of these packets are dns responses that need to be filtered out. In order to do this, I used the byte_test keyword in snort to determine whether a dns packet is a query or a response. The query/response (qr) flag is found in byte offset 2 of the dns header. Specifically, the qr bit is the highest bit in that byte. To detect this bit, i needed to use byte_test in combination with a bitmask (0x80) to evaluate whether the qr bit was set.

    Let’s test it to make sure that this updated alert filters out the DNS responses when triggered:

    It did. The custom rule is now complete.









  • HTTP

    Overview

    In this lab, I explored the intricacies of the HTTP protocol while gaining practical experience with network traffic analysis and intrusion detection systems (IDS), specifically Snort. The goal was to extract and analyze HTTP traffic captured from a live network segment, then prepare the data for further inspection using IDS tools.

    Objectives

    The lab focused on:

    • Deepening understanding of the HTTP protocol and how it manifests in raw packet data.
    • Practicing the use of IDS rule sets related to HTTP activity.
    • Preparing packet captures for detection and analysis using Snort.

    1) Using the analyze tool on the course VM, extract all HTTP traffic observed by the dmz sensor on TCP port 80 during the time range from 05/02/19 09:00:00 to 05/02/19 11:00:00. Save the extracted data to a file named http_extract.pcap.

    Place this file in the directory /sec503/Exercises/Day3
    This will ensure it is accessible for use with Snort or Suricata in the upcoming exercises.

    2) Review the extracted packets using Wireshark and answer the following questions about the capture:

    – Reassemble the session between host 192.168.61.20 and host 134.170.104.154. What version of HTTP is in use? What kind of request is sent? How does the server respond? What kind of server does it appear to be?

    We see that HTTP version 1.1 is in use. The client is sending an HTTP POST request to the server meaning it is sending data to the server, likely for uploading. The body of the request contains 1324 bytes of data. The target host for this request is ssw.live.com.

    The server HTTP response is a successful 200 ok response. The server acknowledged and successfully processed the request but did not return any content. This is typical for many upload or form submission responses that only need to confirm success without sending data back. The webserver is a Microsoft-IIS/7.5.

    3) Create a Snort Rule that can detect a possible indicator of data exfiltration using one or more Snort Http rule options.

    – What in the URL might indicate a potential exfiltration attempt ?

    The URL in question is /UploadData.aspx. Even though it’s not definitive proof, seeing the word “Upload” in the URL is a strong clue that the client may be sending data to the server—something often associated with exfiltration.

    -why is it better to scan for this term in the url instead of the full request or response body?

    Urls are generally short and follow predictable patterns, making them much easier and faster to scan for keywords like “upload”. searching the full session payload for this term could be very inefficient, especially since we wouldn’t know where to start looking.

    – create a snort or rule in your local.rules file under /sec503/exercises/day3 that looks for the word “upload” in a url. confirm that your rule successfully detects the activity.

    Let’s verify if this alert works properly:

    Bingo

  • Snort Writing Rules 2

    Exercise 1
    Task: Develop a new version of the detection rule created in the first lab that identifies the exploit within the payload by applying keyword options such as offset, within, depth, and distance.

    Instructions:
    Begin by duplicating the rule you previously created in the last exercise. Paste it into a new file to work with a clean version. Make sure to update the sid to a new, unique value and reset the rev (revision number) to 1. Although it’s not mandatory, updating the msg field is encouraged to help distinguish this alert from the earlier version visually. This isn’t strictly required because the sid is already unique and is displayed in any alert output.

    1) I analyzed raw packet data to determine the precise starting point of a specific payload pattern: the serie of Xs located in the payload . This task focused on calculating the payload offset using hexadecimal interpretation of both IP and TCP headers.

    The packet began with the hex value 4500, indicating a 20-byte IP header. Further inspection of the header—specifically the value 4500 00d8 bcb2 4000 4006—confirmed the use of TCP, as represented by the protocol number 6. Examining the TCP header at offset 0x0020, I found the value 8018, where 0x80 corresponds to a TCP header length of 8 (multiplied by 4), resulting in 32 bytes.

    By adding the 20-byte IP header to the 32-byte TCP header, I determined the payload begins at byte offset 52, or 0x34 in hexadecimal. Using the tcpdump output, I identified the line starting at offset 0x0030 and noted that the first byte at 0x00340x58—was the beginning of the payload.

    We can use this information to write a more accurate and efficient alert now that we know where our Serie of Xs begins. We can add the option offset 0 right after our content:

    Let’s try and run this alert to make sure it functions as intended.

    Both Alerts successfully Triggered.

    2) I can continue improving my detection rule by optimizing not just where the signature search begins in the payload, but also how far the inspection should go. This optimization involves defining the maximum distance into the payload that the string matching engine will search for the targeted content.

    To implement this, I added one or more rule options to constrain the search window. This ensures the matching engine stops looking after a defined number of bytes, which helps minimize unnecessary processing and improves rule efficiency.

    To do this, I am going to use the Depth option which refers to the size or length of a specified content.

    We added a depth of 13 to our Xs content because we used a string of 13 Xs in our alert content and therefore the minimum amount we can use for our depth option is also 13.

    3) The next step in improving the accuracy of my detection rule is taken by targeting specific binary content found within the payload of the exploit. My goal was to define a content match that would identify a sequence of binary data located between two known strings—XXXXXX and /sh.

    To begin, I used Snort’s support for binary content matching by enclosing hexadecimal byte sequences within pipe symbols (|). For example, a match string like |89 90 1a 2b| would correspond to a precise set of bytes in the payload. This level of specificity helps reduce false positives and ensures that my rule only triggers on highly characteristic patterns of the exploit.

    The bytes of interest—e8 ed ff bf b8—were identified within the packet at a specific payload offset. This additional match was added immediately after the initial XXXXXX... content match:

    Let’s now use the distance and within modifiers to control how closely Snort looks for a second match relative to the first one. This approach is especially useful when trying to match content that consistently appears a specific number of bytes after another known pattern.

    By analysing the hex output of the packet, I identified the relative location of these two key elements in the payload: the string represented by XXXXXX... and the binary sequence (e8 ed ff bf b8). Although these bytes appeared to be directly after the X’s in the data, I had to account for the fact that the initial content match was set with an offset of 0 and a length of 13 characters. This required adjusting the distance value accordingly to avoid skipping the binary data or misaligning the match.

    Additionally, I calculated the exact length of the binary pattern to determine an appropriate within value, ensuring that Snort stops scanning shortly after the desired match.

    4) Simplify the logic by combining both content elements into a single content match. This approach reduces the number of operations Snort needs to perform, improving performance and clarity.

    Initially, my instinct was to match strings at their starting point. However, I discovered that it’s equally valid—and often more effective—to match the trailing portion of a string instead.

    This realization helped me streamline my rule logic. Since the payload in this case happened to start at offset zero, it was tempting to match from the beginning. But had the payload been preceded by other data, that approach would have failed. By refactoring the rule to focus on the ending portion of the string, I improved its reliability and flexibility in different network conditions.

    5) Add offset, within, distance and depth options to try and make this revised rule as efficient as possible

    It was about figuring out how to place each piece of the rule so Snort could accurately spot our target pattern in the payload.

    Here’s what I figured out:

    • We already knew there were 132 X characters in the string, and we’d used 13 of them in a previous rule. That gave us a starting offset of 119 for our match.
    • Those 13 characters were followed by 5 bytes of binary data. So, I set the depth to 18 to capture all of that (13 + 5).
    • Since the /2/sh string comes right after the Xs and binary bytes, it needed to be searched immediately after the first content match, which justified using offset 119.
    • Instead of matching /2/sh right away as the next content, I added it with a distance: 0 from the previous match—basically telling Snort to look for it right after.
    • I also had to make sure Pb/bin, the final part of the payload, was found within 4 bytes after /2/sh. It turns out it starts 3 bytes later and is 6 bytes long, so I used distance: 3 and within: 6.

    Once I put all these details together, I ended up with a rule that’s a lot more efficient and accurate than the one I started with. Here’s what the refined rule looks like now:

    6) In this exercise, I simulated a real-world scenario where my Snort rule—developed through the previous lab steps—had been deployed in a production environment for a while. Unfortunately, despite that setup, someone still managed to exploit the LamanServer, and no alerts were generated. That was a red flag.

    To figure out what went wrong, I pulled packet captures of the suspected exploit and used them to test my detection rule inside my Snort lab setup. Specifically, I ran the rule against the 02_exploit.pcap file.

    The goal here was to determine why the IDS didn’t pick up the attack. I used all the rules I had built in the previous exercises and evaluated them against the new pcap file to see if they triggered as expected—or if the attacker had changed something just enough to evade detection.