memory-safety

4 개의 포스트

cloudflare

프로젝트 글래스윙: Mythos가 우리에게 보여준 것 (새 탭에서 열림)

Mythos Preview는 단순히 취약점을 찾아내는 수준을 넘어, 여러 취약점을 연결해 실제 공격 경로를 구성하고 실행 가능한 증명 코드까지 생성하는 새로운 단계의 보안 도구로 평가된다. 그러나 모델의 자발적 거부는 일관되지 않으며, 탐색적 모델의 높은 오탐률 때문에 대규모 운영에는 별도의 안전장치와 검증·분류 체계가 필요하다. Project Glasswing의 핵심 교훈은 강력한 모델 자체뿐 아니라 이를 통제하고 결과를 검증하는 아키텍처가 함께 발전해야 한다는 점이다. ## Mythos Preview가 달라진 점 - Cloudflare는 Project Glasswing의 일환으로 Mythos Preview를 50개가 넘는 자체 저장소에 적용했다. - 이전 범용 프런티어 모델과의 단순한 성능 비교보다, Mythos가 실제로 수행하는 작업의 성격을 이해하는 것이 중요하다고 설명한다. - 기존 모델도 개별 버그를 발견하거나 영향도를 분석할 수 있었지만, 여러 조각을 하나의 공격으로 연결하는 단계에서 자주 멈췄다. ## 여러 취약점을 연결하는 공격 체인 - 실제 공격은 하나의 버그가 아니라 여러 취약점과 공격 원시 기능을 조합해 완성되는 경우가 많다. - 예를 들어: - use-after-free를 임의 메모리 읽기·쓰기 기능으로 전환 - 제어 흐름 탈취 - ROP(Return-Oriented Programming) 체인 구성 - 최종적으로 시스템 제어권 획득 - Mythos Preview는 낮은 심각도로 분류될 만한 개별 버그들을 결합해 더 심각한 실제 공격 경로를 추론할 수 있었다. - 이러한 추론 과정이 자동화된 스캐너보다는 숙련된 보안 연구자의 작업에 가깝게 나타났다는 점이 두드러졌다. ## 반복 실행을 통한 익스플로잇 증명 - Mythos Preview는 의심되는 취약점을 설명하는 데 그치지 않고, 이를 재현하는 코드를 직접 작성한다. - 생성한 코드를 격리된 scratch 환경에서 컴파일하고 실행해 예상한 동작이 발생하는지 확인한다. - 실패하면 오류 결과를 분석하고 가설을 수정한 뒤 다시 시도한다. - 이 반복 루프를 통해 단순한 “취약할 가능성”과 실제 악용 가능한 취약점을 구분한다. - 따라서 취약점 탐지와 익스플로잇 가능성 입증 사이의 간극을 모델 스스로 줄일 수 있다. ## 정당한 보안 연구에서의 모델 거부 - Project Glasswing에서 제공된 Mythos Preview에는 일반 공개 모델에 적용되는 추가 안전장치가 없었지만, 모델 자체적으로 일부 요청을 거부하는 경향이 나타났다. - 그러나 거부 기준은 일관되지 않았다. - 동일한 코드라도 실행 환경의 사소한 변화에 따라 연구를 허용하거나 거부했다. - 심각한 메모리 버그를 확인한 뒤에도 시연용 익스플로잇 작성은 거부할 수 있었다. - 요청 표현을 바꾸거나 실행 시점을 달리하면 반대 결과가 나오기도 했다. - 이러한 자발적 안전장치는 실제로 존재하지만, 확률적이고 상황 의존적이므로 단독 안전 경계로 사용할 수 없다. - 향후 공개되는 강력한 사이버 보안 모델에는 통제된 연구 환경 밖에서도 사용할 수 있도록 별도의 안전장치가 필요하다. ## 신호 대 잡음 문제와 오탐 - 보안 취약점 분석에서 가장 어려운 일 중 하나는 발견된 문제가 실제인지, 악용 가능한지, 즉시 수정해야 하는지를 판별하는 것이다. - AI 스캐너와 AI가 생성한 코드의 확산은 이 문제를 더욱 악화시켰으며, Cloudflare는 여러 후속 검증 단계를 구축해 대응하고 있다. ### 프로그래밍 언어의 영향 - C와 C++는 메모리를 직접 제어할 수 있어 다음과 같은 취약점이 발생하기 쉽다. - 버퍼 오버플로 - 경계 밖 읽기·쓰기 - 메모리 수명 관리 오류 - Rust와 같은 메모리 안전 언어는 이러한 버그 유형의 상당수를 컴파일 시점에 제거한다. - 실험에서는 메모리 비안전 언어로 작성된 프로젝트에서 오탐이 일관되게 더 많이 발생했다. ### 모델의 탐색 편향 - 숙련된 사람은 발견 내용과 함께 확신 수준을 명확히 제시하지만, 모델은 코드에 문제가 없어도 문제를 찾으려는 경향이 있다. - 결과에는 “가능성이 있다”, “잠재적으로”, “이론상 가능하다”와 같은 추측성 표현이 많이 포함된다. - 탐색 단계에서는 이런 보수적·과잉 탐지 성향이 유용할 수 있다. - 하지만 실제 트리아지 큐에서는 각각의 추측성 결과를 사람이 검증하고 기각해야 하므로, 수천 건으로 확장될 경우 인력과 모델 토큰 비용이 크게 누적된다. ## 실용적인 결론 Mythos Preview 같은 모델은 취약점 후보 발굴을 넘어 공격 체인 구성과 재현 증명까지 수행할 수 있어 보안 연구의 생산성을 크게 높일 수 있다. 다만 결과를 그대로 신뢰해서는 안 되며, 격리된 실행 환경, 다단계 검증, 신뢰도 기반 우선순위화, 일관된 안전 정책을 함께 구축해야 대규모 운영에 적합하다.

meta

대규모 환경의 Rust (새 탭에서 열림)

WhatsApp은 최근 30억 명 이상의 사용자들을 멀웨어 위협으로부터 보호하기 위해 미디어 처리 라이브러리를 Rust 언어로 재구축하여 성공적으로 배포했습니다. 이는 글로벌 규모의 서비스에서 Rust가 프로덕션 환경에 적합함을 증명한 사례로, 특히 메모리 안전성이 취약한 C/C++ 기반의 미디어 파싱 라이브러리에서 발생할 수 있는 보안 취약점을 근본적으로 해결하는 데 중점을 두었습니다. 결과적으로 WhatsApp은 성능과 메모리 효율성을 동시에 개선하면서도 사용자 보안을 한층 더 강화하는 성과를 거두었습니다. **미디어 보안의 취약점과 대응의 역사** - 이미지나 영상처럼 무해해 보이는 파일도 운영체제의 취약점을 공격하는 악성 코드를 포함할 수 있으며, 2015년 안드로이드의 'Stagefright' 취약점이 대표적인 사례입니다. - 당시 WhatsApp은 OS 라이브러리의 패치를 기다리는 대신, 자체 개발한 C++ 기반의 미디어 일관성 검사 라이브러리인 'wamedia'를 통해 표준을 준수하지 않는 파일을 사전에 차단하는 방식을 택했습니다. - 하지만 미디어 체크 로직 자체가 신뢰할 수 없는 입력을 자동으로 처리하기 때문에, 이 라이브러리 자체의 메모리 안전성을 확보하는 것이 보안의 핵심 과제로 떠올랐습니다. **Rust를 통한 대규모 현대화 및 성능 개선** - WhatsApp은 점진적인 수정 대신 기존 C++ 버전과 병행하여 Rust 버전의 라이브러리를 새롭게 개발했습니다. - 두 언어 간의 호환성을 보장하기 위해 '디퍼런셜 퍼징(Differential Fuzzing)'과 광범위한 통합 테스트를 거쳐 안전성을 검증했습니다. - 기존 160,000줄의 C++ 코드를 90,000줄의 Rust 코드로 대체했으며, 결과적으로 이전보다 더 우수한 성능과 낮은 런타임 메모리 사용량을 기록했습니다. - 안드로이드, iOS, 웹, 웨어러블 등 다양한 플랫폼 지원을 위한 빌드 시스템 구축과 바이너리 크기 최적화라는 기술적 난관을 극복하고 글로벌 배포를 완료했습니다. **다층 방어 체계 'Kaleidoscope'의 구축** - Rust로 작성된 이 라이브러리들은 'Kaleidoscope'라 불리는 종합 보안 체크 시스템의 핵심 구성 요소입니다. - 단순히 파일 구조의 결함을 찾는 것을 넘어, PDF 내의 스크립트 요소나 임베디드 파일, 확장자를 위조한 MIME 타입 변조 등을 감지합니다. - 실행 파일이나 앱 설치 파일과 같은 위험한 파일 형식을 식별하여 사용자 인터페이스(UX) 차원에서 특별 관리함으로써 비공식 클라이언트나 악성 첨부파일로부터 사용자를 보호합니다. **메모리 안전 언어 중심의 보안 로드맵** - WhatsApp의 분석에 따르면 심각도가 높은 취약점의 대부분은 C/C++의 메모리 관리 문제에서 발생하며, 이를 해결하기 위해 새로운 코드 작성 시 메모리 안전 언어(Memory Safe Language)를 기본으로 선택하고 있습니다. - 불필요한 공격 표면을 최소화하고, 기존 C/C++ 코드에 대해서는 강화된 메모리 할당자와 보안 버퍼 API를 적용하는 등 보안 보증 투자를 병행하고 있습니다. - 이번 Rust 도입의 성공을 바탕으로 향후 더 많은 영역에 Rust 채택을 가속화하여 내부 방어 체계를 지속적으로 강화할 계획입니다. **결론 및 제언** WhatsApp의 사례는 보안이 중요한 클라이언트 사이드 애플리케이션에서 Rust가 단순한 대안을 넘어 최고의 선택지가 될 수 있음을 보여줍니다. 특히 외부에서 유입되는 미가공 데이터를 파싱해야 하는 시스템이라면, 메모리 안전성이 보장되는 Rust로의 전환을 통해 보안 사고의 근본 원인을 제거하고 운영 효율성을 높이는 전략을 적극 검토할 필요가 있습니다.

datadog

밀리초 하나하나까지 단축하기: Datadog Lambda Extension을 Rust로 재구축한 방법 (새 탭에서 열림)

Datadog은 기존 Go 기반의 AWS Lambda 확장이 가진 높은 오버헤드를 해결하기 위해, 이를 Rust 언어로 완전히 재작성한 'Project Bottlecap'을 진행했습니다. 이를 통해 콜드 스타트 시간을 82% 단축하고 메모리 사용량을 40% 절감했으며, 바이너리 크기를 55MB에서 7MB로 줄이는 획기적인 성능 개선을 달성했습니다. 결과적으로 리소스가 제한된 서버리스 환경에서도 사용자 애플리케이션에 영향을 주지 않고 고정밀 텔레메트리 데이터를 수집할 수 있게 되었습니다. ### 기존 범용 에이전트 기반 설계의 한계 - 초기 Datadog Lambda 확장은 다중 호스트나 클러스터 환경에 최적화된 기존 Datadog 에이전트 코드를 기반으로 구축되었습니다. - 범용 에이전트는 대규모 처리량과 캐싱, 버퍼링에 초점이 맞춰져 있어 리소스가 극도로 제한된 람다의 단기 실행 환경에는 부적합했습니다. - 종속성 제거, 바이너리 압축(UPX), 지연 로딩 등 모든 최적화 수단을 동원했음에도 불구하고 콜드 스타트 지연 시간이 450~500ms 이하로 내려가지 않는 성능 한계에 직면했습니다. - 결국 범용 도구와 서버리스 전용 도구의 스케일 차이를 인정하고, 밑바닥부터 다시 작성하는 결정을 내렸습니다. ### Lambda 환경에서 Rust 언어의 전략적 이점 - **안정성 및 메모리 안전성:** 람다 확장이 충돌하면 함수 전체가 종료되고 샌드박스가 초기화되어 다시 콜드 스타트가 발생하는데, Rust는 컴파일 타임에 메모리 안전성을 보장하여 이러한 위험을 최소화합니다. - **바이너리 경량화:** 가비지 컬렉터와 대규모 런타임이 포함된 Go와 달리, Rust는 킬로바이트 또는 낮은 메가바이트 단위의 매우 작은 바이너리를 생성하여 초기 로딩 시간을 줄입니다. - **제한된 환경의 이점:** 람다는 아마존 리눅스와 x86/Arm 아키텍처라는 고정된 환경만 고려하면 되므로, 다양한 환경을 지원해야 하는 시스템 프로그래밍에서 Rust가 가질 수 있는 복잡성 문제가 크게 완화되었습니다. ### Project Bottlecap의 핵심 설계 원칙 - **철저한 성능 오버헤드 통제:** 모든 풀 리퀘스트(PR)마다 벤치마크를 수행하여 성능 저하를 감시했으며, 성능 향상을 위해 공식 AWS SDK 사용을 포기하고 직접 AWS API 호출과 서명 로직을 작성하는 트레이드오프를 감수했습니다. - **핸들러 영향 최소화:** 람다 확장 API를 활용하여 함수 핸들러가 결과를 반환한 후에 텔레메트리를 처리함으로써, 사용자 API 응답 속도에 미치는 영향을 제거했습니다. - **다양한 플러시(Flush) 전략:** 리소스 사용량이 적은 API 함수부터 대규모 배치 작업까지 대응할 수 있도록 데이터 전송 시점을 유연하게 설정할 수 있는 구조를 갖추었습니다. 범용 소프트웨어를 특정 환경에 맞춰 최적화하는 것에는 한계가 있습니다. 특히 실행 시간과 리소스 사용량이 곧 비용과 직결되는 서버리스 환경에서는, 해당 환경의 제약 조건을 반영한 전용 도구를 구축하는 것이 초기 개발 비용이 높더라도 장기적으로 성능과 안정성 측면에서 압도적인 이점을 제공합니다.

figma

Emscripten으로 데이터 (새 탭에서 열림)

Figma의 저장 파일이 간헐적으로 손상되는 문제는 재현이 어려운 비결정적 이벤트와 C++의 메모리 안전성 문제 때문에 장기간 해결되지 않았다. 일반적인 메모리 디버깅 도구로 원인을 찾지 못한 뒤, 키보드·마우스 입력을 생성하는 퍼저로 이벤트를 반복 실행해 문제를 재현하는 데 성공했다. 최종 수정은 FlatBuffers의 잘못된 메모리 접근을 고치는 세 줄짜리 커밋이었지만, 원인을 추적하는 과정에서 Emscripten과 브라우저의 메모리 모델까지 분석해야 했다. ## 간헐적으로 발생한 저장 파일 손상 - Figma는 때때로 다시 읽을 수 없는 잘못된 저장 파일을 생성했다. - 당시 저장 형식은 다음 구조였다. - ZIP 파일 - 내부에 Google FlatBuffers로 인코딩된 문서 - 파일의 전체 바이트 구조는 대체로 정상처럼 보였지만, 뒤쪽 데이터를 가리키는 일부 오프셋이 0으로 변해 있었다. - 데이터가 원래 위치가 아닌 곳에 기록된 현상은 다음과 같은 메모리 안전성 위반을 의심하게 했다. - 초기화되지 않은 메모리 사용 - 해제된 메모리 접근(use-after-free) - 배열 범위를 벗어난 읽기·쓰기 ## C++ 메모리 오류의 추적 난이도 - Figma 에디터는 C++로 작성되어 있었다. - C++는 다음과 같은 장점 때문에 그래픽 소프트웨어에 적합하다. - FreeType, HarfBuzz, Skia 같은 저수준 라이브러리 활용 - 하드웨어와 직접 연결되는 언어 기능 - 성숙한 디버깅 및 최적화 도구 - 높은 성능과 세밀한 메모리 제어 - 반면 C++는 언어 자체가 메모리 안전성을 보장하지 않는다. - 복잡한 언어 설계, 오류가 발생하기 쉬운 표준 라이브러리 API, C에서 물려받은 레거시가 결합되어 대규모 프로젝트에서 메모리 오류를 완전히 피하기 어렵다. ## 일반적인 디버깅 방법의 한계 개발팀은 다음과 같은 방법을 시도했지만 문제를 발견하지 못했다. - 메모리를 해제하지 않아 use-after-free 가능성 제거 - macOS의 malloc 디버깅 옵션 활성화 - Valgrind가 보고한 모든 문제 수정 - 컴파일러 업그레이드 - Clang 정적 분석기가 발견한 문제 수정 이런 도구들은 많은 오류를 찾아내지만, 실행 조건에 따라 드물게 발생하거나 특정 메모리 배치에서만 나타나는 문제까지 항상 잡아내지는 못한다. ## 이벤트 기록과 퍼징으로 재현성 확보 - 문제의 핵심은 비동기 타이머와 네트워크 이벤트 등 웹 앱의 비결정성이었다. - 해결 전략은 다음과 같았다. - 사용자 이벤트를 기록한다. - 동일한 이벤트를 순서대로 재생한다. - 문제가 발생할 때까지 세션을 반복 실행한다. - 이벤트를 무작위로 제거하면서도 문제가 유지되는 최소 테스트 케이스를 만든다. - 실제 사용자 세션에는 너무 다양한 이벤트가 포함되므로, 범위를 키보드와 마우스 이벤트로 제한했다. - 실제 세션을 수집하는 대신 퍼저가 무작위 키보드·마우스 입력을 생성하도록 했다. - 며칠 동안 실행한 결과 여러 건의 저장 실패를 재현할 수 있었고, 이후 반복 가능한 입력 시퀀스를 기반으로 본격적인 디버깅이 가능해졌다. ## Emscripten이 C++를 브라우저에서 실행하는 방식 - Figma는 플러그인 없이 브라우저에서 실행되는 웹 앱이다. - C++ 코드는 Emscripten을 통해 JavaScript로 컴파일된다. - JavaScript는 기본적으로 메모리 안전성과 가비지 컬렉션을 제공하지만, WebGL과 Typed Arrays 덕분에 C의 메모리 모델을 흉내 낼 수 있다. - Typed Array의 특징은 다음과 같다. - 고정된 크기를 가진다. - 모든 원소가 같은 타입이다. - 여러 Typed Array가 하나의 ArrayBuffer를 공유할 수 있다. - 같은 메모리를 `Float32Array`와 `Uint8Array` 등 서로 다른 타입으로 해석할 수 있기 때문에 C/C++의 포인터 캐스팅과 유사한 동작을 구현할 수 있다. ## Emscripten의 포인터와 메모리 변환 Emscripten은 C++의 주요 요소를 JavaScript 구조로 변환한다. - 포인터 읽기: Typed Array 읽기 - 포인터 쓰기: Typed Array 쓰기 - 레지스터: JavaScript 지역 변수 - 스택: `STACKTOP`이라는 스택 포인터로 관리 - 타입 변환: 동일한 ArrayBuffer를 공유하는 Typed Array를 통해 구현 - 생성된 코드는 asm.js라는 제한적인 JavaScript 부분집합을 사용해 JIT 컴파일러가 타입을 추론하고 빠르게 최적화하도록 한다. - 예를 들어 `+$value`는 값을 double로 취급하도록 JIT에 힌트를 주며, 비트 연산은 정수 연산으로 최적화될 가능성을 높인다. ## 실용적인 결론 재현이 어려운 데이터 손상 문제는 도구를 하나씩 추가하는 것만으로 해결되지 않을 수 있다. 비결정적 입력을 통제하고, 퍼징과 이벤트 재생으로 실패 조건을 반복 가능하게 만드는 것이 핵심이며, 컴파일된 실행 환경에서는 원본 언어뿐 아니라 Emscripten의 메모리 모델과 런타임 동작까지 함께 이해해야 한다.