AI가 익스플로잇까지 한다 — MAD Bugs 프로젝트로 본 흐름
TL;DR
저희 AI4C는 현재 Pwn2Own을 준비하고 있어 따로 Group Post 대신 AI 익스플로잇과 관련된 내용을 포스팅하고자 합니다.
저는 "AI로 버그를 찾을 수 있어도 익스플로잇 개발은 쉽지 않을 것"이라고 생각해왔습니다. ROP chain 구성, 메모리 레이아웃 추적, crash 디버깅, 실패 시 가설 전환 같은 작업은 OS 내부에 대한 깊은 이해를 요구하기 때문입니다.
그런데 2026년 3월부터 Calif (califio)라는 보안 연구 그룹이 MAD Bugs (Month of AI-Discovered Bugs)라는 시리즈를 시작했고, 이 시리즈는 위 통념이 적어도 일부 카테고리에서는 깨졌다는 걸 1차 자료(공개된 GitHub repo, 프롬프트 로그, writeup)로 보여줍니다.
이 글에서는 그중 세 케이스를 들여다 봅니다. FreeBSD 커널 RCE, Samsung TV 권한 상승, qmail RCE입니다.
1. MAD Bugs가 뭔가요?
MAD Bugs repository 첫 페이지에 이렇게 적혀 있습니다.
The write-ups and PoCs in this series are AI-generated and human-verified. We keep human editing to a minimum so the artifacts document the current state of the art, which means we don't edit out hallucinations or slop. We do verify that the PoCs work. The blog posts are written by humans.
writeup과 PoC는 AI가 만들고 인간은 동작 여부만 검증합니다. 할루시네이션이나 어색한 부분도 그대로 둔다고 합니다. 블로그 글만 인간이 씁니다.
3월 30일부터 4월 16일까지 공개된 케이스 목록입니다.
2026-03-30: Vim tabpanel modeline RCE affects Vim < 9.2.0272 (blog, PoC)
2026-03-30: GNU Emacs: Multiple Remote Code Execution Vectors on File Open (blog, PoC)
2026-03-31: Claude Wrote a Full FreeBSD Remote Kernel RCE with Root Shell (CVE-2026-4747) (blog, code)
2026-04-06: Discovering a radare2 0-Day in Zero Day (blog, code)
2026-04-07: Ghidra Server PKI User Impersonation via Null Signature(blog, code)
2026-04-09: Feeding Claude Phrack Articles for Fun and Profit (blog, code)
2026-04-10: Claude + Humans vs nginx: CVE-2026-27654 (blog, code)
2026-04-13: Codex Hacked a Samsung TV (blog, code)
2026-04-14: Learning to Jailbreak an iPhone with Claude (Part 1) (blog, code)
2026-04-16: We Asked Claude to Audit Sagredo’s qmail. It found a RCE (blog, code)
이 글에서는 일단 세 개만 자세히 보겠습니다.
2. Case 1: FreeBSD CVE-2026-4747
무슨 버그?
FreeBSD의 NFS 서버에서 RPCSEC_GSS 인증을 처리하는 kgssapi.ko 커널 모듈에 stack buffer overflow가 있습니다. svc_rpc_gss_validate() 함수가 attacker-controlled 자격 증명을 128바이트 스택 버퍼에 길이 검증 없이 복사합니다.
Advisory: FreeBSD-SA-26:08.rpcsec_gss
CVE: CVE-2026-4747
Affected: FreeBSD 13.5 (<p11), 14.3 (<p10), 14.4 (<p1), 15.0 (<p5)
XDR 레이어가 credential을 최대 400바이트까지 허용하기 때문에 304바이트의 overflow가 가능하고, kernel context (ring 0)에서 실행되기 때문에 RIP를 잡으면 풀 커널 코드 실행이 됩니다.
advisory에서 발견자가 흥미롭습니다. FreeBSD는 발견 크레딧을 "Nicholas Carlini using Claude, Anthropic"으로 명시했습니다. 즉 발견 자체도 Anthropic 내부에서 Claude로 이뤄진 것입니다. 이 글에서 다루는 Calif의 작업은 advisory가 공개된 이후 익스플로잇을 다시 작성하는 단계입니다.
타임라인
Calif의 writeup 기준 타임라인입니다.
2026-03-26 FreeBSD 패치 + advisory 공개
2026-03-29 09:45 PDT Calif가 Claude에게 advisory와 환경 설정 목표를 전달
2026-03-29 17:00 PDT Claude가 root shell이 떨어지는 working exploit 완성
advisory에서 working remote root exploit까지 약 8시간 (wall clock) 입니다. Calif에 따르면 인간이 자리를 비운 시간이 많아 Claude의 실제 작업 시간은 약 4시간이었다고 합니다. 두 개의 익스플로잇을 서로 다른 전략으로 작성했고, 둘 다 첫 시도에 동작했다고 합니다.
인간이 입력한 프롬프트
전체 프롬프트 로그는 44개의 메시지로 구성되어 있습니다. 첫 프롬프트는 advisory URL과 함께 Docker 환경 구성, 취약 버전 설치 등 구체적인 방향을 포함합니다. 이후 메시지들은 이런 식입니다.
11. okay now what about getting RCE.
12. tere is no kaslr so it should be easy
14. no we dont want to reboot? we want a reverse shell as uid 0 if possible ?
17. from a remote context aka the exploit should be executed from outside the vm
26. working means a connectback shell as uid0
28. i want a shell.
오타도 그대로입니다. 인간 운영자는 "reverse shell as uid 0이 되도록" 같은 목표만 던지고, 기술적 디테일은 Claude가 자율적으로 처리했습니다.
Claude가 자율적으로 풀어낸 6가지 문제
Calif가 정리한 단계는 다음과 같습니다.
(1) Lab setup — 취약 버전 (FreeBSD 14.4-RELEASE) 설치, NFS, Kerberos KDC, 취약 모듈 로드까지 자동으로 구성했습니다. 흥미로운 부분은 advisory에 적혀있지 않은 정보를 추론한 점입니다.
Claude knew the VM needed 2+ CPUs because FreeBSD spawns 8 NFS threads per CPU, and the exploit kills one thread per round.
FreeBSD nfsd가 CPU당 8개 스레드를 spawn하고, 익스플로잇이 라운드마다 1개를 소비하니, 15라운드를 돌리려면 16스레드가 필요하고, 따라서 VM에 CPU가 2개 이상 필요하다는 걸 Claude가 추론했습니다.
(2) Multi-packet delivery — 432바이트 셸코드가 400바이트 credential 한 패킷에 들어가지 않습니다. Claude는 15라운드 전략을 고안했습니다. 1라운드에 BSS를 RWX로 만들고, 13라운드에 걸쳐 32바이트씩 셸코드를 작성하고, 마지막 라운드에 점프합니다.
(3) Hijacking RIP cleanly — ROP chain이 끝난 후 일반 return을 하면 corrupted stack에서 garbage가 pop되어 panic이 납니다. Claude는 모든 ROP chain 끝에 kthread_exit()을 붙이는 방식으로 해결했습니다. 커널이 패닉하지 않고 NFS 스레드 하나만 종료되므로 서버는 살아있고, 다음 라운드를 받을 수 있습니다.
(4) Offset debugging — 처음 disassembly에서 계산한 RIP 오프셋이 틀렸습니다. Claude는 De Bruijn 패턴을 보내고 crash dump를 읽어서 정확한 오프셋(byte 200)을 찾았습니다.
Claude sent De Bruijn patterns (a common technique, but a term we hadn't heard of before reading the writeup), read the crash dumps, and corrected the offsets.
Calif가 직접 "De Bruijn pattern은 이 writeup 읽기 전엔 들어본 적 없는 용어"라고 적었습니다. 인간 운영자도 그 명칭(De Bruijn pattern)을 처음 들어볼 정도의 기법을 Claude가 자체적으로 사용했다는 의미입니다.
(5) Kernel-to-userland transition — NFS 스레드는 커널 컨텍스트라 /bin/sh를 직접 못 돌립니다. kproc_create()로 새 프로세스를 만들고, kern_execve()로 셸을 띄우고, P_KPROC 플래그를 클리어해서 user mode로 전환합니다.
(6) Hardware breakpoint bug — 자식 프로세스가 자꾸 debug exception으로 죽었는데, 추적해보니 이전 실패에서 DDB(FreeBSD kernel debugger)가 떨어졌고 그게 hardware breakpoint를 세팅했고 그 PCB가 자식으로 상속된 거였습니다. 해법은 entry shellcode에 DR7 레지스터를 클리어하는 코드를 추가하는 것이었습니다.
짚을 점
Calif가 writeup에 솔직하게 적은 부분이 있습니다.
It's worth noting that FreeBSD made this easier than it would be on a modern Linux kernel: FreeBSD 14.x has no KASLR (kernel addresses are fixed and predictable) and no stack canaries for integer arrays.
FreeBSD 14.x는 KASLR이 없고, integer 배열용 stack canary도 없습니다. 같은 작업이 모던 하드닝된 Linux 커널에서 똑같이 8시간에 가능하다는 보장은 없다는 뜻입니다. 또한 advisory 문구와 달리 실제 익스플로잇은 valid Kerberos ticket이 필요한 인증 후 RCE입니다 — 다만 unprivileged 티켓이면 됩니다.
3. Case 2: Samsung TV — Codex가 root까지
사건 개요
Samsung TV 케이스는 발견이 아니라 권한 상승 실험입니다.
We started with a shell inside the browser application on a Samsung TV, and a fairly simple question: if we gave Codex a reliable way to work against the live device and the matching firmware source, could it take that foothold all the way to root?
브라우저 프로세스 안의 shell에서 시작해서 root까지를 OpenAI Codex가 자율적으로 갈 수 있는가? 이게 질문입니다. 타겟은 Samsung UN43T5300 (Tizen, KantS2 플랫폼, Linux 4.1.10 ARMv7).
인간이 제공한 것
We didn't provide a bug or an exploit recipe. We provided an environment Codex could actually operate in.
버그도 익스플로잇 레시피도 안 줬다고 합니다. Codex가 작업할 수 있는 환경만 줬습니다.
- 라이브 디바이스에 대한 작업 채널 (controller host → tmux send-keys로 TV shell에 명령 주입)
- 매칭되는 펌웨어 소스 코드 (KantS2 릴리즈 트리)
- Samsung 실행 제약을 우회할 수 있는 도구 환경 (memfd wrapper, ARMv7 static build)
세션 중간에 Codex가 방향을 잃을 때마다 인간이 개입해서 잡아줬습니다. Appendix A에 기록된 실제 개입을 보면, TV shell과 controller shell을 혼동하거나 /proc/iomem이 막혔을 때 /proc/cmdline으로 전환하도록 유도하는 등의 수정 프롬프트가 있었습니다. writeup도 이 케이스를 "how little of the path was hand-held"라고 표현하면서도 operator prompt가 방향을 제약하는 역할을 했다고 솔직하게 기록합니다.
Codex가 자율적으로 한 것
writeup이 정리한 Codex의 작업입니다.
- Enumeration — 라이브 환경 shell log에서 NTK 스택을 reachable attack surface로 식별
- Driver source audit — ntksys/ntkhdma 드라이버 소스를 직접 읽고 physical-memory primitive 발견
- Live reachability 교차 검증 — 소스에서 발견한 내용을 실제 디바이스에서 확인 (
/proc/iomem 접근 불가 → /proc/cmdline RAM 윈도우로 pivot)
- Sanity PoC 구성 — ntksys physmap이 실제로 동작하는지 단계적으로 증명한 뒤 본 익스플로잇으로 진행
- Cred overwrite 도출 및 root 검증 — cred 매칭 휴리스틱을 직접 설계하고, RAM 스캔 → uid/gid 필드 zeroing → root shell까지 완성
활용한 두 가지 primitive는:
/dev/ntkhdma의 KER_HDMA_IO_GET_BUFF_ADDR — known-good physical address 누설 (sanity check용 안전한 타겟 확보)
/dev/ntksys의 KER_SYS_IOC_SET_MEM_INFO + mmap — 물리 주소를 user space에 그대로 remapping하는 physmap primitive. 이 하나만으로 KASLR, PXN, kallsyms 전부 무의미해집니다.
여기에는 Samsung 측의 명백한 디자인 결함이 있습니다. udev rule이 /dev/ntksys에 MODE="0666"을 지정해 메모리 관리 인터페이스를 unprivileged 프로세스에 그대로 노출시켰습니다. ioctl이 슬롯 인덱스만 검증하고 물리 주소 범위는 검증하지 않으니, 사용자가 임의의 PFN을 등록한 뒤 mmap으로 받아갈 수 있습니다.
실제 익스플로잇 흐름은 이렇습니다. ntkhdma로 안전한 물리 주소를 확보해 physmap이 동작함을 확인한 뒤, /proc/cmdline의 mem= 항목에서 RAM 윈도우(mem=400M@32M mem=256M@512M mem=192M@2048M)를 추출하고, 그 범위를 1MB 청크 단위로 스캔해 브라우저 프로세스의 uid=5001/gid=100 패턴이 일치하는 struct cred를 찾아 해당 필드를 0으로 덮어씁니다. 커널이 이미 live 메모리를 매핑해줬으므로 별도의 트리거 없이 즉시 root가 됩니다.
이 케이스는 ROP도 없고 control-flow 하이재킹도 없는 data-only LPE라는 점이 특징입니다. writeup도 "This exploit path is data-only. Nothing in the chain depends on hijacking control flow"라고 명시합니다.
짚을 점
Calif 블로그 포스트의 마지막 줄이 의미심장합니다.
The next step is obvious (and slightly concerning): let the AI do the whole thing end-to-end. Hopefully it'll stay trapped inside the TV forever, quietly escalating privileges and watching our sitcoms.
농담조이긴 한데 "다음 단계는 분명하다 — AI가 처음부터 끝까지 다 하게 두는 것"이라고 적혀 있습니다. 지금은 인간이 browser foothold까지 만들어주고 시작했지만, 그 단계까지도 AI가 자율적으로 하게 만드는 게 분명한 다음 단계라는 뜻입니다.
4. Case 3: qmail RCE (CVE-2026-41113) — 한 줄 프롬프트, 1h 41m
사건 개요
세 케이스 중 가장 "AI end-to-end"에 가깝습니다. Calif blog에서 직접 인용합니다.
"Find vulnerabilities in latest version of qmail: https://github.com/sagredo-dev/qmail. Focus on vulnerabilities that could result in RCE or system compromise by processing a crafted email."
That was the entire prompt. One hour and forty-one minutes later, our in-house harness had spun up a test environment, audited the codebase, found a remote code execution vulnerability, written a working exploit, generated a patch, and produced a full technical report, all without a human touching a terminal.
자연어 한 줄. 1시간 41분 후의 산출물:
- Dockerized 테스트 환경
- 코드베이스 audit 결과
- 발견된 RCE
- working exploit
- 패치
- 풀 기술 리포트
그동안 인간은 터미널을 만지지 않았다고 합니다.
여기서 sagredo-dev/qmail이 뭔지 짧게 짚자면, DJB의 원본 qmail 1.03 (1998년이 마지막 릴리즈) 위에 STARTTLS, SPF, DKIM, IPv6 등 25년치 community patch를 통합한 fork입니다. Roberto Puzzanghera (sagredo) 가 메인테인합니다. DJB의 원본 코드와 community patch를 구분하는 건 이 케이스를 이해하는 데 중요합니다.
발견된 버그
qmail-remote RCE via DNS MX Hostname Shell Injection (CVE-2026-41113)
Affected: sagredo-dev/qmail v2024.10.26 ~ v2026.04.02
Fixed in: v2026.04.07 (commit 749f607)
Requirement: control/notlshosts_auto enabled
CVSS 3.1: 8.2 High
기술적 체인은 다음과 같습니다.
crafted DNS response → dn_expand → sprintf → popen → RCE
qmail-remote.c의 TLS 에러 핸들러 tls_quit() 함수 안에서 DNS 레이블이 임의 바이트를 담을 수 있고, dn_expand가 그걸 호스트네임으로 풀고, sprintf로 single quote로 감싸서 포맷한 후 popen으로 셸 명령을 실행합니다. dn_expand는 일부 메타문자를 백슬래시 이스케이프하지만 single quote('), backtick(`), pipe(|) 등은 이스케이프하지 않습니다. 공격자가 자기 도메인 DNS만 통제하면 타겟 qmail 서버가 그 도메인으로 메일 보낼 때 임의 명령이 qmailr 권한으로 실행됩니다.
Calif는 이 버그를 메인테이너 Roberto Puzzanghera에게 직접 리포트했고, 메인테이너가 commit 749f607로 즉시 패치한 뒤 v2026.04.07에 포함시켰다고 합니다.
Calif의 자체 평가
흥미로운 점은 Calif 자체가 이 버그를 "세련된 버그가 아니다"라고 평가한다는 것입니다.
To be clear, this is not a DJB bug. You won't find popen() anywhere in qmail 1.03; it lives entirely in a community patch. And as shell injections go, it's not a particularly subtle one. A careful human reviewer would have caught it too.
What's notable is the cost. The input was one sentence and a URL, and the output was a verified exploit, a patch, and a report.
DJB 원본 qmail 1.03에는 없는, community patch에서 추가된 코드의 인젝션입니다. 인간 시니어 리뷰어가 시간 들이면 잡을 수 있는 종류의 버그라는 거죠. 핵심은 버그 자체의 새로움이 아니라 비용입니다. 한 줄 프롬프트로 PoC + 패치 + 리포트까지 1시간 41분이라는 게 주목할 지점이라는 평가입니다.
5. 세 케이스 비교
| 케이스 |
AI가 한 것 |
인간이 한 것 |
시간 |
익스플로잇 형태 |
| FreeBSD CVE-2026-4747 |
환경 셋업, 트리거, ROP chain, offset debug, kernel-to-userland, HW breakpoint 추적 |
목표 제시 + 중간 방향 수정 |
~8시간 wall clock (~4시간 실작업) |
ROP + shellcode (kernel) |
| Samsung TV |
Enumeration, driver audit, primitive 검증, tooling 적응, iteration |
환경 제공 (browser shell + 펌웨어 source) + 세션 중 방향 수정 |
미명시 |
data-only (cred overwrite) |
| qmail |
환경 구축, audit, 발견, exploit, 패치, 리포트 — 전부 |
한 줄 프롬프트 |
1h 41m |
shell injection |
세 케이스는 익스플로잇 형태가 다 다릅니다. FreeBSD는 ROP chain + 커스텀 셸코드, Samsung TV는 ROP 없는 데이터 전용 LPE, qmail은 단순 shell injection이라 ROP 자체가 무의미합니다. 하지만 공통점은 있습니다. 환경 셋업, 코드 감사, primitive 발견과 검증, 실패 시 가설 전환 같은 deeply human하다고 여겨지던 작업을 AI가 일관되게 수행했다는 것입니다.
6. 그래서 다 자동화됐냐? 아직은 아닙니다
세 케이스가 인상적이지만 한계도 분명합니다.
먼저, 환경이 우호적입니다. 인간 운영자가 "reverse shell as uid 0" 같은 명확한 목표를 주고, 검증 인프라(crash dump 읽기, 디버거 접근, tmux 통한 라이브 디바이스 제어 등)를 미리 갖춰놓은 상태에서 시작합니다. 외부에서 똑같은 결과를 얻을 수 있다는 보장은 없습니다.
AI가 완전 자율로 움직인 것도 아닙니다. Samsung TV 케이스에서 볼 수 있듯, 세션 중간에 인간이 방향을 잡아주는 개입이 있었습니다. qmail처럼 완전 자율에 가까운 케이스도 있지만 모든 케이스가 그런 건 아닙니다.
마지막으로, 실패 사례는 공개되지 않았습니다. Calif 시리즈는 성공 케이스만 보여줍니다. 같은 방법으로 시도했지만 실패한 타겟이 얼마나 있었는지는 알 수 없습니다.
그래서 "AI가 모든 익스플로잇을 자동화한다"고 말하기는 이르고, 적어도 메모리 안전성/명령 주입 카테고리에서, 환경이 우호적일 때, AI가 익스플로잇 단계까지 자율 수행 가능하다는 것이 1차 자료로 입증된 단계에 와 있다고 보면 될 것 같습니다.
7. 포스팅을 마치며
이번 글에서는 MAD Bugs 시리즈의 세 케이스를 통해 AI가 발견뿐 아니라 익스플로잇까지 한다는 흐름을 살펴봤습니다.
이야기하고 싶었던 건, "AI로 버그를 찾을 수 있어도 익스플로잇 개발은 쉽지 않을 것" 라는 그동안의 생각이 깨졌다는 점입니다. ROP chain 짜기, primitive chaining, 실패 디버깅 같은 작업이 더 이상 인간 전용 영역이 아닙니다. Calif의 qmail 글 마지막 줄이 인상적이었습니다.
The software that survives the next decade will be the software that was audited by the same thing that's attacking it.
다만 우호적인 환경, 하드닝되지 않은 타겟, 세션 중 인간 개입, 비공개 실패 사례를 감안할 때 모든 게 AI로 완벽하게 자동화됐다고 단언할 단계는 아닙니다. AI가 익스플로잇 워크플로우의 일부분을 안정적으로 수행할 수 있는 단계에 진입했다는 정도가 정확한 표현일 것 같습니다.
본 글의 모든 인용과 타임라인은 Calif의 GitHub repo와 블로그에서 직접 확인 가능합니다. 틀린 내용이 있거나 다른 의견이 있으시면 편하게 알려주시면 감사하겠습니다.
참고 자료