Merge pull request #593 from Yamato-Security/359-output-default-details-when-undefined-details

output default details when undefined details
This commit is contained in:
DustInDark
2022-06-21 08:12:49 +09:00
committed by GitHub
7 changed files with 100 additions and 6 deletions

View File

@@ -10,6 +10,7 @@
- Clap Crateパッケージの更新 (#413) (@hitenkoku)
- オプションの指定がないときに、`--help`と同じ画面出力を行うように変更した。(#387) (@hitenkoku)
- ルール内に`details`フィールドがないときに、`rules/config/default_details.txt`に設定されたデフォルトの出力を行えるようにした。 (#359) (@hitenkoku)
**バグ修正:**

View File

@@ -10,6 +10,7 @@
- Updated clap crate package to version 3. (#413) (@hitnekoku)
- Updated the default usage and help menu. (#387) (@hitenkoku)
- Added default details output based on `rules/config/default_details.txt` when no `details` field in a rule is specified. (i.e. Sigma rules) (#359) (@hitenkoku)
**Bug Fixes:**

View File

@@ -506,7 +506,7 @@ Hayabusaの結果を標準出力に表示しているときデフォルト
* `Level`: YML検知ルールの`level`フィールドから来ています。(例:`informational`, `low`, `medium`, `high`, `critical`) デフォルトでは、すべてのレベルのアラートとイベントが出力されますが、`-m`オプションで最低のレベルを指定することができます。例えば`-m high`オプションを付けると、`high``critical`アラートしか出力されません。
* `Title`: YML検知ルールの`title`フィールドから来ています。
* `RecordID`: イベントレコードIDです。`<Event><System><EventRecordID>`フィールドから来ています。`-R`もしくは`--hide-record-id`オプションを付けると表示されません。
* `Details`: YML検知ルールの`details`フィールドから来ていますが、このフィールドはHayabusaルールにしかありません。このフィールドはアラートとイベントに関する追加情報を提供し、ログの`<Event><System><EventData>`部分から有用なデータを抽出することができます。イベントキーのマッピングが間違っている場合、もしくはフィールドが存在しない場合で抽出ができなかった箇所は`n/a` (not available)と記載されます。
* `Details`: YML検知ルールの`details`フィールドから来ていますが、このフィールドはHayabusaルールにしかありません。このフィールドはアラートとイベントに関する追加情報を提供し、ログのフィールドから有用なデータを抽出することができます。イベントキーのマッピングが間違っている場合、もしくはフィールドが存在しない場合で抽出ができなかった箇所は`n/a` (not available)と記載されます。YML検知ルールに`details`フィールドが存在しない時のdetailsのメッセージを`./rules/config/default_details.txt`で設定できます。`default_details.txt`では`Provider Name``EventID``details`の組み合わせで設定することができます。
CSVファイルとして保存する場合、以下の列が追加されます:

View File

@@ -505,7 +505,7 @@ When hayabusa output is being displayed to the screen (the default), it will dis
* `Level`: This comes from the `level` field in the YML detection rule. (`informational`, `low`, `medium`, `high`, `critical`) By default, all level alerts will be displayed but you can set the minimum level with `-m`. For example, you can set `-m high`) in order to only scan for and display high and critical alerts.
* `RecordID`: This comes from the `<Event><System><EventRecordID>` field in the event log. You can hidde this output with the `-R` or `--hide-record-id` option.
* `Title`: This comes from the `title` field in the YML detection rule.
* `Details`: This comes from the `details` field in the YML detection rule, however, only hayabusa rules have this field. This field gives extra information about the alert or event and can extract useful data from the `<Event><System><EventData>` portion of the log. For example, usernames, command line information, process information, etc... When a placeholder points to a field that does not exist or there is an incorrect alias mapping, it will be outputted as `n/a` (not available).
* `Details`: This comes from the `details` field in the YML detection rule, however, only hayabusa rules have this field. This field gives extra information about the alert or event and can extract useful data from the fields in event logs. For example, usernames, command line information, process information, etc... When a placeholder points to a field that does not exist or there is an incorrect alias mapping, it will be outputted as `n/a` (not available). If the `details` field is not specified (i.e. sigma rules), default `details` messages to extract fields defined in `./rules/config/default_details.txt` will be outputted. You can add more default `details` messages by adding the `Provider Name`, `EventID` and `details` message you want to output in `default_details.txt`.
The following additional columns will be added to the output when saving to a CSV file:

View File

@@ -6,7 +6,7 @@ use crate::detections::print::AlertMessage;
use crate::detections::print::DetectInfo;
use crate::detections::print::ERROR_LOG_STACK;
use crate::detections::print::MESSAGES;
use crate::detections::print::{CH_CONFIG, IS_HIDE_RECORD_ID, TAGS_CONFIG};
use crate::detections::print::{CH_CONFIG, DEFAULT_DETAILS, IS_HIDE_RECORD_ID, TAGS_CONFIG};
use crate::detections::print::{
LOGONSUMMARY_FLAG, PIVOT_KEYWORD_LIST_FLAG, QUIET_ERRORS_FLAG, STATISTICS_FLAG,
};
@@ -236,6 +236,16 @@ impl Detection {
};
let ch_str = &get_serde_number_to_string(&record_info.record["Event"]["System"]["Channel"])
.unwrap_or_default();
let provider = &get_serde_number_to_string(
&record_info.record["Event"]["System"]["Provider_attributes"]["Name"],
)
.unwrap_or_default();
let eid = get_serde_number_to_string(&record_info.record["Event"]["System"]["EventID"])
.unwrap_or_else(|| "-".to_owned());
let default_output = DEFAULT_DETAILS
.get(&format!("{}_{}", provider, &eid))
.unwrap_or(&"-".to_string())
.to_string();
let detect_info = DetectInfo {
filepath: record_info.evtx_filepath.to_string(),
rulepath: rule.rulepath.to_string(),
@@ -243,8 +253,7 @@ impl Detection {
computername: record_info.record["Event"]["System"]["Computer"]
.to_string()
.replace('\"', ""),
eventid: get_serde_number_to_string(&record_info.record["Event"]["System"]["EventID"])
.unwrap_or_else(|| "-".to_owned()),
eventid: eid,
channel: CH_CONFIG.get(ch_str).unwrap_or(ch_str).to_string(),
alert: rule.yaml["title"].as_str().unwrap_or("").to_string(),
detail: String::default(),
@@ -254,7 +263,10 @@ impl Detection {
};
MESSAGES.lock().unwrap().insert(
&record_info.record,
rule.yaml["details"].as_str().unwrap_or("").to_string(),
rule.yaml["details"]
.as_str()
.unwrap_or(&default_output)
.to_string(),
detect_info,
);
}

View File

@@ -65,6 +65,17 @@ lazy_static! {
pub static ref PIVOT_KEYWORD_LIST_FLAG: bool =
configs::CONFIG.read().unwrap().args.pivot_keywords_list;
pub static ref IS_HIDE_RECORD_ID: bool = configs::CONFIG.read().unwrap().args.hide_record_id;
pub static ref DEFAULT_DETAILS: HashMap<String, String> =
Message::get_default_details(&format!(
"{}/default_details.txt",
configs::CONFIG
.read()
.unwrap()
.args
.config
.as_path()
.display()
));
}
impl Default for Message {
@@ -224,6 +235,59 @@ impl Message {
pub fn clear(&mut self) {
self.map.clear();
}
/// detailsのdefault値をファイルから読み取る関数
pub fn get_default_details(filepath: &str) -> HashMap<String, String> {
let read_result = utils::read_csv(filepath);
match read_result {
Err(_e) => {
AlertMessage::alert(&_e).ok();
HashMap::new()
}
Ok(lines) => {
let mut ret: HashMap<String, String> = HashMap::new();
lines
.into_iter()
.try_for_each(|line| -> Result<(), String> {
let provider = match line.get(0) {
Some(_provider) => _provider.trim(),
_ => {
return Result::Err(
"Failed to read provider in default_details.txt.".to_string(),
)
}
};
let eid = match line.get(1) {
Some(eid_str) => match eid_str.trim().parse::<i64>() {
Ok(_eid) => _eid,
_ => {
return Result::Err(
"Parse Error EventID in default_details.txt.".to_string(),
)
}
},
_ => {
return Result::Err(
"Failed to read EventID in default_details.txt.".to_string(),
)
}
};
let details = match line.get(2) {
Some(detail) => detail.trim(),
_ => {
return Result::Err(
"Failed to read details in default_details.txt.".to_string(),
)
}
};
ret.insert(format!("{}_{}", provider, eid), details.to_string());
Ok(())
})
.ok();
ret
}
}
}
}
impl AlertMessage {
@@ -675,6 +739,17 @@ mod tests {
_check_hashmap_element(&expected, actual2);
}
#[test]
fn _get_default_defails() {
let expected: HashMap<String, String> = HashMap::from([
("Microsoft-Windows-PowerShell_4104".to_string(),"%ScriptBlockText%".to_string()),("Microsoft-Windows-Security-Auditing_4624".to_string(), "User: %TargetUserName% | Comp: %WorkstationName% | IP Addr: %IpAddress% | LID: %TargetLogonId% | Process: %ProcessName%".to_string()),
("Microsoft-Windows-Sysmon_1".to_string(), "Cmd: %CommandLine% | Process: %Image% | User: %User% | Parent Cmd: %ParentCommandLine% | LID: %LogonId% | PID: %ProcessId% | PGUID: %ProcessGuid%".to_string()),
("Service Control Manager_7031".to_string(), "Svc: %param1% | Crash Count: %param2% | Action: %param5%".to_string()),
]);
let actual = Message::get_default_details("test_files/config/default_details.txt");
_check_hashmap_element(&expected, actual);
}
/// check two HashMap element length and value
fn _check_hashmap_element(expected: &HashMap<String, String>, actual: HashMap<String, String>) {
assert_eq!(expected.len(), actual.len());

View File

@@ -0,0 +1,5 @@
Provider, EID, Details
Microsoft-Windows-PowerShell, 4104, %ScriptBlockText%
Microsoft-Windows-Security-Auditing, 4624, User: %TargetUserName% | Comp: %WorkstationName% | IP Addr: %IpAddress% | LID: %TargetLogonId% | Process: %ProcessName%
Microsoft-Windows-Sysmon, 1, Cmd: %CommandLine% | Process: %Image% | User: %User% | Parent Cmd: %ParentCommandLine% | LID: %LogonId% | PID: %ProcessId% | PGUID: %ProcessGuid%
Service Control Manager, 7031, Svc: %param1% | Crash Count: %param2% | Action: %param5%