
Análise aprofundada e exploit para CVE-2019-13272, uma vulnerabilidade de escalada de privilégios ptrace no kernel Linux. Inclui explicação do código e cenário de exploração.
PTRACE_TRACEME é uma vulnerabilidade de escalada de privilégios no kernel Linux descoberta por Jann Horn em julho de 2019.
Ptrace é uma chamada de sistema, que fornece um método para permitir que um processo (tracer) possa observar e controlar a execução de outro processo (tracee), inspecionar e alterar sua imagem de núcleo e registradores, usado principalmente para definir pontos de interrupção em depuração e rastrear chamadas de sistema.``` 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);
Existem duas maneiras de configurar uma relação de trace:
- O processo chama a função fork e seu processo filho chama `PTRACE_TRACEME` (correspondente à função `ptrace_traceme` no kernel) para inicializar o tracee.
- O processo chama `PTRACE_ATTACH` ou `PTRACE_SEIZE` (correspondente à função `ptrace_attach` no kernel) para inicializar um tracer para rastrear outro processo.
Independentemente do método utilizado, a função `ptrace_link` ainda será chamada por último para configurar a relação de trace entre o tracer e o tracee.
- Os dois parâmetros passados para `ptrace_link` no caso de `ptrace_attach` são 'task' (tracee) e 'current' (tracer)
- Os dois parâmetros passados para `ptrace_link` no caso de `ptrace_traceme` são 'current' (tracee) e 'current->real_parent' (tracer)
Aqui, precisamos notar quais são os dois parâmetros passados para o tracer e tracee nos 2 métodos acima ao chamar a função `ptrace_link`, porque a vulnerabilidade estará na função `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
}
A chave para estabelecer a relação de trace é que o tracee registra as credenciais do tracer e as armazena na variável 'ptracer_cred' do tracee.
O conceito de 'ptracer_cred' foi introduzido por um patch em 2016, ptrace: Capture the ptracer's creds not PT_PTRACE_CAP. O propósito da introdução de 'ptracer_cred' é realizar uma verificação de segurança quando o tracee executa exec para carregar um setuid executable
Por que precisamos dessa verificação de segurança?
A família exec pode atualizar a imagem do processo. Se o setuid bit do arquivo executável estiver definido, quando o arquivo executável for executado, o euid do processo será modificado para o uid do proprietário do arquivo executável. Os privilégios do processo são maiores que os do usuário que chamou exec e a execução desse tipo de setuid executable terá um efeito de escalada (escalation).
Imagine, se o próprio processo que executa exec é um tracee, após ele executar um setuid executable para escalar privilégios, seu tracer pode modificar os registradores e a memória do tracee a qualquer momento, e se o tracer com privilégios baixos puder controlar o tracee com privilégios altos, o tracer pode realizar operações não autorizadas através do tracee.
No entanto, no kernel, parece não permitir que tais comportamentos que excedem a autoridade existam, então ao estabelecer as relações de trace, o tracee precisa armazenar as credenciais do tracer (ou seja, ptracer_cred), se o tracee executar um processo exec, ele verificará se o bit setuid do arquivo executável executado está definido; se estiver, ele considerará os privilégios de 'ptracer_cred'. Se os privilégios não forem suficientes, os privilégios de execução do bit setuid (privilégios do proprietário do arquivo) não serão usados para executar exec, mas serão executados com os privilégios do usuário original.
A análise do código desse processo é a seguinte (a análise do código deste artigo é baseada no 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)