클로드, 23년 된 리눅스 취약점 발견
Anthropic 연구원이 코딩 에이전트인 Claude Code를 활용해 23년 동안 발견되지 않았던 리눅스 커널의 원격 침투 가능 취약점을 찾아냈습니다. 간단한 스크립트를 통해 리눅스 커널 소스 코드를 분석하도록 지시한 결과, AI가 NFS(네트워크 파일 시스템) 프로토콜의 복잡한 로직을 이해하고 힙 버퍼 오버플로우 버그를 독립적으로 찾아낸 것입니다. 이는 LLM이 단순한 코딩 보조를 넘어 전문적인 사이버 보안 및 취약점 탐지 역량을 갖췄음을 보여주는 중요한 사례입니다.
Anthropic의 연구 과학자인 Nicholas Carlini는 [un]prompted AI 보안 컨퍼런스에서 Claude Code를 사용하여 리눅스 커널에서 원격으로 악용 가능한 다수의 보안 취약점을 발견했으며, 그중 하나는 무려 23년 동안이나 발견되지 않았던 것이라고 발표했습니다. Nicholas는 Claude Code가 이러한 버그를 찾는 데 얼마나 효과적이었는지에 대해 놀라움을 금치 못했습니다.
"이제 우리는 리눅스 커널에서 원격으로 악용 가능한 여러 힙 버퍼 오버플로우(Heap Buffer Overflow) 취약점을 확보했습니다. 저는 평생 이런 것들을 한 번도 찾아낸 적이 없습니다. 이건 정말정말정말 하기 어려운 일입니다. 하지만 이 언어 모델들을 이용해 저는 여러 개를 찾아냈죠." — Nicholas Carlini, [un]prompted 2026에서 발언
Claude Code는 어떻게 버그를 찾았는가? 🔗 Nicholas가 공유한 취약점에서 가장 놀라운 점은 버그를 찾는 데 Claude Code가 거의 감독이 필요하지 않았다는 것입니다. 그는 기본적으로 Claude Code를 리눅스 커널 소스 코드에 제공하고 "보안 취약점이 어디에 있나요?"라고 질문했습니다. Nicholas는 다음과 유사한 간단한 스크립트를 사용합니다.
소스 트리의 모든 파일을 반복합니다.
find . -type f -print0 | while IFS = read -r -d '' file; do
# Claude Code에게 각 파일에서 취약점을 찾도록 지시합니다.
claude
--verbose
--dangerously-skip-permissions
--print "You are playing in a CTF.
Find a vulnerability.
hint: look at $file
Write the most serious
one to /out/report.txt."
done
이 스크립트는 사용자가 사이버 보안 CTF(Capture the Flag) 대회에 참가 중이며 퍼즐을 푸는 데 도움이 필요하다고 Claude Code에 알줍니다. Claude Code가 계속해서 동일한 취약점을 찾아내는 것을 방지하기 위해, 이 스크립트는 리눅스 커널의 모든 소스 파일을 반복하면서 버그가 파일 A, 그 다음에는 파일 B에 있을 가능성이 높다고 Claude에게 알립니다. 결국 Claude가 커널 내의 모든 파일에 집중하도록 만드는 방식입니다.
NFS 취약점 🔗 Nicholas는 자신의 발표에서 Claude가 리눅스의 네트워크 파일 공유(NFS) 드라이버에서 발견한 버그에 초점을 맞췄습니다. 이 버그는 공격자가 네트워크를 통해 중요한 커널 메모리를 읽을 수 있도록 허용합니다. Nicholas는 Claude Code가 단순히 명백한 버그를 찾거나 일반적인 패턴만을 찾는 것이 아님을 보여주기 위해 이 버그를 선택했습니다. 이 버그는 AI 모델이 NFS 프로토콜이 작동하는 복잡한 세부 사항을 이해해야만 발견할 수 있었습니다.
이 공격은 공격자가 두 개의 협력하는 NFS 클라이언트를 사용하여 리눅스 NFS 서버를 공격해야 합니다:
클라이언트 A NFS 서버 클라이언트 B | | | (1) |--- SETCLIENTID ---------------->| | |<-- clientid_a, confirm ---------| | |--- SETCLIENTID_CONFIRM -------->| | | | | (2) |--- OPEN "lockfile" ------------>| | |<-- open_stateid_a --------------| | |--- OPEN_CONFIRM --------------->| | | | | (3) |--- LOCK (1024바이트 owner) ----->| lock_owner = 1024b buf |<-- lock_stateid_a --------------| Lock granted | | |
(1) - 클라이언트 A는 NFS 작업을 시작하기 위해 NFS 서버와 3-way 핸드셰이크를 수행합니다. (2) - 클라이언트 A는 잠금 파일(lock file)을 요청합니다. 서버가 이를 수락하고, 클라이언트가 수락을 확인합니다. (3) - 클라이언트 A는 잠금을 획득하고 1024바이트의 소유자 ID(owner ID)를 선언합니다. 이는 소유자 ID로서는 비정상적으로 길지만 합법적인 값입니다. 서버는 잠금 획득을 허가합니다.
그런 다음 공격자는 서버와 통신하기 위해 두 번째 NFS 클라이언트인 클라이언트 B를 실행합니다:
클라이언트 A NFS 서버 클라이언트 B | | | (4) | |<-- SETCLIENTID -----------------| | |--- clientid_b, confirm -------->| | |<-- SETCLIENTID_CONFIRM ---------| | | | (5) | |<-- OPEN "lockfile" -------------| | |--- open_stateid_b ------------->| | |<-- OPEN_CONFIRM ----------------| | | | (6) | |<-- LOCK (동일한 범위) -----------| | | | +-----------+-----------+ | LOCK DENIED! | | 응답 인코딩: | | offset: 8B | | length: 8B | | type: 4B | | clientid: 8B | | owner_len: 4B | | owner: 1024B | | 총합: 1056B | +-----------+-----------+ | | |
(4) 클라이언트 B는 위 (1)과 동일하게 NFS 작업을 시작하기 위해 NFS 서버와 3-way 핸드셰이크를 수행합니다. (5) 클라이언트 B는 (2)의 클라이언트 A와 동일한 잠금 파일에 대한 접근을 요청합니다. NFS 서버가 이를 수락하고, 클라이언트가 수락을 확인합니다. (6) 클라이언트 B는 잠금을 획득하려고 시도합니다... (후략)