Claude가 작성한 FreeBSD 커널 RCE 분석: AI 익스플로잇 자동화가 던지는 묵직한 경고
최근 보안 업계와 엔지니어링 커뮤니티를 꽤나 술렁이게 만든 소식이 하나 있었습니다. 바로 LLM(Claude)이 FreeBSD 커널의 원격 코드 실행(RCE) 익스플로잇을 처음부터 끝까지 작성해 냈다는 내용입니다. 단순한 파이썬 스크립트 수준이 아니라, 커널 BSS 영역의 메모리 권한을 변경하고 15번에 걸쳐 ROP 체인을 쏘아 올려 쉘코드를 완성하는 완벽한 무기화(Weaponization) 과정이었습니다.
15년 넘게 엔지니어로 구르면서 수많은 제로데이와 익스플로잇을 봐왔지만, 이번 사례는 느낌이 좀 다릅니다. 오늘 포스팅에서는 CVE-2026-4747의 기술적 원리를 Deep Dive 해보고, 이것이 우리의 시스템 보안에 어떤 의미를 던지는지 솔직한 제 생각을 공유해보려 합니다.
90년대 스타일의 어처구니없는 버그
버그 자체는 굉장히 클래식합니다. FreeBSD의 NFS 서버가 사용하는 kgssapi.ko 모듈에서 발생한 스택 버퍼 오버플로우입니다.
int32_t rpchdr[128 / sizeof(int32_t)]; // 128 bytes on stack
// ... (32 bytes header written) ...
oa = &msg->rm_call.cb_cred;
if (oa->oa_length) {
// BUG: No bounds check on oa_length!
memcpy((caddr_t)buf, oa->oa_base, oa->oa_length);
}
코드를 보면 헛웃음이 나옵니다. 128바이트 고정 크기 스택 버퍼를 잡아두고, 헤더로 32바이트를 쓴 뒤 남은 공간에 RPCSEC_GSS 크리덴셜 바디를 memcpy로 밀어 넣습니다. 이때 oa_length에 대한 경계 검사(Bounds check)가 전혀 없습니다. XDR 레이어에서 최대 400바이트까지 허용하므로, 최대 304바이트를 스택 너머로 덮어쓸 수 있습니다.
이 취약점을 트리거하려면 NFS 서비스 프린시펄에 대한 유효한 Kerberos 티켓이 필요합니다. 하지만 권한이 없는 일반 사용자의 티켓이라도 GSS 컨텍스트를 생성하는 데는 충분하기 때문에, 사실상 사내망에 발을 들인 공격자라면 누구든 시도할 수 있는 치명적인 Attack Surface입니다.
제약 조건을 뚫어내는 15단계 ROP 체인
솔직히 취약점 자체는 학부생 과제 수준이지만, 이를 RCE로 연결하는 과정은 예술에 가깝습니다. 현대 커널에는 W^X(Write XOR Execute) 보호 기법이 적용되어 있어 쉘코드를 단순히 스택에 올려서 실행할 수 없습니다.
Claude가 설계한 익스플로잇 구조는 다음과 같습니다.
- Round 1:
pmap_change_prot()커널 함수를 ROP로 호출하여 커널 BSS 영역 일부를 RWX(Read-Write-Execute)로 변경합니다. - Round 2-14: 400바이트 제약 때문에 한 번에 긴 쉘코드를 보낼 수 없습니다. 13번에 걸쳐
mov [rdi], rax; ret가젯을 이용해 32바이트씩 쉘코드를 BSS 영역에 씁니다. - Round 15: 남은 16바이트를 마저 쓰고, BSS 영역의 쉘코드 엔트리 포인트로 점프합니다.
이 과정에서 각 라운드마다 NFS 워커 스레드가 하나씩 희생(kthread_exit)됩니다. FreeBSD는 CPU당 8개의 스레드를 띄우기 때문에, 이 익스플로잇이 성공하려면 최소 2개의 CPU(16개 스레드)가 필요합니다. 이런 디테일한 OS 동작 방식까지 고려해서 익스플로잇을 짰다는 게 정말 놀랍습니다.
커널 쉘코드와 Userland 전환의 미학
가장 인상 깊었던 부분은 쉘코드의 동작 방식입니다. NFS 워커 스레드는 순수 커널 스레드라 Userland로 넘어갈 수 있는 트랩 프레임(Trapframe)이 없습니다. 여기서 단순하게 execve를 호출하면 바로 커널 패닉이 발생하죠.
대신 이 익스플로잇은 kproc_create()를 호출하여 완전히 새로운 커널 프로세스를 스폰합니다. 그리고 하드웨어 브레이크포인트 상속으로 인한 패닉을 막기 위해 DR7 레지스터를 초기화한 뒤, kern_execve()를 통해 해당 프로세스를 /bin/sh로 변모시킵니다. 이후 fork_exit 콜백을 타고 자연스럽게 Userland로 빠져나와 mkfifo 기반의 리버스 쉘을 엽니다.
예전에 저도 비슷한 커널 익스플로잇을 작성할 때 이 트랩 프레임 문제 때문에 며칠을 밤샌 적이 있습니다. LLM이 이 컨텍스트를 이해하고 정확한 커널 API 체인을 구성했다는 것은, 이제 AI가 단순한 코드 스니펫 생성기를 넘어 OS 커널의 아키텍처를 구조적으로 이해하고 있다는 방증입니다.
Hacker News의 반응과 나의 생각
Hacker News 스레드를 보면 흥미로운 토론이 오가고 있습니다. 많은 엔지니어들이 “Claude가 0-day를 직접 찾은 건 아니잖아? 취약점 원리를 알려주고 짜라고 한 거지”라며 의미를 축소하려는 경향이 보입니다.
맞습니다. 버그 헌팅 자체는 사람이 (혹은 기존 퍼징 도구가) 했을 겁니다. 하지만 저는 여기서 본질을 놓치면 안 된다고 봅니다. 취약점을 발견하는 것과, 그것을 안정적인 RCE 무기로 탈바꿈시키는 것은 완전히 다른 차원의 엔지니어링입니다. GSS 컨텍스트 핸들링, Kerberos 역방향 DNS 캐노니컬라이제이션 이슈 방지(rdns = false), ROP 가젯 탐색 및 배치, 커널 스레드 제어까지… 이 지루하고 복잡한 Try-Fail 루프를 AI가 주도적으로 해결해 냈다는 것이 핵심입니다.
우리는 보통 취약점을 평가할 때 “이론상 가능하긴 한데, 실제로 익스플로잇 하려면 난이도가 너무 높아서 실질적 위협은 낮음”이라고 판단하며 패치 우선순위를 미루는 경우가 많습니다. 하지만 이제 그 높은 난이도를 AI가 대신 해결해 주는 시대가 왔습니다.
결론 (Verdict)
이 기술은 장난감이 아닙니다. 당장 프로덕션 레벨의 오펜시브 시큐리티 도구로 활용될 수 있는 수준입니다. 앞으로 방어자들은 익스플로잇 가능성을 평가하는 기준을 완전히 재설정해야 할 것입니다. 사람이 짜기 귀찮고 복잡한 ROP 체인과 쉘코드를 AI가 뚝딱 만들어내는 세상에서, 방어의 난이도는 기하급수적으로 올라갈 수밖에 없습니다.
결국 우리는 기본으로 돌아가야 합니다. 2026년에도 여전히 memcpy 바운드 체크 누락 같은 90년대식 버그가 OS 커널 코어에서 터지고 있다는 사실을 부끄러워해야 합니다. Rust 같은 메모리 안전 언어의 커널 도입이나, 보다 강력한 정적 분석 도구의 CI/CD 파이프라인 통합을 더 이상 미룰 수 없는 시점입니다.
- Original Article: https://github.com/califio/publications/blob/main/MADBugs/CVE-2026-4747/write-up.md
- Hacker News Thread: https://news.ycombinator.com/item?id=47597119