Flask send_file函數導致的絕對路徑遍歷
平時接觸到的 python 項目并不多,對 python 的代碼審計更是沒有接觸,偶然朋友發來了一個漏洞 Flask send_file函數導致的絕對路徑遍歷 ,感覺打開了新世界的大門,于是就以一個初學者的角度,進行復現分析一下。詳情也可以根據 Python : Flask Path Traversal Vulnerability 進行分析學習
send_file 的妙用
在以 flask 框架開發的系統中,為了直接實現用戶訪問某一個 URL 時就可以下載到文件,我們就使用 send_file 來實現
from flask import Flaskfrom flask import send_file
app = Flask(__name__)
@app.route('/download')def downloadFile(): path = "test.txt" return send_file(path)
if __name__ == '__main__': app.run()

我們看到 如此運行的效果是直接返回了文件的內容,瀏覽器并沒有識別成一個文件下載下來。
要想讓瀏覽器識別成為文件下載的話,只需要加上as_attachment=True
from flask import Flaskfrom flask import send_file
app = Flask(__name__)
@app.route('/download')def downloadFile(): path ="test.txt" return send_file(path, as_attachment=True)
if__name__=='__main__': app.run()

當下載的文件名是中文時
from flask import Flaskfrom flask import send_file
app = Flask(__name__)
@app.route('/download')def downloadFile(): path ="測試.txt" return send_file(path, as_attachment=True)
if__name__=='__main__': app.run()

Content-Disposition:
Content-Disposition
在常規的 HTTP 應答中,Content-Disposition 響應頭指示回復的內容該以何種形式展示,是以內聯的形式(即網頁或者頁面的一部分),還是以附件的形式下載并保存到本地。其可以是inline(默認值,所以可以不指定)或者是attachment,attachment表示附件,瀏覽器看到這個值一般會彈出一個保持文件的確認框,或者像chrome直接下載。


漏洞分析
漏洞的觸發是在 send_file 中,我們跟進看一下
flask.helpers.send_file

繼續跟進查看
werkzeug.utils.send_file

我們在本地構造一個簡單的語句進行嘗試
>>>import os.path>>> _root_path ="path/to/mySafeStaticDir">>> path_or_file ="/../../../../../../../etc/passwd">>> os.path.join(_root_path,path_or_file)'/../../../../../../../etc/passwd'

我們發現 os.path.join 使用不受信任的輸入調用時不安全的。當 os.path.join 調用遇到絕對路徑時,它會忽略在該點之前遇到的所有參數并開始使用新的絕對路徑。當參數可控時,我們控制惡意參數輸入絕對路徑,os.path.join 會完全忽略靜態目錄。所以,當 os.path.join 來獲取來自 flask.send_file 的不受信任的輸入時,可能會目錄遍歷攻擊。
漏洞復現
我們在本地構造簡單的代碼進行測試,獲取從外部傳入的參數 filename
from flask import Flask, requestfrom flask import send_file
app = Flask(__name__)
@app.route('/download')def downloadFile(): filename = request.args.get('filename') return send_file(filename, as_attachment=True)
if__name__=='__main__': app.run()
通過控制 filename 為絕對路徑,就實現了目錄穿越漏洞



總結反思
這個漏洞非常的有趣,漏洞的修復是可以使用flask.safe_join加入不受信任的路徑或用flask.send_file調用替換flask.send_from_directory調用。
這個漏洞雖然很簡單,但是在 github 上很多用 python 開發的項目都用了這個函數,如果不加以修復,會造成很大的危害。