CVE-2009-3895(libexif) 분석
개요
- CVE-2009-3895 : 0.6.18,
enxif-entry.c
의exif_entry_fix
함수에서 발생하는heap-based buffer overflow
취약점
콜스택을 확인해보면, exif_entry_fix
함수에서 호출하는 realloc
함수에서 오류가 발생하였다고 나온다.
취약점 분석
콜스택을 확인해보면, exif_entry_fix
함수에서 호출하는 realloc
함수에서 오류가 발생하였다고 나온다.
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
함수에 인자로 주어진 size
와 data
에서 이상한 점을 찾지 못하고 ASAN
을 활성화 하여 자세한 분석을 진행해보았다.
ASAN
에서는 realloc
함수를 호출하는 시점이 아닌 exif_set_short
함수를 호출하는 시점에 힙 버퍼 오버플로우를 감지하고 종료된다.
이러한 이유는 ASAN
은 Red Zone
이라는 개념을 사용하여 메모리 오류를 감지하는 순간 바로 종료되므로 gdb
와 종료 시점이 다르기 때문이다.
해당 취약점은 다음과 같이 발생된다.
libexif
의 공식 스펙 문서에 적힌 exif_set_short
함수의 설명은 다음과 같다.
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
형의 사이즈만큼 곱해서 인자를 전달한다.
아래 코드를 보면 data
는 format
을 기준으로 사이즈를 정해서 할당하게 된다.
따라서 위의 로직에서는 components * Byte
만큼 할당된 청크에 components * Short
로 접근하게 된다.
그렇게되면 당연하게도 할당된 사이즈를 넘어 다른 청크나 영역에 침범하게 된다.
따라서 해당 로직에서 힙 버퍼가 망가지게 되며, 이후 로직인 realloc
을 호출하는 과정에서 오류가 발생하게 된다.
해당 취약점은 작은 사이즈에서 큰 사이즈로 늘리면서 발생하게 되는 취약점으로 조건에 만족하는 format
은 Byte
또는 SByte
만 존재한다.
다음과 같이 파일을 변경하여 취약점을 트리거 할 수 있다.
취약점 패치
해당 취약점은 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;