“毒液”漏洞使全球數以百萬計的虛擬機處于網絡攻擊風險之中,嚴重威脅各大云服務提供商的數據安全,對此360安全中心發布了深入分析“毒液”漏洞原理的技術報告,并建議廣大廠商盡快在源碼層面對QEMU實現補丁升級。
漏洞簡介
CrowdStrike的Jason Geffner發現開源計算機仿真器QEMU中存在一個和虛擬軟盤控制器相關的安全漏洞,代號VENOM,CVE編號為CVE-2015-3456。利用此漏洞攻擊者可以在有問題的虛擬機中進行逃逸,并且可以在宿主機中獲得代碼執行的權限。更多詳情見作者博客[1]
背景知識
此漏洞位于qemu的虛擬軟驅控制器的模擬代碼中。下面介紹幾個關于軟驅的幾個重要的地方。
· 控制寄存器
軟驅控制器是由9個寄存器進行控制的,這些寄存器可以通過端口0x3f0-0x3f7進行訪問(0x3f6除外[2])。軟驅控制器寄存器的定義如下:
漏洞相關的寄存器是DATA_FIFO。
· MSR
同時軟驅控制器的MSR標記位表明當時軟驅控制器的狀態。此次漏洞相關的MSR標記位的定義如下表:
· FIFO命令
命令是向DATA_FIFO寫入的一個小于32的單字節的值,每個命令后面都要跟著一些指定長度的參數。命令的ID定義如下所示:
更多見OSDev wiki上的關于軟驅控制器的文章
漏洞分析
1.POC觸發不了
國外一名安全研究者Marcus Meissner發布了此漏洞poc如下:
我們可以看到,都是向DATA_FIFO端口寫入數據,筆者拿到poc先在自己的機器上測試發現poc并不能觸發,先不管原因,我們先來分析下qemu對FIFO命令的處理。
2.FIFO命令處理流程
經過分析,我們可以得出其流程如下:首先qemu將FIFO的處理函數以及命令對應的參數個數等信息存放在一個表中,如下所示:
表的每一項都定義了相應命令的一些信息,這里我們將被一項稱為一個Handler下同。當qemu接收到FIFO命令之后,通過命令的ID找到這個命令的Handler,然后再根據這個Handler中保存的參數的個數來繼續接收參數。并將命令ID和參數放在一個buffer中。在接受完參數后調用相應的處理函數。整個FIFO寫操作派發流程都是在函數fdctrl_write_data里。
在處理函數中如果有返回的數據??刂破髂M代碼則調用fdctrl_set_fifo這個函數來設置MSR的狀態為FD_MSR_DIO,已表示控制器處在可被讀狀態。注意:設置完以后控制器是不可讀的見fdctrl_write_data開始的那個檢查。fdctrl_set_fifo代碼如下:
如果沒有要返回的數據或者返回的數據已經被客戶機通過IN指令讀取完了,則會調用fdctrl_reset_fifo來重置FIFO。即將FIFO置為可寫狀態。fdctrl_reset_fifo:
3.為什么不能觸發
通過以上流程的分析,我們再來看Marcus Meissner公布的poc的流程:
首先發送一個id為0xa的控制命令。我們可以看到id為0xa的命令為FD_CMD_READ_ID,其對應的處理函數為fdctrl_handle_readid,參數個數為1個。
1. { FD_CMD_READ_ID, 0xbf, “READ ID”, 1, fdctrl_handle_readid },
之后又會寫入一個0x42作為READ_ID命令的參數。接下來進入到fdctrl_handle_readid函數內。經過筆者調試fdctrl_handle_readid這個函數啟動了一個定時器。在定時器被觸發的時候程序調用了fdctrl_set_fifo來生成返回數據。所以接下來的向FIFO寫0x42的操作完全沒有用,被fdctrl_write_data開始的fdctrl->msr &FD_MSR_DIO這個檢查給攔下了。所以這個poc在筆者的機器上并不能觸發。
4.新的觸發方式
我們先來看下補丁的代碼:
可以看到基本上就是補了一些對fdctrl->fifo這個buffer下標的一些防止越界的操作。我們可以肯定這個肯定是個寫越界操作了。按照這個思路我們查看了所有命令的處理函數,發現FD_CMD_DRIVE_SPECIFICATION_COMMAND的處理函數有問題。先看下FD_CMD_DRIVE_SPECIFICATION_COMMAND命令的Handler,如下:
1. { FD_CMD_DRIVE_SPECIFICATION_COMMAND, 0xff, “DRIVE SPECIFICATION COMMAND”, 5, fdctrl_handle_drive_specification_command }
命令處理函數為fdctrl_handle_drive_specification_command,參數個數為5。再來看下fdctrl_handle_drive_specification_command函數的實現:
我們找到fdctrl->data_len > 7這個判斷是有問題的。我們從fdctrl_write_data這個函數開始,首先傳進命令字節FD_CMD_DRIVE_SPECIFICATION_COMMAND,然后依次傳進5個參數。按照fdctrl_write_data的流程進入處理函數fdctrl_handle_drive_specification_command時fdctrl->data_len 應該是6,所以我們讓fdctrl_handle_drive_specification_command的第一個判斷里fdctrl->fifo[fdctrl->data_pos 1]是我們可控的再加上下面的這個fdctrl->data_len > 7這個判斷也為否,就繞過了所有調用fdctrl_set_fifo和fdctrl_reset_fifo的地方就是控制器的狀態還是可寫,而且buffer沒有被清空。然后我們就可以無限次向fdctrl->fifo里寫入數據,從而超出fdctrl->fifo的邊界造成越界寫。
fdctrl->fifo的初始化是在fdctrl_realize_common里面:
5.可重現POC
6.重現成功
linux guest:
windows guest:
漏洞總結
這個漏洞為典型的堆溢出漏洞,其表現形式為越界寫操作。此漏洞的利用可能還是很大的。另外即使虛擬機沒有設置軟驅,其漏洞還是無法避免的。鑒于該漏洞屬于高危漏洞,建議盡快在源碼層面上對QEMU實現補丁升級。
[1]此漏洞原作者博客 http://venom.crowdstrike.com/
[2] IO端口0x3F6是ATA(硬盤)備用狀態寄存器,并且不使用任何軟盤控制器。來源財經新聞網