]> git.ipfire.org Git - thirdparty/suricata.git/commitdiff
dns: parse and alert on invalid opcodes
authorJason Ish <jason.ish@oisf.net>
Wed, 21 Dec 2022 01:17:38 +0000 (19:17 -0600)
committerVictor Julien <vjulien@oisf.net>
Thu, 8 Jun 2023 08:07:52 +0000 (10:07 +0200)
Accept DNS messages with an invalid opcode that are otherwise
valid. Such DNS message will create a parser event.

This is a change of behavior, previously an invalid opcode would cause
the DNS message to not be detected or parsed as DNS.

Issue: #5444
(cherry picked from commit c98c49d4bad413dbbe4e21a48ebf37260ee5cc8e)

rules/dns-events.rules
rust/src/dns/dns.rs
rust/src/dns/log.rs

index 0e34dae139d05d689520a349d7475f445f8a0384..d4c02b5c2f784ee1f63d852b28503b8200a74a39 100644 (file)
@@ -7,3 +7,4 @@ alert dns any any -> any any (msg:"SURICATA DNS Not a request"; flow:to_server;
 alert dns any any -> any any (msg:"SURICATA DNS Not a response"; flow:to_client; app-layer-event:dns.not_a_response; classtype:protocol-command-decode; sid:2240005; rev:2;)
 # Z flag (reserved) not 0
 alert dns any any -> any any (msg:"SURICATA DNS Z flag set"; app-layer-event:dns.z_flag_set; classtype:protocol-command-decode; sid:2240006; rev:2;)
+alert dns any any -> any any (msg:"SURICATA DNS Invalid opcode"; app-layer-event:dns.invalid_opcode; classtype:protocol-command-decode; sid:2240007; rev:1;)
index ee00986588614bbb0691a5d3f83af9a5c3900f9f..9403a26620dd019a084bdaa5f3ef15a5eb73ebd9 100644 (file)
@@ -133,10 +133,11 @@ static mut ALPROTO_DNS: AppProto = ALPROTO_UNKNOWN;
 
 #[repr(u32)]
 pub enum DNSEvent {
-    MalformedData = 0,
-    NotRequest = 1,
-    NotResponse = 2,
-    ZFlagSet = 3,
+    MalformedData,
+    NotRequest,
+    NotResponse,
+    ZFlagSet,
+    InvalidOpcode,
 }
 
 impl DNSEvent {
@@ -146,6 +147,7 @@ impl DNSEvent {
             DNSEvent::NotRequest => "NOT_A_REQUEST\0",
             DNSEvent::NotResponse => "NOT_A_RESPONSE\0",
             DNSEvent::ZFlagSet => "Z_FLAG_SET\0",
+            DNSEvent::InvalidOpcode => "INVALID_OPCODE\0",
         }
     }
 
@@ -155,6 +157,7 @@ impl DNSEvent {
             1 => Some(DNSEvent::NotRequest),
             2 => Some(DNSEvent::NotResponse),
             4 => Some(DNSEvent::ZFlagSet),
+            5 => Some(DNSEvent::InvalidOpcode),
             _ => None,
         }
     }
@@ -165,6 +168,7 @@ impl DNSEvent {
             "not_a_request" => Some(DNSEvent::NotRequest),
             "not_a_response" => Some(DNSEvent::NotRequest),
             "z_flag_set" => Some(DNSEvent::ZFlagSet),
+            "invalid_opcode" => Some(DNSEvent::InvalidOpcode),
             _ => None
         }
     }
@@ -506,6 +510,7 @@ impl DNSState {
                 }
 
                 let z_flag = request.header.flags & 0x0040 != 0;
+                let opcode = ((request.header.flags >> 11) & 0xf) as u8;
 
                 let mut tx = self.new_tx();
                 tx.request = Some(request);
@@ -517,6 +522,10 @@ impl DNSState {
                     self.set_event(DNSEvent::ZFlagSet);
                 }
 
+                if opcode >= 7 {
+                    self.set_event(DNSEvent::InvalidOpcode);
+                }
+
                 return true;
             }
             Err(nom::Err::Incomplete(_)) => {
@@ -546,6 +555,7 @@ impl DNSState {
                 }
 
                 let z_flag = response.header.flags & 0x0040 != 0;
+                let opcode = ((response.header.flags >> 11) & 0xf) as u8;
 
                 let mut tx = self.new_tx();
                 if let Some(ref mut config) = &mut self.config {
@@ -562,6 +572,10 @@ impl DNSState {
                     self.set_event(DNSEvent::ZFlagSet);
                 }
 
+                if opcode >= 7 {
+                    self.set_event(DNSEvent::InvalidOpcode);
+                }
+
                 return true;
             }
             Err(nom::Err::Incomplete(_)) => {
@@ -692,11 +706,6 @@ impl DNSState {
 const DNS_HEADER_SIZE: usize = 12;
 
 fn probe_header_validity(header: DNSHeader, rlen: usize) -> (bool, bool, bool) {
-    let opcode = ((header.flags >> 11) & 0xf) as u8;
-    if opcode >= 7 {
-        //unassigned opcode
-        return (false, false, false);
-    }
     if 2 * (header.additional_rr as usize
         + header.answer_rr as usize
         + header.authority_rr as usize
index bba983873ed5c85361847dbb559b74f6fe0768c8..27abab245afc508ab5abb98b713ebb9342492d47 100644 (file)
@@ -488,10 +488,12 @@ fn dns_log_json_answer(js: &mut JsonBuilder, response: &DNSResponse, flags: u64)
         js.set_bool("z", true)?;
     }
 
-    for query in &response.queries {
+    let opcode = ((header.flags >> 11) & 0xf) as u8;
+    js.set_uint("opcode", opcode as u64)?;
+
+    if let Some(query) = response.queries.first() {
         js.set_string_from_bytes("rrname", &query.name)?;
         js.set_string("rrtype", &dns_rrtype_string(query.rrtype))?;
-        break;
     }
     js.set_string("rcode", &dns_rcode_string(header.flags))?;
 
@@ -602,6 +604,8 @@ fn dns_log_query(tx: &mut DNSTransaction,
                 if request.header.flags & 0x0040 != 0 {
                     jb.set_bool("z", true)?;
                 }
+                let opcode = ((request.header.flags >> 11) & 0xf) as u8;
+                jb.set_uint("opcode", opcode as u64)?;
                 return Ok(true);
             }
         }