張謙:“Docker逃逸與防護策略” – 運維派
由工業和資訊化部指導,中國資訊通訊研究院主辦,業界知名組織雲端計算開源產業聯盟(OSCAR)承辦的2017全球雲端計算開源大會於4月19日-20日在北京國家會議中心順利召開。本文為本屆大會嘉賓分享的大會演講速記內容,敬請瀏覽。
嘉賓介紹:張謙
公司職務:奇虎360科技有限公司安全研究員
大會演講速記
各位來賓,大家下午好!我是來自奇虎360運營安全研究部的安全研究員張謙。
我想先在這兒介紹一下我們團隊,大家關注虛擬化產品的安全研究上面可能會對我們團隊稍微熟悉一點。我們團隊成立於2015年年底,主要的研究方向是虛擬化產品的安全漏洞,我們團隊先後在虛擬化產品,比如說Docker、VMware,從2015年底到現在一共發現了幾十枚安全漏洞,都通報給了CVD,都有相應的編碼。
說起我們團隊在今年3月份全球頂級的黑客大會上,加了一個逃逸的比賽專案,因為這個比賽是持續三天,最後一天比賽上我們團隊貢獻了一枚VMware的Docker逃逸,我們最後一天搞了一個什麼專案?可以通過一個網頁,攻擊者可以通過網頁完成本地拿我們的VMwareDocker逃避直接到宿主機。
安全研究的思路以攻促防,以我們安全研究的攻擊思路和這些年的技術積累,幫助360在旗下的安全產品上提供一些技術上其他團隊沒有辦法預約的技術壁壘,把這些相應的攻防經驗融入到360旗下的安全產品裡面。
剛才前幾位嘉賓都講解了他們公司也好或者他們公司產品的組織架構,我本身是一個技術出身的,所以我今天帶來的乾貨是一些跟技術關聯比較大的東西。因為我們團隊在2016年上半年完成了Docker逃逸,從Docker容器逃逸到整個Docker的物理機裡邊。今天我就給大家講講Docker逃逸與防護策略。
Docker可能在座各位應該在雲安全行業裡面也是不陌生的這麼一個東西,也是前一陣炒得比較火,Docker本身是開源專案,是用Go語言寫的開源專案,是一個非常輕量化的解決方案,實際上它的基礎就是Linux(LXC),讓使用者感覺處在真實一個虛擬機器裡面一樣。
這是一個對比圖,一個傳統的虛擬機器還有Docker容器帶來的虛擬化,可以看上面,傳統虛擬機器需要虛擬出硬體裝置,也就是需要虛擬處一個作業系統出來。
Docker實際上利用Docker容器和真正的物理機,實際上用的都是一個作業系統的核心,也就是說它在資源消耗上少了一個HostOS本身需要消耗很多資源,相比傳統的虛擬機器來說這是它的優點。
這是從數量級上面的對比,Docker在啟動的時候實際上就跟Linux的程序一樣,啟動的是時候秒級,虛擬機器至少需要幾十秒時間,這是它的硬碟的消耗。
假設我們現在有這麼一種應用場景,比方說一臺16核32G記憶體的主機上面需要跑幾百個應用,每個應用上面是一個網站,虛擬機器需要至少做到兩點,一種是資源隔離,這個虛擬機器的操作不能影響到其他虛擬機器,首先假如說我們在這樣一臺基礎上開500個虛擬機器,本身虛擬化所帶來的消耗是非常嚴重的,但是如果用Docker,Docker本身就相當於是一個Linux上的程序,開500個程序在這樣一臺配置的主機上絕對沒有什麼問題的。
接下來給大家講講Docker的一些核心技術,實際上Docker本身因為本身沒有做完全的虛擬化,很多東西用的都是裡面支援的一些核心特性。比如說第一個是一個名稱空間,Docker本身提供了6個名稱空間,這個名稱空間有沒有用?Docker容器在不同的名稱空間裡邊是不能影響其他容器的。
Docker一共提供了6種NameSpace,就是主機名使用者名稱@之後有一個主機名,Docker就可以就把這個主機隔離開,有一個訊號量,還有程序編號,在初始化之後有一個引進程序,在Docker容器裡面編號啟動之後全都是從E開始的。還有網路裝置,也是全隔離的。還有檔案系統,你在Docker容器裡做檔案操作是不能影響其他容器的。User和Mount是隔離使用者組,每個使用者在容器裡面在裡面的許可權是不一樣的。
這是系統呼叫引數,從名字上來看,可以看clone,有UTS對應,IPC對應,PID也是對應,唯一不同的是Mount,實際上Mount的歷史實際上是第一個支援的NameSpace,當然了核心維護者沒有想到之後還會出來12345,所以它在實現Mount的時候就直接就是NameSpace。
這裡有一些技術,就是掃程式碼,剛才前幾位都講產品,我給大家換換腦子。在NameSpace裡面有一個描述符,就叫做nsproxy,這個count是引用基數,還有Mount檔案系統的NameSpace,還有PID,剛才說有6個,唯一缺少的是User的NameSpace,它所屬於哪個UserNameSpace,實際上所有東西都是用在這幾個資料結構裡面。
同時在Linux下程式設計比較多,會用到clone的函式,這裡需要這麼幾個引數,其中的flags引數就是剛才說的clone裡面需要什麼NameSpace就把相應的值傳到裡面。接下來我們從原始碼上分析一下clone系統呼叫是怎樣把各種NameSpace創建出來,在Docker裡面怎麼把核心裡面把容器創造出來。
這是函式呼叫站的一個圖,首先是使用者傳到clone,平時咱們用Linux程式設計的時候用到clone,fork的話全都是0,傳到do-fork,複製到dup,在所有資料結構做複製,拷貝程序的證書,程序證書很有意思,如果這個cloneflags從一開始傳遞過來,會建立一個新的NameSpace。
這個函式很有意思,在Docker容器裡面,一進去之後,一看咱們在裡面已經入駐的選項,這裡面有幾個Full-SET,把證書的許可權全都是Full-SET,把所有許可權全都付給Docker容器,實際上Linux的許可權劃分一共有38個,38個每個全景都是一個位,所有的許可權通過64位的數全部都能儲存下來,實際上這個就是64位無符號整形的數,把所有的許可權全都付給全部的許可權。證書也屬於UserNameSpace,就屬於從上一步傳過來的這個。
NameSpace創立完了之後就該拷貝其他的五個NameSpace了,接下來執行,執行之後這幾個新的NameSpace也調相應的函式,把新建立的NameSpace傳到裡面,也就是說這幾個創建出來的新的,比如UTS、IPC都是新創建出來的。
剛剛講完NameSpace,實際上就是把資源做一個資源隔離。咱們舉一個非常簡單的例子,就是以主機名稱做非常簡單的例子來看核心是怎麼實現資源隔離的。
這個NameSpace就是主機的NameSpace一共有這麼幾個成員,其中附名字的這塊就是Name,登入了名字之後就儲存在Name裡面,這是相應的資料結構。這邊是現在有一個設定主機名稱,這邊進來之後會先檢查當前的程序是否有許可權修改UTS所屬的User裡面是否有這個許可權,在建立程序的時候實際上已經看到本身給程序附加上所有的Full-SET。接下來檢查程度,把使用者傳過來的HostName拷貝到這個結構裡。
我們來看登入之後,核心怎麼取?就調UTSName,把這個Name取下來,把Name直接拷貝到使用者空間,這個Name就是從使用者開始傳過來的,這是它的長度,把HostName拷貝到使用者空間裡面。實際上它怎麼做到資源隔離呢?實際上把不同的變數存到不同的NameSpace裡面,每次設定的時候,都會從當前的程序裡面的NameSpace裡面去取。
這個許可權檢查,能看到剛才有一個許可權檢查,檢查當前UTS NameSpace所屬的UserNameSpace是否有許可權,再往深一步怎麼做?
首先把當前程序的證書以及需要在哪個NameSpace裡面有傳到這個函式裡面,有三個引數,一個是當前程序的證書,還有目標,我需要在這個NameSpace,要去檢查剛才說的64位,看這裡面有沒有許可權,如果說沒有,就返回許可權不足,基本上在Linux裡面每一次做許可權檢查的時候都會調核心裡面的這個函式,比如你進入一個你不該進入的目錄,它實際上也會調它。
下一步假如說你當前並不是在同一個NameSpace裡邊,這邊有一個init-User-ns,一個是普通賬戶,登入之後普通賬戶要鎖在NameSpace裡面,也是這個init-User-NameSpace,它所有的許可權都是滿的,普通使用者這邊是0,這邊檢查一下當前程序的User-NameSpace是不是附類,如果當前程序建立是目的User NameSpace的附類它擁有全部的許可權。
這是整個跟NameSpace相關的系統呼叫,剛才unshare用得非常頻繁,它的作用是不需要啟動新程序就可以達到NameSpace隔離的效果。這個setes是已經存在的NameSpace,通過兩個Docker符號,當前進入PID,在檔案系統裡面建立自己的目錄,目錄名字就是以程序PID來命名的,可以看出來上面有這些號,這些號就會在這裡面去用。
下面講Docker的ControlGroups,是做系統資源,比如CIO或者網路的流量控制,可以把資源做切片,比現在Control Groups可以對容器的記憶體CPU做一些限制。
我覺得這個容器我不可能給它附100%的CPU資源,我就可以通過CGroups來限制它,像CPU,還有塊裝置,還有網路的,還有記憶體的,這是CGroups的術語,這是典型的CGroups用檔案系統來實現的一個和核心互動的東西,我們可以設定CGroups,像寫一個檔案一樣,比方我在這裡邊新建一個CGroups,就是vsec。我可以限制裡面有一些系統引數,我可以設定直接寫比如說5萬,寫到這個檔案裡邊,比如說cpu.cfs-quota-us,核心處理的時候就會直接限制CPU使用的百分比。
下面是一個例子,我可以寫這樣一個指令碼,這個指令碼就是寫的一個死迴圈,本身不做Control Groups是佔用100%的CPU。
接下來通過CGroups直接寫到這個檔案裡邊的話,再把PID6814寫到tasks檔案裡面就可以直接變成50%,寫到這個檔案裡面,它是sys檔案系統,實際上還是在核心呼叫還是虛擬檔案系統能直接找到這個檔案相當於函式,在這個裡面把程序的PID加到列表裡邊,當程序使用CPU資源的時候就會從這5萬里邊去限制CPU應該管理員希望佔用的百分比。
AUFS時間關係,先略過去,實際上相比Linux啟動的時候,就好像Docker在啟動進項的過程,它的過程就好像Linux啟動核心初始化的時候用的步驟,實際上是一樣的。時間關係,我把這塊先略過去。
說幾個安全策略方面的東西。
比如DockerSwarm有一個叢集管理配置問題,配置失誤在哪?DockerSwarm會監聽一個埠,2375,官方配置繫結的IP是0.0.0.0,假如你要這麼配置,隨便一個人都會遠端訪問Docker容器。這是DockerSwarm叢集的圖,下面從Docker文章裡面出來,繫結的IP地址就是0.0.0.0,任何人都可以在網上直接接入。
這是Linux許可權模型,大家可以看到Docker本身在啟動的時候會禁用一些許可權,實際上有的許可權即使不禁用,Docker的使用者在容器裡面也是沒有辦法用的,為什麼?
舉個例子,就比方說我想往核心裡邊加一個核心模組,這個模組會調這麼一個函式,把這個許可權能否加模組的許可權傳到裡面,又會去掉它,這個東西就會檢查把目的的init-User-ns是否有CAP-SYS這個許可權。
Docker所屬的User-ns和init-User-ns不一樣,首先這一塊條件就是不滿足的,因為本身它倆並不是在同一個NameSpace裡邊。這邊要求在這個NameSpace裡邊,在init-User-ns裡邊,到了這一步之後直接返回許可權不足。
咱們想一想也能知道,Docker肯定是不允許你在容器裡面,即使在容器裡面是入許可權也不可能載入一個模組進去,載入一個模組就相當於你這樣一個許可權了,你有這麼大的許可權,你可以在整個物理機上面做任何你想幹的事,這肯定是不被允許的,即使Docker把這個許可權附給程序了,程序也是沒有辦法用的。
這是DockerLinux另外一個安全特性,它可以限制容器裡面所調動的系統API,這個東西需要看核心是否支援這個選項,現在預設全都是支援這個許可權,全都是預設啟用的。
這是Docker限制44個系統呼叫,比如說其中有一個設定系統實時鍵,這個肯定會被禁用,為什麼?
因為Docker本身沒有關係時間的NameSpace,沒有時間做容器的隔離,要是這個容器把系統時間改了,肯定是影響其他容器的。
這是Docker的Seccomp的系統檔案,是一個白名單,在核心裡面以什麼樣的方式來實現的,時間關係,這邊也得跳過去。
最後我給大家展示一下我們團隊在研究演示Docker逃逸的演示。
文章來自微信公眾號:雲端計算開源產業聯盟