Ansibleを使ってWindows環境を構築する編 [その2]

このブログは、N高等学校とVRChatの世界からやってきた株式会社ArmorisアルバイトのShaderoが書いています。
あるもりすぶろぐの内容は個人の意見です。

弊社が提供する様々なトレーニングプログラムでは、トレーニングを行う際に、演習端末としてWindows環境を用意しています。
そこで先日、Ansibleというツールを使用して、端末の環境構築を効率的に行えるようにしました。
もっとも、実際に使いこなすには様々な試行錯誤がありました。
そこで今回は備忘も兼ねて、その際にいろいろ試したことなども含めてまとめました。

構築したい環境

前回はAnsibleを用いてホストサーバーに接続出来るように様々な準備を行いました。
今回からは以下を満たす環境を実際に作ります!

  • 以下の条件を満たした環境を準備
    • 環境要件
      • 管理者アカウントが存在するWindows環境を準備する
    • 構築内容

f:id:Armoris:20220216183747p:plain

今回はホストサーバーにトレーニング用のユーザーを作成する所までを行います!
作成したユーザーにトレーニングに必要なソフトをインストールしたり、レジストリを編集するなどは次回行います。

環境

ホストサーバー

Name Version
Windows 10 Pro 20H2
WinRM 3.0

Ansibleサーバー

Name Version
MacOS Big Sur 11.4
Ansible 2.11.6

Playbookとは

Playbookとは、YAML形式のファイルです。
そのファイルにホストサーバーに行わせたい処理を書くことで、望んだ環境にする事が出来ます。
また、Playbookがあればコマンド一つで複数のデバイスを自動的に設定することもできるため、手作業で行うよりも大幅に時間を短縮する事が出来ます。

ユーザーを作成するPlaybookを書いてみる

さっそくPlaybookを書いていきます。
ここではホストサーバーに新しいユーザーを作成するPlaybookを書きます。

下記のPlaybookを記述したymlファイルを作成します。(ファイル名として本記事ではbefore.ymlを使用)

- hosts: training          # 実行対象のホストの指定
  gather_facts: false      # 対象ホストの情報を取得して変数に格納するかどうかの設定
  vars:                    # 変数の記述
    # userNameやpasswordの値は、作成するユーザーに合わせて要変更
    userName: AnsibleTest
    password: password
  tasks:
    - name: ユーザーを作成
      win_user:
        name: '{{ userName }}'
        password: '{{ password }}'
        state: present
        groups:
          - Users
          - Remote Desktop Users

    - name: ユーザープロファイルを作成
      win_user_profile:
        username: '{{ userName }}'
        state: present

以下、上記コードの解説。

  • 1行目
    • 対象サーバーの指定。
    • 記述する際は、インベントリ内のグループ名やホスト名などを指定。
    • allも指定可能で、その場合はインベントリ内の全てのサーバーが対象になります。
  • 2行目
    • 対象ホストのOSやホストネームなどのシステム情報を収集し、変数(後述)に格納するかどうかの設定。
    • システム情報を収集するのは僅かに時間がかかるため、使わない場合はfalseにすることをお勧めします。
  • 3~6行目
    • 変数の記述。
    • 予め変数を定義したり、タスクの実行結果を変数として格納し使用することが可能。
    • 変数を使用する際は、値全体ををシングル、又はダブルクォートで囲った上で{{ (変数名) }}と記述。
  • 7行目以降
    • タスクと呼ばれる、対象サーバーに行わせたい処理を記述する箇所。
    • このPlaybookでは、ホストサーバーに新しいユーザーが作成されます。
    • ユーザーを作成するタスク以外に、ホストサーバーにファイルを転送するタスクや、レジストリを変更するタスクなど、様々な種類がありますが、各種タスクの使い方についてはAnsible公式ドキュメントを参照する事をお勧めします。

下記のコマンドで実行するとホストサーバーに新しくユーザーが作成されたのが分かります。

$ ansible-playbook before.yml

f:id:Armoris:20220215155414p:plain

f:id:Armoris:20220215155903p:plain

エラーが出る場合

AnsibleをMacOSで実行するとERROR! A worker was found in a dead stateと書かれたエラーが出る場合があります。
その場合は下記のコマンドで環境変数を登録すると正常に動く場合があります。(.bash_profileに登録するとなお良い)

$ export OBJC_DISABLE_INITIALIZE_FORK_SAFETY=YES

あとがき

今回はホストサーバーにユーザーを作成するPlaybookを作成しました。
次回は今回作成したPlaybookを改良して、作成したユーザーにソフトをインストールしたり、レジストリを編集するタスクを追加します!

本記事がお役に立てましたら幸いです。

Ansibleを使ってWindows環境を構築する編 [その1]

このブログは、N高等学校とVRChatの世界からやってきた株式会社ArmorisアルバイトのShaderoが書いています。
あるもりすぶろぐの内容は個人の意見です。

弊社が提供する様々なトレーニングプログラムでは、トレーニングを行う際に、演習端末としてWindows環境を用意しています。
そこで先日、Ansibleというツールを使用して、端末の環境構築を効率的に行えるようにしました。
もっとも、実際に使いこなすには様々な試行錯誤がありました。
そこで今回は備忘も兼ねて、その際にいろいろ試したことなども含めてまとめました。

本記事ではAnsibleを用いてホストサーバーに接続するための準備を行います。
すでにAnsibleサーバーとホストサーバー(Windows)のセットアップ及び疎通確認が完了している方は、本記事に書かれている操作を行う必要はありません。

Ansibleとは

オープンソースの構成管理ツールです。

Playbookと呼ばれるYAML形式のファイルに処理を書くことで、環境を自動的に構築することが出来ます。
また、Playbookがあればコマンド一つで複数のデバイスを自動的に設定することもできるため、手作業で行うよりも大幅に時間を短縮する事が出来ます。

環境

ホストサーバー

Name Version
Windows 10 Pro 20H2
WinRM 3.0

Ansibleサーバー

Name Version
MacOS Big Sur 11.4
Ansible 2.11.6

WinRMの設定

さっそくAnsibleのインストール!と行きたいのですが、その前にホストサーバー側でWinRMと呼ばれるWindowsを遠隔で操作する際に必要になる管理プロトコルの設定を、Ansibleが使えるように変更します。

下記のスクリプトを管理者権限で起動されたPowerShell上で実行します。

$url = "https://raw.githubusercontent.com/ansible/ansible/devel/examples/scripts/ConfigureRemotingForAnsible.ps1"
$file = "$env:temp\ConfigureRemotingForAnsible.ps1"

(New-Object -TypeName System.Net.WebClient).DownloadFile($url, $file)

powershell.exe -ExecutionPolicy ByPass -File $file

Ansibleのインストール

ここから先はAnsibleサーバー側での作業になります。

下記のコマンドを実行してAnsibleをインストールします。

$ brew install ansible

またAnsibleでWindowsを動かす場合に必要になるpywinrmもインストールします。

$ pip3 install pywinrm

hostsファイルの編集

/etc/ansible/ディレクトリ内にあるhostsファイルを下記のように編集します。
ファイル、またはディレクトリが無かったら作ってください。

[servers]
(ホストサーバーのIPアドレス)

[servers:vars]
ansible_user='(Ansibleでログインする際に使うホストサーバーのユーザー名)'
ansible_password='(Ansibleでログインする際に使うホストサーバーのユーザーのパスワード)'
ansible_port=5986
ansible_connection=winrm
ansible_winrm_server_cert_validation=ignore

接続テスト

正常にホストサーバーと接続できるかを確認するため、下記のコマンドを実行します。
エラーが出なければ成功です。

$ ansible -m win_ping (ホストサーバーのIPアドレス)

f:id:Armoris:20220201161623p:plain

あとがき

今回はAnsibleでホストサーバーに接続できるよう準備を行いました。
次回は実際にホストサーバーの環境を構築するためのPlaybookを書いていく予定です!

本記事がお役に立てましたら幸いです。

フィッシングサイトの調査をしてみた - 可視化したデータの分析

このブログは、数年前にN高等学校を卒業し株式会社Armorisにやってきたアルバイト Kaepi が書いています。

あるもりすぶろぐの内容は個人の意見です。

概要

今回はフィッシングサイトの情報について、twitterで投稿をしている2つのアカウントからデータを取得し、IPや地域情報などの様々な要素の傾向を調べるために、elasticsearch+kibanaでデータの分析と可視化をしてみました。
1. フィッシングサイトの調査をしてみた - データ収集とパース編
2. フィッシングサイトの調査をしてみた - elasticsearch+kibanaを使ってデータの可視化
3. フィッシングサイトの調査をしてみた - 可視化したデータの分析(この記事)

この記事は3回目になります。

elasticsearchとkibanaの構築を解説して、投入したデータの可視化を行いました。集計期間は 2021/11/26 から 12月22日までの約一か月です。

情報元にさせていただいたアカウント
@secbird1
@pingineer_jp

目次

  1. データからフィッシングサイトの傾向を考える
  2. フィッシングサイトの対策方法
  3. まとめ

データからフィッシングサイトの傾向を考える

傾向を考えるにあたって、今回は4つの視点から分析をしてみました。
1. 名前を騙られたブランドについて
2. 国や地域について
3. ドメインやそのレジストラ
4. 総合的な傾向

名前を騙られたブランドについて

今回情報元にしたアカウントでは日本企業のブランドを騙ったフィッシングサイトのみを発信しています。
まずは名前を騙られたブランドの傾向ですが、銀行やクレジットカード、携帯電話の大手キャリアなどが多くみられました。
これは騙された利用者から個人情報(住所や名前、クレジットカードの番号など)を盗み、他の第三者に情報を売ったりクレジットカードを不正に利用し現金化するなど、違法な手段でお金を稼ぐことが目的だからだと考えられます。

名前を騙られたサービスのリスト

国や地域について

次にドメインに割り当てられていた IP アドレスの地域情報を GeoIP のデータを用いて確認すると、約3/4がアメリカでその次に韓国となりました。

これは海外のレンタルサーバに依存した結果となるため、フィッシングサイトを作成している人の国と直接関連するものではないと思われます。

ドメインレジストラ

ドメインについて

まずフィッシングサイトのドメインの特徴について分析してみます。

フィッシングサイトのURLのリスト(discover)
まずは上から5個のドメインですが、名前などではないランダムな文字列に.cfというあまり見かけないドメインになっています。
また、6個目のdqq-amazon[.]comというドメインwww.jp.mercari.clppa[.]comなど、フィッシングサイトのドメインにブランド名の一部が含まれるケースでは、.comなどが使われています。
このように使用されているドメインに差がでるのは、フィッシングサイトを作成している攻撃者のスタンスの違いだと考えられます。
前者のようなURLを見ればすぐに偽物だとわかるフィッシングサイトは短時間で大量に作成されており、フィッシングサイト1個あたりのコストが低いと考えられます。
後者のURLにブランド名を含めたり.comドメインを使用する、見分けがつけにくいフィッシングサイトでは現時点で収集したデータを見る限り少数派で、フィッシングサイト1個あたりのコストが高いと考えられます。

レジストラについて

こちらはwhoisをして入手したレジストラの情報です。

フィッシングサイトのドメインwhoisして得たデータなので表記ゆれがあり、同じレジストラが2つ以上ある場合があります。
現在フィッシングサイトのドメインの登録で一番使われている广州云讯信息科技有限公司はこのようになっており、見たところ使用されているトップレベルドメイン.cnのみのようです。 次に多いWeb Commerce Communications Limited dba WebNic.ccでは.comドメインのみで、同じドメインを使いまわして多数のフィッシングサイトが作成されているようです。
また同じような構成のURLで複数ブランドのフィッシングサイトがあることから、同一人物またはグループが様々なブランドのフィッシングサイトを作成している可能性が大きいと思われます。

総合的な傾向

これは11/26~12/21までの25日間でブランドごとの報告数を積み上げ棒グラフにしたものです。 このグラフを見ると2つの特徴が見えるので、それについて分析します。

  1. メルカリのフィッシングサイト(グラフの緑色のバー)
  2. 三井住友カードのフィッシングサイト(グラフの青色のバー)
  3. auのフィッシングサイト(グラフの紫色のバー)
メルカリのフィッシングサイトについて

メルカリのフィッシングサイトは毎日平均20~30件ほど報告されていて、継続的に攻撃者の攻撃ターゲットとなっている可能性が考えられます。
次に地域とレジストラについて分析します。
レジストラは約半数がWeb Commerce Communications (Singapore) Pte. Ltd. (dba WebNIC)という会社であり、アジアではかなり大手のレジストラのようです。
またこの会社はAlibaba Cloudと連携していて、webホスティングサービスを提供していました。 最後にIPアドレスからフィッシングサイトをホストしているサービスを調べてみました。
12月のはじめあたりから21日までにある緑色のバーは23.95.122[.]118となっており、このIPアドレスを検索してみるとColoCrossingというサービスだということがわかりました。
こちらは先程のWebNICやAlibaba Cloudとはまた別の会社のようですが、格安でWebホスティングができるので利用されていると考えられます。
このことから、今月のメルカリを騙ったフィッシングサイトはWebNICでドメインを取得しColoCrossingでホストされていることが多いということがわかりました。

三井住友カードのフィッシングサイトについて

三井住友カードのフィッシングサイトは12/15以前では報告数が平均20件程度だったのに対し、12/15から一週間後には一日あたり平均70件と大幅に報告数が増えていました。
また、三井住友カードのフィッシングサイトの報告数が多い日はJCBのフィッシングサイトの報告数も増えており、おそらく攻撃者がクレジットカードの締日が近いタイミングを見計らってフィッシングサイトを作成しているものと思われます。

三井住友カードのフィッシングサイトの報告数
JCBのフィッシングサイトの報告数
そしてこれは三井住友カードのフィッシングサイトのIPアドレスを表示したものです。
特に目立つのは、急激に報告数が増えた12/16の204.44.75[.]4(緑色のバー)というIPアドレスで、ipinfo.ioというサービスで調べてみたところ、QuadraNet Enterprises LLCというアメリカのホスティングサービスを提供している会社だということがわかりました。

auのフィッシングサイトについて

auのフィッシングサイトは先に挙げたメルカリや三井住友カードのフィッシングサイト報告数にくらべ少ないですが、不定期で一日60~100件と比較的他のサービスよりも多く報告されています。

ブランドごとの報告数を表示した積み上げ棒グラフ
auのフィッシングサイトに絞ってdiscoverをみると、URLの構成が似ていたりIPアドレスが同じことから、同一人物またはグループが作成したフィッシングサイトであると思われます。
またこれは推測ですが、大量に作成したフィッシングサイトが様々な理由で使用できなくなったら新しくフィッシングサイトを作り直すということを繰り返しているためだと考えます。
また、このようなURLを見ればすぐにわかるフィッシングサイトを大量に作る意図としては、フィッシングサイトのURLなどがGoogle Safe Browsingに報告されることで、このようにブラウザに警告が出るようになるからだと思われます。

なので、Google Safe Browsingにフィッシングサイトとして登録されてもいいように、似た名前のドメインを登録するのではなく、使い捨てのランダムなドメインを使用しているのだと考えられます。
この場合はすぐ使えなくなってもいいように安いドメイン(.top .ga .cfなど)を使用しているようです。

フィッシング被害を防ぐには

フィッシングサイトの被害に遭わないために個人でできることは、フィッシングサイトにアクセスしない・個人情報を入力しないことです。
フィッシングサイトのURLを含む悪意のあるメール・SMSなどを見分ける基準として、
・日本語がおかしい
・個人情報の入力を求める
・緊急や重要などを強調しアクセスを急かす
などがあります。
またIDやパスワードの使いまわしをやめることで、被害を最小限に抑えることができます。

出典・参考資料: フィッシング対策ガイドライン

まとめ

「フィッシングサイトの調査をしてみた」の3回目の記事でした。
IPアドレスから位置情報を取得して地図に並べたり、関連性のありそうな項目を並べてグラフにするなど、大量のデータを集めて可視化をすると見えてくる傾向を調べて分析してみました。
データの取得編と可視化編につづく今回の分析編でこのシリーズは一旦終わりですが、数ヶ月単位でデータが集まって新しい傾向がみえたらまたブログにしようと思います。
ここまで読んでいただきありがとうございました。

フィッシングサイトの調査をしてみた - elasticsearch+kibanaを使ってデータの可視化

このブログは、数年前にN高等学校を卒業し株式会社Armorisにやってきたアルバイト Kaepi が書いています。

あるもりすぶろぐの内容は個人の意見です。

概要

今回はフィッシングサイトの情報について、twitterで投稿をしている2つのアカウントからデータを取得し、IPや地域情報などの様々な要素の傾向を調べるために、elasticsearch+kibanaでデータの分析と可視化をしてみました。
また、今回は記事を3回に分けて投稿します。
1. フィッシングサイトの調査をしてみた - データ収集とパース編
2. フィッシングサイトの調査をしてみた - elasticsearch+kibanaを使ってデータの可視化(この記事)
3. フィッシングサイトの調査をしてみた - 可視化したデータの分析

この記事は2回目になります。
・前回の内容まとめ
前回はtwitterからツイートを収集して、データの追加とパース・elasticsearchに投入するところまでをnode.jsを使って自動化しました。

情報元にさせていただいたアカウント
@secbird1
@pingineer_jp

目次

  1. elasticsearch+kibanaのインストール
  2. elasticsearchの基本操作
  3. kibanaのdashboardを使ったデータの可視化
  4. まとめ

elasticsearch+kibanaのインストール

elasticsearchのインストール

$ wget -qO - https://artifacts.elastic.co/GPG-KEY-elasticsearch | sudo apt-key add -
$ echo "deb https://artifacts.elastic.co/packages/7.x/apt stable main" | sudo tee /etc/apt/sources.list.d/elastic-7.x.list
$ sudo apt-get update && sudo apt-get install elasticsearch
$ sudo systemctl start elasticsearch

動作確認

$ curl http://localhost:9200
{
  "name" : "test",
  "cluster_name" : "elasticsearch",
  "cluster_uuid" : "███████████████",
  "version" : {
    "number" : "7.15.1",
    "build_flavor" : "default",
    "build_type" : "deb",
    "build_hash" : "83c34f456ae29d60e94d886e455e6a3409bba9ed",
    "build_date" : "2021-10-07T21:56:19.031608185Z",
    "build_snapshot" : false,
    "lucene_version" : "8.9.0",
    "minimum_wire_compatibility_version" : "6.8.0",
    "minimum_index_compatibility_version" : "6.0.0-beta1"
  },
  "tagline" : "You Know, for Search"
}

kibanaのインストール

$ sudo apt-get install kibana
$ sudo vim /etc/kibana/kibana.yml

# The default is 'localhost', which usually means remote machines will not be able to connect.
# To allow connections from remote users, set this parameter to a non-loopback address.
server.host: 0.0.0.0

$ sudo systemctl start kibana

http://ubuntuのip:5601でkibanaの画面を開くことができれば構築は完了です。 f:id:Armoris:20211105154153p:plain

elasticsearchの基本操作

Indexの作成

elasticsearchの操作はcurlなどで行うこともできますが、kibanaのDev Toolsでも同じことができます。

PUT /indexの名前?pretty

上記の内容をelasticsearchに送ることでindexが作成されます。

mappingについて

mappingとは、Indexに入るデータの構造を設定することを指します。 基本的には、名前とデータ型がセットになっていてjsonに似ています。
こちらは今回の調査で作成したindexです。

PUT /secbird1/_mapping
{
      "properties" : {
        "country" : {
          "type" : "keyword"
        },
        "created_at" : {
          "type" : "date"
        },
        "ip" : {
          "type" : "keyword"
        },
        "whois": {
          "type" : "text"
        },
        "location" : {
          "type" : "geo_point"
        },
        "name" : {
          "type" : "keyword"
        },
        "org" : {
          "type" : "keyword"
        },
        "url" : {
          "type" : "keyword"
        }
      }
}

ここで注意しないといけないことは、keywordtextの違いです。 どちらも文字列が入りますが、後述のdashboardを使ったグラフの作成にはtextは使うことができません。
また、可視化することを踏まえて座標データが入るようにするなどの工夫をしました。

kibanaのdashboardを使ったデータの可視化

左側にあるハンバーガーメニューを開くとAnalyticsの項目の下にDashboardがあります。
Dashboardを開くと右側にCreate dashboardのボタンがあり、ここからDashboardを作成することができます。

f:id:Armoris:20211119145223p:plain
作成後のDashboard
Create visualizationをクリックするとグラフを作成する画面になります。 f:id:Armoris:20211110133523p:plain グラフの作成は非常に簡単で、グラフにしたいデータをドラッグアンドドロップするだけで作成できます。 f:id:Armoris:20211119135819p:plain グラフの種類は下に表示されているサジェスト以外にも、上にあるプルダウンメニューから使用できるタイプを選ぶことができます。
f:id:Armoris:20211124123511p:plain
今回の分析で作成したグラフ
このような機能を使って、今回収集したデータを可視化してみました。
それぞれのパネルは、

  1. Table
  2. Pie
  3. Map
  4. Area stacked

というvisualizationを使用しています。
Tableには名前を騙られたサービスを報告が多い順に並べて表示しています。
地域情報(国名)と、フィッシングサイトをホストしているサービス名の比率がわかりやすいようにPie(円グラフ)を選択しました。
また、フィッシングサイトがどこでホストされているのかがわかりやすいようにMapを使って投入された座標データの地点にマークを表示させ、Heat mapで分布がわかりやすくなるようにしました。
最後のArea stackedは、取得したツイートの数を時間ごとに表示しています。

まとめ

今回はelasticsearch・kibanaの構築と前回用意したデータの可視化をしました。
自分で構築やデータの投入・可視化をするのは初めてだったので、調べながら手探りで可視化までできましたが、慣れるといろいろなデータの分析が簡単にできそうな便利なツールなので、勉強しておいて損はないと思いました。
次の記事では今回可視化したデータについて分析した結果について書くので、次回もよろしくお願いします。

フィッシングサイトの調査をしてみた - データ収集とパース編

このブログは、数年前にN高等学校を卒業し株式会社Armorisにやってきたアルバイト Kaepi が書いています。

あるもりすぶろぐの内容は個人の意見です。

概要

今回はフィッシングサイトの情報について、twitterで投稿をしている2つのアカウントからデータを取得し、IPや地域情報などの様々な要素の傾向を調べるために、elasticsearch+kibanaでデータの分析と可視化をしてみました。
また、今回は記事を3回に分けて投稿します。
1. フィッシングサイトの調査をしてみた - データ収集とパース編(この記事)
2. フィッシングサイトの調査をしてみた - elasticsearch+kibanaを使ってデータの可視化
3. フィッシングサイトの調査をしてみた - 可視化したデータの分析

情報元にさせていただいたアカウント
@secbird1
@pingineer_jp

今回の調査に必要なツイートの収集からelasticsearchへのデータ投入までを自動化するために、node.jsでプログラムを書きました。

目次

  • 全体図
  • twitterからツイートを収集する
  • ツイート収集の自動化
  • 収集したデータのパース
  • まとめ

全体図

構成はこの様になっており、twitterから取得したツイートを一旦elasticsearchに投入してから、別のプログラムでデータの追加とパースを行います。

f:id:Armoris:20211130131404p:plain

twitterからツイートを収集する

今回情報源にさせていただいたツイートを収集するために、Twitter API v2Search Tweetsという機能を使用しました。

ツイート収集の自動化

Twitter API v2で取得したツイートをelasticsearchに投入するところまでを自動化するために、node.jsでプログラムを書きました。

大まかな機能は

  1. Twitter API v2からツイートを取得する
  2. 送られてきたjsonをツイートごとに分割してelasticsearchに投入する。

となっており、これをnode-cronという指定した時間おき(今回は1時間に1回)に実行するライブラリを使って、自動でツイートを収集するようにしました。

const cron = require('node-cron')
const axiosBase = require('axios')
const twitter = axiosBase.create({
    baseURL: 'https://api.twitter.com/2/tweets/search',
    headers: {
        'Content-type': 'application/json',
        'Authorization': 'Bearer ' + bearar
    }
})

const elastic = axiosBase.create({
    headers: {
        'Content-Type': 'application/json',
        'X-Requested-With': 'XMLHttpRequest'
    }
})

async function dump(account_name) {
  let resCount = 0
  const _date = new Date()
  const date = `${_date.getFullYear()}-${_date.getMonth()+1}-${_date.getDate()}T${_date.getHours()-1}:00:00+09:00`
  twitter.get('/recent', {
    params: {
      'query': `from:${account_name}`,
      'start_time': date,
      'tweet.fields': 'created_at',
      'max_results': 100,
    }
  })
    .then(res => {
      for (let i of res.data.data) {
        const data = { account_name: account_name, full_text: i.text, created_at: i.created_at}
        postData(data, 'tweets')
        resCount++
      }
      postData({state: 'log', log: `Dumped. \naccount: ${account_name}\ndata count: ${resCount}`, time: _date}, 'log')
    })
    .catch(err => {
      const data = err.response.data
      const message = `title: ${data.title}\ndetail: ${data.detail}\nerrors: ${data.errors[0].message}`
      postData({state: 'error', log: message, time: _date}, 'log')
    })
}

async function postData(data, index) {
    elastic.post(`http://localhost:9200/${index}/_doc/`, JSON.stringify(data, null))
      .then(res => { console.log(res.data) })
      .catch(err => { console.log(err.response.data) })
}

cron.schedule('0 * * * *', () => {
    Promise.all(
      [
          dump('secbird1'),
          dump('pingineer_jp')
      ]).catch(e => console.log(e.response.data))
})

beararAPIにアクセスするためのトーク

twitter API v2で取得したデータはこのようなjsonになっています。

[
  {
    created_at: '2021-11-30T03:33:05.000Z',
    id: '1465524255723261953',
    text: '三井住友カードのフィッシングサイトと思われます。だまされてはいけません。\n' +
      '\n' +
      'hxxp://wcermxoj.ml/\n' +
      '( IP:Cloudflare Country:US Org:AS13335 Cloudflare, Inc. )\n' +
      '\n' +
      '#Phishing #フィッシング #三井住友カード #SMBC'
  },
  {
    created_at: '2021-11-30T03:03:01.000Z',
    id: '1465516691312758785',
    text: '三井住友カードのフィッシングサイトと思われます。だまされてはいけません。\n' +
      '\n' +
      'hxxp://wcermxoj.tk/\n' +
      '( IP:Cloudflare Country:US Org:AS13335 Cloudflare, Inc. )\n' +
      '\n' +
      '#Phishing #フィッシング #三井住友カード #SMBC'
  },
  ...
]

収集したデータのパース

ツイートの内容のままだとelasticsearchで扱いにくいので、ツイートの内容を切り分けてそこから更に情報を追加していきます。

まずは上記のようなツイートから以下の要素に切り分けます。

  • 名前を騙られたサービス名
  • フィッシングサイトのURL
  • IP
  • 地域情報
  • org(使用されているホスティングサービスの組織名)

さらに、geoipを用いてIPアドレスから大まかな座標を取得したり、ドメインに対してwhoisを行うなどして情報の追加を行いました。
外部コマンド(whoisやnslookupなど)を実行するときは、この関数から実行することでエラーが発生したかどうかを判別しています。

async function run(command) {
    let res
    try {
        res = await exec(command)
    } catch (e) { res = e }
    if ( Error[Symbol.hasInstance](res)) return
    return res.stdout
}

IPアドレスから座標を取得するためにgeoip-liteというnode.jsのライブラリを使用しています。

console.log(geoip.lookup('107.150.11.137'))

IPアドレスを引数にわたすだけで様々な情報を取得できます。

{
  range: [ 1804992512, 1804996607 ],
  country: 'US',
  region: 'CA',
  eu: '0',
  timezone: 'America/Los_Angeles',
  city: 'Los Angeles',
  ll: [ 34.0549, -118.2578 ],
  metro: 803,
  area: 1000
}

ツイートを行ごとに分けてから、正規表現で要素の抽出と外部コマンドを実行をしています。
処理されたデータはPOSTでelasticsearchに送られます。

async function secbird(res) {
    for (let i of res.body.hits.hits) {
        i = i._source
        let text = i.full_text.split('\n')
        if (text.length > 5) {
            text = text.filter(n => n !== '')
            const name = text[0].match(/.+の/)[0].slice(0, -1)
            const url = text[1].replace('hxxp', 'http')
            const domain = url.replace(/http:\/\/|https:\/\/|/, '').replace('/', '').replace('/index', '')
            let ip = await nslookup(domain) || text[2].match(/IP:[Cloudflare|\d\.]+/)[0].slice(3)
            let whois = await run('whois '+domain) || "failed"
            const country = text[2].match(/Country:\w+/)[0].slice(8)
            const ll = await getll(ip)
            if (ll === "") continue
            const org = text[2].match(/Org:[-,.\s\w]+/)[0].trim().slice(4)
            const data = { name: name, url: url, ip: ip, whois: whois, country: country, location: ll, org: org, created_at: i.created_at}
            postData(data, 'secbird1')
        }
    }
}

elasticsearchに投入するパースされたjsonがこちらです。

{
  "name": "PayPay銀行",
  "url": "https://www.amazon.jp.nqg2.xyz",
  "ip": "107.150.11.137",
  "whois": "The queried object does not exist: DOMAIN NOT FOUND\n",
  "country": "US",
  "location": {
    "lat": 34.0549,
    "lon": -118.2578
  },
  "org": "AS8100 QuadraNet Enterprises LLC",
  "created_at": "2021-11-11T00:45:51+00:00"
}

※このjsonに含まれるurlはフィッシングサイトのurlです。絶対にアクセスしないでください。

整形したデータはelasticsearchに投入してデータ収集とパースは終了です。

まとめ

今回はtwitterからツイートを収集して、データの追加とパース・elasticsearchに投入するところまでをnode.jsを使って自動化しました。
特にnode.jsから外部コマンド(nslookupやwhoisなど)を実行することが多く、コードを書いているwindowsのパソコンからではテストができなかったので大変でした。
次の記事ではelasticsearchの構築と投入したデータの可視化について書くので、次回もよろしくお願いします。

Burp SuiteでCVE-2020-24389を検証してみた

このブログは、N高等学校とVRChatの世界からやってきた株式会社ArmorisアルバイトのShaderoが書いています。 あるもりすぶろぐの内容は個人の意見です。

今回はBurp Suiteの基本的な使い方を学習するために、非常に簡単に再現が行えるCVE-2020-24389を検証してみました。Burp Suiteを用いた検証の大まかな流れや、後述するProxy機能の便利さなどが伝われば幸いです。

Burp Suiteとは

脆弱性検証でよく使われているローカルプロキシツールです。本記事で使う無償のCommunity Edition以外に、有償のProfessionalやEnterpriseがありますが、本記事で使うProxy機能は無償版でも使えます。

検証環境

本記事は以下の環境を使用しています。

Name Version
Windows10 Pro 21H1
Burp Suite Community Edition v2021.6.2

インストール

まずはBurp Suiteをインストールします。公式サイトより「Burp Suite Community Edition」を選択し、インストーラーをダウンロードしてインストールしてください。

起動~通信履歴の観覧

Burp Suiteを起動すると、Project選択画面が表示されます。 本エディションの場合「Temporary project」しか選べないため、そのまま次に進んでください。

f:id:Armoris:20211006190023p:plain

次にBurp Suiteの設定を選択する画面が表示されます。 過去にBurp Suiteをインストールしていてコンフィグファイルをエクスポートしていた方などはここで読み込むことが出来ますが、今回は「Use Burp defaults」を選択し「Start Burp」をクリックします。

f:id:Armoris:20211006190035p:plain

Startすると以下の画面が表示されます。

f:id:Armoris:20211006190048p:plain

ツールバーの下にある「Proxy」をクリックすると以下の画面が表示されます。この画面を使って通信の改竄や通信履歴の観覧を行います。自分のブラウザにプロキシを設定してサイトにアクセスする方法もありますが、今回はBurp suiteに組み込まれているブラウザを使って通信履歴の観覧を行います。

f:id:Armoris:20211006190104p:plain

「Intercept is on」と書かれている青いボタンをクリックし「Intercept is off」にします。オンにしたままにすると通信内容の改竄が出来るのですが、ここでは改竄は行わずに通信履歴を観覧したいためオフにします。

f:id:Armoris:20211006190132p:plain

その後「Open Browser」をクリックし、立ち上がったブラウザを使って任意のWebサイトにアクセスしBurp Suiteにある「HTTP history」をクリックすると通信履歴が表示されます。ヤッタネ!(画像はGoogle.comにアクセスした際の履歴)

f:id:Armoris:20211006190147p:plain

実際にWordpressプラグイン脆弱性検証をする(CVE-2020-24389)

ここでは実際にWordpressプラグイン脆弱性を検証してみます。この脆弱性は一部のファイル拡張子が無効化されていなかったため、任意のPHPコードがアップロード出来てしまう脆弱性です。

Wordpressサーバー環境

Name Version
Ubuntu 20.04
Wordpress 5.5.1
Contact Form 7 5.2.1
Drag and Drop Multiple File Upload - Contact From 7 1.3.5.1

やってみる

今回は通信の改竄を行うので「Intercept is on」と書かれている青いボタンになっているか確認します。「on」になっていない場合はクリックしてオンにしてください。

f:id:Armoris:20211006190203p:plain

「Open Browser」をクリックし、立ち上がったブラウザで該当のWordPressのサイトにアクセスすると、Burp Suiteの方で通信を改竄する画面が表示されますが、ここは何もせず「Forward」をクリックします。

f:id:Armoris:20211006190229p:plain

該当のサイトが表示されたら任意のpngドラッグアンドドロップでアップロードします。ここで通信内容を改竄して画像ファイルではなくPHPコードをアップロードさせます。

f:id:Armoris:20211006190241p:plain

以下は実際に改竄を行うリクエストの画像

f:id:Armoris:20211006190251p:plain

jpg|jpeg|JPG|png|gif|pdf|doc|docx|ppt|pptx|odt|avi|ogg|m4a|mov|mp3|mp4|mpg|wav|wmv|xlsと書かれている所をpharに、filename="(任意の名前).png"filename="(任意の名前).phar"に、Content-Type: image/pngより下、------WebKitFormBoundary………云々より上の文字列をアップロードさせたいPHPのコード(ここではwebshellのコードを使用)に書き換えて「Forward」をクリックすると任意のPHPコードがアップロードされます。

f:id:Armoris:20211006190304p:plain

「(WordpressIPアドレス)/wp-content/uploads/wp_dndcf7_uploads/wpcf7-files/」にアクセスすると先程のPHPファイルが実際にアップロードされているのが確認できます。

f:id:Armoris:20211006190313p:plain

実際にそのPHPファイルにアクセスすると実行することも出来ました。

f:id:Armoris:20211006190322p:plain

最後に

今回初めてBurp Suiteを使ってみましたが、GUIで視覚的に分かりやすく簡単に通信の流れが確認できる上に通信内容を書き換える事が出来てとても便利でした。有償のProfessional版はこれらの無償版の機能に加えて脆弱性スキャン機能やプロジェクト保存機能などもありますが、個人で軽く使う分には無償のCommunity Editionでも問題ないと思います。Proxy機能とても便利なのでぜひ使ってみてください!

Armoris日記 NGINX-Proxy WebSocket編

このブログは、元N高生株式会社Armorisの社員が書いています。

あるもりすぶろぐの内容は個人の意見です。

NGINX ProxyとWebSocketのお話

先日社内システムのNGINX Proxyで設定を変更した際、WebSocket通信で問題が発生しました。今回は問題解決のために行ったこと、解決方法について取り上げます。

NGINXに加えた変更について

これまでNGINX Proxyでの認証にはNGINX標準の機能を利用したBasic Authを利用していましたが、セキュリティ上の問題などから外部モジュールを利用したDigest Authに変更しています。
変更時の作業についてはいずれArmoris日記としてまとめたいと思います。

発生した問題

NGINXの設定を変更後Proxyしている特定のサービスにアクセスすると、「数分ですぐに認証ダイアログが再表示される」「特定の通信がエラーで失敗する」といった不具合が確認されました。
実際に通信時のログなどを確認し、どうやら通信が失敗するのはXHRのリクエストだという事がわかりました。 f:id:Armoris:20210914185409j:plain また、エラーが発生しているのはMattermostとの通信で、このサービスは大量のWebSocket通信を行うことから、WebSocketをProxyさせることで問題が起きているのではないかと考えました。(以前Mattermostを構築した際にも挙動がおかしい原因としてWebSocketの問題があった)

解決策1タイムアウトまでの時間を伸ばす

まず初めに解決策として考えたのはNGINXで導入しているDigest Authモジュールでタイムアウトまでの時間を設定することです。

タイムアウトまでの時間はreadmeを確認するとデフォルトで1分になっています。
そこでauth_digest_timeoutを数時間単位で設定しましたが、この方法では今回の問題は解決できませんでした。

解決策2NGINXにWebSocket用の設定を追記

次の解決策としてNGINXの公式リファレンスにある NGINX as a WebSocket Proxy を参考にproxy_http_versionのパラメータを明示的に設定することです。このパラメータを追加すると、NGINXが通信をWebSocketと認識して通信エラーが発生しなくなります。

実際に設定ファイルには以下の1行を追記しました。

proxy_http_version 1.1;

結果はたったこれだけで発生していた問題が解決しました。
この設定を行ってからは通信エラーが発生することなく安定して動いています。

最後に

今回問題があったのはWebSocket通信の部分ですが、これに気づけないとなかなか問題解決に至るのは難しいと思います。また、今回問題があったのはWebSocket通信を頻繁に行うサービスだったと言うこともポイントになっていると思います。
実際に今回のような問題が発生した際に速やかに原因を特定し、解決するためにも運用しているサービスの特徴を把握しておくことは重要だと感じました。