dfirr

A Note on R How-to in Cybersecurity Investigation

情報セキュリティ実務家のためのPowerShell超特急

まったくRの話題ではないのですが、他に適当な場所もないので、「情報セキュリティ実務家のためのPowerShell超特急」の講演資料へのリンクを置いておきます。ちなみに「超特急」というタイトルは、山本矩一郎さんの「数学超特急」という学習参考書から拝借しました。

SANS Community Night(2023-12-06)講演資料

drive.google.com

MD5ハッシュ値: 2E32876EF80D4894169A4074138CE8A8

OWASP Sendaiでの内容からJSONに関する記述を増やし、参考文献を入れ替えました。

TMCIT × 大和セキュリティ(2023-03-26)講演資料

drive.google.com

MD5ハッシュ値: 389EB388297743344E95CDF80F64DD53

PowerShellでJSONLを扱う方法を取り上げました。JSONLで与えられたWindowsイベントログ(sysmonを含む)を分析するCTFだったためです。

OWASP Sendai(2023-03-15)講演資料

drive.google.com

MD5ハッシュ値: 1F8F3854C4AE20935CE4C352B534C9D2

持ち時間が30分増えたので、内容を増やしました。パス操作(*-Path)、ファイル入出力(ConvertFrom-Csv、Out-File)、作業証跡(*-Transcript)の話題を新規に取り上げたほか、パイプラインやイベントログの解説を強化しています。

SANS Community Night(2022-12-01)講演資料

drive.google.com

MD5ハッシュ値: 25048554FC0EBDFEEED13383B9BF9E25

以下のリンクから講演の動画がご覧になれます(要アカウント登録)。 www.sans.org

contains演算子の構文糖をつくる

今回は思い付きの小ネタです。Rのstringrパッケージは一貫性が売りですが、やや冗長なところがあります。たとえば我々が行(ログの場合にはイベント)を絞り込むとき、大文字・小文字を区別せずにマッチさせたいことが多々あります。これをstringrで表現すると、次の通りです。

log_http |> 
  filter(str_detect(uri, fixed("union", ignore_case = TRUE)))

フィルターが1行だけならともかく、現場ではフィルターを重ねることが少なくないので、str_detect()が重たく感じます。もっと気軽にKQLのcontains的に使いたくて、containsに相当する演算子を作ってみました。

"%~%" <- function(string, pattern) {
  stringi::stri_detect_fixed(string, pattern, case_insensitive = TRUE)
}

こうすると、先ほどのフィルターは

log_http |> 
  filter(uri %~% "union")

と書けます。含まない場合には、

log_http |> 
  filter(!uri %~% "union")

でOK。スマホの画面にも収まる短さです。個人的に、めちゃくちゃ便利になりました。

Shift_JISのURLをデコードする

ログ分析の過程で、URLエンコーディングされたURLをデコードしたいことがあります。ところがurl_decode()関数はUTF-8にしか対応していないため、ファイル名などでShift_JISが登場したときに困ります。

そこで、下のような書き捨ての関数を作りつつ、stringrパッケージや関数のベクトル対応の要点を確認します。特に、str_replace()関数の置換文字列として関数が使用可能な点は、非常に使いでがあります。

RPubs「Shift_JISのURLをデコードする」

url_decode_sjis <- function(urls){
  url_decode_unit <- function(url){
    url |> 
      str_extract_all("(%.{2}|.)") |>
      flatten_chr() |> 
      str_remove_all(fixed("%")) |>
      str_replace_all("^(.)$", function(x) as.hexmode(utf8ToInt(x))) |> 
      as.hexmode() |> 
      as.raw() |> 
      rawToChar() |>
      stringi::stri_encode(from = "cp932", to = "utf8")
  }
  
  map_chr(urls, url_decode_unit)
}

イベントの発生を視覚化する

今回は、横軸に時刻を、縦軸にアセットをとって、イベントの発生をプロットする手法を取り上げました。最終的に下のようなグラフをつくります。こうしたプロットを一貫した文法で記述できるのがggplot2のよいところです。

RPubs「イベントの発生を視覚化する」

f:id:dfirr:20220110134423p:plain

Linuxの認証ログをパースする

今回はsyslogのパースを取り上げました。syslogは典型的な半構造データで、意外と厄介な形式です。

正面から取り組む方法として正規表現を使う方法、裏口から取り組む方法としてElastic Stackにパースさせる方法を取り上げています。前者ではR 4.0から利用可能になったraw string形式を活用するのがポイント。後者では、前回少しだけ取り上げたhoist()関数を利用します。

RPubs「Linuxの認証ログをパースする」

JSONデータを矩形化する

今回はJSONに代表される入れ子構造のリストを矩形化する方法を取り上げました。Rにおける入れ子構造の取り扱いは統計モデルが例に挙がることがほとんどなのですが、情報セキュリティ屋が対峙するケースとしては、情報提供サービスからREST APIで取得したJSONの取り扱いのほうが頻度が高いでしょう。

tidyr 1.0で導入されたunnest_wider / unnest_longer / unnest_auto / hoistの各関数が非常に強力で、ぜひ押さえておきたいものです。

RPubs「JSONデータを矩形化する」

スクリプトに認証情報を埋め込まないようにする

データレイクや各種情報サービスなどからWeb APIを使ってデータを取得することは珍しくありません。たとえばSplunkからデータを取得する方法としては、同社のブログ「Leveraging External Data Science Stacks with Splunk Today」(2019-03-18)が参考になります。

ただ、リンク先の例のような、パスワードをコードの中に平文で書き込む方法は安全ではありません。では、どうするか? 古典的には、環境変数に書いたり暗号化して別ファイルに保存することが行われます。しかしクライアント端末で実行する際には、OSが備えている鍵管理システムを利用するのが便利です。Windowsでは資格情報マネージャー(Credential Manager)が相当します。

資格情報マネージャーはどこにあるか?

資格情報マネージャーは、〈コントロールパネル/ユーザーアカウント/資格情報マネージャー〉にあります。Windows+R(ファイル名を指定して実行)でcontrol /name Microsoft.CredentialManagerと入力しても構いません。左側の「Web 資格情報」は、Internet Explorerが使用するもののようです。私たちがスクリプトで使うのは、右の「Windows 資格情報」です。

f:id:dfirr:20210130062028p:plain

以下に、Rで資格情報マネージャーを取り扱う方法を示します。登録は永続化されますから、いちど登録すれば二度と登録する必要はありません。

keyringパッケージを使う

Rのkeyringパッケージは、資格情報マネージャーだけでなく、Keychain(macOS)やSecret Service APILinux)にも対応しています。GitHubのページから付け加えることはないのですが、いちおう先のSplunkでの具体例を挙げておきます。

keyringパッケージをインストールする

KeyringはCRANに登録されているので、

install.packages("keyring")

でインストール可能です。

パスワードを登録する

keyring::key_set_with_value("splunk",
   username = "test@example.jp",
   password = "testpassword")

登録すると、資格情報マネージャーの「汎用資格情報」欄に追加されます。

f:id:dfirr:20210130062034p:plain

登録一覧を眺める

keyring::key_list()

  service        username
1  splunk test@example.jp

登録されたパスワードを取り出す

pwd <- keyring::key_get("splunk", "test@exapmle.jp")

(代入するとRStudioから丸見えになるので注意。)

登録されたパスワードを削除する

keyring::key_delete("splunk", "test@example.jp")

おまけ

Pythonの場合

keyringはPythonにもあります。私はふだんanacondaを使っているのですが、この環境では最初から入っていました。使い方はまったく同じです。

import keyring
keyring.set_password("splunk", "test@exapmle.jp", "testpassword")
pwd = keyring.get_password("splunk", "test@example.jp")

ならば形式も同じで両言語のkeyringが共用できるかというと、残念ながらうまくいきませんでした。RはUTF-8で、PythonUTF-16で登録されるようです。

Splunk Authentication Token

Splunk 7.3からは、パスワードを使った認証ではなくAuthentication Tokenを使うことができます。ただし設定で有効化する必要があるので、Splunkの管理者に相談するのがよいでしょう。

f:id:dfirr:20210130070209p:plain