이번에는 UEFI를 동적으로 분석하는 방법을 알아보겠다. 먼저 알아두어야 할 것은 Flash Chip에서 덤프한 UEFI를 전체적으로 한번에 실행시키는 것은 불가능하다는 것이다. 펌웨어 이미지 내부에 존재하는 모듈들은 실제 존재하는 하드웨어 장비와의 상호작용이 필요하기 때문에 의존성 문제가 존재하기 때문이다. 고로 우리는 각각의 모듈에 대해 동적 분석하는 방법을 찾아보았고 다음과 같은 동적 분석 방법들을 알게 되었다.
qiling은 Unicorn Engine기반으로 동작하는 바이너리 에뮬레이션 프레임워크로, 다양한 운영체제 뿐만 아니라 UEFI 에뮬레이팅까지 지원한다. 또한 Unicorn 기반이기 때문에 각종 레지스터 변경이나 함수 후킹등의 작업을 수행할 수 있는 장점이 있다. 또한 디버깅까지 할 수 있다.
efi-fuzz는 brick을 만든 Sentinel-One에서 만든 efi 전용 NVRAM 퍼저이다. 이 퍼저는 위의 qiling을 기반으로 동작하고 unicornafl을 통한 퍼징을 수행한다.
usage: efi_fuzz.py [-h] [-c COVERAGE_FILE] [-f {crash,stop,ignore,break}] [-e END] [-t TIMEOUT] [-o {trace,disasm,debug,dump,off}] [-s {memory,smm_callout,uninitialized,smm} [{memory,smm_callout,uninitialized,smm} ...]] [-j JSON_CONF] [-v NVRAM_FILE] [-r ROM_FILE] [-x EXTRA_MODULES [EXTRA_MODULES ...]] {run,fuzz} target {nvram} ...positional arguments: {run,fuzz} What should I do? target Path to the target binary to fuzz {nvram} Fuzzing modes nvram Fuzz contents of NVRAM variablesoptional arguments:-h,--help show this help message and exit-c COVERAGE_FILE,--coverage-file COVERAGE_FILE Path to code coverage file-f {crash,stop,ignore,break},--fault-handler {crash,stop,ignore,break} What to do when encountering a fault?-e END,--end END End address for emulation-t TIMEOUT,--timeout TIMEOUT Emulation timeout in ms-o {trace,disasm,debug,dump,off},--output {trace,disasm,debug,dump,off} Trace execution for debugging purposes -s {memory,smm_callout,uninitialized,smm} [{memory,smm_callout,uninitialized,smm} ...], --sanitize {memory,smm_callout,uninitialized,smm} [{memory,smm_callout,uninitialized,smm} ...]
Enable memory sanitizer-j JSON_CONF,--json-conf JSON_CONF Specify a JSON file to further customize the environment-v NVRAM_FILE,--nvram-file NVRAM_FILE Pickled dictionary containing the NVRAM environment variables-r ROM_FILE,--rom-file ROM_FILE Path to the UEFI ROM file-x EXTRA_MODULES [EXTRA_MODULES ...],--extra-modules EXTRA_MODULES [EXTRA_MODULES ...] Extra modules to load
대표적으로 탐색할 수 있는 것은 GetVariable을 통한 overflow취약점이다. NVRAM변수값을 퍼징하기 때문에 GetVariable을 통해 퍼징된 NVRAM변수값이 들어와서 프로그램이 오동작하면 crash로 판별한다.
위 사진은 프로젝트를 진행하며 진행한 1-Day 취약점 재현에 대한 Crash 발생 사진이다.