作者:
小餅仔
·
2016/01/27 17:03
原文:Rails Dynamic Render to RCE (CVE-2016-0752)
0x00 概述
如果你的應用中使用了動態渲染路徑 ( dynamic render paths
) ,如渲染params[:id]
,透過本地檔案包含(local file inclusion
),可能會導致遠端程式碼執行。可以透過更新到Rails的最新版本,或重構你的controllers來修復漏洞。
文章主要介紹了在特定的場景下,Ruby on Rails
框架的一個缺陷導致攻擊者能夠遠端執行程式碼。
在 Rails controllers
的設計中,會根據被呼叫的方法,隱式的渲染對應的 view
檔案。例如,當呼叫 controller
的 show
方法時,如果程式碼中沒有明確指定要渲染的 view file
,框架就會隱式的渲染 show.html.erb
檔案。
然而在很多情況下,開發者會根據請求格式,如 text, JSON, XML
來明確渲染的內容。此時, view file
是一個模板語言檔案,如 ERB, HAML
等。Rails
框架中有多個方法能夠用於影響和改變 view
內容。本文中重點關注渲染方法(render method
)。Rails
的文件中給出了多種呼叫渲染方法的方式,包括使用 file:
選項來明確指定要渲染的 view file
路徑。
如果你閱讀了該方法的文件,你會對功能的說明感到迷惑。讓我們來看一段程式碼:
def show
render params[:template]
end
第一眼看,這段程式碼非常簡單,該 controller action
的主要功能為渲染 template
引數指定 view 模板。但Rails如何查詢指定的模板呢?在 views
目錄下查詢? 在應用的 root 目錄下查詢?或是在其他位置?;該引數的值是一個模板的名稱?是一個有特定字尾的檔名?或是一個完整的檔案路徑?這裡有許許多多的疑問,我們需要檢視實現的細節才能夠得到答案。
0x01 細節說明
框架的渲染機制(render mechanism
)是一個在單一函式內試圖完成太多功能的例子,這也是導致遠端程式碼執行的原因所在。
我們假定渲染機制的預期行為是渲染在 app/views/user/#{params[:template]}
檔案,該假設看起來似乎是合理的。如果 template
引數的值為 dashboard
,那麼就會去載入 app/views/user/dashboard.{ext}
檔案,其中 .ext
是任意允許的字尾型別,如 .html, .haml, .html.erb
等
假設使用者給出的 template
引數值為 ../admin/dashboard
。 程式執行的結果會是什麼? 如下圖所示,執行後,程式丟擲了缺少模板錯誤(missing template error
)
透過分析報錯資訊,我們可以知道,框架試圖在多個路徑下查詢要渲染的 view file
,包括 RAILS_ROOT/app/views
,RAILS_ROOT
和 檔案系統的根目錄。
從檔案系統的根目錄載入並渲染檔案的行為是非常危險的。如果我們將 /etc/passwd
作為 template
引數的值傳入,並且能夠讀取到 passwd
檔案的內容。那麼將會是一個很嚴重的問題。
如果我們能夠讀取 passwd
檔案的內容,那麼也可以讀取應用程式的原始碼和配置檔案,如 config/initializers/secrettoken.rb
檔案。
導致該問題的原因是,在應用中使用了動態渲染路徑(dynamic render paths
)
def show
render params[:template]
end
這個簡單的程式碼的例子證明了攻擊者能夠讀取我們的原始碼和應用程式配置檔案。不幸的是,這不是最壞的結果。
如 Jeff Jarmo
在他的文章 The Anatomy of a Rails Vulnerability – CVE-2014-0130: From Directory Traversal to Shell 中描述的,我們能夠利用這個漏洞獲得一個shell。Jeff
的文章中描述了 在某些版本的Rails的 隱式渲染機制的一個相似漏洞,能夠允許目錄遍歷(directory traversal
),更精確的說,本地檔案包含(local file inclusion
)。本文中重點關注顯示渲染(explicit rendering
),這是一個由框架開發者所引入的漏洞。
在進入細節分析前,這裡將該漏洞的分類歸為檔案包含而不是目錄遍歷,原因是我們將檔案作為程式碼(ERB
)進行載入、解釋並執行。從傳統意義上來說,目錄遍歷漏洞返回的是不可執行的(non-executable
)的內容,如 CSV
檔案。因此從本質來說,我們不僅僅能夠讀取應用程式的原始碼和其他可讀的系統檔案,還能夠執行 Ruby
程式碼。既然我們能夠執行 Ruby
程式碼,我們也能夠在 web server
上執行系統級別(system-level
)的命令。
在從檔案包含到得到 shell
的過程中,用到了一種關鍵的技術,稱為 日誌檔案汙染 (log file tainting
)。Rails
在執行過程中,會將請求資訊記錄到日誌檔案中,包括請求引數,如 development.log
。日誌檔案是純文字格式,能夠包含 Ruby
程式碼。日誌檔案汙染可以透過向 web
應用程式傳送一個惡意的請求,請求的引數中包含有效的 Ruby
程式碼來實現。
在下面的例子中,我們向web
應用程式發起了一個合法的請求,但在 URL
中帶上了包含惡意的、經過 URL
編碼的引數 。
透過檢視日誌檔案,我們可以看到,日誌中請求引數是以 hash
鍵值對的方式儲存,並進行了 URL解碼。這是有效的 Ruby
程式碼,能夠在檔案被渲染時執行。
因此,我們能夠利用檔案包含漏洞來嘗試載入日誌檔案,執行包含的 Ruby
程式碼。
當日志檔案返回時,我們能夠看到引數的 hash
值。之前包含我們 payload
的部分已經被 ls
命令的執行結果所替換。到這裡,我們已經能夠以 web-server
對應使用者許可權來執行系統命令了。
0x02 修復方法 & 緩解措施
安裝 Rails
特定版本的 補丁
如果還沒有安裝 patch
,可以考慮禁止渲染白名單之外的檔案。具體方法為,在 action
中定義允許的檔名白名單 hash
,對使用者的引數進行校驗,確保引數在檔案白名單 hash
中。這種方法需要在應用程式中所有使用到動態渲染路徑的地方都增加校驗。
def show
template = params[:id]
valid_templates = {
"dashboard" => "dashboard",
"profile" => "profile",
"deals" => "deals"
}
if valid_templates.include?(template)
render " #{valid_templates[template]}"
else
# throw exception or 404
end
end
另一種類似的方法為校驗給定的檔案是否在特定的目錄下存在。
def show
template = params[:id]
d = Dir["myfolder/*.erb"]
if d.include?("myfolder/#{template}.erb")
render "myfolder/#{template}"
else
# throw exception or 404
end
end
此外,我們可以使用 Rails
靜態分析工具 Brakeman來掃描應用程式。Brakeman
檢測報告會給出使用了動態渲染路徑的 controllers
,可以根據此來分析哪些 controllers
會有遠端程式碼執行的風險。
0x03 時間軸
- 2015年2月1日漏洞被報告給
Rails Team
- 2015年2月10日
Rails Team
同意修復漏洞
Patch
在2015年7月內部驗證透過。-- 距離報告日期超過5個月
Patch
在2016年1月25日釋出,漏洞編號 CVE-2016-0752,-- 距離驗證透過超過6個月,距離報告日期將近一年
0x04 結論
Rails
的渲染機制是一個比較神秘的功能,如果沒有深入挖掘實現細節很難弄明白。與 CVE-2014-0130
類似,使用動態渲染路徑會導致目錄遍歷和程式碼執行。在評估多個流行的 Rails
開源專案的過程中,我已經看過許多由框架開發者所引入的類似漏洞。
如果你還沒有閱讀過 Jeff Jarmoc
的文章,我建議你去閱讀以下。他深入了研究了 CVE-2014-0130
的漏洞細節和風險點。本文中的許多內容與他的文章都很相似,事實上,這兩個是非常相似的漏洞。
我已經寫了 Metasploit
的 POC模組,用於檢測和攻擊 web
應用程式的遠端打碼執行漏洞。
已經有白帽子在三個白帽上搭建起了相應的漏洞環境,小夥伴們感興趣的來測試下吧,有效期一個小時:
http://b4bfc94e13431d5d4.jie.sangebaimao.com
本文章來源於烏雲知識庫,此映象為了方便大家學習研究,文章版權歸烏雲知識庫!