關於 Incapsula Redis 安全性報告的澄清
原文由 Salvatore Sanfilippo 于 發布,訂閱此部落格
幾天前,我一早打開 Twitter,就看到動態牆上充斥著像「75% 的 Redis 伺服器遭到惡意軟體感染」這類標題的文章。這顯然是斷章取義的引述,真正指的是 Incapsula 的一項研究,他們發現那些在網際網路上完全開放、毫無防護、直接在公開 IP 位址上監聽的 Redis 執行個體中,有 75% 已經遭到感染 [1]。
[1] https://www.incapsula.com/blog/report-75-of-open-redis-servers-are-infected.html
許多人其實不需要對這件事多做解釋,因為只要對電腦安全和 Redis 的運作方式有基本的掌握,就能輕鬆理解整件事的脈絡。不過我寫這篇部落格文章有兩個原因。最直接的原因是,它能幫助媒體和其他對安全或 Redis 不太熟悉的使用者了解究竟發生了什麼事。第二個原因是,那些暴露在外的 Redis 執行個體,正好是一個關於「安全預設值」的案例研究,應該會讓資安圈的人覺得很有意思。
Incapsula 報告
先從 Incapsula 的報告說起。他們所做的,是分析在網際網路上暴露的 Redis 執行個體。這些執行個體在網際網路上的任何地方、任何人都能存取,因為它們在公開 IP 位址上監聽連線,而且沒有任何密碼保護。感覺就像是 HTTP 伺服器一樣,只是這次是 Redis,而 Redis 本來就不是設計來這樣直接暴露在外的。
這早已不是什麼新鮮事。由於 Redis 非常受歡迎,整體安裝數量極為龐大,其中有一小部分就是這樣被暴露在外。基本上從一開始就是如此。人們在某個雲端服務商上開一台虛擬機器,安裝 Redis,發現無法連上,就把虛擬機器的連接埠對所有人開放,於是這個執行個體就此在毫無防護的狀態下運行。唯一不同的是,過去這些執行個體就算暴露了,多半也都相安無事。頂多就是某個腳本小子連進來執行個「INFO」或幾個指令看看裡面有什麼,多半也就這樣而已。如今,新的加密貨幣挖礦熱潮給了攻擊者一個非常好的理由去入侵別人的系統。老實說,是最好的理由:為了錢。因此,那些同樣暴露的執行個體現在被入侵,用來安裝某種挖礦軟體以挖掘加密貨幣。Incapsula 檢查的就是看起來已遭入侵的執行個體所佔的比例。
他們收集針對 Redis 執行個體的攻擊手法的方式,是刻意運行一組暴露的執行個體,來監控攻擊者會如何對其下手。許多攻擊看起來都是我自己當初示範的攻擊範例 [2] 的變形。
[2] http://antirez.com/news/96
另外值得注意的是,要掃描整個 IPv4 位址空間來找出任何一種服務的暴露執行個體,是再簡單不過的事。舉例來說,你可以用 masscan。不過,早在 20 年前用我自己寫的 hping 就能做到,而且我記得當年確實也成功做過。
TLDR:網際網路上之所以有開放的 Redis 執行個體,是因為它們設定錯誤。攻擊者透過安裝某種挖礦軟體或其他方式從中獲利。但還有更多內幕……請繼續往下看。
保護模式
在我看來,資安是個很糟的領域。我曾在這個領域工作過,一旦它不再是地下圈子的事,我就決定離開。人們對資安議題總是意見一大堆,但同時,卻很少有人真正關心去對現實世界的系統進行安全稽核(順帶一提,Google Project Zero 正在稍稍改變這一點)。所以,在第 N 次收到用 PGP 加密、說 Redis 存在暫存檔建立攻擊弱點的電子郵件後,我寫了 [2] 那篇部落格文章,只是想說:「或許你們搞錯了 Redis 安全性的重點」。我基本上公布了一個針對我自己系統的攻擊手法,那是我花幾分鐘研究就找到的,而且非常嚴重。想傳達的訊息還是一樣:Redis 不是設計來直接暴露在外的。如果你真的想玩那套 hack3rzzz 的把戲,其實有更有趣的事情可以做。然而這樣做的結果,卻是把一個強大的武器交到了腳本小子們手上,讓他們得以入侵散布各地的數千個開放 Redis 執行個體。
為了彌補我的過錯、試圖贖罪,我開始思考能做些什麼來讓情況變得簡單一點。Redis 3.x 的一個問題是,預設會監聽所有的 IP 位址,所以很容易設定錯誤:只要把連接埠開放,或是完全沒有防火牆,執行個體就暴露了。當時資安圈的口頭禪是「採用安全的預設值」。也就是說,以 Redis 這種網路快取來說,預設組態基本上開箱即用就對大多數實際使用者行不通。更糟的是,完全不懂的人只會用「bind *」來解決問題,結果又回到最初的狀況。所以我試著找一個聰明一點的做法,做出了一個我稱之為「保護模式」的功能,讓一群 386 處理器在我家門前抗議了好幾天,大失所望。
保護模式的想法是,仍然監聽所有位址,但如果連線不是來自本機,伺服器就會回傳一個錯誤訊息,解釋*為什麼*它沒有如預期般運作、該怎麼做,以及如果只是傻傻地關掉保護模式、把執行個體對所有人開放會有什麼風險。以下是這個功能的運作範例:
$ nc 192.168.1.194 6379 -DENIED Redis is running in protected mode because protected mode is enabled, no bind address was specified, no authentication password is requested to clients. In this mode connections are only accepted from the loopback interface. If you want to connect from external computers to Redis you may adopt one of the following solutions: 1) Just disable protected mode sending the command 'CONFIG SET protected-mode no' from the loopback interface by connecting to Redis from the same host the server is running, however MAKE SURE Redis is not publicly accessible from internet if you do so. Use CONFIG REWRITE to make this change permanent. 2) Alternatively you can just disable the protected mode by editing the Redis configuration file, and setting the protected mode option to 'no', and then restarting the server. 3) If you started the server manually just for testing, restart it with the '--protected-mode no' option. 4) Setup a bind address or an authentication password. NOTE: You only need to do one of the above things in order for the server to start accepting connections from the outside.
這樣做的好處在於:
- 使用者知道該怎麼做才能解決這個狀況。這比單純的「connection refused」好得多。
- 我們盡量避免使用者在不了解風險的情況下就關掉保護模式,並讓他們知道還有其他解決方案(例如設定密碼或綁定特定介面)。
幻滅
所以我當時的幻想是,或許這會比安全的預設值更有幫助。但不幸的是,你無法改變這個事實:一定比例的使用者根本不在乎,有些人甚至不知道自己正在安裝 Redis,它只是作為其他東西的相依套件而附帶安裝的,或是由某個腳本安裝的,或是包含在你正在執行的 ABI 映像檔裡,諸如此類。
一開始透過在 Shodan 上搜尋,看起來 Redis 4.0 的保護模式確實幫了很大的忙!我找到的開放執行個體大多都回傳「-DENIED」。那很棒。然而在 [1] 發表之後,我請 Shodan(順帶一提,他們超棒,在 Twitter 上非常熱心幫忙)提供暴露的 Redis 執行個體的版本分布。結果顯示,仍然有大量的 Redis 4.0 執行個體暴露在外 [3]。
[3] https://asciinema.org/a/8heQvivQkFmUrisbb9FQLfMBj
的確,它們比 3.0 的執行個體少,但數量仍然很多。再深入調查後,我甚至被告知有些 VM 映像檔的安裝腳本預設就會把 Redis 的保護模式*移除*,因為有些人覺得安全功能很煩。他們希望東西開箱就能在網路上直接運作,所以就這麼做了,把這個煩人的東西拿掉。接著這樣的映像檔變得熱門,許多人就在不知情的狀況下安裝了它,而當 Redis 4 被安裝時,保護模式是關閉的,執行個體也就暴露了。
我認為這是關於「可被使用者輕易關掉的安全預設值」的一課。看起來它們在某種程度上確實有幫助,但也只是在某種程度上降低事件發生的比例,而不是讓這類事件變成罕見的例外。
接下來呢?
Redis 安全性模型的一個根本問題是,伺服器可以透過一般的 API、使用像 CONFIG 這類特殊指令來重新設定。這是 Redis 一個非常有價值的功能,但一旦有人能存取 Redis API,就會讓入侵執行個體變得簡單許多,而正常使用 Redis 的應用程式其實不需要這種層級的存取權限。
因此,在 Redis 6 的開發過程中,我們將會引入 ACL。同樣地,這個功能的引入方式會盡量讓現有使用者「無痛」升級。如果你沒有提供任何憑證就連線,Redis 會自動使用「default」使用者來登入客戶端,這個使用者可以執行應用程式通常會做的所有事情,但會拒絕所有管理類指令。當然,你也可以透過不同的設定讓它更嚴格,建立只能對符合特定模式的鍵執行特定指令的新使用者,等等。
在 Redis 6 的開發過程中,我們也計畫合併對 SSL 連線的支援,雖然這不太可能對本文討論的問題產生任何影響,因為預設情況下 Redis 仍會以未加密的方式運行,這個功能是選擇性啟用的,不過在某些環境下,SSL 也是朝更安全的 Redis 體驗邁出的一步。
不過,我的希望還是放在 ACL 上,因為看起來一般使用者不太可能讓 default 帳號能夠執行管理指令。特別是因為我們計畫讓來自本機的連線直接以「admin」使用者身分登入。如果一切如我所願,我們仍會看到暴露的 Redis 6 執行個體,因為這是無可避免的,但至少那些 Redis 6 執行個體應該會讓入侵整個系統變得更加困難。至少理論上是如此:Redis 的 EVAL 指令允許執行 Lua 腳本,而這項功能預設應該要開放,因為它是 Redis 的核心功能之一。我們試圖提供一種算是沙盒化的 Lua 執行環境,但如果你關注 IT 安全一段時間,就會知道沙盒從來都不是完美的,與其說是一種封閉的解決方案,不如說更多是在練習如何逃脫它。不過這一次,我會避免為了證明觀點而再公布一個攻擊手法。
隨機一篇部落格
留言
登入後參與討論