淺談zip格式處理邏輯漏洞

wyzsk發表於2020-08-19
作者: 路人甲 · 2015/10/22 10:21

前言:zip壓縮格式應用廣泛,各個平臺都有使用,Windows平臺使用來壓縮檔案,Android平臺使用來作為apk檔案的格式。由於zip檔案格式比較複雜,在解析zip檔案格式時,如果處理不當,可能導致一些有意思的邏輯漏洞,本篇文章將挑選有意思的漏洞進行解析。

一、副檔名欺騙漏洞


很早之前,國外安全研究人員爆料Winrar 4.x版本存在副檔名欺騙漏洞,駭客可以透過該漏洞誘騙受害者執行惡意程式。該漏洞的主要原理是:Winrar在檔案預覽和解壓縮顯示檔名使用的是不同結構體的欄位導致的。

1.1 zip格式檔案的結構

在瞭解漏洞的原理前,先熟悉下zip格式的檔案結構。

如果一個壓縮包檔案裡有多個檔案,可以認為每個檔案都是被單獨壓縮,然後再拼成一起。

一個 ZIP 檔案由三個部分組成:壓縮原始檔資料區+壓縮原始檔目錄區+壓縮原始檔目錄結束標誌,如下圖:

enter image description here

1)檔案頭(壓縮原始檔目錄區)在檔案末尾,即圖1中的File Header,記錄了索引段的偏移、大小等等。

2)資料段(壓縮原始檔資料區)在檔案開頭,即圖1中的Local Header,記錄了資料的一些基本資訊,可以用來跟File Header中記錄的資料進行比較,保證資料的完整性。

3)Local Header還包含了檔案被壓縮之後的儲存區,即圖1中的Data區域。

4)圖2和圖3為Local Header(圖2中的ZIPFILERECORD)和File Header(圖3中的ZIPDIRENTRY)的資料對比,兩者資料是一致的。

enter image description here

enter image description here

1.2 漏洞產生原因

Winrar在檔案預覽的時候使用的是ZIPDIRENTRY下面的deFileName欄位來顯示檔名,解壓縮的時候使用的是ZIPFILERECORD下面的frFileName欄位來顯示檔名。如果將deFileName欄位副檔名改成jpggif等圖片的副檔名,可以欺騙使用者執行惡意程式。

Winrar檔案預覽示意圖:

enter image description here

使用者看到的是jpg圖片,開啟的確實exe檔案,真坑啊!

Winrar解壓縮檔案示意圖:

enter image description here

解壓縮之後顯示的exe,兩處顯示的不一樣。

二、Android Master Key漏洞


之前,國外安全研究人員爆出第三個Android Master Key漏洞,該漏洞的主要原理是:android在解析Zip包時,沒有校驗ZipEntryHeader中的FileNameLength是否一致。

2.1 zip檔案格式的結構

在瞭解漏洞的原理前,還是先熟悉下zip格式的檔案結構。

如果一個壓縮包檔案裡有多個檔案,可以認為每個檔案都是被單獨壓縮,然後再拼成一起。

一個 ZIP 檔案由三個部分組成:壓縮原始檔資料區+壓縮原始檔目錄區+壓縮原始檔目錄結束標誌,如圖1所示:

1)檔案頭(壓縮原始檔目錄區)在檔案末尾,即圖1中的File Header,記錄了索引段的偏移、大小等等。

2)資料段(壓縮原始檔資料區)在檔案開頭,即圖1中的Local Header,記錄了資料的一些基本資訊,可以用來跟File Header中記錄的資料進行比較,保證資料的完整性。

3)Local Header還包含了檔案被壓縮之後的儲存區,即圖1中的Data區域。

4)圖2和圖3為Local Header(圖2中的ZIPFILERECORD)和File Header(圖3中的ZIPDIRENTRY)的資料對比,兩者資料是一致的。

enter image description here

enter image description here

2.2 漏洞產生原因

先來看一下是如何定位到Local Header中的Data資料:

off64_t dataOffset = localHdrOffset + 
                     kLFHLen + 
                     get2LE(lfhBuf + kLFHNameLen) +

Data的偏移是透過Header的起始偏移+Header的大小(固定值)+Extra data的大小+檔名的大小,如下圖

enter image description here

回頭看一下,java在獲取Data偏移的處理,在讀取Extra data的長度的時候,它已經預存了檔名在FileHeader中的長度。

// We don't know the entry data's start position. 
// All we have is the position of the entry's local 
// header. At position 28 we find the length of the 
// extra data. In some cases this length differs 
// from the one coming in the central header. 

RAFStream rafstrm = new RAFStream(raf, 
         entry.mLocalHeaderRelOffset + 28); 
DataInputStream is = new DataInputStream(rafstrm); 
int localExtraLenOrWhatever = 

漏洞就在這裡產生了,如果Local Header中的FileNameLength被設成一個大數,並且FileName的資料包含原來的資料,File Header中的FileNameLength長度不變,那麼底層C++執行和上層Java執行就是不一樣的流程。

C++ Header 64k Name Data 
+--------> +----------------------> +----------> 
length=64k classes.dex dex\035\A... dex\035\B... 
+--------> +---------> +----------> 

如上面所示,底層C++的執行會讀取64k的FileName長度,而Java層由於是讀取File Header中的資料,FileName的長度依舊是11,於是Java層校驗簽名透過,底層執行會執行惡意程式碼。

本文章來源於烏雲知識庫,此映象為了方便大家學習研究,文章版權歸烏雲知識庫!

相關文章