1. 환경설정 및 설치
디렉토리 생성
cd $HOME
mkdir fuzzing_libexif && cd fuzzing_libexif/
libexif 0.6.14 다운로드, 압축 해제
wget https://github.com/libexif/libexif/archive/refs/tags/libexif-0_6_14-release.tar.gz
tar -xzvf libexif-0_6_14-release.tar.gz
빌드 및 설치
cd libexif-libexif-0_6_14-release/
sudo apt-get install autopoint libtool gettext libpopt-dev
autoreconf -fvi
./configure --enable-shared=no --prefix="$HOME/fuzzing_libexif/install/"
make
make install
autoreconf
- f: 이전의 파일을 지우고 새로 만든다.
- v: 과정을 자세하게 보여준다.
- i: 원래 없던 필요한 파일도 설치한다.
--enable-shared=no: 공유 라이브러리를 빌드하지 않도록 지시한다.
--prefix="$HOME/fuzzing_libexif/install/"는 설치될 경로를 지정한다.
Interface application 설치
- libexif는 라이브러리이므로 이를 실행하는 Interface application이 필요한데, 여기서는 exif command-line을 사용했다.
exif 다운로드 및 압축해제
cd $HOME/fuzzing_libexif
wget https://github.com/libexif/exif/archive/refs/tags/exif-0_6_15-release.tar.gz
tar -xzvf exif-0_6_15-release.tar.gz
빌드 및 설치
cd exif-exif-0_6_15-release/
autoreconf -fvi
./configure --enable-shared=no --prefix="$HOME/fuzzing_libexif/install/" PKG_CONFIG_PATH=$HOME/fuzzing_libexif/install/lib/pkgconfig
make
make install
PKG_CONFIG_PATH=$HOME/fuzzing_libexif/install/lib/pkgconfig
- 필요한 라이브러리의 위치를 지정한다.
Corpus 생성
- corpus는 Fuzzing시에 사용되는 데이터 셋을 의미하고, 여기서는 exif 이미지 샘플을 사용했다.
cd $HOME/fuzzing_libexif
wget https://github.com/ianare/exif-samples/archive/refs/heads/master.zip
unzip master.zip
afl-clang-lto를 사용한 libexif 빌드
- lto는 Link Time Optimization의 약자로써, link time에 최적화를 진행한다.
- AFL++은 컴파일 과정에서 block의 id를 랜덤으로 설정하는데, 이는 복잡할수록 edge에서 충돌이 많이 발생할 수 있음을 의미하고, 새로운 경로를 찾기 어려울 수 있다.
- 그렇기 때문에 컴파일 과정에서 link time에 instrumentation code를 삽입하고, 이러한 삽입 시에 위해 lto를 사용하고, 원래의 시스템 linker를사용하는 것이 아닌 afl-ld를 사용한다.
- 이러한 lto는 code coverage에서 충돌이 발생하지 않음으로써 속도는 증가하지만 컴파일 시간이 더 길어진다.
libexif 빌드
rm -r $HOME/fuzzing_libexif/install
cd $HOME/fuzzing_libexif/libexif-libexif-0_6_14-release/
make clean
export LLVM_CONFIG="llvm-config-11"
CC=afl-clang-lto ./configure --enable-shared=no --prefix="$HOME/fuzzing_libexif/install/"
make
make install
exif 빌드
cd $HOME/fuzzing_libexif/exif-exif-0_6_15-release
make clean
export LLVM_CONFIG="llvm-config-11"
CC=afl-clang-lto ./configure --enable-shared=no --prefix="$HOME/fuzzing_libexif/install/" PKG_CONFIG_PATH=$HOME/fuzzing_libexif/install/lib/pkgconfig
make
make install
export LLVM_CONFIG="llvm-config-11"
- LLVM_CONFIG'이라는 환경 변수에 "llvm-config-11"을 추가함으로써 llvm-config-11을 사용한다.
CC=afl-clang-lto
- C 언어 컴파일러로 'afl-clang-lto'를 사용하겠다는 것을 의미한다.
2. Fuzzing 단계

afl-fuzz -i $HOME/fuzzing_libexif/exif-samples-master/jpg/ -o $HOME/fuzzing_libexif/out/ -s 123 -- $HOME/fuzzing_libexif/install/bin/exif @@
3. CVE-2012-2836


- exif_get_sshort에서 Segmentation fault가 발생했다.


- TIFF에서 처음 offset은 TIFF header의 0번째 IFD를 시작으로 가져온다.
- EXIF 구조에서 IFD 1은 IFD 0의 offset을 통해 구할 수 있음을 알 수 있다.
코드 분석
main :
// main.c
if (args) {
while (*args) {
ExifLoader *l;
/*
* Try to read EXIF data from the file.
* If there is no EXIF data, exit.
*/
l = exif_loader_new ();
exif_loader_log (l, log);
exif_loader_write_file (l, *args);
ed = exif_loader_get_data (l);
exif_loader_unref (l);
exif_loader_get_data :
ExifData *
exif_loader_get_data (ExifLoader *loader)
{
ExifData *ed;
if (!loader)
return NULL;
ed = exif_data_new_mem (loader->mem);
exif_data_log (ed, loader->log);
exif_data_load_data (ed, loader->buf, loader->bytes_read);
return ed;
}
exif_data_load_data :
/* IFD 0 offset */
offset = exif_get_long (d + 10, data->priv->order);
exif_log (data->priv->log, EXIF_LOG_CODE_DEBUG, "ExifData",
"IFD 0 at %i.", (int) offset);
/* Parse the actual exif data (usually offset 14 from start) */
exif_data_load_data_content (data, EXIF_IFD_0, d + 6, ds - 6, offset, 0);
/* IFD 1 offset */
if (offset + 6 + 2 > ds) {
return;
}
n = exif_get_short (d + 6 + offset, data->priv->order);
if (offset + 6 + 2 + 12 * n + 4 > ds) {
return;
}
- main에서 exif_loader_get_data를 호출하고, exif_loader_get_data는 exif_data_load_data를 호출하는데 여기서 문제가 발생한다.
- 자세히 말하면 IFD 0의 offset을 구하는 과정에서 문제가 발생하는데, offset이 uint32_t이고, ds가 uint32_t이므로 4294967925 + 8 = 0이 되고, 이러면 원래 offset + 6 +2 > ds 조건이 0인 상황에서 조건문이 참이 되어 return되어야 한다.
- 하지만, overflow로 인한 0 으로 인해 조건을 만족하지 않게 되면서 해당 조건문은 거짓이 되고, 결국 잘못된 값이 통과되어 문제가 발생하게 된다.
- IFD 0의 offset이 0xfffffff8이상이면 unsigned data overflow가 발생한다.
exif_get_short :
ExifShort
exif_get_short (const unsigned char *buf, ExifByteOrder order)
{
return (exif_get_sshort (buf, order) & 0xffff);
}
exif_get_sshort :
ExifSShort
exif_get_sshort (const unsigned char *buf, ExifByteOrder order)
{
if (!buf) return 0;
switch (order) {
case EXIF_BYTE_ORDER_MOTOROLA:
return ((buf[0] << 8) | buf[1]);
case EXIF_BYTE_ORDER_INTEL:
return ((buf[1] << 8) | buf[0]);
}
/* Won't be reached */
return (0);
}
- IFD 0의 offset이 0xfffffff8이상이면 unsigned data overflow가 발생하는 이유는 위 코드를 통해 알 수 있다.
- exif_get_short에서 해당 데이터를 하위 2바이트로 변환하는데, 만약 이 함수에서 Offset 값이 0xfffffff8 이상인 경우, 이 값은 부호 있는 2바이트 데이터로 표현될 때 음수의 범위에 들어갈 수 있게 된다.
- 그렇게 음수가 되면, 원래 의도하지 않은 영역에 shift 연산을 함으로써 Segmentation fault가 발생하게 된다.
4. CVE-2009-3895



- vmmap을 통해 buf(0x461000)가 heap영역을 벗어나 Heap buffer overflow가 일어났음을 확인할 수 있다.

- Parameters부분에서 b, order, value가 각각 어떤 값인지 나와있어서 이를 참고해서 코드 분석을 진행했다.
코드 분석
exif_get_short :
case EXIF_TAG_SHARPNESS:
switch (e->format) {
case EXIF_FORMAT_LONG:
if (!e->parent || !e->parent->parent) break;
o = exif_data_get_byte_order (e->parent->parent);
for (i = 0; i < e->components; i++)
exif_set_short (
e->data + i *
exif_format_get_size (
EXIF_FORMAT_SHORT), o,
(ExifShort) exif_get_long (
e->data + i *
exif_format_get_size (
EXIF_FORMAT_LONG), o));
e->format = EXIF_FORMAT_SHORT;
e->size = e->components *
exif_format_get_size (e->format);
e->data = exif_entry_realloc (e, e->data, e->size);
exif_entry_log (e, EXIF_LOG_CODE_DEBUG,
- 인자로 b에 해당하는 data의 위치에 short형의 크기를 기준으로 인자를 전달하기 때문에 문제가 발생하게 된다.
- 정상이었다면 byte가 곱해져야 하지만, 위의 부분에서 short를 곱하게 되면서 자연스럽게 본래 의도하지 않았던 chunk 또는 영역을 참조하게 된다.
- 이러한 이유들로 결국 realloc에서 Segmentation fault가 발생하게 된다.
5. 취약점 패치
CVE-2012-2836 :

- 단순히 offset+8을 비교하는 것이 아닌, 추가로 offset와 ds를 비교함으로써 offset 자체의 값이 크면 바로 return되도록 패치되었다.

- JPEG APP1 section이 64KiB를 넘을 수 없게, 데이터 크기에 제한을 두도록 패치했다.
CVE-2009-3895 :
o = exif_data_get_byte_order(e- > parent- > parent);
newsize = e->components * exif_format_get_size(EXIF_FORMAT_SHORT);
newdata = exif_entry_alloc(e, newsize);
if(!newdata) {
exif_entry_log(e, EXIF_LOG_CODE_NO_MEMORY, "Could not allocate %lu byte(s).", (unsigned long)newsize);
break;
}
for (i = 0; i < e->components; i++)
exif_set_short (
newdata + i *
exif_format_get_size (
EXIF_FORMAT_SHORT), o,
exif_get_short_convert (
e->data + i *
exif_format_get_size (e->format),
e->format, o));
exif_mem_free (e->priv->mem, e->data);
- realloc을 사용하지 않고, 미리 size를 short로 바꾼 다음에 참조하도록 패치했다.