setodaNote

忘れる用のメモ書き

サイバー脅威情報集約システム EXIST を構築する

EXIST(EXternal Information aggregation System against cyber Threat)は NICT の NICTER 解析チームが開発しているサイバー脅威情報集約システムです。 外部の公開サイトのフィード情報や Twitte API などを通じて脅威情報を一箇所に収集し横断的に検索することができます。 連携システムとして MISP が標準で挙げられているので、ここでは EXIST+MISP 環境を構築していきます。

サイバー脅威情報集約システム EXIST とは

EXIST(EXternal Information aggregation System against cyber Threat)は NICT の NICTER 解析チームが開発しているサイバー脅威情報集約システムです。オープンソースソフトウェアとして公開されています。

blog.nicter.jp

EXISTは,サイバー脅威情報を集約し,様々な情報源を横断的に検索することができるWebアプリケーションです. 様々な情報源からサイバー脅威情報をフィードやAPI経由で取得し,EXIST上のデータベースに集約します. 利用者はWebUIもしくはWebAPIで,サイバー脅威情報を特定のキーワードで横断的に検索することが出来ます.

EXIST with MISP 自動インストールスクリプト

CentOS 7 に対して EXIST を自動でインストールするスクリプトを作ってみました。 連携システムとして MISP が標準で挙げられているので、このスクリプトも EXIST+MISP 環境を自動で構築するようにしています。 各種APIとの連携設定を自動適用するスクリプトもあわせて公開しているので、 手っ取り早く構築したい場合はこちらをご利用ください。

github.com

CentOS 7 のインストール

EXIST を導入していきます。ここでは新規にインストールした CentOS 上に構築することにします。環境は以下の通りです。

  • VMware(R) Workstation 15 Pro
  • CentOS 7

作業が簡単になるので CentOS は GUI 付きのサーバを選択してインストールしています。

自動更新を無効にする

OS のインストールが完了し起動したら、 yum コマンドを打ち込む際にバッティングしてしまう場合があるので、 自動更新を無効化にしておきます。

# サービスの停止、無効化
sudo systemctl stop packagekit
sudo systemctl mask packagekit
 
# サービスの削除(必要なら)
#sudo yum remove PackageKit* -y

# 再起動
sudo reboot

その他の設定

インストール中に画面がロックされると煩わしいので、画面のロックを無効化しておきます。

[アプリケーション]>[システムツール]>[設定]>[プライバシー]>[画面ロック]:オフ
[アプリケーション]>[システムツール]>[設定]>[電源管理]>[ブランクスクリーン]:しない

MISP のインストール

MISP はインストールスクリプトを使ってインストールします。 インストールスクリプトの都合上、EXIST より先に MISP を導入しておきます。 スクリプトは以下のリンクにあるものをベースに少し修正したものを使います。

厳密に確認はしていませんが、記述内容からすると公式githubに掲載されている手順をうまく自動化できるように組み上げたもののようです。

作業前提

ここから先の作業はすべて root 前提ですすめます。

sudo su -

MISP インストールスクリプトの取得

wget https://raw.githubusercontent.com/vodkappa/misp-install-centos-7/master/misp.install.sh
wget https://raw.githubusercontent.com/vodkappa/misp-install-centos-7/master/misp.variables.sh
 

MISP インストールスクリプトの修正

そのままではうまく動かないので、一部記載を修正します。

# php のバージョン修正(rh-php56 -> rh-php72)など
sed -i -e "s/rh-php56/rh-php72/g" misp.variables.sh
sed -i -e "s/rh-php56/rh-php72/g" -e "s/sudo yum install python-importlib/##sudo yum install python-importlib/g" misp.install.sh

# DB の文字コードを UTF-8 に変更します
# これをしておかないと EXIST で日本語が文字化けします。
CHANGE_DB_CHARSET_TO_UTF8='sed -i -e "$(grep \\\\[mysqld\\\\] -n /etc/my.cnf.d/server.cnf | cut -d : -f 1)a character-set-server=utf8" /etc/my.cnf.d/server.cnf'
sed -i -e "$(grep 'systemctl enable mariadb.service' -n misp.install.sh | cut -d : -f 1)i $CHANGE_DB_CHARSET_TO_UTF8" misp.install.sh
 

作業の簡易化のために問い合わせ応答部分を自動化しておきます。

# DB のセキュリティ設定(mysql_secure_installation)の問い合わせ部分を自動化する記述です。
sed -i -e "s/mysql_secure_installation/yum install expect -y\nexpect -c \"\nset timeout 5\nspawn mysql_secure_installation\nexpect \\\\\"Enter current password for root\\\\\"\nsend \\\\\"\\\n\\\\\"\nexpect \\\\\"Set root password\\\\\"\nsend \\\\\"y\\\n\\\\\"\nexpect \\\\\"New password\\\\\"\nsend \\\\\"\${DBPASSWORD_ADMIN}\\\n\\\\\"\nexpect \\\\\"Re-enter new password\\\\\"\nsend \\\\\"\${DBPASSWORD_ADMIN}\\\n\\\\\"\nexpect \\\\\"Remove anonymous users\\\\\"\nsend \\\\\"y\\\n\\\\\"\nexpect \\\\\"Disallow root login remotely\\\\\"\nsend \\\\\"y\\\n\\\\\"\nexpect \\\\\"Remove test database and access to it\\\\\"\nsend \\\\\"y\\\n\\\\\"\nexpect \\\\\"Reload privilege tables now\\\\\"\nsend \\\\\"y\\\n\\\\\"\nexpect \\'$\\\\\"\nexit 0\n\"/g" misp.install.sh
 

タイムゾーン設定を日本に合わせて修正しておきます。

sed -i -e "s/Europe\/London/Asia\/Tokyo/g" misp.install.sh

MISP インストール用の環境設定

先程ダウンロードしておいたスクリプトを使って、MISP のインストールに必要な環境変数一式を設定します。

. misp.variables.sh
# 実行結果(例)
# . misp.variables.sh
Admin (root) DB Password: 807d0fbcae7c4b20518d4d85664f6820aafdf936104122c5073e7744c46c4b87
User  (misp) DB Password: 2998b3232d29e8dc5a78d97a32ce83f556f3ed31b057077503df05641dd79158

実行した結果として DB Admin のパスワードが表示されます。 上の値は例示であり、実際の値はランダムに生成されます。 後で必要になるので必ずメモしておきます。

MISP インストールスクリプトの実行

作業に先立って、インストール作業の効率化のために以下のコマンドを入力し yum の取得先を比較的速度の早い国内サイトに限っておきます。

echo include_only=.jp >> /etc/yum/pluginconf.d/fastestmirror.conf

次にMISP のインストールスクリプトを実行します。 スクリプトには「yum update -y」が含まれているので OS も最新化されます。

bash misp.install.sh

通信環境などにもよりますが、概ね20分程度かかります。 以下のような表示になればインストールは完了です。

2019-10-20 16:23:52 Notice: Array to string conversion in [/var/www/MISP/app/Model/Log.php, line 205]
Setting "Session.cookie_timeout" changed to 3600

[root@localhost ~]# 

最後にログのローテートに失敗するのを防ぐための設定をしておきます。

sed -i -e "s/}/    su apache apache\n}/g" /etc/logrotate.d/misp

これで MISP の導入は完了です。

MISP へのログイン

CentOS 上でブラウザを開き、以下のURLにアクセスすると MISP ログイン画面が表示されます。

http://localhost/users/login/
もしくは
http://{Your_VirtualMachine_IP_address}/user/login/

f:id:soji256:20191021002715p:plain
MISP login

MSIP のデフォルトユーザ名とパスワードは以下の通りです。

Username: admin@admin.test
Password: admin

ログイン直後にパスワードの変更が求められます。 それが完了すると Admin ユーザのプロフィールページに遷移します。 左上にある「Home」をクリックすると MISP のダッシュボードが表示できます。

f:id:soji256:20191021003113p:plain
MISP Main page

EXIST のインストール

作業前提

ここから先の作業はすべて root 前提ですすめます。

sudo su -

EXIST に必要な Python3.x 環境を導入

Python 3.x 系をインストールします。

yum install -y https://centos7.iuscommunity.org/ius-release.rpm
yum install python3 python3-libs python3-devel python3-pip -y

EXIST のための Python 仮想環境を作成

EXIST の git をクローンしつつ、Python の仮想環境を作っておきます。 仮想環境にしておくと、後々のパッケージ名に関連するエラーの発生を防ぐ事ができます。

cd /opt
git clone https://github.com/nict-csl/exist.git

cd /opt/exist
python3 -m venv venv-exist
source venv-exist/bin/activate

プロンプトが以下のような表示になっていれば OK です。 以降は仮想環境上での作業を前提としています。

(venv-exist) [root@localhost exist]# 

なお、この仮想環境から抜けるには deactivate と入力します。

EXIST に必要な各種パッケージをインストール

pip install -r requirements.txt

EXIST 用の DB を作成

EXIST に必要な DB とユーザを作成します。 なお DB へのログインには MISP 導入時に設定しているパスワードが必要となります。

mysql -u root -p

MariaDB [(none)]> create database intelligence_db;
MariaDB [(none)]> create user 'exist'@'localhost' identified by 'password';
MariaDB [(none)]> grant ALL on intelligence_db.* to exist;
MariaDB [(none)]> quit

ユーザ名「exist」とパスワード「password」は例示なので、必要に応じて適切なものに変更してください。

次に EXIST の DB 周りの設定ファイルを修正します。

cp intelligence/settings.py.template intelligence/settings.py

修正する項目は以下の通りです。

  • ALLOWED_HOSTS:localhost と EXIST が動いているマシンの IP を追加します
  • USER:DB に作ったユーザ名に修正します
  • PASSWORD:DB に作ったユーザのパスワードに修正します
  • HOST:localhost と入力します
vim intelligence/settings.py
ALLOWED_HOSTS = [
     'localhost',   # add
     'EXIST_Server_IP',   # add (en33)
#    '192.168.56.101',
]

(snip)

DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.mysql',
        'NAME': 'intelligence_db',
        'USER': 'exist',   # modify
        'PASSWORD': 'password',   # modify
        'HOST': 'localhost', # modify
        'PORT': '',
        'OPTIONS': {
            'charset': 'utf8mb4',
            'init_command': 'SET character_set_connection=utf8mb4;'
                            'SET collation_connection=utf8mb4_unicode_ci;'
                            "SET NAMES 'utf8mb4';"
                            "SET CHARACTER SET utf8mb4;"
        },
    }
}

Django マイグレーションをかけます。

python3 manage.py makemigrations exploit reputation threat threat_hunter twitter twitter_hunter
python3 manage.py migrate

結果がすべて緑の OK になっていれば問題有りません。

Redis の確認

Redis はすでに導入済みになっているはずなので確認だけします。 グリーンランプがついていれば OK です。

systemctl status redis

Celery の設定

sysconfig 配下に Celery の設定ファイルを新規に作成します。

vim /etc/sysconfig/celery
# Name of nodes to start
# here we have a single node
CELERYD_NODES="localhost"
# or we could have three nodes:
#CELERYD_NODES="w1 w2 w3"

# Absolute or relative path to the 'celery' command:
CELERY_BIN="/opt/exist/venv-exist/bin/celery"

# App instance to use
# comment out this line if you don't use an app
CELERY_APP="intelligence"
# or fully qualified:
#CELERY_APP="proj.tasks:app"

# How to call manage.py
CELERYD_MULTI="multi"

# Extra command-line arguments to the worker
CELERYD_OPTS="--time-limit=300 --concurrency=8"

# - %n will be replaced with the first part of the nodename.
# - %I will be replaced with the current child process index
# and is important when using the prefork pool to avoid race conditions.
CELERYD_PID_FILE="/var/run/celery/%n.pid"
CELERYD_LOG_FILE="/var/log/celery/%n%I.log"
CELERYD_LOG_LEVEL="INFO"

Celery をサービスとして登録します。 修正箇所は以下の通りです。

  • User:root
  • Group:root
  • WorkingDirectory:/opt/exist (EXIST へのパス)
vim /etc/systemd/system/celery.service
[Unit]
Description=Celery Service
After=network.target

[Service]
Type=forking
User=root
Group=root
EnvironmentFile=/etc/sysconfig/celery
WorkingDirectory=/opt/exist
ExecStart=/bin/sh -c '${CELERY_BIN} multi start ${CELERYD_NODES} \
-A ${CELERY_APP} --pidfile=${CELERYD_PID_FILE} \
--logfile=${CELERYD_LOG_FILE} --loglevel=${CELERYD_LOG_LEVEL} ${CELERYD_OPTS}'
ExecStop=/bin/sh -c '${CELERY_BIN} multi stopwait ${CELERYD_NODES} \
--pidfile=${CELERYD_PID_FILE}'
ExecReload=/bin/sh -c '${CELERY_BIN} multi restart ${CELERYD_NODES} \
-A ${CELERY_APP} --pidfile=${CELERYD_PID_FILE} \
--logfile=${CELERYD_LOG_FILE} --loglevel=${CELERYD_LOG_LEVEL} ${CELERYD_OPTS}'

[Install]
WantedBy=multi-user.target

Celery ログファイル用の設定

Celery のログファイル用にディレクトリを作成します。

mkdir /var/log/celery; chown root:root /var/log/celery
mkdir /var/run/celery; chown root:root /var/run/celery

設定ファイルを新規に作成します。

vim /etc/tmpfiles.d/exist.conf
#Type  Path               Mode  UID        GID         Age  Argument
d      /var/run/celery    0755  root  root  -

Celery サービスを起動

systemctl start celery.service
systemctl enable celery.service

Firewall を設定

EXIST で 8000 番ポートを利用するので開けておきます。

firewall-cmd --zone=public --add-port=8000/tcp --permanent
firewall-cmd --reload

ツイート表示画面にツイートへの直接リンクを追加

EXIST で収集したツイートについて、日時表記部分がツイートへのリンクになるように改修しておきます。これがあったほうが便利です。

cd /opt/exist
sed -i -e "s/{{ tw.datetime }}/\<a href=\"https:\/\/twitter.com\/{{ tw.screen_name }}\/status\/{{ tw.id }}\"\>{{ tw.datetime }}\<\/a\>/g" apps/twitter/templates/twitter/index.html
sed -i -e "s/{{ tw.datetime }}/\<a href=\"https:\/\/twitter.com\/{{ tw.screen_name }}\/status\/{{ tw.id }}\"\>{{ tw.datetime }}\<\/a\>/g" apps/dashboard/templates/dashboard/index.html
sed -i -e "s/{{ tw.datetime }}/\<a href=\"https:\/\/twitter.com\/{{ tw.screen_name }}\/status\/{{ tw.id }}\"\>{{ tw.datetime }}\<\/a\>/g" apps/dashboard/templates/dashboard/crosslist.html
sed -i -e "s/{{ tw.datetime }}/\<a href=\"https:\/\/twitter.com\/{{ tw.screen_name }}\/status\/{{ tw.id }}\"\>{{ tw.datetime }}\<\/a\>/g" apps/twitter_hunter/templates/twitter_hunter/tweets.html
 

ページスクリーンショット取得用の設定

EXIST で URL を検索した際にページのスクリーンショットがうまく表示されるように必要なパッケージの導入と設定を行います。

yum install wkhtmltopdf xorg-x11-server-Xvfb -y
cp scripts/url/url.conf.template scripts/url/url.conf
sed -i -e "s/path\/to\/your\/exist/opt\/exist/g" scripts/url/url.conf
sed -i -e "s/YOUR_DB_USER/exist/g" -e "s/YOUR_DB_PASSWORD/${DBPASSWORD_EXIST}/g" -e "s/YOUR_DB/intelligence_db/g" scripts/url/url.conf
 

EXIST の起動設定

EXIST サービスの作成

EXIST をサービスとして登録します。 Celery サービスの起動後でないと正常に動作しないため、起動時の条件に記載しています。

vim /etc/systemd/system/exist.service
[Unit]
Description = EXIST
After = celery.service

[Service]
WorkingDirectory=/opt/exist
ExecStart=/opt/exist/venv-exist/bin/python3 manage.py runserver 0.0.0.0:8000
Restart=always
Type=simple
KillMode=control-group
Restart=on-failure

[Install]
WantedBy=multi-user.target

EXIST サービスを起動

systemctl start exist.service
systemctl enable exist.service

これで EXIST の導入は完了です。

EXIST へのアクセス

ブラウザから以下にアクセスすると EXIST の画面が表示されます。

http://localhost:8000/

f:id:soji256:20191021012544p:plain
EXIST のメインページ

EXIST の活用について

EXIST は各種 API などを設定することでフルに情報収集プラットフォームとして機能します。 概ねは APIkey を設定することで動き出しますが、Twitter については Twitter API の挙動を理解していないとうまく情報収集ができません。

これらについては別途、記事にしますが、現時点で調べた内容をまとめた記事として投稿しています。 各種APIとの連携設定を自動適用するスクリプトもあわせて公開しているのご参考になれば幸いです。

soji256.hatenablog.jp

なお、API周りの設定については以下の記事がとても参考になりました。

トラブルシューティング

scripts/threat/insert2db.py 実行時のエラー

# python3 scripts/insert2db/threat/insert2db.py
Traceback (most recent call last):
  File "scripts/insert2db/threat/insert2db.py", line 307, in <module>
    json_dict = fetchMispJson()
  File "scripts/insert2db/threat/insert2db.py", line 63, in fetchMispJson
    if res.status_code == 200:
UnboundLocalError: local variable 'res' referenced before assignment

tail でログを確認します。

tail scripts/insert2db/threat/logs/insert2db.log

コンフィグ(scripts/insert2db/conf/insert2db.conf)の MISP の URL 設定(MISP_URL)の最後に "/" がついていなかったために接続に失敗していました。

取得ツイートの日本語が文字化けする

日本語が「????????????????????????????」などと表示されてしまう場合がありました。 DB の文字コード設定が latin1 になっていたことが直接の原因でした。 通常の EXIST 導入では発生しませんが、MISP を先にインストールしたことによる副作用で発生していました。

以下はそうなった場合の対処です。

まず DB の設定を変更(追記)します。

sudo vim /etc/my.cnf.d/server.cnf
[mysqld]
character-set-server=utf8

設定反映のために DB を再起動します。

sudo systemctl restart mariadb

これで次からは UTF-8 で DB が生成されますが、 一度作ってしまったDBについては個別に文字コードの変更が必要です。

MariaDB [intelligence_db]> alter database intelligence_db character set utf8;

さらにテーブルのカラムを UTF-8 に修正します。

MariaDB [intelligence_db]> alter table テーブル名 default charset=utf8;

参考サイト:

Running setup.py install for uWSGI ... error

以下をインストールするとうまくいくかも。

sudo yum groupinstall "Development Tools"

参考サイト:

DB のセキュリティ設定(mysql_secure_installation) で「Switch to unix_socket authentication」と聞かれる

以下を参考に yes か no を決めて設定を進めます。

EXIST のページスクリーンショットがうまく取得できない

EXIST の画面上は画像ファイルが壊れたときのアイコン表示となっており、 ログは以下のようになっていました。

[ERROR]    2019-10-17 00:00:00,000    module:views    /opt/exist/apps/url/views.py    message:No wkhtmltoimage executable found: "b''"
If this file exists please check that this process can read it. Otherwise please install wkhtmltopdf - http://wkhtmltopdf.org

wkhtmltopdf をインストールするなどすると解消されました。

yum install wkhtmltopdf xorg-x11-server-Xvfb -y
cp scripts/url/url.conf.template scripts/url/url.conf
sed -i -e "s/path\/to\/your\/exist/opt\/exist/g" scripts/url/url.conf

ImportError:configparserというモジュールはありません

MISP を導入済みの状態で EXIST を導入しようとした際に発生したエラーです。 直接の原因は大文字小文字「Configparser」と「configparser」で見つかる見つからないというもののようです。 Python2 環境と Python3 環境が絡み合っているのか、どうにも解消できませんでしたが、 Python の仮想環境で EXIST を導入&実行することで解消できました。

参考サイト:

MISP のタイムゾーンがヨーロッパになっている(時刻表示がずれる)

MISP のインストールスクリプトでタイムゾーン設定がヨーロッパになっているため、 修正せずに利用すると時刻表示がずれてしまいます。

vim /etc/opt/rh/rh-php56/php-fpm.d/timezone.ini
date.timezone = "Asia/Tokyo"

変更を適用します。

systemctl restart rh-php72-php-fpm.service

EXIST にアクセスすると DisallowedHost と表示される

設定ファイル /opt/exist/intelligence/settings.py に EXIST が稼働しているサーバのIPアドレスが登録されていない場合に表示されます。 以下のようにIPアドレス(192.168.xxx.xxx の箇所)を設定すると解消されます。

ALLOWED_HOSTS = [
     'localhost',
     '192.168.xxx.xxx',   # <- Add the EXSIT Server IP adress
#    '192.168.56.101',
]

各種検索時に VirusTotal の情報が取得できていない

VirusTotal に関連するコンフィグファイルの読み込みに失敗している可能性があります。

  • コンフィグファイル(conf/vt.conf)の設定が正しいか
  • ログ(logs/django.log)を確認して message:No section: 'vt' といったエラーが出力されていないか
    • エラーが出ている場合は下記の「message:No section: というエラーが出る」をご参照ください

message:No section: というエラーが出る

ログ(logs/django.log)に message:No section: 'vt'message:No section: 'geoip' といったエラーが出る場合、 EXIST の実行スクリプト(manage.py)の作業ディレクトリが EXIST のインストール先と異なっている可能性があります。

EXIST が利用している lib/vt.py などにおいてコンフィグファイルへのパスが相対パスで記述されており、 作業ディレクトリが EXIST のインストール先であることを前提しているため、 それが異なっている場合はうまく動作しません。

カレントディレクトリを適切なものにしてから manage.py を実行するか、 サービスとして起動している場合は以下のように WorkingDirectory を指定すると解消されます。

vim /etc/systemd/system/exist.service
[Service]
WorkingDirectory=/opt/exist

変更を適用するためにはサービスの再起動が必要です。

systemctl daemon-reload
systemctl restart exist.service

参考文献

EXIST 関連

公式

構築周り

各種API

MISP 関連

公式

  • MISP/MISP: MISP (core software) - Open Source Threat Intelligence and Sharing Platform (formely known as Malware Information Sharing Platform)
    https://github.com/MISP/MISP

構築周り

MISP フィード設定

EXIST・MISP 共通

cron 設定周り

service 設定周り

DB 設定周り

その他

自動化スクリプト作成周り

更新履歴

  • 2019/10/23 新規作成
  • 2019/10/23 スクリプトの細かな不具合を修正。その他、一部表記を加筆修正。
  • 2019/10/24 トラブルシューティングに「EXIST にアクセスすると DisallowedHost と表示される」を追加。
  • 2019/10/24 自動化スクリプトについて追記・修正。
  • 2019/10/24 EXIST サービス設定に誤り(WorkingDirectory を設定していないと VirusTotal などの情報取得に失敗する)があったのを修正。トラブルシューティングと参考文献に関連を追記。
  • 2019/10/26 ログのローテートの設定と関連する参考文献を追加。
  • 2019/11/12 ツイートへの直リンク生成部分を一部追加。