消えやすいものから集めて、封をする
目標
RFC 3227の揮発性の順序で証拠を集める計画をファイルに固め、その順序のとおりにたどる収集ツールを作ります。収集時刻・収集者・コマンド・sha256をマニフェストに書き、マニフェストを封印したうえで、封印が壊れていないかを検証する道具を作り、顧客の個人情報が入った項目に線を引きます。
なぜ重要なのか
現場に入ると、手が真っ先にログへ向かいます。ログをダウンロードする10分の間に、プロセス一覧が変わり、接続が閉じ、一時ファイルが消えます。ログは明日もその場所にありますが、それらは今しかなく、消えたあとは「そのとき、そのプロセスは動いていましたか」という質問に答えられません。 RFC 3227の2.1節は、揮発性の高いものから低いものへ進めと書き、7つのグループの順序の例を示しています。この順序が与えるのは優先順位だけではなく、判断の根拠です。なぜそれを先に取ったのかと聞かれたとき、勘ではなく等級で答えられます。 集めるだけでは足りません。数週間後に「このファイルは、あの日あのサーバーから出たもので間違いないのですか」という質問が来ます。項目ごとに、いつ・誰が・何で集めたかと内容のフィンガープリントを書き、マニフェスト自体も封印します。ファイルのハッシュだけを書いておくと、マニフェストを書き換えてしまえば終わりだからです。 逆に、すべて集めようとすると、顧客の個人情報をまるごと持ち出すことになります。線を引きますが、黙って外さず、外したという事実と理由を記録に残します。 採点ツールは、提出された文言を信じません。自分の計画と自分の範囲ファイルで、作成した収集ツールを実際に動かして、順序と時刻とハッシュを照合し、できあがったバンドルのファイルを1つこっそり書き換えて、検証ツールがその名前を挙げるかを確かめます。
ステップ
- /root/evidence/gen_scene.pyを作成して実行し、hostディレクトリを作成してください(保存先: /root/evidence/host/)。設定2つ、ログ2つ、中央ログのコピー1つ、一時ファイル6つ、本番DB(customer 120行・charge 360行)が入ります。
- /root/evidence/plan.jsonに、項目8つを揮発性が高いものから順に書いてください。項目ごとにid・volatility_class・command・whyを書きます。
- /root/evidence/collect.pyを作成し、計画の順序のとおりに集めて、マニフェストを残させてください。
- 項目ごとにsha256とバイト数を、マニフェストには収集者を書かせてください。
--collectorを追加します。 - マニフェスト自体を封印して、MANIFEST.sha256を残させてください。
- /root/evidence/verify.pyを作成し、封印が壊れていないかを検証して、壊れたファイルの名前を出力させてください。
- /root/evidence/scope.jsonに、集めないと決めた項目と理由を書き、
--scopeを追加して、除外した項目が記録には残るようにしてください。 - 本物のバンドルを作成して検証し(保存先: /root/evidence/bundle/)、/root/evidence/evidence_report.mdに4つの節で書いてください。
参考
- 収集する項目8つは次のとおりです。
| id | 内容 | 読み取り元 |
|---|---|---|
proc_table |
現在動いているプロセスの一覧 | このPodのプロセステーブル |
net_state |
ルーティングテーブルと開いている接続 | /proc/net/route, /proc/net/tcp |
kernel_stats |
カーネル統計とメモリ | /proc/stat, /proc/meminfo |
tmp_files |
一時ファイルシステムに残ったもの | host/tmp |
app_logs |
アプリケーションログ | host/var/log |
app_db |
本番データベース | host/var/lib/app.db |
remote_logs |
中央ログサーバーから受け取ったコピー | host/remote |
host_config |
設定と物理構成 | host/etc |
- 実行契約:
python3 /root/evidence/collect.py --plan <계획> --out <묶음 디렉터리> [--collector <이름>] [--scope <범위 파일>](プレースホルダーは計画、バンドルのディレクトリ、収集者の名前、範囲ファイルです)は、1行の要約を標準出力に出して、終了コード0で終わります。計画や範囲ファイルを読み込めない場合は3です。 - 検証契約:
python3 /root/evidence/verify.py --bundle <묶음 디렉터리>(プレースホルダーはバンドルのディレクトリです)は、無事なら0、壊れていれば0以外の値で終わり、壊れたファイルの名前が入った行を標準出力に出します。 - 計画ファイル:
{"items": [{"id": …, "volatility_class": 1..7, "command": "셸 명령", "why": "왜 이 자리인가"}]}(プレースホルダーは順に、シェルコマンドと、なぜこの位置なのかという理由です)。項目の順序がそのまま収集の順序です。収集ツールは並べ替えません。 - マニフェスト:
<묶음>/manifest.jsonに{"created_at", "collector", "items": [...]}を書きます(プレースホルダーはバンドルのディレクトリです)。項目ごとにid・order(1から)・volatility_class・command・collected_at・statusを書き、集めた項目にはfile・exit_code・sha256・bytesを、除外した項目にはreasonを書きます。 - 収集物のファイル名は
<id>.txtで、コマンドの標準出力をそのまま入れます。 - 封印:
<묶음>/MANIFEST.sha256に<manifest.json 의 sha256> manifest.jsonを1行書きます(プレースホルダーはバンドルのディレクトリとmanifest.jsonのsha256で、コード内の韓国語は「の」を意味する助詞です)。 - 範囲ファイル:
{"policy": …, "excluded": [{"id": …, "reason": …}]}。除外した項目はファイルを作らず、マニフェストにstatusexcludedとreasonで残します。 - 時刻はRFC 3339のUTC表記で書きます。項目ごとに別々に書いてこそ、順序が証明されます。
- よくあるミス: 計画を等級で並べ替え直すこと(判断がコードの中に隠れます)、バンドル全体に時刻を1つだけ書くこと、ファイルのハッシュだけを書いてマニフェストを封印しないこと、除外した項目を黙って外すこと。
- 項目8つの構成と、
app_dbを除外の対象にすることは、このラボの前提です。RFC 3227はグループの順序を決めるだけで、どの項目を外すかは決めていません。 - 参考ドキュメント: RFC 3227の2.1節が揮発性の順序を、2.2節が避けるべきことを書いています。NIST SP 800-86が同じテーマをより広く扱い、Python hashlibドキュメントとRFC 3339がこのラボで使う道具です。
現場を手に入れる
/root/evidence/gen_scene.pyを作成して実行し、hostディレクトリを作成してください(保存先: /root/evidence/host/)。etcに2つ、var/logに2つ、remoteに1つ、tmpに6つ、var/lib/app.db(customer 120行・charge 360行)が入ります。
ラボのPodで本物の顧客サーバーを触ることはできないので、ディスク側だけを作っておきます。揮発性が高いものは、まねる必要がありません。このPodの/procが本物だからです。作成したら、treeで一度ざっと見てください。
揮発性の順序で計画を立てる
/root/evidence/plan.jsonに、項目8つを揮発性が高いものから順に書いてください。項目ごとに、id、RFC 3227のvolatility_class(1..7)、実際に実行するcommand、なぜその位置なのかを書いたwhyを入れます。
RFC 3227の2.1節の7つのグループを、そのまま等級として使います。プロセステーブルとカーネル統計とルーティングテーブルが同じグループで、一時ファイルシステムがその次、ディスクがその次、リモートのログ資料がその次、物理構成がその次です。commandは、実際に実行して何かを出力する必要があります。
計画どおりに集める
/root/evidence/collect.pyを作成し、計画の順序をそのまま使って集め、<묶음>/manifest.jsonを残させてください(プレースホルダーはバンドルのディレクトリです)。項目ごとにid・order・volatility_class・command・collected_at・file・exit_code・statusを書きます。
計画を等級で並べ替え直さないでください。並べ替えた瞬間、判断がコードの中に隠れます。コマンドはbashで実行し、標準出力を<id>.txtにそのまま入れます。時刻は項目ごとに別々に書いてこそ、順序が証明されます。
いつ・誰が・何でを書く
--collector <이름>を追加し(プレースホルダーは収集者の名前です)、集めた項目ごとにsha256とbytesを、マニフェストにはcollectorを書かせてください。
数週間後に「このファイルは、あの日あのサーバーから出たもので間違いないのですか」という質問が来ます。そのときに答えられるのは、収集時刻と収集者と実際に実行したコマンド、そして内容のフィンガープリントです。sha256はファイルをまるごと読み込んで計算してください。
マニフェストも封印する
マニフェストを書き終えたら、そのファイルのsha256を計算して、<묶음>/MANIFEST.sha256に<해시> manifest.jsonの1行で残させてください(プレースホルダーはバンドルのディレクトリとハッシュです)。
ファイルのハッシュをマニフェストに書いておくだけでは、マニフェストを書き換えてしまえば終わりです。封印は改ざんを防げませんが、知らないうちに変わることを見つけ出します。エディターで開いて保存した場合やコピー中に切れた場合が、実際にはずっと多く起きます。
壊れたものの名前を挙げる
/root/evidence/verify.pyを作成してください。--bundle <묶음>(プレースホルダーはバンドルのディレクトリです)で封印とファイルのハッシュを照合し、無事なら0、壊れていれば0以外の値で終わり、壊れたファイルの名前が入った行を標準出力に出してください。
「封印が壊れました」としか出力しない検証ツールは、その場に人を立たせたままにします。何が壊れたのか名前を挙げて初めて、次の行動が生まれます。マニフェストが変わった場合と収集物のファイルが変わった場合のどちらも見つけ、除外された項目はファイルがないのが正常です。
集めないと決めたものを記録に残す
/root/evidence/scope.jsonにpolicyとexcludedを書いてapp_dbを除外し、collect.pyに--scope <파일>を追加してください(プレースホルダーはファイルのパスです)。除外した項目はファイルを作らず、マニフェストにstatus excludedとreasonで残ります。
決済サーバーの本番DBには、名前とメールアドレスと電話番号が入っています。それをダンプして持ち出した瞬間、私たちは調べる人ではなく新たなリスクになります。大切なのは、黙って外さないことです。外したという事実が記録に残ってこそ、あとで何を依頼すればよいかがわかります。
バンドルを作成して報告する
本物のバンドルを(計画と範囲の両方を渡して)作成し、verify.pyで検証してください(保存先: /root/evidence/bundle/)。そのあと、/root/evidence/evidence_report.mdに、## 무엇을 어떤 차례로 모았나、## 무엇을 모으지 않았나、## 봉인과 검증、## 남은 위험の4つの節で書いてください(韓国語の見出しは順に「何をどの順に集めたか」「何を集めなかったか」「封印と検証」「残るリスク」という意味です)。
レポートは手で書かず、マニフェストから生成してください。項目名と等級、除外した項目と理由、封印の値と検証コマンドがすべて入っていれば、次の人がそのまま再検証できます。