このglibc問題を回避する最良の方法は何でしょうか?


26

私は、ファイル機能を使用して、setuid-rootバイナリ(たとえば/bin/ping、CAP_NET_RAWなど)の必要性のほとんどを排除するGentoo Hardenedボックスを管理しています。

実際、私が残した唯一のバイナリはこれです:

abraxas ~ # find / -xdev -type f -perm -u=s
/usr/lib64/misc/glibc/pt_chown
abraxas ~ # 

setuidビットを削除するか、ルートファイルシステムを再マウントするnosuidと、sshdとGNU Screenが動作を停止しますgrantpt(3)。これらはマスターの擬似端末を呼び出し、glibcはこのプログラムを実行してスレーブの擬似端末をchownおよびchmodする/dev/pts/ためです。失敗します。

問題は、grantpt(3)Linuxの場合、devptsファイルシステムがマウントされていると、そのようなヘルパーバイナリは不要であると明示的に記載されているマンページです。カーネルは、スレーブのUIDとGIDを、/dev/ptmx(呼び出しによりgetpt(3))開いたプロセスの実際のUIDとGIDに自動的に設定します。

これを示すために、小さなサンプルプログラムを作成しました。

#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
int main(void)
{
    int master;
    char slave[16];
    struct stat slavestat;
    if ((master = getpt()) < 0) {
        fprintf(stderr, "getpt: %m\n");
        return 1;
    }
    printf("Opened a UNIX98 master terminal, fd = %d\n", master);
    /* I am not going to call grantpt() because I am trying to
     * demonstrate that it is not necessary with devpts mounted,
     * the owners and mode will be set automatically by the kernel.
     */
    if (unlockpt(master) < 0) {
        fprintf(stderr, "unlockpt: %m\n");
        return 2;
    }
    memset(slave, 0, sizeof(slave));
    if (ptsname_r(master, slave, sizeof(slave)) < 0) {
        fprintf(stderr, "ptsname: %m\n");
        return 2;
    }
    printf("Device name of slave pseudoterminal: %s\n", slave);
    if (stat(slave, &slavestat) < 0) {
        fprintf(stderr, "stat: %m\n");
        return 3;
    }
    printf("Information for device %s:\n", slave);
    printf("    Owner UID:  %d\n", slavestat.st_uid);
    printf("    Owner GID:  %d\n", slavestat.st_gid);
    printf("    Octal mode: %04o\n", slavestat.st_mode & 00007777);
    return 0;
}

前述のプログラムのsetuidビットを削除して、動作を観察します。

aaron@abraxas ~ $ id
uid=1000(aaron) gid=100(users) groups=100(users)
aaron@abraxas ~ $ ./ptytest 
Opened a UNIX98 master terminal, fd = 3
Device name of slave pseudoterminal: /dev/pts/17
Information for device /dev/pts/17:
    Owner UID:  1000
    Owner GID:  100
    Octal mode: 0620

この問題を回避する方法については、いくつかのアイデアしかありません。

1)プログラムを、単に0を返すスケルトンに置き換えます。

2)libcでgrantpt()をパッチして何もしない。

私はこれらの両方を自動化できますが、誰かが他のものよりも推奨事項、またはこれを解決する他の方法について推奨事項がありますか?

これが解決したら、ようやくできmount -o remount,nosuid /ます。


応答を待っている間、アプローチ1を使用しましたが、sshdとGNU Screenは引き続き機能します。
アーロンジョーンズ

失敗するプログラムとは正確には何ですか?おそらくそれらは壊れており、pty(必要なように)ではなくプログラムをチェックしていますか?
フォンブランド

CAP_CHOWNとCAP_FOWNERを持たず、grantpt()を呼び出し、ヘルパーバイナリがEUID == 0で開始されないプログラムは、grantpt()のゼロ以外のリターンコードを持ち、プログラムはこれが発生したときにPTSの作成を中止する必要があります、ptmx(4)による。
アーロンジョーンズ

3
その「解決策」は私に喜んでくれます...最良の場合、それはバグを報告し、おそらく新しいバグを作成し、最悪の場合、深刻なセキュリティ脆弱性を作成します。glibc開発者と一緒にこれを取り上げてください。
フォンブランド

3
次に、これをglibcの人々に報告してください。
フォンブランド

回答:


2

あなたの場合はglibcのが合理的に現在ある、とdevptsが正しく設定され、起動する必要があってはならないpt_chownすべてでヘルパーを。

からsetuid-rootを削除する既知/潜在的な問題が発生している可能性がありますpt_chown

grantpt()glibc-2.7devfsからサポートされ、変更はglibc-2.11で行われたため、明示的にチェックするのではなく、呼び出しを試行またはフォールバックする前に作業を行う必要があるかどうかを確認します。DEVFS_SUPER_MAGICchown()pt_chown

から glibc-2.17/sysdeps/unix/grantpt.c

  ...
  uid_t uid = __getuid ();
  if (st.st_uid != uid)
    {
       if (__chown (buf, uid, st.st_gid) < 0)
       goto helper;
    }
  ...

同様のスタンザを使用して、gidと権限を確認します。キャッチは、UID、GID、モードは期待(もし、TTY、と一致しなければならないということで、正確に、との確認620 /usr/libexec/pt_chown --help)。そうでない場合はchown()(呼び出し元のバイナリ/プロセスの機能CAP_CHOWN、CAP_FOWNERが必要)、試行されますが、失敗した場合はpt_chown外部ヘルパー(setuid-rootである必要があります)が試行されます。ためにはpt_chown能力、それを(それゆえ、あなたのglibc)を使用できるようにしてコンパイルされている必要がありますHAVE_LIBCAPしかし、それはそうpt_chown(のようであるのglibc-2.17バージョンを明記していないのに、あなたが述べたように)したいためにハードコードされたgeteuid()==0 にかかわらずHAVE_LIBCAP、より関連するコードglibc-2.17/login/programs/pt_chown.c

  ...
  if (argc == 1 && euid == 0)
    {
#ifdef HAVE_LIBCAP
  /* Drop privileges.  */
     if (uid != euid)
  ...
#endif
    /* Normal invocation of this program is with no arguments and
       with privileges.  */
    return do_pt_chown ();
  }
...
  /* Check if we are properly installed.  */
  if (euid != 0)
    error (FAIL_EXEC, 0, gettext ("needs to be installed setuid `root'"));

geteuid()==0機能を使用しようとする前に期待することは、実際には機能の精神に反しているように思われるので、この機能にバグを記録します。)

潜在的な回避策は、CAP_CHOWN、CAP_FOWNERを影響を受けるプログラムに与えることかもしれませんが、もちろんそれをptyに制限することはできないので、私は本当にお勧めしません

それでも解決しない場合は、パッチを適用、それを解決sshdし、screenglibcのパッチ適用をよりわずかに小さい嫌です。ただし、問題はglibc内にあるため、よりクリーンなアプローチは、DLLインジェクションを選択的に使用してダミーを実装することgrantpt()です。


「しかし、問題はglibc内にあるため、よりクリーンなアプローチは、DLL注入を選択的に使用してダミーのgrantpt()を実装することです。」-素晴らしい。なぜ私はそれを考えなかったのですか?ありがとう。:)
アーロンジョーンズ
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.