v2.0.0 - Black Hat Arsenal USA Release
2.0.0 [2026/07/31] - Black Hat Arsenal USA 2026 Release
New Features:
- Added the
azure-timelinecommand to create a DFIR timeline for Azure logs. (#109) (@fukusuket) - New
aws-ct-searchcommand to search through CloudTrail logs. (#117) (@fukusuket) - Added support for an ignore-list file (
config/aws_ignore_rule_list.txt) to skip loading rules by UUID, so superseded/duplicate rules can stay in the repo without being loaded. (#136) (@YamatoSecurity) - Added Parquet (
.parquet) input support to the AWS commands (aws-ct-timeline,aws-ct-metrics,aws-ct-summary,aws-ct-search) for both-fand-d. Each row is converted to one JSON event: nested struct columns become nested objects, the known CloudTrail envelope fields (userIdentity,requestParameters,responseElements, …) stored as serialized JSON strings — the shape Athena CTAS / Glue / Firehose pipelines produce — are parsed back into objects so rules can match nested values, and timezone-lesseventTimeTIMESTAMP columns are marked as UTC so time filtering and summaries keep working. Snappy, gzip, zstd, and lz4 compression are supported. (@nishikawaakira)
Enhancements:
- Replaced the wildcard (
"*") version requirements on ten dependencies with explicit floors, so a future breaking release is a deliberate bump rather than something acargo updateaccepts silently. Six of them are pre-1.0, where every minor is a breaking change under Cargo's semver rules. This is the failure mode behind #131, where a wildcard-pinnedcidr-utilsshipped a backend that stopped accepting abbreviated CIDR and--geo-ipbegan panicking at startup with nothing in Suzaku having changed. No resolved version changes:cargo updateproduces an identicalCargo.lockwith and without the floors. (#189) (@YamatoSecurity) - Errors that end the run are now all printed in red and all honor
--no-color. Previously the color was inconsistent: some (a nonexistent input path, an invalid-m, --min-level, a missing rules folder or output profile, an existing output file without-C, a failedupdate-rules) were printed in the default terminal color, and a few others were hard-coded red that ignored--no-color. They now share a singleerror_msg()helper withfatal_error(). (@fukusuket) - Warnings and errors raised while processing logs (skipped files, a failed directory scan, correlation-event errors, a missing abused-AWS-API list) are now written to
logs/errorlog-<YYYYMMDD_HHMMSS>.loginstead of the terminal, so they no longer interleave with the progress bar or bury the results summary. The file is created only when there is something to report, its first line is the command line that produced it, and the run ends with a singleWarnings and errors: <n> messages saved to <path>pointer. Fatal errors that abort the run are still printed to the terminal as well. (@fukusuket) - Reworked
-t, --output-typeforaws-ct-summaryto take format names like the timeline commands, and added DuckDB output. Passcsv,json,jsonl, and/orduckdb(comma-separated or repeated), e.g.-t csv,duckdb. Because the CSV folds each principal's API calls and attributes into multi-line cells that cannot be queried, the DuckDB output stores them relationally across three tables —summary(one row per principal),summary_api_calls(one row per principal/API, labelledabused_success/abused_failed/other_success/other_failed) andsummary_attributes(one row per principal/value, labelledaws_region/src_ip/access_key_id/user_agent) — so questions the CSV cannot answer, such as which source IPs were used by the principals that called an abused API, become ordinary joins. (Breaking: the numeric-t 1..5form is replaced by names, matching the other commands.) (@YamatoSecurity) - Reworked
-t, --output-typeforaws-ct-timelineandazure-timelineto take format names instead of numbers, and added DuckDB output. Passcsv,json,jsonl, and/orduckdb(comma-separated or repeated) to write any combination at once, e.g.-t csv,duckdb; DuckDB output is a self-contained.duckdbdatabase with atimelinetable whose columns are the output-profile fields. (Breaking: the numeric-t 1..5form is replaced by names;aws-ct-searchshares the option and gains the same named formats.) (@YamatoSecurity) - Added a
Tagscolumn to theaws-ct-timelineandazure-timelineoutput that renders a rule's Sigmatagslist as a single¦-separated string (like Hayabusa) instead of dropping the list. ATT&CK tactics are abbreviated using the editableconfig/mitre_tactics.txttable shared with Hayabusa (e.g.attack.credential-accessbecomesCredAccess), while techniques and groups are shortened (attack.t1562.001becomesT1562.001,attack.g0035becomesG0035); both the hyphen and underscore tactic spellings are handled. JSON output keeps the value as a flat string. (#62) (@YamatoSecurity) - Added a
-l, --localtimeoption to theaws-ct-timelineandazure-timelinecommands that outputs event timestamps in the machine's local timezone with an explicit UTC offset (e.g.2023-07-10 12:27:45becomes2023-07-10 21:27:45+09:00in JST) instead of UTC. Unparseable timestamps fall back to the existing UTC rendering. (#34) (@YamatoSecurity) - Bumped
sigma-rustto the releasedv0.7.1and updated all other dependencies to their latest versions.sigma-rustv0.7.1 keeps the Sigma correlation support suzaku relies on while moving its YAML backend off the deprecatedserde_yml/noyalib(which parsed large unsigned 64-bit values in rules/events as lossy floats) to the actively maintainedyaml_serde, restoring correctu64parsing. (@YamatoSecurity) - Code refactored for easier handling of different log sources. (@fukusuket)
- Added support for Microsoft Graph API JSON format for Azure logs. (#113) (@fukusuket)
azure-timelinenow unwraps the{ "records": [...] }batch envelope used by Azure Monitor diagnostic-settings blobs and Event Hub messages (both whole-file and per-line), so those exports are read record-by-record instead of as a single event, and it now loads/matches theidentity_protection(riskdetection) andprivileged_identity_management(pim) rule types, which were previously dropped at load. (#130) (@YamatoSecurity)azure-timelinenow loads and matches SigmaHQ's Microsoft 365 rules, which declarelogsource.serviceasaudit/exchange/threat_detection/threat_management— onlym365was recognized before, so every upstream m365 rule was dropped at load. These services are routed through the sameWorkload/RecordTypeUnified Audit Log discriminator. (#137) (@YamatoSecurity)- Added support for the M365 Unified Audit Log to
azure-timeline: readsSearch-UnifiedAuditLogCSV exports (and JSON) by unwrapping theAuditDatacolumn/wrapper, folds UAL Name/Value property bags (ExtendedProperties/Parameters/…) into objects so rules can match nested values (e.g.ExtendedProperties.UserAgent), parses single/pretty-printed record objects, no longer drops events when no time filter is set, parses theCreationTimetimestamp, and adds anm365log-source service. The Azure output profile now surfaces DFIR-relevant M365 fields (Workload,Operation,Result,User,SrcIP,TargetObject,UserAgent,AppId,LogonError, and aDetailssummary of the change'sParameters/ModifiedProperties) instead of the previously empty Azure-Monitor-only columns. (#129) (@YamatoSecurity) - Added
--file-date-from/--file-date-tooptions that filter objects by their S3 key date prefix, distinct from the existing--timeline-start/--timeline-endoptions, which operates on in-file event timestamps. (#118) (@fukusuket) - Added
-output-typeoption for theaws-ct-summarycommand to output in JSON. (#123) (@fukusuket) aws-ct-metricscan now aggregate several fields in a single scan and enrich the results.-F, --field-nametakes a comma-separated list (e.g.-F sourceIPAddress,userAgent,userIdentity.arn,awsRegion,userIdentity.accessKeyId) and tallies every field in one pass over the logs, instead of requiring a full scan per field; each value additionally reports theFirstSeen/LastSeenof that value;-G, --geo-ipaddsSrcASN/SrcCity/SrcCountrycolumns for values that parse as IP addresses;-t, --output-typewrites csv/json/jsonl/duckdb (the DuckDB output is a single queryablemetricstable); and-s, --include-sts-keysmatchesaws-ct-summaryby excluding temporaryASIA...STS keys unless asked for. Events that do not have the requested field are now counted as-rather than dropped, so the percentages are a share of every event scanned.-Fis also validated against the CloudTrail record fields before the scan starts, so a mis-cased name fails immediately with'sourceIPaddress' is not a CloudTrail field. Did you mean 'sourceIPAddress'?instead of scanning the whole dataset to report-for every event; any sub-path of an API-specific container (requestParameters,responseElements,additionalEventData, ...) is still accepted as-is. (Breaking: the CSV columns changed fromEventName,Percent,TotaltoField,Value,Count,Percent,FirstSeen,LastSeen.) (@fukusuket)
Bug Fixes:
--geo-ipreported every routable IPv6 address asPrivateand never looked it up:2000::/3, the entire IPv6 global unicast space, was listed as a private range. Public addresses such as a Japanese consumer ISP or an Azure sign-in from anywhere renderedSrcASN: Private/SrcCountry: -, which does not just omit the geolocation but asserts something untrue — an analyst readingPrivateconcludes the traffic was internal. IPv6 is now geolocated like IPv4; unspecified, link-local, unique-local and multicast addresses keep their placeholders. (#182) (@YamatoSecurity)--geo-ipleft the geolocation columns empty whenever an earlier field in the profile'sSrcIPfallback list was present but unusable. The lookup took the first candidate that existed and only then tried to parse it, so aclaims.ipaddrholding"","-", ahost:portpair (the common M365ClientIPshape) or any other non-IP ended the search and the routable address sitting incallerIpAddresswas never consulted —SrcASN/SrcCity/SrcCountryrendered-for a record that plainly carried a public IP. The search now takes the first candidate that parses as an IP address, and theSrcIPcolumn resolves through the same selector, so the address displayed and the address geolocated are always the same field. When no candidate parses,SrcIPstill shows what the log recorded (an AWS-service event writescloudtrail.amazonaws.comthere) and the geo columns show-. (#183) (@YamatoSecurity)- The
aws-ct-metricstable and CSV always labelled the first columnEventName, even when-Faggregated a different field, so a run oversourceIPAddressproduced a column of IP addresses headedEventName. The column is now named after the field that was actually aggregated. The command also silently overwrote an existing-ofile; overwriting now requires-C, --clobber, as it does for every other command. Values with equal counts are additionally sorted by value, so repeated runs produce byte-identical output instead of shuffling rows inHashMaporder. (@fukusuket) - The
Results Summary"Data reduction" line panicked (attempt to subtract with overflow) in debug builds — or printed a nonsensical ~1.8×10¹⁹ count in release, orNaN%on empty input — becauseevent_with_hits(which correlation results increment for events already counted by the base scan) could exceedtotal_events. The count now saturates and the percentage is guarded against an empty dataset. (#163) (@YamatoSecurity) aws-ct-summaryreported the wrong per-entry time range: thefirst_seen/last_seenof every summarized region, source IP, access key, user agent, and API were seeded once from the dataset-global min/max at the moment the key was first inserted and never updated, so they showed the whole dataset's span rather than that entry's own first/last occurrence. Each entry now tracks the first/last event time of the events that actually hit it. (#160) (@YamatoSecurity)- Scanning now warns (
[WARNING] Skipping <file>: <reason>) when it skips an input file it could not read — permission denied, non-UTF-8 content, a file removed mid-scan, or a corrupt/oversized.gz— instead of silently dropping it. Previously such a file was counted in the total but skipped with no indication, so the reported coverage was overstated. Applies to both the directory scan and single-file input, and the gzip size-cap warning was moved to this single call-site path. (#161) (@YamatoSecurity) aws-ct-searchno longer panics on an invalid--regexvalue (it was compiled with.expect()); an invalid pattern now prints a clear error and exits cleanly. A malformed--filtermissing theFIELD:VALUEcolon — previously silently ignored — is now rejected up front. Andaws-ct-summary's warning when the abused-AWS-API list can't be opened now names the path it looked for and states that all API calls will be classified as non-abused. (#162) (@YamatoSecurity)--geo-ipenrichment (SrcASN/SrcCity/SrcCountry) was a silent no-op forazure-timeline/M365 logs: the source IP was resolved from a hardcodedsourceIPAddressfield, which only exists in AWS CloudTrail. The Azure/M365 profile mapsSrcIPtocallerIpAddress/ClientIP/etc., so those columns always rendered-even for events with a routable public IP. The lookup now resolves the source IP from the profile'sSrcIPfield spec (the same|-separated fallback used for other columns), so Azure/M365 enrich just like AWS. (#159) (@YamatoSecurity)- Malformed
--timeline-start/--timeline-end/--time-offsetvalues are now rejected up front with a clear error, instead of being parsed per-event and silently dropping every event (empty timeline, no warning) — e.g. a plain--timeline-start 2024-01-01instead of full RFC 3339. Also fixed aparse_offsetpanic on an empty offset, trailing whitespace, or a multibyte trailing character (the split index was taken from the untrimmed length). (#150) (@YamatoSecurity) - Fixed a panic (
byte index 24 is not a char boundary) when the end-of-scan "Rule Authors" summary truncated an author name longer than 27 bytes whose 24th byte fell mid-codepoint — routine for Japanese/CJK and other non-ASCII author names common in Sigma rule packs. Truncation now counts and cuts by characters, not bytes, so the completed run's output is no longer discarded. (#148) (@YamatoSecurity) - Neutralized CSV/spreadsheet formula injection (CWE-1236) in report output. CSV cells come from attacker-influenceable cloud-log fields (
userAgent, principal ARNs, error strings, …); a value beginning with=,+,-,@, tab, or CR would be evaluated as a formula when the report is opened in Excel/LibreOffice/Sheets. Such values are now prefixed with an apostrophe (spreadsheets treat it as a force-text marker) at all CSV sinks; JSON/JSONL and stdout are unchanged. (#146) (@YamatoSecurity) - Bounded gzip decompression to stop a decompression-bomb OOM: a crafted or corrupt
.gzfile anywhere in the scanned tree could inflate to many GB (DEFLATE reaches ~1032:1) and get the entire scan OOM-killed..gzinputs are now capped at 3 GiB decompressed, and an over-limit file is skipped with a warning instead of aborting the run. (#147) (@YamatoSecurity) --geo-ipcorrupted the timeline output: when a record'ssourceIPAddresswas not a parseable IP address (routine for AWS-service events such ascloudtrail.amazonaws.com), the GeoIP lookup returned that raw string for every output column, overwritingTimestamp,EventName,RuleTitle, etc. Enrichment is now scoped to theSrcASN/SrcCity/SrcCountrycolumns only, which fall back to-when the address cannot be resolved. (#145) (@YamatoSecurity)- Hardened the input/filesystem edge cases that previously aborted a scan with a Rust panic and backtrace: a non-UTF-8 filename anywhere in the scanned tree (which killed the initial file count before any results), an unreadable subdirectory or a file removed mid-scan, and an unwritable
--outputpath now produce a clean, actionable error instead — the walk errors are reported and the output error exits non-zero with aCannot write to output file …message. Applies acrossaws-ct-timeline,azure-timeline,aws-ct-search,aws-ct-metrics, andaws-ct-summary. Non-UTF-8 filenames are now read (the real path is kept through the scan pipeline) instead of being counted but skipped, andaws-ct-summary's JSON/JSONL output also reports an unwritable--outputpath cleanly. (#149) (@YamatoSecurity) - The
aws-ct-timeline,aws-ct-metrics,aws-ct-search, andaws-ct-summarycommands silently dropped JSONL input (one CloudTrail event, or a{ "Records": [...] }batch, per line): the parsers read the whole file as a single JSON document and returned no events when that failed. They now fall back to per-line JSONL parsing, and.jsonlfiles are discovered and read. (#139) (@YamatoSecurity) -T, --no-frequency-timelineoption was not working so we removed it. Also fixed a logic bug in the authors display. (#110) (@fukusuket)- Output file would get saved even if there were no results. (#114) (@fukusuket)
aws-ct-summarywould panic when processing a corrupt or imcomplete log file. (#119) (@fukusuket)--geo-ipwould panic at startup (invalid IP address syntax) because the abbreviated CIDR strings used for the private-IP check (e.g.10/8,172.16/12,2000::/3) are no longer accepted by thecidrcrate. Dropped thecidr-utilsdependency and check private ranges directly withstd'sIpv4Addr::is_private()and a manual IPv6 prefix match; also populated the previously unused GeoIP country/city caches. (#132) (@fukusuket)
新機能:
- Azureログ用のDFIRタイムラインを作成する
azure-timelineコマンドを追加した。 (#109) (@fukusuket) - CloudTrailログを検索するための
aws-ct-searchコマンドを追加した。(#117) (@fukusuket) - UUIDを指定してルールを読み込み対象から除外できる除外リストファイル(
config/aws_ignore_rule_list.txt)に対応した。これにより、置き換えられた重複ルールをリポジトリに残したまま読み込まないようにできる。 (#136) (@YamatoSecurity) - AWS系コマンド(
aws-ct-timeline、aws-ct-metrics、aws-ct-summary、aws-ct-search)の-fと-dの両方で Parquet(.parquet)入力に対応した。各行を1つのJSONイベントに変換する。ネストされたstruct型カラムはネストされたオブジェクトになり、JSON文字列として格納されたCloudTrailの既知のエンベロープフィールド(userIdentity、requestParameters、responseElementsなど。Athena CTAS / Glue / Firehose パイプラインが生成する形式)はオブジェクトに復元してルールがネストされた値にマッチできるようにし、タイムゾーンなしのeventTimeTIMESTAMPカラムはUTCとしてマークして時刻フィルタとサマリが機能するようにした。snappy、gzip、zstd、lz4圧縮に対応。 (@nishikawaakira)
改善:
-
10個の依存クレートのバージョン指定をワイルドカード(
"*")から明示的な下限付きに変更した。これにより、将来の破壊的変更を含むリリースがcargo updateで自動的に取り込まれることがなくなり、意図的なバージョン更新が必要になる。うち6個は1.0未満のクレートであり、Cargoのセマンティックバージョニングではマイナーバージョンの更新がすべて破壊的変更として扱われる。これは#131で実際に発生した問題であり、ワイルドカード指定のcidr-utilsが省略形のCIDR表記を受け付けないバックエンドに更新された結果、Suzaku側は何も変更していないにもかかわらず--geo-ipが起動時にパニックしていた。解決されるバージョンは変わらない(下限指定の有無にかかわらずcargo updateは同一のCargo.lockを生成する)。 (#189) (@YamatoSecurity) -
実行を終了するエラーの表示色をすべて赤に統一し、
--no-colorにも対応させた。従来は色が統一されておらず、一部(存在しない入力パス、不正な-m, --min-level、ルールフォルダや出力プロファイルの未検出、-Cなしでの出力ファイル上書き、update-rulesの失敗)はターミナルの既定色で表示され、逆に一部は--no-colorを無視する赤色のハードコードになっていた。これらをfatal_error()と共通のerror_msg()ヘルパーに集約した。 (@fukusuket) -
ログ処理中に発生した警告・エラー(読み込めずスキップしたファイル、ディレクトリスキャンの失敗、相関イベント処理のエラー、悪用AWS APIリストの読み込み失敗など)を、ターミナルではなく
logs/errorlog-<YYYYMMDD_HHMMSS>.logに出力するようにした。これによりプログレスバーの表示が崩れたり、結果サマリーがメッセージで埋もれたりしなくなる。ファイルは出力すべきメッセージがある場合のみ作成され、先頭行には実行したコマンドラインを出力する。実行の最後にWarnings and errors: <件数> messages saved to <パス>の1行のみを表示する。実行を中断する致命的なエラーは従来どおりターミナルにも表示する。 (@fukusuket) -
aws-ct-summaryの-t, --output-typeをタイムライン系コマンドと同様に形式名で指定する方式に変更し、DuckDB 出力を追加した。csv・json・jsonl・duckdbをカンマ区切り(または繰り返し)で指定できる(例:-t csv,duckdb)。CSV では各プリンシパルの API 呼び出しや属性が複数行のセルにまとめられており SQL で扱えないため、DuckDB 出力ではリレーショナルな3テーブル構成にした。summary(プリンシパルごとに1行)、summary_api_calls(プリンシパルと API ごとに1行、abused_success/abused_failed/other_success/other_failedのラベル付き)、summary_attributes(プリンシパルと値ごとに1行、aws_region/src_ip/access_key_id/user_agentのラベル付き)である。これにより、悪用された API を呼び出したプリンシパルがどの送信元 IP を使っていたかといった、CSV では答えられない問いが通常の JOIN で解けるようになる。(破壊的変更: 数字-t 1..5は他コマンドと同様に名前指定へ置き換えられた。) (@YamatoSecurity) -
aws-ct-timeline・azure-timelineの-t, --output-typeを数字ではなく形式名を指定する方式に変更し、DuckDB 出力を追加した。csv・json・jsonl・duckdbをカンマ区切り(または繰り返し)で指定して、任意の組み合わせを同時に出力できる(例:-t csv,duckdb)。DuckDB 出力は、出力プロファイルの各項目を列に持つtimelineテーブルを含む自己完結型の.duckdbデータベースファイルである。(破壊的変更: 数字-t 1..5は名前指定に置き換えられた。aws-ct-searchは同じオプションを共有するため同様に名前形式に対応する。) (@YamatoSecurity) -
aws-ct-timelineおよびazure-timelineの出力にTagsカラムを追加した。ルールの Sigmatagsリストを(破棄せずに)Hayabusa のように¦区切りの1つの文字列として出力する。ATT&CK のタクティクスは Hayabusa と共通の編集可能なconfig/mitre_tactics.txtテーブルを使って略記され(例:attack.credential-accessはCredAccess)、テクニックやグループも短縮される(attack.t1562.001はT1562.001、attack.g0035はG0035)。タクティクスのハイフン表記とアンダースコア表記の両方に対応する。JSON 出力では値をフラットな文字列のまま保持する。 (#62) (@YamatoSecurity) -
aws-ct-timelineおよびazure-timelineコマンドに、イベントのタイムスタンプを(UTC ではなく)実行環境のローカルタイムゾーンで明示的な UTC オフセット付きで出力する-l, --localtimeオプションを追加した(例: JST では2023-07-10 12:27:45が2023-07-10 21:27:45+09:00になる)。解析できないタイムスタンプは従来どおり UTC 表記にフォールバックする。 (#34) (@YamatoSecurity) -
sigma-rustをリリース版のv0.7.1に更新し、その他の依存クレートもすべて最新版に更新した。sigma-rustv0.7.1 は suzaku が利用している Sigma の相関(correlation)機能を維持したまま、YAML バックエンドを非推奨のserde_yml/noyalib(ルールやイベント中の64ビット符号なし整数の大きな値を精度の落ちた浮動小数点として解析していた)から、活発にメンテナンスされているyaml_serdeに移行し、u64の正しい解析を回復した。 (@YamatoSecurity) -
azure-timelineが SigmaHQ の Microsoft 365 ルールを読み込み・マッチできるようにした。これらのルールはlogsource.serviceをaudit/exchange/threat_detection/threat_managementとして宣言しているが、従来はm365しか認識されず、アップストリームの m365 ルールがすべて読み込み時に破棄されていた。これらのサービスをm365と同じ Unified Audit Log の判別(Workload/RecordType)で振り分けるようにした。 (#137) (@YamatoSecurity) -
異なるログソースの取り扱いを容易にするため、コードをリファクタリングした。 (@fukusuket)
-
Microsoft Graph API JSON形式のAzureログに対応した。 (#113) (@fukusuket)
-
azure-timelineが、Azure Monitorの診断設定BLOBやEvent Hubメッセージで使われる{ "records": [...] }形式のバッチエンベロープを展開するようにした(ファイル全体・行単位の両方に対応)。これにより、これらのエクスポートが単一のイベントではなくレコード単位で読み込まれるようになった。また、従来は読み込み時に破棄されていたidentity_protection(riskdetection)およびprivileged_identity_management(pim)のルール種別も読み込み・マッチできるようにした。 (#130) (@YamatoSecurity) -
azure-timelineをMicrosoft 365統合監査ログ(Unified Audit Log)に対応させた。AuditData列・ラッパーを展開することでSearch-UnifiedAuditLogのCSVエクスポート(およびJSON)を読み込み、UALのName/Value形式のプロパティバッグ(ExtendedProperties/Parametersなど)をオブジェクトに変換してルールがネストされた値(例:ExtendedProperties.UserAgent)にマッチできるようにし、単一・整形済みのレコードオブジェクトも解析できるようにした。あわせて、時刻フィルタが未指定の場合にイベントが破棄されないよう修正し、CreationTimeのタイムスタンプを解析できるようにし、ログソースのサービスとしてm365を追加した。Azureの出力プロファイルも、従来のAzure Monitor専用の空欄になっていたカラムに代わり、DFIRで有用なM365のフィールド(Workload、Operation、Result、User、SrcIP、TargetObject、UserAgent、AppId、LogonError、および変更内容のParameters/ModifiedPropertiesをまとめたDetails)を出力するようにした。 (#129) (@YamatoSecurity) -
既存の
--timeline-start/--timeline-endオプション(ファイル内のイベントタイムスタンプに基づいて動作する)とは異なり、S3キーの日付プレフィックスに基づいてオブジェクトをフィルタリングする--file-date-from/--file-date-toオプションを追加した。 (#118) (@fukusuket) -
aws-ct-summaryコマンドに、JSON形式で出力するための-output-typeオプションを追加した。 (#123) (@fukusuket) -
aws-ct-metricsで複数フィールドを1回のスキャンでまとめて集計し、結果を拡充できるようにした。-F, --field-nameはカンマ区切りのリストを受け付け(例:-F sourceIPAddress,userAgent,userIdentity.arn,awsRegion,userIdentity.accessKeyId)、フィールドごとにフルスキャンを繰り返すのではなく、1回のスキャンですべてのフィールドを集計する。各値について、その値のFirstSeen/LastSeenも出力するようにした。-G, --geo-ipを指定すると、IPアドレスとして解釈できる値にSrcASN/SrcCity/SrcCountry列が追加される。-t, --output-typeでcsv/json/jsonl/duckdbを出力できる(DuckDB出力はそのままクエリできる単一のmetricsテーブル)。-s, --include-sts-keysはaws-ct-summaryと同様で、一時的なASIA...のSTSキーは指定しない限り除外する。また、指定したフィールドを持たないイベントは破棄せず-として集計するようにしたため、割合はスキャンした全イベントに対する比率になる。さらに-Fはスキャン開始前にCloudTrailのレコードフィールドと照合して検証するようにした。大文字小文字を誤った名前は、データセット全体をスキャンして全イベントを-として報告する代わりに、'sourceIPaddress' is not a CloudTrail field. Did you mean 'sourceIPAddress'?として即座に失敗する。APIごとに内容が異なるコンテナ(requestParameters・responseElements・additionalEventDataなど)配下の任意のパスは従来どおりそのまま指定できる。(破壊的変更: CSVの列がEventName,Percent,TotalからField,Value,Count,Percent,FirstSeen,LastSeenに変更された。) (@fukusuket)
バグ修正:
-
--geo-ipがすべてのグローバルユニキャストIPv6アドレスをPrivateとして報告し、データベースを参照していなかった問題を修正した。IPv6のグローバルユニキャスト空間全体である2000::/3がプライベート範囲として登録されていたため、日本のISPやAzureサインイン元などのパブリックアドレスがSrcASN: Private/SrcCountry: -と表示されていた。これは単に位置情報が欠けるだけでなく、事実と異なる情報を提示するものであり、分析者がPrivateを見て内部通信だと誤認する原因となる。IPv6もIPv4と同様に位置情報を付与するようにした。未指定・リンクローカル・ユニークローカル・マルチキャストアドレスは従来どおりプレースホルダーのままとなる。 (#182) (@YamatoSecurity) -
プロファイルの
SrcIPフォールバックリストにおいて、先に並ぶフィールドが存在するものの利用できない値だった場合に--geo-ipの位置情報カラムが空になる問題を修正した。従来は存在する最初の候補を採用してから解析を試みていたため、claims.ipaddrが""、"-"、host:port形式(M365のClientIPでよくある形式)などIPアドレスとして解析できない値を持っていると探索がそこで終了し、callerIpAddressにある有効なアドレスが参照されなかった。その結果、公開IPを含むレコードでもSrcASN/SrcCity/SrcCountryが-と表示されていた。IPアドレスとして解析できる最初の候補を採用するようにした。またSrcIPカラムも同じ選択ロジックを使うようにしたため、表示されるアドレスと位置情報を取得したアドレスが常に一致する。どの候補も解析できない場合は、SrcIPにはログに記録された値(AWSサービスによるイベントのcloudtrail.amazonaws.comなど)がそのまま表示され、位置情報カラムは-となる。 (#183) (@YamatoSecurity) -
aws-ct-metricsのテーブルおよびCSVの1列目の見出しが、-Fで別のフィールドを集計した場合でも常にEventNameになっていた問題を修正した(sourceIPAddressを集計すると、IPアドレスの列にEventNameという見出しが付いていた)。実際に集計したフィールド名を見出しに使用するようにした。また、-oで既存ファイルを無言で上書きしていたため、他のコマンドと同様に上書きには-C, --clobberを必要とするようにした。さらに、件数が同じ値は値でソートするようにしたため、HashMapの順序で行が入れ替わることがなくなり、同じ入力に対して常に同じ出力になる。 (@fukusuket) -
Results Summaryの「Data reduction」行が、デバッグビルドでパニック(attempt to subtract with overflow)していた問題を修正した(リリースビルドでは約 1.8×10¹⁹ という無意味な件数、空入力ではNaN%を表示していた)。相関(correlation)結果はベーススキャンで既に数えたイベントに対してevent_with_hitsを再度加算するため、event_with_hitsがtotal_eventsを上回ることがあった。件数を飽和減算(saturating)で計算し、パーセンテージも空データセットに対してガードするようにした。 (#163) (@YamatoSecurity) -
aws-ct-summaryが各エントリの時間範囲を誤って報告していた問題を修正した。集計対象のリージョン・送信元IP・アクセスキー・ユーザーエージェント・API それぞれのfirst_seen/last_seenが、キーを最初に挿入した時点のデータセット全体の最小/最大値で一度だけ設定され、その後更新されていなかったため、そのエントリ自身の初回/最終発生時刻ではなくデータセット全体の範囲を表示していた。各エントリが、実際にそのキーに該当したイベントの初回/最終時刻を追跡するようにした。 (#160) (@YamatoSecurity) -
読み込めなかった入力ファイル(アクセス権限なし・UTF-8 として不正な内容・スキャン中に削除・破損した/サイズ超過の
.gz)をスキャンがスキップする際に、無言で読み飛ばすのではなく警告([WARNING] Skipping <file>: <reason>)を表示するようにした。従来はそのようなファイルも合計ファイル数には数えられつつ無言でスキップされ、報告されるカバレッジが過大になっていた。ディレクトリスキャンと単一ファイル入力の両方に適用され、gzip のサイズ上限警告もこの呼び出し側の1箇所に集約した。 (#161) (@YamatoSecurity) -
aws-ct-searchが不正な--regex値でパニックしていた問題を修正した(.expect()でコンパイルしていた)。不正なパターンは明確なエラーを表示してクリーンに終了するようにした。また、FIELD:VALUEのコロンを欠いた不正な--filterは、従来は無言で無視されていたが、起動時に拒否するようにした。さらに、aws-ct-summaryが abused AWS API リストを開けなかった際の警告に、探索したパスと「全 API 呼び出しが非不正として分類される」旨を明記するようにした。 (#162) (@YamatoSecurity) -
--geo-ipによるエンリッチメント(SrcASN/SrcCity/SrcCountry)がazure-timeline/M365 ログでは無言で機能していなかった問題を修正した。送信元 IP を AWS CloudTrail にしか存在しないsourceIPAddressフィールドから固定で解決していたため、SrcIPをcallerIpAddress/ClientIPなどにマッピングする Azure/M365 プロファイルでは、ルーティング可能なパブリック IP を持つイベントでもこれらの列が常に-になっていた。送信元 IP を(他の列と同じ|区切りのフォールバックで)プロファイルのSrcIPフィールド仕様から解決するようにし、Azure/M365 でも AWS と同様にエンリッチメントされるようにした。 (#159) (@YamatoSecurity) -
不正な
--timeline-start/--timeline-end/--time-offsetの値を、イベントごとに解析して全イベントを無言で読み飛ばす(空のタイムライン・警告なし)のではなく、起動時に明確なエラーで拒否するようにした(例: RFC 3339 形式ではない--timeline-start 2024-01-01)。また、空のオフセット・末尾の空白・マルチバイトの末尾文字でparse_offsetがパニックしていた問題(分割位置をトリム前の長さから求めていた)を修正した。 (#150) (@YamatoSecurity) -
スキャン終了時の「Rule Authors」サマリーで、27バイトを超え24バイト目がマルチバイト文字の途中に来る作者名を切り詰める際に発生していたパニック(
byte index 24 is not a char boundary)を修正した。日本語などの非ASCII作者名(Sigma ルールパックで一般的)で起きていた。切り詰めをバイトではなく文字単位で行うようにし、完了済みの結果が破棄されないようにした。 (#148) (@YamatoSecurity) -
CSV/表計算ソフトの数式インジェクション(CWE-1236)をレポート出力で無害化した。CSV のセルは攻撃者が影響を与えられるクラウドログのフィールド(
userAgent、プリンシパル ARN、エラー文字列など)に由来し、=・+・-・@・タブ・CR で始まる値は Excel/LibreOffice/Sheets で開いた際に数式として評価されてしまう。これらの値は全ての CSV 出力箇所でアポストロフィを前置(表計算ソフトはテキスト強制マーカーとして扱う)するようにした。JSON/JSONL と標準出力は変更しない。 (#146) (@YamatoSecurity) -
gzip の展開サイズを制限し、展開爆弾(decompression bomb)による OOM を防いだ。スキャン対象ツリー内の細工された・破損した
.gzが数GB(DEFLATE は約1032:1)に展開し、スキャン全体が OOM で強制終了される可能性があった。.gz入力は展開後 3 GiB を上限とし、上限を超えるファイルは実行を中断せず警告を表示してスキップするようにした。 (#147) (@YamatoSecurity) -
--geo-ipがタイムライン出力を破壊していた問題を修正した。レコードのsourceIPAddressが解析可能な IP アドレスでない場合(cloudtrail.amazonaws.comなどの AWS サービスイベントでは一般的)、GeoIP ルックアップがその生の文字列をすべての出力列に返し、Timestamp・EventName・RuleTitleなどを上書きしていた。エンリッチメントをSrcASN・SrcCity・SrcCountryの3列のみに限定し、アドレスを解決できない場合は-を出力するようにした。 (#145) (@YamatoSecurity) -
スキャンをRustのパニックとバックトレースで中断させていた入力・ファイルシステム関連のエッジケースを堅牢化した。スキャン対象ツリー内のUTF-8として不正なファイル名(結果が出る前の初期ファイル数カウントを中断させていた)、読み取り不可のサブディレクトリやスキャン中に削除されたファイル、書き込み不可の
--outputパスは、パニックの代わりにクリーンで実行可能なエラーを表示するようになった。ウォークのエラーは報告され、出力エラーはCannot write to output file …を表示して非ゼロで終了する。aws-ct-timeline・azure-timeline・aws-ct-search・aws-ct-metrics・aws-ct-summaryに適用される。 また、UTF-8 として不正なファイル名は(実パスをスキャン処理全体で保持することで)カウントだけされてスキップされるのではなく実際に読み込まれるようになり、aws-ct-summaryの JSON/JSONL 出力でも書き込み不可の--outputパスをクリーンに報告するようにした。 (#149) (@YamatoSecurity) -
aws-ct-timeline・aws-ct-metrics・aws-ct-search・aws-ct-summaryコマンドが JSONL 入力(1行に1つの CloudTrail イベント、または{ "Records": [...] }バッチ)を無言で読み飛ばしていた問題を修正した。パーサーはファイル全体を単一の JSON として読み込み、失敗するとイベントを1件も返していなかった。行単位の JSONL 解析にフォールバックするようにし、.jsonl拡張子のファイルも認識・読み込みできるようにした。 (#139) (@YamatoSecurity) -
-T, --no-frequency-timelineオプションが機能していなかったため削除した。また、作者表示のロジックバグを修正した。 (#110) (@fukusuket) -
結果がなくても出力ファイルは保存されていた。 (#114) (@fukusuket)
-
aws-ct-summaryは、破損または不完全なログファイルを処理する際にパニックを起こしていた。 (#119) (@fukusuket) -
--geo-ipが起動時にinvalid IP address syntaxでパニックを起こしていた。プライベートIP判定に使っていた省略形のCIDR文字列(10/8、172.16/12、2000::/3など)がcidrクレートで受け付けられなくなったことが原因である。cidr-utilsへの依存をやめ、IPv4は標準ライブラリのIpv4Addr::is_private()で、IPv6は手動のプレフィックス判定でプライベート範囲を確認するようにした。あわせて、それまで使われていなかったGeoIPの国・都市キャッシュを利用するようにした。 (#132) (@fukusuket)