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

成都創(chuàng)新互聯(lián)-專(zhuān)業(yè)網(wǎng)站定制、快速模板網(wǎng)站建設(shè)、高性?xún)r(jià)比通遼網(wǎng)站開(kāi)發(fā)、企業(yè)建站全套包干低至880元,成熟完善的模板庫(kù),直接使用。一站式通遼網(wǎng)站制作公司更省心,省錢(qián),快速模板網(wǎng)站建設(shè)找我們,業(yè)務(wù)覆蓋通遼地區(qū)。費(fèi)用合理售后完善,10余年實(shí)體公司更值得信賴(lài)。
STS(調(diào)用方)身份盜用 (接上文)
這使我們向盜用任意調(diào)用方身份的目標(biāo)更靠近了一步:我們只需要找到一個(gè)STS操作來(lái)反映攻擊者控制的文本,并將它作為其API響應(yīng)的一部分。然后,對(duì)它的請(qǐng)求進(jìn)行序列化,同時(shí)包含一個(gè)Accept: application/json標(biāo)頭,并將一個(gè)任意的GetCallerIdentityResponse XML blob放入反射型payload中。
找到一個(gè)不受字母數(shù)字字符限制的反射型參數(shù)是一件非常棘手的事情。經(jīng)過(guò)反復(fù)嘗試后,我決定以AssumeRoleWithWebIdentity操作和它的SubjectFromWebIdentityToken響應(yīng)元素作為目標(biāo)。其中,AssumeRoleWithWebIdentity用于將OpenID Connect(OIDC)供應(yīng)商簽名的JSON Web Tokens(JWT)轉(zhuǎn)換成AWS IAM身份。
使用有效簽名的JWT向該操作發(fā)送請(qǐng)求,將返回SubjectFromWebIdentityToken字段中的令牌的sub字段。
當(dāng)然,一個(gè)正常的OIDC供應(yīng)商是不會(huì)在主題字段中給帶有XML有效載荷的JWT進(jìn)行簽名的。不過(guò),攻擊者只要直接創(chuàng)建自己的OIDC身份供應(yīng)商(IdP),并將其注冊(cè)到自己的AWS賬戶(hù)上,然后就可以用自己的密鑰對(duì)任意的令牌進(jìn)行簽名了。
讓我們把這一切放在一起,就可以搞定整個(gè)攻擊過(guò)程:
創(chuàng)建一個(gè)OIDC IdP。實(shí)際上,就是生成一個(gè)RSA密鑰對(duì),創(chuàng)建一個(gè)OIDC discovery.json和key.json文檔,并將json文件托管在Web服務(wù)器上(參見(jiàn)這里,這是使用S3的設(shè)置示例)。
使用自己的AWS賬戶(hù)注冊(cè)一個(gè)OID IdP -> AWS IAM角色映射。需要注意的是,這里的AWS賬戶(hù)不需要與我們的目標(biāo)有任何關(guān)系。
現(xiàn)在,就可以使用我們的OIDP給一個(gè)JWT進(jìn)行簽名了,其中可以放入任意的GetCallerIdentityResponse,只要將其作為主題聲明的一部分即可。解碼后的示例令牌可能是這樣的:iss、azp和aud與步驟2中指定的細(xì)節(jié)是完全匹配的。其中,sub中包含我們的偽造的響應(yīng),從而將我們識(shí)別為AWS IAM賬戶(hù)arn:aws:iam::superprivileged-aws-account。
- {'iss': 'https://oidc-test-wrbvvljkzwtfpiikylvpckxgafdkxfba.s3.amazonaws.com/',
- 'azp': 'abcdef', 'aud': 'abcdef',
- 'sub': '',
- 'exp': 1595120834, 'iat': 1594207895}
我們可以使用步驟3中的(已經(jīng)簽名的)令牌和步驟2中使用的RoleArn直接向STS AssumeRoleWithWebIdentity操作發(fā)送請(qǐng)求,以測(cè)試所有設(shè)置是否正確:
- curl -H "Accept: application/json"
- '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'
如果一切按計(jì)劃進(jìn)行,STS將把令牌主題反映為其JSON編碼響應(yīng)的一部分。如上所述,Go XML解碼器將跳過(guò)GetCallerIdentityResponse對(duì)象前后的所有內(nèi)容,從而使Vault認(rèn)為這是一個(gè)有效的STS CallerIdentity響應(yīng)。
- {"AssumeRoleWithWebIdentityResponse":{"AssumeRoleWithWebIdentityResult":
- {"AssumedRoleUser":{"Arn":"arn:aws:iam::XZY::YOUR-OIDC-ROLE/web-identity-federation","AssumedRoleId":"AROATQ4R7PP5JJNLOF5P6:web-identity-federation"},
- "Audience":"abcdef","Credentials":{...},"PackedPolicySize":null,"Provider":"arn:aws:iam::242434931706:oidc-provider/oidc-test-wrbvvljkzwtfpiikylvpckxgafdkxfba.s3.amazonaws.com/",
- "SubjectFromWebIdentityToken":""},
- "ResponseMetadata":....}
最后一步是將該請(qǐng)求轉(zhuǎn)換為Vault所期望的形式(例如使用base64編碼所有所需的標(biāo)頭、url和一個(gè)空的post正文),并將其作為/v1/auth/aws/login上的登錄請(qǐng)求發(fā)送給目標(biāo)Vault服務(wù)器。此后,Vault將反序列化該請(qǐng)求,將其發(fā)送到STS,并錯(cuò)誤地解釋該響應(yīng)。如果我們偽造的GetCallerIdentityResponse中的AWS ARN/UserID在Vault服務(wù)器上具有特權(quán),我們就會(huì)得到一個(gè)有效的會(huì)話(huà)令牌,這樣,我們就可以用它來(lái)與Vault服務(wù)器交互,從而進(jìn)一步獲取更多機(jī)密信息了。
- curl -X POST "https://vault-server/v1/auth/aws/login" -d '{"role":"dev-role-iam",
- "iam_http_request_method": "POST", "iam_request_body": "encoded-body", , "iam_request_headers" :
- "encoded-headers", "iam_request_url" : "encoded-url"}'
- {"request_id":"59b09a0b-f5d5-f4c4-8ed0-af86a2c1f5d4","lease_id":"","renewable":false,"lease_duration":0,"data":null,"wrap_info":null,"warnings":["TTL
- of \"768h\" exceeded the effective max_ttl of \"500h\"; TTL value is capped
- accordingly"],"auth":{"client_token":"s.Kx3bUNw6wEc5bbkrKBiGW6WL","accessor":"TBRh0hvfd4FkYEAyFrUE3i2P","policies":["default","dev","prod"],"token_policies":["default","dev","prod"],
- "metadata":{"account_id":"242434931706","auth_type":"iam","role_id":"47faaf36-c8ab-c589-396c-2643c26e7b30"},
- "lease_duration":1800000,"renewable":true,"entity_id":"447e1efe-0fd4-aa10-3a54-52405c0c69ab","token_type":"service","orphan":true}}
我已經(jīng)編寫(xiě)了一個(gè)概念驗(yàn)證exploit,用于負(fù)責(zé)JWT的創(chuàng)建和序列化等的大部分工作。雖然OIDC供應(yīng)商的設(shè)置增加了一些復(fù)雜性,但我們?nèi)钥梢岳@過(guò)所有啟用AWS的角色的身份驗(yàn)證。這里唯一的要求是,攻擊者需要知道目標(biāo)Vault服務(wù)器中的特權(quán)AWS角色的名稱(chēng)。
那么問(wèn)題出自哪里呢?從攻擊者的角度來(lái)看,整個(gè)認(rèn)證機(jī)制看起來(lái)很機(jī)智,但容易出錯(cuò)。將HTTP請(qǐng)求轉(zhuǎn)發(fā)放入安全產(chǎn)品未經(jīng)身份驗(yàn)證的外部攻擊表面需要對(duì)實(shí)現(xiàn)和底層HTTP庫(kù)具有極強(qiáng)的信心。由于安全性取決于安全令牌服務(wù)的實(shí)現(xiàn)細(xì)節(jié),而安全令牌服務(wù)可能隨時(shí)發(fā)生變化,這會(huì)讓事情變得更加困難。例如,AWS可能會(huì)決定將STS放在負(fù)載均衡前端的后面,使用Host標(biāo)頭進(jìn)行路由決策。出現(xiàn)這種情況后,如果不對(duì)Vault代碼庫(kù)進(jìn)行相應(yīng)的修改,可能會(huì)嚴(yán)重降低這種認(rèn)證機(jī)制的安全性。
當(dāng)然,身份驗(yàn)證之所以這樣工作也是有原因的:AWS IAM沒(méi)有向其他非AWS服務(wù)證明該服務(wù)身份的直接方法。第三方服務(wù)無(wú)法輕松驗(yàn)證預(yù)簽名請(qǐng)求,并且AWS IAM沒(méi)有提供可用于實(shí)現(xiàn)基于證書(shū)的身份驗(yàn)證或JWT的標(biāo)準(zhǔn)簽名原語(yǔ)。
最后,Hashicorp通過(guò)強(qiáng)制執(zhí)行HTTP標(biāo)頭文件的允許列表、限制請(qǐng)求使用GetCallerIdentity操作以及加強(qiáng)對(duì)STS響應(yīng)的驗(yàn)證來(lái)修復(fù)了該漏洞,以期可以防止STS實(shí)現(xiàn)的意外變化或STS與Golang之間的HTTP解析器的差別所帶來(lái)的影響。
在A(yíng)WS身份驗(yàn)證模塊中發(fā)現(xiàn)這個(gè)問(wèn)題后,我決定審查其GCP的等價(jià)物。下一節(jié)將介紹Vault的GCP認(rèn)證是如何實(shí)現(xiàn)的,以及在許多配置中,一個(gè)簡(jiǎn)單的邏輯缺陷是如何導(dǎo)致認(rèn)證繞過(guò)的。
利用Vault-on-GCP的漏洞
Vault支持在谷歌云上部署的gcp認(rèn)證方法。與AWS的同類(lèi)產(chǎn)品類(lèi)似,該認(rèn)證方法支持兩種不同的認(rèn)證機(jī)制:iam和gce機(jī)制。其中,iam機(jī)制能夠支持任意服務(wù)賬戶(hù),并且可以在A(yíng)pp Engine或Cloud Functions等服務(wù)中使用,而gce只能用于對(duì)運(yùn)行在Google Compute Engine上的虛擬機(jī)進(jìn)行身份驗(yàn)證。不過(guò),它還是具有一些優(yōu)勢(shì)的:gce不僅可以根據(jù)服務(wù)帳戶(hù)身份做出身份驗(yàn)證決策,還可以根據(jù)多個(gè)VM屬性授予訪(fǎng)問(wèn)權(quán)限。例如,一個(gè)配置可以只允許特定區(qū)域(europe-west-6)的虛擬機(jī)訪(fǎng)問(wèn)某些機(jī)密信息,允許xyz-prod GCP項(xiàng)目中的所有虛擬機(jī)所有訪(fǎng)問(wèn)權(quán)限,或者使用instance-groups對(duì)訪(fǎng)問(wèn)權(quán)限做進(jìn)一步的限制。
實(shí)際上,iam和gce認(rèn)證機(jī)制都是建立在JWT之上的。一個(gè)vault客戶(hù)端如果想要進(jìn)行身份驗(yàn)證,則需要?jiǎng)?chuàng)建一個(gè)簽名令牌來(lái)證明自己的身份,并將其發(fā)送到vault服務(wù)器來(lái)獲取會(huì)話(huà)令牌。對(duì)于iam機(jī)制來(lái)說(shuō),客戶(hù)端可以直接使用其控制的服務(wù)賬戶(hù)私鑰或使用projects.serviceAccounts.signJwt IAM API方法給令牌簽名。
對(duì)于gce來(lái)說(shuō),客戶(hù)端需要在授權(quán)的GCE虛擬機(jī)上運(yùn)行。它通過(guò)向GCP元數(shù)據(jù)服務(wù)器的實(shí)例身份端點(diǎn)發(fā)送請(qǐng)求來(lái)獲取簽名令牌。與服務(wù)賬戶(hù)令牌相比,這個(gè)令牌是由谷歌官方證書(shū)進(jìn)行簽名的。除了正常的JWT聲明(sub、aud、iat、exp)外,從元數(shù)據(jù)服務(wù)器返回的令牌還包含一個(gè)特殊的compute_engine聲明,它列出了關(guān)于該實(shí)例的相關(guān)細(xì)節(jié),這些細(xì)節(jié)將作為認(rèn)證過(guò)程的一部分進(jìn)行處理。
- "google":{"compute_engine":{"instance_creation_timestamp":1594641932,"instance_id":"671398237781058X
- XXX","instance_name":"vault","project_id":"fwilhelm-testing-XXXX","project_number":950612XXXX,"zone":"europe-west3-c"}}
JWT在設(shè)計(jì)上有很多選擇的余地,這使得它的實(shí)現(xiàn)非常容易出現(xiàn)問(wèn)題(參見(jiàn)securitum的這篇博文,以了解典型問(wèn)題的相關(guān)概述),所以,我決定花一天時(shí)間來(lái)回顧Vault的令牌處理機(jī)制。
實(shí)際上,函數(shù)parseAndValidateJwt是專(zhuān)門(mén)負(fù)責(zé)處理gce和iam令牌的。
該函數(shù)首先在不驗(yàn)證簽名的情況下解析令牌,并將解碼后的令牌傳入getSigningKey helper方法:
- // Process JWT string.
- signedJwt, ok := data.GetOk("jwt")
- if !ok {
- return nil, errors.New("jwt argument is required")
- }
- // Parse 'kid' key id from headers.
- jwtVal, err := jwt.ParseSigned(signedJwt.(string))
- if err != nil {
- return nil, errwrap.Wrapf("unable to parse signed JWT: {{err}}", err)
- }
- key, err := b.getSigningKey(ctx, jwtVal, signedJwt.(string), loginInfo.Role, req.Storage)
- if err != nil {
- return nil, errwrap.Wrapf("unable to get public key for signed JWT: %v", err)
- }
其中,getSigningKey將從token標(biāo)頭中提取密鑰id聲明(kid),并試圖找到一個(gè)具有相同標(biāo)識(shí)符的google級(jí)別(google-wide)的oAuth密鑰。它雖然對(duì)GCE元數(shù)據(jù)令牌有效,但對(duì)服務(wù)賬戶(hù)簽名的令牌無(wú)效:
- func (b *GcpAuthBackend) getSigningKey(...) (interface{}, error) {
- b.Logger().Debug("Getting signing Key for JWT")
- if len(token.Headers) != 1 {
- return nil, errors.New("expected token to have exactly one header")
- }
- kid := token.Headers[0].KeyID
- b.Logger().Debug("kid found for JWT", "kid", kid)
- // Try getting Google-wide key
- k, gErr := gcputil.OAuth2RSAPublicKey(ctx, kid)
- if gErr == nil {
- b.Logger().Debug("Found Google OAuth2 provider key", "kid", kid)
- return k, nil
- }
如果這種方法失敗,Vault服務(wù)器會(huì)從提供的令牌中提取Subject(sub)聲明。對(duì)于有效的令牌,這個(gè)聲明將包含簽名服務(wù)賬戶(hù)的電子郵件地址。知道了令牌的密鑰id和主題后,Vault就能使用服務(wù)賬戶(hù)GCP API獲取用于簽名的公鑰:
- // If that failed, try to get account-specific key
- b.Logger().Debug("Unable to get Google-wide OAuth2 Key, trying service-account public key")
- saId, err := getJWTSubject(rawToken)
- if err != nil {
- return nil, err
- }
- k, saErr := gcputil.ServiceAccountPublicKey(saId, kid)
- if saErr != nil {
- return nil, errwrap.Wrapf(fmt.Sprintf("unable to get public key %q for JWT subject %q: {{err}}", kid, saId), saErr)
- }
- return k, nil
在這兩種情況下,Vault服務(wù)器現(xiàn)在都可以訪(fǎng)問(wèn)驗(yàn)證JWT簽名的公鑰了:
- // Parse claims and verify signature.
- baseClaims := &jwt.Claims{}
- customClaims := &gcputil.CustomJWTClaims{}
- if err = jwtVal.Claims(key, baseClaims, customClaims); err != nil {
- return nil, err
- }
- if err = validateBaseJWTClaims(baseClaims, loginInfo.RoleName); err != nil {
- return nil, err
- }
如果驗(yàn)證成功,Vault將填寫(xiě)loginInfo結(jié)構(gòu)體,該結(jié)構(gòu)體稍后用于授予或拒絕授予訪(fǎng)問(wèn)權(quán)限。如果令牌包含compute_engine聲明,則將其復(fù)制到logininfo.gceMetada字段中:
- loginInfo.JWTClaims = baseClaims
- if len(baseClaims.Subject) == 0 {
- return nil, errors.New("expected JWT to have non-empty 'sub' claim")
- }
- loginInfo.EmailOrId = baseClaims.Subject
- if customClaims.Google != nil && customClaims.Google.Compute != nil && len(customClaims.Google.Compute.InstanceId) > 0 {
- loginInfo.GceMetadata = customClaims.Google.Compute
- }
- if loginInfo.Role.RoleType == gceRoleType && loginInfo.GceMetadata == nil {
- return nil, errors.New("expected JWT to have claims with GCE metadata")
- }
- return loginInfo, nil
如上所述,所有這些代碼都在iam和gce auth方法之間是通用的。這里的問(wèn)題是,沒(méi)有強(qiáng)制要求該令牌是由不包含GCE compute_engine聲明的服務(wù)賬戶(hù)進(jìn)行簽名的。雖然GCE元數(shù)據(jù)令牌中的內(nèi)容是可信的,并且是由Google控制的,但服務(wù)賬戶(hù)令牌則是完全由服務(wù)賬戶(hù)的所有者控制的,因此可能包含任意的聲明。
如果我們按照gce方法的控制流程走到最后,我們將會(huì)發(fā)現(xiàn),Vault會(huì)在pathGceLogin中將loginInfo.GceMetadata作為其認(rèn)證決策的一部分,如果滿(mǎn)足下面兩個(gè)條件的話(huà):
元數(shù)據(jù)部分中描述的VM需要存在。這是使用GCE API驗(yàn)證的,并且需要攻擊者模擬處于運(yùn)行狀態(tài)的VM。實(shí)際上,只有project_id、zone和instance_name需要驗(yàn)證,并且需要設(shè)置為有效值。
JWT令牌的主題聲明中的服務(wù)帳戶(hù)必須是存在的。這是通過(guò)ServiceAccount GCP API進(jìn)行驗(yàn)證的,要求在托管服務(wù)帳戶(hù)的項(xiàng)目中擁有am.ServiceAccounts.Get權(quán)限。由于攻擊者可以在自己的項(xiàng)目中使用服務(wù)帳戶(hù),所以只需將這個(gè)權(quán)限授予Vault GCP身份,甚至是allUsers即可。
最后,調(diào)用AuthorizeGCE來(lái)授予或拒絕訪(fǎng)問(wèn)權(quán)限。如果攻擊者使用正確的屬性(項(xiàng)目、標(biāo)簽、區(qū)域等)冒充的GCE實(shí)例一切正常,攻擊者將得到一個(gè)有效的會(huì)話(huà)令牌。唯一不能繞過(guò)的身份驗(yàn)證限制,就是硬編碼的服務(wù)帳戶(hù)名,因?yàn)樵撝档扔诠粽邘?hù),而不是預(yù)期的VM帳戶(hù)名。
針對(duì)易受攻擊配置的端到端攻擊過(guò)程如下所示:
1. 在你控制的GCP項(xiàng)目中創(chuàng)建一個(gè)服務(wù)賬戶(hù),并使用gcloud生成一個(gè)私鑰:gcloud iam service-accounts keys create key.json --iam-account [email protected]。
2. 用一個(gè)偽造的compute_engine claim來(lái)給一個(gè)JWT簽名,以冒充一個(gè)現(xiàn)有的、有特權(quán)的虛擬機(jī)。請(qǐng)看這里的簡(jiǎn)單的概念驗(yàn)證腳本,其中已經(jīng)考慮到了大部分的細(xì)節(jié)。
3. 現(xiàn)在,只需使用令牌登錄Vault即可:curl --request POST --data '{"role": "my-gce-role", "jwt" : "...."}' http://vault:8200/v1/auth/gcp/login
這是一個(gè)非常有趣的漏洞,需要對(duì)GCP IAM有一定的了解才能發(fā)現(xiàn)它。該漏洞的根源,好像是因?yàn)樵趐arseAndValidateJwt函數(shù)中,將兩個(gè)獨(dú)立的認(rèn)證流合并到一個(gè)代碼路徑中,這使得在編寫(xiě)或?qū)彶榇a時(shí),很難弄清楚所有的安全要求。同時(shí),由于GCP提供了兩種具有完全不同安全屬性的JWT令牌,使得自己很容易中槍。
小結(jié)
本文介紹了用于管理機(jī)密信息的“云原生”軟件HashiCorp Vault中被曝出的兩個(gè)認(rèn)證漏洞。雖然Vault在開(kāi)發(fā)時(shí)明顯考慮到了安全問(wèn)題,并從其實(shí)現(xiàn)語(yǔ)言Go的內(nèi)存安全和高質(zhì)量標(biāo)準(zhǔn)庫(kù)中受益良多,但我仍然能夠在其無(wú)需認(rèn)證的攻擊面中發(fā)現(xiàn)兩個(gè)關(guān)鍵漏洞。
根據(jù)我的經(jīng)驗(yàn),在開(kāi)發(fā)人員必須與外部系統(tǒng)和服務(wù)交互的地方,經(jīng)常會(huì)存在類(lèi)似這樣的棘手漏洞。一個(gè)強(qiáng)大的開(kāi)發(fā)人員也許能夠推理出自己軟件的所有安全邊界、需求和陷阱,但一旦有復(fù)雜的外部服務(wù)出現(xiàn),確保軟件的安全性就變得非常困難。雖然現(xiàn)代云IAM解決方案功能強(qiáng)大,通常比同類(lèi)內(nèi)部解決方案更安全,但也有自己的安全隱患和較高的實(shí)施復(fù)雜性。隨著越來(lái)越多的公司向大型云提供商遷移,熟悉這些技術(shù)棧將成為安全工程師和研究人員的關(guān)鍵技能,可以肯定的是,未來(lái)幾年肯定會(huì)曝出越來(lái)越多的同類(lèi)問(wèn)題。
最后,本文所討論的兩個(gè)漏洞都表明了編寫(xiě)的安全軟件是多么的困難。即使使用內(nèi)存安全的語(yǔ)言、強(qiáng)大的密碼學(xué)原語(yǔ)、靜態(tài)分析和大型模糊基礎(chǔ)結(jié)構(gòu),某些問(wèn)題也只能通過(guò)手動(dòng)代碼審查和攻擊者的思維方式才能發(fā)現(xiàn)。
本文翻譯自:https://googleprojectzero.blogspot.com/2020/10/enter-the-vault-auth-issues-hashicorp-vault.html如若轉(zhuǎn)載,請(qǐng)注明原文地址。
網(wǎng)頁(yè)名稱(chēng):深入剖析HashiCorpVault中的身份驗(yàn)證漏洞(下篇)
文章路徑:http://m.fisionsoft.com.cn/article/cdgchii.html


咨詢(xún)
建站咨詢(xún)
