Sequoia: Linux 파일 시스템 계층의 깊은 루트(CVE-2021-33909)

Qualys Security Advisory Sequoia: Linux 파일 시스템 계층의 깊은 루트(CVE-2021-33909)=========================================================================내용==========================================================================요약 분석 악용 개요 악용 세부 정보 완화 승인 타임라인==========================================================================요약==========================================================================우리는 Linux 커널의 파일 시스템 계층에서 size_t-to-int 변환 취약점을 발견했습니다: 깊은 디렉토리 구조 생성, 마운트 및 삭제 총 경로 길이가 1GB를 초과하는 권한 없는 로컬 공격자는 10바이트 문자열 “//deleted”를 vmalloc()ated 커널 버퍼의 시작 부분 아래 정확히 -2GB-10B 오프셋에 쓸 수 있습니다. 우리는 이 통제되지 않은 범위 밖의 쓰기를 성공적으로 악용했으며 Ubuntu 20.04, Ubuntu 20.10, Ubuntu 21.04, Debian 11 및 Fedora 34 Workstation의 기본 설치에 대한 전체 루트 권한을 얻었습니다. 다른 Linux 배포판은 확실히 취약하고 악용될 수 있습니다. 우리의 익스플로잇에는 약 5GB의 메모리와 1M inode가 필요합니다. 우리는 가까운 장래에 그것을 게시할 것입니다. 이 권고에 기본 개념 증명(크래셔)이 첨부되어 있으며 https://www.qualys.com/research/security-advisories/에서 확인할 수 있습니다. 우리가 아는 한, 이 취약점은 2014년 7월에 도입되었습니다( Linux 3.16) 커밋 058504ed(“fs/seq_file: vmalloc 할당으로 대체”). ===========================================================================분석=============================================================================Linux 커널의 seq_file 인터페이스 레코드 시퀀스를 포함하는 가상 파일을 생성합니다(예: /proc의 많은 파일은 seq_files이고 레코드는 일반적으로 줄임). 각 레코드는 seq_file 버퍼에 맞아야 하므로 242행에서 크기를 두 배로 늘려 필요에 따라 확장됩니다(seq_buf_alloc()은 kvmalloc() 주위의 간단한 래퍼임): ————- ————————————————– ——— 168 ssize_t seq_read_iter(구조체 kiocb *iocb, 구조 iov_iter *iter) 169 { 170 struct seq_file *m=iocb->ki_filp->private_data; … 205 /버퍼가 없으면 버퍼를 잡습니다 */ 206 if (!m->buf) { 207 m->buf=seq_buf_alloc(m->size=PAGE_SIZE); … 210 } … 220 // 버퍼에서 비어 있지 않은 레코드 가져오기 … 223 while (1) { … 227 err=m->op->show(m, p); … 236 if (!seq_has_overflowed(m)) // 얻었습니다 237 goto Fill; 238 // 더 큰 버퍼가 필요합니다 … 240 kvfree(m->buf); … 242 m->buf=seq_buf_alloc(m->size size는 size_t(x86_64에서 부호 없는 64비트 정수)이며 이 곱셈이 정수 m->size를 오버플로하기 훨씬 전에 시스템의 메모리가 부족합니다. 불행히도, 이 size_t는 size_t가 아닌 int(부호 있는 32비트 정수) 인수인 함수에도 전달됩니다. 예를 들어 show_mountinfo() 함수(행 227에서 호출되어 / proc/self/mountinfo)는 seq_dentry()(라인 150에서)를 호출하고, dentry_path()(라인 530에서)를 호출하고(라인 387에서) prepend()를 호출합니다. ———— ————————————————– ———- 135 정적 int show_mountinfo(struct seq_file *m, struct vfsmount *mnt) 136 { … 150 seq_dentry(m, mnt->mnt_root, ” tn\”); ————————————————– ———————- 523 int seq_dentry(struct seq_file *m, struct dentry *dentry, const char *esc) 524 { 525 char *buf; 526 size_t 크기=seq_get_buf(m, &buf); … 529 if (크기) { 530 문자 *p=dentry_path(덴트리, 버퍼, 크기); ————————————————– ———————- 380 char *dentry_path(struct dentry *dentry, char *buf, int buflen) 381 { 382 char *p=NULL; … 385 if (d_unlinked(dentry)) { 386 p=buf + buflen; 387화 ——————————————- 11 정적 int prepend(char buffer, int *buflen, const char *str, int namelen) 12 { 13 *buflen -=namelen; 14 if (*buflen license), …. 2161 /일반 bpf_prog 할당 */ 2162 prog=bpf_prog_alloc(bpf_prog_size(attr->insn_cnt), GFP_USER); ————————————————– ———————- f/ 우리는 첫 번째 1GB seq_file 버퍼를 vfree()하고(여기서 “//deleted”는 범위를 벗어났음) 즉시 1024개의 스레드를 모두 차단 해제합니다. eBPF 프로그램은 vfree()d: 4KB 1GB 4KB 1GB 4KB 2GB —–|—|————– —|—|—————–|—|—————–| … |XXX| eBPF 프로그램 |XXX| seq_file 버퍼 |XXX| seq_file 버퍼 | —|—|—————–|—|—————–| —|—————–| g/ 다음으로 (userfaultfd 또는 FUSE를 통해 다시) eBPF 프로그램이 커널 eBPF 검증자에 의해 검증된 후 커널에 의해 JIT 컴파일되기 전에 스레드 중 하나(12795행에서)를 차단합니다. —– ————————————————– —————— 12640 int bpf_check(struct bpf_prog prog, union bpf_attr *attr, 12641 union bpf_attr __user *uattr) 12642 { ….. 12795 print_verification_stats(env ); ————————————————– ———————- h/ 마지막으로 이 eBPF 프로그램의 명령을 범위를 벗어난 “//deleted” 문자열로 덮어씁니다(다시 2GB seq_file 버퍼), 따라서 커널 eBPF 검증자의 보안 검사를 무효화합니다: “//deleted” | 4KB 대 1GB 4KB 1GB 4KB 2GB —–|—|—+———-|—|———– ——|—|—————–| … |XXX| eBPF 프로그램 |XXX| seq_file 버퍼 |XXX| seq_file 버퍼 | —|—|—+————-|—|—–| —|—————–| | | | | —-

자세히 알아보기

Author: Tyisha Grumbles

Leave a Reply