
자바스크립트 엔진 퍼저
FuzzIL(사용자 정의 중간 언어)을 기반으로 하는 동적 언어 인터프리터용 (커버리지 기반) 가이드 퍼저입니다. FuzzIL은 변형되어 JavaScript로 번역될 수 있습니다.
이 퍼저를 사용하기 위한 기본 단계는 다음과 같습니다.
swift build [-c release].swift run [-c release] FuzzilliCli --profile=<profile> [기타 cli 옵션] /path/to/jsshell. swift run FuzzilliCli --help도 참조하십시오.Docker 및 Google Compute Engine 내에서 Fuzzilli와 지원되는 JavaScript 엔진을 빌드하고 실행하는 것도 지원됩니다.
main.swift를 확인하여 Fuzzilli 라이브러리의 사용 예를 보고 다양한 구성 옵션을 실험해보세요. 그 다음, Fuzzer.swift에서 상위 수준의 퍼징 로직을 살펴보세요. 거기서부터 흥미로워 보이는 부분을 깊이 파보세요.
이 프로젝트에 대한 패치, 추가 사항, 기타 기여 등을 매우 환영합니다! 그러나 을 빠르게 확인해주세요. Fuzzilli는 대체로 를 따릅니다.
이 프로젝트의 도움으로 발견된 취약점이 있으면 [email protected]으로 간단한 노트(CVE 번호 포함 가능)를 보내거나 풀 리퀘스트를 열어주시면 버그 쇼케이스 섹션에 포함될 수 있도록 하겠습니다. 그 외에도 발견한 취약점에 대해 버그 바운티, CVE 크레딧 등을 당연히 청구하실 수 있습니다 :)
JIT 컴파일러와 같은 핵심 인터프리터 버그를 퍼징할 때는 생성된 프로그램의 의미적 정확성이 문제가 됩니다. 이는 런타임 API 퍼징과 같은 대부분의 다른 시나리오와 대조됩니다. 이러한 시나리오에서는 생성된 코드를 try-catch 구문으로 감싸서 의미적 정확성을 쉽게 우회할 수 있습니다. 의미적으로 올바른 샘플의 허용 가능한 비율을 달성하기 위한 여러 가지 가능성이 있으며, 그 중 하나는 코퍼스의 모든 샘플도 의미적으로 유효한 변이적 접근 방식입니다. 이 경우 각 변이는 유효한 샘플을 유효하지 않은 샘플로 바꿀 확률이 낮습니다.
변이 기반 JavaScript 퍼저를 구현하려면 JavaScript 코드에 대한 변이를 정의해야 합니다. AST나 프로그램의 다른 구문 요소를 변이하는 대신, 프로그램의 제어 흐름과 데이터 흐름에 대한 변이를 더 직접적으로 수행할 수 있는 사용자 정의 중간 언어(IL)가 정의됩니다. 이 IL은 나중에 실행을 위해 JavaScript로 번역됩니다. 중간 언어는 대략 다음과 같습니다:
v0 <− LoadInteger '0'
v1 <− LoadInteger '10'
v2 <− LoadInteger '1'
v3 <− LoadInteger '0'
BeginFor v0, '<', v1, '+', v2 −> v4
v6 <− BinaryOperation v3, '+', v4
Reassign v3, v6
EndFor
v7 <− LoadString 'Result: '
v8 <− BinaryOperation v7, '+', v3
v9 <− LoadGlobal 'console'
v10 <− CallMethod v9, 'log', [v8]
예를 들어 다음과 같은 JavaScript 코드로 간단히 번역될 수 있습니다:
const v0 = 0;
const v1 = 10;
const v2 = 1;
let v3 = 0;
for (let v4 = v0; v4 < v1; v4 = v4 + v2) {
const v6 = v3 + v4;
v3 = v6;
}
const v7 = "Result: ";
const v8 = v7 + v3;
const v9 = console;
const v10 = v9.log(v8);
또는 중간 표현식을 인라인하여 다음과 같은 JavaScript 코드로 번역될 수 있습니다:
let v3 = 0;
for (let v4 = 0; v4 < 10; v4++) {
v3 = v3 + v4;
}
console.log("Result: " + v3);
FuzzIL에는 여러 가지 속성이 있습니다:
Reassign 명령어와 같은 전용 연산을 통해서만 재할당할 수 있습니다.그런 다음 이러한 프로그램에 대해 여러 가지 변이를 수행할 수 있습니다:
Fuzzilli 작동 방식에 대한 훨씬 더 자세한 논의는 여기에서 확인할 수 있습니다.
퍼저는 Swift로 구현되었으며, 일부 부분(예: 커버리지 측정, 소켓 상호작용 등)은 C로 구현되었습니다.
퍼저 인스턴스(Fuzzer.swift에 구현됨)는 다음과 같은 핵심 구성 요소로 구성됩니다:
또한 여러 모듈을 선택적으로 사용할 수 있습니다:
퍼저는 이벤트 기반이며, 다른 클래스 간의 대부분의 상호 작용은 이벤트를 통해 발생합니다. 이벤트는 예를 들어 충돌이나 흥미로운 프로그램 발견, 새 프로그램 실행, 로그 메시지 생성 등의 결과로 전달됩니다. 전체 이벤트 목록은 Events.swift를 참조하십시오. 이벤트 메커니즘은 퍼저의 다양한 구성 요소를 효과적으로 분리하고 추가 모듈을 쉽게 구현할 수 있도록 합니다.
FuzzIL 프로그램은 ProgramBuilder 인스턴스를 사용하여 구축할 수 있습니다. ProgramBuilder는 새 명령어를 생성하고 추가하고, 다른 프로그램의 명령어를 추가하고, 기존 변수를 검색하고, 현재 위치의 실행 컨텍스트(예: 루프 내부에 있는지 여부)를 쿼리하는 등의 메서드를 제공합니다.
Fuzzilli는 REPRL(read-eval-print-reset-loop)이라는 사용자 정의 실행 모드를 사용합니다. 이를 위해 대상 엔진이 수정되어 파이프 및/또는 공유 메모리를 통해 스크립트 입력을 받아 실행한 다음 내부 상태를 재설정하고 다음 스크립트를 기다립니다. 이렇게 하면 프로세스 생성 오버헤드와 엔진 초기화의 많은 부분이 제거됩니다.
대상 프로세스당 하나의 Fuzzer 인스턴스가 있습니다. 이를 통해 프로그램의 동기 실행이 가능하며, 연속 변이 및 최소화와 같은 다양한 알고리즘의 구현이 단순화됩니다. 또한 코퍼스와 같은 내부 상태에 대한 스레드 안전 액세스를 구현할 필요가 없습니다. 각 퍼저 인스턴스는 개념적으로 단일 스레드에 해당하는 자체 DispatchQueue를 가지고 있습니다. 일반적으로 퍼저 인스턴스와의 모든 상호 작용은 해당 인스턴스의 디스패치 큐에서 발생해야 합니다. 이는 큐가 직렬이므로 스레드 안전성을 보장합니다. 자세한 내용은 문서를 참조하십시오.
확장을 위해 퍼저 인스턴스는 트리 계층 구조를 형성할 수 있으며, 이 경우 새로 발견된 흥미로운 샘플과 충돌을 상위 노드에 보고합니다. 그러면 상위 노드는 자식 노드와 코퍼스를 동기화합니다. 트리 내 노드 간 통신은 다양한 방식으로 이루어질 수 있으며, 각각 모듈로 구현됩니다:
이 설계를 통해 퍼저는 단일 시스템의 여러 코어는 물론 여러 시스템으로 확장할 수 있습니다. 너무 많은 인스턴스가 프로그램을 보내면 하나의 상위 노드가 빠르게 과부하될 수 있으므로, 여러 수준의 인스턴스를 구성할 수 있습니다(예: 하나의 루트 인스턴스, 루트에 연결된 16개의 중간 노드, 중간 노드에 연결된 256개의 "리프" 인스턴스). 분산 퍼징에 대한 자세한 내용은 Cloud/ 디렉토리를 참조하십시오.
이 퍼저에 대한 추가 자료:
다음은 Fuzzilli의 도움으로 발견된 일부 버그 목록입니다. 이 목록에는 적어도 해당 소프트웨어의 베타 릴리스에 존재했던 보안 영향을 미치는 버그만 포함되어야 합니다. Fuzzilli는 종종 개발 중 지속적인 퍼즈 테스트에 사용되므로, 취약한 코드가 베타 릴리스에 도달하기 전에 발견되는 경우가 많아 이 목록에 포함되지 않습니다. 그러나 V8에서 Fuzzilli가 최근에 발견한 모든 문제 목록은 여기에서 확인할 수 있습니다.
Fuzzilli로 발견한 버그를 보고해 주신 모든 Fuzzilli 사용자에게 특별히 감사드립니다!
Reflect.construct를 최적화할 때 Map 포인터를 범위 밖에서 읽을 수 있음JSCallReducer::ReduceArrayIndexOfIncludes가 Map 검사를 삽입하지 않음Array.prototype.at을 인라인할 때 잘못된 타입 검사이는 공식적으로 지원되는 Google 제품이 아닙니다.