Skip to content
KitploitKITPLOIT
ツールエクスプロイトブログ
Log in
提出
ツールエクスプロイトブログ
提出

ハッキング、侵入テスト、サイバーセキュリティツールをあなたのセキュリティアーセナルに!

Kitploitはハッキング、サイバーセキュリティ、ペネトレーションテストのツールディレクトリです。最新のプロジェクトアップデートを見つけて、脆弱性の発見、システム分析、テストの自動化、セキュリティの強化を行いましょう。

フィードお問い合わせプライバシー© 2026 Kitploit

ツールディレクトリ

カテゴリ

すべてのカテゴリを見る
Loading categories
CVE-2019-13272 — CVE-2019-13272(Linuxカーネルのptrace権限昇格の脆弱性)の詳細な分析とエクスプロイト。コードウォークスルーとエクスプロイトシナリオを含みます。 | Kitploit
ツール/GitHubGitHub/datntsec/cve-2019-13272
特権昇格脆弱性分析エクスプロイト学習と教育バイナリエクスプロイト
GitHubdatntsec/cve-2019-13272

CVE-2019-13272

CVE-2019-13272(Linuxカーネルのptrace権限昇格の脆弱性)の詳細な分析とエクスプロイト。コードウォークスルーとエクスプロイトシナリオを含みます。

リポジトリを見る
75年前未レビュー

人気

すべて見る →

コミュニティで最も使われているツールを見つけましょう。

すべてのツールを探索

ツールコレクションを閲覧

すべてのツールを見る →
共有

CVE-2019-13272

PTRACE_TRACEME CVE-2019-13272 ローカル特権昇格の脆弱性分析

PTRACE_TRACEME は、2019年7月にJann Hornによって発見されたLinuxカーネルの特権昇格の脆弱性です。

脆弱性分析:

Ptrace はシステムコールであり、プロセス(tracer)が別のプロセス(tracee)の実行を監視・制御し、そのコアイメージやレジスタを検査・変更する方法を提供します。主にデバッグにおけるブレークポイントの設定やシステムコール呼び出しの追跡に使用されます。``` c 1 396 kernel/ptrace.c <<ptrace_attach>> ptrace_link(task, current); 2 469 kernel/ptrace.c <<ptrace_traceme>> ptrace_link(current, current->real_parent);

トレース関係を確立する方法は2つある:
  - プロセスが `fork` 関数を呼び出し、その子プロセスが `PTRACE_TRACEME`(カーネル内の `ptrace_traceme` 関数に相当)を呼び出してトレースを初期化する。
  - プロセスが `PTRACE_ATTACH` または `PTRACE_SEIZE`(カーネル内の `ptrace_attach` 関数に相当)を呼び出して、別のプロセスをトレースするためのトレーサを初期化する。

どちらの方法を使っても、最終的には `ptrace_link` 関数が呼び出され、トレーサとトレース間のトレース関係が確立される。
- `ptrace_attach` の場合、`ptrace_link` に渡される2つのパラメータは 'task'(トレース)と 'current'(トレーサ)である。
- `ptrace_traceme` の場合、`ptrace_link` に渡される2つのパラメータは 'current'(トレース)と 'current->real_parent'(トレーサ)である。

ここで注意すべき点は、上記の2つの方法で `ptrace_link` 関数を呼び出す際のトレーサとトレースのパラメータが何であるかである。なぜなら、脆弱性は `ptrace_link` 関数に存在するからである。``` c
static void ptrace_link(struct task_struct *child, struct task_struct *new_parent)
{
        rcu_read_lock();
        __ptrace_link(child, new_parent, __task_cred(new_parent));
        rcu_read_unlock();
}

void __ptrace_link(struct task_struct *child, struct task_struct *new_parent,
                   const struct cred *ptracer_cred)
{
        BUG_ON(!list_empty(&child->ptrace_entry));
        list_add(&child->ptrace_entry, &new_parent->ptraced); // 1. thêm chính nó vào hàng đợi 
                                                                 // ptraced của process cha
        child->parent = new_parent; // 2. Lưu địa chỉ của process cha trong con trỏ parent
        child->ptracer_cred = get_cred(ptracer_cred); // 3. Lưu ptracer_cred lại, ta cần tập trung 
                                                          // vào biến này vì lỗi nằm ở đây
}

トレース関係を設定する鍵は、tracee が tracer の cred を記録し、それを tracee の 'ptracer_cred' 変数に保存することです。

'ptracer_cred' の概念は、2016 年のパッチ ptrace: Capture the ptracer's creds not PT_PTRACE_CAP によって導入されました。 'ptracer_cred' を導入した目的は、tracee が exec を実行して setuid executable をロードする際にセキュリティチェックを行うことです。

なぜこの安全性チェックが必要なのでしょうか?

exec ファミリーはプロセスのイメージを更新できます。 setuid bit が設定された実行ファイルの場合、そのファイルが実行されると、プロセスの euid が実行ファイルの所有者の uid に変更されます。 プロセスの権限は exec を呼び出したユーザーの権限よりも高くなり、このような setuid executable を実行すると、権限昇格 (escalation) が発生します。

想像してみてください。exec を実行するプロセス自体が tracee であり、setuid executable を実行して特権を昇格した後、その tracer はいつでも tracee のレジスタやメモリを変更できます。もし tracer が低特権で、高特権の tracee を制御できる場合、tracer は tracee を通じて不正な操作を行う可能性があります。

しかし、カーネルはそのような権限を超えた動作を許可していないようです。そのため、トレース関係を設定する際、tracee は tracer の cred (すなわち ptracer_cred) を保存する必要があります。tracee が exec プロセスを実行する場合、実行されるファイルの setuid ビットが設定されているかどうかをチェックし、設定されていれば 'ptracer_cred' の権限を確認します。権限が満たされない場合、setuid ビットの実行権限 (ファイル所有者の特権) は exec の実行に使用されず、代わりに元のユーザーの権限で実行されます。

このプロセスのコード分析は以下の通りです (この記事のコード分析は v4.19-rc8 に基づいています)。``` python do_execve -> __do_execve_file -> prepare_binprm -> bprm_fill_uid -> security_bprm_set_creds ->cap_bprm_set_creds -> ptracer_capable ->selinux_bprm_set_creds ->(apparmor_bprm_set_creds) ->(smack_bprm_set_creds) ->(tomoyo_bprm_set_creds)

実行権限に関する操作は主に関数 `prepare_binprm`にあります``` c
1567 int prepare_binprm(struct linux_binprm *bprm)
1568 {
1569         int retval;
1570         loff_t pos = 0;
1571 
1572         bprm_fill_uid(bprm); // <-- fill cred của new process (xem hàm bprm_fill_uid bên dưới sẽ rõ hơn)
1573 
1574         /* fill in binprm security blob */
1575         retval = security_bprm_set_creds(bprm); // <-- kiểm tra bảo mật, để xem xét sửa đổi cred của new process     
1576         if (retval)
1577                 return retval;
1578         bprm->called_set_creds = 1;
1579 
1580         memset(bprm->buf, 0, BINPRM_BUF_SIZE);
1581         return kernel_read(bprm->file, bprm->buf, BINPRM_BUF_SIZE, &pos);
1582 }

上記のように、まず bprm_fill_uid を呼び出して新しいプロセスの cred を埋め、次に security_bprm_set_creds を呼び出してセキュリティをチェックし、必要に応じて新しい cred を修正します。``` c 1509 static void bprm_fill_uid(struct linux_binprm *bprm) 1510 { 1511 struct inode inode; 1512 unsigned int mode; 1513 kuid_t uid; 1514 kgid_t gid; 1515 1516 / 1517 * Since this can be called multiple times (via prepare_binprm), 1518 * we must clear any previous work done when setting set[ug]id 1519 * bits from any earlier bprm->file uses (for example when run 1520 * first for a setuid script then again for its interpreter). 1521 / 1522 bprm->cred->euid = current_euid(); // <--- trước tiên sẽ sử dụng euid của process hiện tại 1523 bprm->cred->egid = current_egid(); 1524 1525 if (!mnt_may_suid(bprm->file->f_path.mnt)) 1526 return; 1527 1528 if (task_no_new_privs(current)) 1529 return; 1530 1531 inode = bprm->file->f_path.dentry->d_inode; 1532 mode = READ_ONCE(inode->i_mode); 1533 if (!(mode & (S_ISUID|S_ISGID))) // <---------- nếu bit setuid/setgid của file thực thi không được set 1534 return; // , hàm sẽ return tại đây. 1535 1536 / Be careful if suid/sgid is set / 1537 inode_lock(inode); 1538 1539 / reload atomically mode/uid/gid now that lock held / 1540 mode = inode->i_mode; 1541 uid = inode->i_uid; // <---- nếu S_ISUID được set,sử dụng i_uid của file 1542 gid = inode->i_gid; 1543 inode_unlock(inode); 1544 1545 / We ignore suid/sgid if there are no mappings for them in the ns */ 1546 if (!kuid_has_mapping(bprm->cred->user_ns, uid) || 1547 !kgid_has_mapping(bprm->cred->user_ns, gid)) 1548 return; 1549 1550 if (mode & S_ISUID) { 1551 bprm->per_clear |= PER_CLEAR_ON_SETID; 1552 bprm->cred->euid = uid; // <------ sử dụng uid của file như là euid của new process 1553 } 1554 1555 if ((mode & (S_ISGID | S_IXGRP)) == (S_ISGID | S_IXGRP)) { 1556 bprm->per_clear |= PER_CLEAR_ON_SETID; 1557 bprm->cred->egid = gid; 1558 } 1559 }

Nhìn vào 2 dòng code sau đối với đoạn code ở trên:
- Dòng 1522, gán euid hiện tại của process vào new euid, vì vậy hầu hết các process thực thi đều thực thi dưới quyền ban đầu.
- Dòng 1552, nếu bit suid được set, gán uid của chủ sở hữu file thực thi vào new uid. Có thể hiểu nó giống như việc setuid. New euid trở thành uid của chủ sở hữu file thực thi, nếu chủ sở hữu là một user đặc quyền, leo thang đặc quyền sẽ diễn ra ở đây.

Tuy nhiên, euid ở đây vẫn chưa phải là kết quả cuối cùng, ta cần xem xẻt hàm `security_bprm_set_creds` để biết thêm về việc kiểm tra bảo mật.

Hàm `security_bprm_set_creds` gọi [LSM](https://en.wikipedia.org/wiki/Linux_Security_Modules) framework

Trên phiên bản kernel mà tôi phân tích, có đến 5 điểm hook lsm frameworks thực hiện kiểm tra bảo mật của 'bprm_set_creds'. Các hàm kiểm tra như sau:``` python
cap_bprm_set_creds
selinux_bprm_set_creds
apparmor_bprm_set_creds
smack_bprm_set_creds
tomoyo_bprm_set_creds

ここで実行されるフック関数は、特定のカーネル構成に依存します。理論的には、すべてのLSMフレームワークが有効になっている場合、上記のすべてのフック関数が実装され、'bprm_set_creds' をチェックします。

私の分析環境では、cap_bprm_set_creds と selinux_bprm_set_creds のフック関数のみが実行されました。

ツールをダウンロード