-
OSS-fuzz 구동 과정에서 적용 예시IT/Open-Source 2024. 2. 21. 05:25
OSS-Fuzz는 Google에서 개발한 오픈 소스 소프트웨어를 위한 지속적인 퍼징(fuzzing) 서비스입니다. 퍼징은 소프트웨어의 보안 취약점이나 버그를 찾기 위해 임의의 데이터를 자동으로 생성하여 프로그램에 입력하는 기법입니다. OSS-Fuzz는 특히 C/C++과 같은 메모리 안전성이 보장되지 않는 언어로 작성된 오픈 소스 프로젝트를 대상으로 합니다.
OSS-Fuzz 구동 과정에서 적용되는 예시는 다음과 같습니다:
- 프로젝트 통합
- 오픈 소스 프로젝트가 OSS-Fuzz에 통합되기 위해서는 프로젝트 소스 코드에 퍼징 대상이 될 함수들을 정의해야 합니다. 이 함수들은 보통 '퍼징 타겟'이라고 불리며, 퍼저(fuzzer)가 생성한 입력 데이터를 처리합니다.
- 퍼즈 타겟 작성
- 퍼즈 타겟은 프로젝트의 API를 호출하고,
- 퍼저가 생성한 임의의 입력 데이터를 이 API에 전달하는 간단한 프로그램입니다.
- 이 타겟은 프로그램의 버그를 드러낼 수 있는 코드 경로를 실행해야 합니다.
- 자동화된 퍼징
- OSS-Fuzz는 통합된 프로젝트에 대해 지속적으로 퍼징을 수행합니다.
- 퍼저는 임의의 입력 데이터를 생성하고, 이를 퍼즈 타겟에 전달하여 프로그램의 반응을 관찰합니다.
- 이 과정에서 발생하는 모든 예외, 충돌, 메모리 누수 등은 자동으로 기록됩니다.
- 결과 분석 및 버그 보고
- 퍼징 중에 발견된 문제점은 OSS-Fuzz 시스템에 의해 자동으로 분석되고,
- 관련 정보와 함께 버그 트래커에 기록됩니다.
- 이 정보에는 충돌을 재현할 수 있는 입력 데이터, 스택 트레이스, 메모리 덤프 등이 포함될 수 있습니다.
- 개발자 피드백
- 프로젝트 개발자는 OSS-Fuzz로부터 버그 보고를 받고,
- 이를 바탕으로 코드를 수정하여 보안 취약점이나 버그를 해결합니다.
- 수정 사항이 프로젝트의 메인 리포지토리에 반영되면, OSS-Fuzz는 변경 사항을 자동으로 감지하고
- 다시 퍼징을 수행하여 수정이 문제를 해결했는지 확인합니다.
OSS-Fuzz는 특히 보안 관련 취약점을 찾는데 매우 효과적이며, 많은 오픈 소스 프로젝트들이 이를 통해 소프트웨어의 안정성과 보안성을 향상시켰습니다.
예를 들어, OpenSSL, systemd, Chrome 등 많은 유명한 프로젝트들이 OSS-Fuzz를 사용하여 버그를 찾고 수정했습니다.
활용 코드
OSS-Fuzz를 활용하는 과정에서의 예시 코드는 다음과 같습니다. 여기서는 간단한 C++ 프로그램을 대상으로 한 퍼즈 타겟의 작성 방법을 설명하겠습니다. 이 예시에서는 가상의 문자열 파싱 함수를 퍼징 대상으로 가정합니다.
프로젝트 코드 예시
가정: 우리가 퍼징할 대상은 ParseMyData라는 함수이며, 이 함수는 문자열을 입력받아 어떤 처리를 수행합니다.
// parse_my_data.cpp #include <string> #include <iostream> void ParseMyData(const std::string &data) { // 가정: 여기서 문자열 'data'를 파싱하는 복잡한 로직이 수행됩니다. // 예를 들어, 특정 패턴을 찾거나, 변환을 수행합니다. // 여기서는 단순화를 위해 입력된 데이터의 길이를 출력합니다. std::cout << "Processing data of length: " << data.length() << std::endl; }
퍼즈 타겟 작성
ParseMyData 함수를 테스트하기 위한 퍼즈 타겟을 작성합니다. 이 코드는 OSS-Fuzz에 의해 자동으로 호출되며, 퍼저가 생성한 임의의 입력 데이터로 ParseMyData 함수를 실행합니다.
// fuzz_parse_my_data.cpp #include <stdint.h> #include <stddef.h> #include <string> extern "C" void ParseMyData(const std::string&); // 퍼즈 타겟 함수 extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) { // 퍼저가 생성한 데이터를 std::string으로 변환합니다. std::string str_data(reinterpret_cast<const char*>(data), size); // 변환된 데이터로 'ParseMyData' 함수를 호출합니다. ParseMyData(str_data); // 정상 종료를 나타내는 0을 반환합니다. return 0; }
이 코드에서 LLVMFuzzerTestOneInput 함수는 퍼즈 타겟의 진입점입니다. OSS-Fuzz는 이 함수에 임의의 바이트 배열(data)과 그 크기(size)를 전달합니다. 이 데이터는 std::string으로 변환되어 ParseMyData 함수에 전달됩니다. 이 과정에서 발생할 수 있는 모든 예외, 충돌, 메모리 누수 등을 OSS-Fuzz가 자동으로 감지하고 기록합니다.
빌드 파일 작성
OSS-Fuzz 통합을 위해서는 프로젝트에 퍼즈 타겟을 빌드하기 위한 빌드 파일(Dockerfile 및 build.sh)이 필요합니다. 이 파일들은 OSS-Fuzz가 퍼즈 타겟을 컴파일하고 실행하는 방법을 정의합니다.
이러한 단계를 거쳐 프로젝트를 OSS-Fuzz에 통합하면, OSS-Fuzz는 지속적으로 퍼즈 타겟을 실행하면서 새로운 입력 데이터를 생성하고, 발견된 버그를 자동으로 추적합니다. 개발자는 OSS-Fuzz로부터의 피드백을 바탕으로 코드를 개선할 수 있습니다.
프로젝트를 OSS-Fuzz에 통합하기 위해 필요한 Dockerfile과 build.sh 예시
Dockerfile 작성
Dockerfile은 OSS-Fuzz가 퍼즈 타겟을 빌드할 환경을 설정합니다. 아래는 간단한 예시입니다:
# 기본 OSS-Fuzz 기반 이미지를 사용 FROM gcr.io/oss-fuzz-base/base-builder # 필요한 의존성 설치 RUN apt-get update && apt-get install -y make autoconf automake libtool # 프로젝트 소스 코드 복사 COPY . $SRC/<프로젝트_이름> # 작업 디렉토리 설정 WORKDIR $SRC/<프로젝트_이름> # 퍼즈 타겟 빌드를 위한 환경 변수 등 설정 가능 # OSS-Fuzz가 프로젝트를 빌드할 때 실행할 빌드 스크립트 경로 지정 CMD ["./build.sh"]
이 Dockerfile은 OSS-Fuzz의 빌드 인프라에서 사용됩니다. 여기서 $SRC는 소스 코드가 위치하는 디렉토리를 나타내며, <프로젝트_이름>은 실제 프로젝트 이름으로 대체해야 합니다.
build.sh 작성
build.sh 스크립트는 퍼즈 타겟을 실제로 빌드하는 명령을 포함합니다. 아래는 간단한 예시입니다:
#!/bin/bash -eu # 프로젝트 빌드 도구를 사용하여 프로젝트 빌드 (예: make, cmake 등) # make, cmake 등 프로젝트에 맞는 빌드 명령을 사용 make -j$(nproc) # 퍼즈 타겟을 $OUT 디렉토리에 복사 # $OUT은 OSS-Fuzz가 퍼즈 타겟 실행 파일을 기대하는 위치 cp $SRC/<프로젝트_이름>/fuzz_parse_my_data $OUT/
이 스크립트는 OSS-Fuzz가 퍼즈 타겟을 빌드할 때 실행됩니다. $SRC는 소스 코드가 위치하는 디렉토리, $OUT은 빌드된 실행 파일을 출력할 디렉토리를 나타냅니다. <프로젝트_이름>과 fuzz_parse_my_data는 각각 실제 프로젝트 이름과 퍼즈 타겟 실행 파일 이름으로 대체해야 합니다.
이러한 파일들을 프로젝트에 추가하고 OSS-Fuzz에 통합 요청을 하면, OSS-Fuzz는 제공된 Dockerfile과 build.sh를 사용하여 퍼즈 타겟을 빌드하고 실행합니다. 이후 OSS-Fuzz는 지속적으로 퍼즈 타겟을 실행하며 새로운 버그를 자동으로 탐지하고, 발견된 버그에 대한 상세 정보를 프로젝트 개발자에게 제공합니다. 이를 통해 개발자는 소프트웨어의 안정성과 보안을 향상시킬 수 있습니다.
- 프로젝트 통합