취약한 VLC 인스턴스 재현하기
비디오 파서는 길이, 비트레이트, 프레임레이트, 오디오와 비디오 포맷 등 다양한 파라미터가 있어서 오류가 발생하기 쉽다. 이러한 구성요소를 퍼징하면 취약점을 발견할 수 있을 것이다. CVE-2011-0531은 VLC video player의 원격 코드 실행할 수 있는 취약점이다.
VLC를 다운로드 받는다.
wget https://get.videolan.org/vlc/3.0.17.3/vlc-3.0.17.3.tar.xz
tar xvf vlc-3.0.17.3.tar.xz
cd vlc-3.0.17.3
modules/demux/mkv/mkv.hpp 파일의 MKV_IS_ID 매크로를 다음과 같이 수정한다. 취약점으로 인해 패치되기 전 코드이다.
#define MKV_IS_ID( el, C ) ( EbmlId( (*el) ) == C::ClassInfos.GlobalId)//vulnerable
vi로 /modules/codec/avcodec/avcommon.h 파일을 열어 139번째 줄부터 주석처리한다. AVCodecContext 타입 누락 문제를 방지하려면 C 코드의 모든 줄 앞에 C 주석 지시문을 추가해야 하는데, vim을 이용하면 된다. vim으로 범위 주석 처리하는 방법은 여기를 참고하면 된다.
퍼징을 위한 VLC 빌딩
컴파일에 필요한 의존성 패키지를 설치한다.
sudo apt-get install pkg-config libtool automake
sudo apt-get install autopoint gettext
sudo apt-get install libxcb-shm0-dev libxcb-xv0-dev libxcb-keysyms1-dev libxcb-randr0-dev libxcb-composite0-dev libmatroska-dev libebml-dev libasound2-dev libswscale-dev
AFL++을 설치할 때, LLVM을 비활성화해야 한다. 취약한 코드를 컴파일할 때 LLVM이 에러를 생성하기 떄문이다.
sudo apt-get update
sudo apt-get install -y build-essential python3-dev automake cmake git flex bison libglib2.0-dev libpixman-1-dev python3-setuptools cargo libgtk-3-dev
# try to install llvm 14 and install the distro default if that fails
sudo apt-get install -y lld-14 llvm-14 llvm-14-dev clang-14 || sudo apt-get install -y lld llvm llvm-dev clang
sudo apt-get install -y gcc-$(gcc --version|head -n1|sed 's/\..*//'|sed 's/.* //')-plugin-dev libstdc++-$(gcc --version|head -n1|sed 's/\..*//'|sed 's/.* //')-dev
sudo apt-get install -y ninja-build # for QEMU mode
git clone https://github.com/AFLplusplus/AFLplusplus
cd AFLplusplus
unset LLVM_CONFIG && make -j3
sudo make install
vlc 패키지를 컴파일한다. afl에 내장된 afl-clang-fast 컴파일러로 컴파일한다.
CC="afl-clang-fast" CXX="afl-clang-fast++" ./configure --prefix="$HOME/vlc-3.0.17.3/install" --disable-a52 --disable-lua --disable-qt --disable-skins2 --disable-mad --disable-postproc --disable-avcodec --with-sanitizer=address --enable-matroska
matroska/mkv 플러그인을 enable하여 취약한 코드가 실행되도록 했다.
gdb로 디버깅하기 용이하려면 컴파일러 최적화를 비활성화하는 편이 좋다. 다음 명령어로 가능하다. 설치가 끝나면 install 폴더가 생성된다.
CFLAGS="-O0 –g" make LDFLAGS="-fsanitize=address" && make install
VLC 선택 구현
탐색 공간을 좁히기 위해 AFL에 테스트 하네스로 넣을 파일이나 함수 리스트를 작성할 수 있다.
다음 내용을 selective.txt 이름으로 저장한다.
demux/mkv/Ebml_parser.cpp
demux/mkv/Ebml_parser.hpp
demux/mkv/chapter_command.cpp
demux/mkv/chapter_command.hpp
demux/mkv/chapters.cpp
demux/mkv/chapters.hpp
demux/mkv/demux.cpp
demux/mkv/demux.hpp
demux/mkv/dispatcher.hpp
demux/mkv/dvd_types.hpp
demux/mkv/events.hpp
demux/mkv/events.cpp
demux/mkv/matroska_segment.cpp
demux/mkv/matroska_segment.hpp
demux/mkv/matroska_segment_parse.cpp
demux/mkv/matroska_segment_seeker.cpp
demux/mkv/matroska_segment_seeker.hpp
demux/mkv/mkv.cpp
demux/mkv/mkv.hpp
demux/mkv/stream_io_callback.cpp
demux/mkv/stream_io_callback.hpp
demux/mkv/string_dispatcher.hpp
demux/mkv/util.cpp
demux/mkv/util.hpp
demux/mkv/virtual_segment.cpp
demux/mkv/virtual_segment.hpp
vlc.c
#Fun Parser
fun: EbmlProcessorEntry
fun: main
fun: WaitKeyFrame
다음 명령으로 다시 빌드한다. 이전의 빌드를 제거하고, 'AFL_LLVM_ALLOW_LIST' 환경변수가 selective.txt를 절대경로로 지정하고, CFLAGS 변수는 컴파일러 최적화를 비활성하고, LDFLAGS는 address sanitizer를 활성화한다.
make clean && AFL_LLVM_ALLOWLIST=$(pwd)/selective.txt CFLAGS="-O0 -g" make LDFLAGS="-fsanitize=address" && make install
다음으로 샘플 WebM 파일을 받는다.
mkdir input && cd input
wget https://file-examples.com/wp-content/storage/2020/03/file_example_WEBM_480_900KB.webm
vlc 패키지를 퍼징한다. -t 파라미터는 타임아웃 밀리세컨드를 지정한다. 여기서는 500 밀리세컨트(5초)로 정한다.
afl-fuzz -t 500 -m none -i './input' -o './output' -D -M master -- ./vlc @@
이 때 아래와 같은 문제가 발생할 수 있다.
AFL_I_DONT_CARE_ABOUT_MISSING_CRASHES=1 을 추가해 해결하거나 echo core > /proc/sys/kernel/core_pattern으로 해결한다
AFL_I_DONT_CARE_ABOUT_MISSING_CRASHES=1 afl-fuzz -t 500 -m none -i './input' -o './output' -D -M master -- ./vlc @@ #cvlc is the command line interface
sudo su
echo core > /proc/sys/kernel/core_pattern
exit
리눅스의 경우 Metasploit 예제의 특정 사례에서 VLC에 충돌이 발생한다. 취약점이 발생하는 코드는 gdb-gef를 이용해 demux/mkv/Ebml_dispatcher.hpp 파일의 73번째 줄에 있는 EbmlTypeDispatcher::send() 함수에 중단점을 지정해 확인할 수 있다. [x19](레지스터명)가 가리키는 주소의 내용을 로드하는 것을 시작으로 뒤따르는 어셈블리 명령어는 사용자 입력인 [x5, #24]가 가리키는 주소로 무조건 분기하기 때문에 특히 흥미롭다. 정확한 동작은 mvk(webm) 형식으로 vlc에 제공된 입력 파일에서 파생된 레지스터 x19의 값에 따라 달라진다.
Metasploit 설치
터미널을 통해 Metasploit을 설치하는 과정은 다음과 같다.
sudo apt install ruby gems ruby-dev
git clone https://github.com/rapid7/metasploit-framework.git
cd metasploit-framework
git checkout 716ba68b25bce30c8e4ee994b59b096d1148ced3
sudo apt install libpq-dev libpcap-dev
sudo gem install bundler
sudo gem install racc -v '1.6.2' --source 'https://rubygems.org/'
sudo gem install pg -v '1.4.5' --source 'https://rubygems.org/'
sudo gem install pcaprub -v '0.13.1' --source 'https://rubygems.org/'
sudo bundle install --gemfile /home/loca/metasploit-framework/Gemfile
다음 명령으로 테스트한다.
./msfconsole #실행
msf6 > exit #종료
gdb init 파일에 pending breakpoints를 셋업하고 gdb로 vlc를 실행한다.
bash -c "$(curl -fsSL https://gef.blah.cat/sh)"
echo "set breakpoint pending on" >> ~/.gdbinit
gdb ./vlc-3.0.17.3/vlc
gdb에서 다음 명령어를 입력한다.
b EbmlTypeDispatcher::send
r -I "dummy" "$@" ~/vlc-3.0.17.3/input/file_example_WEBM_480_900KB.webm
-I "dummy "%@" 옵션은 그래픽 없이 cvlc를 실행하는 결과 유사하게 부드럽게 실행할 수 있는 옵션이다.
참고자료에서는 위와 같이 입력하여 분석하는데, 나의 실행 환경에서는 EbmlTypeDispatcher::send라는 함수가 존재하지 않는다고 하여 진행이 불가능했다. selective instrumentation 없이 전체 VLC 프로그램을 빌드해도 마찬가지였다.
*C pseudo code
typedef struct parse_frame {
uint64_t dummy_type;
uint64_t dummy_pos;
uint64_t dummy_frame_nr;
uint64_t * (*framecallback)();
} frame;
typedef struct format_container {
frame frame_arr[255];
char *dummy_data_ptr;
char dummy_buffer[1024];
} container;
uint64_t* var_x0,var_x5,var_x19,var_x30;
//we load the movie and then its frames, every frame has a callback function associated with it.
//ldr x0, [x19]
var_x0 = (format_container*) loadcontainer("file.webm");
//ldr x30, [x0]
var_x30 = (parse_frame*)loadframes(var_x0);
//ldr x5, [x30, #24]
var_x5 = (uint64_t (*)(void) var_x30->framecallback;
//blr x5
var_x5();
이 예시는 취약점을 이해하기 쉽게 원본 코드를 단순화한 코드이다. 콜백 함수를 x5에 넣는다. x5는 CPU가 코드를 실행하러 점프하는 위치이다. 그러나, x19에서 시작하는 경로는 포인터의 포인터의 포인터나 배열의 배열의 포인터를 대표하는uint64_t*** framecallback 등 여러 단계의 간접 지시를 포함한다. Heap spraying은 영상을 메모리에 로드하는 구조를 덮어쓰는 것을 목적으로 한다. 특히 콜백포인터는 우리가 제어하는 위치를 가리키거나, 우리가 원하는 명령을 실행할 코드 가젯을 가리키는 것을 가능하게 한다.
Heap Spyraing은 성공적인 공격 가능성을 증가시키는 기술이다. 기본 아이디어는 힙 메모리를 특별히 만들어진 데이터(쉘 코드, no operation sled 등)으로 채워 이 데이터가 악성 코드를 실행하게 만드는 것이다. 공격자는 취약점이 exploit될 때 공격자의 코드가 실행되도록 할 확률을 높일 수 있다.
VLC MKV 플러그인에서 ROP gadget 찾기
ROP는 return 명령어로 종료되는 프로그램 코드를 재사용할 수 있게 해주어 쉘코드를 주입하는 것보다 강력한 기법이다. 이는 CPU 관리권을 얻고 원하는 명령을 실행하기 위한 가젯(gadget)이라는 작은 어셈블리 코드 조각을 연결(chaining)하는 것을 포함한다. ropper는 이러한 가젯을 찾는데 도움을 주는 유틸리티이다.
pip install ropper
ropper --file /home/loca/vlc-3.0.17.3/modules/.libs/libmkv_plugin.so
0x000000000002fc28: ret;
위와 같이 ret 명령만 있는 부분이 있다. 이는 ROP 체인의 시작 부분을 가리키는 포인터로 사용될 수 있다. 이러한 가젯의 콤비네이션은 전체 셸코드의 실행을 가능케 한다.
exploit을 위한 Metasploit 입력 생성
여기서 vlc_custom.rb를 다운받고 ~./metasploit-framework/modules/explots/에 이동시키고 metasploit을 실행한다.
~/metasploit-framework$ ./msfconsole
msf6 > search vlc
vlc_custom.rb를 metasploit이 찾지 못한다. 더 이상 진행할 수 없었다.
참고자료
Antonio Nappa , Eduardo Blázquez - Fuzzing Against the Machine
'보안 > fuzzing' 카테고리의 다른 글
[하드웨어 해킹] shannon 퍼징 (0) | 2024.01.22 |
---|---|
[하드웨어 해킹] QEMU 수정하기 (0) | 2024.01.21 |
[하드웨어 해킹] 퍼징 기술 (0) | 2024.01.15 |
[하드웨어 해킹] 퍼징과 분석 기술 - 심볼릭 실행 (0) | 2024.01.11 |
[하드웨어 해킹] QEMU 실행 모드 (1) | 2024.01.11 |