
pspyは、root権限を必要とせずにプロセスを監視するために設計されたコマンドラインツールです。他のユーザーが実行したコマンドやcronジョブなどを、実行時に確認できます。CTFでのLinuxシステムの列挙に最適です。また、コマンドライン引数としてシークレットを渡すことがなぜ悪いアイデアかを同僚に示すのにも最適です。
このツールは、procfsスキャンから情報を収集します。ファイルシステムの選択された部分に配置されたInotifyウォッチャーがこれらのスキャンをトリガーし、短命なプロセスをキャッチします。
調査したいLinuxマシンにツールを入手します。まずバイナリを入手してください。以下のリリースバイナリをダウンロードします:
pspy32 ダウンロードpspy64 ダウンロードpspy32s ダウンロードpspy64s ダウンロード静的にコンパイルされたファイルはどのLinuxシステムでも動作しますが、サイズがかなり大きい(〜4MB)です。サイズが問題なら、libcに依存しUPXで圧縮された小さいバージョン(〜1MB)を試してください。
システムにインストールされたGoを使用するか、リリースを作成するために実行されたDockerベースのビルドプロセスを実行します。後者の場合は、Dockerがインストールされていることを確認し、make build-build-imageを実行してDockerイメージをビルドし、続けてmake buildを実行してバイナリをビルドします。
pspy --helpを実行すると、フラグとその意味を確認できます。概要は以下の通りです:
デフォルト設定はほとんどの用途で問題ありません。/usr内のファイルを監視することが最も重要です。これは多くのツールが内部のライブラリにアクセスするためです。
より複雑な例:
# コマンドとファイルシステムイベントの両方を出力し、procfsを1000ms(=1秒)ごとにスキャン
./pspy64 -pf -i 1000
# 2つのディレクトリを再帰的に、さらに1つのディレクトリを非再帰的に監視
./pspy64 -r /path/to/first/recursive/dir -r /path/to/second/recursive/dir -d /path/to/the/non-recursive/dir
# 発見されたコマンドの出力を無効にし、ファイルシステムイベントを有効にする
./pspy64 -p=false -f
ツールの動作を確認するには、リポジトリをクローンしてmake exampleを実行してください(Dockerが必要)。パスワードをコマンドライン引数として渡すことが安全でないことは知られており、この例はそれを示すために使用できます。このコマンドはDebianコンテナを起動し、その中でrootによって実行される秘密のcronジョブが毎分ユーザーパスワードを変更します。pspyはmyuserとしてフォアグラウンドで実行され、プロセスをスキャンします。以下のような出力が表示されるはずです:
~/pspy (master) $ make example
[...]
docker run -it --rm local/pspy-example:latest
[+] cron started
[+] Running as user uid=1000(myuser) gid=1000(myuser) groups=1000(myuser),27(sudo)
[+] Starting pspy now...
Watching recursively : [/usr /tmp /etc /home /var /opt] (6)
Watching non-recursively: [] (0)
Printing: processes=true file-system events=false
2018/02/18 21:00:03 Inotify watcher limit: 524288 (/proc/sys/fs/inotify/max_user_watches)
2018/02/18 21:00:03 Inotify watchers set up: Watching 1030 directories - watching now
2018/02/18 21:00:03 CMD: UID=0 PID=9 | cron -f
2018/02/18 21:00:03 CMD: UID=0 PID=7 | sudo cron -f
2018/02/18 21:00:03 CMD: UID=1000 PID=14 | pspy
2018/02/18 21:00:03 CMD: UID=1000 PID=1 | /bin/bash /entrypoint.sh
2018/02/18 21:01:01 CMD: UID=0 PID=20 | CRON -f
2018/02/18 21:01:01 CMD: UID=0 PID=21 | CRON -f
2018/02/18 21:01:01 CMD: UID=0 PID=22 | python3 /root/scripts/password_reset.py
2018/02/18 21:01:01 CMD: UID=0 PID=25 |
2018/02/18 21:01:01 CMD: UID=??? PID=24 | ???
2018/02/18 21:01:01 CMD: UID=0 PID=23 | /bin/sh -c /bin/echo -e "KI5PZQ2ZPWQXJKEL\nKI5PZQ2ZPWQXJKEL" | passwd myuser
2018/02/18 21:01:01 CMD: UID=0 PID=26 | /usr/sbin/sendmail -i -FCronDaemon -B8BITMIME -oem root
2018/02/18 21:01:01 CMD: UID=101 PID=27 |
2018/02/18 21:01:01 CMD: UID=8 PID=28 | /usr/sbin/exim4 -Mc 1enW4z-00000Q-Mk
まず、pspyは現在実行中のすべてのプロセスを、PID、UID、コマンドラインとともに出力します。pspyが新しいプロセスを検出すると、このログに行を追加します。この例では、PIDが23のプロセスがあり、myuserのパスワードを変更しているように見えます。これは、rootのプライベートcrontab /var/spool/cron/crontabs/root で使用されているPythonスクリプトの結果であり、このシェルコマンドを実行します(crontabおよびスクリプトを確認してください)。myuserはcrontabもPythonスクリプトも見ることができないことに注意してください。しかしpspyを使えば、それでもコマンドを見ることができます。
以下はHack The BoxのShrekマシンの例です。このCTFチャレンジでは、フォルダ内のすべてのファイルの所有権を変更する隠されたcronジョブを悪用するのが課題です。脆弱性はchmodと一緒にワイルドカードを安全でない方法で使用していることです(詳細)。それを見つけて悪用するにはかなりの推測が必要です。しかしpspyを使えば、cronジョブは簡単に見つけて分析できます:

Linuxシステム上で実行されたすべてのプロセス(終了したものを含む)をリストするツールが存在します。例えばforkstatがあります。これは、forkやexecなどのプロセス関連イベントに関するカーネルからの通知を受け取ります。
これらのツールはroot権限を必要としますが、それで安心してはいけません。Linuxシステムで実行中のプロセスを覗き見ることを妨げるものは何もありません。プロセスが実行中である限り、procfsでは多くの情報が見えます。唯一の問題は、短命なプロセスをその生存中の非常に短い時間枠でキャッチしなければならないことです。無限ループで/procディレクトリを新しいPIDについてスキャンする方法は機能しますが、CPUを大量に消費します。
よりステルスな方法は、以下のトリックを使用することです。プロセスは、/usrのライブラリ、/tmpの一時ファイル、/varのログファイルなどのファイルにアクセスする傾向があります。inotify APIを使用すると、これらのファイルが作成、変更、削除、アクセスされるたびに通知を受け取ることができます。LinuxはこのAPIに特権ユーザーを必要としません。これは多くの無害なアプリケーション(最新のファイルエクスプローラを表示するテキストエディタなど)に必要だからです。したがって、root以外のユーザーはプロセスを直接監視できませんが、ファイルシステムに対するプロセスの影響を監視できます。
ファイルシステムイベントをトリガーとして/procをスキャンし、プロセスをキャッチするのに十分な速さで実行できることを期待します。これがpspyの動作です。見逃さないという保証はありませんが、私の実験では確率は高いようです。一般的に、プロセスの実行時間が長いほど、キャッチできる可能性が高くなります。
ロゴ:"By Creative Tail [CC BY 4.0 (http://creativecommons.org/licenses/by/4.0)], via Wikimedia Commons"(リンク)