Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fixed unescaped control character c causing jq parsing errors #1082

Merged
Merged
1 change: 1 addition & 0 deletions CHANGELOG-Japanese.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@

- `json-timeline`コマンドを利用した出力で、`MitreTactics`、`MitreTags`、`OtherTags`フィールドが出力されていない問題を修正した。 (#1062) (@hitenkoku)
- `no-summary`オプションを使用した時にイベント頻度のタイムラインが出力されない問題を修正した。 (#1072) (@hitenkoku)
- `json-timline`コマンドの出力に制御文字が含まれる問題を修正した。 (#1068) (@hitenkoku)
- `metrics`コマンドでは、チャンネル名が小文字の場合、省略されなかった。 (#1066) (@garigariganzy)

## 2.5.1 [2023/05/14] "Mothers Day Release"
Expand Down
3 changes: 2 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,9 @@

**Bug Fixes:**

- `MitreTactics`, `MitreTags`, `OtherTags` fields were not being outputed in the `json-timeline` command. (#1062) (@hitenkoku)
- `MitreTactics`, `MitreTags`, `OtherTags` fields were not being outputted in the `json-timeline` command. (#1062) (@hitenkoku)
- The detection frequency timeline (`-T`) would not output when the `no-summary` option was also enabled. (#1072) (@hitenkoku)
- Control characters would not be escaped in the `json-timeline` command causing a JSON parsing error. (#1068) (@hitenkoku)
- In the `metrics` command, channels would not be abbreviated if they were lowercase. (#1066) (@garigariganzy)

## 2.5.1 [2023/05/14] "Mothers Day Release"
Expand Down
53 changes: 49 additions & 4 deletions src/afterfact.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
use crate::detections::configs::{
Action, OutputOption, StoredStatic, CURRENT_EXE_PATH, GEOIP_DB_PARSER,
Action, OutputOption, StoredStatic, CONTROL_CHAT_REPLACE_MAP, CURRENT_EXE_PATH, GEOIP_DB_PARSER,
};
use crate::detections::message::{self, AlertMessage, LEVEL_FULL, MESSAGEKEYS};
use crate::detections::utils::{
Expand Down Expand Up @@ -1229,19 +1229,64 @@ fn _create_json_output_format(
space_cnt: usize,
) -> String {
let head = if key_quote_exclude_flag {
key.to_string()
key.chars()
.map(|x| {
if let Some(c) = CONTROL_CHAT_REPLACE_MAP.get(&x) {
c.to_string()
} else {
String::from(x)
}
})
.collect::<CompactString>()
} else {
format!("\"{key}\"")
.chars()
.map(|x| {
if let Some(c) = CONTROL_CHAT_REPLACE_MAP.get(&x) {
c.to_string()
} else {
String::from(x)
}
})
.collect::<CompactString>()
};
// 4 space is json indent.
if let Ok(i) = i64::from_str(value) {
format!("{}{}: {}", " ".repeat(space_cnt), head, i)
} else if let Ok(b) = bool::from_str(value) {
format!("{}{}: {}", " ".repeat(space_cnt), head, b)
} else if concat_flag {
format!("{}{}: {}", " ".repeat(space_cnt), head, value)
format!(
"{}{}: {}",
" ".repeat(space_cnt),
head,
value
.chars()
.map(|x| {
if let Some(c) = CONTROL_CHAT_REPLACE_MAP.get(&x) {
c.to_string()
} else {
String::from(x)
}
})
.collect::<CompactString>()
)
} else {
format!("{}{}: \"{}\"", " ".repeat(space_cnt), head, value)
format!(
"{}{}: \"{}\"",
" ".repeat(space_cnt),
head,
value
.chars()
.map(|x| {
if let Some(c) = CONTROL_CHAT_REPLACE_MAP.get(&x) {
c.to_string()
} else {
String::from(x)
}
})
.collect::<CompactString>()
)
}
}

Expand Down
39 changes: 38 additions & 1 deletion src/detections/configs.rs
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,8 @@ lazy_static! {
current_exe().unwrap().parent().unwrap().to_path_buf();
pub static ref IDS_REGEX: Regex =
Regex::new(r"^[0-9a-z]{8}-[0-9a-z]{4}-[0-9a-z]{4}-[0-9a-z]{4}-[0-9a-z]{12}$").unwrap();
pub static ref CONTROL_CHAT_REPLACE_MAP: HashMap<char, CompactString> =
create_control_chat_replace_map();
}

pub struct ConfigReader {
Expand Down Expand Up @@ -1757,11 +1759,29 @@ fn load_eventcode_info(path: &str) -> EventInfoConfig {
config
}

fn create_control_chat_replace_map() -> HashMap<char, CompactString> {
let mut ret = HashMap::new();
let replace_char = '\0'..='\x1F';
for c in replace_char.into_iter().filter(|x| x != &'\x0A') {
ret.insert(
c,
CompactString::from(format!(
"\\u00{}",
format!("{:02x}", c as u8).to_uppercase()
)),
);
}
ret
}

#[cfg(test)]
mod tests {
use crate::detections::configs;
use chrono::{DateTime, Utc};
use hashbrown::HashSet;
use compact_str::CompactString;
use hashbrown::{HashMap, HashSet};

use super::create_control_chat_replace_map;

// #[test]
// #[ignore]
Expand Down Expand Up @@ -1827,4 +1847,21 @@ mod tests {
assert!(ret.contains(&contents.to_string()));
}
}

#[test]
fn test_create_control_char_replace_map() {
let mut expect: HashMap<char, CompactString> =
HashMap::from_iter(('\0'..='\x1F').map(|c| {
(
c as u8 as char,
CompactString::from(format!(
"\\u00{}",
format!("{:02x}", c as u8).to_uppercase()
)),
)
}));
expect.remove(&'\x0A');
let actual = create_control_chat_replace_map();
assert_eq!(expect, actual);
}
}