Post

CVE-2009-3895(libexif) 분석

개요


  • CVE-2009-3895 : 0.6.18, enxif-entry.cexif_entry_fix 함수에서 발생하는 heap-based buffer overflow 취약점

콜스택을 확인해보면, exif_entry_fix 함수에서 호출하는 realloc 함수에서 오류가 발생하였다고 나온다.

취약점 분석


콜스택을 확인해보면, exif_entry_fix 함수에서 호출하는 realloc 함수에서 오류가 발생하였다고 나온다.

0

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
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
/* These tags all need to be of format SHORT. */
	case EXIF_TAG_YCBCR_SUB_SAMPLING:
	case EXIF_TAG_SUBJECT_AREA:
	case EXIF_TAG_COLOR_SPACE:
	case EXIF_TAG_PLANAR_CONFIGURATION:
	case EXIF_TAG_SENSING_METHOD:
	case EXIF_TAG_ORIENTATION:
	case EXIF_TAG_YCBCR_POSITIONING:
	case EXIF_TAG_PHOTOMETRIC_INTERPRETATION:
	case EXIF_TAG_CUSTOM_RENDERED:
	case EXIF_TAG_EXPOSURE_MODE:
	case EXIF_TAG_WHITE_BALANCE:
	case EXIF_TAG_SCENE_CAPTURE_TYPE:
	case EXIF_TAG_GAIN_CONTROL:
	case EXIF_TAG_SATURATION:
	case EXIF_TAG_CONTRAST:
	case EXIF_TAG_SHARPNESS:
	case EXIF_TAG_ISO_SPEED_RATINGS:
		switch (e->format) {
		case EXIF_FORMAT_LONG:
		case EXIF_FORMAT_SLONG:
		case EXIF_FORMAT_BYTE:
		case EXIF_FORMAT_SBYTE:
		case EXIF_FORMAT_SSHORT:
			if (!e->parent || !e->parent->parent) break;
			exif_entry_log (e, EXIF_LOG_CODE_DEBUG,
				_("Tag '%s' was of format '%s' (which is "
				"against specification) and has been "
				"changed to format '%s'."),
				exif_tag_get_name_in_ifd(e->tag,
							exif_entry_get_ifd(e)),
				exif_format_get_name (e->format),
				exif_format_get_name (EXIF_FORMAT_SHORT));
			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,
					 exif_get_short_convert (
					  e->data + i *
					  exif_format_get_size (e->format),
					  e->format, 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);
			break;


gdb로는 realloc 함수를 호출하는 시점에서 종료되는 것을 보고 해당 시점에서 문제가 발생하는 것으로 이해했다.

하지만 realloc 함수에 인자로 주어진 sizedata에서 이상한 점을 찾지 못하고 ASAN을 활성화 하여 자세한 분석을 진행해보았다.

1

ASAN에서는 realloc 함수를 호출하는 시점이 아닌 exif_set_short 함수를 호출하는 시점에 힙 버퍼 오버플로우를 감지하고 종료된다.

이러한 이유는 ASANRed Zone이라는 개념을 사용하여 메모리 오류를 감지하는 순간 바로 종료되므로 gdb와 종료 시점이 다르기 때문이다.


해당 취약점은 다음과 같이 발생된다.

libexif의 공식 스펙 문서에 적힌 exif_set_short 함수의 설명은 다음과 같다.

2

b는 버퍼의 주소이며, value는 버퍼에 쓸 값이다.

Short형 데이터를 버퍼에 쓰는 함수이다.


1
2
3
4
5
6
7
8
9
10
11
12
13
for (i = 0; i < e->components; i++)
				exif_set_short (
					e->data + i *                   /* <--------- */
					exif_format_get_size (          /* vulnerable */
					 EXIF_FORMAT_SHORT), o,         /* <--------- */
					 exif_get_short_convert (
					  e->data + i *
					  exif_format_get_size (e->format),
					  e->format, 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);

해당 로직에서 b에는 data의 위치에 Short형의 사이즈만큼 곱해서 인자를 전달한다.


아래 코드를 보면 dataformat을 기준으로 사이즈를 정해서 할당하게 된다.

따라서 위의 로직에서는 components * Byte 만큼 할당된 청크에 components * Short로 접근하게 된다.

그렇게되면 당연하게도 할당된 사이즈를 넘어 다른 청크나 영역에 침범하게 된다.


따라서 해당 로직에서 힙 버퍼가 망가지게 되며, 이후 로직인 realloc을 호출하는 과정에서 오류가 발생하게 된다.


해당 취약점은 작은 사이즈에서 큰 사이즈로 늘리면서 발생하게 되는 취약점으로 조건에 만족하는 formatByte 또는 SByte만 존재한다.


다음과 같이 파일을 변경하여 취약점을 트리거 할 수 있다.

3

4

취약점 패치


해당 취약점은 realloc 함수를 사용하지 않고 calloc 함수로 미리 Short형으로 할당한 후 데이터를 옮기도록 패치되었다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
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);
			e->data = newdata;
			e->size = newsize;
			e->format = EXIF_FORMAT_SHORT;
			break;
This post is licensed under CC BY 4.0 by the author.