CVE-2009-3895
Description
Heap-based buffer overflow in the exif_entry_fix function (aka the tag fixup routine) in libexif/exif-entry.c in libexif 0.6.18 allows remote attackers to cause a denial of service or possibly execute arbitrary code via an invalid EXIF image. NOTE: some of these details are obtained from third party information.
libexif 0.6.18의 libexif/exif-entry.c 에 있는 exif_entry_fix 함수(태그 수정 루틴)의 heap-based buffer overflow가 발생했다는 뜻이다.
exif란?
우선 exif는 교환 이미지 파일 형식(EXchangable Image File format)으로 디지털 카메라에서 이용되는 이미지 파일 포멧이다. 이 데이터는 JPEG, TIFF 6.0 , RIFF에서 지원된다.
우선 libexif의 취약점이기 때문에 libexif와 libexif를 사용하는 툴인 exif를 빌드해야한다.
libexif 빌드 후 exif를 빌드한다.
afl-clang-lto 를 사용하려면 llvm 15 를 설치해야한다.
wget <https://apt.llvm.org/llvm.sh>
chmod +x llvm.sh
sudo ./llvm.sh <version number> all
cd ~/AFLplusplus
export LLVM_CONFIG="llvm-config-15"
make distrib
sudo make install
libexif
$ cd $HOME
$ mkdir fuzzing_libexif && cd fuzzing_libexif/
$ wget <https://github.com/libexif/libexif/archive/refs/tags/libexif-0_6_14-release.tar.gz>
$ tar -zxvf libexif-0_6_14-release.tar.gz
$ cd libexif-libexif-0_6_14-release/
이 파일에는 configure 파일이 없다. 이럴 경우 autoreconf 로 configure를 만들 수 있다.
autoreconf -ivf 로 configure을 만들 수 있다.
sudo apt-get install autopoint libtool gettext libpopt-dev
autoreconf를 설치한다.
autoreconf -ivf
configure 파일을 만든다.
export LLVM_CONFIG="llvm-config-15"
CC=$(which afl-clang-lto) ./configure --enable-shared=no PKG_CONFIG_PATH="$HOME/fuzzing_libexif/install/lib/pkgconfig" --prefix="$HOME/fuzzing_libexif/install/"
make
make install
명령어 주소를 찾을때 which [파일이름] 으로 할 수 있다.
exif
$ cd $HOME
$ cd fuzzing_libexif/
$ wget <https://github.com/libexif/exif/archive/refs/tags/exif-0_6_15-release.tar.gz>
$ tar -zxvf exif-0_6_15-release.tar.gz
$ cd exif-exif-0_6_15-release/
autoreconf -ivf
export LLVM_CONFIG="llvm-config-15" PKG_CONFIG_PATH="$HOME/fuzzing_libexif/install/lib/pkgconfig" CC=$(which afl-clang-lto) ./configure --enable-shared=no --prefix="$HOME/fuzzing_libexif/install/"
make
make install
이런 오류가 뜨는데 버전이 안 맞아서 이다. 여기서는 6_15 버전을 사용해야 한다.
설치하고 sample 파일을 넣어보았다.
afl-fuzz -i $HOME/fuzzing_libexif/exif-samples/ -o $HOME/fuzzing_libexif/out/ -s 123 -D -- $HOME/fuzzing_libexif/install/bin/exif @@ $HOME/fuzzing_libexif/output
fuzzing start
충돌 재현
cp ~/fuzzing_libexif/out/default/crashes/id:000000,sig:11,src:000011,time:36314,execs:21805,op:flip4,pos:67 ~/fuzzing_libexif/crash1
일단 rebuild를 한다.
rm -r $HOME/fuzzing_xpdf/install
cd $HOME/fuzzing_xpdf/libexif-libexif-0_6_14-release/
make clean
CFLAGS="-g -O0" CXXFLAGS="-g -O0" ./configure --prefix="$HOME/fuzzing_libexif/install/"
make
make install
cd $HOME/fuzzing_xpdf/exif-exif-0_6_15-release/
make clean
CFLAGS="-g -O0" CXXFLAGS="-g -O0" ./configure --prefix="$HOME/fuzzing_libexif/install/"
make
make install
디버깅을 해보자
gdb --args ./install/bin/exif ./crash1
exif_get_slong 함수에서 터졌다.
원인 분석
backtrace를 보면
#2 exif_entry_fix (exif-entry.c)
#1 exif_get_long (exif-utils.c)
#0 exif_get_slong (exif-utils.c)
터진 순간의 chunk를 확인해봤는데 상당히 수상해보이는 chunk가 있다.
overflow가 되는 chunk는 그 위의 chunk인 0x555555564900 chunk이다. 해당 chunk가 c코드에서 무엇인지 찾아보았다.
여기서 rax+0x10을 참조하여 rbx에 저장하고 저장된 값이 0x555555564910이다.
해당 chunk에서 0x555555564910 chunk를 발견했다. 이 chunk는 exif-entry.h 라는 헤더파일의 ExifEntry라는 구조체이다. 0x555555564910 는 data포인터이다.
추가로 0x4는 format, 0x112는 tag, 0x80000001는 component이다.
여기서 component만큼 반복을 한다. 즉 0x80000001번 반복한다.
여기서 exif_format_get_size로 값을 가져오는 함수가 있다.
EXIF_FORMAT_SHORT와 EXIF_FORMAT_LONG을 인자로 어떤 값을 반환한다.
EXIF_FORMAT_SHORT를 예로 들면..
ExifFormatTable구조체에서 하나하나 비교해가면서 알맞는 값을 가져온다. 우선 i = 0이면
ExifFormatTable[0].format == format인지 비교한다. 여기서 format은 EXIF_FORMAT_SHORT이다. ExifFormatTable[0].format은 EXIF_FORMAT_BYTE이니 둘이 다르다. 다음으로
ExifFormatTable[1].format은 EXIF_FORMAT_ASCII이니 둘이 다르다. 다음으로
ExifFormatTable[2].format은 EXIF_FORMAT_SHORT이니 둘이 같다.
결과적으로 ExifFormatTable[2].size인 2를 반환하게 된다.
같은 방법으로 exif_format_get_size(EXIF_FORMAT_LONG)은 4를 반환한다.
overwrite된 chunk를 보자.
이 반복 과정을 몇 번 실행해보았다.
반복할 때 마다 0x5556 이 쓰이고 있다. 이 쓰이는 과정은 exif_set_short에 있다.
여기서 b는 e→data + i * 2 이다. 이 주소의 index 0과 index 1 에 3번째 인자인 exif_get_long함수의 결과물에 쉬프트 연산을 취한 값을 입력한다.
그런데 이전에 말했듯이 이 과정은 0x80000001 번 실행된다. 즉 exif_get_short의 첫번째 인자(b)의 주소는 실행될 때마다 2 씩 증가하고 exif_get_long의 첫 번째 인자(b)는 실행할 때 마다 4 씩 늘어나는 것이다. 그 결과 0x555555564900에서부터 계속 어떤 값이 쓰이고 exif_get_long이 참조하는 주소는 계속 4씩 늘어나는 것이다. 결국 계속 늘어나다가 heap의 끝까지 가고 segmentation fault가 발생한다.
터졌을 때를 보자.
0x5556이 계속 쓰여있다.
참조하는 곳이 heap의 끝 이다.
패치
여기서 newsize = e→components * exif_format_get_size (EXIF_FORMAT_SHORT); 로 newsize를 정의하고 exif_entry_alloc(e, newsize)로 newdata를 정의한 후 newdata가 0이라면 exif_entry_log를 실행한다.
newdata = exif_entry_alloc( e, newsize); newdata
newsize가 매우 큰 값이라면 exif_entry_alloc에서 NULL을 반환하고 그 결과 아래 if 문 검증에서 break가 된다.
<틀린 부분이 있다면 비난과 욕설을 해주세요>
'Fuzzing > CVE 분석' 카테고리의 다른 글
Fuzzing101 Exercise5 (1) | 2024.06.14 |
---|---|
Fuzzing101 Exercise4 (0) | 2024.05.10 |
Fuzzing101 Exercise3 (1) | 2024.04.19 |
Fuzzing101 Exercise2_2 (0) | 2024.03.13 |
Fuzzing101 Exercise1 (1) | 2024.01.16 |