一、簡介
最近在做文檔類的惡代檢測,寫個總結。
本篇文章負責介紹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檔案格式圖示:
3. PDF檔案的邏輯結構
本段主要介紹PDF檔案體的讀取方式。
作為一種結構化的檔案格式,一個PDF文檔是由一些稱為“對象”的子產品組成的。每個對象都有數字标号,這樣的話可以這些對象就可以被其他的對象所引用。這些對象不需要按照順序出現在PDF文檔裡面,出現的順序可以是任意的,比如一個PDF檔案有3頁,第3頁可以出現在第1頁以前,對象按照順序出現唯一的好處就是能夠增加檔案的可讀性,對象的資訊以偏移+索引的形式儲存在交叉引用表内。
檔案尾說明了根對象的對象号,并且說明交叉引用表的位置,通過對交叉引用表的查詢可以找到目錄對象(Catalog)。這個目錄對象是該PDF文檔的根對象,包含PDF文檔的大綱(outline)和頁面組對象(pages)引用。大綱對象是指PDF檔案的書簽樹;頁面組對象(pages)包含該檔案的頁面數,各個頁面對象(page)的對象号。
4. PDF的層級結構圖示:
頁面(page)對象為PDF中最重要的對象,包含如何顯示該頁面的資訊,例如使用的字型,包含的内容(文字,圖檔等),頁面的大小。裡面的資訊可以直接給出,當然裡面的子項更多的是對其他對象的引用,真正的資訊存放在其他對象裡面。頁面中包含的資訊是包含在一個稱為流(stream)的對象裡,這個流的長度(位元組數)必須直接給出或指向另外一個對象(包含一個整數值,表明這個流的長度)。
可見stream流對象我們惡代分析需要擷取的重點。
5. 頁面資訊圖示:
了解了上面的内容之後,我們可以得出針對惡代分析的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的惡意攻擊樣本毫無疑問會使用一些特殊手段對抗殺軟的掃描檢查,下面統計了一下惡意樣本常見的規避行為:
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。