一、前言
在我之前發(fā)布的關(guān)于Yahoobleed #1(YB1)的一篇文章中,我們了解到如何利用未初始化漏洞獲取其他用戶的私密圖像信息。當(dāng)時(shí)泄露的內(nèi)存數(shù)據(jù)受制于JPEG圖像的壓縮機(jī)制,這種限制對(duì)某個(gè)圖像大盜來(lái)說(shuō)不成問(wèn)題,然而如果我們想要竊取其他類型的內(nèi)存內(nèi)容,已泄露的這些數(shù)據(jù)仍稍顯不足。
在這篇文章中,我會(huì)向大家介紹Yahoo!縮略圖服務(wù)器上存在的另一個(gè)滴血(bleed)類的漏洞,我們可以將這個(gè)稱之為Yahoobleed #2(YB2)。我們可以利用這個(gè)JPEG壓縮缺陷(現(xiàn)在這個(gè)缺陷依然存在)提取原始的內(nèi)存字節(jié)。結(jié)合“strings”工具的使用,我們還可以發(fā)現(xiàn)更多驚喜。
Yahoo!通過(guò)棄用ImageMagic的方式,在修復(fù)YB1漏洞的同時(shí)也修復(fù)了YB2漏洞。大家可以參考我之前的一篇文章,通過(guò)Yahoo!的回復(fù)進(jìn)一步了解漏洞的修復(fù)細(xì)節(jié)。
二、可視化

當(dāng)我們觸發(fā)該漏洞時(shí),Yahoo!服務(wù)器返回JPEG圖片上會(huì)出現(xiàn)某些噪點(diǎn)。如上圖所示,漏洞觸發(fā)后,Yahoo!服務(wù)器返回了某張JPEG圖片,將圖片的某個(gè)64x4區(qū)域轉(zhuǎn)化為png?www.myhack58.com格式(即無(wú)損格式),放大后可以看到某些噪點(diǎn),如上圖所示。
這張圖片有許多有趣的地方值得注意,就現(xiàn)在而言,我們可以觀察到的是,圖片的頂部存在幾個(gè)指針圖像。我曾在Project Zero上發(fā)表過(guò)有關(guān)指針可視化的一篇文章,如果你對(duì)這篇文章有所了解,你可以在各種內(nèi)存泄露場(chǎng)景中發(fā)現(xiàn)這類指針的存在。至少對(duì)于x86_64架構(gòu)的Linux來(lái)說(shuō),該系統(tǒng)中的指針通常都包含一個(gè)0x00007f的前綴。這類指針的包含幾個(gè)特點(diǎn),如:
1、使用相同的對(duì)齊方式重復(fù)類似的結(jié)構(gòu)(64位系統(tǒng)上使用8字節(jié)對(duì)齊);
2、在指針塊中,白色垂直條紋指向的是被對(duì)齊的最重要的字節(jié),表示的是0x00(需要注意的是,由于輸入文件的特殊格式,所泄露的字節(jié)實(shí)際上經(jīng)過(guò)了取反處理);
3、在指針塊中,緊挨其后的黑色垂直條紋所代表的含義是某行的7個(gè)比特的值被設(shè)置為0x7f。
4、在指針塊中,緊挨其后的白色垂直細(xì)長(zhǎng)條紋所代表的含義是該行的1個(gè)比特的值沒(méi)有被設(shè)置為0x7f。
但我們還是會(huì)遇到JPEG壓縮算法,這會(huì)不會(huì)對(duì)逐字節(jié)數(shù)據(jù)提取造成困難?我們會(huì)慢慢揭開(kāi)這個(gè)謎題。
三、漏洞分析
我們不知道Yahoo!所使用的ImageMagic是否是最新版本,因此我們開(kāi)始尋找相關(guān)的漏洞是否存在。我們已經(jīng)知道Yahoo!支持RLE格式,之前我在一篇文章中介紹了如何使用某個(gè)技巧挖掘Box.com的內(nèi)存損壞漏洞,也許我們可以使用相同的技巧對(duì)Yahoo!進(jìn)行測(cè)試。
非常有趣的是,漏洞利用文件并不會(huì)導(dǎo)致任何文件崩潰,但服務(wù)器依然渲染了所有的測(cè)試文件,而最新版本的ImageMagic完全不會(huì)渲染這些文件。經(jīng)過(guò)一番思考,我認(rèn)為最有可能的解釋就是,Yahoo!所用的ImageMagic的確是存在漏洞的老版本,但根據(jù)我們對(duì)YB1漏洞的分析,Yahoo!使用了不一樣的堆設(shè)置,受對(duì)齊方式影響,我們的堆越界訪問(wèn)測(cè)試用例無(wú)法在Yahoo!上生效。
為了驗(yàn)證這個(gè)假設(shè),我們構(gòu)造了一個(gè)堆越界寫RLE文件,足以覆蓋堆中的某一小塊數(shù)據(jù)(64字節(jié)大小,大約會(huì)有16個(gè)字節(jié)被覆蓋),并將該文件上傳到Y(jié)ahoo!上。訪問(wèn)縮略圖URL后,服務(wù)器會(huì)較為穩(wěn)定地(以大約50%左右的概率)返回如下結(jié)果:

這個(gè)結(jié)果看上去像是一個(gè)非常明顯的后端錯(cuò)誤,我們猜測(cè)它最有可能是一個(gè)SIGSEGV錯(cuò)誤,這個(gè)錯(cuò)誤與已有兩年歷史的RLE內(nèi)存損壞問(wèn)題有關(guān)。
但是我們今天的目標(biāo)并不是如何利用這個(gè)RCE內(nèi)存損壞錯(cuò)誤,雖然這個(gè)過(guò)程肯定非常有趣。我們的目標(biāo)是通過(guò)滴血攻擊來(lái)提取數(shù)據(jù)。現(xiàn)在,我們手上擁有的是一個(gè)已有2.5歲年齡的ImageMagick,對(duì)于這個(gè)版本的ImageMagick而言,肯定有許多漏洞已經(jīng)被修復(fù)了。經(jīng)過(guò)一番搜索,我們找到了一個(gè)候選漏洞:一個(gè)已有2年以上歷史的、已經(jīng)在SUN解碼器中修復(fù)的越界漏洞。在這個(gè)漏洞的修復(fù)代碼中,開(kāi)發(fā)者似乎對(duì)長(zhǎng)度做了相關(guān)檢查,同時(shí)更加徹底地應(yīng)用長(zhǎng)度檢查過(guò)程,使其能夠?qū)⑸睿╞it depth)為1的圖像包含在內(nèi)。讓我們稍微研究一下沒(méi)有打上補(bǔ)丁的代碼,并且深入跟蹤程序?qū)ι顬?的圖片的解碼路徑,我們可以發(fā)現(xiàn)如下代碼(位于coders/sun.c中):
sun_info.width=ReadBlobMSBLong(image);
sun_info.height=ReadBlobMSBLong(image);
sun_info.depth=ReadBlobMSBLong(image);
sun_info.length=ReadBlobMSBLong(image);
[...]
number_pixels=(MagickSizeType) image->columns*image->rows;
if ((sun_info.type != RT_ENCODED) && (sun_info.depth >= 8) &&
((number_pixels*((sun_info.depth+7)/8)) > sun_info.length))
ThrowReaderException(CorruptImageError,"ImproperImageHeader");
bytes_per_line=sun_info.width*sun_info.depth;
sun_data=(unsigned char *) AcquireQuantumMemory((size_t) sun_info.length,
sizeof(*sun_data));
[...]
count=(ssize_t) ReadBlob(image,sun_info.length,sun_data);
if (count != (ssize_t) sun_info.length)
ThrowReaderException(CorruptImageError,"UnableToReadImageData");
sun_pixels=sun_data;
bytes_per_line=0;
[...]
p=sun_pixels;
if (sun_info.depth == 1)
for (y=0; y rows; y++)
{
q=QueueAuthenticPixels(image,0,y,image->columns,1,exception);
if (q == (Quantum *) NULL) break;
for (x=0; x columns-7); x+=8)
{
for (bit=7; bit >= 0; bit--)
{
SetPixelIndex(image,(Quantum) ((*p) & (0x01
q);
q+=GetPixelChannels(image);
}
p++;
}
因此,我們發(fā)現(xiàn)代碼在處理色深為1的圖像時(shí)存在一個(gè)非常明顯的問(wèn)題:
1、假設(shè)該圖片的屬性為:width(寬度)=256, height(高度)=256, depth(色深)=1, length(長(zhǎng)度)=8;
2、色深為1的圖像會(huì)導(dǎo)致與sun_info.length對(duì)比的number_pixels的檢查過(guò)程被繞過(guò);
3、sun_data所分配的緩沖區(qū)大小為8字節(jié),而程序從輸入文件中讀取了這8個(gè)字節(jié);
4、然后程序按每個(gè)像素1個(gè)比特的方式對(duì)圖片進(jìn)行解碼。在這個(gè)過(guò)程中,sun_data總共需要(256*256)/8 == 8192個(gè)字節(jié),但只有8個(gè)字節(jié)可用;
5、然后就出現(xiàn)大量的越界讀取問(wèn)題;程序?qū)嶋H上是在一片越界內(nèi)存中對(duì)圖片進(jìn)行渲染。
四、漏洞利用
漏洞利用文件只有40個(gè)字節(jié),因此我們可以在十六進(jìn)制編輯器中觀察這個(gè)圖片的每個(gè)字節(jié)的含義:

59 A6 6A 95: 頭部
00 00 01 00 00 00 01 00: 圖片大小256 x 256
00 00 00 01: 圖片色深:每像素1比特
00 00 00 08: 圖片數(shù)據(jù)長(zhǎng)度為8
00 00 00 01: 圖片類型為1:標(biāo)準(zhǔn)圖片
00 00 00 00 00 00 00 00: map類型為空,長(zhǎng)度為0
41 41 41 41 41 41 41 41: 8字節(jié)的圖片數(shù)據(jù)
對(duì)于這個(gè)漏洞利用文件,我們可以操縱的最為有趣的一個(gè)變量就是圖片數(shù)據(jù)長(zhǎng)度(image data length)變量。只要我們將其大小控制在(256 * 256) / 8以內(nèi),我們就能復(fù)現(xiàn)越界讀取漏洞。但越界讀取從哪個(gè)位置開(kāi)始呢?它會(huì)在圖片數(shù)據(jù)分配緩沖區(qū)的末尾開(kāi)始。通過(guò)改變圖片數(shù)據(jù)的大小,我們最終有可能操縱堆中不同的相對(duì)地址(也許這些地址與緩沖區(qū)的開(kāi)始或結(jié)束位置在距離上關(guān)系更為密切)。這樣一來(lái)我們有可能能夠讀取某些有趣的數(shù)據(jù)。
五、信息提取
顯然我們可以利用這個(gè)漏洞來(lái)提取信息,這也是這個(gè)漏洞真正有價(jià)值的地方所在。正如可視化部分所描述的內(nèi)容,我們可以通過(guò)一個(gè)JPEG壓縮文件來(lái)嘗試提取信息。事實(shí)上我們可以通過(guò)ImageMagick獲得一張JPEG灰度圖像,因?yàn)榍懊婷肯袼?比特的漏洞利用文件會(huì)生成一個(gè)黑白相間的圖形。我們可以利用JPEG灰度圖片較好地實(shí)現(xiàn)信息提取。人們之所以設(shè)計(jì)JPEG壓縮算法,主要是想欺騙人眼對(duì)圖片的直觀感知。相對(duì)比真實(shí)的顏色而言,人類視覺(jué)對(duì)顏色的亮度更為敏感,因此在壓縮算法中,顏色數(shù)據(jù)通常會(huì)比亮度數(shù)據(jù)丟失的信息更多(我們主要是通過(guò)YCbCr顏色空間實(shí)現(xiàn)這一點(diǎn))。而JPEG灰度圖像中只有亮度數(shù)據(jù)存在。
回頭看看用于信息提取的那張JPEG圖像,我們?nèi)匀粫?huì)遇到JPEG壓縮算法。我們會(huì)被這個(gè)困難所阻攔嗎?答案是否定的。因?yàn)槲覀兊穆┒蠢脠D片是一張每像素1比特(黑色或白色)的圖片,因此我們只需要在信息提取的JPEG文件中精確控制每個(gè)像素的1比特信息熵即可。雖然某些白色像素實(shí)際上是有點(diǎn)淺灰色而不是全白色,且某些黑色像素實(shí)際上是有點(diǎn)深灰色而不是全黑色,但每個(gè)像素仍然非常接近于黑色或者白色。我當(dāng)然沒(méi)有經(jīng)過(guò)嚴(yán)密的數(shù)學(xué)計(jì)算,但事實(shí)上,對(duì)于Yahoo!服務(wù)器所使用的JPEG壓縮算法而言,每個(gè)像素的確包含1比特的信息熵。根據(jù)我的統(tǒng)計(jì),與白色偏差最大的那個(gè)像素顏色大約為85%的白色(即非常淺的灰色),而信息丟失的閾值當(dāng)然更低,可以達(dá)到50%或者以下。
我們可以嘗試從JPEG文件中恢復(fù)原始的字節(jié),只需要使用如下的ImageMagick轉(zhuǎn)換命令即可:
convert yahoo_file.jpg -threshold 50% -depth 1 -negate out.gray
對(duì)于本文開(kāi)頭的那個(gè)JPEG文件而言,恢復(fù)出來(lái)的原始內(nèi)存字節(jié)如下所示:
0000000 d0 f0 75 9b 83 7f 00 00 50 33 76 9b 83 7f 00 00
0000020 31 00 00 00 00 00 00 00 2c 00 00 00 00 00 00 00
從中我們可以看到兩個(gè)指針:0x00007f839b75f0d0以及0x00007f839b763350。
六、字符串及密鑰
因此,現(xiàn)在我們有了一個(gè)在字節(jié)數(shù)據(jù)上相當(dāng)可靠的信息提取方法,我們可以從Yahoo!縮略圖服務(wù)器上發(fā)現(xiàn)哪些有趣的字符串嗎?我們找到了許多信息量很大的字符串,我們用“redacted”替換了其中敏感的信息:
SSLCOOKIE: SSL=v=1&s=redacted&kv=0
Yahoo-App-Auth: v=1;a=yahoo.mobstor.client.mailtsusm2.prod;h=10.210.245.245;t=redacted;k=4;s=redacted
https://dl-mail.ymail.com/ws/download/mailboxes/@.id==redacted/messages/@.id==redacted/content/parts/@.id==2/raw?appid=getattachment&token=redacted&ymreqid=redacted
是的,結(jié)果看起來(lái)非常嚴(yán)重。
除了會(huì)話密鑰等有趣的字符串之外,我們還能看到其他信息嗎?是的,我們可以通過(guò)某些路徑、錯(cuò)誤信息以及版本字符串發(fā)現(xiàn)服務(wù)器的確在使用ImageMagick,而且版本非常老:/usr/lib64/ImageMagick-6.8.9/modules-Q16/coders/sun.so
ImageMagick 6.8.9-6 Q16 x86_64 2014-07-25 http://www.myhack58.com
unrecognized PerlMagick method
顯然,這些字符串可以作為這個(gè)漏洞的佐證,同時(shí)我們還可以注意到字符串中涉及到PerlMagick。我對(duì)PerlMagick并不熟悉,可能PerlMagick是用來(lái)實(shí)現(xiàn)進(jìn)程中的ImageMagick,這也是為什么我們的越界圖像數(shù)據(jù)能夠讀取這么多有趣信息的原因所在。
七、總結(jié)
這個(gè)漏洞非常有趣,我們發(fā)現(xiàn)服務(wù)器上存在內(nèi)存泄露漏洞,可以將每個(gè)經(jīng)過(guò)JPEG壓縮的像素的少量數(shù)據(jù)編碼后返回給我們,因此我們能夠較為穩(wěn)定地重新構(gòu)造原始的字節(jié),還原服務(wù)器所泄露的內(nèi)存數(shù)據(jù)。
在已啟用的解碼器中,不加限制地使用老版本的ImageMagick是一件非常危險(xiǎn)的事情,通過(guò)棄用ImageMagick,Yahoo!應(yīng)該已經(jīng)修復(fù)了這些問(wèn)題。
|