编辑
2023-08-03
CVE
00
请注意,本文编写于 610 天前,最后修改于 610 天前,其中某些信息可能已经过时。

目录

CVE-2023-37176
版本
综述
编译方式
触发方式
效果
漏洞分析
总结

👍

CVE-2023-37176


版本
bash
commit 231df822d2ec2096d4ccf8d0b9e6a9c6bbfd3b69 (HEAD -> master, origin/master, origin/HEAD) Author: Thomas Bernard <miniupnp@free.fr> Date: Sat Mar 4 11:48:58 2023 +0100
综述

在输入特定构造的gif文件之后可以导致目标segment fault

编译方式
bash
$ CC="${CarpetFuzz}/fuzzer/afl-clang-fast -fsanitize=address -g" CFLAGS+=-DNGIFLIB_NO_FILE make
触发方式
$ ./gif2tga -i ./poc

效果

漏洞分析

漏洞存在于158行中 要求条件使用NGIFLIB_MODE_INDEXED其实就是加上-i参数 可以看到本身只是一个写入操作 将当前GIF图像的palette写入到目标的tga文件中

其中img来自113行的gif->cur_imggif来自107行的err = LoadGif(gif) 之前的gif变量只是一个初始化 对其进行分配内存空间 追入LoadGif

其中的cur_img相关的操作在ngiflib.c::864附近 没有看到palette相关操作 追入DecodeGifImg

追入DecodeGifImg后 关于palette相关操作在494行附近 其分支是由flags&128决定的 经过动态调试发现flags == 0 直接进入下面的分支 也就是i->palette = i->parent->palette 而这个i->parent就是上级函数中的g

![image-20230627173441273](/Users/du4t/Library/Application Support/typora-user-images/image-20230627173441273.png)

经过查询资料 每个GIF图片中都可能包含多个图片信息(废话 不然为啥有GIF动图) 所以为了区分每一帧的图片信息 采用标志位0x2C标志着每个图像的开始 然后前4个WORD分别为x偏移量 y偏移量 图像宽 图像高 经过审阅DecodeGIFImg内容发现在483行之前已经读取过相应数据 而GetByteGetWord内部有个计数器 类似fseek的作用 指定当前读到哪里防止越界读 本来是好心 但是内部审查不严 当可能越界读时不抛出异常而是直接返回0 造成了flags == 0

返回上级查看g->palette 相关操作在714行附近 分支决定了相关值直接是NULL还是从文件中取

![image-20230627174815393](/Users/du4t/Library/Application Support/typora-user-images/image-20230627174815393.png)

tmp相关操作在694行 在此之前对于文件流的操作有校验文件头和版本 取宽 取高

![image-20230627174923203](/Users/du4t/Library/Application Support/typora-user-images/image-20230627174923203.png)

那么就是文件中的此位置 经过动态调试确认正确 程序会流向g->palette = NULL

总结

那么至此 因为特定的flag导致GIF的palette属性为NULL 进而搭配GetByte的审查不严格 导致了GIF下每帧的palette属性都为NULL 最终在生成tga文件时造成了无效访问 且由此发现该程序对GIF文件结尾特定标志位0x3B校验不严格

c
static u8 GetByte(struct ngiflib_gif * g) { #ifndef NGIFLIB_NO_FILE if(g->mode & NGIFLIB_MODE_FROM_MEM) { #endif /* NGIFLIB_NO_FILE */ if (g->input.buffer.count > 0) { g->input.buffer.count--; return *(g->input.buffer.bytes++); } else { return 0; } #ifndef NGIFLIB_NO_FILE } else { #ifdef DEBUG int c = getc(g->input.file); if (c != EOF) return (u8)c; else { if(g->log) fprintf(g->log, "getc() returned EOF !\n"); return 0; } #else return (u8)(getc(g->input.file)); #endif } #endif /* NGIFLIB_NO_FILE */ }

本文作者:Du4t

本文链接:

版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!