如何使用Ghostscript調試PostScript
我最近接觸了威脅研究領域的一位朋友,他正在研究PostScript中shellcode提取的問題。PostScript是一種由Adobe Systems開發的簡單的解釋性編程語言,具有強大的圖形功能,已集成到當今大多數現代印表機中。在過去幾年中,該軟體一直是攻擊者的目標,進行了多次臭名昭著的攻擊,包括FortiGuard Labs去年發現的利用CVE-2015-2545 Encapsulated PostScript(EPS)漏洞一項行動。攻擊者利用PostScript中發現的漏洞通常會使用編碼演算法對其進行混淆,從而使分析人員的工作更加艱難。因此,如果你是那些只具有PostScript基本知識的分析人員,那麼可能會發現靜態地理解混淆代碼很難。如果確實存在這種情況,也很平常。這就是為什麼我決定寫一篇關於如何分析PostScript的快速指南。
PostScript 101
有些讀者可能會問,如果只是想知道它的行為的話,為什麼需要靜態分析PostScript。不幸的是,一些惡意樣本在執行時不能動態工作,可能是由於各種原因,包括不同的測試環境,如不兼容的操作系統或軟體版本,損壞或截斷的文件等。那些黑盒分析是不可行的,分析人員需要能夠對惡意樣本靜態分析。在我的朋友引介下,我看到了一個shellcode,其中包含用PostScript編寫的解碼常式,如清單1所示。乍一看,我認為靜態解碼很容易,但經過幾次試錯嘗試我沒有獲得解碼的shellcode。顯然,主要原因是因為首先我不了解PostScript。所以我決定玩玩PostScript,這是我一直想嘗試的東西。
Line 1:/shellcode def
Line 2:0 1 shellcode length 1
Line 3:{
Line 4: /xor_proc exch def shellcode dup
Line 5: xor_proc get 16#29 xor
Line 6: xor_proc xor 255 and xor_proc exch put
Line 7:} for
清單1: PostScript寫的shellcode
也許在搜索查詢中沒有使用正確的關鍵字,因為在找到Ghostscript之前我找不到任何關於PostScript shellcode分析的有用文章,Ghostscript是PostScript和PDF文件的解釋器。
當我啟動Ghostscript時,我很高興看到類似於Python的互動式控制台的互動式shell提示符,這讓我覺得我可以調試遇到的shellcode。如果剛開始嘗試使用PostScript,可能會對PostScript命令的語法和順序感到困惑,如清單1所示。幸運的是,一旦將命令輸入Ghostscript的控制台,將獲得更好的視感。值得一提的是,Adobe Systems對PostScript命令進行了詳細記錄。
您可能聽說PostScript被描述為基於堆棧的語言,並想知道這意味著什麼。這是清單2中的線索:
$ gs -dNODISPLAY
GPL Ghostscript 9.18 (2015-10-05)
Copyright (C) 2015 Artifex Software,
Inc. All rights reserved.
This software comes with NO WARRANTY:
see the file PUBLIC for details.
GS>1
GS2
GSstack
2
1
GS
清單2: Ghostscript 交互shell
你明白了嗎?PostScript中的每個命令都被推送到操作數堆棧。換句話說,在PostScript中使用的最後一個命令將始終位於堆棧的頂部。如果熟悉逆向工程,我相信這個概念對你來說很熟悉。Ghostscript也足夠用戶友好,通過「」括弧之間的數字來顯示堆棧中的命令/元素數量。當想要清除堆棧時,可以使用clear命令,根據Adobe的術語,它也稱為堆棧運算符。
$ gs -dNODISPLAY
GPL Ghostscript 9.18 (2015-10-05) Copyright (C) 2015 Artifex Software,
Inc. All rights reserved.
This software comes with NO WARRANTY:
see the file PUBLIC for details.
GS>1
GS2
GSstack
2
1
GSclear
GS>
清單3: Ghostscript堆棧操作符 - clear
之所以提到這一點,是因為在開始調試之前確保堆棧clear是很重要的,確保您不會弄亂要調試的內容。
PostScript Debugging 101
接下來我們準備查看清單4中顯示的shellcode:
GS>/shellcode
def
GS>stack
GS>
清單4: 將文本名稱定義為變數
可以看出,在Ghostscript中輸入的命令定義一個名為shellcode的變數。當名稱附加正斜杠「/」並以def運算符結束時,它是一個文本名稱。雖然括弧「」中的數據被定義為十六進位字元串,但在此示例中,我們使用虛擬shellcode位元組進行演示,因為我們獲取的實際shellcode數據大約為10千位元組。不幸的是,Ghostscript很難解析大數據,可能是由於內存溢出。
GS>shellcode ==(AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA)GS>shellcode =AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGS>shellcode printAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
清單5: 檢查變數值的運算符
基本上,如果我們將清單5中的命令分開,它們可以被讀作
1.在操作數堆棧中推送shellcode值
2.Pop(==)來自操作數堆棧的值
值得一提的是「==」運算符類似於「=」,但它擴展了數組的值,如清單5所示。
到目前為止,我們已經討論了如何解釋PostScript命令,以及一些有助於調試PostScript的運算符。我們現在可以進入清單1中的第2行。它是"for循環"控制結構的開始。本質上,for循環控制結構的語法類似於「初始 增量 限制 for」:
GS>0 1 shellcode length 1
GSstack
1
48
1
清單6: for loop控制結構
當我們分解清單6中的命令時,可以解釋為:
1.將0推送到操作數堆棧,這是初始值
2.將1推送到操作數堆棧,這是增量值
3.將shellcode對象推送到操作數堆棧。將length運算符(數組/字元串對象的特殊運算符)推送到操作數堆棧。它從前一個操作數獲取一個參數,並返回元素數,即限制值。
4.將1推到操作數堆棧
GS/xor_proc exch def shellcode dup
GSstack
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
48
1
GSxor_proc ==
1
GS
清單7: 清單1中的第4行命令
清單7中的命令可以如下定義:
1./xor_proc exch def =>定義變數xor_proc並將變數推送到操作數堆棧。然後它交換堆棧頂部兩個元素的位置(xor_proc, 1, 48, 1, 0) - >(1, xor_proc, 48, 1, 0)。最後,def運算符獲取堆棧頂部的值,將其分配給xor_proc,然後從堆棧中刪除這兩個值
2.將shellcode對象推送到操作數堆棧
3.複製shellcode對象
第5行和第6行,如清單1所示,包含實際的解碼命令。執行第5行的命令後,會產生如下結果:
GSxor_proc get 16#29 xor
GSstack
104
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
48
1
清單8: 清單1中第五行的命令
1.將xor_proc值推送到操作數堆棧,該堆棧用作shellcode數組對象的索引
2.將GET運算符(數組/字元串對象的特殊運算符)推送到操作數堆棧,以獲取前一個操作數堆棧中指定的索引值。結果,shellcode [1]的值將被推送到操作數堆棧
3.將十六進位0x29壓入操作數堆棧,然後將其用作XOR密鑰。請注意,可以通過在數字前使用#標籤符號來定義整數的基數(例如:16#表示十六進位數字,8#表示八進位數字)
4.將XOR運算符推送到操作數堆棧,從前兩個操作數堆棧中獲取值進行算術運算。結果為104(0x29 ^ 0x41)
如果我們擴展第6行中的命令,可以看到解碼常式執行另一個XOR操作,該操作獲取數組的當前索引XOR在清單8中的堆棧頂部中的值:
GSxor_proc stack
1
104
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
48
1
GSxor stack
105
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
48
1
GS255 stack
255
105
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
48
1
GSand stack
105
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
48
1
GSxor_proc stack
1
105
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
48
1
GSexch stack
105
1
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
48
1
GSput stack
48
1
GSshellcode ==
(AiAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA)
清單9: 情單1中第6行的命令
最後,如清單9所示,PUT運算符從堆棧中取出三個值(105,1,shellcode),並將第一個參數中指定的元素(105)存儲到第二個參數中指定的數組/字元串對象(shellcode)的索引(1)。
通過分析,我們可以在Python腳本中演示清單1中的PostScript解碼常式的更高層次表示,它包含兩輪XOR操作,一個靜態密鑰0x29和一個動態密鑰(它是以下字元串的索引):
#!/bin/python
shellcode_fn ="postcript_shellcode.bin"
buff = open(shellcode_fn,"rb").read()
buff_len = len(buff)
shellcode_defn ="postscript_shellcode_de.bin"
out = open(shellcode_defn, "wb")
for i in range(1, buff_len):
[3 spaces indentation]res =ord(buff[i]) ^ 0x29
[3 spaces indentation]res = (res ^ i)& 0xff
out.write(chr(res))
out.close()
總結
希望這篇對PostScript的介紹以及一些常見的PostScript操作符在調試PostScript時會有所幫助。也希望這可以讓你了解在將來遇到時如何分析PostScript shellcode。最後一點,由於學習解碼PostScript的原因,我能夠通過檢測從shellcode下載的有效載荷來幫助我的朋友。
IOC
Payload URLs:
[1] hxxps://tpddata[.]com/skins/skin-8.thm
[2] https://tpddata[.]com/skins/skin-6.thm
Payloads detections:
skin-8.thm - 4838f85499e3c68415010d4f19e83e2c9e3f2302290138abe79c380754f97324
(W32/Manuscrypt.BA!tr)
skin-6.thm - c10363059c57c52501c01f85e3bb43533ccc639f0ea57f43bae5736a8e7a9bc8
(W64/Manuscrypt.BA!tr)
※美國政府最新技術警報:警惕朝鮮黑客組織Hidden Cobra正在使用的兩款RAT和蠕蟲病毒
※黑客無處不在:美國軍用收割者無人機文件在暗網泄露
TAG:嘶吼RoarTalk |