반응형
Recent Posts
Recent Comments
Link
«   2024/07   »
1 2 3 4 5 6
7 8 9 10 11 12 13
14 15 16 17 18 19 20
21 22 23 24 25 26 27
28 29 30 31
Tags
more
Archives
Today
Total
관리 메뉴

ZZoMb1E_PWN

[Fuzzing 101] libexif (CVE-2012-2836) 본문

STUDY/CVE && Fuzzing

[Fuzzing 101] libexif (CVE-2012-2836)

ZZoMb1E 2024. 1. 8. 03:53
728x90
반응형

 자료들을 참고하여 분석을 진행하였기에 잘못된 부분이 있을지도 모릅니다. 

※ 보완 혹은 수정해야 되는 부분이 있다면 알려주시면 확인 후 조치하도록 하겠습니다.
 

1. 분석대상


libexif란?

  사진 파일의 메타데이터인 EXIF 형식을 볼 수 있게 해주는 라이브러이다.

EXIF 형식에는 카메라의 설정 정보, gps 정보 등이 포함되어 있다.

 

2. CVE Code


CVE-2012-2836

EXIF 파싱 라이브러리의 exif-data.c에 있는 exif_data_load_data()에 의해 발생하는 취약점으로, 공격자가 EXIF 태그를 통해 DoS를 일으키거나 시스템 메모리를 유출시키는 등의 문제가 발생한다.

CWE-125

Out-of-bounds Read
민감한 정보를 읽거나 충돌을 유발시키는 것으로 문자열 등의 데이터를 읽는 과정에서 NULL과 같이 문자열의 끝을 가리키는 값이 존재한다는 가정하에 발생한다. 문자열 범위 밖에 해당 값이 존재할 경우 메모리 유출 및 BOF로 이어질 수 있다.
 

3. CVE 관련 정보



https://www.google.com/url?sa=t&rct=j&q=&esrc=s&source=web&cd=&ved=2ahUKEwj_g4bZ8eGDAxVohq8BHU8NClQQFnoECAsQAQ&url=https%3A%2F%2Fnvd.nist.gov%2Fvuln%2Fdetail%2FCVE-2012-2836&usg=AOvVaw0MrQFcUpm_mW75vzaME5dc&opi=89978449
 
 

 

https://www.google.com/url?cd=&esrc=s&opi=89978449&q=&rct=j&sa=t&source=web&url=https%3A%2F%2Fnvd.nist.gov%2Fvuln%2Fdetail%2FCVE-2012-2836&usg=AOvVaw0MrQFcUpm_mW75vzaME5dc&ved=2ahUKEwj_g4bZ8eGDAxVohq8BHU8NClQQFnoECAsQAQ

 

www.google.com

4. 분석  환경 및 구현


분석 환경 : Ubuntu 20.04
 
 

실습을 위한 설치 과정


 
libexif 파일 실습할 디렉터리 경로 생성

cd $HOME
mkdir fuzzing_libexif && cd fuzzing_libexif/

  
취약한 libexif 버젼 설치 과정

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

 
일반적인 build

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

 


 

TARGET 프로그램 설치

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


 build

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

실습에 사용할 예제 파일 설치

cd $HOME/fuzzing_libexif
wget https://github.com/ianare/exif-samples/archive/refs/heads/master.zip
unzip master.zip

 
 
예제 파일 확인 및 exif 실행

$HOME/fuzzing_libexif/install/bin/exif $HOME/fuzzing_libexif/exif-samples-master/jpg/Canon_40D_photoshop_import.jpg

 


  
libexif/exif를 AFL++ 이용하여 빌드 - 계측코드 삽입
 
recompile

sudo rm -rf $HOME/fuzzing_libexif/install
cd $HOME/fuzzing_libexif/libexif-libexif-0_6_14-release/
make clean
export LLVM_CONFIG="llvm-config-11"
export CC=afl-clang-fast
export CXX=afl-clang-fast++
./configure --enable-shared=no --prefix="$HOME/fuzzing_libexif/install/" 
make
make install


cd $HOME/fuzzing_libexif/exif-exif-0_6_15-release
make clean
export LLVM_CONFIG="llvm-config-11"
export CC=afl-clang-fast
export CXX=afl-clang-fast++
./configure --enable-shared=no --prefix="$HOME/fuzzing_libexif/install/" PKG_CONFIG_PATH=$HOME/fuzzing_libexif/install/lib/pkgconfig 
make
make install

 
※ 제 환경에서 AFL에 문제가 있는지 afl-clang-lto에서 오류가 발생하여 afl-clang-fast로 진행했습니다. 
실습하실 때 afl-clang-lto로 하시면 됩니다.

※ 지금은 해당 문제를 해결했습니다.


fuzzer 수행
 

afl-fuzz -i $HOME/fuzzing_libexif/exif-samples-master/jpg/ -o $HOME/fuzzing_libexif/out/ -s 123 -- $HOME/fuzzing_libexif/install/bin/exif @@

-i : 입력에 사용할 예제의 디렉터리 경로 설정 옵션
-o : AFL++의 결과를 저장할 디렉터리 경로 설정 옵션
-s : 무작위 정적 시드 사용
@@ : 입력 파일 이름으로 대체할 대상 표시
 


 
 

5. 루크 커즈 분석


out/default/crashes/을 살펴보면 crash파일이 들어있다.
 
해당 crash 파일은 인자로 gdb를 다음과 같이 실행한다.

gdb -q --args $HOME/fuzzing_libexif/install/bin/exif id:000000,sig:11,src:000008,time:76228,execs:6958,op:havoc,rep:5

 

 

분석



bt로 살펴보면 main()  exif_loader_get_data()  exif_data_load_data()  exif_mnote_data_load()  exif_mnote_data_olympus_load() 순으로 호출되는 것을 확인할 수 있다.

 


EXIF 구조


EXIF 구조에 대해 간략하게만 다루겠다.

디지털 카메라 등에서 사용되는 이미지 파일의 메타데이터 포멧으로 TIFF의 TAG 구조를 이용하여 표현된다.

EXIF에는 IFD라는 데이터에 대한 정보를 가지는 부분이 있다. 일반적으로 카메라에 대한 정보를 저장하지 않기 때문에 IFD0에서 SubIFD의 offset에 대한 정보를 갖는다.

SubIFD의 시작주소는 IFD0에 있는 SubIFD@offset + offset의 계산 시작 지점이다. 

 

코드 분석


main()에서 EXIF 데이터를 불려오기 위해 libexif/exif-loader.c에 있는 exif_loader_get_data()를 호출한다.


exif-loader.c: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()를 호출한다.


 exif-data.c: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;
	}
	offset = exif_get_long (d + 6 + offset + 2 + 12 * n, data->priv->order);
	if (offset) {
		exif_log (data->priv->log, EXIF_LOG_CODE_DEBUG, "ExifData",
			  "IFD 1 at %i.", (int) offset);

		/* Sanity check. */
		if (offset > ds - 6) {
			exif_log (data->priv->log, EXIF_LOG_CODE_CORRUPT_DATA,
				  "ExifData", "Bogus offset.");
			return;
		}

		exif_data_load_data_content (data, EXIF_IFD_1, d + 6, ds - 6, offset, 0);
	}

IFD0의 값을 구한 다음에 IFD1의 위치를 계산하는 부분이다.

이때 첫 번째 offset인 IFD0의 값이 매우 큰 값으로 주어졌을 경우 OOB의 취약점이 발생하게 된다.

 

 

 

IFD의 계산 부분을 살펴봤으니 다시 backtrace의 흐름대로 코드를 살펴보도록 하겠다.

 

exif-data.c:exif_loader_get_data()

exif_log (data->priv->log, EXIF_LOG_CODE_DEBUG, "ExifData",
		  "Parsing %i byte(s) EXIF data...\n", ds);

	/*
	 * It can be that the data starts with the EXIF header. If it does
	 * not, search the EXIF marker.
	 */
	if (ds < 6) {  
		LOG_TOO_SMALL;
		return;
	}

ds 변수의 경우 exif 파일의 크기를 의미하는 것을 확인할 수 있다.

(다른 코드로도 분석하여 확인할 수 있다.)

 

 

 

 

exif-data.c:exif_data_load_data()

if (data->priv->md) {
		exif_mnote_data_log (data->priv->md, data->priv->log);
		exif_mnote_data_set_byte_order (data->priv->md,
						data->priv->order);
		exif_mnote_data_set_offset (data->priv->md,
					    data->priv->offset_mnote);
		exif_mnote_data_load (data->priv->md, d, ds);
	}

ds를 3번째 인자로 exif_mnote_data_load()를 호출한다.

crash 파일을 대상으로 실행했을 때 ds는 0x12bb이다.

 

 

exif-mnote-data.c:exif_mnote_data_load()

void
exif_mnote_data_load (ExifMnoteData *d, const unsigned char *buf,
		      unsigned int buf_size)
{
	if (!d || !d->methods.load) return;
	d->methods.load (d, buf, buf_size);
}

3번째 인자로 받은 ds는 buf_size로 씌이게 된다.

buf_size가 0x12bb인 것을 확인할 수 있다.

 

d->methods.load()는 exif-mnote-data-olympus.c에 정의되어 있다.

ExifMnoteData *
exif_mnote_data_olympus_new (ExifMem *mem)
{
	ExifMnoteData *d;

	if (!mem) return NULL;
	
	d = exif_mem_alloc (mem, sizeof (ExifMnoteDataOlympus));
	if (!d) return NULL;

	exif_mnote_data_construct (d, mem);

	/* Set up function pointers */
	d->methods.free            = exif_mnote_data_olympus_free;
	d->methods.set_byte_order  = exif_mnote_data_olympus_set_byte_order;
	d->methods.set_offset      = exif_mnote_data_olympus_set_offset;
	d->methods.load            = exif_mnote_data_olympus_load;
	d->methods.save            = exif_mnote_data_olympus_save;
	d->methods.count           = exif_mnote_data_olympus_count;
	d->methods.get_id          = exif_mnote_data_olympus_get_id;
	d->methods.get_name        = exif_mnote_data_olympus_get_name;
	d->methods.get_title       = exif_mnote_data_olympus_get_title;
	d->methods.get_description = exif_mnote_data_olympus_get_description;
	d->methods.get_value       = exif_mnote_data_olympus_get_value;

	return d;
}

 

 

exif-mnote-data-olympus.c

s = exif_format_get_size (n->entries[i].format) * n->entries[i].components;

s는 데이터의 총 바이트 크기를 저장하는 용도로 씌인다.

 

/* Sanity check */
	    n->entries[i].data = exif_mem_alloc (en->mem, s);
	    if (!n->entries[i].data) continue;
	    n->entries[i].size = s;
	    memcpy (n->entries[i].data, buf + o, s);

해당 s를 가지고 memcpy()을 실행하는 과정에서 취약점이 발생하게 된다.

 

 

 

전달되는 인자들을 가지고 메모리를 살펴보겠다.



 

0x0000000100000000 크기의 힙 영역에 0x000000000045c7e7에 있는 데이터들을 0x00000000ffffff14만큼 덮어 씌울려고 한다.

0x00000000ffffff14는 0x420C에 해당하며 해당 위치는 앞에서 언급한 계산에 의해서 나오게 된다.

 

힙 영역을 확인해보면 0x456000~0x477000인 것을 확인할 수 있다.

0x000000000045c7e7와의 거리를 계산하면 0x1a819이다.

메모리 주소를 계산하여 확인해보면 0x1a819만큼 떨어져 있는 위치에는 접근할 수 없는 영역인 것을 확인할 수 있다.

 

그대로 진행을 하게 되면 Segmentation fault가 뜨게 된다.

 

크기에 대한 값을 임의로 수정하여 진행해보겠다.

 

rdx레지스터의 값을 0x10으로 수정하였다.

이렇게 되면 정상적으로 실행이 되는 것을 확인할 수 있다.

 

 

6. 패치 파악


패치된 버젼과의 비교를 수행했다.

패치되기 전에는 d[0]을 가지고 비교를 수행하였지만 기존 ds를 이용하여 검증 연산을 하는 코드를 추가하여 ds에 해당하는 값을 활용해도 되는지 혹은 문자열의 끝을 가리키는 NULL 값과 같은 값이 없어서 넘어가지 않는지에 대한 검증을 수행한다.


 

728x90
반응형

'STUDY > CVE && Fuzzing' 카테고리의 다른 글

[Fuzzing 101] libtiff (CVE-2016-9297)  (1) 2024.06.01
[Fuzzing 101] TCPdump (CVE-2017-13028)  (0) 2024.01.19
[Fuzzing 101] libexif (CVE-2009-3895)  (0) 2024.01.16
[Fuzzing 101] Xpdf (CVE-2019-13288)  (0) 2023.11.18
[CVE] Curl (CVE-2023-38545)  (0) 2023.11.16