はじめに

MSTICPyは、 Jupyter Notebookで情報セキュリティ調査を行うためのPythonライブラリで、 Microsoftが開発しています。

MSTICPyの機能の1つとして、さまざまなログ基盤にクエリーを投げて、結果をPandasデータフレームとして取り込むことができます。 Microsoft Sentinelに最もよく対応していますが、 Microsoft 365 Defender、Microsoft Graph API、Splunk、 SumoLogic、Azure Data Explorer、Cybereason、OSQuery、 Velociraptorと種類は多岐にわたります。

私たちR利用者も、ここに便乗できます。 Reticulateパッケージを使えば、 Pythonオブジェクトをシームレスに扱えるからです。

以下ではSplunkを例にして話を進めます。

セットアップ

MSTICPyの準備

VirtualEnvかCondaかで、Pythonの専用環境を作ります。私はMambaforgeを使っています。以前は手動でインストールしていましたが、いまはChocolateyから入れています。

Mambaforgeが入っていることを前提に、以下のコマンドで「msticpy」という名前の Conda環境を作ります。そしてアクティベーションし、pipでMSTICPyライブラリをインストールします。「all」をつけると補助パッケージが全部入りになります。

mamba create -n msticpy pip notebook ipykernel
conda activate msticpy
pip install msticpy[all]

RProjectの準備

RStudioでは、プロジェクトごとにPython実行環境を指定できます。〈Tools〉-〈Project Options…〉のPythonタブにて、先に指定した実行環境のPythonを選択します。

このとき、最初はConda環境の検索に失敗するかもしれません。その場合には、フィールドに直接パスを書き込みます。パスは、たとえばMambaforgeをC:/Users/MyName/home/mambafogeにインストールしているとすると、

~/home/mambaforge/envs/msticpy/python.exe

のようになります。

RからMSTICPyを利用する

RStudioでMSTICPyを使うには、2つの方法があります。 1つは、RMarkdownのコード片で言語をPythonに指定して、純粋にPythonコードとして書くことです。もう1つは、RからPythonオブジェクトを使うことです。

ここでは、後者の方法を選択します。このときのポイントは、 Pythonの「.」が「$」になる――という点です。

Splunkをクエリプロバイダーに指定する

以下のようにします。RStudioで指定しているので use_python()は不要ですが、私は備忘のためと、 VSCodeなどRStudio以外から利用するときに備えて記載しています。

pacman::p_load(tidyverse, reticulate)
use_python("~/home/mambaforge/envs/msticpy/python.exe")
mp <- import("msticpy")
prov_splunk <- mp$QueryProvider("Splunk")

3行目~4行目は、Pythonの次のコードに相当します。

# Python
import msticpy as mp
prov_splunk = mp.QueryProvider("Splunk")

Splunkに接続する

私は手元のPCにVMware Workstation Proを入れているので、今回の例のためにSplunkの試験環境を作りました。会社で利用しているSplunk Cloudでも同様に接続できます。

# このように書いてはいけない
prov_splunk$connect(host="splunk-photon", port="8089", username="admin", password="testpassword")

上は接続例ですが、パスワードが平文で入る上記の書き方をすべきではありません。せめてkeyringパッケージを使いましょう。

pacman::p_load(keyring)
keyring::key_set_with_value("splunk-photon",
   username = "admin",
   password = "testpassword")

として資格情報マネージャーにパスワードを登録しておけば、

prov_splunk$connect(host="splunk-photon", port="8089", 
                    username="admin", password=keyring::key_get("splunk-photon", "admin"))
## Connected.

上のようなコードで呼び出すことができます。

もっとよいのは、パスワードではなくアクセストークンを使うことです。トークン認証が有効化されているSplunk環境では、 Settings -> TokensでJWTトークン(eyJraWで始まる)を生成することができます。それを下のようにkeyringに登録しておくと、

pacman::p_load(keyring)
keyring::key_set_with_value("splunk-photon",
   username = "admin-token",
   password = "eyJraW...")

下のように呼び出すことができます。

prov_splunk$connect(host="splunk-photon", port="8089", 
                    splunkToken=keyring::key_get("splunk-photon", "admin-token"))
## Connected.

ちなみに、接続情報を設定ファイルである msticpyconfig.yamlに書いておくと、 connect()の引数は必要ありません。

クエリを実行する

ふつうはサーチ文を作るところですが、急ごしらえの環境のため、 Splunkに同梱されているサンプルデータをinputlookupで持ってくるクエリを作ります。

spl_1st <- r"(
| inputlookup security_example_data.csv 
| table timestamp threat_src_ip threat_dest_ip threat_status threat_type 
| head 5
)"

df_1st <- prov_splunk$exec_query(spl_1st, timeout=300)
df_1st
##    timestamp   threat_src_ip threat_dest_ip threat_status threat_type
## 1 1659312000 202.236.255.160 37.216.171.101      detected      origin
## 2 1659340800   55.253.83.181 141.20.243.217     mitigated      origin
## 3 1659369600   207.88.11.238 120.140.35.130     mitigated      origin
## 4 1659398400   30.29.160.213 19.109.245.203      detected      origin
## 5 1659427200   3.124.241.253 45.232.249.117     mitigated      origin

データフレームを見ると、timestampの型が文字列型になっていることに気づかされます。これは、Splunk REST APIの仕様ではないかと思います。というのは、cURLで直接書いても文字列として返ってくるからです。

$ curl -s -k -u admin:testpassword https://splunk-photon:8089/services/search/jobs/export `
-d search="| inputlookup security_example_data.csv `
| table timestamp threat_src_ip threat_dest_ip threat_status threat_type `
| head 1" `
-d output_mode=json -d exec_mode=oneshot | jq 

{
  "preview": false,
  "offset": 0,
  "result": {
    "timestamp": "1659312000",
    "threat_src_ip": "202.236.255.160",
    "threat_dest_ip": "37.216.171.101",
    "threat_status": "detected",
    "threat_type": "origin"
  }
}

型変換を行う

そういうわけで、型の変換は自前で行いましょう。ついでにデータフレームをtibbleにしています。

df_1st_mod <- 
  prov_splunk$exec_query(spl_1st, timeout=300) |> 
  mutate(
    timestamp = timestamp |> as.integer() |> as_datetime(tz = "UTC")
  ) |> 
  as_tibble()

df_1st_mod
## # A tibble: 5 × 5
##   timestamp           threat_src_ip   threat_dest_ip threat_status threat_type
##   <dttm>              <chr>           <chr>          <chr>         <chr>      
## 1 2022-08-01 00:00:00 202.236.255.160 37.216.171.101 detected      origin     
## 2 2022-08-01 08:00:00 55.253.83.181   141.20.243.217 mitigated     origin     
## 3 2022-08-01 16:00:00 207.88.11.238   120.140.35.130 mitigated     origin     
## 4 2022-08-02 00:00:00 30.29.160.213   19.109.245.203 detected      origin     
## 5 2022-08-02 08:00:00 3.124.241.253   45.232.249.117 mitigated     origin

ここまでくれば、こちらのものです。あとはTidyverseの関数群を使って、自由自在に加工できます。

おわりに

今回はMSTICPyでデータを取得することに焦点を当てましたが、嬉しいことは他にもあります。

  1. SplunkにはNotebook機能がないが、その代替になる。
  2. Splunkのログは永遠には残らないが、退避できる。
  3. SplunkだけでなくM365など別のデータソースと容易に組み合わせられる。
  4. RStudio Serverを使えば、共同で調査ができる。

私自身は、もっと可能性を追求していきたいと考えています。

最後にここまで書いたコード断片を、以下にまとめておきます。

pacman::p_load(tidyverse, reticulate)
use_python("~/home/mambaforge/envs/msticpy/python.exe")
mp <- import("msticpy")
prov_splunk <- mp$QueryProvider("Splunk")

prov_splunk$connect(host="splunk-photon", port="8089", 
                    splunkToken=keyring::key_get("splunk-photon", "admin-token"))

spl_1st <- r"(
| inputlookup security_example_data.csv 
| table timestamp threat_src_ip threat_dest_ip threat_status threat_type 
| head 5
)"

df_1st <- 
  prov_splunk$exec_query(spl_1st, timeout=300) |> 
  mutate(
    timestamp = timestamp |> as.integer() |> as_datetime(tz = "UTC")
  ) |> 
  as_tibble()

df_1st

更新履歴

  • 2023-09-29 初版
  • 2023-10-07 MSTICPyがトークン認証に対応したことを受け、訂正。