DynamoRIO-7.0.0\bin32> drrun,exe -t drcov -- [程序/模块名] [参数]
从被执行的函数中可以看出调用了内存操作函数、数据格式转换函数、拷贝函数、初始化函数、标签操作函数以及加载函数等。
从覆盖率上看 ABC 看图软件调用了约 4.26% 的 FreeImage 库函数代码,说明 FreeImage 可以解析的图片格式还是非常的多。
用 windbg 看一下调用函数的顺序,以便编写测试文件用来进行模糊测试。
读取的图片为 jpg 格式,命令如下:
.logopen E:\log.txt
lmm FreeImagex FreeImage*bm FreeImge!FreeImage* ".echo; kb 1; g"
通过 python 脚本筛选出如下结果,总体可以概括为使用 Initialise 函数初始化库,使用一系列的 Get 函数获取图片格式信息,使用 Load 函数加载图片,对图片进行拷贝和获取详细图片信息操作,卸载图片并进行标签操作。
至于参数分析就不写了,这个过程比较繁琐,具体可以概括为大部分函数的参数都传入了加载图片函数的返回值(^ _ ^)。
经过动态和静态的简单分析后,决定对 FreeImage 库的载入函数、格式转换函数、获取图片信息函数、图片格式判断函数进行模糊测试。
至于为什么不包括标签和内存操作函数,因为这个过程属于设置而不是获取,并且是在 PhotoViewer 模块中实现的,而非 FreeImage 模块。
那么首先编写的是加载图片和获取图片信息的功能,测试代码如下:
/*
test.exe
*/#define _CRT_SECURE_NO_WARNINGS#include #include #include using namespace std;extern "C" __declspec(dllexport) int main(int argc, char** argv);void FreeImage_test(HINSTANCE hinstLib, wchar_t* pathfile);wchar_t* charToWChar(const char* text);// 加载卸载函数typedef DWORD(__stdcall *FreeImage_GetFileTypeU)(const wchar_t* lpszPathName, int flag);typedef DWORD(__stdcall *FreeImage_Initialise)(BOOL load_local_plugins_only);typedef DWORD(__stdcall *FreeImage_DeInitialise)();typedef DWORD(__stdcall *FreeImage_LoadU)(DWORD format, const wchar_t* lpszPathName, int flag);typedef DWORD(__stdcall *FreeImage_UnLoad)(DWORD dib);// 获取信息函数typedef DWORD(__stdcall *FreeImage_GetFIFFromFilenameU)(const wchar_t* lpszPathName); FreeImage_GetFIFFromFilenameU GetFIFFromFilenameU;typedef DWORD(__stdcall *FreeImage_GetDotsPerMeterX)(DWORD dib); FreeImage_GetDotsPerMeterX GetDotsPerMeterX;typedef DWORD(__stdcall *FreeImage_GetDotsPerMeterY)(DWORD dib); FreeImage_GetDotsPerMeterY GetDotsPerMeterY;typedef DWORD(__stdcall *FreeImage_GetTransparencyCount)(DWORD dib); FreeImage_GetTransparencyCount GetTransparencyCount;typedef DWORD(__stdcall *FreeImage_GetTransparencyTable)(DWORD dib); FreeImage_GetTransparencyTable GetTransparencyTable;typedef DWORD(__stdcall *FreeImage_GetBlueMask)(DWORD dib); FreeImage_GetBlueMask GetBlueMask;typedef DWORD(__stdcall *FreeImage_GetRedMask)(DWORD dib); FreeImage_GetRedMask GetRedMask;typedef DWORD(__stdcall *FreeImage_GetThumbnail)(DWORD dib); FreeImage_GetThumbnail GetThumbnail;typedef DWORD(__stdcall *FreeImage_GetPalette)(DWORD dib); FreeImage_GetPalette GetPalette;typedef DWORD(__stdcall *FreeImage_GetGreenMask)(DWORD dib); FreeImage_GetGreenMask GetGreenMask;typedef DWORD(__stdcall *FreeImage_GetImageType)(DWORD dib); FreeImage_GetImageType GetImageType;typedef DWORD(__stdcall *FreeImage_GetICCProfile)(DWORD dib); FreeImage_GetICCProfile GetICCProfile;typedef DWORD(__stdcall *FreeImage_GetBackgroundColor)(DWORD dib); FreeImage_GetBackgroundColor GetBackgroundColor;typedef DWORD(__stdcall *FreeImage_GetInfo)(DWORD dib); FreeImage_GetInfo GetInfo; // 不确定的typedef DWORD(__stdcall *FreeImage_GetFormatFromFIF)(DWORD dib); FreeImage_GetFormatFromFIF GetFormatFromFIF;typedef DWORD(__stdcall *FreeImage_GetFIFExtensionList)(DWORD dib); FreeImage_GetFIFExtensionList GetFIFExtensionList;typedef DWORD(__stdcall *FreeImage_GetMetadataCount)(DWORD format, DWORD dib); FreeImage_GetMetadataCount GetMetadataCount;
FreeImage_Initialise Initialise;
FreeImage_GetFileTypeU LoadFileType;
FreeImage_LoadU LoadU; DWORD load;
FreeImage_UnLoad UnLoad;
FreeImage_DeInitialise DeInitialise;int main(int argc, char** argv){if (argc < 2) {printf("Usage: %s \n", argv[0]);return 0;
}wchar_t* PathName = charToWChar(argv[1]);
HINSTANCE hinstLib; BOOL fFreeResult, fRunTimeLinkSuccess = FALSE; DWORD Error = NULL;
hinstLib = LoadLibrary(TEXT("E:\\FreeImage.dll"));if (hinstLib != NULL)
{
fRunTimeLinkSuccess = TRUE;
Initialise = (FreeImage_Initialise)GetProcAddress(hinstLib, (LPCSTR)163); // 初始化 FreeImage 库
LoadFileType = (FreeImage_GetFileTypeU)GetProcAddress(hinstLib, (LPCSTR)126);// 获取位图文件类型
LoadU = (FreeImage_LoadU)GetProcAddress(hinstLib, (LPCSTR)181); // 加载位图
UnLoad = (FreeImage_UnLoad)GetProcAddress(hinstLib, (LPCSTR)242);// 卸载位图
DeInitialise = (FreeImage_DeInitialise)GetProcAddress(hinstLib, (LPCSTR)83);//卸载 FreeImage 库// 获取信息函数
LoadFileType = (FreeImage_GetFileTypeU)GetProcAddress(hinstLib, (LPCSTR)126);
GetFIFFromFilenameU = (FreeImage_GetFIFFromFilenameU)GetProcAddress(hinstLib, (LPCSTR)118);
GetDotsPerMeterX = (FreeImage_GetDotsPerMeterX)GetProcAddress(hinstLib, (LPCSTR)112);
GetDotsPerMeterY = (FreeImage_GetDotsPerMeterY)GetProcAddress(hinstLib, (LPCSTR)113);
GetTransparencyCount = (FreeImage_GetTransparencyCount)GetProcAddress(hinstLib, (LPCSTR)155);
GetTransparencyTable = (FreeImage_GetTransparencyTable)GetProcAddress(hinstLib, (LPCSTR)156);
GetBlueMask = (FreeImage_GetBlueMask)GetProcAddress(hinstLib, (LPCSTR)105);
GetRedMask = (FreeImage_GetRedMask)GetProcAddress(hinstLib, (LPCSTR)145);
GetGreenMask = (FreeImage_GetGreenMask)GetProcAddress(hinstLib, (LPCSTR)128);
GetThumbnail = (FreeImage_GetThumbnail)GetProcAddress(hinstLib, (LPCSTR)154);
GetPalette = (FreeImage_GetPalette)GetProcAddress(hinstLib, (LPCSTR)141);
GetImageType = (FreeImage_GetImageType)GetProcAddress(hinstLib, (LPCSTR)132);
GetICCProfile = (FreeImage_GetICCProfile)GetProcAddress(hinstLib, (LPCSTR)131);
GetBackgroundColor = (FreeImage_GetBackgroundColor)GetProcAddress(hinstLib, (LPCSTR)103);
GetInfo = (FreeImage_GetInfo)GetProcAddress(hinstLib, (LPCSTR)133);
GetFormatFromFIF = (FreeImage_GetFormatFromFIF)GetProcAddress(hinstLib, (LPCSTR)127);
GetFIFExtensionList = (FreeImage_GetFIFExtensionList)GetProcAddress(hinstLib, (LPCSTR)116);
GetMetadataCount = (FreeImage_GetMetadataCount)GetProcAddress(hinstLib, (LPCSTR)139);
(Initialise)(FALSE);
FreeImage_test(hinstLib, PathName);
(DeInitialise)();
fFreeResult = FreeLibrary(hinstLib);
}if (!fRunTimeLinkSuccess)cout << "加载函数失败, Error: " << Error << endl;return 0;
}void FreeImage_test(HINSTANCE hinstLib, wchar_t* pathfile){// 加载函数
DWORD FileType = (LoadFileType)(pathfile, 0);
load = (LoadU)(FileType, pathfile, 0);// 获取信息函数
GetFIFFromFilenameU(pathfile);
GetDotsPerMeterX(load);
GetDotsPerMeterY(load);
GetTransparencyCount(load);
GetTransparencyTable(load);
GetBlueMask(load);
GetRedMask(load);
GetGreenMask(load);
GetThumbnail(load);
GetPalette(load);
GetImageType(load);
GetICCProfile(load);
GetInfo(load);
GetFormatFromFIF(load);
GetFIFExtensionList(load);// 卸载函数
(UnLoad)(load);
}wchar_t* charToWChar(const char* text)
{size_t size = strlen(text) + 1;wchar_t* wa = new wchar_t[size];
mbstowcs(wa, text, size);return wa;
}
测试代码编写完之后使用 WinAFL 的案例最小化功能筛选案例,这一步主要是提高代码运行时的覆盖率。
命令如下:
winafl\python winafl-cmin.py --working-dir E:\winafl\bin32 -D E:\DynamoRIO-7.0.0\bin32 -t 100000 -i [案例路径] -o [输出案例路径] -coverage_module FreeImage.dll -target_module test.exe -target_method main -nargs 2 -- E:\test.exe @@ // 表示引用
筛选过的图片储存在 MIX_* 文件夹中,并且保留了不大于 4KB 的图片,因为大于 4KB 的图片 WinAFL 读起来会非常的慢,而这次测试的时间计划为 6h 左右:
筛选完图片之后就可以开始模糊测试了,这里规定每个图片格式测试时间为 1h,命令如下:
winafl\bin32\afl-fuzz.exe -i [输入案例路径]-o [结果输出路径]-D E:\DynamoRIO-7.0.0\bi3n2-t 20000 -- -coverage_module FreeImage.dll-fuzz_iterations 5000-target_module test.exe-target_offset [函数偏移] // 这里进行模糊测试的函数为 FreeImage_test()-nargs 2 -- E:\test.exe @@
如下图所示 WinAFL 已经开始正常工作了,但是否运行的良好呢,首先来看一下运行速度 stage progress->exec speed,在 WinAFL 中速度分为三挡:
极慢(zzzz....)、慢(slow)、正常,此时的速度为 2352 次每秒属于正常速度;
其次看一下 Fuzzing strategy yields,这个板块表示样本变异的程度,数字越大样本越模糊,测试效率越高。
之后看一下 stage progress -> now trying && stage execs,now trying 表示目前执行的任务,如图所示目前正在执行算数变异(arith),而 stage execs 表示任务执行的进度,用百分率表示。
如果按上面的标准来看,下图算是一个良好的测试开始。
如果想在测试过程中查询 WinAFL 的运行状态,可以使用 winafl/winafl-plot.py 脚本进行查询。
命令如下:
python winafl-plot.py sync_dir out_dir
# sync_dir 是 afl-fuzz -o 参数所指定的路径,路径下应该包括 fuzzer_stats 文件;out_dir 目录表示结果输出目录,输出的是 index.html 文件,直接拉到浏览器中查看即可
经过几个小时的模糊测试后,图片格式为 BMP、ICO、TAG 的 crash 目录中均发现了 Bug,包括重复的;
但是 TIFF、JPG、PNG 格式则没有测试出 Bug,一方面可能是因为测试的时间有点短(只有 1 个小时),另一方面可能删除了稍大一点的图片导致测试的不是很充分。
测试完图片信息获取函数和加载函数之后,使用 BMP 格式测试一下图片格式判断函数和转换函数。
#define _CRT_SECURE_NO_WARNINGS#include #include #include using namespace std;extern "C" __declspec(dllexport) int main(int argc, char** argv);void FreeImage_test(HINSTANCE hinstLib, wchar_t* pathfile);wchar_t* charToWChar(const char* text);// 加载卸载函数typedef DWORD(__stdcall *FreeImage_GetFileTypeU)(const wchar_t* lpszPathName, int flag);typedef DWORD(__stdcall *FreeImage_Initialise)(BOOL load_local_plugins_only);typedef DWORD(__stdcall *FreeImage_DeInitialise)();typedef DWORD(__stdcall *FreeImage_LoadU)(DWORD format, const wchar_t* lpszPathName, int flag);typedef DWORD(__stdcall *FreeImage_UnLoad)(DWORD dib);// 图片格式判断函数typedef DWORD(__stdcall *FreeImage_HasBackgroundColor)(DWORD dib); FreeImage_HasBackgroundColor HasBackgroundColor;typedef DWORD(__stdcall *FreeImage_HasPixels)(DWORD dib); FreeImage_HasPixels HasPixels;typedef DWORD(__stdcall *FreeImage_HasRGBMasks)(DWORD dib); FreeImage_HasRGBMasks HasRGBMasks;typedef DWORD(__stdcall *FreeImage_IsTransparent)(DWORD dib); FreeImage_IsTransparent IsTransparent;// 转换函数typedef DWORD(__stdcall *FreeImage_ConvertTo24Bits)(DWORD dib); FreeImage_ConvertTo24Bits ConvertTo24Bits;typedef DWORD(__stdcall *FreeImage_ConvertTo32Bits)(DWORD dib); FreeImage_ConvertTo32Bits ConvertTo32Bits;
FreeImage_Initialise Initialise;
FreeImage_GetFileTypeU LoadFileType;
FreeImage_LoadU LoadU; DWORD load;
FreeImage_UnLoad UnLoad;
FreeImage_DeInitialise DeInitialise;int main(int argc, char** argv){if (argc < 2) {printf("Usage: %s \n", argv[0]);return 0;
}wchar_t* PathName = charToWChar(argv[1]);
HINSTANCE hinstLib; BOOL fFreeResult, fRunTimeLinkSuccess = FALSE; DWORD Error = NULL;
hinstLib = LoadLibrary(TEXT("E:\\FreeImage.dll"));if (hinstLib != NULL)
{
fRunTimeLinkSuccess = TRUE;
Initialise = (FreeImage_Initialise)GetProcAddress(hinstLib, (LPCSTR)163); // 初始化 FreeImage 库
LoadFileType = (FreeImage_GetFileTypeU)GetProcAddress(hinstLib, (LPCSTR)126);// 获取位图文件类型
LoadU = (FreeImage_LoadU)GetProcAddress(hinstLib, (LPCSTR)181); // 加载位图
UnLoad = (FreeImage_UnLoad)GetProcAddress(hinstLib, (LPCSTR)242);// 卸载位图
DeInitialise = (FreeImage_DeInitialise)GetProcAddress(hinstLib, (LPCSTR)83);//卸载 FreeImage 库//图片格式加载函数
HasBackgroundColor = (FreeImage_HasBackgroundColor)GetProcAddress(hinstLib, (LPCSTR)160);
HasPixels = (FreeImage_HasPixels)GetProcAddress(hinstLib, (LPCSTR)161);
HasRGBMasks = (FreeImage_HasRGBMasks)GetProcAddress(hinstLib, (LPCSTR)162);
IsTransparent = (FreeImage_IsTransparent)GetProcAddress(hinstLib, (LPCSTR)168);//转换函数
ConvertTo24Bits = (FreeImage_ConvertTo24Bits)GetProcAddress(hinstLib, (LPCSTR)65);
ConvertTo32Bits = (FreeImage_ConvertTo32Bits)GetProcAddress(hinstLib, (LPCSTR)66);
(Initialise)(FALSE);
FreeImage_test(hinstLib, PathName);
(DeInitialise)();
fFreeResult = FreeLibrary(hinstLib);
}if (!fRunTimeLinkSuccess)cout << "加载函数失败, Error: " << Error << endl;return 0;
}void FreeImage_test(HINSTANCE hinstLib, wchar_t* pathfile){// 加载函数
DWORD FileType = (LoadFileType)(pathfile, 0);
load = (LoadU)(FileType, pathfile, 0);//图片格式加载函数
HasBackgroundColor(load);
HasPixels(load);
HasRGBMasks(load);
IsTransparent(load);//转换函数
ConvertTo24Bits(load);
ConvertTo32Bits(load);
(UnLoad)(load);
}wchar_t* charToWChar(const char* text)
{size_t size = strlen(text) + 1;wchar_t* wa = new wchar_t[size];
mbstowcs(wa, text, size);return wa;
}
等所有测试结束之后就可以开始整理一下被模糊测试找出的 Bug,调试一下出现 Crash 的原因,以及是否可以被利用。
当然这个过程比较繁琐,利用 BugID 可以帮助你进行 Bug 分类,或者编写 Windbg 脚本。
筛选之后如图下图所示:
首先第一个 Bug 是读写未初始化的堆,通过查看函数调用发现是 FreeImage_GetImageType 出的问题。
通过 !heap -p -a 和 !address 可以看出非法访问未初始化的堆。
样本信息如下:
第二个 Bug 是读写未分配的堆,其实和上面差不多,但是读写的内存地址不一样。
如下图所示出问题的函数为 FreeImage_GetFIFFromFilenameU。
从内存信息上来看访问了保留的内存地址。
样本信息如下:
第三个 Bug 是堆溢出,出问题的函数是 FreeImage_OutputMessageProc,父函数为 FreeImage_LoadU,这里函数调用关系可能没有 IDA 的准确。
edi = 0x11eadd08,esi = 0x11ea6000,0x11eadd08 + 0x2f3 - 11eae000 = -5,
从堆空间 +5 的地方开始溢出。
最后一个是堆越界写入,出问题的函数为 FreeImage_OutputMessageProc,从 +1 的位置开始写入,写入的了 1 个 DWORD 大小的字节。
样本如下图所示:
对比漏洞库的漏洞看看是否发现了旧的 Bug,通过仔细的分析相关 POC 后发现只有堆溢出被记录在 CVE 中。
本次测试只是简单的 Fuzzing 了一下 ABC 看图软件的加载图片功能,但是效果并不是很理想。
由于时间比较紧张,错误在所难免,请勿喷。
- End -
看雪ID:护花使者cxy_
https://bbs.pediy.com/user-856782.htm
*本文由看雪论坛 护花使者cxy_ 原创,转载请注明来自看雪社区推荐文章++++
* 从shellcode学习到缓冲区溢出实战
* Ubuntu 18.04.3 LTS 编译安卓6.0.0_r1
* 送给最好的TA App分析
* 开源自己开发的一些工具--封包嗅探工具SnifferView
* Windows提权逆向
进阶安全圈,不得不读的一本书 ﹀ ﹀ ﹀ 戳