天天看點

PDF檔案解析與PDF惡代分析中的一些坑

一、簡介

最近在做文檔類的惡代檢測,寫個總結。

本篇文章負責介紹pdf文檔的格式以及惡代分析中需要注意的問題以及相應工具推薦。希望能給各位做惡代分析時提供一些幫助。

後序會更新一些其他文檔格式解析與惡代分析内容等,歡迎各位關注。

PDF檔案解析與PDF惡代分析中的一些坑

二、PDF檔案格式介紹

PDF(便攜式檔案格式,Portable Document Format)是由Adobe Systems于1993年基于檔案交換所發展出的一種檔案格式。Adobe公司素有“漏洞之王”的美譽,是以學習PDF檔案格式對研究分析漏洞具有極大幫助。PDF格式較為複雜,本文以研究漏洞的目的分析PDF格式,探尋如何找出并分析PDF中存在的惡意代碼,而并非做一個詳細的PDF parser解析器,是以會省略對不相關關鍵字的介紹,請各位留意。

PDF的結構可以從檔案結構和邏輯結構兩個方面來了解。PDF的檔案結構指的是其檔案實體組織方式,邏輯結構則指的是其内容的邏輯組織方式。

1. PDF的檔案結構

PDF檔案格式包含以下4個部分:

檔案頭——指明了該檔案所遵從的PDF規範的版本号,它出現在PDF檔案的第一行。

檔案體——又稱對象集合,PDF檔案的主要部分,由一系列對象組成。

交叉引用表——對對象進行随機存取而設立的一個間接對象的位址索引表。(實際以偏移+索引的方式儲存對象位址,下文會提及)

檔案尾——聲明了交叉引用表的位址,即指明了檔案體的根對象(Catalog),進而能夠找到PDF檔案中各個對象體的位置,達到随機通路。另外還儲存了PDF檔案的加密等安全資訊。

2. PDF檔案格式圖示:

PDF檔案解析與PDF惡代分析中的一些坑

3. PDF檔案的邏輯結構

本段主要介紹PDF檔案體的讀取方式。

作為一種結構化的檔案格式,一個PDF文檔是由一些稱為“對象”的子產品組成的。每個對象都有數字标号,這樣的話可以這些對象就可以被其他的對象所引用。這些對象不需要按照順序出現在PDF文檔裡面,出現的順序可以是任意的,比如一個PDF檔案有3頁,第3頁可以出現在第1頁以前,對象按照順序出現唯一的好處就是能夠增加檔案的可讀性,對象的資訊以偏移+索引的形式儲存在交叉引用表内。

檔案尾說明了根對象的對象号,并且說明交叉引用表的位置,通過對交叉引用表的查詢可以找到目錄對象(Catalog)。這個目錄對象是該PDF文檔的根對象,包含PDF文檔的大綱(outline)和頁面組對象(pages)引用。大綱對象是指PDF檔案的書簽樹;頁面組對象(pages)包含該檔案的頁面數,各個頁面對象(page)的對象号。

4. PDF的層級結構圖示:

PDF檔案解析與PDF惡代分析中的一些坑

頁面(page)對象為PDF中最重要的對象,包含如何顯示該頁面的資訊,例如使用的字型,包含的内容(文字,圖檔等),頁面的大小。裡面的資訊可以直接給出,當然裡面的子項更多的是對其他對象的引用,真正的資訊存放在其他對象裡面。頁面中包含的資訊是包含在一個稱為流(stream)的對象裡,這個流的長度(位元組數)必須直接給出或指向另外一個對象(包含一個整數值,表明這個流的長度)。

可見stream流對象我們惡代分析需要擷取的重點。

5. 頁面資訊圖示:

PDF檔案解析與PDF惡代分析中的一些坑

了解了上面的内容之後,我們可以得出針對惡代分析的PDF檔案的大緻解析思路:

PDF檔案解析與PDF惡代分析中的一些坑

當然,也可以采取針對PDF層級結構的文檔解析方式,見仁見智,因人而異。

三、以二進制文本解析Pdf文檔結構

PDF檔案是一種文本和二進制混排的格式,但是Adobe更願意讓人把它當成二進制的檔案,是以,PDF檔案可以直接拖入16進制編輯器中打開。前面我們介紹了PDF的檔案結構以及邏輯結構,現在我們在16進制編輯器中打開PDF檔案,更直白的展示PDF的關鍵字段以及檔案結構。

%PDF-1.6 #檔案頭+版本号,16進制讀取檔案0x25 0x50 0x44 0x46開頭即證明是pdf檔案 

%çóÏÓ    #下面就是很多的Object對象 

2 0 obj  #Object對象,其中2是Obj順序号,0是Obj的版本号,obj也是對象開始的标志 

<<       #<<>>之間為Object對象的字典内容,包含關鍵字 

[/ICCBased 3 0 R] 

>> 

Endobj   #Object結束關鍵字 

7 0 obj 

<< 

/Filter 

/FlateDecode   #流對象的壓縮方式為/FlateDecode   

/Length 148    #流對象的長度 

Stream         #流對象 

#檔案内容資訊,注:此處為直覺進而手動填寫的 

Endstream      #流對象結束标志 

Endobj 

8 0 obj 

/Contents 7 0 R              #頁面内容對象的對象号為7 

/MediaBox [0 0 595.2 841.68] #頁面顯示大小,以像素為機關 

/PageIndex 1 

/Parent 1 0 R               #其父對象号為1以及Pages對象 

/Resources                  #該頁包含的資源 

<</Font <</F4 4 0 R >>      #字型的類型 

/Shading <<>> 

/XObject <<>>               #外部對象 

/ColorSpace <</CS1 2 0 R>>  

/Type /Page 

1 0 obj 

/Count 1        #頁碼數量為1 

/Kids [8 0 R ]  #kids對象說明它的子頁對象為8 

/Type /Pages 

13 0 obj 

/Author (? Cryin') 

/CreationDate (D:20100926145832+08'00') 

/Title (? PDF檔案格式分析) 

endobj 

Xref           #表示交叉引用表開始 

0 14           #0表明引用表描述的對象編号從0開始,8說明共有8個對象#此行在交叉引用表中可出現多個 

0000000000 65536 f #一般pdf都是以這行開始交叉引用表的,起始位址0和産生号 

0000003195 00000 n #表示對象1,就是catalog,3195為偏移位址n表示對象在使用 

0000000018 00000 n 

0000000051 00000 n 

0000003464 00000 n 

0000000000 00000 f 

0000004282 00000 n 

0000002728 00000 n 

0000002992 00000 n 

0000003256 00000 n 

0000003892 00000 n 

0000003620 00000 n 

0000008660 00000 n 

0000008712 00000 n 

Trailer      #說明檔案尾對象開始 

<</Size 14   #14說明PDF檔案對象數目 

/Root 12 0 R #說明跟對象号為12 

/Info 13 0 R>> 

startxref 

8980     #8980為交叉引用表的偏移位址,此處為十進制表示 

%%EOF    #檔案結束标志 

對于對象的額外解釋:如果一個樣本檔案的交叉引用表格式如下

xref 

0 5 

0000000000 00000 n #第1行 

0000004996 00000 n #第2行 

0000000022 00000 n #第3行 

0000005101 00000 n #第4行 

0000004976 00000 n #第5行 

0000004996 00000 n #第n行 

4 0 obj 

Xxxxx 

即交叉引用表中第五行順序數為4的對象,其偏移為4976

四、Pdf檔案混淆

如圖,下面的樣本進行了混淆

%PDF-1.5 

<</#54#79P#65 R 0 5 

O#70e#6e#41c#74i#6fn 3 Pages C#61ta#6c#6f#67>> 

解釋:<<>>代表obj對象之間的字典内容,儲存了流的關鍵字和特征資訊,是以去除混淆是必要的第一步操作,pdf檔案的混淆隻出現在這裡#54代表0x54,上面的内容去除混淆之後即為

<</TyPe R 0 5 OpenAction 3 Pages 

Catalog>> 

五、關鍵字

下面介紹了PDF檔案解析時所需要的關鍵字

obj            #obj對象開始 

endobj         #obj對象結束 

stream         #stream流對象開始 

endstream      #stream流對象結束 

xref           #交叉引用表開始 

trailer        #檔案尾對象開始 

startxref      #交叉引用表結束 

/Page        #檔案頁數 

/Encrypt       #是否加密 

/ObjStm        #objectstreams的數量,objectstreams可包含其他Object對象,即嵌套 

/JS            #代表javascript嵌有JavaScript代碼,可直接提取惡意代碼 

/JavaScript    #代表javascript嵌有JavaScript代碼,可直接提取惡意代碼 

/AA            #以下三個為特定特征,打開對象自動執行 

/OpenAction 

/AcroForm 

/URI           #内嵌url連結 

/Filter        #/Filter字段出現,表示了下面的stream流進行了加密 

/RichMedia     #富文本 

/Launch        #執行Action的次數與OpenAction字段關聯 

#/xxxx 帶斜杠的關鍵字包含在<<>>字典内部 

六、流的提取

/Filter關鍵字之後儲存了stream流的編碼資訊一共包括以下幾種:

/FlateDecode 

/ASCIIHexDecode 

/ASCII85Decode 

/LZWDecode 

/DCTDecode 

/RunLengthDecode 

/CCITTFaxDecode 

/JBIG2Decode 

/JPXDecode 

/Crypt 

一共包括上面幾種編碼方式,按常見順序進行了排序,可以級聯編碼。例如:

0 0 obj 

<</Filter 

[/FlateDecode /ASCIIHexDecode] 

/Length 14278>> 

表示流先經過了ASCIIHexDecode再經過了FlateDecode編碼解密是即先對流進行FlateDecode解碼再對流進行ASCIIHexDecode解碼目前遇到2種級聯編碼樣本(如上),可能會有更多級聯編碼方式(3級或以上)解碼後能夠觸發攻擊的流對象為javascript腳本或者圖檔對象,常見的惡意攻擊代碼儲存在javascript腳本中。

下面的圖檔是提取自樣本中的PDF steam流檔案中的js腳本,已經很明顯是攻擊代碼了:

PDF檔案解析與PDF惡代分析中的一些坑

七、一些坑

PDF的惡意攻擊樣本毫無疑問會使用一些特殊手段對抗殺軟的掃描檢查,下面統計了一下惡意樣本常見的規避行為:

1. 交叉引用表

(1) 坑1 引用表偏移不正确

Xref    #表示交叉引用表開始 

0 2     #0表明引用表描述的對象編号從0開始,8說明共有8個對象 

8980  #8980為交叉引用表的偏移位址,此處為十進制表示 

%%EOF #檔案結束标志 

上面有提到過交叉引用表的偏移位址為固定數值,推測adobe的parser是從檔案尾開始解析,獲得交叉引用表的偏移位址(Xref中X在文檔中所在的位置即為偏移位址),找到交叉引用表再定位到各個對象,實際測試發現偏移位址可以不正确8980偏移位址實際可能為任意位址。

(2) 坑2 引用表可以有多個

0 4 

0000000000 65535 f 

0000000000 65536 n 

0000039095 00000 n 

0000000015 00000 n 

trailer 

<</ID 

[<386e381fac5d8245e24ee620741d0d06><b15c4bebcdae6f2210f09460b841e7a3>]/Root 

26 0 R/Size 28/Info 27 0 R>> 

39630 

%%EOF 

<</Filter/FlateDecode/Length 

6960/Subtype/Type1C>> 

Stream 

Ddd 

endstream 

20 1 

0000040341 00000 n 

26 4 

0000040380 00000 n 

0000040484 00000 n 

0000040677 00000 n 

0000040734 00000 n 

55 2 

0000172790 00000 n 

0000172925 00000 n 

<</Root 26 0 R/Info 27 0 

R/ID[<386E381FAC5D8245E24EE620741D0D06><39FE58436C8CC909F538F88909F1EE55>]/Size 

63/Prev 39630>> 

173446 

樣本如上,正常來講,一個文檔隻存在一個%EOF結束符,但是這個樣本裡出現了兩個

2. 字元串長度

(1) 坑1 流對象長度可以直接跟對象

正常一個字典語句中/Length之後的數值代表stream~endstream兩個關鍵字之間流的長度,如下

<</Filter/FlateDecode/Length 6960/Subtype/Type1C>> 

stream 

但測試發現流的長度可以是obj對象

2 0 obj 

<< /Length 4 0 R /Filter 

/FlateDecode >> 

#對應的obj對象中包含的長度如下 

4880 

是以stream流對象壓縮前的實際長度為4880雖然是PDF格式的正規使用方法,但同時也是規避殺軟的一種手法。

(2) 坑2 流對象長度可以為任意值

同理,正常流對象長度為上圖,實際測試發現樣本

16 0 obj 

/Length ANIWAY_____LEN 

WTF is ANIWAY_LEN??? 長度可以為填ascii字元???是以,/Length後面可以不跟數值stream流的實際長度實際==關鍵字endstream偏移-關鍵字stream偏移-包含的0x0D或0x0A

3. 解碼問題

(1) 坑1 javascript可以支援文本和八進制

<< /Type /Action 

/S /JavaScript 

/JS (\145\166\141\154\050\146\165\156) 

/JS 16 0 R 

function urpl(k,sc){ 

var c = "\x75"; 

var kkc=k+c; 

var re = /MM/g; 

scsc = sc.replace(re,kc); 

return sc; 

padding_0c = "MM0c0cMM0c0c"; 

padding00="MM0000"; 

padding_41 = "MM4141"; 

var x1=0; 

var x2=0; 

var x3=0; 

有JS程式設計基礎的肯定注意到了,是以在這裡需要判斷javascript内容在對象中還是在()内亦或是否需要轉碼

(2) 坑2 編碼方式縮寫形式

<</Filter [/Fl /Fl] /Length 8331 

xœíÜYXù 

÷qÆÌ0K3ÆØRÆVdoUƒQ"[M!S(íRÓ¾0–h¥B*d•hß~5´© 

正常檔案預設/Filter關鍵字之後會出現xxdecode關鍵字表示stream流編碼方式,但測試發現樣本可以沒有xxdecode關鍵字,但同樣進行了編碼處理,如上/Fl字段即為/FlateDecode的簡寫,對應表如下:

/FlateDecode     /Fl 

/ASCIIHexDecode  /AHx 

/ASCII85Decode   /A85 

/LZWDecode       /LZW 

/RunLengthDecode /RL 

/CCITTFaxDecode  /CCF 

/JBIG2Decode #/JBIG2Decode其實和/DCTDecode解碼方式是一樣的 

/DCTDecode       /DCT 

(3) 坑3 編碼形式可以級聯

10 0 obj 

如上表示流先經過了ASCIIHex加密再進行了FlateDecode加密解碼時需要先進行FlateDecode解密之後再進行ASCIIHexDecode解密

八、常用分析工具推薦

介紹完惡代格式後,推薦一些惡代分析的基本工具

1. PdfStreamDumper

stream流解析工具

vb開源項目。工具存在一些bug,無法解析級聯編碼後的stream流,例如/Fl/Fl編碼就無法解析

2. PDFParser

c++開源項目,pdf格式解析,邏輯比較清晰,可以參考

3. ParanoiDF

python開源項目,惡代分析

此外還有一些很贊的開源項目如pyew、peepdf等等,不一一貼位址了。

4. References

PDF, let me count the way...

Cryin/PDFTear

Adobe PDF 官方文檔

Malicious Document PDF analysis in 5 steps

C#實作的PDF解析器

原文釋出時間為:2017-11-06

本文作者:red0range

本文來自雲栖社群合作夥伴51CTO,了解相關資訊可以關注51CTO。

繼續閱讀