Malicious Traffic Detection on DNS
Malicious Traffic Detection on DNS (Linux Edition)
A direct approach guide to monitor and secure DNS servers against the most common attacks.
DNS Server first line of defense and attack, the time that it takes to evaluate the correct action according to the fragmentation of the packet, can give you enough information about the responses time and how to bypass it, in the majority of the cases, default behavior of a particular system can be enough to understand points of failure, that’s why it’s required to implement a zero-trust set of practices that can be used as a protocol for the organization, allowing you to monitor, identify, analyze and handle an incident.
Introduction
As a fundamental part of the network, DNS servers are part of the resources that needs to be monitored constantly, specially to have a detailed filter related to the kind of requests that your services are consuming, before going into any internal system, DNS servers are redirecting the packets according to the type and characteristics of the header and body, this means that needs an extra-attention in particular the registry and access to these resolvers, in this article we will cover how to apply a general protocol for securing, monitoring, analyze and mitigate the most common types of attacks for DNS, includes and additional section to implement IoC detection rules.
Again, why DNSSEC?
Just to simplify, this is an extended set of dependencies and tools to allow the use of cryptography on DNS.
Providing you the possibility to create an authentication layer, that can help to preserve the current data delivered by the resolvers in the zone, still requires some other components to be effective, but keep in mind that it’s not a silver bullet, you still can apply more complex scenarios based on your requirements.
Specially if you have an IR team ready to take action, in this particular case the monitoring it’s fundamental, the logs and the prune of the sensible information on the cache in a daily basis, with a detailed protocol applied to it.
You can follow up this article to learn the basis around DNS configuration using BIND and DNSSEC generalities on Linux.
Do we have alternatives?
Yes, eliptic curve cryptography applied to the DNS functionality, same as DNSSEC but better, we have some other options too, but if you want to take a look: DNSCURVE
Vectors
1. DoS and DDoS Attacks: Denial-of-service (DoS) and distributed-denial-of-service (DDoS) attacks flood internet servers with so many requests that they can’t answer them all, causing the system to crash. A simple DoS attack uses one computer and one internet connection to flood a remote server. In a DDoS attack, multiple computers and internet connections target a site. There are also three sub-types of DDoS attacks:
- Protocol attacks: This attack cripples actual server resources or other network equipment like firewalls and load balancers1.
- Application layer attacks: To crash the web server, the attacker sends requests that seem harmless but actually exploit the target’s vulnerabilities1.
- Flood attacks: Floods aim to make a server unavailable to real traffic by ‘flooding’ the targeted server’s resources1. DNS Amplification Attacks: A DNS amplification attack is a type of DDoS attack in which attackers use publicly accessible open DNS servers to flood a target with DNS response traffic1.
2. DNS Amplification Attacks: A DNS amplification attack is a type of DDoS attack in which attackers use publicly accessible open DNS servers to flood a target with DNS response traffic.
3. DNS Tunneling: This attack uses DNS as a covert communication channel to bypass the firewall. Attackers may also tunnel through other protocols like SSH, TCP or Web within DNS.
4. TCP SYN Floods: This type of attack uses the three-way handshake that begins a TCP connection. After this, the attacker sends spoofed SYN packets using the source IP address of bogus destinations.
5. Cache Poisoning: In a typical cache poisoning attack, the attacker queries a recursive name server for the IP address of a malicious site2. The recursive server does not have the IP address and queries a malicious DNS resolver. The malicious resolver then provides the requested rogue IP address and also maps the rogue IP address to additional legitimate sites.
Vulnerability surface
1. DoS and DDoS Attacks:
- The vulnerability surface for these attacks includes network, software, and hardware design.
- Attackers exploit network vulnerabilities when they launch DDoS attacks that cause the target system or service to crash.
- The impact of DDoS attacks may often be negligible—depending on the scale of the attack—it could be severe and include loss or degradation of critical services, loss of productivity, extensive remediation costs, and acute reputational damage.
2. DNS Amplification Attacks:
- The vulnerability surface for DNS Amplification Attacks is primarily DNS servers configured to allow recursion.
- Attackers take advantage of these servers to generate a large response to the target IP address, amplifying the attack traffic.
3. DNS Tunneling:
- The vulnerability surface for DNS Tunneling involves DNS servers that allow external network connectivity.
- Attackers need to control a server and a domain that may function as an authoritative server to carry out data payload executable programs and server-side tunneling.
4. TCP SYN Floods:
- The vulnerability surface for TCP SYN Floods involves the TCP/IP connection establishment mechanism.
- Attackers exploit this mechanism by sending a flood of TCP SYN requests to fill the pending connection queue on the server, preventing other users from establishing network connections.
5. Cache Poisoning:
- The vulnerability surface for Cache Poisoning involves DNS servers that allow unrestricted recursive resolution for any client on the Internet.
- Attackers exploit this by sending a DNS name lookup request to an open DNS server with the source address spoofed to be the target’s address.
Mitigation
1. DoS and DDoS Attacks: Mitigation strategies for these attacks include:
- Network Capacity: Your DDoS mitigation service needs to have enough bandwidth in its network to block the large volume of false traffic generated during an attack.
- Processing Capacity: A DDoS mitigation strategy needs to be able to process a large amount of data quickly.
- Scalability: Your DDoS mitigation service needs to be scalable because attack sizes are likely to grow rather than diminish.
- Flexibility: The system needs to be adaptable to recognize attacks even when there are large fluctuations in legitimate traffic.
- Reliability: Your DDoS service, like a firewall, needs to be reliable.
2. DNS Amplification Attacks: Mitigation strategies for DNS Amplification Attacks include:
- Reducing the number of DNS resolvers: This will help to answer the DNS queries only from within the organization and trusted sources which reduces the risk of any amplification attack.
- Source IP verification: This helps to reject unknown bot traffic that may put in danger the entire DNS server.
- Response Rate Limiting (RRL): This restricts the amount of traffic.
3. DNS Tunneling: Mitigation strategies for DNS Tunneling include:
- Embrace Zero Trust Principles: Zero trust security approaches combat DNS tunneling attacks through a default “deny” posture that can significantly mitigate this risk.
- Establish Granular Access Control: This helps to control who has access to your DNS servers.
- Embrace AI/ML: Artificial Intelligence and Machine Learning can help to identify and block DNS tunneling attacks.
- Evaluate Security Posture Continuously: Regularly check your security measures to ensure they are effective.
4. TCP SYN Floods: Mitigation strategies for TCP SYN Floods include:
- Firewalls and Proxies: These can filter out malicious SYN requests based on specific patterns or known malicious IP addresses.
- Reducing SYN-RECEIVED Timer: By reducing the time the server waits for an ACK response after sending a SYN-ACK, resources allocated to half-open connections are freed up more quickly.
- SYN Cache: Instead of allocating significant resources for each incoming SYN request, the server can use a cache to store a smaller amount of information about each request, conserving resources.
5. Cache Poisoning: Mitigation strategies for Cache Poisoning include:
- Do not trust data in HTTP headers: Never return HTTP headers to users in cached, and if needed, sanitise user-supplied data.
- Cache only static files and static content: This reduces the risk of cache poisoning.
- Regularly monitor web security advisories: Stay updated with the latest security threats and how to mitigate them.
Monitoring
Step 1: Install Snort on Ubuntu
- Update the Ubuntu Server.
$ apt-get update
$ apt-get upgrade
- Install dependencies required.
$ apt-get install libdumbnet-dev libhwloc-dev libluajit*-dev libpcap-dev liblzma-dev flex build-essential
- Install DAQ Library to be used with snort3.
$ git clone https://github.com/snort3/libdaq
$ cd libdaq
$ ./bootstrap
$ ./configure
$ make
$ make install
- Download Snort from the repository.
$ git clone https://github.com/snort3/snort3
$ cd snort3
- Make, build and install locally.
$ ./configure_cmake.sh --with-daq-includes=/usr/local/include/ --with-daq-libraries=/usr/local/lib/
$ cd build
$ make
$ make -j $(nproc) install
- Create folder structure for general configuration
$ mkdir -p /etc/snort
$ mkdir -p /etc/snort/rules
$ mkdir -p /var/log/snort
$ mkdir -p /usr/local/lib/snort_dynamicrules
$ touch /etc/snort/rules/black_list.rules
$ touch /etc/snort/rules/white_list.rules
$ touch /etc/snort/rules/local.rules
$ touch /etc/snort/sid-msg.map
$ mkdir -p /etc/snort/rules/iplists
$ mkdir -p /etc/snort/preproc_rules
$ mkdir -p /etc/snort/so_rules
- Create folder structure for local configuration
$ mkdir /usr/local/etc/rules
$ mkdir /usr/local/etc/so_rules/
$ mkdir /usr/local/etc/lists/
$ touch /usr/local/etc/rules/local.rules
$ touch /usr/local/etc/lists/default.blocklist
$ mkdir /var/log/snort
- Provide permissions
$ chmod -R 5775 /etc/snort
$ chmod -R 5775 /etc/snort/rules
$ chmod -R 5775 /etc/snort/so_rules
$ chmod -R 5775 /usr/local/lib/snort_dynamicrules
$ chmod -R 5775 /var/log/snort
$ chown -R snort:snort /var/log/snort
Step 2: Configure Snort
- Create a symlink to point to your local space
$ ldconfig
$ echo 'export SNORTROOT=/usr/local/snort' >> ~/.bashrc
$ echo 'export PATH=$SNORTROOT/bin:$PATH' >> ~/.bashrc
$ source ~/.bashrc
- Adding community rules
$ wget https://www.snort.org/downloads/community/snort3-community-rules.tar.gz
$ tar -zxvf snort3-community-rules.tar.gz
$ cd snort3-community-rules
$ cp -r . /usr/local/etc/rules
- Installing Snort OpenAppID.
$ wget https://snort.org/downloads/openappid/33380 -O OpenAppID.tgz
$ tar -xzvf OpenAppID.tgz
$ cp -R odp /usr/local/lib/
- Download community rules
$ wget https://www.snort.org/downloads/community/snort3-community-rules.tar.gz
$ tar -zxvf snort3-community-rules.tar.gz
- Edit the lua configuration (usually in usr/local/etc/snort) file with the following values to include the libraries path, rules path and alert logging
...
appid =
{
-- appid requires this to use appids in rules
-- linking library path
app_detector_dir = 'usr/local/lib',
log_stats = true,
}
...
ips =
{
-- use this to enable decoder and inspector alerts
-- define explicit paths for rules
enable_builtin_rules = true,
include = RULE_PATH .. '/local.rules',
include = RULE_PATH .. '/snort3-community.rules',
-- use include for rules files; be sure to set your path
-- note that rules files can include other rules files
-- (see also related path vars at the top of snort_defaults.lua)
variables = default_variables,
}
...
alert_fast = {
-- configuring snort logging
file = true,
packet = false,
limit = 10,
}
...
- Network gateway can be defined in the same script with the following values
...
-- HOME_NET and EXTERNAL_NET must be set now
-- setup the network addresses you are protecting
HOME_NET = 'any' -- YOUR LOCAL NETWORK
-- set up the external network addresses.
-- (leave as "any" in most situations)
EXTERNAL_NET = 'any'
-- default configurations
include 'snort_defaults.lua'
- Now you can add a new user to be dedicated as a part of the service
$ useradd -r -s /usr/sbin/nologin -M -c SNORT_IDS snort
- Add the service file
$ cd /etc/systemd/system
$ nano snort.service
- Once you have the file created, add the following values on the service file
snort.service
[Unit]
Description=Snort3 NIDS Daemon
After=syslog.target network.target
[Service]
Type=simple
ExecStart=/usr/local/bin/snort -c /usr/local/snort/etc/snort/snort.lua -s 65535 -k none -l /var/log/snort -D -u snort -g snort -i <YOUR-NETWORK-INTERFACE> -m 0x1b --create-pidfile
ExecStop=/bin/kill -9 $MAINPID
[Install]
WantedBy=multi-user.target
- Enable the service
$ sudo systemctl enable snort
$ sudo service snort start
- Verify if it’s running the service
$ sudo service snort status
Step 3: Prepare a Custom DNS Server as a Honeypot
Simple Honeypot, download it from gobee.go
package main
import (
"log"
"net"
"strconv"
"github.com/miekg/dns"
)
var domainsToAddresses map[string]string = map[string]string{
"google.com.": "1.2.3.4",
"not-a.xyz.": "1.3.3.7",
/* Add more use cases */
}
var domainsToMX map[string][]*dns.MX = map[string][]*dns.MX{
"google.com.": { {Hdr: dns.RR_Header{Name: "google.com.", Rrtype: dns.TypeMX, Class: dns.ClassINET, Ttl: 60}, Preference: 10, Mx: "mail.google.com."} },
"not-a.xyz.": { {Hdr: dns.RR_Header{Name: "not-a.xyz.", Rrtype: dns.TypeMX, Class: dns.ClassINET, Ttl: 60}, Preference: 10, Mx: "mail.not-a.xyz."} },
/* Add more use cases */
}
var domainsToCNAME map[string][]*dns.CNAME = map[string][]*dns.CNAME{
"google.com": { {Hdr: dns.RR_Header{Name: "google.com", Rrtype: dns.TypeCNAME, Class: dns.ClassINET, Ttl: 60}, Target: "www.not-a.xyz"} },
"not-a.xyz": { {Hdr: dns.RR_Header{Name: "not-a.xyz", Rrtype: dns.TypeCNAME, Class: dns.ClassINET, Ttl: 60}, Target: "www.not-a.xyz"} },
/* Add more use cases */
}
var domaintToCAA map[string][]*dns.CAA = map[string][]*dns.CAA{
"google.com": { {Hdr: dns.RR_Header{Name: "google.com", Rrtype: dns.TypeCAA, Class: dns.ClassINET, Ttl: 60}, Value: "m.google.com"} },
"not-a.xyz": { {Hdr: dns.RR_Header{Name: "not-a.xyz", Rrtype: dns.TypeCAA, Class: dns.ClassINET, Ttl: 60}, Value: "m.not-a.xyz"} },
/* Add more use cases */
}
type handler struct{}
func (h *handler) ServeDNS(w dns.ResponseWriter, r *dns.Msg) {
msg := dns.Msg{}
msg.SetReply(r)
switch r.Question[0].Qtype {
case dns.TypeA:
msg.Authoritative = true
domain := msg.Question[0].Name
address, ok := domainsToAddresses[domain]
if ok {
msg.Answer = append(msg.Answer, &dns.A{
Hdr: dns.RR_Header{Name: domain, Rrtype: dns.TypeA, Class: dns.ClassINET, Ttl: 60},
A: net.ParseIP(address),
})
/* Add rule */
}
/* Add more events */
case dns.TypeAAAA:
msg.Authoritative = true
domain := msg.Question[0].Name
address, ok := domainsToAddresses[domain]
if ok {
msg.Answer = append(msg.Answer, &dns.AAAA{
Hdr: dns.RR_Header{Name: domain, Rrtype: dns.TypeAAAA, Class: dns.ClassINET, Ttl: 60},
AAAA: net.ParseIP(address),
})
/* Add rule */
}
/* Add more events */
case dns.TypeCNAME:
msg.Authoritative = true
domain := msg.Question[0].Name
address, ok := domainsToCNAME[domain]
if ok {
msg.Answer = append(msg.Answer, &dns.CNAME{
Hdr: dns.RR_Header{Name: domain, Rrtype: dns.TypeCNAME, Class: dns.ClassINET, Ttl: 60},
Target: address[0].String(),
})
/* Add rule */
}
/* Add more events */
case dns.TypeCAA:
msg.Authoritative = false
domain := msg.Question[0].Name
address, ok := domaintToCAA[domain]
if ok {
msg.Answer = append(msg.Answer, &dns.CAA{
Hdr: dns.RR_Header{Name: domain, Rrtype: dns.TypeCAA, Class: dns.ClassINET, Ttl: 60},
Value: address[0].Value,
})
/* Add rule */
}
/* Add more events */
case dns.TypeMX:
msg.Authoritative = false
domain := msg.Question[0].Name
address, ok := domainsToMX[domain]
if ok {
msg.Answer = append(msg.Answer, &dns.MX{
Hdr: dns.RR_Header{Name: domain, Rrtype: dns.TypeMX, Class: dns.ClassINET, Ttl: 60},
Mx: address[0].Mx,
})
/* Add rule */
}
/* Add more events */
case dns.TypeTXT:
msg.Authoritative = false
domain := msg.Question[0].Name
address, ok := domainsToAddresses[domain]
if ok {
msg.Answer = append(msg.Answer, &dns.TXT{
Hdr: dns.RR_Header{Name: domain, Rrtype: dns.TypeTXT, Class: dns.ClassINET, Ttl: 60},
Txt: []string{address},
})
/* Add rule */
}
/* Add more events */
}
w.WriteMsg(&msg)
}
func main() {
srv := &dns.Server{Addr: ":" + strconv.Itoa(53), Net: "udp"}
srv.Handler = &handler{}
defer srv.Shutdown()
if err := srv.ListenAndServe(); err != nil {
log.Fatalf("Failed to set udp listener %s\n", err.Error())
}
}
Step 4: Monitor Connections to the DNS Server
- Download the following bind-exporter module for prometheus and start monitorin
$ go get github.com/prometheus-community/bind_exporter
$ cd $GOPATH/src/github.com/prometheus-community/bind_exporter
$ make
$ ./bind_exporter [flags]
Alerting
Alerts, how it works?
Snort is a powerful open-source network intrusion detection system (NIDS) that can be used to detect and prevent a wide range of network attacks.
Identify the Rule Structure
Snort rules are divided into two logical sections: the rule header and the rule options. The rule header contains the rule’s action, protocol, source and destination IP addresses and netmasks, and the source and destination ports. The rule options section contains alert messages and information about which parts of the packet should be inspected to match the rule.
Write the Rule
A basic Snort rule begins with an action (like alert, log, or pass), followed by the protocol (like tcp, udp, icmp, or ip), the source and destination IP addresses and ports, and then the rule options enclosed in parentheses. For example, a rule to detect a SYN flood attack might look like this:
alert tcp any any -> $HOME_NET 22 (flags: S; msg:"Possible SYN Flood"; threshold: type both, track by_src, count 100, seconds 10; sid:10001;)
Test the Rule
After writing your rule, it’s important to test it to make sure it works as expected. You can do this by running Snort in IDS mode with your rules file. Tune and Optimize Your Rules: Depending on the complexity of your network and the specificity of your rules, you may need to tune your Snort rules to reduce false positives and improve performance.
Basic Examples and Use Cases
- Detecting a Specific User-Agent: This rule will alert if it detects HTTP traffic with a specific User-Agent string, which could indicate a specific type of malware or tool.
alert tcp any any -> any 80 (msg:"Specific User-Agent Detected"; content:"User-Agent\: BadBot"; http_header; sid:10003;)
- Detecting SSH Brute Force Attacks: This rule will alert if it detects more than 5 SSH (port 22) connection attempts from the same source IP within 60 seconds, which could indicate a brute force attack.
alert tcp any any -> any 22 (msg:"Possible SSH Brute Force Attack"; flags: S; threshold: type both, track by_src, count 5, seconds 60; sid:10004;)
- Detecting SQL Injection Attacks: This rule will alert if it detects a URL with a common SQL injection string, which could indicate an SQL injection attack.
alert tcp any any -> any 80 (msg:"Possible SQL Injection Attack"; uricontent:"' OR '1'='1"; sid:10005;)
- Detecting Credit Card Leakages: This rule will alert if it detects unencrypted credit card numbers being sent over HTTP, which could indicate a data leakage.
alert tcp any any -> any 80 (msg:"Possible Credit Card Leakage"; pcre:"/\b\d{13,16}\b/"; sid:10006;)
- Detecting DNS Tunneling: This rule will alert if it detects unusually large DNS queries, which could indicate DNS tunneling.
alert udp any any -> any 53 (msg:"Possible DNS Tunneling"; dsize:>500; sid:10007;)
- Detecting a Specific User-Agent: This rule will alert if it detects HTTP traffic with a specific User-Agent string, which could indicate a specific type of malware or tool.
alert tcp any any -> any 80 (msg:"Specific User-Agent Detected"; content:"User-Agent\: BadBot"; http_header; sid:10003;)
- Detecting SSH Brute Force Attacks: This rule will alert if it detects more than 5 SSH (port 22) connection attempts from the same source IP within 60 seconds, which could indicate a brute force attack.
alert tcp any any -> any 22 (msg:"Possible SSH Brute Force Attack"; flags: S; threshold: type both, track by_src, count 5, seconds 60; sid:10004;)
- Detecting SQL Injection Attacks: This rule will alert if it detects HTTP traffic containing a common SQL injection pattern, which could indicate an SQL injection attack.
alert tcp any any -> any 80 (msg:"Possible SQL Injection Attack"; content:"1' OR '1'='1"; http_client_body; sid:10005;)
- Detecting ICMP Flood Attacks: This rule will alert if it detects more than 1000 ICMP packets from the same source IP within 60 seconds, which could indicate an ICMP flood attack.
alert icmp any any -> any any (msg:"Possible ICMP Flood Attack"; threshold: type both, track by_src, count 1000, seconds 60; sid:10006;)
- Detecting DNS Amplification Attacks: This rule will alert if it detects a DNS response larger than 1500 bytes, which could indicate a DNS amplification attack.
alert udp any 53 -> any any (msg:"Possible DNS Amplification Attack"; dsize:>1500; sid:10007;)
From IoCs to Snort Rules File
- Save the downloaded IoCs into a Snort rule file.
You can download a tool to help you on this, use gosoc.go
package main
import (
"encoding/json"
"fmt"
"io/ioutil"
"log"
"net/http"
"os"
)
type IoCList struct {
Data []data `json:"data"`
}
type data struct {
Artifact string `json:"artifact"`
Type string `json:"artifact_type"`
Date string `json:"created_date"`
RefLink string `json:"reference_link"`
RefTxt string `json:"reference_text"`
}
func main() {
resp, err := http.Get("https://labs.inquest.net/api/iocdb/list")
if err != nil {
fmt.Println(err)
return
}
defer resp.Body.Close()
body, err := ioutil.ReadAll(resp.Body)
if err != nil {
fmt.Println(err)
return
}
iocList := IoCList{}
err = json.Unmarshal(body, &iocList)
if err != nil {
fmt.Println(err)
return
}
rules := []string{}
for _, value := range iocList.Data {
if value.Artifact != "" && value.Type == "domain" || value.Type == "url" {
rule := fmt.Sprintf("alert ip any any -> %s any (msg:\"Potential IOC detected of TYPE %s reference %s\"; sid:1000001; rev:1;)\n", value.Artifact, value.Type, value.RefLink)
rules = append(rules, rule)
}
}
filePath := "inquest.rules"
file, err := os.Create(filePath)
if err != nil {
log.Fatalf("Failed creating file: %s", err)
}
for _, f := range rules {
_, err = file.WriteString(f)
if err != nil {
log.Fatalf("Failed writing the rule content: %s", err)
}
}
defer func(file *os.File) {
err := file.Close()
if err != nil {
log.Fatalf("Failed closing file: %s", err)
}
}(file)
log.Printf("File created: %s", filePath)
}
This command will save the IoCs to a file named inquest.rules in the local directory.
Compile the script locally, then copy the binary on /usr/local/etc/routes rules, to eventually link the script to the rules namespace.
Include Rules File in Snort Configuration
Include the rules file in your Snort configuration. Open the Snort configuration file (usually located at /usr/local/etc/snort/snort.lua) and add the following line:
include RULE_PATH .. 'inquest.rules'
Automate the Update Process
To have the rules automatically and periodically updated, you can create a cron job that runs the wget command at regular intervals. For example, to update the rules every day at midnight, you can add the following line to your crontab:
update.sh
#/bin/bash
# Go to the rules folder
cd /usr/local/etc/rules
# Remove previous existent rule file
rm inquest.rules
# Execute the binary
./gosoc
# Restart the service
service snort restart
Copy the script to the following path usr/local/etc/rules
0 0 * * * ./usr/local/etc/rules/update.sh
This line will execute the bash script to grab the latest IoCs from labs.inquest.net and save them to the inquest.rules file every day at midnight.
Conclusion
Providing a basic coverage to monitor, detect, analyze and mitigate, requires a proper setup to handle the data received in a affordable way, without adding too much complexity to the analysts, time it’s essential to handle an incident, granulated information, with precise details.
Disclaimer
DISCLAIMER: The information and code provided here are intended for educational and informational purposes only. The user assumes full responsibility for the use of this information and code. The provider of this information and code makes no representations or warranties, express or implied, about the completeness, accuracy, reliability, suitability, or availability of the information and code provided. Any reliance you place on such information and any use of this code is therefore strictly at your own risk. In no event will the provider be liable for any loss or damage including without limitation, indirect or consequential loss or damage, arising out of, or in connection with, the use of this information and code.