Chris Rohlf
지난 1년 동안, 우리는 최첨단 AI 모델(frontier AI models)이 보안 취약점을 발견하고 악용할 수 있는 능력이 충분하다는 데이터를 많이 발표해 왔습니다. 하지만 이 글에서 보여주는 것처럼, 이전에 알려지지 않은 취약점이 어떻게 악용될 수 있는지 보여주는 것은 최첨단 AI 모델이 사이버 방어 분야에서 할 수 있는 일의 시작에 불과합니다. 본 게시물에서는 자동화된 공격 표면 분석 및 축소(automated attack surface analysis and reduction)에 대한 초기 연구 결과를 요약합니다. 이는 AI 모델을 사용하여 도달 가능한 코드(reachable code)를 매핑한 다음, 주어진 배포 환경이 노출할 필요가 없는 모든 것에 대한 접근을 제거하거나 제한하는 과정입니다.
자동화된 공격 표면 분석 및 축소는 단기적으로 배포된 소프트웨어의 보안을 의미 있게 향상시킬 수 있습니다. 개별 취약점을 찾아 수정하여 소프트웨어를 보호하는 것은 확장성이 떨어집니다. 심지어 치명적인 취약점을 수정하더라도, 남아 있는 다른 취약점들에 대해서는 아무것도 알려주지 못합니다. 반면에 코드 도달 가능성(code reachability)을 줄이는 것은 현재 활발하게 악용되고 있을 수 있는 취약점을 포함하여 알려지지 않은 취약점들의 심각도를 낮출 수 있습니다. 이러한 기술들은 코드에 존재하는 알려지지 않은 취약점들을 제거하는 것이 아니라, 해당 취약점에 도달하기 어렵게 만들고 그로 인해 심각도를 낮춥니다. 코드베이스의 공격 표면을 최소화하는 것은 개별 취약점을 찾거나 수정할 필요 없이 더 나은 보안 결과를 가져옵니다.
아래에서는 Claude를 사용하여 대규모 오픈 소스 코드베이스에 공격 표면 축소 기술을 적용하여 수집한 데이터 중 일부를 미리 보여드립니다. 저희의 목표는 우리가 설명하는 방법을 통해 Claude가 소프트웨어 보안을 의미 있고 경제적으로 개선할 수 있음을 보여주는 것입니다. 구체적으로, 우리는 25개 프로젝트(다양한 언어)에 걸쳐 Claude를 실행했으며, 이 과정에서 493개의 이슈를 발견했습니다. 이러한 결과를 염두에 두고, 개발자들이 자신들의 코드에 유사한 기술을 어떻게 적용할 수 있는지 논의합니다.
코드베이스 공격 표면 축소 방법론
시간이 흐르면서 소프트웨어는 추상화 계층(abstraction layers)의 추가, 새로운 의존성(dependencies), 그리고 새로운 기능들이 하나둘씩 쌓여가면서 점차 복잡해지는 경향이 있습니다. 이로 인해 원래 개발자가 의도했던 방식과 매우 다르게 작동하는 경우가 많습니다. 이것이 공격자들이 종종 성공할 수 있는 큰 이유 중 하나입니다. 그들은 코드의 개발자가 의도했거나 설계한 대로 작동하는 방식보다는, 실제 관행에서 해당 코드 조각이 어떻게 동작하는지를 정확하게 연구하기 때문입니다.
자동화된 공격 표면 분석(attack surface analysis) 및 축소 기술은 불필요한 공격 표면을 발견하고, 그 코드를 적절한 게이트 뒤로 이동시키는 것을 목표로 합니다. 여기에는 인증 전(pre-authentication)에서 인증 후(post-authentication)로, 0클릭(0-click)에서 1클릭(1-click)으로, 기본 활성화(on-by-default)에서 명시적 옵트인(explicit opt-in)으로, 또는 프로토콜의 첫 메시지에서 세션 상태 머신(session state machine)의 더 깊은 곳으로 코드를 이동시키는 것이 포함될 수 있습니다. 일부 코드는 이러한 단계 이전에 도달할 수 있도록 의도되었지만, 실제로는 그렇지 않은 경우가 많으며, 수천 또는 수백만 줄에 달하는 코드 변경(code churn)이 가져오는 복잡성을 고려할 때, 기능의 불필요한 노출은 종종 간과됩니다.
코드의 공격 표면을 축소하는 방법은 매우 다양합니다. 저희 자체 연구 노력에서는 특히 세 가지 방법에 초점을 맞췄습니다:
기본 구성(Default configuration). 배포된 기본 구성 상태에서 도달할 수 있지만, 어떤 기본 기능에 의해서도 사용되도록 의도되지 않은 코드를 식별했습니다.
구성 상호작용(Configuration interaction). 개별적으로는 합리적으로 보이지만, 함께 구성될 때 문제를 일으키는 옵션 쌍을 발견했습니다 (예를 들어, 보안 제어를 조용히 비활성화하거나, 해당 코드가 여전히 도달 가능한 상태임에도 불구하고 기능의 동작을 정의하지 않은 채로 남겨두는 경우).
초기 도달 가능성(Early reachability). 적절한 보안 제어(예: 인증) 이전에, 피해자 상호작용 없이, 또는 프로토콜 상태 전이(protocol state transition) 이전에 실행되는 보안에 민감하고 복잡한 코드를 식별했습니다.
클로드(Claude)가 공격 표면(attack surface)에 대한 이 세 가지 설명을 바탕으로 대규모의 복잡한 코드베이스에서 실제 문제를 얼마나 잘 발견할 수 있는지 이해하고자 했습니다. 우리는 C, C++, Java, PHP, Python 등 다양한 언어로 작성된 25개의 오픈 소스 프로젝트(총 5백만 라인 이상의 코드를 대표)를 분석했습니다. 클로드는 이 프로젝트들 전반에 걸쳐 17,000개가 넘는 구성 옵션 목록을 만들었습니다. 그중 3,842개는 보안과 관련된 제어 장치나 기능의 구성을 다루고 있었습니다.
저희 연구 목적으로, 이러한 오픈 소스 프로젝트들을 세 가지 광범위한 범주로 나누었습니다:
공개(Public). 이 프로젝트들은 접근할 수 있는 모든 사용자에게 서비스를 제공하거나, 식별할 수 없는 당사자가 작성한 데이터를 소비하도록 구축되었습니다. (예: 상위 응답을 파싱하는 DNS 리졸버 또는 원본 응답을 파싱하는 프록시)
게이트(Gated). 이 프로젝트들은 인증된 사용자(비밀번호, 키 또는 인증서 소지자, 또는 등록된 장치를 가진 사용자 등)에게 서비스를 제공하도록 의도되었습니다. 소프트웨어는 여전히 임의의 클라이언트 요청을 허용하지만, 사용자가 인증되기 전까지는 어떤 기능도 사용할 수 없습니다.사설(Private). 이들은 운영자의 자체 사설 환경에 서비스를 제공하기 위해 구축되고 배포됩니다. 이러한 경우, 인증 계층과 같은 제어를 추가하는 것은 선택 사항이지만, 소프트웨어의 범위 밖에 있습니다.
이러한 범주들은 저희 결과에 맥락을 부여하는 데 도움이 됩니다. 예를 들어, 공개(Public) 코드베이스는 기본 구성에서 접근 가능해야 하는 비율이 높을 것으로 예상되지만, 게이트(Gated) 범주의 프로젝트에서는 그 반대가 사실일 것입니다.
클로드의 발견 사항
클로드는 위에서 설명한 세 가지 방법을 사용하여 493개의 문제를 발견했습니다. 그 결과에서 몇 가지 주제가 눈에 띄었습니다.
먼저, Claude는 11개 게이티드 프로젝트 모두에 의도치 않게 도달 가능한 코드(unintentionally reachable)가 있음을 발견했습니다 (비록 이 코드가 해당 프로젝트 전체 코드 라인 수의 상당한 비율을 차지하지는 않았지만). 구체적으로, Claude는 게이티드 코호트의 기본 설정에서 93개의 불필요한 사전 인증 표면(pre-authentication surfaces), 인증 전에 코드가 실행되거나 상태가 기록되는 위치 37곳, 그리고 관리자가 구성한 사전 인증 제어를 조용히 약화시키는 61가지 구성 조합을 발견했습니다. 이 문제들 중 어느 것도 단독으로 즉시 악용 가능하지는 않았지만, 필요한 것보다 훨씬 더 큰 공격 표면(attack surface)을 만듭니다.
각 기술별로 발견된 이슈의 분류 내역입니다. 우리가 평가한 25개 프로젝트 중 10개는 Public, 11개는 Gated, 그리고 4개는 Private이었습니다. 게이티드 코호트가 가장 놀라운 결과를 포함하고 있었으며 높은 수의 조기 도달 가능성(early reachability) 발견을 보였습니다.
둘째, 구성 상호작용 문제 완화에 대한 Claude의 작업은 (모든 프로젝트 유형에 걸쳐) 가장 흥미롭고 영향력 있는 발견 중 일부를 산출했습니다. Claude는 오류나 경고 없이 수락되었으며 그 상호작용에 대한 예상 동작 문서가 없는 보안 민감한 구성 조합 162개를 발견했습니다. 이 사례 중 94개에서 해당 조합은 보안 제어를 조용히 비활성화시켰습니다. 나머지 68개 사례는 동일한 보안 메커니즘의 불일치였습니다 (예: 설정상 비활성화되었다고 되어 있어도 리스너나 로그인 경로가 여전히 도달 가능하게 남아 있음; 두 가지 설정이 충돌하며, 어떤 설정이 우선권을 가질지는 구성 파일에서의 순서에 따라 달라짐; 관리자가 구성한 보호 기능이 아무것도 하지 않으면서 그 코드는 계속 도달 가능한 상태로 남아 있음). 이러한 구성 조합 중 일부는 비표준적이거나 자주 사용되지 않을 수 있지만, 이 프로젝트의 관리자에게는 이런 방식으로 코드를 구성하고 배포하는 것을 막을 것이 없습니다.
Claude는 또한 네 개의 관련 없는 프로젝트에서 독립적으로 나타난 특정 패턴을 발견했습니다. 이는 다음과 같이 작동합니다. 관리자가 IP 주소 대신 호스트 이름 또는 와일드카드 패턴을 사용하여 원격 클라이언트를 위한 접근 규칙을 작성합니다. 클라이언트가 연결하면 서버는 해당 규칙과 클라이언트의 호스트 이름을 비교합니다. 서버가 이전에 그 호스트 이름을 학습했는지 여부는 역방향 DNS 조회(reverse DNS lookups)를 제어하는 별도의 옵션에 달려 있습니다 (대부분의 프로젝트에서는 이 기능이 비활성화되어 있었습니다). 조회가 꺼져 있으면, 서버는 클라이언트의 IP 주소만을 갖게 됩니다. 서버는 그 규칙을 평문 텍스트로 비교합니다. 하지만 호스트 이름 패턴은 절대 IP 주소와 일치할 수 없습니다. 이는 거부(deny) 규칙이 결코 작동하지 않으며, 목록이 열린 상태(fail open)가 된다는 것을 의미합니다. 영향을 받은 프로젝트 중 일부는 문서에서 호스트 이름 규칙이 DNS 조회가 활성화되는 데 의존한다고 언급하지만, 이 방식으로 작성된 거부 목록이 열린 상태로 실패한다는 점은 아무도 명시하지 않았습니다.
세 번째로, Claude는 종종 두 가지 구성 옵션이 보안 검사를 조용히 무효화하는 방식으로 충돌하는 것을 발견했습니다. 한 사례에서는 관리자가 연결하는 모든 클라이언트가 인증서로 자신의 신원을 증명하도록 요구할 수 있었습니다. 다른 사례에서는 해당 코드가 그 확인을 필요할 때까지 지연시키도록 의도되었지만, 실제로는 검사 자체를 완전히 비활성화했습니다. 따라서 신뢰할 수 있는 클라이언트만 허용하도록 구성된 서버는 누구로부터든 연결을 수락하게 됩니다. 이는 많은 요청 처리 코드(인증서 확인 뒤에 있어야 했음)가 인증되지 않은 클라이언트에 노출되게 했습니다. 이 소프트웨어는 기본 설정에서 두 가지 구성 옵션을 모두 경고 없이 수락했으며, 문서에는 인증서 요구 사항이 무효화되었다는 언급이 전혀 없었습니다. 이러한 상충되는 구성 옵션 세트는 해당 소프트웨어에 21년 동안 존재해 왔습니다.
마지막으로, Claude는 기본적으로 인증을 비활성화한(그리고 관리자가 직접 활성화할 것으로 예상했던) 프로젝트의 문제를 발견했습니다. TLS 클라이언트 인증서가 필요했던 옵션은 프로젝트 네트워크 엔드포인트 대부분에서 작동했지만, 일부 다른 엔드포인트에서는 조용히 무시되었습니다 (이들은 서로 다른 네임스페이스를 가진 경로를 통해 TLS 설정을 로드하는 곳들). 이는 인증서가 없는 클라이언트도 TLS 핸드셰이크를 완료하고 영향을 받는 엔드포인트에서 세션을 열 수 있음을 의미했습니다. 이 무시된 설정에 대해서는 아무런 로그도 기록되지 않았습니다. 해당 옵션이 실제로 작동할지 여부는 관리자가 구성에서 사용하는 네임스페이스에 따라 달라졌습니다. 설정 이름의 한 형태는 모든 엔드포인트에서 작동하지만, 대부분의 예제에서 사용되는 더 짧은 형태는 영향을 받는 곳에서는 누락되었습니다.
사례 연구: OpenSSH
놀랍게도, 전체 코드 라인 수 대비 비율로 측정했을 때, Claude가 공격 표면을 가장 크게 줄일 수 있었던 프로젝트는 OpenSSH였습니다. OpenSSH는 안전한 원격 액세스를 제공하는 고품질의 C 기반 코드베이스입니다. Claude는 10.4 버전의 SSH 데몬에서 입력 경로를 추적하여, 인증되지 않은 클라이언트가 기본 구성에서 접근할 수 있는 ML-DSA-44(양자내성 서명 알고리즘)에 대한 새롭고 실험적인 코드가 약 4,500줄에 달한다는 것을 발견했습니다. ML-DSA는 기본적으로 활성화되어 있지 않지만, 관리자가 해당 알고리즘을 명시적으로 제외했음에도 불구하고 코드는 접근 가능했습니다. 데몬은 클라이언트가 보낸 어떤 키 유형이든 파싱했고(그리고 인증서의 경우 임베디드 CA 서명도 검증했습니다). 그 후에야 해당 키 유형이 허용되었는지 확인했습니다. 이 복잡하고 메모리 안전하지 않은 코드가 구성에 의해 활성화되지 않았을 때 접근 가능할 이유가 없습니다. ML-DSA-44에 있는 4,500줄의 코드는 기본 sshd에서 OpenSSH 자체의 사전 인증 코드 중 약 16%를 차지합니다. 저희는 이 내용을 패치와 함께 OpenSSH 개발자들에게 보고했습니다. 10.5 버전에서는 확인 절차를 파싱 전에 이동시켰습니다.
Claude는 10.5 체크에서 두 가지 허점을 지적했습니다. 첫 번째는 클라이언트가 주장한 내용과 실제로 전송한 내용 간의 불일치였습니다. 이 체크는 클라이언트가 광고하는 알고리즘 이름을 확인했지만, 파서는 키 블롭(key blob) 내부에 포함된 타입 문자열에 의해 선택되었습니다. 따라서 클라이언트는 ssh-ed25519를 광고하여 체크를 통과하면서도, 실제로는 파싱될 ML-DSA 키를 전송할 수 있었습니다.