
Analisi approfondita ed exploit per CVE-2019-13272, una vulnerabilità di escalation dei privilegi ptrace del kernel Linux. Include una spiegazione del codice e uno scenario di sfruttamento.
PTRACE_TRACEME è una falla di escalation dei privilegi nel kernel Linux scoperta da Jann Horn nel luglio 2019.
Ptrace è una system call che fornisce un metodo per consentire a un processo (tracer) di osservare e controllare l'esecuzione di un altro processo (tracee), esaminare e modificare l'immagine core e i suoi registri, utilizzato principalmente per impostare breakpoint nel debug e tracciare le chiamate di 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);
Ci sono due modi per stabilire una relazione di trace:
- Un processo chiama la funzione fork e il suo processo figlio chiama `PTRACE_TRACEME` (corrispondente alla funzione `ptrace_traceme` nel kernel) per inizializzare il tracee.
- Un processo chiama `PTRACE_ATTACH` o `PTRACE_SEIZE` (corrispondente alla funzione `ptrace_attach` nel kernel) per inizializzare un tracer per tracciare un altro processo.
Indipendentemente dal metodo utilizzato, la funzione `ptrace_link` viene chiamata alla fine per stabilire la relazione di trace tra tracer e tracee.
- I due parametri passati a `ptrace_link` per `ptrace_attach` sono 'task' (tracee) e 'current' (tracer)
- I due parametri passati a `ptrace_link` per `ptrace_traceme` sono 'current' (tracee) e 'current->real_parent' (tracer)
Qui, dobbiamo notare quali sono i due parametri passati di tracer e tracee nei due metodi sopra quando si chiama la funzione `ptrace_link`, poiché la vulnerabilità risiede nella funzione `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
}
Il punto chiave per stabilire una relazione di trace è che il tracee registra le credenziali del tracer e le memorizza nella variabile 'ptracer_cred' del tracee.
Il concetto di 'ptracer_cred' è stato introdotto da una patch nel 2016, ptrace: Capture the ptracer's creds not PT_PTRACE_CAP. Lo scopo dell'introduzione di 'ptracer_cred' è eseguire un controllo di sicurezza quando il tracee esegue exec per caricare un setuid executable.
Perché abbiamo bisogno di questo controllo di sicurezza?
La famiglia di exec può aggiornare l'immagine del processo. Se il setuid bit del file eseguibile è impostato, quando il file eseguibile viene eseguito, l'euid del processo viene modificato nell'uid del proprietario del file eseguibile. I privilegi del processo sono superiori a quelli dell'utente che invoca exec, e l'esecuzione di questo tipo di setuid executable ha un effetto di escalation (escalation).
Immagina, se il processo stesso che esegue exec è un tracee, dopo aver eseguito un setuid executable per aumentare i privilegi, il suo tracer può modificare i registri e la memoria del tracee in qualsiasi momento, e se il tracer ha privilegi bassi può controllare un tracee con privilegi elevati, il tracer potrebbe compiere operazioni non autorizzate attraverso il tracee.
Tuttavia, nel kernel, sembra che tali comportamenti oltre le autorizzazioni non siano permessi, quindi quando si stabilisce una relazione di trace, il tracee deve salvare le cred del tracer (cioè ptracer_cred). Se il tracee esegue un processo exec, verificherà se il bit setuid del file eseguibile eseguito è impostato; in caso affermativo, esaminerà i permessi di 'ptracer_cred'. Se i permessi non sono soddisfatti, il privilegio di esecuzione del bit setuid (privilegio del proprietario del file) non verrà utilizzato per eseguire exec, ma verrà eseguito con i permessi dell'utente originale.
L'analisi del codice di questo processo è la seguente (l'analisi del codice di questo articolo si basa su 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)