
보안 감사관으로서의 AI 에이전트: LLM이 Cloudflare의 CIRCL에서 어떻게 7개의 실제 암호학 버그를 찾아냈는가 (그리고 모든
요약
LLM 기반 AI 에이전트가 Cloudflare의 CIRCL 라이브러리에서 7개의 실제 암호학적 보안 취약점을 발견한 사례를 다룹니다. 전문가도 놓친 정밀한 버그를 탐지하기 위한 AI 보안 감사 파이프라인 구축 방법과 멀티 모델 리뷰 체인 전략을 소개합니다.
핵심 포인트
- LLM 에이전트가 Cloudflare의 실제 암호학 코드에서 7개의 취약점 발견
- float64 오버플로로 인한 정밀도 손실 등 인간이 놓치기 쉬운 버그 탐지
- 전문가 지식을 프롬프트에 인코딩하는 '기술(Skills)' 아키텍처 활용
- 멀티 모델 리뷰 체인을 통한 새로운 프로덕션 보안 표준 제시
보안 감사관으로서의 AI 에이전트: LLM이 Cloudflare의 CIRCL에서 어떻게 7개의 실제 암호학 버그를 찾아냈는가 (그리고 모든 개발자가 다음에 구축해야 할 것)
발행일: 2026년 7월 8일 · 읽기 시간 18분

목차
- AI가 가장 먼저 찾아낸 버그
- zkSecurity 실험: 아키텍처 및 설정
- 7개의 버그 분석 — AI가 인간이 놓친 것을 본 것
- 자신만의 LLM 보안 감사 파이프라인(Pipeline) 구축하기
- "기술(Skills)" 아키텍처: 프롬프트(Prompts)에 전문가 지식 인코딩하기
- AI 심각도 등급이 실패하는 이유 (그리고 보완 방법)
- 더 나은 모델, 더 나쁜 도구 문제
- 멀티 모델 리뷰 체인(Multi-Model Review Chains): 새로운 프로덕션 표준
- 한계, 함정, 그리고 솔직한 주의사항
- 미래: 지속적인 AI 보안 커버리지
- 결론 — 당신의 다음 단계
AI가 가장 먼저 찾아낸 버그
다음은 널리 사용되고 전문가의 검토를 거친 프로덕션 암호학(Cryptography) 코드베이스인 Cloudflare의 CIRCL 라이브러리에서 발췌한 한 줄의 코드입니다:
// tss/rsa/rsa_threshold.go
xi := int64(math.Pow(float64(x), float64(i)))
이 한 줄은 임계값 RSA 비밀 공유(threshold RSA secret sharing)를 위한 다항식 평가(polynomial evaluation)를 수행합니다. 계수(Coefficients)는 big.Int입니다. 하지만 거듭제곱 연산이 float64를 거치게 되는데, 이는 가수부(mantissa)가 53비트뿐인 타입입니다. 플레이어 수가 약 20명을 초과하는 경우, x^i는 정수로 다시 캐스팅(cast)되기 전에 조용히 오버플로(overflow)가 발생하고 반올림됩니다. 생성된 키 공유(key shares)는 잘못됩니다. 프로토콜이 깨진 것입니다.
인간 전문가는 이를 잡아낼 수 있습니다. 하지만 생업으로 심도 있는 암호학을 다루는 Cloudflare의 팀들도 이 코드가 배포되기 전에는 이를 잡아내지 못했습니다. AI 에이전트는 잡아냈습니다.
2026년 7월 7일, zkSecurity는 전문가가 설계한 "기술 (skills)"를 갖춘 Claude Opus 4.6 및 GPT-5.3 기반의 AI 감사 파이프라인(AI audit pipeline)이 Cloudflare의 CIRCL 라이브러리에서 어떻게 7개의 확인된 비사소한(non-trivial) 보안 취약점을 발견했는지를 기록한 상세 포스트를 게시했습니다. 7개 모두 현재 패치되었습니다. 일부는 HackerOne 포상금을 받았습니다.
이것은 데모가 아닙니다. 선별된 장난감 예시도 아닙니다. 이것은 **LLM 에이전트 보안 감사 (LLM agents security auditing)**의 현재 가능한 최전선에서 실행되며, 실제 운영 중인 암호학(cryptography)에서 실제 버그를 찾아내는 LLM 에이전트의 모습입니다.
만약 당신이 소프트웨어를 개발한다면 — 특히 암호학, 인증(authentication), 또는 보안에 민감한 경로를 다루는 소프트웨어라면 — 이 포스트는 무슨 일이 일어났는지, 왜 작동했는지, 그리고 이러한 기술을 당신의 엔지니어링 실무에 어떻게 적용할 수 있는지 이해하기 위한 현장 가이드가 될 것입니다.
zkSecurity 실험: 아키텍처 및 설정

zkSecurity는 Cloudflare의 CIRCL을 대상으로 두 가지 구성으로 실험을 진행했습니다:
모드 1: 가공되지 않은 LLM + 단순 프롬프트 (Raw LLM + Simple Prompt)
"이 파일의 보안 취약점을 검토하세요."
평범하고 구조화되지 않은 방식입니다. 모델은 코드를 검토하고 발견하는 대로 결과를 생성합니다.
모드 2: LLM + 기술 (LLM + Skills)
전문가가 작성한 "기술 (skill)" 모듈은 숙련된 암호학 감사관이 찾는 특정 취약점 클래스, 추론 패턴, 그리고 위험 신호(red flags)를 인코딩합니다. 이러한 기술들은 코드 검토가 시작되기 전에 구조화된 컨텍스트(structured context)로 주입됩니다.
두 모드 사이의 출력 품질 차이는 상당합니다. 모드 2는 더 많은 버그를 발견했고, 거짓 양성(false positives)은 더 적었으며, 더 실행 가능한(actionable) 보고서를 생성했습니다. 아래에서 기술 (Skills) 아키텍처를 심도 있게 살펴보겠습니다.
두 가지 설정을 모두 실행한 후, 팀은 동일한 코드베이스에 대해 그들의 독자적인 AI 감사 에이전트(AI audit agent)인 zkao를 실행했습니다. zkao는 다른 실행 결과들이 식별한 7개의 버그를 모두 찾아냈을 뿐만 아니라, 더 단순한 설정들이 완전히 놓쳤던 추가적인 복잡도 수준(complexity-level)의 문제들까지 포착했습니다.
Human-in-the-Loop 레이어
zkSecurity가 강조하며, 이 패턴을 기반으로 구축하는 모든 개발자가 내재화해야 할 중요한 아키텍처적 참고 사항이 있습니다: AI는 후보 발견 사항(candidate findings)을 생성하고, 인간은 신뢰할 수 있는 보고서를 생성합니다.
AI는 광범위한 가설 세트를 생성하는 데 빠르고 저렴합니다. 하지만 각 후보 발견 사항은 여전히 인간의 다음과 같은 작업이 필요합니다:
- 공격 가능성(exploitability) 검증 (이것이 실제로 도달 가능한가?)
- 개념 증명(proof-of-concept) 최소화 (이를 재현할 수 있는가?)
- 배포 컨텍스트 위험(deployment-context risk) 평가 (영향을 받는 코드 경로가 중요한가?)
- 책임 있는 공개(responsible disclosure) 처리
이러한 인간의 단계를 완전히 제거하는 것은 여전히 미해결 과제로 남아 있습니다. zkao와 같은 시스템의 목표는 인간의 개입을 없애는 것이 아니라, 확인된 발견 사항당 투입되는 인간의 노력을 최소화하는 것입니다.
7개의 버그 분석 — AI가 인간이 놓친 것을 어떻게 보았는가
확인된 7개의 취약점을 모두 살펴보겠습니다. 코드는 실제이며, 수정 사항은 커밋되었습니다. 이것은 AI 기반 보안 감사(security auditing)가 무엇을 할 수 있는지, 그리고 그 추론(reasoning)이 어디에서 놀라운지를 이해할 수 있는 가장 신호가 강한(highest-signal) 방법입니다.
버그 1: RSA 임계값 서명(Threshold Signing)에서의 Float64 정밀도 손실 (낮음)
// 버그가 있는 코드 — tss/rsa/rsa_threshold.go
xi := int64(math.Pow(float64(x), float64(i)))
big.Int 다항식이 float64 거듭제곱을 사용하여 계산됩니다. float64는 53비트 가수(mantissa, 약 15자리 십진수)를 가집니다. 플레이어 수가 약 20명을 초과하는 경우, 100^26 = 10^52와 같은 값은 이 가수 범위를 36자릿수만큼 초과하여 오버플로(overflow)됩니다. 그 결과는 정수로 다시 캐스팅(cast)되기 전에 조용히 반올림됩니다. 이로 인해 키 쉐어(Key shares)가 잘못되게 됩니다.
수정 방법: 전체 과정을 big.Int 내에서 유지하는 호너 방법(Horner's method) 평가를 사용했습니다. 코드베이스 자체의 TODO 주석에서도 이 접근 방식을 제안하고 있었습니다.
여기서 흥미로운 점은: AI는 이를 Critical (심각) 등급으로 평가했습니다. Cloudflare는 이를 _Low (낮음)_로 확인했습니다. 왜냐하면 이를 트리거하는 데 필요한 특정 매개변수 조합이 실제 상황에서는 발생할 가능성이 낮기 때문입니다. 이는 아래에서 살펴볼 심각도 보정 (severity-calibration) 문제에 대한 첫 번째 힌트입니다.
버그 2: 증명자 제어 보안 매개변수를 통한 DLEQ 증명 위조 (Low)
// 버그가 있는 코드 — zk/qndleq
type Proof struct {
Z, C *big.Int
...
챌린지 비트 길이 (challenge bit-length)를 결정하는 보안 매개변수 (security parameter)가 증명자 (prover)가 제어하는 Proof 구조체 내부에 존재했습니다. SecParam = 1로 설정하면 건전성 (soundness)이 동전 던지기 수준으로 무너집니다. 해결책은 구조적입니다: Proof에서 SecParam을 제거하고 검증자 (verifier)가 이를 명시적으로 전달하도록 합니다.
버그 3: 메시지 구별성 없는 BLS 집합 검증 (High)
이것은 AI가 과소평가 (underrated) 한 사례입니다 — 등급이 Medium에서 High로 상향되었습니다. 모든 메시지가 서로 다르다는 것을 확인하지 않고 BLS 서명을 집합화할 때 전형적인 로그 키 공격 (rogue key attack)이 적용됩니다:
// 버그 발생: VerifyAggregate가 페어링 방정식 (pairing equation)은 확인했지만 메시지 구별성 (message distinctness)은 확인하지 않음
func VerifyAggregate(pks []PublicKey, msgs [][]byte, sig Signature) bool {
// 누락됨: 모든 msgs가 서로 다른지 확인하는 assert
...
피해자의 공개 키 pk_v와 메시지 m을 알고 있는 공격자는 pk_a = g^sk_a - pk_v를 등록함으로써, 피해자의 비밀 키를 알지 못해도 (pk_v, m)과 (pk_a, m)에 대한 집합 서명 (aggregate signature)을 위조할 수 있습니다.
AI는 왜 이를 Medium이라고 불렀을까요? AI는 누락된 확인 절차를 정확히 식별했고 로그 키 공격 (rogue key attack)의 명칭까지 언급했습니다. 하지만
// 공격 방식: 문장 S1 = (g, gx, h, hx)에 대한 정직한 증명 π를 제시하되,
// 이를 위조된 문장 S2 = (g, -gx, h, hx)와 결합함
...
이것이 왜 작동할까요? 두 가지 요소가 일치하기 때문입니다:
레이어 1 — 대수학 (Algebra): (-gx)^c mod N = (-1)^c * gx^c mod N. c가 짝수일 때, (-1)^c = 1이 되므로 공격자는 정직한 증명자 (honest prover)와 동일한 중간값들을 얻게 됩니다.
레이어 2 — 직렬화 (Serialization): 챌린지 (challenge)는 FillBytes를 사용하여 해싱되는데, 이 함수는 big.Int의 절댓값을 쓰고 부호를 제거합니다. 따라서 hash(-gx) == hash(gx)가 성립합니다.
각 레이어 자체는 개별적으로 틀리지 않았습니다. 하지만 이들이 결합되면서 정직하게 생성된 모든 증명의 약 50%에 대해 건전성 (soundness)을 깨뜨립니다. 해결책은 checkBounds 단계를 추가하는 것입니다. 즉, 모든 입력은 0 < x < N을 만족해야 하며, 이를 통해 음수 입력을 거부합니다.
이것이 바로 LLM 보안 감사 (security auditing)를 진정으로 놀랍게 만드는 경계 간 추론 (cross-boundary reasoning)의 유형입니다. 집중력 있는 인간 검토자는 대수학을 확인하거나 직렬화를 확인할 수는 있지만, 이 둘 사이의 도약을 수행하려면 놓치기 쉬운 정신적 컨텍스트 스위칭 (mental context-switch)이 필요합니다.
미묘한 대수적 상호작용 버그에서 고전적인 언어적 함정으로 넘어가 보겠습니다:
처음 네 개의 버그는 암호학적 대수학, 직렬화 의미론 (serialization semantics), 그리고 증명자-검증자 계약 (prover-verifier contracts)에 대한 추론을 필요로 했습니다. 다음 버그는 겉보기에는 더 단순하지만, 그 영향력은 결코 작지 않습니다.
버그 5: 비트 단위 OR 스위치로 인한 HPKE PSK 검증 우회 (Medium — 중복)
Go 언어의 고전적인 실수 (footgun): switch 문에서의 case a | b:는 a와 b라는 두 개의 별개 케이스가 아니라, a와 b의 비트 단위 OR 연산 결과값을 갖는 단일 케이스입니다.
// 버그 발생 — hpke/util.go
switch mode {
case modeBase | modeAuth: // == 0x02, 오직 modeAuth (0x02)에만 일치함
...
SetupPSK(..., nil, nil)는 거부되는 대신 빈 PSK로 진행됩니다. 해결책은 쉼표로 구분된 케이스(case modePSK, modeAuthPSK:)를 사용하는 것입니다. 이 버그는 별도로 제출된 보고서의 중복임이 확인되었습니다.
버그 6: int64로 계산된 라그랑주 계수 (Lagrange Coefficients) (Medium)
하나의 발견 내에 포함된 두 개의 독립적인 버그이며, 둘 다 computeLambda에서 발생했습니다:
// 버그 발생 — tss/rsa/rsa_threshold.go
num := int64(1)
den := int64(1)
...
Bug A (오버플로 (overflow)): 약 21명의 플레이어가 참여할 경우, 곱셈 결과가 int64 상한값(~9.2×10¹⁸)을 초과하여 조용히 래핑(wrap)됩니다. 패닉(panic)이 발생하지 않으며, 잘못된 계수(coefficients)가 생성됩니다.
Bug B (절삭 순서 (truncation order)): Shoup의 스킴(scheme)은 δ × num이 den으로 나누어떨어짐을 보장하지만, num 단독으로는 나누어떨어지지 않을 수 있습니다. num/den을 먼저 계산한 다음 δ를 곱하면, 연속되지 않은 쉐어 인덱스(share indices)(일반적인 경우)에 대해 결과값이 절삭(truncate)됩니다.
해결책: 모든 산술 연산을 big.Int로 옮기고, δ × num / den이 왼쪽에서 오른쪽 방향으로 계산되도록 순서를 재조정합니다.
Bug 7: AND-Share 버그를 통한 CP-ABE 접근 제어 무력화 (심각)
이것이 이번 발견의 정수입니다. zkao가 인간이 작성한 기술(skills) 없이 스스로 찾아낸 심각한 취약점입니다:
Ciphertext-Policy Attribute-Based Encryption (CP-ABE, 암호문 정책 속성 기반 암호화)에서 접근 제어는 정책 트리(policy tree)에 의해 정의됩니다. AND 노드는 자식 노드들 사이에 비밀 쉐어(secret shares)를 분할합니다. AND-쉐어 분배 과정에서의 단 한 줄짜리 오프 바이 원(off-by-one) 오류로 인해, 특정 정책 구조는 사용자의 실제 속성과 관계없이 항상 만족되는 것으로 평가되었습니다. 필요한 속성이 없는 공격자가 결코 접근해서는 안 될 암호문을 복호화할 수 있는, 완전한 접근 제어 무력화(access control break)가 발생한 것입니다.
커밋 디프(commit diff)는 이 상황을 명확히 보여줍니다. 해결책은 자식 쉐어 인덱스 오프셋(child-share index offset)을 한 줄로 수정한 것이었습니다. 이는 수학적 명세(mathematical specification)와는 거리가 먼 구현 세부 사항에 존재하는 미묘한 논리 오류이며, 이를 발견하기 위해서는 전체 정책 평가 트리(policy evaluation tree) 전반에 걸쳐 불변량(invariants)을 추적해야 합니다.
자신만의 LLM 보안 감사 파이프라인 구축하기
zkSecurity 실험은 매우 설득력이 있으며, 그 패턴은 재현 가능합니다. Python과 Anthropic SDK를 사용하여 자신만의 LLM 보안 감사 파이프라인을 구축하기 위한 구체적인 시작 아키텍처는 다음과 같습니다:
import anthropic
import os
from pathlib import Path
...
{code}
"""
response = client.messages.create(
...
이것은 간단한 시작점이지만, 세 가지 결정적인 엔지니어링 결정이 귀하의 파이프라인이 유의미한 신호(signal)를 생성할지 아니면 소음(noise)을 생성할지를 결정할 것입니다:
- 프롬프트 길이보다 기술(Skills)의 품질 — 정밀하게 작성된 500토큰(token) 규모의 기술은 5000토큰 규모의 일반적인 보안 프롬프트보다 언제나 더 뛰어납니다.
- 파일 청킹(File chunking) 전략 — 대용량 파일은 의미론적 문맥(semantic context)을 보존하는 지능적인 분할이 필요합니다 (함수를 함께 유지하고, 구조체(struct) 중간에서 분할하지 마십시오).
- 중복 제거(Deduplication) 및 순위 지정(Ranking) — 동일한 코드에 대한 여러 번의 감사 패스(audit passes)는 중복되는 결과물을 생성합니다. 사람이 검토하기 전에 중복 제거 레이어(dedup layer)를 구축하십시오.
"기술(Skills)" 아키텍처: 프롬프트에 전문가 지식 인코딩하기
AI 자동 생성 콘텐츠
본 콘텐츠는 Dev.to AI tag의 원문을 AI가 자동으로 요약·번역·분석한 것입니다. 원 저작권은 원저작자에게 있으며, 정확한 내용은 반드시 원문을 확인해 주세요.
원문 바로가기