개요
fuzzing101에서 소개한 취약점은 2가지이다.
1. CVE-2009-3895
exif-entry.c의 exif_entry_fix 함수에서 발생하는 힙 오버플로우 취약점
https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2009-3895
2. CVE-2012-2836
exif-data.c의 exif_data_load_data 함수에서 발생하는 OOB Read 취약점
https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2012-2836
열심히 퍼징을 돌렸지만
아쉽게도 CVE-2009-3895는 발견하지 못했다.
따라서 CVE-2012-2836만 찾아보겠다.
EXIF
EXIF 파일 파싱 과정에서 발생하는 취약점이라고 했으니
먼저 EXIF 파일의 구조에 대해 알아봐야겠다.
EXIF는 EXchangeable Image File format의 약자로 디지털 카메라에서 이용되는 이미지 파일 포맷이다.
이 데이터는 JPEG, TIFF 6.0과 RIFF, WAV 파일 포맷에서 이용되며 사진에 대한 정보를 포함하는 메타데이터를 추가한다. Exif는 JPEG 2000, PNG나 GIF 파일에서는 지원하지 않는다.
크래시 분석
먼저 크래시 1개를 분석해보면서 흐름을 파악해보자.
콜스택 확인해서 exif-loader.c부터 어떻게 타고타고 들어가는지 살펴보며 분석한다.
[exif-loader.c]
exif_data_load_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.c]
exif_data_load_data_content로 들어간다.
/* 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);
switch ~ case를 거처 exif_data_load_data_thumbnail로 점프한다.
exif_data_load_data_content (ExifData *data, ExifIfd ifd,
const unsigned char *d,
unsigned int ds, unsigned int offset, unsigned int recursion_depth)
{
ExifLong o, thumbnail_offset = 0, thumbnail_length = 0;
ExifShort n;
ExifEntry *entry;
unsigned int i;
ExifTag tag;
if (!data || !data->priv)
return;
if ((ifd < 0) || (ifd >= EXIF_IFD_COUNT))
return;
if (recursion_depth > 150) {
exif_log (data->priv->log, EXIF_LOG_CODE_CORRUPT_DATA, "ExifData",
"Deep recursion detected!");
return;
}
/* Read the number of entries */
if (offset >= ds - 1)
return;
n = exif_get_short (d + offset, data->priv->order);
exif_log (data->priv->log, EXIF_LOG_CODE_DEBUG, "ExifData",
"Loading %i entries...", n);
offset += 2;
/* Check if we have enough data. */
if (offset + 12 * n > ds)
n = (ds - offset) / 12;
for (i = 0; i < n; i++) {
tag = exif_get_short (d + offset + 12 * i, data->priv->order);
switch (tag) {
case EXIF_TAG_EXIF_IFD_POINTER:
case EXIF_TAG_GPS_INFO_IFD_POINTER:
case EXIF_TAG_INTEROPERABILITY_IFD_POINTER:
case EXIF_TAG_JPEG_INTERCHANGE_FORMAT_LENGTH:
case EXIF_TAG_JPEG_INTERCHANGE_FORMAT:
o = exif_get_long (d + offset + 12 * i + 8,
data->priv->order);
switch (tag) {
case EXIF_TAG_EXIF_IFD_POINTER:
CHECK_REC (EXIF_IFD_EXIF);
exif_data_load_data_content (data, EXIF_IFD_EXIF, d, ds, o, recursion_depth + 1);
break;
case EXIF_TAG_GPS_INFO_IFD_POINTER:
CHECK_REC (EXIF_IFD_GPS);
exif_data_load_data_content (data, EXIF_IFD_GPS, d, ds, o, recursion_depth + 1);
break;
case EXIF_TAG_INTEROPERABILITY_IFD_POINTER:
CHECK_REC (EXIF_IFD_INTEROPERABILITY);
exif_data_load_data_content (data, EXIF_IFD_INTEROPERABILITY, d, ds, o, recursion_depth + 1);
break;
case EXIF_TAG_JPEG_INTERCHANGE_FORMAT:
thumbnail_offset = o;
if (thumbnail_offset && thumbnail_length)
exif_data_load_data_thumbnail (data, d,
ds, thumbnail_offset,
thumbnail_length);
break;
case EXIF_TAG_JPEG_INTERCHANGE_FORMAT_LENGTH:
thumbnail_length = o;
if (thumbnail_offset && thumbnail_length)
exif_data_load_data_thumbnail (data, d,
ds, thumbnail_offset,
thumbnail_length);
break;
default:
return;
}
break;
default:
해당 함수에서 memcpy를 호출하는데 실행 중에 프로그램이 터진다.
static void
exif_data_load_data_thumbnail (ExifData *data, const unsigned char *d,
unsigned int ds, ExifLong offset, ExifLong size)
{
if (ds < offset + size) {
exif_log (data->priv->log, EXIF_LOG_CODE_DEBUG, "ExifData",
"Bogus thumbnail offset and size: %i < %i + %i.",
(int) ds, (int) offset, (int) size);
return;
}
if (data->data)
exif_mem_free (data->priv->mem, data->data);
data->size = size;
data->data = exif_data_alloc (data, data->size);
if (!data->data)
return;
memcpy (data->data, d + offset, data->size);
}
gdb로 해당 시점에 브포를 걸고 보면 size 값이 가장 큰 값으로 되어 있다.
파일 상에선 이 부분 값이 size로 들어가는 것 같다.
해당 CVE는 exif_data_load_data 함수에서 발생한다고 했는데 이 크래시는 아닌 것 같다..
소스 코드를 줬으니 오디팅으로도 살펴보자.
소스 코드 오디팅
해당 취약점은 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);
OOB read 취약점이라고 했으니 exif_data_load_data_content 호출 직전에 브포를 걸고
인자 중 offset 값을 살펴보자.
offset이 8로 설정되어 있다.
아까 그림에서 살펴봤을 때 0x22부터 4바이트가 오프셋 역할을 수행한다고 했었다.
해당 값을 최대값인 0xffffffff로 바꿔보자.
오프셋 값이 0xffffffff으로 바뀐 것을 확인할 수 있다.
그리고 역시 이렇게 프로그램이 터진다.
결국 저 offset 값에 대한 검증이 제대로 수행되지 않았기 때문에 발생한 건데
이게 CVE-2012-2836가 맞는 지는 잘 모르겠다.
(아님 말구..)
https://nightohl.tistory.com/entry/EXIF-Format
'background > fuzzing' 카테고리의 다른 글
[fuzzing101] Exercise 3 - TCPdump (CVE-2017-13028) (2) (0) | 2024.04.05 |
---|---|
[fuzzing101] Exercise 3 - TCPdump (CVE-2017-13028) (1) (0) | 2024.04.04 |
[fuzzing101] Exercise 2 - libexif (CVE-2009-3895 & CVE-2012-2836) (1) (0) | 2024.03.30 |
[fuzzing101] Exercise 1 - Xpdf (CVE-2019-13288) (0) | 2024.03.27 |