
CVE-2018-1000533の概念実証エクスプロイト。GitList 0.6.0におけるリモートコード実行脆弱性で、検索機能の未サニタイズ入力により任意のコマンド実行を許容する。
GitListは現代的なGitリポジトリビューアです。
GitList 0.6.0 以下のバージョンでは、システム関数に不適切に検証された入力値を渡す脆弱性が存在し、PHPユーザー権限で任意のコマンドを実行できる可能性があります。
GitListサーバー0.6.0を起動するには、次のコマンドを実行します。
docker compose up -d
poc.py実行前に依存関係をインストールします。
pip install requests
環境構築が完了したら、http://127.0.0.1:8080にアクセスし、exampleというテストリポジトリが存在することを確認します。
GitListはリポジトリ内のコードを検索する際に git grep を使用します。
public function searchTree($query, $branch)
{
if (empty($query)) {
return null;
}
$query = escapeshellarg($query);
try {
$results = $this->getClient()->run($this, "grep -i --line-number {$query} $branch");
} catch (\RuntimeException $e) {
return false;
}
ここで $query は検索キーワード、$branch は検索対象ブランチを意味します。
もし攻撃者が検索語として --open-files-in-pager=id; を渡すと、システムでidコマンドが実行されます。
この脆弱性が発生する理由は2つあります:
escapeshellarg() 関数の限界理論的には $query = escapeshellarg($query); コードを経ると入力値はシングルクォートで囲まれた文字列になります。しかし escapeshellarg() はシェル(shell)の観点からのインジェクションは防ぎますが、git 自身のオプションパーサーが -- で始まる文字列をオプションとして解釈することまでは防げません。
--open-files-in-pager オプションの特性git grepの当該オプションは、検索結果を表示する外部ページャー(Pager)プログラムを指定する役割を果たしますが、ここに渡された値をシステムコマンドとして直接実行してしまう特性があります。
[repo_name]/tree/[keyword]/search)には、2つの引数 [repo_name] と [keyword] が存在します。[repo_name] はGitListに存在するリポジトリである必要があり、[keyword] は少なくとも1つ以上の検索結果を生成する検索キーワードである必要があります。
(本再現ではリポジトリとして example、キーワードとして a を使用しました。)次のコマンドで poc.py を実行します。
このスクリプトにより touch /tmp/success_dddo0 が実行され、サーバーの /tmp ディレクトリに success_dddo0 という空のファイルが作成されます。
python3 poc.py http://127.0.0.1:8080
リクエスト送信後、次のコマンドで success_dddo0 が正常に作成されたか確認します。
docker compose exec web ls -l /tmp/success_dddo0
PoC実行後、サーバー内部で任意のシステムコマンド(touch /tmp/success_dddo0)が正常に実行され、ファイルが作成されたことを確認できます。

docker compose down
GitListのバージョンを最新バージョンにアップデートする必要があります。
コード修正内容:
public function searchTree($query, $branch)
{
if (empty($query)) {
return null;
}
$query = preg_replace('/(--?[A-Za-z0-9\-]+)/', '', $query);
$query = escapeshellarg($query);
try {
$results = $this->getClient()->run($this, "grep -i --line-number -- {$query} $branch");
} catch (\RuntimeException $e) {
return false;
}
GitListでは、正規表現を使用して検索語($query)から不正な - 接頭辞を削除し、git grep コマンドに --(オプションの終了を示す識別子)を追加することで、以降のすべての入力値がコマンドオプションではなく通常の文字列(検索語)としてのみ扱われるように強制して、この脆弱性を修正しました。