打开APP
userphoto
未登录

开通VIP,畅享免费电子书等14项超值服

开通VIP
如何使用Fuzzing挖掘ImageMagick的漏洞“>如何使用Fuzzing挖掘ImageMagick的漏洞

1. ImageMagick 圖片處理庫簡介

? ImageMagick 是一個易於使用和功能強大的圖片處理庫,很重要的一點是ImageMagick 在比較多的後端應用中出現(比如說:WordPress和Discuz!).挖掘大型CMS 框架的漏洞的難度比較高,那麽另一種思路則是通過挖掘CMS 框架使用到的第三方庫漏洞讓CMS 框架來觸發(比如:PHPMailer等第三方庫)。由ImageMagick 庫引發的CVE-2016-3714 CVE-2016-5118 遠程命令執行和兩個月前的Yahoo! ImageMagick Heartbleed 已經足以說明從ImageMagick 中觸發的漏洞對主機安全性的危害。那麽接下來我們來討論怎麽把Fuzzing 技術應用到ImageMagick 的漏洞挖掘上。

2. ImageMagick 的使用方式

? Fuzzing ImageMagick 之前,先來了解ImageMagick 庫是怎麽樣使用的。

? ImageMagick 在編譯完成之後,在 utilities

ADVERTISEMENT
目錄下會生成 magick ,這就是ImageMagick 圖形庫的命令行使用工具. magick 命令行工具使用如下:
./magick magick_command other_arguments

? magick_command 指的是執行命令。有:identify (查看圖像信息),convert (圖像處理與格式轉換)等,更多命令通過以下鏈接查詢: https://www.imagemagick.org/script/command-line-processing.php

? other_arguments 指的是命令參數。對於Fuzzing ImageMagick 庫,常用的命令是: ./magick convert (接下來會慢慢討論為何要用convert 命令而不用其它命令)。對ImageMagick 做讀寫測試的命令: ./magick convert input_file.png output.bmp 。ImageMagick 庫則會讀取PNG 格式的圖片並且轉換成BMP 格式的圖像;只對ImageMagick 做讀測試的命令: ./magick convert input_file.png /dev/null

ADVERTISEMENT
,ImageMagick 庫則解析完圖片之後就退出了。

? 接下來,我們就開始要對ImageMagick 庫進行Fuzzing 了。先從github 上復制最新的代碼(建議使用git clone 而不是直接下載源碼),命令是:

? git clone https://github.com/ImageMagick/ImageMagick.git

? 每次ImageMagick 更新代碼,只需要在git pull 即可。

3. 如何使用AFL Fuzzing ImageMagick?

? AFL 在現在是很流行的Fuzzing 測試工具,關於它的介紹在此不多復述.它的使用方法如下:

? 1.在編譯項目時使用afl-clang 或afl-gcc 等AFL 編譯器編譯。

2.使用afl-fuzz 命令對程序進行fuzz,afl-fuzz 包含以下參數:

? afl-fuzz -i %input% -o %output% -t %timeout% -m %memory% fuzz_program fuzz_program_arguments

ADVERTISEMENT

? -i %input% : 使用指定目錄下的測試樣本。 -o %output% : 把Fuzzing 結果保存到指定目錄。 ? -t %timeout% : 可選參數,程序運行超時值,以毫秒為單位。 -m %memory% : 可選參數,最大運行內存,以MB 為單位。 ? fuzz_program fuzz_program_arguments : 指定fuzzing 程序和程序運行參數。

? 對ubuntu 用戶來說,AFL 可以從apt 中直接安裝(sudo apt install afl)。或者從AFL 的官網上下載源碼,然後make install.關於Fuzzing 的樣本下載,MozillaSecurity 團隊有一份開源的測試樣本 https://github.com/MozillaSecurity/fuzzdata

? 回到ImageMagick 庫Fuzzing ,第一步先使用AFL 編譯器編譯:

cd ImageMagick  ./configure CC="afl-clang" CXX="afl-clang++"  make
ADVERTISEMENT

? 編譯完成之後,接下來使用AFL 對ImageMagick 庫進行Fuzzing :

cd utilities  mkdir fuzzing_output  afl-fuzz -i ../../fuzzdata/samples/png -o fuzzing_output -t 300000 -m 200 ./magick convert @@ /dev/null

? 這句afl-fuzz 命令行的意思是:使用fuzzdata 中的PNG 測試樣本作為輸入,把Fuzzing 的結果保存到fuzzing_output 目錄中.afl-fuzz 程序運行30 秒超時值是為了讓ImageMagick 有更多的時間來執行代碼,很有可能Fuzzing 到一些執行很久循環代碼,被afl-fuzz 當作超時樣本來記錄,但這些樣本並不是Out-of-Memory 或者CPU exhaustion 類的漏洞.最後利用ImageMagick 的convert 命令來測試ImageMagick 的讀操作。

? 下面是Fuzzing 的情況,在AFL fuzzing 裏獲得了7 個CVE.

跑AFL 一定需要註意樣本,好的樣本能夠大大提升Fuzzing 效率。

? *參考鏈接: http://lcamtuf.coredump.cx/afl/

4. AddressSanitizer (ASAN)與崩潰調試

? 跑到新的崩潰文件時,往往需要進一步定位崩潰點.筆者推薦使用AddressSanitizer ,它是非常強大的內存檢測工具,它能夠檢測不同的內存漏洞以及內存泄漏(內存泄漏也算CVE ,具體請到CVE 官往搜索ImageMagick 的漏洞).我們需要在編譯的時候引入ASAN:

cd ImageMagick  ./configure CC="gcc" CXX="g++" CFLAGS="-g -fsanitize=address"  make

? 在CFLAGS 參數裏:-g 指編譯的時候添加符號信息,-fsanitize=address 則是讓編譯器在編譯時添加ASAN .然後在 utilities 下生成的 ./magick 程序就帶有ASAN 了.運行一個崩潰樣本,輸出如下:

fuzzing@ubuntu:~/fuzzing/ImageMagick/utilities$ ./magick convert all_fuzzing_format_2017_7_16_5_13_10/crash/heap-buffer-overflow-READ-0x7f58970bcdc4_output_ps_1500207243.43 output.ps  ==61378==ERROR: AddressSanitizer: heap-buffer-overflow on address 0x7f06a32fedcc at pc 0x7f06aec0f9c2 bp 0x7ffcb67d5a30 sp 0x7ffcb67d5a20  READ of size 4 at 0x7f06a32fedcc thread T0  #0 0x7f06aec0f9c1 in GetPixelAlpha MagickCore/pixel-accessor.h:59  #1 0x7f06aec17ff8 in WritePSImage coders/ps.c:2046  #2 0x7f06ae7491c6 in WriteImage MagickCore/constitute.c:1114  #3 0x7f06ae749e42 in WriteImages MagickCore/constitute.c:1333  #4 0x7f06adf9c3eb in ConvertImageCommand MagickWand/convert.c:3280  #5 0x7f06ae094d98 in MagickCommandGenesis MagickWand/mogrify.c:183  #6 0x4017f1 in MagickMain utilities/magick.c:149  #7 0x4019d2 in main utilities/magick.c:180  #8 0x7f06ad80982f in __libc_start_main (/lib/x86_64-linux-gnu/libc.so.6+0x2082f)  #9 0x401308 in _start (/home/fuzzing/fuzzing/ImageMagick/utilities/.libs/lt-magick+0x401308)  0x7f06a32fedcc is located 12 bytes to the right of 556480-byte region [0x7f06a3277000,0x7f06a32fedc0)  allocated by thread T0 here:  #0 0x7f06af3e5076 in __interceptor_posix_memalign (/usr/lib/x86_64-linux-gnu/libasan.so.2+0x99076)  #1 0x7f06ae8ed8de in AcquireAlignedMemory MagickCore/memory.c:262  #2 0x7f06ae6e4731 in OpenPixelCache MagickCore/cache.c:3523  #3 0x7f06ae6dd0d1 in GetImagePixelCache MagickCore/cache.c:1667  #4 0x7f06ae6ec1f0 in SyncImagePixelCache MagickCore/cache.c:5222  #5 0x7f06ae8b9609 in SetImageExtent MagickCore/image.c:2554  #6 0x7f06aec53c99 in ReadSGIImage coders/sgi.c:374  #7 0x7f06ae746068 in ReadImage MagickCore/constitute.c:497  #8 0x7f06ae748267 in ReadImages MagickCore/constitute.c:866  #9 0x7f06adf060ad in ConvertImageCommand MagickWand/convert.c:641  #10 0x7f06ae094d98 in MagickCommandGenesis MagickWand/mogrify.c:183  #11 0x4017f1 in MagickMain utilities/magick.c:149  #12 0x4019d2 in main utilities/magick.c:180  #13 0x7f06ad80982f in __libc_start_main (/lib/x86_64-linux-gnu/libc.so.6+0x2082f)  SUMMARY: AddressSanitizer: heap-buffer-overflow MagickCore/pixel-accessor.h:59 GetPixelAlpha  Shadow bytes around the buggy address:  0x0fe154657d60: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  0x0fe154657d70: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  0x0fe154657d80: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  0x0fe154657d90: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  0x0fe154657da0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  =>0x0fe154657db0: 00 00 00 00 00 00 00 00 fa[fa]fa fa fa fa fa fa  0x0fe154657dc0: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa  0x0fe154657dd0: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa  0x0fe154657de0: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa  0x0fe154657df0: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa  0x0fe154657e00: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa  Shadow byte legend (one shadow byte represents 8 application bytes):  Addressable: 00  Partially addressable: 01 02 03 04 05 06 07  Heap left redzone: fa  Heap right redzone: fb  Freed heap region: fd  Stack left redzone: f1  Stack mid redzone: f2  Stack right redzone: f3  Stack partial redzone: f4  Stack after return: f5  Stack use after scope: f8  Global redzone: f9  Global init order: f6  Poisoned by user: f7  Container overflow: fc  Array cookie: ac  Intra object redzone: bb  ASan internal: fe  ==61378==ABORTING

? ASAN 同樣也能用在漏洞挖掘上(比如Heap-overflow READ ,這樣的漏洞不容易觸發崩潰(除非讀到不能夠訪問的內存上,否則是不會產生崩潰的)).但是在AFL 裏,不可以在./configure 時把-fsanitize=address 直接添加在CFLAGS 裏面,而是需要在make 的時候這樣寫:

AFL_USE_ASAN=1 make

? 把ASAN 作為異常捕獲的,還有libFuzzer。

5. 如何使用libFuzzer Fuzzing ImageMagick?

? libFuzzer 與AFL 的區別在於:libFuzzer 傾向於對某個功能或者函數來進行Fuzzing,AFL 則是對整體程序進行Fuzzing。下面是一段使用libFuzzer Fuzzing C-ares DNS 庫的示例代碼(CVE-2017-1000381):

#include <malloc.h>  #include <ares.h>  extern "C" int LLVMFuzzerTestOneInput(const unsigned char* data,size_t size) {      ares_naptr_reply* naptr_out = 0;      ares_parse_naptr_reply(data,size,&naptr_out);      if (0 != naptr_out)          free(naptr_out);  }

? LLVMFuzzerTestOneInput() 函數是libFuzzer 庫進行單元測試的調用點,這裏面的實現就是我們將要編寫的測試用例.libFuzzer 能夠更深入去挖掘潛在的漏洞,但是需要對源碼有一定的了解.以ImageMagick 為例,convert 命令中的圖片最後會被ReadImage() 調用去解析,ReadImage() 函數會根據目錄去打開文件再從中讀取數據來給指定的格式來解析.下面是針對ImageMagick 的圖片解析Fuzzing 代碼:

#include <malloc.h>  #include <memory.h>  #include <stdio.h>  #include <stdlib.h>  #define MAGICK_IMPLEMENTATION  #define MagickExport  #include "magick/common.h"  #include "magick/image.h"  #include "magick/error.h"  #include "magick/constitute.h"  #include "magick/magick_types.h"  #include "magick/utility.h"  #include "magick/magick.h"  static const struct {    char      *name;    unsigned char      *magic;    unsigned int      length,      offset;  } StaticMagic[] = {  #define MAGIC(name,offset,magic) {name,(unsigned char *)magic,sizeof(magic)-1,offset}    MAGIC("WEBP", 8, "WEBP"),    MAGIC("AVI", 0, "RIFF"),    MAGIC("8BIMWTEXT", 0, "8\000B\000I\000M\000#"),    MAGIC("8BIMTEXT", 0, "8BIM#"),    MAGIC("8BIM", 0, "8BIM"),    MAGIC("BMP", 0, "BA"),    MAGIC("BMP", 0, "BM"),    MAGIC("BMP", 0, "CI"),    MAGIC("BMP", 0, "CP"),    MAGIC("BMP", 0, "IC"),    MAGIC("BMP", 0, "PI"),    MAGIC("CALS", 21, "version: MIL-STD-1840"),    MAGIC("CALS", 0, "srcdocid:"),    MAGIC("CALS", 9, "srcdocid:"),    MAGIC("CALS", 8, "rorient:"),    MAGIC("CGM", 0, "BEGMF"),    MAGIC("CIN", 0, "\200\052\137\327"),    MAGIC("DCM", 128, "DICM"),    MAGIC("DCX", 0, "\261\150\336\72"),    MAGIC("DIB", 0, "\050\000"),    MAGIC("DOT", 0, "digraph"),    MAGIC("DPX", 0, "SDPX"),    MAGIC("DPX", 0, "XPDS"),    MAGIC("EMF", 40, "\040\105\115\106\000\000\001\000"),    MAGIC("EPT", 0, "\305\320\323\306"),    MAGIC("FAX", 0, "DFAX"),    MAGIC("FIG", 0, "#FIG"),    MAGIC("FITS", 0, "IT0"),    MAGIC("FITS", 0, "SIMPLE"),    MAGIC("FPX", 0, "\320\317\021\340"),    MAGIC("GIF", 0, "GIF8"),    MAGIC("HDF", 1, "HDF"),    MAGIC("HPGL", 0, "IN;"),    MAGIC("HPGL", 0, "\033E\033"),    MAGIC("HTML", 1, "HTML"),    MAGIC("HTML", 1, "html"),    MAGIC("ILBM", 8, "ILBM"),    MAGIC("IPTCWTEXT", 0, "\062\000#\000\060\000=\000\042\000&\000#\000\060\000;\000&\000#\000\062\000;\000\042\000"),    MAGIC("IPTCTEXT", 0, "2#0=\042\042"),    MAGIC("IPTC", 0, "\034\002"),    MAGIC("JNG", 0, "\213JNG\r\n\032\n"),    MAGIC("JPEG", 0, "\377\330\377"),    MAGIC("JPC", 0, "\377\117"),    MAGIC("JP2", 4, "\152\120\040\040\015"),    MAGIC("MAT", 0, "MATLAB 5.0 MAT-file,"),    MAGIC("MIFF", 0, "Id=ImageMagick"),    MAGIC("MIFF", 0, "id=ImageMagick"),    MAGIC("MNG", 0, "\212MNG\r\n\032\n"),    MAGIC("MPC", 0, "id=MagickCache"),    MAGIC("MPEG", 0, "\000\000\001\263"),    MAGIC("PCD", 2048, "PCD_"),    MAGIC("PCL", 0, "\033E\033"),    MAGIC("PCX", 0, "\012\002"),    MAGIC("PCX", 0, "\012\005"),    MAGIC("PDB", 60, "vIMGView"),    MAGIC("PDF", 0, "%PDF-"),    MAGIC("PFA", 0, "%!PS-AdobeFont-1.0"),    MAGIC("PFB", 6, "%!PS-AdobeFont-1.0"),    MAGIC("PGX", 0, "PG ML"),    MAGIC("PGX", 0, "PG LM"),    MAGIC("PICT", 522, "\000\021\002\377\014\000"),    MAGIC("PNG", 0, "\211PNG\r\n\032\n"),    MAGIC("PBM", 0, "P1"),    MAGIC("PGM", 0, "P2"),    MAGIC("PPM", 0, "P3"),    MAGIC("PBM", 0, "P4"),    MAGIC("PGM", 0, "P5"),    MAGIC("PPM", 0, "P6"),    MAGIC("P7", 0, "P7 332"), /* XV Thumbnail */    MAGIC("PAM", 0, "P7"), /* Should be listed after "P7 332" */    MAGIC("PS", 0, "%!"),    MAGIC("PS", 0, "\004%!"),    MAGIC("PS", 0, "\305\320\323\306"),    MAGIC("PSD", 0, "8BPS"),    MAGIC("PWP", 0, "SFW95"),    MAGIC("RAD", 0, "#?RADIANCE"),    MAGIC("RAD", 0, "VIEW= "),    MAGIC("RLE", 0, "\122\314"),    MAGIC("SCT", 0, "CT"),    MAGIC("SFW", 0, "SFW94"),    MAGIC("SGI", 0, "\001\332"),    MAGIC("SUN", 0, "\131\246\152\225"),    MAGIC("SVG", 1, "?XML"),    MAGIC("SVG", 1, "?xml"),    MAGIC("TIFF", 0, "\115\115\000\052"),    MAGIC("TIFF", 0, "\111\111\052\000"),    MAGIC("BIGTIFF", 0, "\115\115\000\053\000\010\000\000"),    MAGIC("BIGTIFF", 0, "\111\111\053\000\010\000\000\000"),    MAGIC("VICAR", 0, "LBLSIZE"),    MAGIC("VICAR", 0, "NJPL1I"),    MAGIC("VIFF", 0, "\253\001"),    MAGIC("WMF", 0, "\327\315\306\232"),    MAGIC("WMF", 0, "\001\000\011\000"),    MAGIC("WPG", 0, "\377WPC"),    MAGIC("XBM", 0, "#define"),    MAGIC("XCF", 0, "gimp xcf"),    MAGIC("XPM", 1, "* XPM *"),    MAGIC("XWD", 4, "\007\000\000"),    MAGIC("XWD", 5, "\000\000\007")  };  int random(const unsigned char* data,unsigned int size) {      unsigned int random_code = 0;      for (unsigned int data_index = 0;data_index < size;++data_index)          random_code += data[data_index];      return random_code % (sizeof(StaticMagic) /sizeof(StaticMagic[0]));  }  #define GENARATE_FILE_NAME "./libFuzzerGenarateImageSample"  extern "C" int LLVMFuzzerTestOneInput(const unsigned char* data,unsigned int size) {      int random_image_flag_index = random(data,size);      unsigned int random_image_flag_offset = StaticMagic[random_image_flag_index].offset;      unsigned int random_image_flag_length = StaticMagic[random_image_flag_index].length;      unsigned int image_buffer_length = random_image_flag_offset + random_image_flag_length + size;      unsigned char* image_buffer = (unsigned char*)malloc(image_buffer_length);      memset(image_buffer,0,image_buffer_length);      memcpy(image_buffer,StaticMagic[random_image_flag_index].name,StaticMagic[random_image_flag_index].length);      FILE* file = fopen(GENARATE_FILE_NAME,"w");      if (NULL != file) {          fwrite(image_buffer,1,image_buffer_length,file);          fclose(file);          printf("buffer=%s(0x%X), size=%d,input format=%s\n",image_buffer,image_buffer,image_buffer_length,StaticMagic[random_image_flag_index].name);          ExceptionInfo exception;          ImageInfo* read_image_info;          ImageInfo* write_image_info;          Image*     image;          GetExceptionInfo(&exception);          read_image_info = CloneImageInfo((ImageInfo*)NULL);          write_image_info = CloneImageInfo((ImageInfo*)NULL);          strlcpy(read_image_info->filename,GENARATE_FILE_NAME,MaxTextExtent);          strlcpy(write_image_info->filename,"/dev/null",MaxTextExtent);          SetImageInfo(read_image_info,SETMAGICK_READ,&exception);          SetImageInfo(write_image_info,SETMAGICK_WRITE,&exception);          image = ReadImage(read_image_info,&exception);          if (NULL != image)              WriteImage(write_image_info,image);          DestroyImageInfo(read_image_info);          DestroyImageInfo(write_image_info);          DestroyExceptionInfo(&exception);      }      free(image_buffer);      return 0;  }  extern "C" int LLVMFuzzerInitialize(int *argc, char ***argv) {      InitializeMagick((const char*)argv[0]);      return 1;  }
使用libFuzzer Fuzzing 時,編譯時一定要添加ASAN :
cd ImageMagick  ./configure CC="clang" CXX="clang++" CFLAGS="-O2 -g -fsanitize-coverage=trace-pc-guard -fsanitize=address"  make  cd utilities  clang++ read_image_fuzzer.cc -O2 -g -fsanitize-coverage=trace-pc-guard -fsanitize=address -I .. -ljbig -lwebp -llcms2 -ltiff -lfreetype -ljpeg -lpng12 -lwmflite -lXext -lSM -lICE -lX11 -llzma -lbz2 -lxml2 -lz -lm -lgomp -lpthread ../magick/.libs/libMagickCore-7.Q16HDRI.a ../../libFuzzer/Fuzzer/libFuzzer.a -o read_image_fuzzer

? 第一次編譯ImageMagick 時,一定要把ASAN 也編譯進去,否則在ImageMagick 庫裏面跑出來漏洞libFuzzer 不會提示!接下來編譯我們的測試Fuzzer ,註意需要這兩次編譯都要用到clang 5 ,因為插件-fsanitize-coverage=trace-pc-guard 需要在高版本的clang 裏才有,否則libFuzzer 會沒有數據輸入(筆者也是在讀libFuzzer 的源碼裏發現的),後面需要引用的庫則根據自己的系統來添加,會有些差異。

? 關於更多libFuzzer 的知識,可以參考這兩個鏈接:

? 1、 https://github.com/Dor1s/libfuzzer-workshop

? 2、 http://llvm.org/docs/LibFuzzer.html

6. 程序模型與代碼覆蓋

? 程序模型簡單地來說可以分為三部分:輸入,處理,輸出.無論是WEB 還是二進制都一樣成立, 漏洞會隱藏在這三個階段的某個角落裏 .舉個例子,輸入階段有:ETERNALBLUE ,S2-045 (上傳組件的異常信息中可以執行OGNL 導致GetShell ),CVE-2017-7269—IIS 6.0 WebDAV ;處理階段:各大SRC 裏的業務邏輯漏洞,BadKernel ;輸出階段:XSS 漏洞.Fuzzing 的時候也需要考慮到這點,盡可能讓程序所有的階段都能夠執行,達到更高的代碼覆蓋率

? 在前面AFL 與libFuzzer 的例子裏,我們暫時只Fuzzing ImageMagick 讀取圖片部分(對應到代碼的ReadXXXImage() 函數),接下來我們要針對ImageMagick 的寫圖片部分進行Fuzzing(對應到代碼的WriteXXXImage() 函數)

7. 使用python 實現ImageMagick Fuzzer

? 我們自己在實現Fuzzer 的時候,如何準確地檢測觸發的漏洞點是一個難題,現在已經有ASAN 作為強力的Bug 檢測工具,我們只需要針對Fuzzing 的邏輯來編寫即可.前面提到,ImageMagick 可以實現不同圖像格式之間的轉換,所以在Fuzzing 時要觸發到WriteXXXImage() 這類函數,必須要以指定的格式來輸出.比如說觸發WritePNGImage() 函數,magick 就必須要輸出.png 格式圖像;觸發WriteARTImage() 函數,magick 就必須要輸出.art 格式圖像,命令如下:

./magick convert test_image output.png  ./magick convert test_image output.art

ImageMagick 庫支持很多格式的圖像轉換,更多信息可以在SourceInsight 裏搜索Write*Image() 函數:

? 最後得到支持輸出格式的列表:

imagemagick_output_format = ['output.aai','output.art','output.avs','output.bgr','output.bmp','output.braille','output.cals','output.cin','output.cip','output.clipboard','output.cmyk','output.dds','output.debug','output.dib','output.dpx','output.ept','output.exr','output.fax','output.fits','output.flif','output.fpx','output.gif','output.gray','output.histogram','output.hrz','output.html','output.icon','output.info','output.inline','output.ipl','output.jbig','output.jp2','output.jpeg','output.json','output.magick','output.map','output.mask','output.mat','output.matte','output.meta','output.miff','output.mono','output.mpc','output.mpeg','output.mpr','output.msl','output.mtv','output.mvg','output.null','output.otb','output.palm','output.pcd','output.pcl','output.pcx','output.pdb','output.pdf','output.pgx','output.pict','output.jng','output.mng','output.png','output.pnm','output.ps','output.ps2','output.ps3','output.psd','output.raw','output.rgb','output.rgf','output.sgi','output.sixelt','output.sun','output.svg','output.tga','output.thumbnail','output.ptif','output.tiff','output.txt','output.uil','output.uyvy','output.vicar','output.vid','output.viff','output.vips','output.wbmp','output.webp','output.xbm','output.picon','output.xpm','output.xtrn','output.xwd','output.ycbcr','output.ps3mask','output.group4','output.yuv''output.x']

然後給ImageMagick 來運行即可,代碼如下:

def run_imagemagick_convert(input_file,output_file) :      process = subprocess.Popen(['./magick','convert',input_file,output_file],stdout = subprocess.PIPE,stderr = subprocess.PIPE)  #  使用樣本運行magick convert      process_timeout_exit = lambda : process.kill()      timeout = threading.Timer(MAX_PROCESS_WAIT_TIME,process_timeout_exit)  #  執行超時退出      timeout.start()      process.wait()      timeout.cancel()      std_error_output = process.stderr.read()  #  從stderr 中讀取ASAN 的崩潰信息輸出      if len(std_error_output) and not -1 == std_error_output.find('========') :  #  ASAN Check ..          flag_address_sanitize = 'ERROR: AddressSanitizer: '          flag_address_sanitize_offset = std_error_output.find(flag_address_sanitize)          flag_leak_sanitize = 'ERROR: LeakSanitizer: '          flag_leak_sanitize_offset = std_error_output.find(flag_leak_sanitize)          if not -1 == flag_address_sanitize_offset :  #  內存漏洞分析              crash_type = std_error_output[flag_address_sanitize_offset + len(flag_address_sanitize) : ]              crash_type = crash_type[ : crash_type.find('on') ].strip()              crash_type_detail = ''              flag_at_pc = 'pc '              flag_at_pc_offset = std_error_output.find(flag_at_pc)              crash_point = std_error_output[flag_at_pc_offset + len(flag_at_pc) : ]              crash_point = crash_point[ : crash_point.find(' bp')]              if not -1 == crash_type.find('buffer-overflow') :  #  stack and heap                  flag_of_size = ' of size'                  flag_of_size_offset = std_error_output.find(flag_of_size)                  crash_type_detail = std_error_output[ : flag_of_size_offset]                  crash_type_detail = crash_type_detail[crash_type_detail.rfind('\n') : ].strip()              elif 'SEGV' == crash_type :  #  Null point reference ..                  flag_of_address = 'unknown address'                  flag_of_address_offset = std_error_output.find(flag_of_address)                  crash_type_detail = std_error_output[flag_of_address_offset + len(flag_of_address) : ]                  crash_type_detail = crash_type_detail[ : crash_type_detail.find('(')].strip()              return True,crash_type,crash_type_detail,crash_point,std_error_output          elif not -1 == flag_leak_sanitize_offset :  #  內存泄漏分析              flag_direct_leak = 'Direct leak'              flag_indirect_leak = 'Indirect leak'              memory_leak_id = std_error_output.count(flag_direct_leak) + std_error_output.count(flag_indirect_leak)              return True , 'Memory-Leak' , str(memory_leak_id) , None , std_error_output          return True , None , None , None , std_error_output      return False , None , None , None , None            #  省略無關代碼            for graphicsmagick_input_file_index in graphicsmagick_input_file_list :  #  樣本列表          for graphicsmagick_output_format_index in imagemagick_output_format :  #  測試輸出格式列表              graphicsmagick_output = crash_dir_crash_output_dir + '/' + graphicsmagick_input_file_index + '_' + graphicsmagick_output_format_index              result = run_graphicsmagick_convert(graphicsmagick_input_file_index_path,graphicsmagick_output)  #  調用magick convert

? 還需要註意的一點是,ImageMagick 會在 /tmp 目錄下生成臨時文件而且它不會去刪除這些文件,所以還需要去手動刪除:

import os  import time  if __name__ == '__main__' :      while True :          file_list = os.listdir('/tmp')          for file_index in file_list :              if file_index.startswith('magick') :                  try :                      os.remove('/tmp/' + file_index)                  except :                      pass          time.sleep(10)

? 上面的Fuzzer 需要依賴樣本,我們也可以使用Python 像libFuzzer 那樣生成數據到給ImageMagick 測試:

def build_random_data(data_length) :  #  隨機生成指定長度的數據      data = http://www.tuicool.com/articles/b''      for data_index in range(data_length) :          data += chr(random.randrange(255))      return data  def build_random_image(data_length) :  #  生成測試圖像      random_image_header = random.choice(image_header)  #  image_header 和libFuzzer 裏的ImageMagick 頭一樣      return build_random_data(random_image_header[1]) + random_image_header[2] + build_random_data(data_length)        #  省去多余代碼        while True :      random_image = build_random_image(max_fuzzing_image_length)            write_file('fuzzing_genarate_image',random_image)            for graphicsmagick_output_format_index in imagemagick_output_format :          result = run_graphicsmagick_convert('fuzzing_genarate_image',fuzzing_dir + '/' +graphicsmagick_output_format_index)                    if result[0] :  #  catch crash              ...

? 在此限於篇幅,所有Fuzzing 的完整代碼在我的github

.執行效果如下:

? 筆者已經使用這個Python Fuzzer 收獲了至少20 個CVE (數據生成挖到了3 個崩潰,利用樣本測試ImageMagick 寫測試至少獲得了17 個崩潰),在此通過這篇文章和各位讀者分析對於二進制軟件的Fuzzing 的一些常用技術,預祝大家順利挖到CVE .下面是挖掘到的漏洞列表:

本站仅提供存储服务,所有内容均由用户发布,如发现有害或侵权内容,请点击举报
打开APP,阅读全文并永久保存 查看更多类似文章
猜你喜欢
类似文章
【热】打开小程序,算一算2024你的财运
R语言直接将图片导出至pptx或docx
PHP图像处理类库MagickWand用法实例分析 PHP
ImageMagick: Install from Binary Distribution
Magick 1.0:高级图形和图像处理R
图片服务器
ZBar在Windows上的使用(1)
更多类似文章 >>
生活服务
热点新闻
分享 收藏 导长图 关注 下载文章
绑定账号成功
后续可登录账号畅享VIP特权!
如果VIP功能使用有故障,
可点击这里联系客服!

联系客服