
Analyse approfondie et exploit pour CVE-2019-13272, une vulnérabilité d'élévation de privilèges ptrace du noyau Linux. Comprend une présentation du code et un scénario d'exploitation.
PTRACE_TRACEME est une vulnérabilité d'escalade de privilèges dans le noyau Linux découverte par Jann Horn en juillet 2019.
Ptrace est un appel système, il fournit une méthode permettant à un processus (tracer) d'observer et de contrôler l'exécution d'un autre processus (tracee), d'examiner et de modifier l'image mémoire et ses registres, principalement utilisé pour placer des points d'arrêt dans le débogage et suivre l'appel des appels système.``` 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);
Có hai cách để thiết lập một trace relationship:
- Process sẽ gọi hàm fork và process con của nó sẽ gọi `PTRACE_TRACEME` (tương ứng hàm `ptrace_traceme` trong kernel) để khởi tạo tracee.
- Process gọi `PTRACE_ATTACH` hoặc `PTRACE_SEIZE` (tương ứng hàm `ptrace_attach` trong kernel) để khởi tạo một tracer để trace process khác.
Dù sử dùng cách nào đi nữa thì hàm `ptrace_link` vẫn sẽ được gọi cuối cùng để thiết lập trace relationship giữa tracer và tracee
- Hai tham số truyền vào `ptrace_link` đối với `ptrace_attach` là 'task' (tracee) và 'current' (tracer)
- Hai tham số truyền vào `ptrace_link` đối với `ptrace_traceme` là 'current' (tracee) và 'current->real_parent' (tracer)
Ở đây, ta cần phải lưu ý hai tham số truyền vào của tracer và tracee là gì ở 2 cách trên khi gọi hàm `ptrace_link`, vì lổ hỏng sẽ nằm ở hàm `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
}
Mấu chốt để thiết lập trace relationship là tracee sẽ ghi lại cred của tracer và lưu nó trong biến 'ptracer_cred' của tracee.
Khái niệm về 'ptracer_cred' đã được giới thiệu bởi một bản vá vào năm 2016, ptrace: Capture the ptracer's creds not PT_PTRACE_CAP. Mục đích của việc giới thiệu 'ptracer_cred' là để thực hiện kiểm tra bảo mật khi tracee thực thi exec để load setuid executable
Tại sao chúng ta cần kiểm tra sự an toàn này?
Family của exec có thể cập nhật image của process. Nếu setuid bit của file thực thi được set, khi file thực thi được chạy, euid của process sẽ được sửa đổi thành uid của chủ sở hữu file thực thi. Quyền của process cao hơn quyền của người dùng gọi exec và việc chạy loại setuid executable này sẽ có tác động leo thang (escalation).
Hãy tưởng tượng, nếu bản thân process thực thi exec là một tracee, sau khi nó thực hiện setuid executable để leo thang đặc quyền, tracer của nó có thể sửa đổi các thanh ghi và bộ nhớ của nó (tracee) bất kỳ lúc nào, và nếu tracer có đặc quyền thấp có thể kiểm soát tracee có đặc quyền cao, tracer có thể thực hiện các hoạt động trái phép thông qua tracee.
Tuy nhiên, trong kernel, dường như không cho phép tồn tại những hành vi vượt quá thẩm quyền như vậy, vì vậy khi thiết lập trace relationships, tracee cần lưu cred của tracer (tức là ptracer_cred), nếu tracee thực thi một exec process, nó sẽ kiểm tra xem setuid bit của file thực thi được chạy có được set hay không, nếu có, nó sẽ xem xét quyền của 'ptracer_cred'. Nếu quyền không thỏa, quyền thực thi của setuid bit (đặc quyền của chủ sở hữu file) sẽ không được dùng để thực thi exec mà sẽ được thực thi với quyền của người dùng ban đầu.
Phân tích code của process này như sau (phân tích code của bài viết này dựa trên 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)