2017年6月12日 星期一

Wannacry病毒深度技術分析(三)-漏洞利用篇



如果說EternalBlue開了Windows的後門
那DoublePulsar就是從這後門侵門踏戶的真正攻擊者
因此網路上有些關於這漏洞的分析文章會把他們搞在一起
畢竟Wannacry已經把這兩階段的攻擊合併了
沒見過原本EternalBlue和DoublePulsar組合攻擊的人很難分辨出來

承上篇,我們要繼續分析Wannacry感染其他電腦的第二階段
其實只是因為EternalBlue+DoublePulsar的內容加起來太長
我只好把他們拆成兩篇...
這篇同樣也需要對EternalBlue漏洞的背景知識有所理解

MS-17 010漏洞分析-DoublePulsar

EternalBlue已經先在對方電腦種下了後門

現在需要有人來跟這個後門接應
在NSA的Exploit之中,擔任這角色的就是DoublePulsar

解析DoublePulsar發送的指令和回應

DoublePulsar會先對目標機器發送正常的SMB2封包
如Negotiate Protocol Request、Session Setup for anonymous等正常封包
下圖的來源(192.168.1.1)是攻擊者電腦
目標(192.168.1.2)是受害者電腦


接著DoublePulsar會發送含有特殊暗號指令(op code)的封包
他「借用了SMB2 Protocol裡的Timeout欄位來塞這個特殊暗號指令


我們可以從微軟的文件中得知
原本Timout欄位是用來檢查SMB在進行SESSION_SETUP的時候該Session有沒有過期的
超過時間就需要重新認證,預設是45秒
但從上圖你可以看到DoublePulsar塞了一個1個小時半的Timeout進去
在之後的封包裡,DoublePulsar也持續不斷發出SESSION_SETUP封包


從上圖可以看到受害者的電腦不斷回應出原本在SMB Protocol被定義為「Not Implemented」的封包
知道這API已經被hook過並種下後門的我們,很快就可以看出這裡是有玄機的
這個原本應該是「Not Implemented」的回應已經被後門函式給取代了
為了動態追蹤Doublepulsar在這裡作了什麼,我們仍然可以用Windbg來觀察他
因為我們已經知道他一定會執行的地方了
就是被EternalBlue種下後門的函式
所以我們可以直接根據上一個畫面的hook函式入口處來添加中斷點:
bp fffffa80`00c79060 
後門函式裡有一段程式碼是用來解碼Timeout欄位的值的


經過此程式碼計算的al,後門函式會比較這個值
看對方是否在對他下達指令
DoublePulsar指令種類

op code
指令
描述
0x23
Ping
測試受害者電腦是否被植入後門函式
0xC8
Exec
要求受害者電腦執行任意程式碼
0x77
Uninstall
反安裝後門函式

EternalBlue+DoublePulsar原本是美國國安局手上用來監視目標的Exploit
反安裝令自有其必要性

hook函式的判斷程式碼如下圖:

我們以前面封包的Timeout值0xfbcb5d00為範例
來計算DoublePulsar送的指令是如何經過hook後門函式計算的

xor    eax,eax   ;eax = 0x000000, ecx = 0x0020ac57
mov    al,cl     ;eax = 0x000000, ecx = 0xfbcb5d00
shr    ecx,8     ;eax = 0x000000, ecx = 0x00fbcb5d
add    al,cl     ;eax = 0x00005d, ecx = 0x00fbcb5d
shr    ecx,9     ;eax = 0x00005d, ecx = 0x0000fbcb
add    al,cl     ;eax = 0x000028, ecx = 0x0000fbcb
shr    ecx,8     ;eax = 0x000028, ecx = 0x000000fb
add    al,cl     ;eax = 0x000023, ecx = 0x000000fb
所以我們可以知道DoublePulsar下了一個Ping指令給hook後門函式
其實就人類直觀來看的話
把這幾個byte加起來並去掉溢位就可以算出指令了

後門函式在收到這個「暗號」時,是如何回應的呢?
後門函式同樣借用了「Signature」和「Multiplex ID」欄位來回應DoublePulsar的封包
Signature是只有收到Ping指令才有意義的
代表加密過的4-byte XOR key和目標平台種類 (後面會提到這個key的用途)
Multiplex ID加上狀態碼就是後門函式對DoublePulsar的回應
意義如下表:
後門函式回應種類

狀態碼
Multiplex ID 值
回應類型
描述
65
一般SMB封包的回應值
代表此電腦尚未被hook後門函式
66
0x10
81 (65+16)
Pong
收到Ping指令的回應
82 (66+16)
Exec
完成DoublePulsar的指令
0x20
98 (66+32)
Exec
參數不合法
0x30
113 (65+48)
Pong
分配記憶體失敗
114 (66+48)
Exec

下圖是一個回應封包的例子:


我們可以看到Singnature為 0x01afd666
其後的byte如果是0x01,及代表目標系統是x64平台
Multiplex ID是81
代表此封包為收到DoublePulsar ping指令的回應

0x01afd666是加密後的4-byte XOR key
計算公式如下:
def calculate_doublepulsar_xor_key(s):
    x = (2 * s ^ (((s & 0xff00 | (s << 16)) << 8) | (((s >> 16) | s & 0xff0000) >> 8)))
    x = x & 0xffffffff  # this line was added just to truncate to 32 bits
    return x
如果s代入0x01afd666
算出來的4-byte XOR key則為0x658903cd
這把key在加密封包的時候會用到

加密封包

受害者的後門函式收到0xC8指令時
代表要執行發送者的任意攻擊指令,例如注入DLL等
DoublePulsar在執行指令前用一個加密算法將要傳送的TCP封包內容加密
他會拿Ping封包裡回應的Signature欄位值當作4-byte XOR key來加密
加密後的封包如下圖


我們把紅框的部分用4-byte XOR key作XOR計算
即可得到解密後的封包
以下是XOR運算的python原始碼
src = 'encrypted_file'
dst = 'decrypt_file'

def xor(data, key):
    l = len(key)
    return bytearray((
        (data[i] ^ key[i % l]) for i in range(0,len(data))
    ))

data = bytearray(open(src, 'rb').read())
key = bytearray([0xcd, 0x03, 0x89, 0x65])
decode = xor(data, key)
f = open(dst, 'wb')
f.write(decode)
上述紅框處的封包即可解密如下:


看到解密後的內容
我們一眼便知這是預備要注入的DLL的標頭內容

其實反組譯DoublePulsar我們也可以發現他的加密算法:


解密算法的程式碼則如下圖所示:


注入DLL的過程

DoublePulsar對後門函式下達0xC8指令後
除了傳送按照命令接收DLL檔之外
接收完畢後、後門函式還會執行傳送過來的shellcode
Shellcode執行的內容如下:

1. 定位ntoskrnl.exe的Base address
透過Export函式表找出自己需要的的函式位址

這步驟和EternalBlue定位ntoskrnl.exe的過程幾乎是完全一樣的,我們就不再贅述
差別只在於找的函式不同
DoublePulsar尋找的函式為: (照搜尋順序排列)
ExAllocatePool
KeGetcurrentThread KeInitializeApc KeInsertQueueApc KeStackAttatchProcess KeUnstackDetachProcess
PsGetCurrentProcess
PsGetProcessImageFileName
PsGetProcessPeb PsGetThreadTeb
ObfDereferenceObject
PsLookupProcessByProcessId
ZwAllocateVirtualMemory

在這其中以Apc (Asynchronous Procedure Calls) 相關的函式最為重要
Apc系列函式是注入DLL的關鍵呼叫
下圖是DoublePulsar搜尋到KeInsertQueueApc時所擷取的圖


附帶一提,PsGetProcessImageFileName、KeStackAttatchProcess和KeUnstackDetachProcess都是Undocument API
2. 呼叫PsGetProcessImageFileName尋找目標系統上指定的行程是否存在
不管是DoublePulsar或是利用他的Wannacry
他們都是搜尋系統行程lsass.exe
不過為了demo方便,所以我把它改成了calc.exe
下圖就是PsGetProcessImageFileName找到calc.exe當下的截圖


話說在一般Inject DLL的流程
通常這部分的API用的是ZwSystemInformation而不是這函式

3. 找到指定的行程後
DoublePulsar呼叫PsLookupProcessByProcessId
取得行程的EPROCESS


4. 有了EPROCESS

DoublePulsar便可依此內容Attach到行程的記憶體地址空間中
他是呼叫KeStackAttatchProcess來實現的


5. 於此同時DoublePulsar也需要該行程的PEB (Process Environment Block)

所以呼叫PsGetProcessPeb來取得他


6. 從EPROCESS->ThreadListHead取得ETHREAD後

DoublePulsar以此為參數呼叫PsGetThreadTeb得到TEB (Thread Enrivonment Block)


7. 接著DoublePulsar在目標行程的記憶體空間內分配記憶體區塊

這區塊是為了之後要寫入的DLL而準備的


8. 同樣的另一個準備工作

DoublePulsar呼叫ExAllocatePool來分配記憶體給APC Object


9. DoublePulsar使用在第6步得到的TEB作為參數來初始化APC object

最後DoublePulsar呼叫了KeInsertQueueApc
把APC插入到執行緒的APC隊列(APC Queue)
APC的內容當然就是載入DoublePulsar/Wannacry指定的DLL了


注入結束後DoublePulsar會稍等一段時間等系統取出APC來執行

執行APC成功後,剩下的只是收尾的工作
其實上述這些步驟就是一般Hacker利用APC來注入DLL常見的手法 

注入DLL成功後
我們可以在受害者電腦的行程裡發現被注入的DLL區塊


試著把這個記憶體位置dump出來,它的確是個DLL檔案
說明了DLL被注入到此處


我把上面描述的內容過程作成影片了,方便大家直接看攻擊效果
讀者可以參考看看
影片裡攻擊端用的NSA的Exploit平台-FuzzBunch
注入的DLL是用Metasploit作的
為了避免用Windbg反復Attach/Deattach系統行程lsass.exe讓系統不穩定
所以我把目標改成calc.exe


利用MS17-010的Wannacry

我們在前面一再提到Wannacry利用了EternalBlue感染其他電腦
這裡我們從上一篇提到的MS17-010Attack開始
逐步列出它所利用的部分及程式碼


同前面EternalBlue描述的
Wannacry先送幾個正常的SMB封包 (EternalBlueAttack_TreeConnect)以取得TreeID
接著檢查對方是否已經被EternalBlue埋藏後門 (IsDoublePulsarPresent)
沒有的話就發起EternalBlue攻擊 (EternalBlueAttack_PoolSpray)
已經被埋後門的話,就注入DLL (RunPayloadOnTarget)

這裡我們簡單看一下這整串流程內部的程式碼


Send SMB TreeConnect的過程比較單純
Wannacry直接把封包標頭都內嵌在檔案裡了,直接拿出來用
如果Wannacry發現TreeConnect失敗的話
這時沒有TreeID,不能自己發起攻擊了怎麼辦?
於是它乾脆孤注一擲,直接偵測受害者電腦是否已被埋藏後門
有的話則照樣注入DLL
再來就是看重點IsDoublePulsarPresent了


看到紅框內的部分,有沒有覺得似曾相識?
我們再拿出https://github.com/countercept/doublepulsar-detection-script/blob/master/detect_doublepulsar_smb.py 的部分內容來看



最後是我寫的偵測程式顯示的內容:



之前的文章就有提過,我本來就是借用countercept的code來寫偵測程式的

也就是說,Wannacry、countercept和我用的檢測程式碼是完全一樣的
這也是我的偵測程式為何有效的原因

IsDoublePulsarPresent其實就是對目標機器發送前面介紹的「Ping」指令封包
如果目標機器的回應中,MID是0x51而不是平常的0x41/0x42
那說明了此機器已經被埋藏了後門
對Wannacry來說那便可能代表已經在此機器進行加密了

回頭再看Wannacry裡呼叫IsDoublePulsarPresent的方式
你會發現它的Uninstall參數都是1,意即發現被種後門就把它移除
其實Wannacry當然沒那麼安好心眼
它發現受害者的電腦已經先被別人的Eternablue + DoublePulsar種過後門了
所以先反安裝它,再重新注入自己的DLL進去
這是一種黑吃黑的概念


上圖是EternalBlueAttack_PoolSpray的片段
它上方還有一段While(1)迴圈
在滿足條件前,Wannacry在這裡不斷配置0x10000大小的封包送給受害者以消耗對方的NonPagedPool
以觸發Pool Spray造成前面所述的記憶體拷貝越界

最後的RunPayloadOnTarget程式碼如下圖:


看過IsDoublePulsarPresent的我們會發現它們的程式碼片段怎麼這麼像
事實上,除了最後那段注入DLL的程式碼外
他們倆個函式是一模一樣的

ComputeDoublePulsarArch就只是檢查Platform資訊、x64就是1
ComputeDoublePulsarXorKey則是跟前面反組譯DoublePulsar差不多的結果:


除了隨機數是從外面傳入外,其他加密算法Wannacry和DoublePulsar均相同

最後InjectDLL的部分呼叫了InjectDLLViaDoublePulsar
首先根據Platform資訊決定注入哪種DLL:


接著以剛才ComputeDoublePulsarXorKey算出的XorKey來加密封包並送給受害者電腦


這裡可以看到Wannycry每送4096 bytes就會檢查MID是不是預期的82
其實DoublePulsar的行為也是如此

至於加密封包的程式碼如下圖所示:


我們可以從第18行看出每4個byte的資料就用4-byte XOR key加密了起來

漏洞歷史

最後我們來說說這漏洞的歷史
這次WannaCry利用的這個EternalBlue + DoublePulsar漏洞
已經埋了有10年以上都沒被外人發現過
唯一發現的駭客團體是
美國NSA的方程式組織(Equation Group)
他們把這漏洞保留了起來從未公開
也寫了控制程式來鑽目標的漏洞、這些目標自然是NSA的監控目標了
只要知道對方的IP,就能神不知鬼不覺的操控/監視對方電腦
雖然他們還發現其他未公開的漏洞
但光這EternalBlue漏洞就能讓NSA幾乎無往不利了

然而這時
有另一個駭客團體入侵了美國NSA
他們叫做「Shadow Brokers
旗下的方程式組織也遭受池魚之殃
(據說也可能是內鬼)
並盜走了他們大量的入侵工具
當時Shadow Brokers還拍賣過NSA的機密文件
但因為索價實在太高 (100萬比特幣)
一直到今年(2017年)年初都乏人問津
失望透頂的Shadow Brokers
終於在4月初,把這些入侵工具和機密文件公開了大部分出來
其中就有這個EternlaBlue漏洞

漏洞一公開之後,所有的駭客團體和安全研究團體可樂了
大家孜孜不倦地投入於研究這些漏洞中
想盡辦法用在對自己最有利的用途上
其中最知名的,就是我們這次系列文的Wannacry了

Wannacry分析系列文:
Wannacry病毒深度技術分析(一)-傳播篇
Wannacry病毒深度技術分析(二)-系統漏洞篇
Wannacry病毒深度技術分析(三)-漏洞利用篇
Wannacry病毒深度技術分析(四)-佈局篇
Wannacry病毒深度技術分析(五)-加密篇
Wannacry病毒深度技術分析(六)-勒索篇