Difuze
유효한 입력을 자동으로 생성하고 커널 드라이버의 실행을 트리거하는 인터페이스 인식 퍼징 도구인 difuze를 제공한다.
difuze는 드라이버 핸들러 식별부터 장치 파일 이름에 매핑, 복잡한 인수인스턴스 구성 등 완전히 자동화 된 기능이다.
- 사용자 공간 응용 프로그램
- 주소공간 레이아웃 랜덤화
- 데이터 실행보호
- SELinux등
커널은 코어 커널 코드와 장치 드라이버 두가지 유형이있다
코어 커널 코드
- syscall 인터페이스를 통해 엑세스
- open()
- execve()…
장치 드라이버: POSIZE 호환 시스템은 ioctl인터페이스를 통해 액세스
- 버그는 타사 업체가 제공한 드라이버 코드가 주요 원인
- ioctls는 장치 드라이버에 따라 다름
DIFUZE는 유효한 명령 및 관련 데이터 구조체를 포함하여 특정 ioctl 인터페이스를 복구하기 위해 커널 드라이버 코드의 자동 정적 분석을 수행한다. 복구된 인터페이스를 사용하여 ioctl 호출에 대한 입력을 생성하며, 사용자 공간 프로그램에서 커널로 전송될 수 있다. 이러한 입력은 드라이버가 사용하는 명령과 구조가 일치하여 ioctl을 효율적이고 심층적으로 탐색을 가능하게 한다. 복구된 인터페이스를 통해 퍼저는 데이터를 변경할 때 의미 있는 선택을 할 수 있다.
즉, 포인터, 열거 형, 정수와 같은 입력된 필드는 단순한 바이트의 시퀀스로 처리하면 안 된다.
– POSIX 표준은 사용자 공간 애플리케이션과 장치 드라이버의 상호작용을 위한 인터페이스를 명시
- syscall
- read
- write
- seek…
ioctl
- 기존 시스템 호출을 통해 구현될 수 없는 일부기능은 ioctl 인터페이스 1을 통해 POSIX 표준에서 지원
- ioctl() 인터페이스는 임의의 드라이버 지정 구조체를 입력으로 수신
- ioctl호출에 제공된 데이터는 복잡하고 비표준이라서 분석이어렵다.
심볼릭 실행(symbolic execution)과 정적 분석(static analysis)
- Symbolic execution
심볼릭 실행은 심볼릭 변수를 사용하여 제한된 입력을 생성하고 복잡한 검사를 만족시키는 기술이다.
- Static analysis
정적 분석은 해당 프로그램을 실행하지 않고 프로그램 취약점을 찾아내는 널리 사용되는 기술이다. 정밀도를 극대화하기 위해, 이러한 기법들도 분석을 수행하기 위해 일반적으로 소스 코드가 필요된다. 많은 시스템 커널(리눅스 커널 포함)과 장치 드라이버가 오픈 소스이기 때문에 커널 보안은 정적 분석에서 큰 이익을 얻을 수 있다. 예를 들어, Ashcraft 등은 리눅스와 OpenBSD 커널의 신뢰할 수 없는 소스에서 읽은 정수를 잡기 위해 컴파일러 확장을 개발했다. Post 등 리눅스 커널에서 교착 상태 및 메모리 누수를 찾기 위해 경계 모델 검사기를 사용했다. Ball 등 윈도우 드라이버의 정확성을 증명하기 위해 일련의 규칙으로 정적 분석 도구를 만들었다.
요약
- Interface recovery
첫 번째 단계에서 DIFUZE는 제공된 소스를 분석하여 대상 호스트에서 어떤 드라이버가 활성화되어 있는지, 어떤 장치 파일을 사용하여 상호작용할 수 있는지, 어떤 ioctl 명령을 받을 수 있는지, 이러한 구조체로 이들 명령에 전달 될 구조체를 탐지한다. 이 일련의 분석은 LLVM을 사용하여 구현되며 ,4절에서 자세히 설명되어 있다. 이 단계의 최종 결과는 대상 드라이버, 대상 ioctl 명령어 및 구조체 유형 정의에 대한 장치 파일 이름의 튜플(Tuples) 집합이다.
- Structure generation
각 구조체에 대해 DIFUZE는 구조체 인스턴스(Instance)를 지속적으로 생성하는데, 이는 이전 단계에서 복구된 유형 정보의 인스턴스화를 나타내는 매모리 내용이다. 이러한 인스턴스는 관련 대상 장치 파일 이름 및 대상 ioctl 명령 식별자와 함께 대상 호스트에 기록 및 전송된다. 이 단계는 5절에서 자세히 설명되어 있다.
- On-device execution
실제 ioctl 트리거링 컴포넌트는 대상 호스트 자체에 있다. 대상 장치 파일 이름, 대상 ioctl 명령 및 생성된 구조체 인스턴스를 수신하면 실행자는 ioctl 실행을 트리거합니다. 이 단계는 6절에서 논의한다. 장치 드라이버의 인터페이스는 장치와 통신하는 데 사용되는 장치 파일의 이름/경로, 해당 장치에 대한 ioctl 명령에 대한 유효한 값 및 다른 ioctl 명령어에 대한 ioctl 데이터 인수의 구조체 정의로 구성
Interface recovery
DIFUZE
- 분석하여 대상 호스트에서 어떤 드라이버가 활성화되어 있는지
- 어떤 장치 파일을 사용하여 상호작용할 수 있는지, 어떤 ioctl 명령을 받을 수 있는지
- 이러한 구조체로 이들 명령에 전달 될 구조체를 탐지
이 일련의 분석은 LLVM을 사용하여 구현되며 4절에 자세히 설명되어 있다. 이 단계의 최종 결과는 대상 드라이버, 대상 ioctl 명령어 및 구조체 유형 정의에 대한 장치 파일 이름의 튜플(Tuples) 집합이다.
DIFUZE가 리눅스 장치 드라이버에서 LLVM 분석을 수행할 수 있도록 하기 위해 몇 가지 단계를 수행한다
-
GCC 컴파일
- 먼저, GCC를 이용하여 컴파일을 위한 대상 호스트의 커널과 드라이버 소스를 설정하는 수동 단계를 수행해야 한다. 이것은 일반적으로 문서화가 잘되어있는 프로세스이지만, 모바일 장치 공급 업체는 GPL로 규정된 소스 코드 릴리스를 쉽게 컴파일할 수 있는 방법을 찾지 못하므로 일부 수동 구성 작업이 필요하다. 일단 소스 트리를 GCC로 컴파일할 수 있게 되면 전체 컴파일을 실행하고 실행된 모든 명령을 기록한다.
- GCC-LLVM 변환
- 컴파일 단계에서 DIFUZE 용으로 만든 GCC-LLVM 명령 변환 유틸리티를 사용하여 실행된 명령 로그를 처리한다. 이 유틸리티는 GCC에 의해 예상되는 형식에서 LLVM 유틸리티에 의해 예상되는 형식으로 명령 행 플래그를 변환하고 LLVM을 통해 커널 소스를 컴파일할 수 있다. 컴파일 과정에서 LLVM은 각 소스 파일에 대한 바이트코드(Bitecode) 파일을 생성한다. 바이트코드 파일에 디버그 정보를 포함시킬 수 있으므로 4.6절에 설명된 구조체 정의를 추출하는 데 도움이 된다.
-
바이트코드 통합
- DIFUZE가 수행하는 분석은 각 드라이버에서 별도로 적용된다. 따라서 다양한 바이트코드 파일을 통합하여 드라이버 당 단일 바이트코드 파일을 만든다. 이를 통해 단일 바이트코드 파일에 대한 인터페이스 복구 분석을 수행할 수 있어 분석이 단순화된다. 이 통합 바이트코드 파일은 다음 단계에서 분석을 수행하는 데 사용된다.
Structure generation
각 구조체에 대해 DIFUZE는 구조체 인스턴스(Instance)를 지속적으로 생성하는데, 이는 이전 단계에서 복구된 유형 정보의 인스턴스화를 나타내는 매모리 내용이다. 이러한 인스턴스는 관련 대상 장치 파일 이름 및 대상 ioctl 명령 식별자와 함께 대상 호스트에 기록 및 전송된다. 이 단계는 5절에서 자세히 설명되어 있다.
On-device execution
실제 ioctl 트리거링 컴포넌트는 대상 호스트 자체에 있다. 대상 장치 파일 이름, 대상 ioctl 명령 및 생성된 구조체 인스턴스를 수신하면 실행자는 ioctl 실행을 트리거합니다. 이 단계는 6절에서 논의한다.