Skip to content

Commit

Permalink
Add option to prefix enriched fields instead of capitalizing them
Browse files Browse the repository at this point in the history
This seems useful for mis-designed log analysis software that treats
JSNO object keys as case-insensitive.

Close threathunters-io#244
  • Loading branch information
hillu committed Jan 13, 2025
1 parent ff24bd3 commit 045951c
Show file tree
Hide file tree
Showing 4 changed files with 52 additions and 13 deletions.
7 changes: 7 additions & 0 deletions etc/laurel/config.toml
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,13 @@ script = true
# Add groups that the user (uid) is a member of. Default: true
user-groups = true

# Add a prefix to enriched fields; this may be useful if logs are
# consumed by analysis software that doesn't properly understand
# uppercase and lowercase JSON object fields as identical. This
# setting has no affect enriched fields passed in from auditd.
# Default: unset
# prefix = "enriched_"

[label-process]

# Audit records that contain certain keys can be reused as a label
Expand Down
5 changes: 5 additions & 0 deletions man/laurel.8.md
Original file line number Diff line number Diff line change
Expand Up @@ -140,6 +140,11 @@ Options that can be configured here actually add information to events
mentioned in `SYSCALL.exe`. Default: true
- `user-groups`: Add groups that the user ("uid") is a member of.
Default: true
- `prefix`: Add a prefix to enriched fields; this may be useful if
logs are onsumed by analysis software that doesn't properly
understand uppercase and lowercase JSON object fields as identical.
This setting does not affect enriched fields passed in from
`auditd(8)`. Default: unset

## `[label-process]` section

Expand Down
49 changes: 36 additions & 13 deletions src/coalesce.rs
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ pub struct Settings {
pub enrich_pid: bool,
pub enrich_script: bool,
pub enrich_uid_groups: bool,
pub enrich_prefix: Option<String>,

pub proc_label_keys: HashSet<Vec<u8>>,
pub proc_propagate_labels: HashSet<Vec<u8>>,
Expand Down Expand Up @@ -71,6 +72,7 @@ impl Default for Settings {
enrich_pid: true,
enrich_script: true,
enrich_uid_groups: true,
enrich_prefix: None,
proc_label_keys: HashSet::new(),
proc_propagate_labels: HashSet::new(),
translate_universal: false,
Expand Down Expand Up @@ -330,7 +332,7 @@ impl<'a, 'ev> Coalesce<'a, 'ev> {
}

/// Create an enriched pid entry in rv.
fn add_record_procinfo(&self, rec: &mut Body, key: &[u8], proc: &Process) {
fn add_record_procinfo(&self, rec: &mut Body, name: &[u8], proc: &Process) {
let mut m: Vec<(Key, Value)> = Vec::with_capacity(4);
match &proc.key {
ProcessKey::Event(id) => {
Expand All @@ -341,7 +343,7 @@ impl<'a, 'ev> Coalesce<'a, 'ev> {
m.push(("START_TIME".into(), format!("{sec}.{msec:03}").into()));
}
}
if key != b"pid" {
if name != b"pid" {
if let Some(comm) = &proc.comm {
m.push(("comm".into(), Value::from(comm.as_slice())));
}
Expand All @@ -368,7 +370,11 @@ impl<'a, 'ev> Coalesce<'a, 'ev> {
}
}

rec.push((Key::NameTranslated(key.into()), Value::Map(m)));
let key = match &self.settings.enrich_prefix {
Some(s) => Key::Name(NVec::from_iter(s.bytes().chain(name.iter().cloned()))),
None => Key::NameTranslated(name.into()),
};
rec.push((key, Value::Map(m)));
}

/// Translates UID, GID and variants, e.g.:
Expand All @@ -383,26 +389,34 @@ impl<'a, 'ev> Coalesce<'a, 'ev> {
return false;
}
match (key, value) {
(Key::NameUID(r), Value::Number(Number::Dec(d))) => {
(Key::NameUID(name), Value::Number(Number::Dec(d))) => {
let translated = if *d == 0xffffffff {
"unset".to_string()
} else if let Some(user) = self.userdb.get_user(*d as u32) {
user
} else {
format!("unknown({d})")
};
rec.push((Key::NameTranslated(r.clone()), Value::from(translated)));
let key = match &self.settings.enrich_prefix {
Some(s) => Key::Name(NVec::from_iter(s.bytes().chain(name.iter().cloned()))),
None => Key::NameTranslated(name.clone()),
};
rec.push((key, Value::from(translated)));
true
}
(Key::NameGID(r), Value::Number(Number::Dec(d))) => {
(Key::NameGID(name), Value::Number(Number::Dec(d))) => {
let translated = if *d == 0xffffffff {
"unset".to_string()
} else if let Some(group) = self.userdb.get_group(*d as u32) {
group
} else {
format!("unknown({d})")
};
rec.push((Key::NameTranslated(r.clone()), Value::from(translated)));
let key = match &self.settings.enrich_prefix {
Some(s) => Key::Name(NVec::from_iter(s.bytes().chain(name.iter().cloned()))),
None => Key::NameTranslated(name.clone()),
};
rec.push((key, Value::from(translated)));
true
}
_ => false,
Expand Down Expand Up @@ -1019,10 +1033,18 @@ impl<'a, 'ev> Coalesce<'a, 'ev> {
}

if let (Some(arch_name), true) = (arch_name, self.settings.translate_universal) {
body.push((Key::Literal("ARCH"), Value::Literal(arch_name)));
let key = match &self.settings.enrich_prefix {
Some(s) => Key::Name(NVec::from_iter(s.bytes().chain(b"arch".iter().cloned()))),
None => Key::Literal("ARCH"),
};
body.push((key, Value::Literal(arch_name)));
}
if let (Some(syscall_name), true) = (syscall_name, self.settings.translate_universal) {
body.push((Key::Literal("SYSCALL"), Value::Literal(syscall_name)));
let key = match &self.settings.enrich_prefix {
Some(s) => Key::Name(NVec::from_iter(s.bytes().chain(b"syscall".iter().cloned()))),
None => Key::Literal("SYSCALL"),
};
body.push((key, Value::Literal(syscall_name)));
}

self.add_record_procinfo(body, b"pid", proc);
Expand Down Expand Up @@ -1058,10 +1080,11 @@ impl<'a, 'ev> Coalesce<'a, 'ev> {
.unwrap_or(format!("unknown({id})"))
};

body.push((
Key::NameTranslated(NVec::from(*name)),
Value::from(translated),
));
let key = match &self.settings.enrich_prefix {
Some(s) => Key::Name(NVec::from_iter(s.bytes().chain((*name).iter().cloned()))),
None => Key::NameTranslated((*name).into()),
};
body.push((key, Value::from(translated)));
}
}

Expand Down
4 changes: 4 additions & 0 deletions src/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,8 @@ pub struct Enrich {
pub script: bool,
#[serde(default = "true_value", rename = "uid-groups")]
pub uid_groups: bool,
#[serde(default)]
pub prefix: Option<String>,
}

impl Default for Enrich {
Expand All @@ -108,6 +110,7 @@ impl Default for Enrich {
pid: true,
script: true,
uid_groups: true,
prefix: None,
}
}
}
Expand Down Expand Up @@ -335,6 +338,7 @@ impl Config {
enrich_pid: self.enrich.pid,
enrich_script: self.enrich.script,
enrich_uid_groups: self.enrich.uid_groups,
enrich_prefix: self.enrich.prefix.clone(),
proc_label_keys: self
.label_process
.label_keys
Expand Down

0 comments on commit 045951c

Please sign in to comment.