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

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

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

피드문의개인정보© 2026 Kitploit

도구 디렉토리

카테고리

모든 카테고리 보기
Loading categories
CVE-2019-13272 — CVE-2019-13272에 대한 심층 분석 및 익스플로잇으로, Linux 커널 ptrace 권한 상승 취약점입니다. 코드 분석 및 익스플로잇 시나리오를 포함합니다. | Kitploit
도구/GitHubGitHub/datntsec/cve-2019-13272
Privilege EscalationVulnerability AnalysisExploitationLearning & EducationBinary Exploitation
GitHubdatntsec/cve-2019-13272

CVE-2019-13272

CVE-2019-13272에 대한 심층 분석 및 익스플로잇으로, Linux 커널 ptrace 권한 상승 취약점입니다. 코드 분석 및 익스플로잇 시나리오를 포함합니다.

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

인기

모두 보기 →

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

모든 도구 탐색

도구 컬렉션을 둘러보세요

모든 도구 보기 →
공유

CVE-2019-13272

PTRACE_TRACEME CVE-2019-13272 로컬 권한 상승 취약점 분석

PTRACE_TRACEME는 Jann Horn이 2019년 7월에 발견한 Linux Kernel의 권한 상승 취약점입니다.

취약점 분석:

Ptrace는 system call로, 하나의 프로세스(tracer)가 다른 프로세스(tracee)의 실행 과정을 관찰하고 제어할 수 있게 해주는 방법을 제공하며, 코어 이미지와 레지스터를 검사하고 변경할 수 있게 합니다. 주로 디버깅 시 중단점(break point)을 설정하고 system call 호출 과정을 추적하는 데 사용됩니다.``` c 1 396 kernel/ptrace.c <<ptrace_attach>> ptrace_link(task, current); 2 469 kernel/ptrace.c <<ptrace_traceme>> ptrace_link(current, current->real_parent);

트레이스 관계를 설정하는 두 가지 방법이 있습니다:
  - 프로세스는 fork 함수를 호출하고, 그 자식 프로세스는 `PTRACE_TRACEME`(커널의 `ptrace_traceme` 함수)를 호출하여 tracee를 초기화합니다.
  - 프로세스는 `PTRACE_ATTACH` 또는 `PTRACE_SEIZE`(커널의 `ptrace_attach` 함수)를 호출하여 다른 프로세스를 추적하기 위한 tracer를 초기화합니다.

어떤 방법을 사용하든 결국 `ptrace_link` 함수가 호출되어 tracer와 tracee 사이의 트레이스 관계를 설정합니다.
- `ptrace_attach`의 경우 `ptrace_link`에 전달되는 두 매개변수는 'task'(tracee)와 'current'(tracer)입니다.
- `ptrace_traceme`의 경우 `ptrace_link`에 전달되는 두 매개변수는 'current'(tracee)와 'current->real_parent'(tracer)입니다.

여기서 우리는 위 두 방식에서 `ptrace_link` 함수를 호출할 때 tracer와 tracee에 전달되는 두 매개변수가 무엇인지 주의해야 합니다. 취약점은 `ptrace_link` 함수에 있기 때문입니다.``` c
static void ptrace_link(struct task_struct *child, struct task_struct *new_parent)
{
        rcu_read_lock();
        __ptrace_link(child, new_parent, __task_cred(new_parent));
        rcu_read_unlock();
}

void __ptrace_link(struct task_struct *child, struct task_struct *new_parent,
                   const struct cred *ptracer_cred)
{
        BUG_ON(!list_empty(&child->ptrace_entry));
        list_add(&child->ptrace_entry, &new_parent->ptraced); // 1. thêm chính nó vào hàng đợi 
                                                                 // ptraced của process cha
        child->parent = new_parent; // 2. Lưu địa chỉ của process cha trong con trỏ parent
        child->ptracer_cred = get_cred(ptracer_cred); // 3. Lưu ptracer_cred lại, ta cần tập trung 
                                                          // vào biến này vì lỗi nằm ở đây
}

trace relationship을 설정하는 핵심은 tracee가 tracer의 cred를 기록하여 이를 tracee의 'ptracer_cred' 변수에 저장한다는 점입니다.

'ptracer_cred' 개념은 2016년 패치인 ptrace: Capture the ptracer's creds not PT_PTRACE_CAP에 의해 도입되었습니다. 'ptracer_cred'를 도입한 목적은 tracee가 setuid executable을 로드하기 위해 exec를 실행할 때 보안 검사를 수행하기 위해서입니다.

이 보안 검사가 왜 필요할까요?

exec 계열은 프로세스의 image를 업데이트할 수 있습니다. 실행 파일의 setuid bit가 설정되어 있으면, 해당 실행 파일이 실행될 때 프로세스의 euid가 실행 파일 소유자의 uid로 변경됩니다. 프로세스의 권한은 exec를 호출한 사용자의 권한보다 높아지며, 이러한 setuid executable을 실행하면 권한 상승(escalation) 효과가 발생합니다.

상상해 보겠습니다. exec를 실행하는 프로세스 자체가 tracee라면, tracee가 setuid executable을 실행하여 권한을 상승시킨 후에도 tracer는 언제든지 tracee의 레지스터와 메모리를 수정할 수 있습니다. 그리고 낮은 권한의 tracer가 높은 권한의 tracee를 제어할 수 있다면, tracer는 tracee를 통해 권한 없는 작업을 수행할 수 있습니다.

하지만 커널에서는 이처럼 권한을 초과하는 행위를 허용하지 않는 것으로 보입니다. 따라서 trace relationship을 설정할 때 tracee는 tracer의 cred(즉, ptracer_cred)를 저장해야 하며, tracee가 exec 프로세스를 실행하면 실행되는 실행 파일의 setuid bit가 설정되어 있는지 확인합니다. 설정되어 있다면 'ptracer_cred'의 권한을 검사합니다. 권한이 충족되지 않으면 setuid bit의 실행 권한(파일 소유자의 특권)으로 exec를 실행하지 않고 원래 사용자의 권한으로 실행합니다.

이 프로세스의 코드 분석은 다음과 같습니다 (이 글의 코드 분석은 v4.19-rc8을 기준으로 합니다).``` python do_execve -> __do_execve_file -> prepare_binprm -> bprm_fill_uid -> security_bprm_set_creds ->cap_bprm_set_creds -> ptracer_capable ->selinux_bprm_set_creds ->(apparmor_bprm_set_creds) ->(smack_bprm_set_creds) ->(tomoyo_bprm_set_creds)

실행 권한과 관련된 작업은 주로 `prepare_binprm` 함수에 있습니다.``` c
1567 int prepare_binprm(struct linux_binprm *bprm)
1568 {
1569         int retval;
1570         loff_t pos = 0;
1571 
1572         bprm_fill_uid(bprm); // <-- fill cred của new process (xem hàm bprm_fill_uid bên dưới sẽ rõ hơn)
1573 
1574         /* fill in binprm security blob */
1575         retval = security_bprm_set_creds(bprm); // <-- kiểm tra bảo mật, để xem xét sửa đổi cred của new process     
1576         if (retval)
1577                 return retval;
1578         bprm->called_set_creds = 1;
1579 
1580         memset(bprm->buf, 0, BINPRM_BUF_SIZE);
1581         return kernel_read(bprm->file, bprm->buf, BINPRM_BUF_SIZE, &pos);
1582 }

위와 같이, 먼저 bprm_fill_uid를 호출하여 새 프로세스의 cred를 채운 다음, security_bprm_set_creds를 호출하여 보안을 확인하고 필요 시 새 cred를 수정한다.``` c 1509 static void bprm_fill_uid(struct linux_binprm *bprm) 1510 { 1511 struct inode inode; 1512 unsigned int mode; 1513 kuid_t uid; 1514 kgid_t gid; 1515 1516 / 1517 * Since this can be called multiple times (via prepare_binprm), 1518 * we must clear any previous work done when setting set[ug]id 1519 * bits from any earlier bprm->file uses (for example when run 1520 * first for a setuid script then again for its interpreter). 1521 / 1522 bprm->cred->euid = current_euid(); // <--- trước tiên sẽ sử dụng euid của process hiện tại 1523 bprm->cred->egid = current_egid(); 1524 1525 if (!mnt_may_suid(bprm->file->f_path.mnt)) 1526 return; 1527 1528 if (task_no_new_privs(current)) 1529 return; 1530 1531 inode = bprm->file->f_path.dentry->d_inode; 1532 mode = READ_ONCE(inode->i_mode); 1533 if (!(mode & (S_ISUID|S_ISGID))) // <---------- nếu bit setuid/setgid của file thực thi không được set 1534 return; // , hàm sẽ return tại đây. 1535 1536 / Be careful if suid/sgid is set / 1537 inode_lock(inode); 1538 1539 / reload atomically mode/uid/gid now that lock held / 1540 mode = inode->i_mode; 1541 uid = inode->i_uid; // <---- nếu S_ISUID được set,sử dụng i_uid của file 1542 gid = inode->i_gid; 1543 inode_unlock(inode); 1544 1545 / We ignore suid/sgid if there are no mappings for them in the ns */ 1546 if (!kuid_has_mapping(bprm->cred->user_ns, uid) || 1547 !kgid_has_mapping(bprm->cred->user_ns, gid)) 1548 return; 1549 1550 if (mode & S_ISUID) { 1551 bprm->per_clear |= PER_CLEAR_ON_SETID; 1552 bprm->cred->euid = uid; // <------ sử dụng uid của file như là euid của new process 1553 } 1554 1555 if ((mode & (S_ISGID | S_IXGRP)) == (S_ISGID | S_IXGRP)) { 1556 bprm->per_clear |= PER_CLEAR_ON_SETID; 1557 bprm->cred->egid = gid; 1558 } 1559 }

위 코드에 대한 다음 2줄의 코드를 살펴보겠습니다:
- 1522행: 프로세스의 현재 euid를 new euid에 할당하므로, 실행되는 대부분의 프로세스는 원래 권한으로 실행됩니다.
- 1552행: suid 비트가 설정된 경우, 실행 파일 소유자의 uid를 new uid에 할당합니다. 이는 setuid와 유사한 것으로 이해할 수 있습니다. new euid는 실행 파일 소유자의 uid가 되며, 소유자가 특권 사용자라면 여기서 권한 상승이 발생합니다.

그러나 여기서의 euid는 아직 최종 결과가 아닙니다. 보안 검사에 대해 더 알아보려면 `security_bprm_set_creds` 함수를 확인해야 합니다.

`security_bprm_set_creds` 함수는 [LSM](https://en.wikipedia.org/wiki/Linux_Security_Modules) 프레임워크를 호출합니다.

제가 분석한 커널 버전에는 'bprm_set_creds'의 보안 검사를 수행하는 LSM 프레임워크 hook 지점이 최대 5개 있습니다. 검사 함수는 다음과 같습니다:``` python
cap_bprm_set_creds
selinux_bprm_set_creds
apparmor_bprm_set_creds
smack_bprm_set_creds
tomoyo_bprm_set_creds

여기서 실행될 hook 함수는 각 커널의 특정 구성에 따라 달라진다. 이론적으로 모든 LSM 프레임워크가 활성화되어 있다면, 위에서 언급한 모든 hook 함수가 'bprm_set_creds'를 검사하도록 구현될 것이다.

내 분석 환경에서는 cap_bprm_set_creds와 selinux_bprm_set_creds hook 함수만 실행되었다.

도구 다운로드