dns/detect: dns.opcode keyword

Add a rule keyword, dns.opcode to match on the opcode flag
found in the DNS request and response headers.

Only exact matches are allowed with negation.

Examples:
  - dns.opcode:4;
  - dns.opcode:!1;
pull/4206/head
Jason Ish 6 years ago
parent c68fbfcfe6
commit d79c23baa3

@ -61,6 +61,7 @@ type_map = {
"c_void": "void",
"std::os::raw::c_char": "char",
"c_char": "char",
"std::os::raw::c_int": "int",
"c_int": "int",
"std::os::raw::int8_t": "int8_t",
@ -104,6 +105,7 @@ type_map = {
"CLuaState": "lua_State",
"Store": "Store",
"AppProto": "AppProto",
"DetectDnsOpcode": "void *",
}
def convert_type(rs_type):

@ -0,0 +1,209 @@
/* Copyright (C) 2019 Open Information Security Foundation
*
* You can copy, redistribute or modify this Program under the terms of
* the GNU General Public License version 2 as published by the Free
* Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* version 2 along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
* 02110-1301, USA.
*/
use super::dns::DNSTransaction;
use crate::core;
use std::ffi::CStr;
use std::os::raw::{c_char, c_void};
#[derive(Debug, PartialEq)]
pub struct DetectDnsOpcode {
negate: bool,
opcode: u8,
}
/// Parse a DNS opcode argument returning the code and if it is to be
/// negated or not.
///
/// For now only an indication that an error occurred is returned, not
/// the details of the error.
fn parse_opcode(opcode: &str) -> Result<DetectDnsOpcode, ()> {
let mut negated = false;
for (i, c) in opcode.chars().enumerate() {
match c {
' ' | '\t' => {
continue;
}
'!' => {
negated = true;
}
_ => {
let code: u8 = (&opcode[i..]).parse().or_else(|_| Err(()))?;
return Ok(DetectDnsOpcode {
negate: negated,
opcode: code,
});
}
}
}
Err(())
}
/// Perform the DNS opcode match.
///
/// 1 will be returned on match, otherwise 0 will be returned.
#[no_mangle]
pub extern "C" fn rs_dns_opcode_match(
tx: &mut DNSTransaction,
detect: &mut DetectDnsOpcode,
flags: u8,
) -> u8 {
let header_flags = if flags & core::STREAM_TOSERVER != 0 {
if let Some(request) = &tx.request {
request.header.flags
} else {
return 0;
}
} else if flags & core::STREAM_TOCLIENT != 0 {
if let Some(response) = &tx.response {
response.header.flags
} else {
return 0;
}
} else {
// Not to server or to client??
return 0;
};
if match_opcode(detect, header_flags) {
1
} else {
0
}
}
fn match_opcode(detect: &DetectDnsOpcode, flags: u16) -> bool {
let opcode = ((flags >> 11) & 0xf) as u8;
if detect.negate {
detect.opcode != opcode
} else {
detect.opcode == opcode
}
}
#[no_mangle]
pub unsafe extern "C" fn rs_detect_dns_opcode_parse(carg: *const c_char) -> *mut c_void {
if carg.is_null() {
return std::ptr::null_mut();
}
let arg = match CStr::from_ptr(carg).to_str() {
Ok(arg) => arg,
_ => {
return std::ptr::null_mut();
}
};
match parse_opcode(&arg) {
Ok(detect) => std::mem::transmute(Box::new(detect)),
Err(_) => std::ptr::null_mut(),
}
}
#[no_mangle]
pub unsafe extern "C" fn rs_dns_detect_opcode_free(ptr: *mut c_void) {
if ptr != std::ptr::null_mut() {
let _: Box<DetectDnsOpcode> = std::mem::transmute(ptr);
}
}
#[cfg(test)]
mod test {
use super::*;
#[test]
fn parse_opcode_good() {
assert_eq!(
parse_opcode("1"),
Ok(DetectDnsOpcode {
negate: false,
opcode: 1
})
);
assert_eq!(
parse_opcode("123"),
Ok(DetectDnsOpcode {
negate: false,
opcode: 123
})
);
assert_eq!(
parse_opcode("!123"),
Ok(DetectDnsOpcode {
negate: true,
opcode: 123
})
);
assert_eq!(
parse_opcode("!123"),
Ok(DetectDnsOpcode {
negate: true,
opcode: 123
})
);
assert_eq!(parse_opcode(""), Err(()));
assert_eq!(parse_opcode("!"), Err(()));
assert_eq!(parse_opcode("! "), Err(()));
assert_eq!(parse_opcode("!asdf"), Err(()));
}
#[test]
fn test_match_opcode() {
assert_eq!(
match_opcode(
&DetectDnsOpcode {
negate: false,
opcode: 0,
},
0b0000_0000_0000_0000,
),
true
);
assert_eq!(
match_opcode(
&DetectDnsOpcode {
negate: true,
opcode: 0,
},
0b0000_0000_0000_0000,
),
false
);
assert_eq!(
match_opcode(
&DetectDnsOpcode {
negate: false,
opcode: 4,
},
0b0010_0000_0000_0000,
),
true
);
assert_eq!(
match_opcode(
&DetectDnsOpcode {
negate: true,
opcode: 4,
},
0b0010_0000_0000_0000,
),
false
);
}
}

@ -18,6 +18,7 @@
pub mod parser;
pub mod dns;
pub mod log;
pub mod detect;
#[cfg(feature = "lua")]
pub mod lua;

@ -112,6 +112,7 @@ detect-depth.c detect-depth.h \
detect-detection-filter.c detect-detection-filter.h \
detect-distance.c detect-distance.h \
detect-dnp3.c detect-dnp3.h \
detect-dns-opcode.c detect-dns-opcode.h \
detect-dns-query.c detect-dns-query.h \
detect-tls-ja3-hash.c detect-tls-ja3-hash.h \
detect-tls-ja3-string.c detect-tls-ja3-string.h \

@ -0,0 +1,106 @@
/* Copyright (C) 2019 Open Information Security Foundation
*
* You can copy, redistribute or modify this Program under the terms of
* the GNU General Public License version 2 as published by the Free
* Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* version 2 along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
* 02110-1301, USA.
*/
#include "suricata-common.h"
#include "detect-parse.h"
#include "detect-engine.h"
#include "detect-dns-opcode.h"
#include "app-layer-dns-common.h"
#include "rust-dns-detect-gen.h"
static int dns_opcode_list_id = 0;
static void DetectDnsOpcodeFree(void *ptr);
static int DetectDnsOpcodeSetup(DetectEngineCtx *de_ctx, Signature *s,
const char *str)
{
SCEnter();
if (DetectSignatureSetAppProto(s, ALPROTO_DNS) != 0) {
return -1;
}
void *detect = rs_detect_dns_opcode_parse(str);
if (detect == NULL) {
SCLogNotice("Failed to parse dns.opcode: %s", str);
return -1;
}
SigMatch *sm = SigMatchAlloc();
if (unlikely(sm == NULL)) {
goto error;
}
sm->type = DETECT_AL_DNS_OPCODE;
sm->ctx = (void *)detect;
SigMatchAppendSMToList(s, sm, dns_opcode_list_id);
SCReturnInt(0);
error:
DetectDnsOpcodeFree(detect);
SCReturnInt(-1);
}
static void DetectDnsOpcodeFree(void *ptr)
{
SCEnter();
if (ptr != NULL) {
rs_dns_detect_opcode_free(ptr);
}
SCReturn;
}
static int DetectDnsOpcodeMatch(DetectEngineThreadCtx *det_ctx,
Flow *f, uint8_t flags, void *state, void *txv, const Signature *s,
const SigMatchCtx *ctx)
{
return rs_dns_opcode_match(txv, (void *)ctx, flags);
}
static int DetectEngineInspectRequestGenericDnsOpcode(ThreadVars *tv,
DetectEngineCtx *de_ctx, DetectEngineThreadCtx *det_ctx,
const Signature *s, const SigMatchData *smd,
Flow *f, uint8_t flags, void *alstate,
void *txv, uint64_t tx_id)
{
return DetectEngineInspectGenericList(tv, de_ctx, det_ctx, s, smd,
f, flags, alstate, txv, tx_id);
}
void DetectDnsOpcodeRegister(void)
{
sigmatch_table[DETECT_AL_DNS_OPCODE].name = "dns.opcode";
sigmatch_table[DETECT_AL_DNS_OPCODE].desc = "Match the DNS header opcode flag.";
sigmatch_table[DETECT_AL_DNS_OPCODE].Setup = DetectDnsOpcodeSetup;
sigmatch_table[DETECT_AL_DNS_OPCODE].Free = DetectDnsOpcodeFree;
sigmatch_table[DETECT_AL_DNS_OPCODE].Match = NULL;
sigmatch_table[DETECT_AL_DNS_OPCODE].AppLayerTxMatch =
DetectDnsOpcodeMatch;
DetectAppLayerInspectEngineRegister("dns.opcode",
ALPROTO_DNS, SIG_FLAG_TOSERVER, 0,
DetectEngineInspectRequestGenericDnsOpcode);
DetectAppLayerInspectEngineRegister("dns.opcode",
ALPROTO_DNS, SIG_FLAG_TOCLIENT, 0,
DetectEngineInspectRequestGenericDnsOpcode);
dns_opcode_list_id = DetectBufferTypeGetByName("dns.opcode");
}

@ -0,0 +1,23 @@
/* Copyright (C) 2019 Open Information Security Foundation
*
* You can copy, redistribute or modify this Program under the terms of
* the GNU General Public License version 2 as published by the Free
* Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* version 2 along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
* 02110-1301, USA.
*/
#ifndef __DETECT_DNS_OPCODE_H__
#define __DETECT_DNS_OPCODE_H__
void DetectDnsOpcodeRegister(void);
#endif /* __DETECT_DNS_OPCODE_H__ */

@ -45,6 +45,7 @@
#include "detect-engine-payload.h"
#include "detect-engine-dcepayload.h"
#include "detect-dns-opcode.h"
#include "detect-dns-query.h"
#include "detect-tls-sni.h"
#include "detect-tls-certs.h"
@ -436,6 +437,7 @@ void SigTableSetup(void)
DetectHttpStatCodeRegister();
DetectDnsQueryRegister();
DetectDnsOpcodeRegister();
DetectModbusRegister();
DetectCipServiceRegister();
DetectEnipCommandRegister();

@ -195,6 +195,7 @@ enum {
DETECT_IPREP,
DETECT_AL_DNS_QUERY,
DETECT_AL_DNS_OPCODE,
DETECT_AL_TLS_SNI,
DETECT_AL_TLS_CERTS,
DETECT_AL_TLS_CERT_ISSUER,

Loading…
Cancel
Save