文書化されていないコンテナエスケープ方法

文書化されていないコンテナエスケープ方法

作者: ニトロ@360GearTeam

背景

最近、非公開のコンテナ エスケープ メソッドが発見されました。コンテナがホスト PID 名前空間を共有し、uid 0 で実行される場合 (ユーザー名前空間は有効になっておらず、追加の機能も追加されていません)、特定のプロセスのシンボリック リンクを使用することで実現できます。/proc/[pid]/root. コンテナが逃げ出す。

分析する

/proc/[pid]/root の紹介

proc(5)マニュアルによると、/proc/[pid]/rootシンボリックリンクを介して、現在のプロセスとターゲットプロセスが同じマウント名前空間に属しているかどうかに関係なく、任意のプロセスのrootfsにアクセスできます。その後、シンボリック リンクにアクセスする際の権限の問題についてマニュアルで次のような説明を見つけました/proc/[pid]/root

Permission to dereference or read (readlink(2)) this
symbolic link is governed by a ptrace access mode
PTRACE_MODE_READ_FSCREDS check; see ptrace(2).

つまり、このシンボリック リンクにアクセスするには、ptrace(2)関連する権限チェックを通過する必要があります。実際、この説明は非常に曖昧ですが、なぜシンボリック リンクにアクセスするときに ptrace システム コールに関連する権限を確認する必要があるのでしょうか。

ptrace(2)マニュアルを見て、PTRACE_MODE_READ_FSCREDSフラグに関連するセクションを見つけます。

Ptrace access mode checking
       Various parts of the kernel-user-space API (not just ptrace()
       operations), require so-called "ptrace access mode" checks, whose
       outcome determines whether an operation is permitted (or, in a
       few cases, causes a "read" operation to return sanitized data).
       These checks are performed in cases where one process can inspect
       sensitive information about, or in some cases modify the state
       of, another process.  The checks are based on factors such as the
       credentials and capabilities of the two processes, whether or not
       the "target" process is dumpable, and the results of checks
       performed by any enabled Linux Security Module (LSM)—for example,
       SELinux, Yama, or Smack—and by the commoncap LSM (which is always
       invoked).

       Prior to Linux 2.6.27, all access checks were of a single type.
       Since Linux 2.6.27, two access mode levels are distinguished:

       PTRACE_MODE_READ
              For "read" operations or other operations that are less
              dangerous, such as: get_robust_list(2); kcmp(2); reading
              /proc/[pid]/auxv, /proc/[pid]/environ, or
              /proc/[pid]/stat; or readlink(2) of a /proc/[pid]/ns/*
              file.

       PTRACE_MODE_ATTACH
              For "write" operations, or other operations that are more
              dangerous, such as: ptrace attaching (PTRACE_ATTACH) to
              another process or calling process_vm_writev(2).
              (PTRACE_MODE_ATTACH was effectively the default before
              Linux 2.6.27.)

       Since Linux 4.5, the above access mode checks are combined (ORed)
       with one of the following modifiers:

       PTRACE_MODE_FSCREDS
              Use the caller's filesystem UID and GID (see
              credentials(7)) or effective capabilities for LSM checks.

       PTRACE_MODE_REALCREDS
              Use the caller's real UID and GID or permitted
              capabilities for LSM checks.  This was effectively the
              default before Linux 4.5.

       Because combining one of the credential modifiers with one of the
       aforementioned access modes is typical, some macros are defined
       in the kernel sources for the combinations:

       PTRACE_MODE_READ_FSCREDS
              Defined as PTRACE_MODE_READ | PTRACE_MODE_FSCREDS.

       PTRACE_MODE_READ_REALCREDS
              Defined as PTRACE_MODE_READ | PTRACE_MODE_REALCREDS.

       PTRACE_MODE_ATTACH_FSCREDS
              Defined as PTRACE_MODE_ATTACH | PTRACE_MODE_FSCREDS.

       PTRACE_MODE_ATTACH_REALCREDS
              Defined as PTRACE_MODE_ATTACH | PTRACE_MODE_REALCREDS.

この説明を見た後、/proc/[pid]/rootシンボリック リンクへのアクセスにptrace(2)適切な権限チェックが必要な理由が理解できました。これは、/proc/[pid]/rootシンボリック リンクを介してターゲット プロセスの rootfs にアクセスする操作は、あるプロセスが別のプロセスにアクセスするという点で、ptrace システム コールを介してプロセスを追跡することに似ているためです。データ。

PTRACE_MODE_READ_FSCREDSフラグ ビットはPTRACE_MODE_READPTRACE_MODE_FSCREDSフラグ ビットの組み合わせであるため、呼び出しプロセスには、/proc/[pid]/rootシンボリック リンクを介してターゲット プロセスの rootfs にアクセスするための十分なファイル システム権限または機能が必要です。

**カーネルはパーミッションチェックをどのように正確に実行しますか? **この質問に答えるには、関連するカーネル ソース コードを分析する必要があります。

関連する関数呼び出し関係図は次のとおりです。
ここに画像の説明を挿入します

proc ファイル システム内のほとんどのファイルの実装は/fs/proc/base.cファイル内にあります。シンボリック リンクにアクセスすると、カーネルはシンボリック リンク ファイル i ノード内のメソッドを通じて、.get_link対応する実際のパスを取得します。の場合/proc/[pid]/root、使用されるメソッドは関数.get_linkです。proc_pid_get_link()

proc_pid_get_link()この関数は同じファイル内の関数を呼び出して、proc_fd_access_allowed()呼び出しプロセスに十分な権限があるかどうかを確認します。

proc_fd_access_allowed()この関数は、シンボリック リンクの i ノードを通じてターゲット プロセスのインスタンスを取得しtask_structptrace_may_access()関数を呼び出して権限を確認します。この関数を呼び出すときの 2 番目のパラメーターの値は ですPTRACE_MODE_READ_FSCREDS

ptrace_may_access()関数は/kernel/ptrace.cファイルで定義され、実際の作業は__ptrace_may_access()関数に委任されます。

/* Returns 0 on success, -errno on denial. */
static int __ptrace_may_access(struct task_struct *task, unsigned int mode)
{
    
    
    const struct cred *cred = current_cred(), *tcred;
    struct mm_struct *mm;
    kuid_t caller_uid;
    kgid_t caller_gid;

    if (!(mode & PTRACE_MODE_FSCREDS) == !(mode & PTRACE_MODE_REALCREDS)) {
    
    
        WARN(1, "denying ptrace access check without PTRACE_MODE_*CREDS\n");
        return -EPERM;
    }

    /* May we inspect the given task?
     * This check is used both for attaching with ptrace
     * and for allowing access to sensitive information in /proc.
     *
     * ptrace_attach denies several cases that /proc allows
     * because setting up the necessary parent/child relationship
     * or halting the specified task is impossible.
     */

    /* Don't let security modules deny introspection */
    if (same_thread_group(task, current))
        return 0;
    rcu_read_lock();
    if (mode & PTRACE_MODE_FSCREDS) {
    
    
        caller_uid = cred->fsuid;
        caller_gid = cred->fsgid;
    } else {
    
    
        /*
         * Using the euid would make more sense here, but something
         * in userland might rely on the old behavior, and this
         * shouldn't be a security problem since
         * PTRACE_MODE_REALCREDS implies that the caller explicitly
         * used a syscall that requests access to another process
         * (and not a filesystem syscall to procfs).
         */
        caller_uid = cred->uid;
        caller_gid = cred->gid;
    }
    tcred = __task_cred(task);
    if (uid_eq(caller_uid, tcred->euid) &&
        uid_eq(caller_uid, tcred->suid) &&
        uid_eq(caller_uid, tcred->uid)  &&
        gid_eq(caller_gid, tcred->egid) &&
        gid_eq(caller_gid, tcred->sgid) &&
        gid_eq(caller_gid, tcred->gid))
        goto ok;
    if (ptrace_has_cap(tcred->user_ns, mode))
        goto ok;
    rcu_read_unlock();
    return -EPERM;
ok:
    rcu_read_unlock();
    /*
     * If a task drops privileges and becomes nondumpable (through a syscall
     * like setresuid()) while we are trying to access it, we must ensure
     * that the dumpability is read after the credentials; otherwise,
     * we may be able to attach to a task that we shouldn't be able to
     * attach to (as if the task had dropped privileges without becoming
     * nondumpable).
     * Pairs with a write barrier in commit_creds().
     */
    smp_rmb();
    mm = task->mm;
    if (mm &&
        ((get_dumpable(mm) != SUID_DUMP_USER) &&
         !ptrace_has_cap(mm->user_ns, mode)))
        return -EPERM;

    return security_ptrace_access_check(task, mode);
}

現在のプロセスとターゲット プロセスが同じスレッド グループ内にある場合、それらは完全なアクセス許可を持っていることがわかります。

次に、mode の値にはPTRACE_MODE_FSCREDSフラグビットが含まれているため、まず呼び出し元のプロセスの合計が対象のプロセスの合計と一致fsuidするかどうかがチェックされます矛盾する場合、関数が呼び出されて、呼び出し元のプロセスがターゲット プロセスのユーザー名前空間にアクセス許可を持っているかどうかがチェックされ、そうでない場合はアクセスが拒否されます。fsgidfsuidfsgidptrace_has_cap()CAP_SYS_PTRACE

CAP_SYS_PTRACE次に、ターゲット プロセスがダンプ不可に設定されており、呼び出しプロセスにターゲット プロセスのユーザー名前空間のアクセス許可がない場合、アクセスは拒否されます。

最後にsecurity_ptrace_access_check()関数を呼び出して最終チェックを実行します。commcapこの関数は LSM に関連しています。ここでは、yama や AppArmor などの他の実装ではなく、LSM の実装のみに焦点を当てます。

の場合commcapsecurity_ptrace_access_check()最後の呼び出しは/security/commcap.cファイル内の関数ですcap_ptrace_access_check()

/**
 * cap_ptrace_access_check - Determine whether the current process may access
 *               another
 * @child: The process to be accessed
 * @mode: The mode of attachment.
 *
 * If we are in the same or an ancestor user_ns and have all the target
 * task's capabilities, then ptrace access is allowed.
 * If we have the ptrace capability to the target user_ns, then ptrace
 * access is allowed.
 * Else denied.
 *
 * Determine whether a process may access another, returning 0 if permission
 * granted, -ve if denied.
 */
int cap_ptrace_access_check(struct task_struct *child, unsigned int mode)
{
    
    
    int ret = 0;
    const struct cred *cred, *child_cred;
    const kernel_cap_t *caller_caps;

    rcu_read_lock();
    cred = current_cred();
    child_cred = __task_cred(child);
    if (mode & PTRACE_MODE_FSCREDS)
        caller_caps = &cred->cap_effective;
    else
        caller_caps = &cred->cap_permitted;
    if (cred->user_ns == child_cred->user_ns &&
        cap_issubset(child_cred->cap_permitted, *caller_caps))
        goto out;
    if (ns_capable(child_cred->user_ns, CAP_SYS_PTRACE))
        goto out;
    ret = -EPERM;
out:
    rcu_read_unlock();
    return ret;
}

まず、PTRACE_MODE_FSCREDSモードフラグがセットされているか否かに基づいて、有効なケイパビリティセット(有効ケイパビリティセット)を使用して許可チェックを実行するか、許可されたケイパビリティセット(許可されたケイパビリティセット)を使用するかを決定する。次に、呼び出しプロセスとターゲット プロセスが同じユーザー名前空間に属し、ターゲット プロセスの権限機能セットが呼び出しプロセスの機能セットのサブセットである場合、呼び出しプロセスは権限チェックに合格します。それ以外の場合は、呼び出し元のプロセスがターゲット プロセスが存在するユーザー名前空間に機能があるかどうかをチェックし、CAP_SYS_PTRACEそうである場合は権限チェックに合格し、そうでない場合はアクセスが拒否されます。

この時点で、記事の冒頭で挙げた質問に答えることができます。つまり、デフォルト構成では、すべてのコンテナー プロセスが同じ機能セットを持っているため、ホスト PID 名前空間を共有するこのコンテナーは他のコンテナー プロセスの rootfs にアクセスできます。 。ホスト上で非 root ユーザーとして実行されているプロセスの rootfs にはアクセスできません。呼び出し側プロセスの fsuid と fsgid がターゲット プロセスの euid、suid、uid、egid、sgid、gid とそれぞれ一致しないためです。ホスト マシン上で root ユーザーとして実行されているプロセスの rootfs にはアクセスできません。これは、ホスト マシン上で root ユーザーとして実行されているプロセスがすべての機能を備えており、その機能セットが呼び出し側プロセスの機能セットのサブセットではないためです。 。

最終的な要約は次のとおりです。

  • 呼び出し元のプロセスと対象のプロセスが同じプロセスグループに属している場合、アクセスが許可されます。
  • アクセス モード フラグが指定されている場合PTRACE_MODE_FSCREDS、呼び出しプロセスのファイル システム UID (fsuid) とファイル システム GID (fsgid) が、後続のファイル システム アクセス許可チェックで使用されます。アクセス モード フラグが指定されている場合PTRACE_MODE_REALCREDS、呼び出しプロセスの実 UID (uid) と実 GID (gid) が、後続のファイル システムのアクセス許可チェックで使用されます。
  • 次の条件のいずれかが満たされない場合、アクセスは拒否されます。
    • 呼び出し元プロセスの fsuid と fsgid は、ターゲット プロセスの euid、suid、uid、egid、sgid、gid とそれぞれ一致します。
    • 呼び出し元のプロセスには、ターゲット プロセスのユーザー名前空間の機能がありますCAP_SYS_PTRACE
  • ターゲット プロセスがダンプ不可に設定されており、呼び出しプロセスがターゲット プロセスのユーザー名前空間で機能を持たない場合CAP_SYS_PTRACE、アクセスは拒否されます。
  • 次の条件のいずれかが満たされない場合、カーネルの commcap LSM モジュールはアクセスを拒否します。
    • 呼び出し側プロセスとターゲット プロセスは同じユーザー名前空間に属しており、呼び出し側プロセスの機能セットは、ターゲット プロセスの許可された機能セットのスーパーセットです。
    • 呼び出し側プロセスは、ターゲット プロセスと同じユーザー名前空間内の機能を持ちますCAP_SYS_PTRACE

アイデアを活用する

上記の研究から、コンテナ脱出のための新しいアイデアを要約することができます。

上記の調査によると、この場合、コンテナがホスト上で非 root ユーザーとして実行されているプロセスの rootfs にアクセスできない理由は、コンテナ プロセスの fsuid と fsgid がそれぞれ euid、suid と同じであるためです。 、uid と対象プロセスの egid、sgid、および gid が一致しません。では、どうすればそれらを一致させることができるでしょうか? 実際、これは非常に簡単で、ホスト上で非 root ユーザーとして実行されているプロセスを見つけた後、ターゲット プロセスと同じ UID と GID を持つユーザーをコンテナー内に作成し、su コマンドを使用して次のプロセスに切り替えます。ユーザーであり、ターゲットにアクセスする権限を持っていると、プロセスは/proc/[pid]/root終了します。もちろん、ターゲットプロセスはダンプ可能である必要があることに注意してください。

次の例では、ホスト PID 名前空間を共有する Pod が最初に作成され、次にコマンドが通常のユーザーとしてホスト上で実行されますsleep 36000。最後に、Pod 内のこのプロセスを通じてホスト ファイル システムにアクセスできます。

ここに画像の説明を挿入します

同時に、補助グループの作成と参加の概念を通じて、コンテナ内のアクセス権を拡張できます。次の例では、現在のユーザーを docker グループに追加して、コンテナー内のホストの Docker エンジンにアクセスし、コンテナーのエスケープを実現します。

この例では、ホスト PID 名前空間を共有するコンテナーが最初に Docker を通じて起動され、次にホストの一般ユーザーの uid と gid が ps コマンドを通じて検索され、対応するユーザーがコンテナー内に作成されます。次に、通常のプロセスを通じて/proc/[pid]/rootホスト ディレクトリにアクセスし/run、Docker エンジンとの通信に使用されるソケット ファイルが/run/docker.sockgid 969 のユーザーによるアクセスを許可されていることを確認します。次に、コンテナ内に gid 969 のグループを作成し、前に作成したユーザーをこのグループに追加します。最後に、ホスト マシンの Docker エンジンにアクセスするための Docker クライアントをインストールします。

ここに画像の説明を挿入します

ここに画像の説明を挿入します

: コンテナーがホスト PID 名前空間を共有する場合、コンテナーには CAP_SYS_PTRACE 機能もあり、/proc/[pid]/rootシンボリック リンク経由でのエスケープが容易になります。

次の例では、共有ホスト PID 名前空間と CAP_SYS_PTRACE 機能を持つポッドがクラスターに追加されます。

その後、ポッド内で、 を/proc/1/root介してホスト ファイル システムにアクセスできます。

ここに画像の説明を挿入します
ここに画像の説明を挿入します

防御と検出

セキュリティ強化の観点から、上記の問題を回避するには、Kubernetes クラスターで次のことを行う必要があります。

  • コンテナは非 root ユーザーとして実行する必要があります。
  • ホスト PID 名前空間を任意に共有することは禁止されています。
  • コンテナに任意の権限を付与することは禁止されていますCAP_SYS_PTRACE

検知方法としては、ランタイムリアルタイム脅威検知システムのパスプレフィックスとしてコンテナ内の/proc/<pid>/rootファイルアクセス操作を監視することが考えられます。

参考文献

  • https://man7.org/linux/man-pages/man2/ptrace.2.html
  • https://man7.org/linux/man-pages/man5/proc.5.html

rnetes クラスターで行う必要があること:

  • コンテナは非 root ユーザーとして実行する必要があります。
  • ホスト PID 名前空間を任意に共有することは禁止されています。
  • コンテナに任意の権限を付与することは禁止されていますCAP_SYS_PTRACE

検知方法としては、ランタイムリアルタイム脅威検知システムのパスプレフィックスとしてコンテナ内の/proc/<pid>/rootファイルアクセス操作を監視することが考えられます。

参考文献

  • https://man7.org/linux/man-pages/man2/ptrace.2.html
  • https://man7.org/linux/man-pages/man5/proc.5.html

元のリンク: https://www.anquanke.com/post/id/290540

おすすめ

転載: blog.csdn.net/LSW1737554365/article/details/132824589