
CVE-2021-3156の技術分析とエクスプロイト開発。Sudoのヒープベースのバッファオーバーフローで、主要なLinuxディストリビューションでのローカル権限昇格のための3つの動作するエクスプロイトが含まれます。
Qualys セキュリティアドバイザリ
Baron Samedit: Sudo におけるヒープベースのバッファオーバーフロー (CVE-2021-3156)
概要 分析 悪用 謝辞 タイムライン
私たちは Sudo にヒープベースのバッファオーバーフローを発見しました (https://www.sudo.ws/)。この脆弱性は次のとおりです:
認証なしで、任意のローカルユーザー(通常ユーザーおよびシステムユーザー、 sudoers と非 sudoers)によって悪用可能です(つまり、攻撃者は ユーザーのパスワードを知る必要はありません);
2011年7月(コミット 8255ed69)に導入され、デフォルト設定において、 すべてのレガシーバージョン 1.8.2 から 1.8.31p2 と、 すべての安定版 1.9.0 から 1.9.5p1 に影響します。
私たちはこの脆弱性に対する3つの異なるエクスプロイトを開発し、 Ubuntu 20.04 (Sudo 1.8.31)、Debian 10 (Sudo 1.8.27)、 Fedora 33 (Sudo 1.9.2) で完全な root 権限を取得しました。他のオペレーティングシステムや ディストリビューションでも悪用可能である可能性が高いです。
Sudo が "shell" モード(shell -c command)でコマンドを実行するために実行される場合:
-s オプション(Sudo の MODE_SHELL フラグを設定する)による方法;
または -i オプション(Sudo の MODE_SHELL と MODE_LOGIN_SHELL フラグを設定する)による方法;
その場合、Sudo の main() の先頭で parse_args() は argv を書き換えます(行 609-617)。 すべてのコマンドライン引数を連結し(行 587-595)、すべてのメタ文字をバックスラッシュでエスケープします(行 590-591):
その後、sudoers_policy_main() 内の set_cmnd() は、コマンドライン引数を ヒープベースのバッファ "user_args" に連結し(行 864-871)、 メタ文字のエスケープを解除します(行 866-867)。これは "sudoers の マッチングとロギングのため" です:
残念ながら、コマンドライン引数が単一のバックスラッシュ文字で終わる場合、次のようになります:
行 866 では、"from[0]" はバックスラッシュ文字であり、"from[1]" は 引数のヌル終端文字です(つまり、スペース文字ではありません);
行 867 では、"from" がインクリメントされ、ヌル終端文字を指します;
行 868 では、ヌル終端文字が "user_args" バッファにコピーされ、 "from" が再度インクリメントされ、ヌル終端文字の後の最初の文字を指します (つまり、引数の境界外です);
行 865-869 の "while" ループは、境界外の文字を読み取って "user_args" バッファにコピーします。
言い換えると、set_cmnd() は、ヒープベースのバッファオーバーフローに対して脆弱です。 "user_args" バッファにコピーされる境界外の文字が、 そのサイズ(行 852-853 で計算)に含まれていないためです。
しかし理論上は、コマンドライン引数が単一のバックスラッシュ文字で終わることはありません: MODE_SHELL または MODE_LOGIN_SHELL が設定されている場合(行 858、 これは脆弱なコードに到達するための必要条件)、MODE_SHELL が設定され(行 571)、 parse_args() はすでにバックスラッシュを含むすべてのメタ文字をエスケープしています (つまり、すべての単一バックスラッシュを2番目のバックスラッシュでエスケープしています)。
しかし実際には、set_cmnd() 内の脆弱なコードと parse_args() 内のエスケープコードは、 わずかに異なる条件で囲まれています:
一方:
では、私たちの疑問は次のとおりです: MODE_SHELL と MODE_EDIT または MODE_CHECK のいずれかを設定し(脆弱なコードに到達するため)、 デフォルトの MODE_RUN を設定しない(エスケープコードを回避するため)ことは可能でしょうか?
答えは、どうやら「いいえ」のようです: MODE_EDIT(-e オプション、行 361)または MODE_CHECK(-l オプション、行 423 および 519)を設定すると、 parse_args() は "valid_flags" から MODE_SHELL を削除し(行 363 および 424)、 MODE_SHELL のような無効なフラグを指定するとエラーで終了します(行 532-533):
しかし、私たちは抜け穴を見つけました: "sudo" の代わりに "sudoedit" として Sudo を実行すると、 parse_args() は自動的に MODE_EDIT を設定しますが(行 270)、 "valid_flags" をリセットせず、"valid_flags" にはデフォルトで MODE_SHELL が含まれます(行 127 および 249):
したがって、"sudoedit -s" を実行すると、MODE_EDIT と MODE_SHELL の両方を設定し(ただし MODE_RUN は設定しない)、 エスケープコードを回避し、脆弱なコードに到達し、単一のバックスラッシュ文字で終わるコマンドライン引数を介して ヒープベースのバッファ "user_args" をオーバーフローさせます:
perl -e 'print "A" x 65536'
malloc(): corrupted top size
Aborted (core dumped)攻撃者の観点から見ると、このバッファオーバーフローは理想的です:
私たちはオーバーフローさせる "user_args" バッファのサイズを制御します(連結されたコマンドライン引数のサイズ、行 852-854);
オーバーフロー自体のサイズと内容を独立して制御します(最後のコマンドライン引数の直後に最初の環境変数が続きます。これらは行 852-853 のサイズ計算には含まれません);
オーバーフローさせるバッファにヌルバイトを書き込むことさえできます(単一のバックスラッシュで終わるすべてのコマンドライン引数または環境変数は、"user_args" にヌルバイトを書き込みます、行 866-868)。
たとえば、amd64 Linux では、次のコマンドは 24 バイトの "user_args" バッファ(32 バイトのヒープチャンク)を割り当て、 次のチャンクの size フィールドを "A=a\0B=b\0" (0x00623d4200613d41)、 fd フィールドを "C=c\0D=d\0" (0x00643d4400633d43)、 bk フィールドを "E=e\0F=f\0" (0x00663d4600653d45) で上書きします: