Skip to content
KitploitKITPLOIT
도구익스플로잇블로그
Log in
제출
도구익스플로잇블로그
제출

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

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

피드문의개인정보© 2026 Kitploit

도구 디렉토리

카테고리

모든 카테고리 보기
Loading categories
CVE-2017-5123 — PoC CVE-2017-5123 - LPE - SMEP/SMAP 우회. KASLR 없음 | Kitploit
도구/GitHubGitHub/c3r34lk1ll3r/cve-2017-5123
Privilege EscalationExploitationLearning & EducationBinary ExploitationLabs & Practice
GitHubc3r34lk1ll3r/cve-2017-5123

CVE-2017-5123

PoC CVE-2017-5123 - LPE - SMEP/SMAP 우회. KASLR 없음

저장소 보기
334116년 전Kitploit 검토 완료

인기

모두 보기 →

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

모든 도구 탐색

도구 컬렉션을 둘러보세요

모든 도구 보기 →
공유

CVE-2017-5123

PoC CVE-2017-5123 - LPE - SMEP/SMAP 우회. KASLR 없음

업스트림 커널의 waitid 구현은 정보 결과를 복사할 대상 목적지를 제한하지 않았습니다. 이로 인해 로컬 사용자가 보호된 커널 메모리에 쓸 수 있어 권한 상승으로 이어질 수 있습니다.

소개

이 간단한 분석에서는 root 권한을 얻을 수 있는 커널 취약점을 분석하겠습니다.

이 문서는 네 부분으로 구성됩니다:

  1. VM 설정;
  2. 취약점 분석;
  3. 익스플로잇;
  4. PoC.

이 CVE를 익스플로잇하는 더 좋은 방법이 많다는 점을 지적하고 싶습니다(실제로 이것은 커널 학습을 위한 _PoC_일 뿐이며 _실전_에서는 사용할 수 없습니다). 하지만 이 방법론이 커널 익스플로잇 입문에 유용할 수 있다고 생각합니다.

VM 설정

커널 빌드

이 취약점은 _4c48abe91be0_에서 도입되었으므로 해당 버전의 커널을 빌드해야 합니다.

이것은 오래된 버전이고 코드에 패치가 필요하기 때문에 약간 까다로울 수 있습니다. 이미 패치된 커널 코드가 있는 저장소와 .config 파일을 만들었으므로 _클론하고 빌드_할 수 있습니다.

git clone https://github.com/c3r34lk1ll3r/kernel_mirror.git
cd kernel_mirror
git checkout origin/modified_v4.14
wget https://gist.githubusercontent.com/c3r34lk1ll3r/c9c34ae86140cc7a24d0d90141686ee8/raw/52431b577a71e3fe8f89d6ce355ce9c1c54c53b6/.config
make -j 8 --output-sync=recurse

참고: 이 커널은 virtio 드라이버로 빌드되므로 VM과 파일을 공유하기 위해 _virtio 디스크_를 사용할 수 있습니다.

Rootfs 설정

이제 초기 _rootfs_를 생성합니다:

qemu-img create -f raw hda.raw 10G
# Format the disk to ext4
mkfs.ext4 ./hda.raw 
# Make a mountpoint for the image
mkdir /tmp/mount1
# Mount the disk
sudo mount -o loop ./hda.raw /tmp/mount1

그런 다음 기본 Linux 배포판을 설치해야 합니다. 예를 들어 pacstrap 또는 debootstrap을 사용합니다.

sudo pacstrap /tmp/mount1 base base-devel vim

마지막으로 시스템을 수정합니다:

# Add a 'test' user
echo 'test:x:1000:1000::/home/test:/bin/bash' | sudo tee -a /tmp/mount1/etc/passwd
# without password
echo 'test::14871::::::' | sudo tee -a /tmp/mount1/etc/shadow 
# we can mount a virtio disk in order to share files between host and guest
echo '/transient /home/test/shared 9p trans=virtio,version=9p2000.L,rw,user,exec 0 0' | sudo tee -a /tmp/mount1/etc/fstab
sudo mkdir -p /tmp/mount1/home/test/shared 
# It is usefull to have sudo permission
echo '%wheel ALL=(ALL) NOPASSWD: ALL' | sudo tee -a /tmp/mount1/etc/sudoers
echo 'wheel:x:998:test' | sudo tee -a /tmp/mount1/etc/group

sudo chown -R 1000:1000 /tmp/mount1/home/test
sudo umount /tmp/mount1

모든 것이 정상이면 _qemu_로 테스트 시스템을 시험해볼 수 있습니다:

qemu-system-x86_64 \
    -kernel ./kernel_mirror/arch/x86_64/boot/bzImage \
    -hda ./hda.raw \
    -m 4G \
    -cpu "Skylake-Client-IBRS,ss=on,vmx=on,hypervisor=on,tsc-adjust=on,clflushopt=on,umip=on,md-clear=on,stibp=on,arch-capabilities=on,ssbd=on,xsaves=on,pdpe1gb=on,ibpb=on,amd-ssbd=on,skip-l1dfl-vmentry=on,hle=off,rtm=off" \
    -smp 4 \
    -vga virtio \
    -enable-kvm \
    -nographic \
    -machine type=q35,accel=kvm \
    -virtfs "fsdriver=local,id=fs.1,path=./trans_fs,security_model=mapped,writeout=immediate,mount_tag=/transient" \
    -append "root=/dev/sda rw noquiet nokaslr console=ttyS0 loglevel=5" \
    -chardev "vc,id=vc.0,cols=1920,rows=1080" \
    -net "user,hostfwd=tcp::10022-:22" \
    -net "nic" \
    -s

취약점

CVE 설명에 따르면 waitid 시스템 호출 중에 제한되지 않은 쓰기 작업이 있습니다.

kernel/exit.c를 열고 코드를 봅시다:

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();
    unsafe_put_user(signo, &infop->si_signo, Efault);
    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();
    return err;
Efault:
    user_access_end();
    return -EFAULT;
}

이 함수는 매우 간단합니다. 몇 가지 검사 후 unsafe_put_user(...)에 대한 여러 호출이 있고 함수가 반환됩니다.

이 함수의 주요 부분은 unsafe_put_user(...) 함수로 구성되어 있으므로 (arch/x86/include/asm/uaccess.h)로 이동합니다:

/*
 * The "unsafe" user accesses aren't really "unsafe", but the naming
 * is a big fat warning: you have to not only do the access_ok()
 * checking before using them, but you have to surround them with the
 * user_access_begin/end() pair.
 */
#define user_access_begin()	__uaccess_begin()
#define user_access_end()	__uaccess_end()

#define unsafe_put_user(x, ptr, err_label)					\
do {										\
    int __pu_err;								\
    __typeof__(*(ptr)) __pu_val = (x);					\
    __put_user_size(__pu_val, (ptr), sizeof(*(ptr)), __pu_err, -EFAULT);	\
    if (unlikely(__pu_err)) goto err_label;					\
} while (0)

#define unsafe_get_user(x, ptr, err_label)					\
do {										\
    int __gu_err;								\  
    __inttype(*(ptr)) __gu_val;						\
    __get_user_size(__gu_val, (ptr), sizeof(*(ptr)), __gu_err, -EFAULT);	\
    (x) = (__force __typeof__(*(ptr)))__gu_val;				\
    if (unlikely(__gu_err)) goto err_label;					\
} while (0)

주석에는 크고 뚱뚱한 경고가 있습니다: unsafe_put/get_user를 사용하려면 먼저 access_ok()를 호출하고 user_access_begin/end()로 감싸야 합니다.

이전 코드(waitid)를 살펴보면 access_ok()가 호출되지 않았으므로 시스템 호출이 이 _경고_를 _위반_합니다.

하지만 그 매크로들은 무엇일까요?

SMAP/SMEP

_SMAP_와 _SMEP_는 익스플로잇 작성을 어렵게 하기 위해 커널에 도입된 두 가지 보안 기능입니다. 이 기능들은 CPU에 의해 강제된다는 점에 유의하십시오.

_SMEP_는 CPU가 수퍼바이저 모드에 있을 때 사용자 공간 코드를 실행하는 것을 방지합니다. 반면 _SMAP_는 사용자 메모리에 대한 읽기/쓰기 액세스를 차단합니다.

커널은 사용자 메모리에 데이터를 쓰거나 읽어야 하며, 이는 두 가지 방법으로 수행할 수 있습니다:

  1. 커널 공간에서 메모리를 복사할 수 있는 함수(예: copy_from_user)가 있습니다.
  2. 일시적으로 _SMAP_를 비활성화합니다.

unsafe_put_user의 정의에서 볼 수 있듯이 이 함수는 ptr이 가리키는 메모리에 x의 값만 복사합니다(오류가 있으면 err_label로 점프). 우리는 SMAP 때문에 커널이 사용자 공간에 액세스할 수 없다고 말했으며, 이것이 바로 이러한 함수들이 user_access_begin/end()로 감싸져야 하는 이유입니다.

#define __uaccess_begin() stac()
#define __uaccess_end()   clac()

기본적으로 이 두 매크로는 _SMAP_를 활성화/비활성화합니다.

이전의 "경고"는 access_ok 함수도 언급합니다:

/**
 * access_ok: - Checks if a user space pointer is valid
 * @type: Type of access: %VERIFY_READ or %VERIFY_WRITE.  Note that
 *        %VERIFY_WRITE is a superset of %VERIFY_READ - if it is safe
 *        to write to a block, it is always safe to read from it.
 * @addr: User space pointer to start of block to check
 * @size: Size of block to check
 *
 * Context: User context only. This function may sleep if pagefaults are
 *          enabled.
 *
 * Checks if a pointer to a block of memory in user space is valid.
 *
 * Returns true (nonzero) if the memory block may be valid, false (zero)
 * if it is definitely invalid.
 *
 * Note that, depending on architecture, this function probably just
 * checks that the pointer is in the user space range - after calling
 * this function, memory access functions may still return -EFAULT.
 */
#define access_ok(type, addr, size)					\
({									\
	WARN_ON_IN_IRQ();						\
	likely(!__range_not_ok(addr, size, user_addr_max()));		\
})

여기서 주석은 자명합니다: 이 매크로는 포인터가 유효한 사용자 공간 포인터인지 확인합니다.

임의 쓰기

waitid 코드를 다시 살펴보겠습니다:

	user_access_begin();
	unsafe_put_user(signo, &infop->si_signo, Efault);
	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();

이미 짐작하셨겠지만, access_ok()의 부재로 인해 infop 포인터가 공격자에 의해 완전히 제어되므로 메모리 어디에나 _임의 쓰기_가 가능합니다.

도구 다운로드